/*   Riassence Framework
 *   Copyright 2006 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/** = Description
  * The extended object model.
  *
  * HClass is the foundation class of all other classes. It implements
  * a proper object oriented environment in Javascript. It supports
  * inheritance (HClass.extend), superclass invocation ( this.base ),
  * interfaces (HClass.implement) and such.
  *
  * It's derived from and backwards compatible with  Dean Edward's Base.js,
  * so you can mix and match code written against Base.js.
  *
  * It is intended for the following main purposes:
  *   * To easily create classes without the MyClass.prototype cruft
  *   * Method overriding with intuitive access to the overridden method (like Ruby's super)
  *   * To avoid calling a class' constructor function during the prototyping phase
  *   * To easily add static (class) properties and methods
  *   * To achieve the above without resorting to global functions to build prototype chains
  *   * To achieve the above without affecting Object.prototype
  *
  * The HClass class extends the 'Object' object by adding one instance method (base) 
  * and two class methods (extend, implement). Instance method extend can be also called directly.
  *
  * == Example:
  *    MyClass = HClass.extend({
  *      constructor: function( foo ){
  *        this.setFoo( foo );
  *      },
  *      setFoo: function( foo ){
  *        this.foo = foo;
  *      }
  *    });
  *    
  *    myClassInstance1 = MyClass.nu( 'rabbids' );
  *    myClassInstance2 = new MyClass( 'ribbit' );
  *    
  *    MyEqualsClass = MyClass.extend({
  *      testFoo: function( that ){
  *        return this.foo === that.foo;
  *      }
  *    });
  *    myEqualsClassInstance1 = MyEqualsClass.nu( 'woof' );
  *    equals1 = myEqualsClassInstance1.testFoo( myClassIntance1 );
  *    myClassInstance2.setFoo( myEqualsClassInstance1.foo );
  *    equals2 = myEqualsClassInstance1.testFoo( myClassIntance2 );
  *
  **/
HClass = function() {
  if ( arguments.length ) {
    if (this === window) {
      HClass.prototype.extend.call( arguments[0], arguments.callee.prototype );
    }
    else {
      this.extend( arguments[0] );
    }
  }
};


HClass.prototype = {
  
 /* The property copying method. */
  extend: function(_source, _value) {
    var _extend = HClass.prototype.extend;
    if (arguments.length === 2) {
      var _ancestor = this[_source];
      // only methods are inherited
      if ((_ancestor instanceof Function) && (_value instanceof Function) &&
          _ancestor.valueOf() !== _value.valueOf() && (/\bbase\b/).test(_value)) {
        var _method = _value;
        _value = function() {
          // saves the this.base that is the this.base method of this child
          var _previous = this.base;
          // copies previous this.base from the direction from HClass
          this.base = _ancestor;
          // current class's method is called
          // now inside the function when called this.base points to parent method
          var _returnValue = _method.apply(this, arguments);
          // then because event this function can be called from child method
          // resets the base to as is was before calling this function
          this.base = _previous;
          return _returnValue;
        };
        _value.valueOf = function() {
          return _method;
        };
        _value.toString = function() {
          return String(_method);
        };
      }
      return this[_source] = _value;
    // this is called when called by HClass.extend
    } else if (_source) {
      var _prototype = {toSource: null};
      var _protected = ["toString", "valueOf"];
      // we want default constructor function
      if (HClass._prototyping) {
        // 3. index
        _protected.push("constructor");
      }
      for (var i = 0; (_name = _protected[i]); i++) {
        if (_source[_name] !== _prototype[_name]) {
          _extend.call(this, _name, _source[_name]);
        }
      }
      for (var _name in _source) {
        if (!_prototype[_name]) {
          _extend.call(this, _name, _source[_name]);
        }
      }
    }
    
    // alternative constructor (use instead of the new keywoard)
    this.nu = function() {
      return new (
        this.extend( {
          constructor: function( args ){
            this.base.apply( this, args );
          }
        } )
      )( arguments );
    };
    return this;
  },
  /** = Description
    * If a method has been overridden then the base method provides access to the overridden method. 
    * Call this method from any other method to invoke that method's ancestor.
    * It is also possible to call the base method from within a constructor function.
    *
    **/
  base: function() {
    // this method can be called from any other method to invoke that method's parent
  }
};

/** = Description
  * The class method method to create a new extended class.
  *
  * If the method name constructor is defined null as the +_instance+ parameter,
  * it will return a single instance.
  *
  * = Parameters:
  * +_instance+:: An object that has properties and methods of inherited class.
  * +_static+::   An object that has properties and methods of inherited class's
  *               class methods if the method named constructor is defined null
  *               in _instance parameter.
  *
  * = Returns:
  *   Returns the extended class object.
  *
  * = Usage:
  *   Point = HClass.extend({
  *   constructor: function(x, y) {
  *     this.x = x;
  *     this.y = y;
  *   }
  *   });
  *   Rectangle = Point.extend({
  *   constructor: function(x, y, width, height) {
  *     this.base(x, y);
  *     this.width = width;
  *     this.height = height;
  *   },
  *   getWidth: function() {
  *     return this.width;
  *   },
  *   getHeight: function() {
  *     return this.height;
  *   }
  *   },
  *   {
  *   // class methods
  *   description: "this is a Rectangle",
  *   getClass: function() {
  *     return this;
  *   }
  *   });
  *
  **/
HClass.extend = function(_instance, _static) {
  // reference to HClass's prototype extend method (HClass's class structure extend method)
  var _extend = HClass.prototype.extend;
  // if _instance is undefined,null,"" etc. creates object so that code below works
  if (!_instance) {
    _instance = {};
  }
  HClass._prototyping = true;
  // this is base for single instance or prototype (class structure) for object that are created
  // from this class
  var _prototype = new this;
  // copies properties and methods from _instance to _prototype (class structure)
  _extend.call(_prototype, _instance);
  // this constructor came from _instance
  var _constructor = _prototype.constructor;
  _prototype.constructor = this;
  delete HClass._prototyping;
  
  var _klass = function() {
    if (!HClass._prototyping) {
      _constructor.apply(this, arguments);
    }
    this.constructor = _klass;
  };
  // this is the new class's prototype (class structure)
  _klass.prototype = _prototype;
  // copies static method (class method)
  // acts like HClass.extend
  _klass.extend = this.extend;
  // copies static method (class method)
  // acts like HClass.implement
  _klass.implement = this.implement;
  _klass.toString = function() {
    return String(_constructor);
  };
  // copies properties and methods from _static directly to statc methods (class methods)
  // of new class
  _extend.call(_klass, _static);
  // if _constructor is marked as null returns the created instance (that is also class structure for
  // instances if class is returned
  var _object = (_constructor !== null) ? _klass : _prototype;
  if (_object.init instanceof Function) {
    _object.init();
  }
  return _object;
};

/** = Description
  * Copies the prototype properties and methods from _interface or if it is an object it's properties and functions
  * to HClass or class inherited from HClass. Mimics the interface behaviour of ordinary programming languages.
  *
  * = Usage:
  * Defines an interface:
  *  
  *  AreaInterface = HClass.extend({
  *  constructor: null,
  *    // implement
  *    // don't define here
  *    //getWidth: function() {},
  *    //getHeight: function() {},
  *  area: function() {
  *    return this.getWidth() * this.getHeight();
  *  }
  *  });
  *  
  *  Rectangle = HClass.extend({
  *  constructor: function(x, y, width, height) {
  *    this.x = x;
  *    this.y = y;
  *    this.width = width;
  *    this.height = height;
  *  },
  *  getWidth: function() {
  *    return this.width;
  *  },
  *  getHeight: function() {
  *    return this.height;
  *  }
  *  });
  *  
  *  Rectangle.implement(AreaInterface);
  *
  **/
HClass.implement = function(_interface) {
  // copies prototype fields and methods (class structures properties and methods)
  if (_interface instanceof Function) {
    _interface = _interface.prototype;
  }
  this.prototype.extend(_interface);
};

var Base = HClass;

// Array fix
if ([]['indexOf']===undefined) {
  Object.extend = function(destination, source) {
    for (property in source) {
      destination[property] = source[property];
    }
    return destination;
  };
  Object.extend(Array.prototype, {
    indexOf: function(_anObject){
      var i = 0, l = this.length;
      for (; i < l; i++) {
        if (this[i] === _anObject) {
          return i;
        }
      }
      return -1;
    }
  });
}


// ff version 3.0.3 fails on this, when firebug installed: try/catch block
try {

// console.log surrogate for browsers without a console
if(window['console']===undefined){
  console = {
    log: function(){
    }
  };
}


} catch(e) {}


/*   Riassence Framework
 *   Copyright 2007 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/** = Description
  * The interval to flush the buffer specified in milliseconds.
**/
ELEMTickerInterval = 10;

/** = Description
  * An object that contains the browser types detected as booleans.
**/
BROWSER_TYPE = {
  
/* Any version of Microsoft Internet Explorer */
  ie: false,
  
/* Microsoft Internet Explorer version 6 */
  ie6: false,
  
/* Microsoft Internet Explorer version 7 */
  ie7: false,
  
/* Microsoft Internet Explorer version 8 */
  ie8: false,
  
/* Any version of Opera */
  opera: false,
  
/* Any version of Safari (and other KHTML/WebKit -derived browsers) */
  safari: false,
  
/* The Symbian version of KHTML/WebKit/Safari, is also registered as +safari+ */
  symbian: false,
  
/* Any version of Google Chrome, is also registered as +safari+ */
  chrome: false,
  
/* Any version of Mozilla Firefox */
  firefox: false,

/* Mozilla Firefox version 2 */
  firefox2: false,

/* Mozilla Firefox version 3 */
  firefox3: false
};

/** = Description
  * The DOM Abstraction collection. Implements a managed buffer
  * for style properties.
**/
ELEM = {

  // stuff moved inside this function, because (surprise, surprise!) ie6 had some issues with it.
  _constructor: function() {
    var _this = ELEM;

    _this._enableRecycler = false;

    // pre-init queue
    _this._domLoadQueue = [];
    _this._domLoadTimer = null;

    // turns true when document is actually loaded:
    _this._domLoadStatus = false;

    // initial tasks
    _this._initDone = false;

    _this._makeCount = 0;

    _this._setStyleCount = 0;
    _this._setStyleDiffCount = 0;
    _this._getStyleCount = 0;
    _this._getStyleMissCount = 0;

    _this._flushLoopCount = 0;
    _this._flushLoopFlushedCount = 0;
    _this._flushStylCount = 0;

    _this._flushTime = 0;
    _this._flushCounter = 0;
    _this._idleDelay = 500;

    _this._timer = null;
    _this._minDelay = ELEMTickerInterval;
    _this._flushing = false;
    _this._needFlush = false;
    _this._slowness = 1;

    _this._elements = [];
    if (_this._enableRecycler) {
      _this._recycler = {
        _tagNames: []
      };
    } else {
      _this._freeElemIds = [];
    }
    _this._styleCache = {};
    _this._styleTodo = {};
    _this._attrTodo = {};
    _this._attrCache = {};
    _this._elemTodo = [];
    _this._elemTodoH = {};
    _this._blockElems = ",ADDRESS,BLOCKQUOTE,CENTER,DIR,DIV,DL,FIELDSET,FORM,H1,H2,H3,H4,H5,H6,HR,ISINDEX,MENU,NOFRAMES,NOSCRIPT,OL,P,PRE,TABLE,UL,";
  },


  _fillTrash: function(_count, _tagName) {
    if (!ELEM._enableRecycler) {
      return;
    }
    var _this = ELEM,
        i = 0,
        _toDel = [],
        _recycler = _this._initRecycler(_tagName),
        _trashId = _recycler._trashId;
    for (; i !== _count; i++) {
      _toDel.push(_this.make(_trashId, _tagName));
    }
    for (i = 0; i !== _count; i++) {
      _this.del(_toDel[i]);
    }
  },

  // adds an element reference
  // returns its id
  _add: function(_elem) {
    var _id,
        _this = ELEM,
        _elements = _this._elements,
        _hasFreeElemIds = (_this._freeElemIds.length !== 0);
    if (_hasFreeElemIds) {
      _id = _this._freeElemIds.pop();
      _elements[_id] = _elem;
    }
    else {
      // Adds the element to the cache
      _elements.push(_elem);
      // Get cache size == serial id
      _id = _elements.length - 1;
    }
    return _id;
  },

  // makes new style caches
  _initCache: function(_id) {
    var _this = ELEM;
    _this._styleTodo[_id] = [];
    _this._styleCache[_id] = {};
    _this._attrTodo[_id] = [];
    _this._attrCache[_id] = {};
    _this._elemTodoH[_id] = false;
  },

/** = Description
  * Binds a DOM element by the DOM ID-attribute.
  *
  * = Parameters
  * +_domId+::   The element's ID-attribute to bind.
  *
  * = Returns
  * The ELEM ID (to use with other ELEM methods.
  *
  **/
  bindId: function(_domId) {
    var _this = ELEM,
        _elem = document.getElementById(_domId),
        _elemId = _this._add(_elem);
    _this._initCache(_elemId);
    return _elemId;
  },

/** = Description
  * Binds a DOM element by the DOM Element Object itself.
  *
  * = Parameters
  * +_elem+::   The DOM Element object to bind.
  *
  * = Returns
  * The ELEM ID (to use with other ELEM methods.
  *
  **/
  bind: function(_elem) {
    var _this = ELEM,
        _id = _this._add(_elem);
    _this._initCache(_id);
    return _id;
  },

  // deprecated; backwards-compatibility
  _replace: function(_id, _elem) {
    var _this = ELEM;
    _this._elements[_id] = _elem;
  },

/** = Description
  * Gets a DOM Element by its ELEM ID.
  *
  * = Parameters
  * +_id+::  The ELEM ID.
  *
  * = Returns
  * The DOM Element object.
  *
  **/
  get: function(_id) {
    return ELEM._elements[_id];
  },

/** = Description
  * Sets the innerHTML of an element by its ELEM ID.
  *
  * = Parameters
  * +_id+::   The ELEM ID.
  * +_html+:: The HTML Block as a string to set the innerHTMl property with.
  *
  **/
  setHTML: function(_id, _html) {
    try {
      var _this = ELEM;
      if (!_this._elements[_id]) {
        return;
      }
      if (! ((typeof _html === 'string') || (typeof _html === 'number'))) {
        return;
      }
      _this._elements[_id].innerHTML = _html;
    } catch(e) {}
    //_this._initCache(_id);
  },

/** = Description
  * Gets the innerHTML of an element by its ELEM ID.
  *
  * = Parameters
  * +_id+::   The ELEM ID.
  *
  * = Returns
  * The innerHTML of the element the ELEM ID is bound to.
  *
  **/
  getHTML: function(_id) {
    try {
      var _this = ELEM;
      if (_this._elements[_id]) {
        return _this._elements[_id].innerHTML;
      }
    } catch(e) {}
    //_this._initCache(_id);
    return '';
  },

  _initRecycler: function(_tagName) {
    if (!ELEM._enableRecycler) {
      return null;
    }
    var _this = ELEM,
        _recycler = _this._recycler;
    if (!_recycler[_tagName]) {
      _recycler._tagNames.push(_tagName);
      _recycler[_tagName] = [];
      _recycler[_tagName]._countIn = 1;
      _recycler[_tagName]._countOut = 0;
      _recycler[_tagName]._trashId = _this.make(_this._trashId, 'div');
    }
    return _recycler[_tagName]._trashId;
  },

/** = Description
  * Deletes an element and its associated buffers by ELEM ID.
  *
  * = Parameters
  * +_id+::   The ELEM Id to delete.
  *
  **/
  del: function(_id) {
    var _this = ELEM,
        _elem = _this._elements[_id];
    if (_this._flushing) {
      _this.del(_id);
    }
    _this._flushing = true;
    if (_this._enableRecycler) {
      var _tagName = _elem.tagName,
          _trashId = _this._initRecycler(_tagName),
          _recycler = _this._recycler[_tagName];
      _this.append(_id, _trashId);
    }

    var _elemTodoIdx = _this._elemTodo.indexOf(_id);
    if (_elemTodoIdx !== -1) {
      _this._elemTodo.splice(_elemTodoIdx, 1);
    }
    _this._initCache(_id);
    if (_this._enableRecycler) {
      _recycler._countIn++;
      _recycler.push(_id);
    } else {
      _this._freeElemIds.push(_id);
      var _parentNode = _elem.parentNode;
      if (_parentNode !== null) {
        _parentNode.removeChild(_elem);
      }
      _elem = null;
      _this._elements[_id] = null;
    }
    _this._flushing = false;
  },

/** = Description
  * Moves the source element inside the target element.
  *
  * = Parameters
  * +_sourceId+::   The source element's ELEM ID to move.
  * +_targetId+::   The target element's ELEM ID to move the source element into.
  *
  **/
  append: function(_sourceId, _targetId) {
    var _this = ELEM,
        _source = _this._elements[_sourceId],
        _target = _this._elements[_targetId];
    _target.appendChild(_source);
  },

/** = Description
  * Replaces all styles of the element with the string containing css source.
  *
  * = Parameters
  * +_id+::         The ELEM ID which all styles will be replaced
  *                 with the css source.
  * +_cssText+::    A string containing the CSS Text.
  *
  **/
  setCSS: function(_id, _cssText) {
    ELEM._elements[_id].style.cssText = _cssText;
  },

/** = Description
  * Returns the CSS source style of the element.
  * 
  * = Parameters
  * +_id+::   The ELEM ID which all styles will be returned as
  *           a string containing the CSS source.
  * 
  * = Returns
  * A string containing the CSS source of the element.
  *
  **/
  getCSS: function(_id) {
    return ELEM._elements[_id].style.cssText;
  },

/** = Description
  * Returns the visible size of an element.
  *
  * = Parameters
  * +_id+::  The ELEM ID.
  *
  * = Returns
  * An [ width, height ] pair as an Array.
  *
  **/
  getVisibleSize: function(_id) {
    var _parentOverflow,
        _this = ELEM,
        _elem = _this._elements[_id],
        w = _elem.offsetWidth,
        h = _elem.offsetHeight,
        _parent = _elem.parentNode;
    while (_parent && _parent.nodeName.toLowerCase() !== 'body') {
      if (!_this._is_ie) {
        _parentOverflow = document.defaultView.getComputedStyle(
        _parent, null
        ).getPropertyValue('overflow');
      }
      else {
        _parentOverflow = _parent.currentStyle.getAttribute('overflow');
      }
      _parentOverflow = _parentOverflow !== 'visible';
      if (w > _parent.clientWidth && _parentOverflow) {
        w = _parent.clientWidth - _elem.offsetLeft;
      }
      if (h > _parent.clientHeight && _parentOverflow) {
        h = _parent.clientHeight - _elem.offsetTop;
      }
      _elem = _elem.parentNode;
      _parent = _elem.parentNode;
    }
    return [w, h];
  },

/** = Description
  * Returns the full size of the element.
  *
  * = Parameters
  * +_id+::  The ELEM ID.
  *
  * = Returns
  * An [ width, height ] pair as an Array.
  *
  **/
  getSize: function(_id) {
    var _this = ELEM,
        _elem = _this._elements[_id],
        w = _elem.offsetWidth,
        h = _elem.offsetHeight;
    return [w, h];
  },

/** = Description
  * Returns the scroll size of the element.
  *
  * = Parameters
  * +_id+::  The ELEM ID.
  *
  * = Returns
  * An [ width, height ] pair as an Array.
  *
  **/
  getScrollSize: function(_id) {
    var _this = ELEM,
        _elem = _this._elements[_id],
        w = _elem.scrollWidth,
        h = _elem.scrollHeight;
    return [w, h];
  },
  
/** = Description
  * Returns the real position of the element, subtracting whatever
  * scroll bars do to the position..
  *
  * = Parameters
  * +_id+::  The ELEM ID.
  *
  * = Returns
  * An [ x, y ] coordinate pair as an Array.
  *
  **/
  getVisiblePosition: function(_id) {
    var _this = ELEM,
        x = 0,
        y = 0,
        _elem = _this._elements[_id];
    while (_elem !== document) {
      x += _elem.offsetLeft;
      y += _elem.offsetTop;
      x -= _elem.scrollLeft;
      y -= _elem.scrollTop;
      _elem = _elem.parentNode;
      if (!_elem) {
        break;
      }
    }
    return [x, y];
  },

/** = Description
  * Returns the opacity of the element.
  *
  * = Parameters
  * +_id+::  The ELEM ID.
  *
  * = Returns
  * The opacity as a floating point number between 0.0 (transparent) and 1.0 (opaque).
  *
  **/
  getOpacity: function(_id) {
    var _opacity,
        _try_opacity,
        _this = ELEM,
        _getStyle = _this.getStyle;
    // old safari (1.x):
    if (_opacity === _getStyle(_id, '-khtml-opacity')) {
      return parseFloat(_opacity);
    }
    // old mozilla (ff 1.0 and below):
    if (_opacity === _getStyle(_id, '-moz-opacity')) {
      return parseFloat(_opacity);
    }
    _try_opacity = _getStyle(_id, 'opacity', true);
    if (_opacity === _try_opacity || (_try_opacity === 0)) {
      return parseFloat(_opacity);
    }
    if (_opacity === (_this._elements[_id].currentStyle['filter'] || '').match(/alpha(opacity=(.*))/)) {
      if (_opacity[1]) {
        return parseFloat(_opacity[1]) / 100;
      }
    }
    return 1.0;
  },
  
/** = Description
  * Sets the opacity of the element.
  *
  * = Parameters
  * +_id+::       The ELEM ID.
  * +_opacity+::  The opacity as a floating point number between 0.0 (transparent) and 1.0 (opaque).
  *
  **/
  setOpacity: function(_id, _opacity) {
    var _this = ELEM;
    if (_opacity === 1 && _this._is_ie6) {
      _this._elements[_id].style.setAttribute('filter', _this.getStyle(_id, 'filter', true).replace(/alpha([^)]*)/gi, ''));
    }
    else {
      if (_opacity < 0.01) {
        _opacity = 0;
      }
      if (_this._is_ie6) {
        _this._elements[_id].style.setAttribute('filter', _this.getStyle(_id, 'filter', true).replace(/alpha([^)]*)/gi, '') + 'alpha(opacity=' + _opacity * 100 + ')');
      }
      else if (_this._is_ie) {
        (_this._elements[_id].style.setAttribute('opacity', _opacity));
      }
      else {
        _this._elements[_id].style.setProperty('opacity', _opacity, '');
      }
    }
  },

/** = Description
  * Like getStyle, but always return an integer number.
  *
  * = Parameters
  * +_id+::   The ELEM ID.
  * +_key+::  The style property name.
  *
  * = Returns
  * The value of the style property.
  *
  **/
  getIntStyle: function(_id, _key) {
    var _value = ELEM.getStyle(_id, _key);
    return parseInt(_value, 10);
  },
  
/** = Description
  * Sets the box coordinates (x,y,width,height) all at once.
  *
  * = Parameters
  * +_id+::      The ELEM ID.
  * +_coords+::  An array containing exactly four coordinates in the following
  *              order: x, y, width, height
  *
  **/
  setBoxCoords: function(_id, _coords) {
    ELEM.setStyle(_id, 'left', _coords[0] + 'px');
    ELEM.setStyle(_id, 'top', _coords[1] + 'px');
    ELEM.setStyle(_id, 'width', _coords[2] + 'px');
    ELEM.setStyle(_id, 'height', _coords[3] + 'px');
  },
  
/** = Description
  * Returns the amount of width of the element taken by 'extra' space: border
  * and padding size.
  *
  * = Parameters
  * +_id+::      The ELEM ID.
  *
  * = Returns
  * The amount of extra width as an integer.
  *
  **/
  getExtraWidth: function(_id) {
    var _int = ELEM.getIntStyle;
    return _int(_id, 'padding-left') + _int(_id, 'padding-right') + _int(_id, 'border-left-width') + _int(_id, 'border-right-width');
  },
  
/** = Description
  * Returns the amount of height of the element taken by 'extra' space: border
  * and padding size.
  *
  * = Parameters
  * +_id+::      The ELEM ID.
  *
  * = Returns
  * The amount of extra height as an integer.
  *
  **/
  getExtraHeight: function(_id) {
    var _int = ELEM.getIntStyle;
    return _int(_id, 'padding-top') + _int(_id, 'padding-bottom') + _int(_id, 'border-top-width') + _int(_id, 'border-bottom-width');
  },
  
/** = Description
  * Re-calculates the amount of delay to achieve a new target frame rate.
  *
  * The DOM refreshes are typically the most expensive tasks of an Javascript
  * application, it's a good idea to set an optimal frame rate for the DOM
  * updates to let your logic code get as many cpu cycles as possible without
  * wasting most of them on more DOM refreshes than necessary.
  *
  * The default frame rate is 30 and the maximum frame rate allowed is 100.
  *
  * = Parameters
  * +_fps+::      The target frame rate (DOM updates per second).
  *
  **/
  setFPS: function(_fps) {
    var _this = ELEM;
    _this._minDelay = 1000 / _fps;
    if (_this._minDelay < ELEMTickerInterval) {
      _this._minDelay = ELEMTickerInterval;
    }
  },
  
/** = Description
  * An additional adjustment multiplier to offset the slowness of a
  * target browser.
  *
  * The default is 1.0 (no change to the flush delay calculated by setFPS)
  * A higher value than 1.0 means less frequent updates (slower than 
  * target machine).
  *
  * You'll need a benchmark in your application to calculate the multiplier
  * correctly depending on the tasks necessary. The multiplier is useless 
  * without a benchmark, unless you want to temporarily make updates very
  * infrequent (or frequent) depending on what your application needs to do.
  *
  * = Parameters
  * +_slowness+::      The multiplier used to offset the DOM update delay.
  *
  **/
  setSlowness: function(_slowness) {
    // we should replace this with an
    // actual browser speed benchmark
    ELEM._slowness = _slowness;
  },
  
/** = Description
  * Sets the idle delay.
  *
  * The idle delay is the amount of milliseconds between polling for something
  * to flush to DOM. This is the setting affecting how long (max) it takes to
  * start flushing the buffers after the buffers have been emptied.
  *
  * = Parameters
  * +_idleDelay+::     The amount of milliseconds to wait before re-checking
  *                    the buffers after going idle.
  *
  **/
  setIdleDelay: function(_idleDelay) {
    ELEM._idleDelay = _idleDelay;
  },
  
  // flag for IE6
  _ieFixesNeeded: false,
  
/** = Description
  * Flushes the buffers.
  * Call this method manually, if you need to ensure all changes are
  * flushed to the DOM before doing another operation.
  *
  * = Parameters:
  * +_delay+::  Time (ms) before retrying, if another flushLoop is busy.
  *
  **/
  flushLoop: function(_delay) {
    var _this = ELEM;
    _this._flushLoopCount++;
    if (_this._is_ie6 && (_this._flushLoopCount % 5 === 0) && _this._ieFixesNeeded) {
      //window.status = 'traversetree0:'+_this._flushLoopCount;
      iefix._traverseTree();
      _this._ieFixesNeeded = false;
    }
    clearTimeout(_this._timer);
    if (_this._flushing) {
      _delay *= 2;
      _this._timer = setTimeout(
        function(){
          ELEM.flushLoop( _delay );
        }, _delay
      );
      return;
    } else {
      if (!_this._needFlush) {
        // goto sleep mode
        if (_this._is_ie6 && _this._ieFixesNeeded) {
          //window.status = 'traversetree1:'+_this._flushLoopCount;
          iefix._traverseTree();
          _this._ieFixesNeeded = false;
        }
        _this._timer = setTimeout(
          function(){
            ELEM.flushLoop(_delay);
          }, _this._idleDelay
        );
        return;
      }
      _delay = parseInt(_this._slowness * (_this._flushTime / _this._flushCounter), ELEMTickerInterval);
      if (_delay < _this._minDelay || !_delay) {
        _delay = _this._minDelay;
      }
      _this._flushing = true;
      _this._timer = setTimeout(
        function(){
          ELEM.flushLoop(_delay);
        }, _delay
      );
    }
    _this._flushTime -= new Date().getTime();
    var i,
        _id,
        _elemTodo = _this._elemTodo,
        _loopMaxL = _elemTodo.length,
        _currTodo = _elemTodo.splice(0, _loopMaxL),
        _flushStartTime = new Date().getTime();
    for (i = 0; i < _loopMaxL; i++) {
      _this._flushLoopFlushed++;
      _id = _currTodo.pop();
      _this._elemTodoH[_id] = false;
      _this._flushStyleCache(_id);
      _this._flushAttrCache(_id);
    }
    _this._flushCounter++;
    _this._flushTime += new Date().getTime();
    if (_this._elemTodo.length === 0 && _this._needFlush) {
      _this._needFlush = false;
    }
    _this._flushing = false;
  },
  
  /* Method for flushing the attribute cache */
  _flushAttrCache: function(_id) {
    var _this = ELEM,
        _attrTodo = _this._attrTodo[_id],
        _attrCache = _this._attrCache[_id],
        _elem = _this._elements[_id],
        //_elemP=_elem.setAttribute,
        _key,
        _val,
        i,
        _iMax = _attrTodo.length,
        _currTodo = _attrTodo.splice(0, _iMax);
    for (i = 0; i !== _iMax; i++) {
      _key = _currTodo.pop();
      _val = _attrCache[_key];
      _elem.setAttribute(_key, _val);
      //_elem[_key]=_val;
    }
  },
  
/** = Description
  * Gets a named element attribute.
  *
  * Regular element attributes are cached like the style attributes.
  * Use this method to get an attribute from the element.
  *
  * = Parameters
  * +_id+::       The ELEM ID.
  * +_key+::      The Attribute name.
  * +_bypass+::   A flag used to bypass the buffers (Optional, default: false)
  *
  * = Returns
  * The attribute value.
  *
  **/
  getAttr: function(_id, _key, _bypass) {
    var _this = ELEM,
        _attrVal = _this._attrCache[_id][_key],
        _val;
    if (_attrVal !== undefined && !_bypass) {
      return _attrVal;
    }
    var _elem = _this._elements[_id];
    if (_elem.getAttribute(_key) === null) {
      _elem[_key] = '';
    }
    _val = _elem.getAttribute(_key);
    _this._attrCache[_id][_key] = _val;
    return _val;
  },
  
/** = Description
  * Sets a named element attribute value.
  *
  * Regular element attributes are cached like the style attributes.
  * Use this method to set an attribute value to the element.
  *
  * = Parameters
  * +_id+::       The ELEM ID.
  * +_key+::      The Attribute name.
  * +_value+::    The Attribute value.
  * +_bypass+::   A flag used to bypass the buffers (Optional, default: false)
  *
  **/
  setAttr: function(_id, _key, _value, _bypass) {
    var _this = ELEM,
        _attrTodo = _this._attrTodo[_id],
        _attrCache = _this._attrCache[_id],
        _differs = _value !== _this.getAttr(_id, _key);
    if (_differs) {
      _attrCache[_key] = _value;
      if (_bypass) {
        _this._elements[_id].setAttribute(_key, _value);
      }
      else {
        if (_attrTodo.indexOf(_key) === -1) {
          _attrTodo.push(_key);
        }
        if (!_this._elemTodoH[_id]) {
          _this._elemTodo.push(_id);
          _this._elemTodoH[_id] = true;
          _this._checkNeedFlush();
        }
      }
    }
  },
  
/** = Description
  * Deletes a named element attribute
  *
  * = Parameters
  * +_id+::       The ELEM ID.
  * +_key+::      The Attribute name.
  *
  **/
  delAttr: function(_id, _key) {
    var _differs,
        _this = ELEM,
        _attrTodo = _this._attrTodo[_id],
        _attrCache = _this._attrCache[_id];
    delete _attrCache[_key];
    _this._elements[_id].removeAttribute(_key);
    if (_attrTodo.indexOf(_key) !== -1) {
      _attrTodo.splice(_attrTodo.indexOf(_key, 1));
    }
    if (_this._elemTodoH[_id]) {
      _this._elemTodo.splice(_this._elemTodo.indexOf(_id, 1));
      _this._elemTodoH[_id] = false;
      _this._checkNeedFlush();
    }
  },

/** = Description
  * Checks if a element has a CSS class name.
  *
  * = Parameters
  * +_elemId+::       The ELEM ID.
  * +_className+::    The CSS class name to check.
  *
  * = Returns
  * Returns null, if the element does not exist
  * Returns true, if the element has the class name set
  * Returns false otherwise
  *
  **/
  hasClassName: function(_elemId, _className) {
    var _element = ELEM.get(_elemId);
    if (!_element) {
      return null;
    }
    var _classNames = _element.className.split(' ');
    return (_classNames.indexOf(_className) !== -1);
  },
  
/** = Description
  * Adds a CSS class name to the element.
  *
  * = Parameters
  * +_elemId+::       The ELEM ID.
  * +_className+::    The CSS class name to add.
  *
  **/
  addClassName: function(_elemId, _className) {
    var _this = ELEM,
        _element = _this.get(_elemId);
    if (!_element) {
      return;
    }
    
    if(_element.className === '' || _element.className === ' '){
      _element.className = _className;
    }
    else{
      var _classNames = _element.className.split(' '),
          _index = _classNames.indexOf(_className);
      if(_index===-1){
        _classNames.push(_className);
        _element.className = _classNames.join(' ');
      }
    }
  },
  
/** = Description
  * Removes the CSS class name from the element.
  *
  * = Parameters
  * +_elemId+::       The ELEM ID.
  * +_className+::    The CSS class name to remove.
  *
  **/
  removeClassName: function(_elemId, _className) {
    var _this = ELEM,
        _element = _this.get(_elemId);
    if (!_element) {
      return;
    }
    
    if(!_this.hasClassName(_elemId, _className)){
      return;
    }
    
    var _classNames = _element.className.split(' '),
        _index = _classNames.indexOf(_className);
    if(_index!==-1){
      _classNames.splice(_index,1);
      _element.className = _classNames.join(' ');
    }
  },
  
  /* Checks, if the buffers need to be flushed. */
  _checkNeedFlush: function() {
    var _this = ELEM;
    if (!_this._needFlush) {
      _this._needFlush = true;
      if (!_this._flushing) {
        clearTimeout(_this._timer);
        _this._timer = setTimeout( function(){ELEM.flushLoop(ELEM._minDelay);}, _this._minDelay);
      }
    }
  },
  
/** = Description
  * Sets the named element style attribute value.
  *
  * Use this method to set a style attribute value optimally.
  * Element style attributes are buffered.
  * The buffers are flushed on regular intervals.
  *
  * = Parameters
  * +_id+::       The ELEM ID.
  * +_key+::      The Style Attribute name.
  * +_value+::    The Style Attribute value.
  * +_bypass+::   A flag used to bypass the buffers (Optional, default: false)
  *
  **/
  setStyle: function(_id, _key, _value, _bypass) {
    var _this = ELEM,
        _cached = _this._styleCache[_id],
        _elems = _this._elements,
        _differs,
        _styleTodo;
    _this._setStyleCount++;
    if (_cached === undefined) {
      _this._initCache(_id);
      _cached = _this._styleCache[_id];
    }
    _differs = _value !== _cached[_key];
    if (_differs) {
      _this._setStyleDiffCount++;
      _cached[_key] = _value;
      if (_bypass) {
        if (_key === 'opacity') {
          _this.setOpacity(_id, _value);
        }
        else {
          if( _this._is_ie ) {
            var _camelKey = _key.replace(
              /((-)([a-z])(\w))/g,
              function($0, $1, $2, $3, $4) {
                return $3.toUpperCase() + $4;
              }
            );
            _elems[_id].style[_camelKey] = _cached[_key];
          }
          else {
            _elems[_id].style.setProperty(_key, _cached[_key], '');
          }
        }
        if (_this._is_ie6) {
          if (iefix._traverseStyleProperties.indexOf(_key) !== -1) {
            _this._ieFixesNeeded = true;
          }
        }
      }
      else {
        _elemTodoH = _this._elemTodoH;
        _styleTodo = _this._styleTodo[_id];
        if (_styleTodo.indexOf(_key) === -1) {
          _styleTodo.push(_key);
        }
        if (!_elemTodoH[_id]) {
          _this._elemTodo.push(_id);
          _elemTodoH[_id] = true;
          _this._checkNeedFlush();
        }
      }
    }
  },

/** = Description
  * Creates a new element inside another.
  *
  * Use this method to create a new DOM element.
  *
  * = Parameters
  * +_targetId+:: The ELEM ID of the parent element.
  *               (Optional, default: 0; the document body)
  *
  * +_tagName+::  The tag name of the element.
  *               (Optional, default: 'DIV')
  *
  * = Returns
  * The new ELEM ID.
  *
  **/
  make: function(_targetId, _tagName) {
    if (_targetId === undefined) {
      _targetId = 0;
    }
    if (_tagName === undefined) {
      _tagName = 'DIV';
    } else {
      _tagName = _tagName.toUpperCase();
    }
    var _this = ELEM,
        _elem,
        _id;
    _this._makeCount++;
    if (_this._enableRecycler) {
      if (_this._recycler[_tagName]) {
        if (_this._recycler[_tagName].length !== 0) {
          // Recycle the id of a previously deleted element
          _id = _this._recycler[_tagName].pop();
          _this._recycler[_tagName]._countOut++;
          _elem = _this._elements[_id];
          //_elem.innerHTML='';
          /*
      if(_elem.tagName!==_tagName){
      _elem.outerHTML='<'+_tagName+'></'+_tagName+'>';
      }
      */
          if (_this._blockElems.indexOf(',' + _tagName + ',') !== -1) {
            _this.setCSS(_id, 'display:block;');
          } else {
            _this.setCSS(_id, 'display:inline;');
          }
          _this.append(_id, _targetId);
          return _id;
        }
      }
    }
    _elem = document.createElement(_tagName);
    _this._elements[_targetId].appendChild(_elem);
    _id = _this._add(_elem);
    _this._initCache(_id);
    return _id;
  },
  
/** = Description
  * Returns the inner size of the browser window.
  *
  * = Returns
  * An [ width, height ] pair as an Array.
  *
  **/
  windowSize: function() {
    return [
      (window.innerWidth) ? window.innerWidth: document.documentElement.clientWidth,
      (window.innerHeight) ? window.innerHeight: document.documentElement.clientHeight
   ];
  },
  
/** = Description
  * Gets the named element style attribute value.
  *
  * Use this method to get a style attribute value optimally.
  * Element style attributes are buffered.
  *
  * = Parameters
  * +_id+::       The ELEM ID.
  * +_key+::      The Style Attribute name.
  * +_bypass+::   A flag used to bypass the buffers (Optional, default: false)
  *
  * = Returns
  * The element style attribute value.
  *
  **/
  getStyle: function(_id, _key, _bypass){
    var _this=ELEM,
        _cached=_this._styleCache[_id],
        _retval;
        _this._getStyleCount++;
    if ((_cached[_key] === undefined) || _bypass) {
      if (!_bypass) {
        _this._getStyleMissCount++;
      }
      if ((_key === 'opacity') && _bypass) {
        _retval = _this.getOpacity(_id);
      }
      else {
        _retval = document.defaultView.getComputedStyle(_this._elements[_id], null).getPropertyValue(_key);
      }
      _cached[_key] = _retval;
    }
    return _cached[_key];
  },
  
  /* The Internet Explorer version of getStyle */
  _getStyleIE: function( _id, _key, _bypass){
    var _this=ELEM,
        _cached=_this._styleCache[_id],
        _retval;
        _this._getStyleCount++;
    if ((_cached[_key] === undefined) || _bypass) {
      if (!_bypass) {
        _this._getStyleMissCount++;
      }
      if ((_key === 'opacity') && _bypass) {
        _retval = _this.getOpacity(_id);
      }
      else {
        _camelName = _key.replace(
          /((-)([a-z])(\w))/g,
          function($0, $1, $2, $3, $4) {
            return $3.toUpperCase() + $4;
          }
        );
        _this._elements[_id].currentStyle[_camelName];
      }
      _cached[_key] = _retval;
    }
    return _cached[_key];
  },
  
  /* Style buffer flushing algorithm */
  _flushStyleCache: function(_id) {
    var _this = ELEM,
        _styleTodo = _this._styleTodo[_id],
        _cached = _this._styleCache[_id],
        _elem = _this._elements[_id],
        _elemS,
        _loopMaxP,
        _cid,
        _key,
        _currTodo,
        _retval;
    if (!_elem) {
      return;
    }
    _elemS = _elem.style;
    _loopMaxP = _styleTodo.length;
    _currTodo = _styleTodo.splice(0, _loopMaxP);
    for (_cid = 0; _cid !== _loopMaxP; _cid++) {
      _key = _currTodo.pop();
      _this._flushStylCount++;
      if (_key === 'opacity') {
        _retval = _this.setOpacity(_id, _cached[_key]);
      }
      else {
        _elemS.setProperty(_key, _cached[_key], '');
      }
    }
  },
  
  /* Internet Explorer version of _flushStyleCache */
  _flushStyleCacheIE: function(_id) {
    var _this = ELEM,
        _styleTodo = _this._styleTodo[_id],
        _cached = _this._styleCache[_id],
        _elem = _this._elements[_id];
    if (!_elem) {
      return;
    }
    var _elemS = _elem.style,
        _loopMaxP = _styleTodo.length,
        i = 0,
        _key,
        _currTodo = _styleTodo.splice(0, _loopMaxP);
    for (; i !== _loopMaxP; i++) {
      _key = _currTodo.pop();
      _this._flushStylCount++;
      if (_key === 'opacity') {
        _this.setOpacity(_id, _cached[_key]);
      }
      else {
        if (_this._is_ie6) {
          if (iefix._traverseStyleProperties.indexOf(_key) !== -1) {
            _this._ieFixesNeeded = true;
          }
        }
        try {
          var _camelKey = _key.replace(
            /((-)([a-z])(\w))/g,
            function($0, $1, $2, $3, $4) {
              return $3.toUpperCase() + $4;
            }
          );
          // _elemS[_camelKey] = _cached[_key];
          _elemS.setAttribute(
            _camelKey,
            _cached[_key]
          );
        }
        catch(e) {
          console.log(e);
        }
      }
    }
  },
  
  /* The ELEM "post-constructor" */
  _init: function() {
    var _this = ELEM,
        _cmd,
        _type,
        _cmdResult;
    if (_this._is_ie) {
      ELEM.getStyle = _this._getStyleIE;
    }
    if (_this._is_ie) {
      ELEM._flushStyleCache = _this._flushStyleCacheIE;
    }
    
    _this.bind(document.body);
    
    // creates an 'trash' for div elements
    if (_this._enableRecycler) {
      _this._trashId = _this.make(0, 'div');
      _this.setCSS(_this._trashId, "display:none;visibility:hidden;");
      _this.setAttr(_this._trashId, 'id', 'trashcan_' + _this._trashId);
    }
    
    _this._timer = setTimeout( function(){ ELEM.flushLoop(ELEM._minDelay); }, _this._minDelay);
    
    if (!_this._domLoadQueue) {
      return;
    }
    
    while (_this._domLoadQueue.length !== 0) {
      _cmd = _this._domLoadQueue.shift();
      _type = (typeof _cmd);
      if (_type === 'function') {
        _cmd.call();
      }
      else if (_type === 'string') {
        _cmdResult = eval(_cmd);
        if (typeof _cmdResult === 'string') {
          _this._domLoadQueue.push(_cmdResult);
        }
      }
    }
    _this._initDone = true;
  },
  
  /* Checks browser versions and starts the document load check */
  _warmup: function() {
    var _this = ELEM,
        _ua = navigator.userAgent,
        _isIE = (document.all && _ua.indexOf("Opera") === -1),
        _browserTypesTable = [
          [ 'opera',    '_is_opera',   _ua.indexOf("Opera") !== -1           ],
          [ 'safari',   '_is_safari',  _ua.indexOf("KHTML") !== -1           ],
          [ 'symbian',  '_is_symbian', _ua.indexOf("SymbianOS") !== -1       ],
          [ 'chrome',   '_is_chrome',  _ua.indexOf("Chrome") !== -1          ],
          [ 'ie',       '_is_ie',      _isIE                                 ],
          [ 'ie6',      '_is_ie6',     _isIE && _ua.indexOf("MSIE 6") !== -1 ],
          [ 'ie7',      '_is_ie7',     _isIE && _ua.indexOf("MSIE 7") !== -1 ],
          [ 'ie8',      '_is_ie8',     _isIE && _ua.indexOf("MSIE 8") !== -1 ],
          [ 'firefox',  '_is_ff',      _ua.indexOf("Firefox") !== -1         ],
          [ 'firefox2', '_is_ff2',     _ua.indexOf("Firefox/2.") !== -1      ],
          [ 'firefox3', '_is_ff3',     _ua.indexOf("Firefox/3.") !== -1      ]
        ],
        i = 0,
        _typeKeyGlobal,
        _typeKeyLocal,
        _typeBool;
    for( ; i < _browserTypesTable.length; i++ ){
      _typeKeyGlobal = _browserTypesTable[i][0];
      _typeKeyLocal  = _browserTypesTable[i][1];
      _typeBool      = _browserTypesTable[i][2];
      BROWSER_TYPE[_typeKeyGlobal] = _typeBool;
      _this[_typeKeyLocal] = _typeBool;
    }
    _this._domWaiter();
  },
  
  /* Adds commands to be run when the document load check turns true */
  _domLoader: function(_cmd) {
    var _this = ELEM,
        _type = (typeof _cmd);
    if (_this._initDone === true) {
      if( _type === 'string' ) {
        eval(_cmd);
      }
      else if (_type === 'function'){
        _cmd.call();
      }
    } else {
      _this._domLoadQueue.push(_cmd);
    }
  },
  
  /* Checks if the document is fully loaded */
  _domWaiter: function() {
    var _isloaded = false,
        _this = ELEM;
    // A hack for ie (ripped from DomLoaded.js)
    // http://www.cherny.com/demos/onload/domloaded.js
    if (_this._is_ie) {
      var _ie_proto = "javascript:void(0)";
      if (location.protocol === "https:") {
        _ie_proto = "src=//0";
      }
      document.write("<scr" + "ipt id=__ie_onload defer src=" + _ie_proto + "></scr" + "ipt>");
      var _ie_script = document.getElementById("__ie_onload");
      _ie_script.onreadystatechange = function() {
        if (this.readyState === "complete") {
          ELEM._domLoadStatus = true;
          ELEM._init();
          delete ELEM._domLoadQueue;
          clearTimeout(ELEM._domLoadTimer);
          delete ELEM._domLoadTimer;
        }
      };
      // the event will trigger on ie, so we don't have to keep on polling:
      return;
    }

    // Safari / KHTML readyness detection:
    else if ((/KHTML|WebKit/i.test(navigator.userAgent)) &&
    (/loaded|complete/.test(document.readyState))) {
      _this._domLoadStatus = true;
    }

    // Works for Mozilla:
    else if (document.body) {
      _this._domLoadStatus = true;
    }

    if (!_this._domLoadStatus) {
      _this._domLoadTimer = setTimeout('ELEM._domWaiter()', ELEMTickerInterval * 10);
    } else {
      _this._init();
      delete _this._domLoadQueue;
      clearTimeout(_this._domLoadTimer);
      delete _this._domLoadTimer;
    }
  }
};
ELEM._constructor();

LOAD = ELEM._domLoader;

ELEM._warmup();

/*   Riassence Framework
 *   Copyright 2007 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** Abstracts the DOM Event differences between browsers.
***/
Event = {
  
/** Returns the element of the event.
  **/
  element: function(e) {
    return e.target || e.srcElement;
  },
  
/** Returns the mouse cursor x -coordinate of the event.
  **/
  pointerX: function(e) {
    return e.pageX || e.clientX + document.documentElement.scrollLeft;
  },

/** Returns the mouse cursor y -coordinate of the event.
  **/
  pointerY: function(e) {
    return e.pageY || e.clientY + document.documentElement.scrollTop;
  },

/** Stops event propagation
  **/
  stop: function(e) {
    if (e.preventDefault) {
      e.preventDefault();
      e.stopPropagation();
    }
    else {
      e.returnValue = false;
      e.cancelBubble = true;
    }
  },
  
/** Returns true if the left mouse butten was clicked.
  **/
  isLeftClick: function(e) {
    // IE: left 1, middle 4, right 2
    if (ELEM._is_ie || ELEM._is_safari) {
      return (e.button === 1);
    }
    else {
      return (e.button === 0);
    }
  },
  
/** List of event observers
  **/
  observers: false,
  
  /* Implementation of observe */
  _observeAndCache: function(_elem, _name, _function, _useCapture) {
    if (!Event.observers) {
      Event.observers = [];
    }
    if (_elem.addEventListener) {
      this.observers.push([_elem, _name, _function, _useCapture]);
      _elem.addEventListener(_name, _function, _useCapture);
    }
    else if (_elem.attachEvent) {
      this.observers.push([_elem, _name, _function, _useCapture]);
      _elem.attachEvent("on" + _name, _function);
    }
  },
  
/** Flushes the event observer cache.
  **/
  unloadCache: function() {
    if (!Event.observers) {
      return;
    }
    var i,
        l = Event.observers.length;
    for (i = 0; i < l; i++) {
      Event.stopObserving.apply(this, Event.observers[0]);
    }
    Event.observers = false;
  },
  
/** Starts observing the named event of the element and 
  * specifies a callback function.
  **/
  observe: function(_elem, _name, _function, _useCapture) {
    _useCapture = _useCapture || false;
    Event._observeAndCache(_elem, _name, _function, _useCapture);
  },
  
/** Stops observing the named event of the element and
  * removes the callback function.
  **/
  stopObserving: function(_elem, _name, _function, _useCapture) {
    if (_elem === undefined) {
      console.log('Warning Event.stopObserving of event name: "' + _name + '" called with an undefined elem!');
      return;
    }
    _useCapture = _useCapture || false;
    if (_elem['removeEventListener']) {
      _elem.removeEventListener(_name, _function, _useCapture);
    }
    else if (detachEvent) {
      _elem.detachEvent("on" + _name, _function);
    }
    var i = 0;
    while (i < Event.observers.length) {
      var eo = Event.observers[i];
      if (eo && eo[0] === _elem && eo[1] === _name && eo[2] === _function && eo[3] === _useCapture) {
        Event.observers[i] = null;
        Event.observers.splice(i, 1);
      }
      else {
        i++;
      }
    }
  },
  
  // List of ASCII "special characters":
  KEY_BACKSPACE: 8,
  KEY_TAB: 9,
  KEY_RETURN: 13,
  KEY_ESC: 27,
  KEY_LEFT: 37,
  KEY_UP: 38,
  KEY_RIGHT: 39,
  KEY_DOWN: 40,
  KEY_DELETE: 46,
  KEY_HOME: 36,
  KEY_END: 35,
  KEY_PAGEUP: 33,
  KEY_PAGEDOWN: 34
};

// Activates the garbage collector of Internet Explorer 
// when the document is unloaded:
if (ELEM._is_ie) {
  Event.observe(window, "unload", Event.unloadCache, false);
}

// Default options for focus (all false)
_defaultFocusOptions = {
  mouseMove: false,
  mouseDown: false,
  click: false,
  mouseUp: false,
  draggable: false,
  droppable: false,
  keyDown: false,
  keyUp: false,
  mouseWheel: false,
  textEnter: false
};

/*** = Description
  ** Mid-level event abstraction manager.
  **
  ** This engine serves the HControl classes, so usually the existence of it
  ** is not obvious. The main interface to use it is the methods in HControl.
***/
EVENT = {
  
/** = Description
  * Array that keeps the last known global event status type.
  *
  * The format is an array with an index per "interesting" event value.
  *
  * = Indexes
  * +EVENT.status[ EVENT.button1 ]+::  The state of the left mouse button.
  *                                    0 when not pressed
  *                                    1 when pressed.
  *
  * +EVENT.status[ EVENT.button2 ]+::  The state of the right mouse button.
  *                                    0 when not pressed
  *                                    1 when pressed.
  *
  * +EVENT.status[ EVENT.crsrX ]+::    The x-coordinate of the mouse cursor.
  *
  * +EVENT.status[ EVENT.crsrY ]+::    The y-coordinate of the mouse cursor.
  *
  * +EVENT.status[ EVENT.keysDown ]+:: A list of keycodes of all the keys
  *                                    currently held down in the order of
  *                                    occurrence (first pressed comes first,
  *                                    last pressed last).
  *
  * +EVENT.status[ EVENT.altKeyDown ]+::    The boolean status of the Alt
  *                                         modifier key being held down.
  *
  * +EVENT.status[ EVENT.ctrlKeyDown ]+::   The boolean status of the Ctrl
  *                                         modifier key being held down.
  *
  * +EVENT.status[ EVENT.shiftKeyDown ]+::  The boolean status of the Shift
  *                                         modifier key being held down.
  *
  **/
  status: [false, false, 0, 0, [], false, false, false],
  
/** The index in the status array for the left mouse button.
  **/
  button1: 0,

/** The index in the status array for the right mouse button.
  **/
  button2: 1,

/** The index in the status array for the mouse cursor x coordinate.
  **/
  crsrX: 2,

/** The index in the status array for the mouse cursor y coordinate.
  **/
  crsrY: 3,
  
/** The index in the status array for the list of keycodes of all the
  * keys currently held down in the order of occurrence (first pressed
  * comes first, last pressed last).
  *
  **/
  keysDown: 4,

/** The index in the status orray for the state of the Alt modifier key.
  **/
  altKeyDown: 5,

/** The index in the status orray for the state of the Ctrl modifier key.
  **/
  ctrlKeyDown: 6,

/** The index in the status orray for the state of the Shift modifier key.
  **/
  shiftKeyDown: 7,

/** A flag to disable, if your applications don't need drop events.
  * Setting this to false when not needed improves overall performance,
  * because the drop events need constant calculation of the mouse cursor
  * location against all possible drop targets.
  *
  **/
  enableDroppableChecks: true,
  
/** Initializes the members used by the drop -related events.
  * This method is called from within EVENT and is never called,
  * if enableDroppableChecks is false.
  **/
  startDroppable: function() {
    var _this = EVENT;
    _this.hovered = [];
    // items currently under the mouse cursor
    _this.hoverInterval = 50;
    // 50 means send hover events at most with 50ms intervals
    _this.hoverTimer = new Date().getTime();
    // Time since last hover event triggered
  },
  
/** Starts event listening.
  **/
  start: function() {
    var _globalEventTargetElement = ELEM._is_ie?document:window,
        _this = EVENT,
        _eventMap = [
          ['mousemove', _this.mouseMove],
          ['mouseup', _this.mouseUp],
          ['mousedown', _this.mouseDown],
          ['click', _this.click],
          ['keyup', _this.keyUp],
          ['keydown', _this.keyDown],
          ['keypress', _this.keyPress],
          ['contextmenu', _this.contextMenu],
          ['resize', _this.resize],
          ['mousewheel', _this.mouseWheel]
        ],
        i = 0;
    for (; i !== _eventMap.length; i++) {
      Event.observe(_globalEventTargetElement, _eventMap[i][0], _eventMap[i][1]);
    }
    if (window.addEventListener) {
      window.addEventListener('DOMMouseScroll', EVENT.mouseWheel, false);
      window.addEventListener('resize', EVENT.resize, false);
    }
    //window.onmousewheel=document.onmousewheel=EVENT.mouseWheel;
    _this.listeners = [];
    // keep elemId buffer of all listeners
    _this.focused = [];
    // keep elemId buffer of all focused listeners
    _this.resizeListeners = [];
    // list of resize-event listeners
    _this.coordListeners = [];
    // global mouse movement listeners
    _this.focusOptions = {};
    // keep property lists by elemId
    _this.dragItems = [];
    // elemId of currently dragged items
    if (_this.enableDroppableChecks) {
      _this.startDroppable();
    }

    _this.topmostDroppable = null;
    // the currently hovered element accepting droppable items
    _this.textEnterCtrls = [];
    // ID of controls with textfields
    // position caching benefits performance, see coordCacheFlush
    _this._coordCache = [];
    _this._coordCacheFlag = true;
    _this._lastCoordFlushTimeout = null;

    _this.activeControl = null;
    // control that currently has the focus
    _this._lastKeyDown = null;
    // the most recent keypress
  },
  
/** Flushes the position cache by elemId, if no elemId is specified,
  * everything is flushed.
  **/
  coordCacheFlush: function(_elemId) {
    if (_elemId) {
      EVENT._coordCache[_elemId] = null;
    }
    else {
      EVENT._coordCache = [];
    }
  },
  
/** Registers the _ctrl object by event listener flags in _focusOptions.
  **/
  reg: function(_ctrl, _focusOptions) {
    var _elemId,
        _elem,
        _this = EVENT,
        _propIn;
    // Binds the class to the element (so it can be called on the event)
    _elemId = _ctrl.elemId;
    _elem = ELEM.get(_elemId);
    if (ELEM._is_ie) {
      _elem.setAttribute('ctrl', _ctrl);
    }
    else {
      _elem.ctrl = _ctrl;
    }
    _this.listeners[_elemId] = true;
    _this.focused[_elemId] = false;
    for (_propIn in _defaultFocusOptions) {
      if (_focusOptions[_propIn] === undefined) {
        _focusOptions[_propIn] = _defaultFocusOptions[_propIn];
      }
    }
    _this.focusOptions[_elemId] = _focusOptions;
    var _coordListenIdx = _this.coordListeners.indexOf(_elemId);
    if (_focusOptions.mouseMove) {
      if (_coordListenIdx === -1) {
        _this.coordListeners.push(_elemId);
      }
    }
    else if (_coordListenIdx !== -1) {
      _this.coordListeners.splice(_coordListenIdx, 1);
    }
    //console.log('focusOptions:',_focusOptions);
    //console.log('focusOptions.textEnter: ',_focusOptions.textEnter);
    if (_focusOptions.textEnter) {
      if (_this.textEnterCtrls.indexOf(_ctrl.viewId) === -1) {
        _this.textEnterCtrls.push(_ctrl.viewId);
      }
    }
    if (_focusOptions.resize) {
      if (_this.resizeListeners.indexOf(_ctrl.viewId) === -1) {
        _this.resizeListeners.push(_ctrl.viewId);
      }
    }
    Event.observe(_elem, 'mouseover', _this._mouseOver);
  },
  
/** Unregisters the  _ctrl object event listeners
  **/
  unreg: function(_ctrl) {
    var _this = EVENT,
    _elemId,
    _elem;
    if (_ctrl === this.activeControl) {
      _this.changeActiveControl(null);
    }
    _elemId = _ctrl.elemId;
    _elem = ELEM.get(_elemId);
    this.listeners[_elemId] = false;
    this.focused[_elemId] = false;
    this._coordCache[_elemId] = null;
    var _textEnterIndex = _this.textEnterCtrls.indexOf(_ctrl.viewId);
    if (_textEnterIndex !== -1) {
      _this.textEnterCtrls.splice(_textEnterIndex, 1);
    }
    var _resizeIndex = _this.resizeListeners.indexOf(_ctrl.viewId);
    if (_resizeIndex !== -1) {
      _this.resizeListeners.splice(_resizeIndex, 1);
    }
    if (_elem !== undefined) {
      Event.stopObserving(_elem, 'mouseover', _this._mouseOver);
    }
  },

/** Receiver of the onResize event.
  * Delegates calls to the high-level event receivers of all
  * controls registered for the event.
  **/
  resize: function(e) {
    var i = 0,
        _this = EVENT,
        _ctrlID,
        _ctrl;
    for (; i < _this.resizeListeners.length; i++) {
      _ctrlID = _this.resizeListeners[i];
      _ctrl = HSystem.views[_ctrlID];
      if (_ctrl['onResize']) {
        _ctrl.onResize();
      }
    }
  },

  /* Element-specific mouse over/out event receiver. */
  _mouseOver: function(e) {
    if (!Event.element) {
      return;
    }
    var _that = Event.element(e);
    while (_that && _that.ctrl === undefined) {
      _that = _that.parentNode;
    }
    if (!_that) {
      return;
    }
    var _this = _that.ctrl;
    EVENT.focus(_this);
    Event.stop(e);
  },

  /* Element-specific mouse over/out event receiver. */
  _mouseOut: function(e) {
    if (!Event.element) {
      return;
    }
    var _that = Event.element(e);
    while (_that && _that.ctrl === undefined) {
      _that = _that.parentNode;
    }
    if (!_that) {
      return;
    }
    var _this = _that.ctrl;
    EVENT.blur(_this);
    Event.stop(e);
  },

/** Mid-level focus manager.
  * Gets called on the onMouseOver event.
  * Starts listening for onMouseOut to blur.
  * Delegates focus calls to the high-level event receivers of all
  * enabled controls registered.
  **/
  focus: function(_ctrl) {
    var _this = EVENT,
        _elemId = _ctrl.elemId,
        _elem = ELEM.get(_elemId);
    if (_this.focused[_elemId] === false ){ // && _this.dragItems.indexOf(_elemId) === -1) {
      Event.stopObserving(_elem, 'mouseover', _this._mouseOver);
      Event.observe(_elem, 'mouseout', _this._mouseOut);
      _this.focused[_elemId] = true;
      if (_ctrl['focus']) {
        _ctrl.focus();
      }
    }
  },
  
/** Mid-level blur (focus lost) manager.
  * Gets called on the onMouseOut event.
  * Starts listening for onMouseOver to (re)focus.
  * Delegates blur calls to the high-level event receivers of all
  * enabled controls registered.
  **/
  blur: function(_ctrl) {
    var _this = EVENT,
        _elemId = _ctrl.elemId,
        _elem = ELEM.get(_elemId);
    if (_this.focused[_elemId] === true ){ // && _this.dragItems.indexOf(_elemId) === -1) {
      Event.stopObserving(_elem, 'mouseout', _this._mouseOut);
      Event.observe(_elem, 'mouseover', _this._mouseOver);
      _this.focused[_elemId] = false;
      if (_ctrl['blur']) {
        _ctrl.blur();
      }
    }
  },

/** Mid-level mouse movement manager.
  * Gets called on the onMouseMove event.
  * Delegates the following calls to the high-level event receivers of all
  * enabled controls registered, depending on the events they registered:
  * - drag
  * - mouseMove
  * - endHover
  * - startHover
  *
  **/
  mouseMove: function(e) {
    var _this = EVENT,
        x = Event.pointerX(e),
        y = Event.pointerY(e),
        _currentlyDragging = _this.flushMouseMove(x, y);
    _this.status[_this.crsrX] = x;
    _this.status[_this.crsrY] = y;
    _this._modifiers(e);
    // might work
    if (_currentlyDragging) {
      Event.stop(e);
    }
    // Only prevent default action when we are dragging something.
  },
  
/** Processes dragging calculations, triggered by mouseMove.
  **/
  flushMouseMove: function(x, y) {
    var _this = EVENT,
        _currentlyDragging = false,
        i = 0,
        j,
        _elemId,
        _ctrl;
    
    clearTimeout(_this._lastCoordFlushTimeout);
    
    // send drag event to all drag-interested ctrls
    for (; i !== _this.dragItems.length; i++) {
      _elemId = _this.dragItems[i];
      _this.focusOptions[_elemId].ctrl.drag(x, y);
      _this.coordCacheFlush(_elemId);
      _currentlyDragging = true;
    }

    if (_this.enableDroppableChecks) {
      // Check which items are under the mouse coordinates now.
      if (new Date().getTime() > _this.hoverTimer + _this.hoverInterval) {
        // sends mouseMove pseudo-events to ctrls interested
        for (i = 0; i !== _this.coordListeners.length; i++) {
          _elemId = _this.coordListeners[i];
          _ctrl = _this.focusOptions[_elemId].ctrl;
          _ctrl.mouseMove(x, y);
        }
        if (_this.enableDroppableChecks) {
          _this._updateHoverItems();
        }
        // sends drag&drop pseudo-events
        var _wasTopmostDroppable;
        for (i = 0; i !== _this.dragItems.length; i++) {
          // Find the current droppable while dragging.
          _wasTopmostDroppable = _this.topmostDroppable;
          _this.topmostDroppable = null;
          _elemId = _this.dragItems[i];
          _ctrl = _this.focusOptions[_elemId].ctrl;

          // Check for a drop target from the currently hovered items
          var _hoverIndex,
          _dropCtrl;
          for (j = 0; j !== _this.hovered.length; j++) {
            _hoverIndex = _this.hovered[j];
            if (_hoverIndex !== _elemId && _this.focusOptions[_hoverIndex].ctrl) {
              _dropCtrl = _this.focusOptions[_hoverIndex].ctrl;
              if (!_this.topmostDroppable ||
              // First time
              _dropCtrl.zIndex() > _this.topmostDroppable.zIndex() ||
              // Z beaten
              _dropCtrl.supr === _this.topmostDroppable) {
                // subview
                if (_this.focusOptions[_dropCtrl.elemId].droppable) {
                  _this.topmostDroppable = _dropCtrl;
                  // Finally, the item must accept drop events.
                }
              }
            }
          }

          // Topmost item has changed, send startHover or endHover to the droppable.
          if (_wasTopmostDroppable !== _this.topmostDroppable) {
            if (_wasTopmostDroppable) {
              _wasTopmostDroppable.endHover(_ctrl);
            }
            if (_this.topmostDroppable) {
              _this.topmostDroppable.startHover(_ctrl);
            }
          }
        }
        _this.hoverTimer = new Date().getTime();
      }
      else {
        _this._lastCoordFlushTimeout = setTimeout(
          function(){
            EVENT.flushMouseMove(x,y);
          }, _this.hoverInterval
        );
      }
    }
    return _currentlyDragging;
  },

  // Loops through all registered items and store indices of elements
  // that are currenly under the mouse cursor in .hovered array. Uses
  // cached position and dimensions value when possible.
  _updateHoverItems: function() {
    var _this = EVENT,
        x = _this.status[_this.crsrX],
        y = _this.status[_this.crsrY],
        i = 0,
        _ctrl,
        _elem,
        _pos,
        _size,
        _coords;
    _this.hovered = [];
    for (; i !== _this.listeners.length; i++) {
      if (!_this.listeners[i] || !_this.focusOptions[i].ctrl) {
        continue;
      }
      _ctrl = _this.focusOptions[i].ctrl;
      _elem = ELEM.get(i);
      if (!_this._coordCacheFlag || !_this._coordCache[i]) {
        _pos = ELEM.getVisiblePosition(_ctrl.elemId);
        // [x,y]
        _size = ELEM.getVisibleSize(_ctrl.elemId);
        // [w,h]
        _this._coordCache[i] = [_pos[0], _pos[1], _size[0], _size[1]];
      }
      _coords = _this._coordCache[i];
      
      // Is the mouse pointer inside the element's rectangle?
      if (x >= _coords[0] && x <= _coords[0] + _coords[2] && y >= _coords[1] && y <= _coords[1] + _coords[3]) {
        _this.hovered.push(i);
      }
    }
  },

/** = Description
  * Starts dragging the control given.
  *
  * Call this method to start dragging another component.
  *
  * = Parameters
  * +_ctrl+::   An object that uses the HControl API, becomes new drag target.
  *
  **/
  startDragging: function(_ctrl) {
    var _this = EVENT;
    _this.dragItems = [_ctrl.elemId];
    _this.focus(_ctrl);
    _this.changeActiveControl(_ctrl);
    _ctrl.startDrag(_this.status[_this.crsrX], _this.status[_this.crsrY]);
  },

/** Mid-level mouse button press manager.
  * Gets called on the onMouseDown event.
  * Delegates the following calls to the high-level event receivers of all
  * enabled controls registered, depending on the events they registered:
  * - mouseDown
  * - startDrag
  *
  **/
  mouseDown: function(e, _isLeftButton) {
    var _this = EVENT,
        _didStartDrag = false,
        x = _this.status[_this.crsrX],
        y = _this.status[_this.crsrY],
        i = 0,
        
        // Unset the active control when clicking on anything.
        _newActiveControl = null,
        
        // The startDrag and mouseDown event receivers are first collected into
        // these arrays and the events are sent after the active control status has
        // been changed.
        _startDragElementIds = [],
        _mouseDownElementIds = [];
    
    _this._modifiers(e);
    if (_isLeftButton === undefined) {
      _isLeftButton = Event.isLeftClick(e);
    }
    if (_isLeftButton) {
      _this.status[_this.button1] = true;
    }
    else {
      _this.status[_this.button2] = true;
    }
    
    for (; i !== _this.focused.length; i++) {
      if (_this.focused[i] === true) {
        // Set the active control to the currently focused item.
        if (_this.focusOptions[i].ctrl.enabled) {
          _newActiveControl = _this.focusOptions[i].ctrl;
        }
        if ((_this.focusOptions[i].draggable === true) && _this.dragItems.indexOf(i) === -1) {
          _startDragElementIds.push(i);
        }
        else if (_this.focusOptions[i].mouseDown === true) {
          _mouseDownElementIds.push(i);
        }
      }
    }
    // Handle the active control selection.
    if (_newActiveControl) {
      _this.changeActiveControl(_newActiveControl);
    }
    // Call the mouseDown and startDrag events after the active control change has been handled.
    for (i = 0; i !== _startDragElementIds.length; i++) {
      _this.dragItems.push(_startDragElementIds[i]);
      _this.focusOptions[_startDragElementIds[i]].ctrl.startDrag(x, y);
      _didStartDrag = true;
    }

    var _stopEvent = _mouseDownElementIds.length;
    for (i = 0; i !== _mouseDownElementIds.length; i++) {
      if (_this.focusOptions[_mouseDownElementIds[i]].ctrl.mouseDown(x, y, _isLeftButton)) {
        _stopEvent--;
      }
    }
    if (_didStartDrag) {
      // Remove possible selections.
      document.body.focus();
      // Prevent text selection in MSIE when dragging starts.
      _this._storedOnSelectStart = document.onselectstart;
      document.onselectstart = function() {
        return false;
      };
      Event.stop(e);
    }
    // Stop the event only when we are hovering over some control, allows normal DOM events to co-exist.
    if (this.enableDroppableChecks) {
      if ((_stopEvent === 0) && (_this.hovered.length !== 0) && (_newActiveControl && (_newActiveControl.textElemId === false))) {
        Event.stop(e);
      }
    }
    return true;
  },

/** Mid-level mouse click manager.
  * Gets called on the onClick event.
  * Delegates click calls to the high-level event receivers of all
  * controls registered for that event.
  *
  **/
  click: function(e, _isLeftButton) {
    var _this = EVENT,
        x = _this.status[_this.crsrX],
        y = _this.status[_this.crsrY],
        i = 0,
        // Unset the active control when clicking on anything.
        _newActiveControl = null,
        // The startDrag and mouseDown event receivers are first collected into
        // these arrays and the events are sent after the active control status has
        // been changed.
        _clickElementIds = [];
        _this._modifiers(e);
    if (_isLeftButton === undefined) {
      _isLeftButton = Event.isLeftClick(e);
    }
    if (_isLeftButton) {
      _this.status[_this.button1] = true;
    }
    else {
      _this.status[_this.button2] = true;
    }
    for (; i !== _this.focused.length; i++) {
      if (_this.focused[i] === true) {
        // Set the active control to the currently focused item.
        if (_this.focusOptions[i].ctrl.enabled) {
          _newActiveControl = _this.focusOptions[i].ctrl;
        }
        if (_this.focusOptions[i].click === true) {
          _clickElementIds.push(i);
        }
      }
    }
    // Handle the active control selection.
    if (_newActiveControl) {
      _this.changeActiveControl(_newActiveControl);
    }
    var _stopEvent = _clickElementIds.length;
    for (i = 0; i !== _clickElementIds.length; i++) {
      if (_this.focusOptions[_clickElementIds[i]].ctrl.click(x, y, _isLeftButton)) {
        _stopEvent--;
      }
    }
    // Stop the event only when we are hovering over some control, allows normal DOM events to co-exist.
    if (_this.enableDroppableChecks) {
      if ((_stopEvent === 0) && (_this.hovered.length !== 0) && (_newActiveControl && (_newActiveControl.textElemId === false))) {
        Event.stop(e);
      }
    }
    //if(_this.hovered.length!==0){Event.stop(e);}
    return true;
  },

/** Changes active ctrl.
  * The previous active ctrl gets the _lostActiveStatus pseudo-event,
  * The new active ctrl gets the _gainedActiveStatus pseudo-event
  **/
  changeActiveControl: function(_ctrl) {
    //console.log('EVENT.changeActiveControl: ',_ctrl);
    var _this = EVENT,
        // Store the currently active control so we can inform it, if it no longer is the active control.
        _prevActiveCtrl = _this.activeControl;
    // Did the active control change?
    if (_ctrl !== _prevActiveCtrl) {
      if (_prevActiveCtrl) {
        // Previously active control just lost the active status.
        _prevActiveCtrl.active = false;
        _prevActiveCtrl._lostActiveStatus(_ctrl);
      }
      _this.activeControl = null;
      if (_ctrl) {
        // A new control gained the active status.
        _ctrl.active = true;
        _this.activeControl = _ctrl;
        _ctrl._gainedActiveStatus(_prevActiveCtrl);
      }
    }
  },


/** Mid-level mouse button release manager.
  * Gets called on the onMouseUp event.
  * Delegates the following calls to the high-level event receivers of all
  * enabled controls registered, depending on the events they registered:
  * - mouseUp
  * - endHover
  * - drop
  * - endDrag
  *
  **/
  mouseUp: function(e) {
    var _this = EVENT,
        _didEndDrag = false,
        _isLeftButton = Event.isLeftClick(e),
        x = _this.status[_this.crsrX],
        y = _this.status[_this.crsrY],
        _elemId,
        _ctrl,
        i = 0;
    _this._modifiers(e);
    _this.status[_this.button1] = false;
    _this.status[_this.button2] = false;
    // Send endDrag for the currently dragged items even when they don't have focus, and clear the drag item array.
    for (; i !== _this.dragItems.length; i++) {
      _elemId = _this.dragItems[i];
      _ctrl = _this.focusOptions[_elemId].ctrl;
      _ctrl.endDrag(x, y);
      _didEndDrag = true;
      // If the mouse slipped off the dragged item before the mouse button was released, blur the item manually
      if (_this.enableDroppableChecks) {
        _this._updateHoverItems();
        if (_this.hovered.indexOf(_elemId) === -1) {
          _this.blur(_ctrl);
        }
      }
      // If there is a drop target in the currently hovered items, send drop to it.
      if (_this.topmostDroppable) {
        // Droppable found at the release point.
        _this.topmostDroppable.endHover(_ctrl);
        _this.topmostDroppable.drop(_ctrl);
        _this.topmostDroppable = null;
      }
    }
    _this.dragItems = [];
    // Restore MSIE's ability to select text after dragging has ended.
    if (_didEndDrag) {
      document.onselectstart = _this._storedOnSelectStart;
    }
    // Check for mouseUp listeners.
    for (i = 0; i !== _this.focused.length; i++) {
      if (_this.focused[i] === true) {
        if (_this.focusOptions[i].mouseUp === true) {
          _this.focusOptions[i].ctrl.mouseUp(x, y, _isLeftButton);
        }
      }
    }
    return true;
  },


/** Mid-level key press manager.
  * Gets called on the onKeyDown event.
  * Delegates keyDown calls to the high-level event receivers of all
  * controls registered for that event.
  **/
  keyDown: function(e) {
    var _this = EVENT,
        _theKeyCode = e.keyCode;
    _this._modifiers(e);
    if (_this.activeControl && _this.focusOptions[_this.activeControl.elemId].keyDown === true) {
      Event.stop(e);
      // Workaround for msie rapid fire keydown
      if (_this._lastKeyDown !== _theKeyCode) {
        _this.activeControl.keyDown(_theKeyCode);
      }
    }
    // Insert key to the realtime array, remove in keyUp
    if (_this.status[_this.keysDown].indexOf(_theKeyCode) === -1) {
      _this.status[_this.keysDown].push(_theKeyCode);
    }
    _this._lastKeyDown = _theKeyCode;
  },


/** Mid-level key release manager.
  * Gets called on the onKeyUp event.
  * Delegates keyUp calls to the high-level event receivers of all
  * controls registered for that event.
  * Also delegates the textEnter calls to all controls
  * registered for that event.
  **/
  keyUp: function(e) {
    var _this = EVENT,
        _theKeyCode = e.keyCode,
        _keyCodeIndex,
        i = 0,
        _ctrlId,
        _ctrl;
    _this._modifiers(e);
    _this._lastKeyDown = null;
    if (_this.activeControl && _this.focusOptions[_this.activeControl.elemId].keyUp === true) {
      _this.activeControl.keyUp(_theKeyCode);
    }
    for (; i < _this.textEnterCtrls.length; i++) {
      _ctrlId = _this.textEnterCtrls[i];
      _ctrl = HSystem.views[_ctrlId];
      if (_ctrl.textEnter) {
        _ctrl.textEnter();
      }
    }
    // Remove the key from the realtime array, inserted in keyDown
    _keyCodeIndex = _this.status[_this.keysDown].indexOf(_theKeyCode);
    if (_keyCodeIndex !== -1) {
      _this.status[_this.keysDown].splice(_keyCodeIndex, 1);
    }
  },

  /* Prevents the onKeyPress event (key being hold down; we don't need that event) */
  keyPress: function(e) {
    var _this = EVENT;
    if (_this.activeControl && _this.focusOptions[_this.activeControl.elemId].keyDown === true) {
      Event.stop(e);
    }
  },


/** Mid-level mouse scroll wheel event manager.
  * Delegates mouseWheel calls to the high-level event receivers of all
  * controls registered for that event.
  **/
  mouseWheel: function(e) {
    var _this = EVENT,
        _delta = 0,
        i = 0;
    if (!e) {
      e = window.event;
    }
    if (e.wheelDelta) {
      _delta = 0 - (e.wheelDelta / 120);
    }
    else if (e.detail) {
      _delta = 0 - (e.detail / 3);
    }
    if (BROWSER_TYPE.opera || BROWSER_TYPE.safari) {
      _delta = 0 - _delta;
    }
    for (; i !== _this.focused.length; i++) {
      if (_this.focused[i] === true) {
        if (_this.focusOptions[i].mouseWheel === true) {
          Event.stop(e);
          _this.focusOptions[i].ctrl.mouseWheel(_delta);
        }
      }
    }
  },

  /* Alternative right button detection, wrapper for the mouseDown method */
  contextMenu: function(e) {
    EVENT.mouseDown(e, false);
    Event.stop(e);
  },

  /* Handle the event modifiers. */
  _modifiers: function(e) {
    var _this = EVENT;
    _this.status[_this.altKeyDown] = e.altKey;
    _this.status[_this.ctrlKeyDown] = e.ctrlKey;
    _this.status[_this.shiftKeyDown] = e.shiftKey;
  }

};

/** Starts the only instance
  */
LOAD(
  function() {
    EVENT.start();
  }
);

/*   Riassence Framework
 *   Copyright 2007 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */



/*** = Description
  ** A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
  ** in FIPS 180-1. Also includes a Base64 encoder.
  **
  ** = Original implementation:
  ** Copyright Paul Johnston 2000 - 2009.
  ** Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
  ** Distributed under the BSD License
  ** See http://pajhome.org.uk/crypt/md5 for details.
  **
  ***/
SHAClass = HClass.extend({

/** = Description
  * Constructs an instance of SHAClass.
  *
  * = Parameters
  * +_chrsz+::   The input character size, in bits. Optional.
  *
  **/
  constructor: function(_chrsz){
    _chrsz && this.setChrsz(_chrsz);
  },
  
  /* hex output format. 0 - lowercase; 1 - uppercase        */
  _hexcase: 0,
  
/** = Description
  * Returns the letter case of the hexadecimal output.
  *
  * = Returns
  * 1 if upporcase, 0 if lowercase.
  **/
  hexCase: function(){
    return this._hexcase;
  },
  
/** = Descrition
  * Sets the letter case of the hexadecimal output.
  *
  * = Parameters:
  * +_case+::  1: Upper case
  *            0: Lower case
  *
  **/
  setHexCase: function(_case){
    this._hexcase = _case;
  },
  
  /* base-64 pad character. "=" for strict RFC compliance   */
  _b64pad: "=",
  
/** Returns the Base64 padding character. Is '=' by default.
  **/
  base64Pad: function(){
    return this._b64pad;
  },
  
/** = Description
  * Sets the Base64 padding character.
  *
  * Set to '=' (default) for strict RFC compliance.
  *
  * = Parameters
  * +_pad+::   The padding character
  *
  **/
  setBase64Pad: function(_pad){
    this._b64pad = _pad;
  },
  
  /* bits per input character. 8 - ASCII; 16 - Unicode      */
  _chrsz: 8,
  
/** Returns the number of bits per input character. The default is 8.
  **/
  chrsz: function(){
    return this._chrsz;
  },
  
/** = Description
  * Sets the number of bits per input character.
  *
  * = Parameters
  * +_bits+::  Amount of bits per input character. 8 for ascii, 16 for unicode.
  *
  **/
  setChrsz: function(_bits){
    this._chrsz = _bits;
  },
  
/** = Description
  * Calculates the SHA1 of a string and returns the result encoded in hexadecimal.
  *
  * = Parameters
  * +_s+::  The input string.
  *
  * = Returns
  * A hexadecimal-encoded string containing the SHA1 result.
  *
  **/
  hexSHA1: function(_s){
    var _this=this;
    return _this._binb2hex(
      _this._coreSHA1(
        _this._str2binb(_s),
        _s.length * _this._chrsz
      )
    );
  },
  
/** = Description
  * Calculates the SHA1 of a string and returns the result encoded in Base64.
  *
  * = Parameters
  * +_s+::  The input string.
  *
  * = Returns
  * A Base64-encoded string containing the SHA1 result.
  *
  **/
  b64SHA1: function(_s){
    var _this=this;
    return _this._binb2b64(
      _this._coreSHA1(
        _this._str2binb(_s),
        _s.length * _this._chrsz
      )
    );
  },
  
/** = Description
  * Calculates the SHA1 of a string and returns the result as a raw string.
  *
  * = Parameters
  * +_s+::  The input string.
  *
  * = Returns
  * A raw string containing the SHA1 result.
  *
  **/
  strSHA1: function(_s){
    var _this=this;
    return _this._binb2str(
      _this._coreSHA1(
        _this._str2binb(_s),
        _s.length * _this._chrsz
      )
    );
  },
  
/** = Description
  * Calculates the HMAC-SHA1 of a string and returns the result encoded in hexadecimal.
  *
  * = Parameters
  * +_key+::   The key to use.
  * +_data+::  The input data.
  *
  * = Returns
  * A hexadecimal-encoded string containing the HMAC-SHA1 result.
  *
  **/
  hexHmacSHA1: function(_key, _data){
    var _this=this;
    return _this._binb2hex(
      _this._coreHmacSHA1(_key, _data)
    );
  },
  
/** = Description
  * Calculates the HMAC-SHA1 of a string and returns the result encoded in Base64.
  *
  * = Parameters
  * +_key+::   The key to use.
  * +_data+::  The input data.
  *
  * = Returns
  * A Base64-encoded string containing the HMAC-SHA1 result.
  *
  **/
  b64HmacSHA1: function(_key, _data){
    var _this=this;
    return _this._binb2b64(
      _this._coreHmacSHA1(_key, _data)
    );
  },
  
/** = Description
  * Calculates the HMAC-SHA1 of a string and returns the result as a raw string.
  *
  * = Parameters
  * +_key+::   The key to use.
  * +_data+::  The input data.
  *
  * = Returns
  * A raw string containing the HMAC-SHA1 result.
  *
  **/
  strHmacSHA1: function(_key, _data){
    var _this=this;
    return _this._binb2str(
      _this._coreHmacSHA1(_key, _data)
    );
  },
  
/** = Description
  * Encodes a string to Base64.
  *
  * = Parameters
  * +_str+::    The input data.
  *
  * = Returns
  * The Base64 encoded version of the input data.
  *
  **/
  str2Base64: function(_str){
    var _this=this;
    return _this._binb2b64(_this._str2binb(_str));
  },
  
/** Performs a simple self-test to see if the VM is working
  **/
  test: function(){
    return this.hexSHA1("abc") === "a9993e364706816aba3e25717850c26c9cd0d89d";
  },

  /*
   * Calculate the SHA-1 of an array of big-endian words, and a bit length
   */
  _coreSHA1: function(_x, _len){
    var _this=this;
    /* append padding */
    _x[_len >> 5] |= 0x80 << (24 - _len % 32);
    _x[((_len + 64 >> 9) << 4) + 15] = _len;

    var _w = new Array(80),
        _a =  1732584193,
        _b = -271733879,
        _c = -1732584194,
        _d =  271733878,
        _e = -1009589776,
        i, _olda, _oldb, _oldc, _oldd, _olde,
        j, _t;

    for(i = 0; i < _x.length; i += 16){
      _olda = _a;
      _oldb = _b;
      _oldc = _c;
      _oldd = _d;
      _olde = _e;

      for(j = 0; j < 80; j++){
        if(j < 16){
          _w[j] = _x[i + j];
        }
        else {
          _w[j] = _this._rol(_w[j-3] ^ _w[j-8] ^ _w[j-14] ^ _w[j-16], 1);
        }
        _t = _this._safeAdd(_this._safeAdd(_this._rol(_a, 5), _this._sha1FT(j, _b, _c, _d)),
             _this._safeAdd(_this._safeAdd(_e, _w[j]), _this._sha1KT(j)));
        _e = _d;
        _d = _c;
        _c = _this._rol(_b, 30);
        _b = _a;
        _a = _t;
      }

      _a = _this._safeAdd(_a, _olda);
      _b = _this._safeAdd(_b, _oldb);
      _c = _this._safeAdd(_c, _oldc);
      _d = _this._safeAdd(_d, _oldd);
      _e = _this._safeAdd(_e, _olde);
    }
    return [_a, _b, _c, _d, _e];

  },

  /*
   * Perform the appropriate triplet combination function for the current
   * iteration
   */
  _sha1FT: function(_t, _b, _c, _d) {
    if(_t < 20){
      return (_b & _c) | ((~_b) & _d);
    }
    if(_t < 40){
      return _b ^ _c ^ _d;
    }
    if(_t < 60){
      return (_b & _c) | (_b & _d) | (_c & _d);
    }
    return _b ^ _c ^ _d;
  },

  /*
   * Determine the appropriate additive constant for the current iteration
   */
  _sha1KT: function(_t){
    return (_t < 20) ?  1518500249 : (_t < 40) ?  1859775393 :
           (_t < 60) ? -1894007588 : -899497514;
  },

  /*
   * Calculate the HMAC-SHA1 of a key and some data
   */
  _coreHmacSHA1: function(_key, _data){
    var _this=this,
        _bkey = _this._str2binb(_key),
        _ipad = new Array(16),
        _opad = new Array(16),
        i, _hash;
    if(_bkey.length > 16){
      _bkey = _this._coreSHA1(_bkey, _key.length * _this._chrsz);
    }
    for(i = 0; i  < 16; i++){
      _ipad[i] = _bkey[i] ^ 0x36363636;
      _opad[i] = _bkey[i] ^ 0x5C5C5C5C;
    }
    
    _hash = _this._coreSHA1(_ipad.concat(_this._str2binb(_data)), 512 + _data.length * _this._chrsz);
    return _this._coreSHA1(_opad.concat(_hash), 512 + 160);
  },

  /*
   * Add integers, wrapping at 2^32. This uses 16-bit operations internally
   * to work around bugs in some JS interpreters.
   */
  _safeAdd: function(_x, _y){
    var _lsw = (_x & 0xFFFF) + (_y & 0xFFFF),
        _msw = (_x >> 16) + (_y >> 16) + (_lsw >> 16);
    return (_msw << 16) | (_lsw & 0xFFFF);
  },

  /*
   * Bitwise rotate a 32-bit number to the left.
   */
  _rol: function(_num, _cnt){
    return (_num << _cnt) | (_num >>> (32 - _cnt));
  },
  
  /*
   * Convert an 8-bit or 16-bit string to an array of big-endian words
   * In 8-bit function, characters >255 have their hi-byte silently ignored.
   */
  _str2binb: function(_str){
    var _this=this,
        _bin = [],
        _mask = (1 << _this._chrsz) - 1,
        _strLenChrSZ = _str.length * _this._chrsz,
        i;
    for(i = 0; i < _strLenChrSZ; i += _this._chrsz){
      _bin[i>>5] |= (_str.charCodeAt(i / _this._chrsz) & _mask) << (32 - _this._chrsz - i%32);
    }
    return _bin;
  },

  /*
   * Convert an array of big-endian words to a string
   */
  _binb2str: function(_bin){
    var _this=this,
        _str = "",
        _mask = (1 << _this._chrsz) - 1,
        i,
        _binLen32 = _bin.length * 32,
        _32chrsz = 32 - _this._chrsz;
    for(i = 0; i < _binLen32; i += _this._chrsz){
      _str += String.fromCharCode((_bin[i>>5] >>> (_32chrsz - i%32)) & _mask);
    }
    return _str;
  },

  /*
   * Convert an array of big-endian words to a hex string.
   */
  _binb2hex: function(_binarray){
    var _this=this,
        _hexTab = _this._hexcase ? "0123456789ABCDEF" : "0123456789abcdef",
        _str = "",
        i,
        _binLen = _binarray.length * 4;
    for(i = 0; i < _binLen; i++){
      _str += _hexTab.charAt((_binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
              _hexTab.charAt((_binarray[i>>2] >> ((3 - i%4)*8  )) & 0xF);
    }
    return _str;
  },

  /*
   * Convert an array of big-endian words to a base-64 string
   */
  _binb2b64: function(_binarray){
    var _this=this,
        _tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
        _str = "",
        i,
        _binLen = _binarray.length * 4,
        _t1, _t2, _3,
        _triplet,
        j,
        _binLen32 = _binarray.length * 32;
    for(i = 0; i < _binLen; i += 3){
      _t1 = (((_binarray[i   >> 2] >> 8 * (3 -  i   %4)) & 0xFF) << 16);
      _t2 = (((_binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 );
      _t3 = ((_binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF);
      _triplet = (_t1 | _t2 | _t3);
      for(j = 0; j < 4; j++){
        if(i * 8 + j * 6 > _binLen32){
          _str += _this._b64pad;
        }
        else {
          _str += _tab.charAt((_triplet >> 6*(3-j)) & 0x3F);
        }
      }
    }
    return _str;
  }
});

// Assigns SHA as a SHAClass instance that uses unicode input.
SHA = SHAClass.nu(16);

/*   Riassence Framework
 *   Copyright 2006 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */


/*** = Description
  ** Main container of global operations on +HView+ and
  ** +HApplication+ -derived classes.
  **
  ** HSystem is used to keep +HApplication+ and +HView+ instances
  ** globally managed. The managed classes themself calls +HSystem+ methods,
  ** so there is no real need to access +HSystem+ directly from user-level code.
  **
***/
HSystem = HClass.extend({
  
/** When the focus behaviour is 1, clicking on any subview brings
  * the window to front, if attached to a HWindow instance.
  * If the behaviour is 0, only direct clicks on the HWindow controls
  * brings the window to front.
  *
  **/
  windowFocusBehaviour: 1,
  
/** Singleton class; has no constructor **/
  constructor: null,
  
/** An array of HApplication instances, index is the appId **/
  apps: [],
  
/** An array (in the same order as apps): holds priority values **/
  appPriorities: [],
  
/** An array (in the same order as apps): holds busy status **/
  busyApps: [],
  
/** This array holds free app id:s **/
  freeAppIds: [],
  
/** The default HSystem ticker interval. Unit is milliseconds. **/
  defaultInterval: 10,
  
/** The default HApplication priority. Unit is "On the n:th tick: poll". **/
  defaultPriority: 20,
  
/** The z-index of root-level +HView+ instances. All the array operations 
  * are done by the inner logic of +HApplication+ and +HView+ instances.
  **/
  viewsZOrder: [],
  
/** This is the internal "clock" counter. Gets updated on every tick. **/
  ticks: 0,
  
/** Time in milliseconds for the timeout of a poll to finish before
  * being classified as stuck and thus forcibly terminated.
  **/
  maxAppRunTime: 5000,
  
/** Calls applications, uses the prority as a prioritizer.
  **/
  scheduler: function(){
    
    // Loop through all applications:
    for( var _appId=0; _appId<this.apps.length; _appId++ ){
      // Check, if the application exists:
      if( this.apps[ _appId ] ){
        // Check, if the application is busy:
        if( !this.busyApps[ _appId ] ){
          // Check, if the tick count matches the priority of the app:
          if( (this.ticks % this.appPriorities[ _appId ]) === 0 ){
            // Set the app busy, the app itself should "unbusy" itself, when the idle call is done.
            // That happens in <HApplication._startIdle>
            
            // If the app is not busy, then make a idle call:
            if(HSystem.apps[_appId]){
              HSystem.apps[_appId]._startIdle();
            }
          }
        }
      }
    }
    
    if(this._updateZIndexOfChildrenBuffer.length!==0){
      this._flushUpdateZIndexOfChilden();
    }
    
  },
  
  
/** Calls the scheduler and then calls itself after a timeout to keep
  * the loop going on.
  **/
  ticker: function(){
    // Increment the tick counter:
    this.ticks++;
    this.scheduler();
    this._tickTimeout = setTimeout( function(){HSystem.ticker();},this.defaultInterval);
  },
  
  
/** = Description
  * Adds the structures needed for a new +HApplication+ instance.
  * 
  * Called from inside the +HApplication+ constructor.
  * Binds an app and gives it a unique id.
  *
  * = Parameters
  * +_app+::       The reference to the +HApplication+ instance object.
  * +_priority+::  The app priority.
  *
  * = Returns
  * The app id.
  *
  **/
  addApp: function(_app, _priority){
    var _appId;
    if(this.freeAppIds.length !== 0){
      _appId = this.freeAppIds.unshift();
      this.apps[_appId] = _app;
    } else {
      this.apps.push(_app);
      _appId = this.apps.length-1;
    }
    
    // sets self as parent
    _app.parent  = this;
    _app.parents = [this];
    
    _app.appId = _appId;
    
    this.startApp(_appId, _priority);
    
    return _appId;
  },
  
/** = Description
  * Starts polling an app instance (and its components).
  *
  * = Parameters
  * +_appId+::      The unique id of the app.
  * +_priority+::   The app priority.
  *
  **/
  startApp: function(_appId,_priority){
    if(_priority===undefined){
      _priority = this.defaultInterval;
    }
    this.appPriorities[ _appId ] = _priority;
    this.busyApps[_appId] = false;
  },
  
/** = Description
  * Stops polling an app instance (and its components).
  *
  * = Parameters
  * +_appId+::   The id of the app.
  *
  **/
  stopApp: function(_appId){
    this.busyApps[_appId] = true;
  },
  
/** = Description
  * Changes the priority of the app. Calls +stopApp+ and +startApp+.
  *
  * = Parameters
  * +_appId+::     The id of the app.
  * +_priority+::  The app priority.
  *
  **/
  reniceApp: function(_appId,_priority){
    this.appPriorities[ _appId ] = _priority;
  },
  
/** = Description
  * Stops polling and deletes an app instance (and its components).
  *
  * = Parameters
  * +_appId+::    The unique id of the app.
  * +_forced+::   (Optional) The doesn't wait for the last poll to finish.
  *
  **/
  killApp: function(_appId, _forced){
    if( !_forced ){
      var _startedWaiting = new Date().getTime();
      while( this.busyApps[ _appId ] === true ) {
        /* Waiting for the app to finish its idle loop... */
        if (new Date().getTime() > _startedWaiting + this.maxAppRunTime) {
          break;
        }
      }
    }
    this.busyApps[_appId] = true;
    
    this.apps[ _appId ].destroyAllViews();
    this.apps[ _appId ] = null;
    
    this.freeAppIds.push( _appId );
  },
  
/** All +HView+ instances that are defined **/
  views: [],
  
/** List of free +viwes+ indexes **/
  _freeViewIds: [],
  
/** = Description
  * Adds a view and assigns it an id.
  *
  * = Parameters
  * +_view+::   The +HView+ instance.
  *
  * = Returns
  * The new view id.
  *
  **/
  addView: function(_view){
    var _newId;
    if(this._freeViewIds.length===0){
      _newId = this.views.length;
      this.views.push(_view);
    }
    else {
      _newId = this._freeViewIds.pop();
      this.views[_newId] = _view;
    }
    return _newId;
  },
  
/** = Description
  * Removes a view and recycles its id.
  *
  * = Parameters
  * +_viewId+::  The view id to delete.
  *
  **/
  delView: function(_viewId){
    this.views[_viewId] = null;
    this._freeViewIds.push(_viewId);
  },
  
/** The view id of the active window. 0 means none. **/
  activeWindowId: 0,
  
/** = Description
  * Focuses the window given and blurs the previous one.
  *
  * = Parameters
  * +_view+::   The +HView+ instance, this is almost always a
  *             +HWindow+ instance.
  *
  **/
  windowFocus: function(_view){
    if(!_view){
      this.activeWindowId=0;
      return;
    }
    var _activeWindowId = this.activeWindowId,
        _views = this.views,
        _viewId = _view.viewId;
    if(_views[_activeWindowId]){
      if (_views[_activeWindowId]["windowBlur"]) {
        _views[_activeWindowId].windowBlur();        
      }
    }
    this.activeWindowId=_viewId;
    _view.bringToFront();
    _view.windowFocus();
  },
  
/** optimization of zindex buffer, see +HView+ **/
  _updateZIndexOfChildrenBuffer: [],
  
/** Updates the z-indexes of the children of the given +_viewId+. **/
  updateZIndexOfChildren: function(_viewId) {
    if(this._updateZIndexOfChildrenBuffer.indexOf(_viewId)===-1){
      this._updateZIndexOfChildrenBuffer.push(_viewId);
    }
  },
  
/** Flushes the z-indexes. This is a fairly expensive operation,
  * thas's why the info is buffered.
  **/
  _flushUpdateZIndexOfChilden: function() {
    
    var j=0, // buffer index
        
        // reference to the HSystem namespace
        _this = HSystem,
        
        // reference to the buffer
        _buffer = this._updateZIndexOfChildrenBuffer,
        
        // the length of the buffer
        _bufLen = _buffer.length;
        
    // loop buffer length times to get the items
    for ( ; j < _bufLen; j++ ) {
      
      
      // get and remove view the view id from the z-index flush status buffer:
      var _viewId = _buffer.shift(),
          
          // reference to the view's z-index array or the system root-level views if _viewId is 0
          _views = ((_viewId === null)?(_this.viewsZOrder):(_this.views[ _viewId ].viewsZOrder)),
          
          // the length of the view's z-index array
          _viewLen = _views.length,
          
          // reference to the setStyle method of the element manager
          _setStyl = ELEM.setStyle,
          
          // reference to HSystem.views (collection of all views, by index)
          _sysViews = _this.views,
          
          
      // assign variables for use inside the inner loop:
          
          // the viewId of the view to be updated
          _subViewId,
          
          // the view itself with the viewId above
          _view,
          
          // the elemId property, used as a [] -lookup in the loop
          _elemIdStr = 'elemId',
          
          // the css property name
          _zIdxStr = 'z-index',
          
          // the loop index
          i=0,
          
          // the element id of the view
          _elemId;
      
      // end of var declarations
      
      // loop through all subviews and update the indexes:
      for ( ; i < _viewLen; i++ ) {
        
        // get the viewId to be updated based on the z-index array
        _subViewId = _views[ i ];
        
        // reference to the view itself
        _view = _sysViews[ _subViewId ];
        
        // the element id of the view
        _elemId = _view[ _elemIdStr ];
        
        // do the element manager call itself to update the dom property
        _setStyl( _elemId, _zIdxStr, i );
      }
    }
  }

  
});

// Starts the ticking, when the document is loaded:
LOAD(
  function(){
    HSystem.ticker();
  }
);
/*   Riassence Framework
 *   Copyright 2006 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */


/*** = Description
  **
  ** Simple application template.
  **
  ** Depends on <HSystem>
  **
  ** HApplication instances are good namespaces to bind your client-side logic to.
  ** Feel free to extend HApplication to suit your needs. The primary default
  ** purpose is root-level component (<HView>) management and being the
  ** root controller for <onIdle> events.
  **
  ** = Instance members
  ** +views+::    A list of child component ids bound to it via +HView+ and +HSystem+
  ** +parent+::   Usually +HSystem+.
  ** +parents+::  An array containing parent instances. In this case, just +HSystem+.
  ** +isBusy+::   A flag that is true when the app is doing onIdle events or stopped.
  **
  ** = Usage
  ** Creates the +HApplication+ instance +myApp+, makes a +HWindow+ instance
  ** as its first view.
  **   var myApp = HApplication.nu(10,'Sample Application');
  **   HWindow.nu( [10,10,320,200], myApp, {label:'myWin'} );
  **
***/
HApplication = HClass.extend({
  
  componentBehaviour: ['app'],
  
/** = Description
  *
  * = Parameters
  * All parameters are optional.
  *
  * +_priority+::   The priority, a number between 1 and Infinity. Smaller
  *                 number means higher priority, affects onIdle polling.
  *
  * +_label+::      A label for the application; for process managers.
  *
  **/
  constructor: function(_priority, _label){
    
    // Special null viewId for HApplication instances,
    // they share a system-level root view; the document object
    this.viewId = null;
    
    // storage for views
    this.views = [];
    
    // storage for dom element id's in view, not utilized in HApplication by default
    this.markupElemIds = [];
    
    // Views in Z order. The actual Z data is stored in HSystem, this is just a
    // reference to that array.
    this.viewsZOrder = HSystem.viewsZOrder;
    
    // Finalize initialization via HSystem
    HSystem.addApp(this,_priority);
    
    if(_label){
      this.label = _label;
    }
    else{
      this.label = 'ProcessID='+this.appId;
    }
  },
  
/** = Description
  * Used by addView to build a +self.parents+ array of parent classes.
  *
  * = Parameters
  * +_viewId+::   The target view's ID.
  **/
  buildParents: function(_viewId){
    var _view = HSystem.views[_viewId],
        i = 0;
    _view.parent = this;
    _view.parents = [];
    for(; i < this.parents.length; i++) {
      _view.parents.push(this.parents[i]);
    }
    _view.parents.push(this);
  },
  
/** = Description
  * Adds a view to the app, +HView+ defines an indentical structure for subviews.
  *
  * Called from inside the +HView+ constructor and should be automatic for all 
  * components that accept the +_parent+ parameter, usually the second argument,
  * after the +HRect+ instance.
  *
  * = Parameters
  * +_view+::   Usually +this+ inside +HView+ -derived components.
  *
  * = Returns
  * The view ID.
  *
  **/
  addView: function(_view) {

    var _viewId = HSystem.addView(_view);
    this.views.push(_viewId);
    
    this.buildParents(_viewId);
    this.viewsZOrder.push(_viewId);
    
    return _viewId;
  },
  
/** = Description
  * Removes the view of the given +_viewId+.
  *
  * Call this if you need to remove a child view from its parent without
  * destroying its view, making it in effect a view without parent.
  * Useful, for example, for moving a view from one parent component to
  * another when dragging a component to a droppable container.
  *
  * = Parameters
  * +_viewId+::   The view ID.
  *
  **/
  removeView: function(_viewId){
    HSystem.views[_viewId].remove();
  },

/** = Description
  * Removes and destructs the view of the given +_viewId+.
  *
  * Call this if you need to remove a child view from its parent, destroying
  * its child views recursively and removing all of the DOM elements too.
  *
  * = Parameters
  * +_viewId+::   The view ID.
  *
  **/
  destroyView: function(_viewId){
    HSystem.views[_viewId].die();
  },
  
/** = Description
  * The destructor of the +HApplication+ instance.
  *
  * Stops this application and destroys all its views recursively.
  *
  **/
  die: function(){
    HSystem.killApp(this.appId, false);
  },
  
  
/** = Description
  * Destructs all views but doesn't destroy the +HApplication+ instance.
  *
  * Destroys all the views added to this application but doesn't destroy the
  * application itself.
  *
  **/
  destroyAllViews: function(){
    for(var i = 0; i < this.views.length; i++) {
      HSystem.views[this.views[i]].die();
    }
  },
  
  
  /* Calls the idle method of each view. Don't extend this method. */
  _pollViews: function(){
    var i, _viewId, _view;
    for(i=0;i<this.views.length;i++){
      _viewId = this.views[i];
      _view = HSystem.views[_viewId];
      if((_view!==null)&&(_view['onIdle']!==undefined)){
        _view.onIdle();
      }
    }
  },
  
/** Gets called by +HSystem+. It makes +onIdle+ extensions more failure
  * resistant. Do not extend.
  **/
  _startIdle: function(){
    HSystem.busyApps[ this.appId ] = true;
    this.onIdle();
    this._pollViews();
    HSystem.busyApps[ this.appId ] = false;
  },
  
/** = Description
  * The receiver of the +onIdle+ "poll event". The app priority defines the interval.
  *
  * Extend this method, if you are going to perform regular actions in a app.
  *
  * Intended for "slow infinite loops".
  *
  **/
  onIdle: function(){
    /* Your code here */
  }
});

/*   Riassence Framework
 *   Copyright 2009 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */


/*** = Description
  ** XMLHttpRequest abstractor, and handler and utilities.
  ** The only public method is +request+
  ** The namespace also holds the following services:
  **
  ** Service Name::          Service Description
  ** +COMM.Queue+::          Singleton, used for queuing tasks
  **                         awaiting asynchronous requests to complete.
  ** +COMM.Session+::        Session key secure hashing service.
  ** +COMM.SessionWatcher+:: Polls server at regular intervals by reporting
  **                         the client's local time.
  ** +COMM.Transporter+::    Automated synchronization service, pulls server
  **                         data, tasks etc. The main server interface.
  ** +COMM.Values+::         Data value manager, handles synchronized data in
  **                         both directions (server-client-server).
  ** +COMM.JSLoader+::       Loads Javascript libraries asynchronously.
  ** +COMM.JSONRenderer+::   Constructs nodes from JSON structures as GUI
  **                         tree structures. Lowers the learning curve of GUI
  **                         development, because Javascript knowledge is not
  **                         required to define user interfaces.
  **                         The main purpose is to ease the development of
  **                         user interfaces by defining them as data on the
  **                         server, converting the data to JSON GUI trees and
  **                         letting the client handle the rest. The end result
  **                         is the same as defining the structures in
  **                         JavaScript code.
  **
***/
COMM = {
  
/** Displays an error alert, if the browser doesn't support XMLHttpRequests
  **/
  _FakeHttpRequest: function(){
    alert("'ERROR: This web browser doesn't support XMLHttpRequest. Please upgrade; unable to continue.");
  },
  
/** = Description
  * Finds and calls the state responder method upon a readyState change to 4.
  * - On readyStates other than 4 does nothing.
  * - The default responder for a successful response code (between 200 to 299) is onSuccess.
  * - The default responder for unsuccessful response codes is onFailure.
  * - The responders are called with the request object as the parameter.
  * - Custom response code handling is implemented as 'on' + status code,
  *   eg. 'on404' for a 404 not found error.
  **/
  _stateChange: function(_this){
    if(_this.X.readyState === 4){
      var _status = _this.X.status,
          _responderName = 'on'+_status,
          _success = ((_status >= 200 && _status < 300) || (_status === 0));
      _this[_responderName]?_this[_responderName](_this):_success?_this.onSuccess(_this):_this.onFailure(_this);
    }
  },
  
/** = Description
  * Converts arrays to valid query strings.
  *
  * = Usage
  * Returns 'productId=100&customerName=J-J%20Heinonen'
  *   COMM._arrayToQueryString(['productId',100,'customerName','J-J Heinonen'])
  *
  **/
  _arrayToQueryString: function(_params){
    var i = 0,
        _length = _params.length,
        _queryString = '';
    for(;i<_length;i++){
      _queryString += encodeURIComponent(_params[i]);
      _queryString += (i===_length-1)?'':(i%2===0)?'=':'&';
    }
    return _queryString;
  },
  
/** = Description
  * The main Request-handling object. Provides a general and fairly easy to use
  * interface for making "Ajax" requests.
  *
  * = Parameters
  * +_url+::        Full or relative url of the response handler
  * +_options+::    An +Object+, see below for content:
  *
  *
  * == Required properties for +_options+:
  * +onSuccess+::   A function that is called on a successful response.
  *                 Must accept one parameter: the request object.
  * +onFailure+::   A function that is called on an unsuccessful response.
  *                 Must accept one parameter: the request object.
  *
  * == Optional properties for +_options+:
  * +method+::      The HTTP Request Method, usually 'POST' or 'GET', but will handle
  *                 DAV and other extensions if the server supports them.
  *                 Defaults to 'POST'.
  * +async+::       Boolean; Uses asyncronous requests when true.
  *                 Defaults to true.
  * +params+::      Extra parameters to send, format: Array, see COMM._arrayToQueryString()
  * +headers+::     Extra HTTP headers to send for POST requests, format: Hash.
  * +body+::        The HTTP POST Body
  * +username+::    Username for basic authentication
  * +password+::    Password for basic authentication
  * +contentType+:: The 'content-type' -header to send.
  *                 Defaults to 'application/x-www-form-urlencoded'.
  * +charset+::     The charset type to use. Defaults to 'UTF-8'.
  *
  * = Returns
  * An +Object+ extended from the +_options+ given in the input.
  *
  **/
  request: function(_url,_options){
    var _comm = COMM,
      
        _this = _options?_options:{},
      
        _method = _options.method?_options.method.toUpperCase():'GET',
        _async = (_options.async===undefined)?true:_options.async,
        _params = _options.params?_options.params:[],
        _headers = _options.headers?_options.headers:{},
        _contentType = _options.contentType?_options.contentType:'application/x-www-form-urlencoded',
        _charset = _options.charset?_options.charset:'UTF-8',
        _username = _options.username?_options.username:null,
        _password = _options.username?_options.password:null;
    if(!_options.onFailure){
      _this.onFailure = function(resp){console.log('No failure handler specified, response: ',resp);};
    }
    if(!_options.onSuccess){
      _this.onSuccess = function(resp){console.log('No success handler specified, response: ',resp);};
    }
    _this.url = _url;
    _this.options = _options;
    _this.X   = _comm._XMLHttpRequest();
    if(_method === 'GET' && _params.length !== 0){
      _url += ((_url.indexOf('?')!==-1)?'&':'?')+_comm._arrayToQueryString(_params);
    }
    if(!_async){
      console.log("WARNING: Synchronous "+_method+" request to "+_url+", these will fail on the Symbian web browser.");
    }
    _this.X.open(
      _method,
      _url,
      _async,
      _username,
      _password
    );
    _this.X.onreadystatechange = function(){
      _comm._stateChange(_this);
    };
    if(_method === 'POST'){
      _headers['Content-Type'] = _contentType + '; charset=' + _charset;
      var _body = _options.body?_options.body:'';
      for(var _header in _headers){
        _this.X.setRequestHeader(_header,_headers[_header]);
      }
      _this.X.send(_body);
    }
    else if(_method === 'GET'){
      _this.X.send(null);
    }
    if(!_async){
      _comm._stateChange(_this);
    }
    return _this;
  }
};

/** = Description
  * Creates a new instance of the XMLHttpRequest
  * object supported by the browser. Evaluated only once,
  * after that does its things without any extra statements
  *
  **/
if(window['XMLHttpRequest']!==undefined){
  COMM._XMLHttpRequest = function(){
    return new XMLHttpRequest();
  };
}
else if(BROWSER_TYPE.ie){
  COMM._XMLHttpRequest = function(){
    return new ActiveXObject("Msxml2.XMLHTTP");
  };
}
else {
  COMM._XMLHttpRequest = function(){
    console.log("No XMLHttpRequest object types known. Can't Communicate.");
    return new COMM._FakeHttpRequst();
  };
}

/*   Riassence Framework
 *   Copyright 2009 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** COMM.Queue executes javascript blocks in a managed queue.
  **
  ** COMM.Queue is used by COMM.Transporter and JSLoader to continue
  ** javascript command execution after a XMLHttpRequest finishes.
  **
  ** COMM.Queue runs as a single instance, dan't try to reconstruct it.
***/
COMM.Queue = HApplication.extend({
  
/** The constructor takes no arguments and starts queue flushing automatically.
  **/
  constructor: function(){
    
    // The queue itself, is packed with anonymous functions
    this.commandQueue = [];
    
    // Flag to signal the pause and resume status.
    this.paused = false;
    
    // Run with priority 10; not too demanding but not too sluggish either
    this.base(10);
  },
  
/** Checks periodically, if the queue needs flushing.
  **/
  onIdle: function(){
    // Runs the flush operation, if the queue is not
    // empty and the state is not resumed:
    !this.paused && this.commandQueue.length !== 0 && this.flush();
  },
  
/** = Description
  * Pauses the queue.
  *
  * Use to stop execution, if some data or code needs to be loaded that the
  * rest of the queue depends on.
  * Typically called before an +XMLHttpRequest+ with the +onSuccess+
  * event defined to call +resume+ when the request is done.
  *
  **/
  pause: function(){
    this.paused = true;
  },
  
/** = Description
  * Resumes queue flushing.
  *
  * Use to resume execuption, when some depending code for the rest
  * of the queue has been loaded.
  * Typically on an +XMLHttpRequest+ +onSuccess+ event.
  *
  **/
  resume: function(){
    this.paused = false;
    this.flush();
  },
  
/** A Group of localizable strings; errors and warnings.
  **/
  STRINGS: {
    ERR: 'COMM.Queue Error: ',
    JS_EXEC_FAIL: 'Failed to execute the Javascript function: ',
    REASON: ' Reason:'
  },
  
/** = Description
  * Flushes the queue until stopped.
  *
  * Iterates through the +commandQueue+ and calls each function.
  * Removes items from the queue after execution.
  *
  **/
  flush: function(){
    var i = 0, // current index in the for-loop.
        _item, // the current item to execute
        _function, // the function to run
        _arguments, // the arguments of the function
        _len = this.commandQueue.length; // the length of the queue
    
    // Iterates through the items.
    for(;i<_len;i++){
      
      // Checks that the queue hasn't been paused.
      if(this.paused){
        break; // stops flushing, if paused.
      }
      
      // The first item in the queue is removed from the queue.
      _item = this.commandQueue.shift();
      
      // Execute the item, with arugments if the item
      try{
        if(typeof _item === 'function'){
          _item.call();
        }
        else {
          _function = _item[0];
          _arguments = _item[1];
          _function.call(_arg);
        }
      }
      
      // Displays an error message in the Javascript console, if failure.
      catch(e){
        var _strs = this.STRINGS;
        console.log([
          _strs.ERR_PREFIX,
          _strs.JS_EXEC_FAIL,
          _item,
          _strs.REASON,
          e.description
        ].join(''));
      }
    }
  },
  
/** = Description
  * Adds an item to the beginning of the queue.
  *
  * Use to make the given +_function+ with its 
  * optional +_arguments+ the next item to flush.
  *
  * = Parameters:
  * +_function+::  An anonymous function. Contains the code to execute.
  *
  * +_arguments+:: _Optional_ arguments to pass on to the +_function+
  **/
  unshift: function(_function,_arguments){
    if(_arguments!==undefined){
      this.commandQueue.unshift([_function,_arguments]);
    }
    else {
      this.commandQueue.unshift(_function);
    }
  },
  
/** = Description
  * Adds an item to the end of the queue.
  *
  * Use to make the given +_function+ with its 
  * optional +_arguments+ the last item to flush.
  *
  * = Parameters:
  * +_function+::  An anonymous function. Contains the code to execute.
  *
  * +_arguments+:: _Optional_ arguments to pass on to the +_function+
  **/
  push: function(_function,_arguments){
    if(_arguments!==undefined){
      this.commandQueue.push([_function,_arguments]);
    }
    else {
      this.commandQueue.push(_function);
    }
  },
  
/** Like +unshift+, but uses the code block as a string to be
  * passed on as an evaluated anonymous function.
  **/
  unshiftEval: function(_evalStr,_arguments){
    var _function;
    eval('_function = function(){'+_evalStr+'}');
    this.unshift(_function);
  },
  
/** Like +push+, but uses the code block as a string to be
  * passed on as an evaluated anonymous function.
  **/
  pushEval: function(_evalStr){
    var _function;
    eval('_function = function(){'+_evalStr+'}');
    this.push(_function);
  }
}).nu();
/*   Riassence Framework
 *   Copyright 2009 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** COMM.Session is the session key manager.
  **
  ** COMM.Session is used by COMM.Transporter to generate key hashes for
  ** the session management of COMM.Transporter's keys.
  **
  ** The server expects this exact algorithm and refuses to serve unless
  ** the SHA1 hash sum of the keys matches.
  **
  ** Uses a +SHAClass+ instance for generation.
  **
  ** +COMM.Queue+ runs as a single instance, dan't try to reconstruct it.
***/
COMM.Session = HClass.extend({
  
/** The constructor takes no arguments.
  **/
  constructor: function(){
    var _this = this;
    _this.sha = SHAClass.nu(8);
    _this.sha_key = _this.sha.hexSHA1(((new Date().getTime())*Math.random()*1000).toString());
    _this.ses_key = '0:.o.:'+_this.sha_key;
    _this.req_num = 0;
  },
  
/** = Description
  * Generates a new SHA1 sum using a combination of
  * the previous sum and the +_newKey+ given.
  *
  * Sets +self.ses_key+ and +self.sha_key+
  *
  * = Parameters:
  * +_newKey+:: A key set by the server.
  *
  **/
  newKey: function(_sesKey){
    var _this = this,
        _shaKey = _this.sha.hexSHA1(_sesKey+_this.sha_key);
    _this.req_num++;
    _this.ses_key = _this.req_num+':.o.:'+_shaKey;
    _this.sha_key = _shaKey;
  }
}).nu();
/*   Riassence Framework
 *   Copyright 2009 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** Implements the client-server interface.
  **
  ** COMM.Transporter manages the client side of the server-client-server
  ** data synchronization and the server-client command channel.
  **
  ** It uses COMM.Session for session key handling, COMM.Queue for command
  ** queuing and COMM.Values for data value management.
  **
  ** COMM.Transporter operates in a fully automatic mode and starts when
  ** the document has been loaded.
  **
  ** Don't call any of its methods from your code.
***/
COMM.Transporter = HApplication.extend({
  
/** Sets up the default settings upon construction.
  **/
  constructor: function(){
    var _this = this;
    this.serverLostMessage = 'Server Connection Lost: Reconnecting...';
    _this.label = 'Transporter';
    _this.url = false;
    _this.busy = false;
    _this.stop = true;
    _this._serverInterruptView = false;
    _this._clientEvalError = false;
    _this._busyFlushTimeout = false;
    _this.base(1);
  },
  
/** Tries to (re)connect to the server as often as possible,
  * mandated essentially by the priority of its
  * HApplication instance.
  **/
  onIdle: function(){
    this.sync();
  },
  
/** (Re)sets the priority of itself, effects how
  * frequently +onIdle+ is called.
  * Usually set by the server.
  **/
  poll: function(_pri){
    HSystem.reniceApp(this.appId,_pri);
  },
  
/** Returns the last transaction error of itself. Used by +sync+
  * to report js errors to the server.
  * If no error, returns an empty string.
  **/
  getClientEvalError: function(){
    var _this = COMM.Transporter;
    return _this._clientEvalError?'&err_msg=' +
           COMM.Values._encodeString(_this._clientEvalError):'';
  },
  
/** = Description
  * Handles synchronization responses.
  *
  * Upon a successful request, this method is called by
  * the onSuccess event of the XMLHttpRequest.
  *
  * It splits up the response string and passes the response
  * messages to COMM.Queue for execution.
  *
  * Parameters:
  * +resp+:: The response object.
  *
  **/
  success: function(resp){
    var _this = COMM.Transporter;
    if(!resp.X.responseText){
      _this.failure(resp);
      return;
    }
    var _responseArray = eval(resp.X.responseText),
        i = 1,
        _responseArrayLen = _responseArray.length,
        _sesKey = _responseArray[0],
        _session = COMM.Session,
        _queue = COMM.Queue;
    if(_sesKey === ''){
      console.log('Invalid session, error message should follow...');
    }
    else {
      _session.newKey(_sesKey);
    }
    for(;i<_responseArrayLen;i++){
      try {
        _queue.pushEval( _responseArray[i] );
      }
      catch(e) {
        console.log( 'clientError:'+e+" - "+e.description+' - '+_responseArray[i]);
        _this._clientEvalError = e+" - "+e.description+' - '+_responseArray[i];
      }
    }
    if(_this._serverInterruptView){
      _this._serverInterruptView.die();
      _this._serverInterruptView = false;
    }
    _queue.push( function(){COMM.Transporter.flushBusy();} );
    _queue.flush();
  },
  
/** Sets the +busy+ flag to false and resynchronizes immediately,
  * if COMM.Values contain any unsynchronized values.
  **/
  flushBusy: function(){
    var _this = COMM.Transporter;
    _this.busy = false;
    COMM.Values.tosync.length !== 0 && _this.sync();
  },
  failMessage: function(_title,_message){
    var _this = COMM.Transporter,
        _queue = COMM.Queue;
    console.log('failMessage?');
    _this.stop = true;
    _queue.push(function(){jsLoader.load('default_theme');});
    _queue.push(function(){jsLoader.load('controls');});
    _queue.push(function(){jsLoader.load('servermessage');});
    _queue.push(function(){ReloadApp.nu(_title,_message);});
  },
  
/** Called by the XMLHttpRequest, when there was a failure in communication.
  **/
  failure: function(_resp){
    var _this = COMM.Transporter;
    // server didn't respond, likely network issue.. retry.
    if(_resp.X.status===0){
      console.log(_this.serverLostMessage);
      if(!_this._serverInterruptView){
        _this._serverInterruptView = HView.extend({
          _setFailedResp: function(_resp){
            if(_resp!==undefined){
              this._failedResp = _resp;
            }
            this._errorIndex++;
            return this;
          },
          _retry: function(){
            this._retryIndex++;
            var _resp = this._failedResp;
            COMM.request(
              _resp.url,
              _resp.options
            );
          },
          onIdle: function(){
            var _currentDate = new Date().getTime();
            this.bringToFront();
            if( this._errorIndex > 0 &&
                (this._retryIndex !== this._errorIndex) &&
                (this._lastError + 2000 < _currentDate) &&
                this._failedResp ){
              this._lastError = _currentDate;
              this._retry();
            }
            this.base();
          },
          _errorIndex: 0,
          _retryIndex: 0,
          _lastError: new Date().getTime(),
          die: function(){
            var _app = this.app;
            HSystem.reniceApp(_app.appId,this._origPriority);
            this.base();
            _app.sync();
          },
          drawSubviews: function(){
            var _style = [
              ['padding-left', '8px'],
              ['background-color', '#600'],
              ['text-align','center'],
              ['color', '#fff'],
              ['font-size', '16px'],
              ['opacity', 0.85]
            ], i = 0;
            for( ; i<_style.length; i++ ){
              this.setStyle( _style[i][0], _style[i][1] );
            }
            this.setHTML(this.app.serverLostMessage);
            this._origPriority = HSystem.appPriorities[this.appId];
            if(HSystem.appPriorities[this.appId]<10){
              HSystem.reniceApp(this.appId,10);
            }
            this._anim = HView.extend({
              _animIndex: 0,
              _anim: function(){
                var _targetRect,
                    _width = ELEM.getSize(this.parent.elemId)[0];
                this._animIndex++;
                if(this._animIndex%2===0){
                  _targetRect = HRect.nu(0,0,80,20);
                }
                else {
                  _targetRect = HRect.nu(_width-80,0,_width,20);
                }
                this.animateTo(_targetRect,2000);
              },
              onAnimationEnd: function(){
                if(this.drawn){
                  this._anim();
                }
              }
            }
          ).nu( [0,0,80,20], this ).setStyle('background-color','#fff').setStyle('opacity',0.8)._anim();
          }
        }).nu([0,0,200,20,0,null],_this)._setFailedResp(_resp);
      }
      else {
        _this._serverInterruptView._setFailedResp();
      }
    }
    else {
      _this.failMessage('Transporter Error','Transporter was unable to complete the synchronization request.');
    }
  },
  
/** Starts requests.
  **/
  sync: function(){
    if(this.stop){
      // console.log('sync stop');
      return;
    }
    if(this.busy){
      // console.log('sync busy');
      return;
    }
    // console.log('sync.');
    this.busy = true;
    var _this = this,
        _values = COMM.Values.sync(),
        _sesKey = 'ses_key='+COMM.Session.ses_key,
        _errorMessage = _this.getClientEvalError(),
        _body = [_sesKey,_errorMessage,_values?'&values='+_values:''].join('');
    COMM.request(
      _this.url, {
        _this: _this,
        onSuccess: COMM.Transporter.success,
        onFailure: COMM.Transporter.failure,
        method: 'POST',
        async: true,
        body: _body
      }
    );
  }
}).nu();
/*   Riassence Framework
 *   Copyright 2009 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */


/*** = Description
  ** The single instance of this class is constructed after the first
  ** handshake request with the server by the 'main' plugin.
  **
  ** It has dual functionality:
  ** - It tells the the client time.
  **   It's available as the server HValue instance
  **   +msg.session[:main][:client_time]+ from
  **   any Plugin instance.
  ** - It polls the server on regular intervals.
  **   The polling interval is defined by the server
  **   as the _timeoutSecs constructor parameter.
  **
***/
COMM.SessionWatcher = HApplication.extend({
  constructor: function( _timeoutSecs, _sesTimeoutValueId ){
    
    // onIdle is called when HSystem's ticker count % 100 == 0
    // this means it's 5 seconds with HSystemTickerInterval 50
    this.base(10, 'SesWatcher'); 
    
    // gets the HValue represented by
    // sesTimeoutValueId (:client_time in server)
    this.sesTimeoutValue = HVM.values[_sesTimeoutValueId];
    this.timeoutSecs = _timeoutSecs;
  },
  
  // Tells the server the client's current time
  onIdle: function(){
    if((new Date().getTime() - this.sesTimeoutValue.value) > this.timeoutSecs ){
      this.sesTimeoutValue.set( new Date().getTime() );
    }
  }
});
/*   Riassence Framework
 *   Copyright 2009 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** This application registers url responders to hide/show
  ** certain views automatically whenever the anchor
  ** part of the url is changed.
  **
  ** It is bound to the server HValue instance
  ** +msg.session[:main][:location_href]+ by
  ** the 'main' plugin. By default it runs with 
  ** a client-side-only HValue instance until then.
  **
***/
COMM.URLResponder = HApplication.extend({
  constructor: function(){
    this.urlMatchers = [];
    this.urlCallBack = [];
    this.defaultCallBack = null;
    this.prevCallBack = false;
    this.prevMatchStr = '';
    this.base(1, 'URLResponder');
    this.value = 0;
    this.clientValue = HValue.nu( false, '' );
    this.clientValue.bind( this );
    this.serverValue = false;
  },
  
  // sets the view to show when there is
  // no matches (like a virtual 404)
  setDefaultResponder: function(_callBack){
    this.defaultCallBack = _callBack;
  },
  
  // Removes responder
  // - matchStr is an url that the callBack will
  //   respond to
  // - callBack is the component registered
  delResponder: function(_matchStr,_callBack){
    _callBack.hide();
    if(_callBack === this.prevCallBack){
      this.prevCallBack = false;
      this.prevMatchStr = '';
    }
    var i=0, _urlMatch, _urlCallBack;
    for(;i<this.urlMatchers.length;i++){
      _urlMatch = this.urlMatchers[i].test(_matchStr);
      if(_urlMatch){
        this.urlMatchers.splice(i,1);
        this.urlCallBack.splice(i,1);
        return 1;
      }
    }
    return 0;
  },
  
  // Adds responder
  // - matchRegExp is the regular expression
  //   that matches the anchor part of the uri
  // - callBack is the component that will receive hide/show calls
  // - activate is a flag that tells the view to be immediately 
  //   activate (and the previous one to deactivate)
  addResponder: function(_matchRegExp,_callBack,_activate){
    this.urlMatchers.push(new RegExp(_matchRegExp));
    this.urlCallBack.push(_callBack);
    this.checkMatch(this.value);
    if(_activate!==undefined){
      location.href=_activate;
    }
  },
  
  // Checks the matchStr agains regular expressions
  checkMatch: function(_matchStr){
    if(_matchStr === this.prevMatchStr){
      return 0;
    }
    var i=0, _urlMatch, _urlCallBack;
    for(;i<this.urlMatchers.length;i++){
      _urlMatch = this.urlMatchers[i].test(_matchStr);
      if(_urlMatch){
        _urlCallBack = this.urlCallBack[i];
        if(this.prevCallBack){
          this.prevCallBack.hide();
        }
        _urlCallBack.show();
        this.prevCallBack = _urlCallBack;
        this.prevmatchStr = _matchStr;
        return 1;
      }
    }
    if(this.defaultCallBack){
      if(this.prevCallBack){
        this.prevCallBack.hide();
      }
      this.defaultCallBack.show();
      this.prevCallBack = this.defaultCallBack;
    }
    return -1;
  },
  
  refresh: function(){
    var _value = this.value;
    if(_value.length === 0){ return; }
    if (!this.serverValue && this.valueObj.id !== this.clientValue.id) {
      this.clientValue.die();
    }
    if(location.href !== _value){
      location.href = _value;
    }
    this.checkMatch( _value );
  },
  
  onIdle: function(){
    if(!this['valueObj']){return;}
    var _href = location.href;
    if(_href!==this.valueObj.value){
      this.setValue(_href);
    }
  }
});
/*   Riassence Framework
 *   Copyright 2009 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

// Starts the synchronization upon page load.
LOAD(
  function(){
    COMM.URLResponder.implement(HValueResponder);
    COMM.urlResponder=COMM.URLResponder.nu();
    urlResponder=COMM.urlResponder; // backwards compatibility
    COMM.Transporter.url=HCLIENT_HELLO;
    COMM.Transporter.stop=false;
    COMM.Transporter.sync();
  }
);
/*   Riassence Framework
 *   Copyright 2009 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** Manages data value synchronization.
  **
  ** Keeps track of all +HValue+ instances present.
***/
COMM.Values = HClass.extend({
  
/** No constructor, singleton class.
  **/
  constructor: null,
  
/** An +Object+ containing all values by key.
  **/
  values: {},
  
/** A list of value keys whose value has changed. To be synchronized asap.
  **/
  tosync: [],
  
/** = Description
  * Creates a new +HValue+ instance. Its main purpose is to act as the main
  * client-side value creation interface for the server representation of
  * +HValue+.
  *
  * = Parameters
  * +_id+::     The unique id of the +HValue+ instance (set by the server)
  * +_data::    The initial data of the +HValue+ instance (set by the server)
  *
  **/
  create: function(_id,_data){
    HValue.nu(_id,_data);
  },
  
/** = Description
  * Binds a +HValue+ instance created externally to +self.values+.
  * Called from +HValue+ upon construction.
  *
  * = Parameters
  * +_id+::     The unique id of the +HValue+ instance (set by the server)
  * +_value+::  The +HValue+ instance itself.
  *
  **/
  add: function(_id,_value){
    this.values[_id] = _value;
  },
  
/** = Description
  * Sets the data of the +HValue+ instance by +_Id+.
  *
  * = Parameters
  * +_id+::     The unique id of the +HValue+ instance (set by the server)
  * +_data+::   The new data, any Object type supported by JSON.
  *
  **/
  set: function(_id,_data){
    this.values[_id].set(_data);
  },
  
/** = Description
  * Sets and decodes the +_data+. Main value setter interface 
  * for the server representation of +HValue+.
  *
  * = Parameters
  * +_id+::     The unique id of the +HValue+ instance (set by the server)
  * +_data+::   The new data from the server, to be decoded.
  *
  **/
  s: function(_id,_data){
    var _this = this;
    _data = _this.decode(_data);
    _this.values[_id].s(_data);
  },
  
/** = Description
  * Deletes a +HValue+ instance by +_id+.
  *
  * = Parameters
  * +_id+::     The unique id of the +HValue+ instance (set by the server)
  *
  **/
  del: function(_id){
    var _this = this,
        _values = _this.values,
        _value = _values[_id],
        _views = _value.views,
        _viewsLen = _views.lengt,
        i = 0,
        _view;
    for(;i<_viewsLen;i++){
      _view = _views[i];
      _view.valueObj = HDummyValue.nu(0,_value.value);
    }
    _value.views = [];
    delete _values[_id];
  },
  
/** = Description
  * Marks the +HValue+ instance as changed and tries to send it
  * immediately, unless COMM.Transporter has an ongoing transfer.
  * Usually called by the +HValue+ instance internally.
  *
  * = Parameters
  * +_value+::     The +HValue+ instanche that changed.
  *
  **/
  changed: function(_value){
    var _this = this;
    if(_this.tosync.indexOf(_value.id)===-1){
      _this.tosync.push(_value.id);
      var _transporter = COMM.Transporter;
      if(!_transporter.busy){
        _transporter.sync();
      }
    }
  },
  
  // List of primitive object types.
  _builtins: [
    'b', // boolean
    'n', // number
    's'  // string
  ],
  
/** = Description
  * Use this method to detect the type of the object given.
  *
  * Returns the type of the object given as a character code. Returns false,
  * if unknown or unsupported objcet type.
  *
  * = Returns
  * _One of the following:_
  * - 'a': Array
  * - 'h': Hash (Generic Object)
  * - 'd': Date
  * - 'b': Boolean (true/false)
  * - 'n': Number (integers and floats)
  * - 's': String
  *
  **/
  type: function(_obj){
    if(_obj === null || _obj === undefined){
      console.log('null or undefined obj:',_obj);
      return '-';
    }
    var _type = (typeof _obj).slice(0,1);
    if(this._builtins.indexOf(_type)!==-1){
      return _type;
    }
    else if(_type==='o'){
      if(_obj.constructor === Array){
        return 'a'; // array
      }
      else if(_obj.constructor === Object){
        return 'h'; // hash
      }
      else if(_obj.constructor === Date){
        return 'd'; // date
      }
      return false;
    }
    return false;
  },
  
  // Returns an encoded version of the array _arr as a string
  _encodeArr: function(_arr){
    var _str = '[',
        _output = [],
        _len = _arr.length,
        _this = this,
        _item,
        i = 0;
    for(;i<_len;i++){
      _item = _this.encode(_arr[i]);
      _output.push( _item );
    }
    _str += _output.join(',')+']';
    return _str;
  },
  
  // Returns a decoded array with decoded content of array _arr
  _decodeArr: function(_arr){
    var _output = [],
        _len = _arr.length,
        _this = this,
        _item,
        i = 0;
    for(;i<_len;i++){
      _item = _this.decode(_arr[i]);
      _output.push( _item );
    }
    return _output;
  },
  
  // Returns an encoded version of the Hash (Object) _hash as a string
  _encodeHash: function(_hash){
    var _str = '{',
        _output = [],
        _this = this,
        _key,
        _value;
    for(_key in _hash){
      _value = _hash[_key];
      _output.push( _this.encode( _key ) + ':' + _this.encode( _value ) );
    }
    _str += _output.join(',')+'}';
    return _str;
  },
  
  // Returns a decoded version of the Hash (Object) _hash
  _decodeHash: function(_hash){
    var _output = {},
        _this = this,
        _key,
        _value;
    for(_key in _hash){
      _value = _hash[_key];
      _output[_this.decode(_key)] = _this.decode(_value);
    }
    return _output;
  },
  
  // Regular expression match/replace pairs for string escaping.
  _stringSubstitutions: [
    [(/\\/g), '\\\\'],
    //[(/\b/g), '\\b'],
    [(/\t/g), '\\t'],
    [(/\n/g), '\\n'],
    [(/\f/g), '\\f'],
    [(/\r/g), '\\r'],
    [(/"/g),  '\\"']
  ],
  
  // Quotes a string (escapes quotation marks and places quotes around)
  _quoteString: function(_str){
    var _this = this,
        _substitutions = _this._stringSubstitutions,
        i = 0, _len = _substitutions.length,
        _line, _from, _to, _output = '';
    for(;i<_len;i++){
      _line = _substitutions[i];
      _from = _line[0];
      _to = _line[1];
      _str = _str.replace( _from, _to );
    }
    return '"' + _str + '"';
  },
  
  // Encodes the native character set to url-encoded unicode.
  // Likely causes issues with non-ascii strings, shouldn't be called (for now).
  _encodeString: function(_str){
    console.log( 'WARNING: encodeString called with string: ',_str );
    var _outStr;
    try {
      _outStr = unescape( encodeURIComponent( _str ) );
    }
    catch(e) {
      _outStr = _str;
    }
    return _outStr;
  },
  
  // Decodes the url-encoded unicode to the native character set
  _decodeString: function(_str){
    var _outStr;
    try {
      _outStr = decodeURIComponent( escape( _str ) );
    }
    catch(e) {
      _outStr = _str;
    }
    return _outStr;
  },
  
/** = Description
  * Encodes an object to a ascii string (special characters url-encoded).
  *
  * = Parameters
  * +_obj+::  Any object
  *
  * = Returns
  * A +String+ representation of the +_obj+
  *
  **/
  encode: function(_obj){
    var _str, _type, _this = this;
    if(_obj === null){
      return 'null';
    }
    else if(_obj !== undefined){
      _type = _this.type(_obj);
      if(!_type){
        return 'null';
      }
      switch(_type){
        case 'b': _str = String(_obj); break;
        case 'n': _str = String(_obj); break;
        case 's': _str = _this._quoteString(_obj); break;
        // Might need further investigation, but _encodeString is disabled for now:
        // case 's': _str = _this._quoteString(_this._encodeString(_obj)); break;
        case 'd': _str = '"@'+String(_obj.getTime()/1000)+'"'; break;
        case 'a': _str = _this._encodeArr(_obj); break;
        case 'h': _str = _this._encodeHash(_obj); break;
        default:  _str = 'null'; break;
      }
    }
    else {
      return 'null';
    }
    return _str;
  },
  
/** = Description
  * Decodes a JSON object. Decodes url-encoded strings contained.
  *
  * = Parameters
  * +_ibj+::  A raw object with special characters url-encoded.
  *
  * = Returns
  * An version of the object with the contained strings decoded to unicode.
  *
  **/
  decode: function(_ibj){
    var _obj, _type, _this = this;
    if(_ibj !== null && _ibj !== undefined){
      _type = _this.type(_ibj);
      if(!_type){
        return null;
      }
      switch(_type){
        case 'b': _obj = _ibj; break;
        case 'n': _obj = _ibj; break;
        case 's': _obj = _this._decodeString(_ibj); break;
        case 'd': _obj = _ibj; break;
        case 'a': _obj = _this._decodeArr(_ibj); break;
        case 'h': _obj = _this._decodeHash(_ibj); break;
        default: _obj = null; break;
      }
    }
    else {
      return null;
    }
    return _obj;
  },
  
/** = Description
  * Makes a deep copy of the object.
  *
  * When you use assignment of a js object, only primitive object types
  * (strings, numbers and booleans) are copied. This method makes a deep
  * (nested) clone of the input object.
  *
  * = Parameters
  * +_obj+:: Any object.
  *
  * = Returns
  * A copy of the object.
  *
  **/
  clone: function( _obj ){
    if(_obj === null || _obj === undefined){
      console.log('null or undefined obj:',_obj);
      return _obj;
    }
    var _item,
        _cloned;
    if( _obj instanceof Array ){
      _cloned = [];
      for( _item = 0; _item < _obj.length; _item ++ ){
        _cloned[ _item ] = this.clone( _obj[ _item ] );
      }
      return _cloned;
    }
    else if( _obj instanceof Object ){
      _cloned = {};
      for( _item in _obj ){
        _cloned[ _item ] = this.clone( _obj[ _item ] );
      }
      return _cloned;
    }
    else {
      return _obj;
    }
  },
  
/** = Description
  * Returns an URI-encoded string representation of all the changed values to
  * synchronize to the server.
  *
  * = Returns
  * An encoded string representation of values to synchronize.
  **/
  sync: function(){
    if(this.tosync.length===0){
      return false;
    }
    var _syncValues = {},
        _this = this,
        _values = _this.values,
        _tosync = _this.tosync,
        _len = _tosync.length,
        i = 0, _id, _value;
    for(;i<_len;i++){
      _id = _tosync.shift();
      _value = _values[_id].value;
      _syncValues[_id] = _value;
    }
    return encodeURIComponent(_this.encode(_syncValues));
  }
});

// Backwards compatibility assignment for code that still
// uses HVM as a reference of the Value Manager:
HVM = COMM.Values;

/*   Riassence Framework
 *   Copyright 2006 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** Data value synchonization container object.
  **
  ** +HValue+ is the default class to use for data syrchronization purposes.
  ** It's smart enough to tell COMM.Values (Value Manager) that it has
  ** been changed.
  **
  ** A single HValue instance can be bound to any number of responders, the
  ** main client-side responder class is +HControl+.
  **
  ** When you change the value in one of the instances bound to the HValue
  ** instances, all other instances get notified too and the server is also
  ** notified and can be further bound on the server side to any number of
  ** responders there too.
  **
  ** An instance constructed with "false" (Boolean) as its id is not reported
  ** to the server. Such an instance can be used for client-side responder
  ** synchronization.
  **
  ** Priority-wise, only the server can create a value id. If a value id is
  ** created on the client, the server won't recognize nor accept it.
  **
  ** If a value is changed on the server, it overrides the changes on
  ** the client, so no server-client lock is needed in this model.
  **
  ** = Instance variables:
  **  +id+::     Value Id, used by the whole value management system to identify individual values.
  **  +type+::   '[HValue]'
  **  +value+::  The container/"payload" data value itself.
  **  +views+::  A list of Components that uses this value. 
  **             Used for automatic value syncronization between responders.
***/
HValue = HClass.extend({
  
/** = Description
  * Constructs a value with the initial value +_value+ and the unique id +_id+.
  *
  * Only the server can create value id's, so use false when constructing
  * from the client. A value with a false id is not reported to the server.
  *
  * = Parameters
  * +_id+::     The source id (ideally set by server, should be unique)
  * +_value+::  The initial data 
  *
  **/
  constructor: function(_id,_value){
    this.id    = _id;
    this.value = _value;
    this.views = [];
    if(_id){
      COMM.Values.add(_id,this);
    }
  },
  
/** Destructor method. Releases all bindings.
  **/
  die: function(){
    for(var _viewNum=0;_viewNum<this.views.length;_viewNum++){
      var _tryObj = this.views[_viewNum];
      _tryObj.setValueObj( HDummyValue.nu() );
      this.views.splice(_viewNum,1);
    }
    if(this.id){
      COMM.Values.del(this.id);
    }
  },
  
/** = Description
  * Replaces the data of the value.
  *
  * Extend this method, if you want client-side validation in the value itself.
  *
  * = Parameters
  * +_value+::  The new data to replace the old data with.
  *
  **/
  set: function(_value){
    if(this.differs(_value)){
      this.value = _value;
      if(this.id){
        COMM.Values.changed(this);
      }
      this.refresh();
    }
  },
  
/** Compares +_value+ with +self.value+.
  * = Returns
  * true or false, depending on the equality
  **/
  differs: function(_value){
    return (COMM.Values.encode(_value) !== COMM.Values.encode(this.value));
  },
  
/** = Description
  * Setter for the server.
  * 
  * Just as +self.set+, but doesn't re-notify the server about the change.
  **/
  s: function(_value){
    this.value = _value;
    this.refresh();
  },
  
/** = Description
  * Return the data, returns the +self.value+ instance variable
  *
  * Returns:
  *  The value instance variable (the data "payload")
  **/
  get: function(){
    return this.value;
  },
  
/** = Description
  * Bind a responder to the value, use to attach HValues to responders derived from HControl.
  *
  * = Parameters
  * +_responder+::   Any responder that is derived from HControl or any other 
  *                  class instance that implements HValueResponder or has
  *                  compatible typing.
  **/
  bind: function(_responder){
    if(_responder===undefined){
      throw("HValueBindError: responder is undefined!");
    }
    if(this.views.indexOf(_responder)===-1){
      this.views.push(_responder);
      _responder.setValueObj( this );
    }
  },
  
/** = Description
  * Release a responder bound to the HValue instance itself.
  *
  * = Parameters
  * +_responder+::   Any responder that is derived from HControl or any other 
  *                  class instance that implements HValueResponder or has
  *                  compatible typing.
  **/
  unbind: function(_responder){
    for(var _viewNum=0;_viewNum<this.views.length;_viewNum++){
      var _tryObj = this.views[_viewNum];
      if(_tryObj===_responder){
        this.views.splice(_viewNum,1);
        return;
      }
    }
  },

/** Alias of +self.unbind+, opposite of +bind+.
  **/
  release: function(_responder){
    return this.unbind(_responder);
  },
  
/** Calls the setValue method all responders bound to this HValue.
  **/
  refresh: function(){
    for(var _viewNum=0;_viewNum<this.views.length;_viewNum++){
      var _responder = this.views[_viewNum];
      if(_responder.value !== this.value){
        if(!_responder._valueIsBeingSet){
          _responder._valueIsBeingSet=true;
          _responder.setValue( this.value );
          _responder._valueIsBeingSet=false;
        }
      }
    }
  }
  
});


/*   Riassence Framework
 *   Copyright 2007 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** A class for asynchronously fetching Javascript libraries from the server.
  **
  ** Loads and evalueates the code returned as a string from the server.
  ** Use the jsLoader instance to get packaged Javascript libraries from the
  ** standard package url.
***/
COMM.JSLoader = HClass.extend({
  
/** = Description
  * Construct with the base url.
  *
  * The this is the base url used by the +load+ method.
  *
  **/
  constructor: function(_uri){
    var _this = this;
    _this._loadedJS = [];
    _this.uri  = _uri;
    _this._okayed = false;
  },
  
  // Error catcher for failed requests.
  _fail: function(_this,_resp){
    console.log("failed to load js: "+_resp.url);
  },
  
/** = Description
  * Loads a js package using the name.
  *
  * The base url given in the constructor is used as the prefix.
  * Omit the '.js' suffix, because it's appended automatically.
  *
  * = Parameters
  * +_jsName+::   The name of the js file to load (without the .js suffix)
  *
  * = Usage:
  * Uses the main instance set to the base path of the server's
  * js package url. Loads a package containing list components.
  *   jsLoader.load('lists');
  *
  **/
  load: function(_jsName){
    var _this = this;
    if((_this._loadedJS.indexOf(_jsName)!==-1)) {
      return;
    }
    COMM.Queue.pause();
    _this._loadedJS.push(_jsName);
    if(BROWSER_TYPE.ie || BROWSER_TYPE.symbian){
      _this._req = COMM.request(
        _this.uri+_jsName+'.js', {
          onSuccess: function(_resp){
            COMM.Queue.unshiftEval(_resp.X.responseText);
            COMM.Queue.resume();
          },
          onFailure: _this._fail,
          method: 'GET',
          async: true
        }
      );
    }
    else {
      // The following doesn't always work reliably on Internet Explorer:
      var _script = document.createElement('script');
      _script.onload = function(){COMM.Queue.resume();};
      _script.src = _this.uri+_jsName+'.js';
      _script.type = 'text/javascript';
      document.getElementsByTagName('head')[0].appendChild(_script);
    }
  }
  
});

/** -- Global reference ++ **/
JSLoader = COMM.JSLoader;

// Makes the standard jsLoader instance based on the client base url 
// of the server when the page is loaded.
LOAD(
  function(){
    COMM.jsLoader = COMM.JSLoader.nu( HCLIENT_BASE + '/js/' );
    // backwards compatibility aliases:
    jsLoader = COMM.jsLoader;
  }
);



/*   Riassence Framework
 *   Copyright 2009 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */


/*** Constructs nodes from JSON structures as GUI
  ** tree structures. Lowers the learning curve of GUI
  ** development, because Javascript knowledge is not
  ** required to define user interfaces.
  ** The main purpose is to ease the development of
  ** user interfaces by defining them as data on the
  ** server, converting the data to JSON GUI trees and
  ** letting the client handle the rest. The end result
  ** is the same as defining the structures in
  ** JavaScript code.
  **
  ** This class is still in development, so expect more
  ** features and documentation as it matures.
***/
COMM.JSONRenderer = HClass.extend({
  
  version: 0.4,

/** = Description
  * Renders JSON structured data, see some of the demos for usage examples.
  *
  * = Parameters:
  * +_data+:   The data structure used for building.
  * +_parent+: The parent view (or app) (Optional)
  **/
  constructor: function(_data, _parent){
    if((_data['type'] === 'GUITree') && (this.version >= _data['version'])){
      this.data   = _data;
      this.parent = _parent;
      this.render();
    }
    else{
      throw("JSONRenderer: Only GUITree version "+this.version+" or older data can be handled.");
    }
  },
  render: function(){
    this.scopes = [window];
    this.scopeDepth = 0;
    this.view = this.renderNode( this.data, this.parent );
  },
  defineInScope: function( _definition ){
    var _isArr = (_definition instanceof Array),
        _isObj = (_definition instanceof Object);
    if( _isArr || !_isObj ){
      console.log("JSONRenderer; definition must be an Object, got: '"+(typeof _definition)+"'. Definition: ",_definition);
      return;
    }
    var _extension = {},
        _reserved = ['class','extend','implement'],
        _className = _definition[_reserved[0]],
        _extendName = _definition[_reserved[1]],
        _implementName = _definition[_reserved[2]],
        _extend = _extendName?this.findInScope(_extendName):false,
        _implement = _implementName?this.findInScope(_implementName):false,
        _scope = this.scopes[ this.scopeDepth ],
        _key, _value;
    if( _className === undefined ) {
      console.log("JSONRenderer; class name missing in definition scope.");
      return;
    }
    if( !_extend ){
      _extend = HClass;
    }
    for( _key in _definition ){
      if( _reserved.indexOf( _key ) === -1 ){
        _value = _definition[_key];
        _extension[_key] = this.extEval( _value );
      }
    }
    _scope[ _className ] = _extend.extend( _extension );
    if( _implement ){
      _scope[ _className ].implement( _implement );
    }
  },
  undefineInScope: function( ){
    
  },
  findInScope: function( _className ){
    var _class = false,
        _scopes = this.scopes,
        i = _scopes.length-1,
        _scope;
    for( ; i > -1 ; i-- ){
      _scope = _scopes[i];
      if( _scope[ _className ] !== undefined ){
        return _scope[ _className ];
      }
    }
    return _class;
  },
  extEval: function(_block){
    if( _block.indexOf("function(") === 0 ){
      eval( '_block = '+_block );
    }
    return _block;
  },
  renderNode: function( _dataNode, _parent ){
    var // Currently only window-level classes are supported
        _className = _dataNode['class'],
        _class = this.findInScope( _className ),
        
        // Currently only HView -derived classes are supported, so 
        // the rect is mandatory.
        _rect = _dataNode['rect'],
        _hasRect = (_rect !== undefined) && (_rect instanceof Array),
        
        // Checks, if any sub-views are defined.
        _hasSubviews = _dataNode['subviews'] !== undefined,
        _subViews    = _hasSubviews?_dataNode['subviews']:null,
        
        // Checks, if any options are defined.
        _hasOptions  = _dataNode['options'] !== undefined,
        _options     = _hasOptions?_dataNode['options']:null,
        
        // JS Extension block
        _hasExtension = _dataNode['extend'] !== undefined,
        _extension    = _hasExtension?_dataNode['extend']:null,
        
        // JS Definition block
        _hasDefinition = _dataNode['define'] !== undefined,
        _definitions   = _hasDefinition?_dataNode['define']:null,
        
        // The HView-derived class instance, instance is by default the parent
        _instance = _parent,
        
        i,
        
        _subView;
    this.scopeDepth ++;
    this.scopes.push({});
    try{
      if(_hasDefinition){
        if(_definitions instanceof Array){
          for( i = 0; i < _definitions.length; i++ ){
            this.defineInScope( _definitions[i] );
          }
        }
        else {
          console.log('renderNode definitions not Array, definitions:',_definitions);
        }
      }
      if(_class){
        if(_hasExtension){
          var _extBlock = {},
              _name,
              _block;
          for(_name in _extension){
            _block = _extension[_name];
            if( typeof _block === 'string' ){
              try {
                _block = this.extEval( _block );
              }
              catch(e){
                console.log('renderNode ext eval error:',e,', name:',_name,', block:',_block);
              }
            }
            _extBlock[_name] = _block;
          }
          _class = _class.extend( _extBlock );
        }
        if(_hasOptions){
          if(_options['valueObjId'] !== undefined){
            var _valueObjId = _options['valueObjId'];
            _options['valueObj'] = COMM.Values.values[_options['valueObjId']];
          }
        }
        // For HApplication -derived classes
        if(!_hasRect && _hasOptions){
          _instance = _class.nu(_options);
        }
        // For HView and HControl -derived classes
        else if(_hasRect){
          _instance = _class.nu(_rect,_parent,_options);
        }
      }
      else if(!(!_class && _hasSubviews)) {
        console.log('renderNode warning; No such class: '+_className+', node: ',_dataNode);
      }
    }
    catch(e){
      console.log('renderNode error:',e,', rect:',_rect,', class:',_dataNode['class'],', options:', _options);
    }
    // Iterates recursively through all subviews, if specified.
    if(_hasSubviews){
      for( i = 0; i < _subViews.length; i++ ){
        _subView = this.renderNode( _subViews[i], _instance );
      }
    }
    
    this.scopes.pop();
    this.scopeDepth --;
    
    // Returns the main view (or subview when recursively called).
    return _instance;
  }
});

/** -- Global reference ++ **/
JSONRenderer = COMM.JSONRenderer;


/*   Riassence Framework
 *   Copyright 2006 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** An interface to implement for classes that are members of +HValueMatrix+.
  **
  ** A HRadioButton is an example of an class that implements
  ** the HValueMatrixInterface.
***/
HValueMatrixInterface = {
  
  componentBehaviour: ['view','control','matrix'],
  
/** = Description
  * Standard +HControl+ constructor model.
  * Passes parameters through to its base class's constructor,
  * then calls +self.setValueMatrix+.
  *
  **/
  constructor: function(_rect, _parent, _options) {
    this.base(_rect, _parent, _options);
    this.setValueMatrix();
  },
  
/** = Description
  * Binds itself to the parent's HValueMatrix instance.
  *
  * Creates a valueMatrix member into the parent of itself, if none is present yet.
  * Sets the +self.value+ to true or false depending on the parent's valueMatrix value.
  *
  **/
  setValueMatrix: function(){
    if(this.parent['valueMatrix'] === undefined){
      this.parent.valueMatrix = HValueMatrix.nu();
    }
    this.valueMatrixIndex = this.parent.valueMatrix.addControl(this);
  },
  
/** Sets itself active using the parent's valueMatrix on the click event.
  **/
  click: function(){
    if (this.parent.valueMatrix instanceof HValueMatrix) {
      this.parent.valueMatrix.setValue( this.valueMatrixIndex );
    }
  },
  
/** The destructor makes sure +self+ is released from the parent's valueMatrix
  * upon destruction.
  **/
  die: function(){
    if(this['parent']){
      if(this.parent['valueMatrix']){
        this.parent.valueMatrix.release(this);
      }
    }
    this.base();
  }
};

// Alias for backwards compatibility.
HValueMatrixComponentExtension = HValueMatrixInterface;

/*** = Description
  ** A HValueMatrix operates a number of components that operate on boolean
  ** values, like the HRadioButton using a separate value for selecting the
  ** index of selected items.
***/
HValueMatrix = HClass.extend({
  
/** The constructor doesn't take parameters.
  **/
  constructor: function(){
    // An array to hold member components
    this.ctrls = [];
    // The index of the value member chosen
    this.value = -1;
    this.valueObj = new HDummyValue();
  },
  
/** Method for value interfaces, called by the HValue instance when bound.
  **/
  setValueObj: function(_valueObj){
    this.valueObj = _valueObj;
    this.setValue(_valueObj.value);
  },
  
/** Method for value interfaces, the +_index+ is the 
  * index of the item to select.
  **/
  setValue: function(_index){
    if( _index !== this.value ){
      // Set the previous value object to false (reflects to its container component(s))
      if(this.value !== -1){
        if(this.ctrls[this.value]){
          this.ctrls[this.value].setValue(false);
        }
      }
      // Store the new index as the currently active value
      this.value = _index;
      if(_index !== -1){
        // Set the new value object to true (reflects to its container component(s))
        if(_index<this.ctrls.length){
          this.ctrls[_index].setValue(true);
        }
      }
      this.valueObj.set(_index);
    }
  },
  
/** A method for attaching a +HControl+ -derived class to the matrix.
  **/
  addControl: function(_ctrl) {
    this.ctrls.push(_ctrl);
    var _newIndex = this.ctrls.length-1;
    if(_ctrl.value){
      this.setValue(_newIndex);
    }
    return _newIndex;
  },
  
/** A method for releasing a +HControl+ -derived class from the matrix.
  **/
  release: function(_ctrl) {
    var _index = this.ctrls.indexOf(_ctrl);
    if(_index !== -1){
      this.ctrls.splice( _index, 1 );
      if(_index === this.value){
        this.setValue(-1);
      }
    }
  }
});

/*   Riassence Framework
 *   Copyright 2006 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** Point objects represent points on a two-dimensional coordinate grid. The
  ** object's coordinates are stored as public x and y data members.
  **
  ** = Instance Variables
  ** +type+::  '[HPoint]'
  ** +x+::     The X coordinate of the point
  ** +y+::     The Y coordinate of the point
  **
  ***/
HPoint = HClass.extend({

/** = Description
  * Creates a new Point object that corresponds to the point (x, y), or that's
  * copied from point. If no coordinate values are assigned, the Point's
  * location is indeterminate.
  *
  * = Parameters
  * by using a HPoint instance:
  * +point+::  Another +HPoint+ or other compatible structure.
  *
  * by using separate numeric coordinates:
  * +x+::  x coordinate
  * +y+::  y coordinate
  *
  * = Usage
  *  var myPoint = new HPoint(100,200);
  *  var mySameCoordPoint = new HPoint( myPoint );
  *
  **/
  constructor: function() {
    this.type = '[HPoint]';
    var _args=arguments;
    
    if (_args.length === 0) {
      this._constructorDefault();
    }
    else if (_args.length === 2) {
      this._constructorValues(_args[0],_args[1]);
    }
    else if (_args.length === 1) {
      this._constructorPoint(_args[0]);
    }
    else {
      throw "Invalid number of arguments.";
    }

  },
  _constructorDefault: function() {
    this.x = null;
    this.y = null;
  },
  _constructorValues: function(x, y) {
    this.x = x;
    this.y = y;
  },
  _constructorPoint: function(_point) {
    this.x = _point.x;
    this.y = _point.y;
  },
  
/** = Description
  * Sets the Point's x and y coordinates.
  *
  * = Parameters
  * +x+::  The new X coordinate of the point
  * +y+::  The new Y coordinate of the point
  *
  **/
  set: function() {
    var _args=arguments;
    
    if (_args.length === 0) {
      this._constructorDefault();
    }
    else if (_args.length === 2) {
      this._constructorValues(_args[0],_args[1]);
    }
    else if (_args.length === 1) {
      this._constructorPoint(_args[0]);
    }
    else {
      throw "Invalid number of arguments.";
    }
  },
  
/** = Description
  * Ensures that the Point lies within rect. If it's already contained in the
  * rectangle, the Point is unchanged; otherwise, it's moved to the rect's
  * nearest edge.
  *
  * = Parameters
  * +_rect+::   A HRect instance to constrain to.
  *
  **/
  constrainTo: function(_rect) {
    
    if (this.x < _rect.left) {
        this.x = _rect.left;
    }
    if (this.y < _rect.top) {
      this.y = _rect.top;
    }
    if (this.x > _rect.right) {
      this.x = _rect.right;
    }
    if (this.y > _rect.bottom) {
      this.y = _rect.bottom;
    }
    
  },
  
/** = Description
  * Creates and returns a new Point that adds the given Point and this Point
  * together. The new object's x coordinate is the sum of the operands' x
  * values; its y value is the sum of the operands' y values.
  *
  * = Parameters 
  * with HPoint:
  * +_point+::  An HPoint to add to.
  *
  * with coordinates:
  * +_x+::  An X-coordinate to add to.
  * +_y+::  An Y-coordinate to add to.
  *
  * = Returns
  * A new HPoint.
  *
  **/
  add: function(_point) {
    _args = arguments;
    if((_args.length===1)&&(_args[0].type===this.type)){
      _point = _args[0];
      return new HPoint( (this.x + _point.x), (this.y + _point.y) );
    }
    else if(_args.length===2){
      return new HPoint( (this.x + _args[0]), (this.y + _args[1]) );
    } else {
      return new HPoint( 0, 0 );
    }
  },
  
  
/** = Description
  * Creates and returns a new Point that subtracts the given Point from this
  * Point. The new object's x coordinate is the difference between the
  * operands' x values; its y value is the difference between the operands'
  * y values.
  *
  * = Parameters 
  *
  * with HPoint:
  * +_point+:: An HPoint to subtract from.
  *
  * with coordinates:
  * +_x+::  An X-coordinate to subtract from.
  * +_y+::  An Y-coordinate to subtract from.
  *
  * = Returns
  * A new HPoint.
  *
  **/
  subtract: function(){
    _args = arguments;
    if((_args.length===1)&&(_args[0].type===this.type)){
      _point = _args[0];
      return new HPoint( this.x-_point.x, this.y-_point.y );
    }
    else if(_args.length===2){
      return new HPoint( this.x-_args[0], this.y-_args[1] );
    } else {
      return new HPoint( 0, 0 );
    }
  },
  
  
/** = Description
  * Returns true if the two objects' point exactly coincide.
  *
  * = Parameter
  * +_point+::  A HPoint to compare to.
  *
  * = Returns
  * The result; true or false.
  *
  **/
  equals: function(_point) {
    return ( this.x === _point.x && this.y === _point.y );
  }


});


/*   Riassence Framework
 *   Copyright 2006 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** A Rect object represents a rectangle. Rects are used throughout the
  ** Components to define the frames of windows, views, bitmaps even the 
  ** screen itself. A HRect is defined by its four sides, expressed as the public
  ** data members left, top, right, and bottom.
  **
  ** If you change a component's rect, you should call its HView.drawRect method.
  **
  ** = Instance Variables
  ** +type+::         '[HRect]'
  ** +top+::          The position of the rect's top side (from parent top)
  ** +left+::         The position of the rect's left side (from parent left)
  ** +bottom+::       The position of the rect's bottom side (from parent top)
  ** +right+::        The position of the rect's right side (from parent left)
  ** +leftTop+::      A HPoint representing the coordinate of the rect's left top corner
  ** +leftBottom+::   A HPoint representing the coordinate of the rect's left bottom corner
  ** +rightTop+::     A HPoint representing the coordinate of the rect's right top corner
  ** +rightBottom+::  A HPoint representing the coordinate of the rect's right bottom corner
  ** +width+::        The width of the rect.
  ** +height+::       The height of the rect.
  ***/  
HRect = HClass.extend({
/** = Description
  * Initializes a Rect as four sides, as two diametrically opposed corners,
  * or as a copy of some other Rect object. A rectangle that's not assigned
  * any initial values is invalid, until a specific assignment is made, either
  * through a set() function or by setting the object's data members directly.
  *
  * = Parameters 
  * using a HRect instance:
  * +rect+::  Another HRect.
  *
  * using two HPoint instances:
  * +leftTop+::       Coordinates of the left top corner.
  * +rightBottom+::   Coordinates of the right bottom corner.
  *
  * using separate numeric coordinates:
  * +left+::    The coordinate of left side.
  * +top+::     The coordinate of top side.
  * +right+::   The coordinate of right side.
  * +bottom+::  The coordinate of bottom side.
  *
  * = Usage
  *  var myLeftTopPoint = new HPoint(100,200);
  *  var myBottomRightPoint = new HPoint(300,400);
  *  var myRectFromOppositeCornerPoints = new HRect( myLeftTopPoint, myBottomRightPoint );
  *  var myRectFromSideCoordinates = new HRect(100,200,300,400);
  *  var myRectFromAnotherRect = new HRect( myRectFromEdgeCoordinates );
  *
  **/
  constructor: function() {
    this.type = '[HRect]';
    var _args=arguments;
    
    if (_args.length === 0) {
      this._constructorDefault();
    } else if (_args.length === 4) {
      this._constructorSides(_args[0],_args[1],_args[2],_args[3]);
    }
    else if (_args.length === 2) {
      this._constructorPoint(_args[0],_args[1]);
    }
    else if (_args.length === 1) {
      if(_args[0] instanceof Array){
        this._constructorSides(_args[0][0],_args[0][1],_args[0][2],_args[0][3]);
      }
      else{
        this._constructorRect(_args[0]);
      }
    }
    else {
      throw "Invalid number of arguments.";
    }
    this.updateSecondaryValues();
  },
  _constructorDefault: function() {
    this.top = 0;
    this.left = 0;
    this.bottom = -1;
    this.right = -1;
  },
  _constructorSides: function(_left, _top, _right, _bottom) {
    this.top = _top;
    this.left = _left;
    this.bottom = _bottom;
    this.right = _right;
  },
  _constructorPoint: function(_leftTop, _rightBottom) {
    this.top = _leftTop.y;
    this.left = _leftTop.x;
    this.bottom = _rightBottom.y;
    this.right = _rightBottom.x;
  },
  _constructorRect: function(_rect) {
    this.top = _rect.top;
    this.left = _rect.left;
    this.bottom = _rect.bottom;
    this.right = _rect.right;
  },

/** = Description
  * You should call this on the instance to update secondary values, like
  * width and height, if you change a primary (left/top/right/bottom) value
  * straight through the property.
  *
  * Do not change properties other than the primaries through properties.
  *
  * Use the accompanied methods instead.
  *
  **/
  updateSecondaryValues: function() {
    /**
      * isValid is true if the Rect's right side is greater than or equal to its left
      * and its bottom is greater than or equal to its top, and false otherwise.
      * An invalid rectangle can't be used to define an interface area (such as
      * the frame of a view or window).
      **/
    this.isValid = ( this.right >= this.left && this.bottom >= this.top );
    
    /**
      *
      * The Point-returning functions return the coordinates of one of the
      * rectangle's four corners. 
      **/
    this.leftTop = new HPoint(this.left, this.top);
    this.leftBottom = new HPoint(this.left, this.bottom);
    this.rightTop = new HPoint(this.right, this.top);
    this.rightBottom = new HPoint(this.right, this.bottom);
    
    /**
      * The width and height of a Rect's rectangle, as returned through these
      * properties.
      **/
    this.width = (this.right - this.left);
    this.height = (this.bottom - this.top);
  },
  
/** = Description
  * Sets the object's rectangle by defining the coordinates of all four
  * sides.
  *
  * The other set...() functions move one of the rectangle's corners to the
  * Point argument; the other corners and sides are modified concomittantly.
  *
  * None of these methods prevents you from creating an invalid rectangle.
  *
  * = Parameters
  * +_left+::     The coordinate of the left side.
  * +_top+::      The coordinate of the top side.
  * +_right+::    The coordinate of the right side.
  * +_bottom+::   The coordinate of the bottom side.
  *
  **/
  set: function() {
    var _args=arguments;
    
    if (_args.length === 0) {
      this._constructorDefault();
    } else if (_args.length === 4) {
      this._constructorSides(_args[0],_args[1],_args[2],_args[3]);
    }
    else if (_args.length === 2) {
      this._constructorPoint(_args[0],_args[1]);
    }
    else if (_args.length === 1) {
      this._constructorRect(_args[0]);
    }
    else {
      throw "Invalid number of arguments.";
    }
    this.updateSecondaryValues();
  },
  
/** = Description
  * Moves the rect's left side to a new coordinate.
  *
  * = Parameters
  * +_left+::  The new left side coordinate (in px)
  *
  **/
  setLeft: function(_left){
    this.left = _left;
    this.updateSecondaryValues();
  },
  
/** = Description
  * Moves the rect's right side to a new coordinate.
  *
  * = Parameters
  * +_right+::  The new right side coordinate (in px)
  *
  **/
  setRight: function(_right){
    this.right = _right;
    this.updateSecondaryValues();
  },
  
/** = Description
  * Moves the rect's top side to a new coordinate.
  *
  * = Parameters
  * +_top+::  The new top side coordinate (in px)
  *
  **/
  setTop: function(_top){
    this.top = _top;
    this.updateSecondaryValues();
  },
  
/** = Description
  * Moves the rect's bottom side to a new coordinate.
  *
  * = Parameters
  * +_bottom+::  The new bottom side coordinate (in px)
  *
  **/
  setBottom: function(_bottom){
    this.bottom = _bottom;
    this.updateSecondaryValues();
  },
  
/** = Description
  * Moves the rects left and top sides to a new point. Affects the position,
  * width and height.
  *
  * = Parameters
  * +_point+::  A HPoint instance to mode the sides to.
  *
  **/
  setLeftTop: function(_point) {
    this.left=_point.x;
    this.top=_point.y;
    this.updateSecondaryValues();
  },
  
/** = Description
  * Moves the rects left and bottom sides to a new point. Affects the left
  * position, width and height.
  *
  * = Parameters
  * +_point+::  A HPoint instance to mode the sides to.
  *
  **/
  setLeftBottom: function(_point) {
    this.left=_point.x;
    this.bottom=_point.y;
    this.updateSecondaryValues();
  },
  
/** = Description
  * Moves the rects right and top sides to a new point. Affects the top
  * position, width and height.
  *
  * = Parameters
  * +_point+::  A HPoint instance to mode the sides to.
  *
  **/
  setRightTop: function(_point) {
    this.right=_point.x;
    this.top=_point.y;
    this.updateSecondaryValues();
  },
  
/** = Description
  * Moves the rects right and bottom sides to a new point. Affects the width
  * and height. Does not affect the position.
  *
  * = Parameters
  * +_point+::  A HPoint instance to mode the sides to.
  *
  **/
  setRightBottom: function(_point) {
    this.right=_point.x;
    this.bottom=_point.y;
    this.updateSecondaryValues();
  },
  
/** = Description
  * Moves the rects right side to a new coordinate. Does not affect the position.
  *
  * = Parameters
  * +_width+::  A numeric value representing the new target width of the rect.
  *
  **/
  setWidth: function(_width){
    this.right = this.left + _width;
    this.updateSecondaryValues();
  },

/** = Description
  * Moves the rects bottom side to a new coordinate. Does not affect the position.
  *
  * = Parameters
  * +_height+::   A numeric value representing the new target height of the rect.
  *
  **/
  setHeight: function(_height){
    this.bottom = this.top + _height;
    this.updateSecondaryValues();
  },

/** = Description
  * Moves the rects right and bottom sides to new coordinates. Does not affect the position.
  *
  * = Parameters 
  * by separate numeric values:
  * +_width+::   A numeric value representing the new target width of the rect.
  * +_height+::  A numeric value representing the new target height of the rect.
  *
  * by HPoint used as "HSize":
  * +_point.x+::   A numeric value representing the new target width of the rect.
  * +_point.y+::   A numeric value representing the new target height of the rect.
  *
  **/
  setSize: function(){
    var _args=arguments;
    // Using width and height:
    if (_args.length === 2) {
      _width = _args[0];
      _height = _args[1];
    }
    // Using a point:
    else if (_args.length === 1) {
      _width = _args.x;
      _height = _args.y;
    }
    this.right = this.left + _width;
    this.bottom = this.top + _height;
    this.updateSecondaryValues();
  },
  
/** = Description
  * Returns true if the Rect has any area even a corner or part 
  * of a side in common with rect, and false if it doesn't.
  *
  * = Parameters
  * +_rect+::   A HRect instance to intersect this rect with
  *
  * = Returns
  * A Boolean (true/false) depending on the result.
  *
  **/
  intersects: function(_rect) {
    return (
      ((_rect.left >= this.left && _rect.left <= this.right) ||
        (_rect.right >= this.left && _rect.right <= this.right)) && 
      ((_rect.top >= this.top && _rect.top <= this.bottom) ||
        (_rect.bottom >= this.top && _rect.bottom <= this.bottom)));
  },
  
/** = Description
  * Returns true if point or rect lies entirely within the Rect's
  * rectangle (and false if not). A rectangle contains the points that lie
  * along its edges; for example, two identical rectangles contain each other.
  * 
  * Also works with HPoint instances.
  *
  * = Parameters
  * +_obj+::  A HRect or HPoint to check the containment with.
  *
  * = Returns
  * A Boolean (true/false) depending on the result.
  *
  **/
  contains: function(_obj) {
    if(_obj instanceof HPoint) {
      return this._containsPoint(_obj);
    }
    else if(_obj instanceof HRect) {
      return this._containsRect(_obj);
    }
    else {
      throw "Wrong argument type.";
    }
  },
  _containsPoint: function(_point) {
    return ( _point.x >= this.left && _point.x <= this.right &&
             _point.y >= this.top && _point.y <= this.bottom );
  },
  _containsRect: function(_rect) {
    return ( _rect.left >= this.left && _rect.right <= this.right &&
             _rect.top >= this.top && _rect.bottom <= this.bottom );
  },
  
/** = Description
  * Insets the sides of the Rect's rectangle by x units (left and
  * right sides) and y units (top and bottom). Positive inset values shrink
  * the rectangle; negative values expand it. Note that both sides of each
  * pair moves the full amount. For example, if you inset a Rect by (4,4), the
  * left side moves (to the right) four units and the right side moves (to the
  * left) four units (and similarly with the top and bottom).
  *
  * = Parameters 
  * using a HPoint:
  * +point+::  A HPoint to inset by.
  *
  * using separate x and y coordinates:
  * +x+::  x Coordinate 
  * +y+::  y Coordinate
  *
  **/
  insetBy: function() {
    var _args=arguments;
    if (_args.length === 1) {
      this._insetByPoint(_args[0]);
    } else if (_args.length === 2) {
      this._insetByXY(_args[0],_args[1]);
    } else {
      throw "Invalid number of arguments.";
    }
    this.updateSecondaryValues();
  },
  _insetByPoint: function(_point) {
    this._insetByXY(_point.x, _point.y);
  },
  _insetByXY: function(x, y) {
    this.left += x;
    this.top += y;
    this.right -= x;
    this.bottom -= y;
  },
  
/** = Description
  * Moves the Rect horizontally by x units and vertically by y
  * units. The rectangle's size doesn't change.
  *
  * = Parameters 
  * using a HPoint:
  * +point+::  A HPoint to offset by.
  *
  * using separate x and y coordinates
  * +x+::  X coordinate
  * +y+::  Y coordinate
  *
  **/
  offsetBy: function() {
    var _args=arguments;
    if (_args.length === 1) {
      this._offsetByPoint(_args[0]);
    } else if (_args.length === 2) {
      this._offsetByXY(_args[0],_args[1]);
    } else {
      throw "Invalid number of arguments.";
    }
    this.updateSecondaryValues();
  },
  _offsetByPoint: function(_point) {
    this._offsetByXY(_point.x, _point.y);
  },
  _offsetByXY: function(x, y) {
    this.left += x;
    this.top += y;
    this.right += x;
    this.bottom += y;
  },
  
/** = Description
  * Moves the Rect to the location (x,y).
  *
  * = Parameters 
  * using a HPoint:
  * +point+::  A HPoint to offset to.
  *
  * using separate x and y coordinates):
  * +x+::  X coordinate 
  * +y+::  Y coordinate
  *
  **/
  offsetTo: function() {
    var _args=arguments;
    if (_args.length === 1) {
      this._offsetToPoint(_args[0]);
    } else if (_args.length === 2) {
      this._offsetToXY(_args[0],_args[1]);
    } else {
      throw "Invalid number of arguments.";
    }
    this.updateSecondaryValues();
  },
  _offsetToPoint: function(_point) {
    this._offsetToXY(_point.x, _point.y);
  },
  _offsetToXY: function(x, y) {
    this.right += x-this.left;
    this.left = x;
    this.bottom += y-this.top;
    this.top = y;
  },
  
/** = Description
  * Returns true if the two objects' rectangles exactly coincide.
  *
  * = Parameters
  * +_rect+::  A HRect instance to compare to.
  *
  * = Returns
  * A Boolean (true/false) depending on the result.
  *
  **/
  equals: function(_rect) {
    return (this.left === _rect.left && this.top === _rect.top &&
            this.right === _rect.right && this.bottom === _rect.bottom);
  },
  
/** = Description
  * Creates and returns a new Rect that's the intersection of this Rect and
  * the specified Rect. The new Rect encloses the area that the two Rects have
  * in common. If the two Rects don't intersect, the new Rect will be invalid.
  *
  * = Parameters
  * +_rect+::   A HRect instance to compare to.
  *
  * = Returns
  * A new HRect instance.
  *
  **/
  intersection: function(_rect) {
    return new HRect(
       Math.max(this.left, _rect.left), Math.max(this.top, _rect.top),
       Math.min(this.right, _rect.right), Math.min(this.bottom, _rect.bottom)
    );
  },
  
/** = Description
  * Creates and returns a new Rect that minimally but completely encloses the
  * area defined by this Rect and the specified Rect.
  *
  * = Parameters
  * +_rect+::   A HRect instance to compare to.
  *
  * = Returns
  * A new HRect instance.
  *
  **/
  union: function(_rect) {
    return new HRect(
      Math.min(this.left, _rect.left), Math.min(this.top, _rect.top),
      Math.max(this.right, _rect.right), Math.max(this.bottom, _rect.bottom)
    );
  },
  
  // HValue and HView support
  valueObj: null,
  viewIds: [],
  
/** = Description
  * Bind function
  *
  * = Parameters
  * +_view+::  view
  *
  **/
  bind: function(_view){
    if(this.viewIds.indexOf(_view.viewId) !== -1){
      this.viewIds.push( _view.viewId );
    }
  },
  
/** = Description
  * Release function
  **/
  release: function(_view){
    var _viewIdx = this.viewIds.indexOf(_view.viewId);
    if(_viewIdx !== -1){
      this.viewIds.splice( _viewIdx, 1 );
    }
  },
  
/** = Description
  * Sets valueObj for this component given as parameter.
  *
  * = Parameters
  * +_valueObj+::   valueObj to use
  *
  **/
  setValueObj: function(_valueObj){
    this.valueObj = _valueObj;
  },
  
/** = Description
  * setValue function
  *
  * = Parameters
  * +_value+::      value
  * +_srcViewId+::  srcViewId
  *
  **/
  setValue: function(_value,_srcViewId){
    if(this.valueObj){
      this.valueObj.set(_value);
    }
    this.set(_value[0],_value[1],_value[2],_value[3]);
    var i=0, _viewId;
    for(;i<this.viewIds.length;i++){
      _viewId = this.viewIds[i];
      HSystem.views[_viewId].drawRect();
    }
  }
  
});


/*   Riassence Framework
 *   Copyright 2006 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** A single instance class.
  ** The theme manager knows the name of the currently loaded theme and handles
  ** the loading of theme specific markup snippets and style declarations.
  **
  ** = Instance variables
  ** +themePath+::        Relative path to the components' top directory. 
  ** +currentTheme+::     The name of the theme currently in use. Initially the
  **                      default unnamed theme is used.
  ** +usesComponentDir+:: True when the components are separated in their own
  **                      directories, usually when using the 
  **                      source/development version. False when the components
  **                      are all in same directory. This is the case in the 
  **                      release build.
  **
  ***/

HDefaultThemePath = '/H/themes';
HDefaultThemeName = 'default';
HNoComponentCSS = [];
HNoCommonCSS = [];
HThemeHasIE6GifsInsteadOfPng = [];

HThemeManager = HClass.extend({
  
  constructor: null,
  
  init: function(){
    
    // Default root path of the themes path, should contain at least the default theme.
    this.themePath = HDefaultThemePath;
    
    // Hash map of loaded template markup (html templates), by theme.
    // componentName is used as the secondary key.
    this._tmplCache = {};
    
    // Hash map of loaded css templates, by theme.
    // componentName is used as the secondary key.
    this._cssCache = {};
    
    // The currently selected default theme name.
    this.currentTheme = HDefaultThemeName;
  },
  
  setThemePath: function( _path ){
    this.themePath = _path;
  },
  
  // Error messages, should be refined.
  _errTemplateNotFound: function( _url ) {
    console.log( "ERROR: Template Not Found: '" + _url + "' ");
  },
  _errTemplateFailure: function( _url ) {
    console.log( "ERROR: Template Failure: '" + _url + "' ");
  },
  _errTemplateException: function( _url ) {
    console.log( "ERROR: Template Exception: '" + _url + "' ");
  },
  
/** = Description
  * Loads a template file and returns its contents.
  * If errors occurred, calls the error management functions.
  *
  * = Parameters
  * +_url+::         A valid local file path or http url pointing to the 
  *                  resource to load.
  * +_contentType+:: An optional parameter, specifies the content type wanted, 
  *                  defaults to text/html.
  *
  * = Returns
  * The contents of the path.
  **/
  fetch: function( _url, _contentType, _callBack, _async ) {
    var _callBackFun;
    if( !_contentType ){
      _contentType = 'text/html; charset=UTF-8';
    }
    if(_async){
      _callBackFun = function( resp ){
        _callBack( resp.X.responseText );
      };
    }
    else{
      // console.log('WARNING: Fetching synchronously using HThemeManager is not recommended. Use pre-packaged themes instead.');
      var _respText;
      _callBackFun = function( resp ){
        _respText = resp.X.responseText;
      };
    }
    COMM.request(
      _url, {
        onSuccess:    _callBackFun,
        on404:        function(resp){ HThemeManager._errTemplateNotFound(  resp.url ); },
        onFailure:    function(resp){ HThemeManager._errTemplateFailure(   resp.url ); },
        onException:  function(resp){ HThemeManager._errTemplateException( resp.url ); },
        method: 'GET',
        async: _async
      }
    );
    if(!_async){
      return _respText;
    }
  },
  
  
/** Returns the theme/component -specific path, called from inside css
  * themes, a bit kludgy approach to tell the theme graphics file paths. 
  **/
  getThemeGfxPath: function() {
    var _themeName      = this._cssEvalParams[0],
        _componentName  = this._cssEvalParams[1],
        _themePath      = this._cssEvalParams[2],
        _urlPrefix      = this._urlPrefix( _themeName, _componentName, _themePath );
    return this._joinPath( _urlPrefix, 'gfx' );
  },
  
/** = Description
  * Returns the theme/component -specific graphics file path with proper css wrappers.
  * Used from inside css themes, a bit kludgy approach to tell the file name path.
  *
  * = Parameters
  * +_fileName+:: The File name to load.
  *
  **/
  getCssFilePath: function( _fileName ){
    var _themeName      = this._cssEvalParams[0];
    if((HThemeHasIE6GifsInsteadOfPng.indexOf(_themeName)!==-1) && ELEM._is_ie6){
      return "url('"+this._joinPath( this.getThemeGfxPath(), _fileName.replace('.png','-ie6.gif') )+"')";
    }
    else {
      return "url('"+this._joinPath( this.getThemeGfxPath(), _fileName )+"')";
    }
  },
  
/** = Description
  * Loads a css file based on the given url (or file path).
  * Evaluates the css data.
  * Makes sure the browser uses the data for component styles.
  *
  * = Parameter
  * +_url+:: A valid url that points to a valid css file.
  *
  * = Returns
  * The source of the url.
  *
  **/
  loadCSS: function( _url ) {
    var _contentType = 'text/css',
        _cssFun = function(_cssText){
          // Don't try to do anything with empty or invalid css data:
          if (!_cssText || _cssText === "") {
            return;
          }
          HThemeManager.useCSS( _cssText );
        };
    this.fetch( _url, _contentType, _cssFun, true );
  },
    
  useCSS: function( _cssText ){
    var _contentType = 'text/css';
    // Evaluate the css text
    _cssText = this._bindCSSVariables( _cssText );
    
    var _style, _styleSheet, _head;
    
    if(ELEM._is_ie) {
      // Internet Explorer (at least 6.x; check what 7.x does)
      _style         = document.createStyleSheet();
      _style.cssText = _cssText;
    }
    
    else {
      // Common, standard <style> tag generation in <head>
      _style        = document.createElement( "style" );
      _style.type   = _contentType;
      _style.media  = "all";
      
      _head = document.getElementsByTagName('head')[0];
      _head.appendChild(_style);
      
      if (BROWSER_TYPE.safari) {
        // Work-around for safari
        var _cssTextElement = document.createTextNode(_cssText);
        _style.appendChild(_cssTextElement);
      }
      else {
        // This works for others (add more checks, if problems with new browsers)
        _style.innerHTML = _cssText;
      }
    }

  },
  
  _addSlash: function( _str ){
    if( _str[ _str.length-1 ] !== '/' ){
      _str += '/';
    }
    return _str;
  },
  
  _joinPath: function( _str1, _str2 ){
    return this._addSlash( _str1 ) + _str2;
  },
  
  // Makes a common url prefix for template files
  _urlPrefix: function( _themeName, _componentName, _themePath ) {
    
    var _path = _themePath;
    
    // Usually null
    if( _themePath === null ) {
      _path = this.themePath;
    }
    
    _path = this._joinPath( _path, _themeName );
    
    return _path;
  },
  
  // Makes a valid css template url
  _cssUrl: function( _themeName, _componentName, _themePath ) {
    this._cssEvalParams = [_themeName, _componentName, _themePath];
    var _cssPrefix = this._urlPrefix( _themeName, _componentName, _themePath ),
        _cssSuffix = this._joinPath( 'css', _componentName+'.css' ),
        _cssUrl = this._joinPath( _cssPrefix, _cssSuffix );
    return _cssUrl;
  },
  
  // Makes a valid html template url
  _markupUrl: function( _themeName, _componentName, _themePath ) {
    var _htmlPrefix = this._urlPrefix( _themeName, _componentName, _themePath ),
        _htmlSuffix = this._joinPath( 'html', _componentName+'.html' ),
        _htmlUrl = this._joinPath( _htmlPrefix, _htmlSuffix );
    return _htmlUrl;
  },
  
/** = Description
  * Loads HTML templates of components. Handles caching independently and 
  * intelligently.
  *
  * = Parameters
  * +_themeName+::     The name of the template to use.
  * +_componentName+:: The name of the component template (css/html) to load.
  * +_themePath+::     Optional, parameter to override the global theme path.
  *
  * = Returns
  * The Pre-Evaluated HTML Template.
  *
  **/
  loadMarkup: function( _themeName, _componentName, _themePath ) {
    if( !this._tmplCache[_themeName] ){
      this._tmplCache[_themeName] = {};
    }
    var _cached = this._tmplCache[_themeName][_componentName];
    
    if (null === _cached || undefined === _cached) { 
      var _markupUrl = this._markupUrl( _themeName, _componentName, _themePath ),
          _markup = this.fetch( _markupUrl, null, null, false );
      return this.setMarkup( _themeName, _componentName, _markup );
    }
    return _cached;
  },
  
/** = Description
  * Sets the html template for the theme and component name combination.
  *
  * = Parameters
  * +_themeName+::     The name of the template to use..
  * +_componentName+:: The name of the component template.
  * +_markup+::        The content of the html markup.
  **/
  setMarkup: function( _themeName, _componentName, _markup ){
    // Save an empty string to template cache to prevent repeated failing
    // requests.
    if (null === _markup || undefined === _markup) {
      _markup = "";
    }
    this._tmplCache[_themeName][_componentName] = _markup;
    return _markup;
  },
  
/** = Description
  * Loads CSS and HTML templates of components. Called from HView#_loadMarkup.
  * Returns the HTML Template as text.
  * Manages file caches independently and intelligently.
  *
  * = Parameters
  * +_themeName+::     The name of the template to use.
  * +_componentName+:: The name of the component template (css/html) to load.
  * +_themePath+::     Optional, parameter to override the global theme path.
  *
  * = Returns
  * The Pre-Evaluated HTML Template.
  *
  **/
  getMarkup: function( _themeName, _componentName, _themePath ) {
    /* Load Theme-Specific CSS: */
    if(!this._cssCache[_themeName]){
      this._cssCache[_themeName] = {};
      if(HNoCommonCSS.indexOf(_themeName)===-1){
        var _commonCssUrl = this._cssUrl( _themeName, 'common', _themePath, null );
        this.loadCSS( _commonCssUrl );
      }
    }
    
    /* Load Component-Specific CSS, unless configured to only load the common css: */
    if(HNoComponentCSS.indexOf(_themeName)===-1){
      if (!this._cssCache[_themeName][_componentName]){
        var _componentCssUrl = this._cssUrl( _themeName, _componentName, _themePath );
        this._cssCache[_themeName][_componentName] = true;
        this.loadCSS( _componentCssUrl );
      }
    }
    
    /* Load/Return Component-Specific HTML: */
    return this.loadMarkup( _themeName, _componentName, _themePath );
  },
  
  
  _componentGfxPath: function( _themeName, _componentName, _themePath ) {
    var _urlPrefix      = this._urlPrefix( _themeName, _componentName, _themePath ),
        _url = this._joinPath( _urlPrefix, 'gfx' );
    return _url;
  },
  _componentGfxFile: function( _themeName, _componentName, _themePath, _fileName ){
    if((HThemeHasIE6GifsInsteadOfPng.indexOf(_themeName)!==-1) && ELEM._is_ie6){
      return this._joinPath( this._componentGfxPath(_themeName, _componentName, _themePath), _fileName.replace('.png','-ie6.gif') );
    }
    return this._joinPath( this._componentGfxPath(_themeName, _componentName, _themePath), _fileName );
  },
  
  
/** = Description
  * Returns the full path of the +_fileName+ given. Uses the default theme.
  **/
  getThemeGfxFile: function( _fileName ) {
    return this.getThemeGfxPath() + _fileName;
  },
  
  
/** = Description
  * Sets the active theme.
  * 
  * = Parameters
  * +_theme+:: The name of the theme to be set as the active theme.
  *
  **/
  setTheme: function(_theme) {
    this.currentTheme = _theme;
  },
  
/** Sets the default theme ( HDefaultTheme ) to be the active theme.
  **/
  restoreDefaultTheme: function() {
    this.setTheme( HDefaultThemeName );
  },
  
/** regexp: _variable_match
  * A regular expression to match the template evaluation syntax: #{stuff_to_evaluate}
  **/
  _variable_match: new RegExp(/#\{([^\}]*)\}/),
  
/** = Description
  * Evaluates the _variable_match regular expression for the string _markup.
  *
  * = Parameters
  * +_cssTmpl+:: The css template file to be evaluated. 
  *
  * = Returns
  * An evaluated CSS Template.
  **/
  _bindCSSVariables: function( _cssTmpl ) {
    while ( this._variable_match.test( _cssTmpl ) ) {
      _cssTmpl = _cssTmpl.replace(  this._variable_match, eval( RegExp.$1 )  );
    }
    return _cssTmpl;
  }
  
});
/*   Riassence Framework
 *   Copyright 2006 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
 ** Serves as a mixin class for classes that need to draw markup but don't
 ** inherit from the HView.
 **
 ** = Instance variables
 ** +markup+:: The markup from the component's HTML template.
***/
HMarkupView = HClass.extend({

/** = Description
  * Evaluates the pieces of code defined in the markup template marked with
  * syntax #{}.
  * Can't use } characters in the code though. 
  * --
  * This might need another look...
  * ++
  * Places the resulting markup in the instance variable.
  * 
  **/
  bindMarkupVariables: function() {
    
    var _markup = this.markup;
    
    while ( HMarkupView._assignment_match.test(_markup) ) {  
      _markup = _markup.replace( HMarkupView._assignment_match, this.evalMarkupVariable(RegExp.$1,true) );
    }
    while ( HMarkupView._variable_match.test(_markup) ) {  
      _markup = _markup.replace( HMarkupView._variable_match, this.evalMarkupVariable(RegExp.$1) );
    }
    
    this.markup = _markup;
    
    return this;
  },
/** = Description
  * Evaluates a string and throws an error into console.log 
  * if the string doesn't pass evaluation. 
  * If _isAssignment flag is set to true returns empty string. 
  * if _isAssignment is set to false will return string if it passes 
  * evaluation.
  *
  * = Parameters
  * +_strToEval+::      A String to evaluate.
  * 
  * +_isAssignment+::   Flag to indicate return the String upon passed 
  *                     evaluation(false) or return of an empty String(true).
  *
  * = Returns
  * +String+ 
  * 
  **/
  evalMarkupVariable: function(_strToEval,_isAssignment){
    try {
      var _ID     = this.elemId.toString(),
          _WIDTH  = this.rect.width,
          _HEIGHT = this.rect.height,
          _result = eval(_strToEval);
      if(_isAssignment){
        return '';
      }
      if(_result===undefined){
        return '';
      }
      else {
        return _result;
      }
    }
    catch(e) {
      console.log("Warning, the markup string '"+_strToEval+"' failed evaluation. Reason:"+e+' '+e.description);
      return '';
    }
  },
  
/** = Description
  * Sets or unsets the _cssClass into a DOM element that goes by the ID 
  * _elementId.
  * 
  * = Parameters
  * +_elementId+:: ID of the DOM element, or the element itself, to be 
  *                modified.
  * +_cssClass+::  Name of the CSS class to be added or removed.
  * +_setOn+::     Boolean value that tells should the CSS class be added or 
  *                removed.
  *
  * = Returns
  * +self+
  * 
  **/
  toggleCSSClass: function(_elementId, _cssClass, _setOn) {
    if(_elementId) {
      if (_setOn) {
        ELEM.addClassName(_elementId, _cssClass);
      }
      else {
        ELEM.removeClassName(_elementId, _cssClass);
      }
    }
    return this;
  }

},{
  _variable_match: new RegExp(/#\{([^\}]*)\}/),
  _assignment_match: new RegExp(/\$\{([^\}]*)\}/)
});

/*   Riassence Framework
 *   Copyright 2006 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** HMorphAnimation defines morphing animation.
  **
  ** HMorphAnimation is implemented by HView as an interface. 
  ** Its methods define the functionality to zoom from the current geometry to a 
  ** target geometry with animated intervals.
***/
HMorphAnimation = HClass.extend({
  
/** = Description
  * Moves the view smoothly into another location and/or size. The
  * onAnimationStart event on the view gets called when the animation starts.
  * 
  * = Parameters:
  * +_obj+::      An instance of <HPoint> or <HRect>, depending on the desired 
  *               animation result. When a point is passed, the view is moved in
  *               that position. When a rect is passed, the view can also be resized
  *               with or without moving to different coordinates.
  * 
  * +_duration+:: _optional_ The duration of the animation in milliseconds. The
  *               default duration is 500 ms.
  * 
  * +_fps+::      _optional_ The frame rate for the animation. The default fps is 50.
  *
  * = Returns:
  * +self+
  *   
  * = Usage:
  *  HView.extend({
  *    onAnimationEnd: function(){
  *      this.die();
  *    }
  *  }).nu(
  *    [ 0, 0, 300, 300 ],
  *    HApplication.nu()
  *  ).setStyle(
  *    'background-color', 'red'
  *  ).animateTo(
  *    HRect.nu( 300, 300, 500, 500 )
  *  );
  *   
  **/
  animateTo: function(_obj, _duration, _fps) {
    if(!this.drawn){
      return this;
    }
    // Redirect the method call to _animateTo(HRect).
    if(_obj instanceof HPoint) {
      var _rect = new HRect(_obj, _obj);
      _rect.setSize(this.rect.width, this.rect.height);
      this._animateTo(_rect, _duration);
    }
    else if(_obj instanceof HRect) {
      this._animateTo(_obj, _duration);
    }
    else {
      throw "Wrong argument type.";
    }
    return this;
  },
  
  
/** = Description
  * Stops the current animation for this view. If the view is not being
  * animated, this method has no effect.  The onAnimationEnd event on the view
  * gets called when the animation finishes (reaches the end position/size), but
  * onAnimationCancel gets called when this method is called while the animation
  * is still in action.
  *  
  */
  stopAnimation: function() {
    if (this._animateInterval) {
      // Stop the animation interval only if it has been set.
      window.clearInterval(this._animateInterval);
      this._animateInterval = null;
      
      // Update the rect after the new position and size have been reached.
      var _left   = parseInt(this.style('left'), 10),
          _top    = parseInt(this.style('top'), 10),
          _width  = parseInt(this.style('width'), 10),
          _height = parseInt(this.style('height'), 10);
      this.rect.set(_left, _top, _left + _width, _top + _height);
      this.drawRect();
      
      if (this._animationDone) {
        this.onAnimationEnd();
      }
      else {
        this.onAnimationCancel();
      }
      
    }
    return this;
  },
  
  
  // --Private method.++
  // --Starts the animation with the target _rect.++
  _animateTo: function(_rect, _duration, _fps) {
    
    if (null === _duration || undefined === _duration) {
      _duration = 500; // default duration is half second
    }
    if (null === _fps || undefined === _fps || _fps < 1) {
      _fps = 50; // default fps
    }
    
    if (!this._animateInterval) {
      
      this._animationDone = false;
      this.onAnimationStart();
      
      var _startTime = new Date().getTime();
      
      var _that = this;
      this._animateInterval = window.setInterval(
        function() {
          if(!_that){
            return;
          }
          _that._animateStep({
            startTime: _startTime,
            duration: _duration,
            // Linear transition effect.
            transition: function(t, b, c, d) { return c * t / d + b; },
            props: [{
              prop: 'left',
              from: _that.rect.left,
              to: _rect.left,
              unit: 'px'
            },{
              prop: 'top',
              from: _that.rect.top,
              to: _rect.top,
              unit: 'px'
            },{
              prop: 'width',
              from: _that.rect.width,
              to: _rect.width,
              unit: 'px'
            },{
              prop: 'height',
              from: _that.rect.height,
              to: _rect.height,
              unit: 'px'
            }]
          });
        }, Math.round(1000 / _fps)
      );
    }
    return this;
  },
  
  
  // --Private method.++
  // --Moves the view for one step. This gets called repeatedly when the animation++
  // --is happening.++
  _animateStep: function(_obj) {
    
    var _time = new Date().getTime(), i;
    if (_time < _obj.startTime + _obj.duration) {
      var _cTime = _time - _obj.startTime;
      
      // Handle all the defined properties.
      for (i = 0; i < _obj.props.length; i++) {
        var _from = _obj.props[i].from;
        var _to = _obj.props[i].to;
        
        if (_from !== _to) {
          // The value of the property at this time.
          var _propNow = _obj.transition(_cTime, _from, (_to - _from),
            _obj.duration);
          ELEM.setStyle(this.elemId,_obj.props[i].prop, _propNow + _obj.props[i].unit);
        }
      }
      
    } else {
      // Animation is done, clear the interval and finalize the animation.
      for (i = 0; i < _obj.props.length; i++) {
        ELEM.setStyle(this.elemId,_obj.props[i].prop,
          _obj.props[i].to + _obj.props[i].unit);
      }
      this._animationDone = true;
      this.stopAnimation();
    }
    return this;
  },
  
/** = Description
  * Extend the onAnimationStart method, if you want to do something special 
  * when this view starts animating.
  *
  * = Usage:
  *  HView.extend({
  *     onAnimationStart: function() {
  *       this.setStyle('background-color','green')
  *     }, 
  *     onAnimationEnd: function() {
  *       this.setStyle('background-color','grey')}
  *     }).nu(
  *       [0,0,300,300],
  *       HApplication.nu()
  *     ).setStyle(
  *       'background-color','blue'
  *     ).animateTo(
  *       HRect.nu(
  *         300,300,700,700
  *       )
  *     );
  *
  **/
  onAnimationStart: function() {
    
  },
  
/** Extend the onAnimationEnd method, if you want to do something special 
  * when an animation on this view is finished.
  **/
  onAnimationEnd: function() {
    
  },
  
/** Extend this method if functionality is desired upon cancellation of animation.
  **/
  onAnimationCancel: function() {
    
  }
});/*   Riassence Framework
 *   Copyright 2006 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** HView is the foundation class for all views. HView is useful for
  ** any type of view and control grouping. It is designed for easy extension
  ** and it's the foundation for HControl and all other controls.
  ** 
  ** The major differences between HView and HControl is that HView handles
  ** only visual representation and structurization. In addition to HView's
  ** features, HControl handles labels, values, events, states and such.
  ** However, HControl is more complex, so use HView instead whenever you don't
  ** need the additional features of HControl. HView implements the HMarkupView
  ** interface for template-related task.
  **
  ** = Usage
  **  var myAppInstance = HApplication.nu();
  **  var rect1 = [10, 10, 100, 100];
  **  var myViewInstance = HView.nu( rect1, myAppInstance );
  **  var myViewInstance.setStyle('background-color','#ffcc00');
  **  var rect2 = [10, 10, 70, 70];
  **  var mySubView1 = HView.nu( rect2, myViewIntance );
  **  var rect3 [20, 20, 50, 50];
  **  var mySubView2 = HView.nu( rect3, mySubView1 );
  **
***/

HView = HClass.extend({
  
/** Component specific theme path.
  **/
  themePath:   null,
  
/** True, if the component using absolute positioning.
  * False, if the component is using relative positioning.
  **/
  isAbsolute: true,
  
/** True, if the coordinates are right-aligned.
  * False, if the coordinates are left-aligned.
  * Uses flexRightOffset if true. Defined with 6-item arrays
  * for the _rect parameter of setRect or the constructor.
  * Can be set directly using the setFlexRight method.
  **/
  flexRight:  false,
  
/** True, if the coordinates are left-aligned.
  * False, if the coordinates are right-aligned.
  * Uses the X-coordinate of rect, if true.
  * Disabled using 6-item arrays with null x-coordinate
  * for the _rect parameter of setRect or the constructor.
  * Can be set directly using the setFlexLeft method.
  **/
  flexLeft:   true,
  
/** True, if the coordinates are top-aligned.
  * False, if the coordinates are bottom-aligned.
  * Uses the Y-coordinate of rect, if true.
  * Disabled using 6-item arrays with null x-coordinate
  * for the _rect parameter of setRect or the constructor.
  * Can be set directly using the setFlexTop method.
  **/
  flexTop:    true,
  
/** True, if the coordinates are bottom-aligned.
  * False, if the coordinates are top-aligned.
  * Uses flexBottomOffset if true. Defined with 6-item arrays
  * for the _rect parameter of setRect or the constructor.
  * Can be set directly using the setFlexRight method.
  **/
  flexBottom: false,
  
/** The amount of pixels to offset from the right edge when
  * flexRight is true. Defined with 6-item arrays
  * for the _rect parameter of setRect or the constructor.
  * Can be set directly using the setFlexRight method.
  **/
  flexRightOffset:  0,
  
/** The amount of pixels to offset from the bottom edge when
  * flexBottom is true.Defined with 6-item arrays
  * for the _rect parameter of setRect or the constructor.
  * Can be set directly using the setFlexBottom method.
  **/
  flexBottomOffset: 0,
  
/** The componentBehaviour array tells other classes what to
  * expect of the component's API and visual behaviour.
  * The first index is 'view' by default for all classes
  * inherited from HView.
  **/
  componentBehaviour: ['view'],
  
/** The drawn flag is false before the component is visually
  * drawn, it's true after it's drawn.
  **/
  drawn: false,
  
/** The theme the component is constructed with. By default,
  * uses the HThemeManager.currentTheme specified at the time
  * of construction.
  **/
  theme: null,
  
/** The preserveTheme flag prevents the view from being redrawn
  * if HThemeManager.currentTheme is changed after the view
  * has been drawn. Is true, if theme has been set.
  **/
  preserveTheme: false,
  
/** The optimizeWidthOnRefresh flag, when enabled, allows
  * automatic width calculation for components that support
  * that feature.
  **/
  optimizeWidthOnRefresh: true,
  
/** The parent is the +_parent+ supplied to the constructor.
  * This is a complete object reference to the parent's namespace.
  **/
  parent: null,
  
/** The parents is an array containing parent instances up to
  * the root controller level. The root controller is almost
  * always an instance of HApplication.
  **/
  parents: null,
  
/** The viewId is the unique ID (serial number) of this view.
  * This means the view can be looked up globally based on its
  * id by using the +HSystem.views+ array.
  **/
  viewId: null,
  
/** The appId is the unique ID (serial number) of the app process
  * acting as the root controller of the view tree of which this
  * view is a member.
  * This means the app can be looked up globally based on this
  * id by using the +HSystem.apps+ array.
  **/
  appId: null,
  
/** The app is the reference of the app process acting as
  * the root controller of the view tree of which this view is a
  * member.
  * This is a complete object reference to the app's namespace.
  **/
  app: null,
  
/** The views array contains a list of subviews of this view
  * by id. To access the object reference, use the +HSystem.views+
  * array with the id.
  **/
  views: null,
  
/** The viewsZOrder array contains a list of subviews ordered by
  * zIndex. To change the order, use the bringToFront,
  * sendToBack, bringForwards, sendBackwards, bringToFrontOf and
  * sentToBackOf methods.
  **/
  viewsZOrder: null,
  
/** The isHidden flog reflects the visibility of the view.
  **/
  isHidden: true,
  
/** The +HRect+ instance bound to +self+ using the +constructor+ or +setRect+.
  **/
  rect: null,
  
/** = Description
  * Constructs the logic part of a HView.
  * The view still needs to be drawn on screen. To do that, call draw after
  * subcomponents of the view are initialized.
  *
  * = Parameters
  * +_rect+::     An instance of +HRect+, defines the position and size of views.
  *               It can be also defined with an array, see below.
  * +_parent+::   The parent instance this instance will be contained within.
  *               A valid parent can be another HView compatible instance,
  *               an HApplication instance, a HControl or a similar extended
  *               HView instance. The origin of the +_rect+ is the same as the
  *               parent's offset. For HApplication instances, the web browser's
  *               window's left top corner is the origin.
  *
  * == The +_rect+ dimensions as arrays
  * Instead of an instance of +HRect+, dimensions can also be supplied as arrays.
  * The array length must be either 4 or 6. If the length is 4, the dimensions are
  * specified as follows: +[ x, y, width, height ]+. Note that this is different
  * from the construction parameters of +HRect+ that takes the coordinates as two
  * points, like: +( left, top, right, bottom )+.
  * Arrays with 6 items are a bit more complex (and powerful) as they can specify
  * the flexible offsets too.
  * 
  * === The array indexes for a +_rect+ configured as an 4-item array:
  * Always left/top aligned, all items must be specified.
  * Index::            Description
  * +0+::              The X-coordinate (measured from the parent's left edge)
  * +1+::              The Y-coordinate (measured from the parent's top edge)
  * +2+::              The width.
  * +3+::              The height.
  *
  * === The array indexes a +_rect+ configured as an 6-item array:
  * Can be any configuration of left/top/right/bottom alignment and supports
  * flexible widths. At least 4 items must be specified.
  * Index::            Description
  * +0+::              The left-aligned X-coordinate or +null+ if the view is
  *                    right-aligned and using a right-aligned X-coordinate at
  *                    index +4+ as well as the width specified at index +2+.
  * +1+::              The top-aligned Y-coordinate or +null+ if the view is
  *                    bottom-aligned and using a right-aligned X-coordinate at
  *                    index +5+ as well as the height specified at index +3+.
  * +2+::              The width, if only one X-coordinate specifies the
  *                    position (at indexes +0+ or +4+).
  *                    If both X-coordinates (at indexes +0+ and +4+) are
  *                    specified, the width can be specified with a +null+ for
  *                    automatic (flexible) width. If the width is specified,
  *                    it's used as the minimum width.
  * +3+::              The height, if only one Y-coordinate specifies the
  *                    position (at indexes +1+ or +5+).
  *                    If both Y-coordinates (at indexes +1+ and +5+) are
  *                    specified, the height can be specified with a +null+ for
  *                    automatic (flexible) height. if the height is specified,
  *                    it's used as the minimum height.
  * +4+::              The right-aligned X-coordinate or +null+ if the view is
  *                    left-aligned and using a left-aligned X-coordinate at
  *                    index +0+ as well as the width specified at index +2+.
  * +5+::              The bottom-aligned Y-coordinate or +null+ if the view is
  *                    top-aligned and using a top-aligned X-coordinate at
  *                    index +1+ as well as the height specified at index +3+.
  * == Usage examples of +_rect+:
  * Specified as two instances of +HPoint+,
  * x: 23, y: 75, width: 200, height: 100:
  *  HRect.nu( HPoint.nu( 23, 75 ), HPoint.nu( 223, 175 ) )
  *
  * The same as above, but without +HPoint+ instances:
  *  HRect.nu( 23, 75, 223, 175 )
  *
  * The same as above, but with an array as the constructor
  * parameter for +HRect+:
  *  HRect.nu( [ 23, 75, 223, 175 ] )
  *
  * The same as above, but with an array instead of a +HRect+ instance:
  *  [ 23, 75, 200, 100 ]
  *
  * The same as above, but with a 6-item array:
  *  [ 23, 75, 200, 100, null, null ]
  *
  * The same as above, but aligned to the right instead of left:
  *  [ null, 75, 200, 100, 23, null ]
  *
  * The same as above, but aligned to the right/bottom edges:
  *  [ null, null, 200, 100, 23, 75 ]
  *
  * The same as above, but aligned to the left/bottom edges:
  *  [ 23, null, 200, 100, null, 75 ]
  *
  * Flexible width (based on the parent's dimensions):
  *  [ 23, 75, null, 100, 23, null ]
  *
  * Flexible height (based on the parent's dimensions):
  *  [ 23, 75, 200, null, null, 75 ]
  *
  * Flexible width and height (based on the parent's dimensions):
  *  [ 23, 75, null, null, 23, 75 ]
  *
  * Flexible width and height, but limited to a minimum width
  * of 200 and a minimum height of 100 (based on the parent's dimensions):
  *  [ 23, 75, 200, 100, 23, 75 ]
  *
  **/
  constructor: function(_rect, _parent) {
    
    // Moved these to the top to ensure safe theming operation
    if(!this.theme){
      this.theme = HThemeManager.currentTheme;
      this.preserveTheme = false;
    }
    else {
      this.preserveTheme = true;
    }
    
    // adds the parentClass as a "super" object
    this.parent = _parent;
    
    this.viewId = this.parent.addView(this);
    // the parent addView method adds this.parents
    
    this.appId = this.parent.appId;
    this.app = HSystem.apps[this.appId];
    
    // subview-ids, index of HView-derived objects that are found in HSystem.views[viewId]
    this.views = [];
    
    // Subviews in Z order.
    this.viewsZOrder = [];
    
    // Keep the view (and its subviews) hidden until its drawn.
    this._createElement();
    
    // Set the geometry
    this.setRect(_rect);
    
    this._cachedLeft = _rect.left;
    this._cachedTop = _rect.top;
    
    // Additional DOM element bindings are saved into this array so they can be
    // deleted from the element manager when the view gets destroyed.
    this._domElementBindings = [];
    
    if(!this.isinherited) {
      this.draw();
      this.show();
    }
  },
  
/** = Description
  * When the +_flag+ is true, the view will be aligned to the right.
  * The +_px+ offset defines how many pixels from the parent's right
  * edge the right edge of this view will be. If both setFlexRight
  * and setFlexLeft are set, the width is flexible.
  * Use the constructor or setRect instead of calling this method
  * directly.
  *
  * = Parameters
  * +_flag+::    Boolean flag (true/false). Enables
  *              right-alignment when true.
  * +_px+::      The amount of pixels to offset from the right
  *              edge of the parent's right edge.
  *
  * = Returns
  * +self+
  **/
  setFlexRight: function(_flag,_px){
    if(_flag===undefined){_flag=true;}
    this.flexRight = _flag;
    if(_px===undefined){_px=0;}
    this.flexRightOffset = _px;
    return this;
  },
  
/** = Description
  * When the +_flag+ is true, the view will be aligned to the left (default).
  * The +_px+ offset defines how many pixels from the parent's left
  * edge the left edge of this view will be. If both setFlexLeft
  * and setFlexRight are set, the width is flexible.
  * Use the constructor or setRect instead of calling this method
  * directly.
  *
  * = Parameters
  * +_flag+::    Boolean flag (true/false). Enables
  *              left-alignment when true.
  * +_px+::      The amount of pixels to offset from the left
  *              edge of the parent's left edge.
  *
  * = Returns
  * +self+
  **/
  setFlexLeft: function(_flag,_px){
    if(_flag===undefined){_flag=true;}
    this.flexLeft = _flag;
    if((_px || _px === 0) && this.rect){
      this.rect.setLeft(_px);
    }
    return this;
  },
  
/** = Description
  * When the +_flag+ is true, the view will be aligned to the top (default).
  * The +_px+ offset defines how many pixels from the parent's top
  * edge the top edge of this view will be. If both setFlexTop
  * and setFlexBottom are set, the height is flexible.
  * Use the constructor or setRect instead of calling this method
  * directly.
  *
  * = Parameters
  * +_flag+::    Boolean flag (true/false). Enables
  *              top-alignment when true.
  * +_px+::      The amount of pixels to offset from the top
  *              edge of the parent's top edge.
  *
  * = Returns
  * +self+
  **/
  setFlexTop: function(_flag,_px){
    if(_flag===undefined){_flag=true;}
    this.flexTop = _flag;
    if((_px || _px === 0) && this.rect){
      this.rect.setTop(_px);
    }
    return this;
  },
  
/** = Description
  * When the +_flag+ is true, the view will be aligned to the bottom.
  * The +_px+ offset defines how many pixels from the parent's bottom
  * edge the bottom edge of this view will be. If both setFlexBottom
  * and setFlexTop are set, the height is flexible.
  * Use the constructor or setRect instead of calling this method
  * directly.
  *
  * = Parameters
  * +_flag+::    Boolean flag (true/false). Enables
  *              bottom-alignment when true.
  * +_px+::      The amount of pixels to offset from the bottom
  *              edge of the parent's bottom edge.
  *
  * = Returns
  * +self+
  **/
  setFlexBottom: function(_flag,_px){
    if(_flag===undefined){_flag=true;}
    this.flexBottom = _flag;
    if(_px===undefined){_px=0;}
    this.flexBottomOffset = _px;
    return this;
  },
  
/** = Description
  * The +_flag+ enables or disables the absolute positioning mode.
  * (It's enabled by default). If absolute positioning mode is 
  * off, the coordinate system has little or no effect.
  *
  * = Parameters
  * +_flag+::    Boolean flag (true/false). Enables
  *              absolute positioning when true.
  *              Enables relative positioning when false.
  *
  * = Returns
  * +self+
  **/
  setAbsolute: function(_flag){
    if(_flag===undefined){_flag=true;}
    this.isAbsolute = _flag;
    return this;
  },
  
/** = Description
  * The +_flag+ enables or disables the relative positioning mode.
  * (It's disabled by default). If relative positioning mode is 
  * on, the coordinate system has little or no effect.
  *
  * = Parameters
  * +_flag+::    Boolean flag (true/false). Enables
  *              absolute relative when true.
  *              Enables absolute positioning when false.
  *
  * = Returns
  * +self+
  **/
  setRelative: function(_flag){
    if(_flag===undefined){_flag=true;}
    this.isAbsolute = (!_flag);
    return this;
  },
  
/** = Description
  * Used by html theme templates to get the theme-specific full image path.
  *
  * = Returns
  * The full path of the theme-specific gfx path as a string.
  **/
  getThemeGfxPath: function() {
    var _themeName;
    if( this.preserveTheme ){
      _themeName = this.theme;
    } else {
      _themeName = HThemeManager.currentTheme;
    }
    return HThemeManager._componentGfxPath( _themeName,  this.componentName, this.themePath );
  },
  
/** = Description
  * Used by html theme templates to get the theme-specific full path
  * of the _fileName given.
  *
  * = Returns
  * The full path of the file.
  **/
  getThemeGfxFile: function( _fileName ) {
    if( this.preserveTheme ){
      _themeName = this.theme;
    } else {
      _themeName = HThemeManager.currentTheme;
    }
    return HThemeManager._componentGfxFile( _themeName,  this.componentName, this.themePath, _fileName );
  },
  
/** --
  * = Description
  * The _makeElem method does the ELEM.make call to create
  * the <div> element of the component. It assigns the elemId.
  * It's a separate method to ease creating component that require
  * other element types.
  * ++
  **/
  _makeElem: function(_parentElemId){
    this.elemId = ELEM.make(_parentElemId,'div');
  },
  
/** --
  * = Description
  * The _setCSS method does the initial styling of the element.
  * It's a separate method to ease creating component that require
  * other initial styles.
  * ++
  **/
  _setCSS: function(_additional){
    var _cssStyle = 'display:none;overflow:hidden;visibility:hidden;';
    if(this.isAbsolute){
      _cssStyle += 'position:absolute;';
    } else {
      _cssStyle += 'position:relative;';
    }
    _cssStyle += _additional;
    ELEM.setCSS(this.elemId,_cssStyle);
  },
  
/** --
  * = Description
  * The _getParentElemId method returns the ELEM ID of the parent.
  * ++
  **/
  _getParentElemId: function(){
    var _parentElemId;
    // if the parent does not have an element:
    if(this.parent.elemId === undefined) {
      _parentElemId = 0;
    }
    // if a subview element is defined in the template, use it:
    else if(this.parent.markupElemIds&&this.parent.markupElemIds['subview']){
      _parentElemId = this.parent.markupElemIds['subview'];
    }
    // otherwise, use main elemId
    else {
      _parentElemId = this.parent.elemId;
    }
    return _parentElemId;
  },
  
/** --
  * = Description
  * The _createElement method calls the methods required to initialize the
  * main DOM element of the view.
  * ++
  **/
  _createElement: function() {
    if(!this.elemId) {
      
      this._makeElem(this._getParentElemId());
      this._setCSS('');
      
      // Theme name == CSS class name
      if(this.preserveTheme){
        ELEM.addClassName( this.elemId, this.theme );
      }
      else {
        ELEM.addClassName( this.elemId, HThemeManager.currentTheme );
      }
    }
  },
  
/** = Description
  * The +drawRect+ method refreshes the dimensions of the view.
  * It needs to be called to affect changes in the rect.
  * It enables the drawn flag.
  *
  * = Returns
  * +self+
  *
  **/
  drawRect: function() {
    if (this.parent && this.rect.isValid) {
      var _this = this,
          _elemId = _this.elemId,
          _styl = ELEM.setStyle,
          _rect = _this.rect;
    
      _styl( _elemId, 'left', _this.flexLeft?(_rect.left+'px'):'auto', true);
      _styl( _elemId, 'top', _this.flexTop?(_rect.top+'px'):'auto', true);
      _styl( _elemId, 'right', _this.flexRight?(_this.flexRightOffset+'px'):'auto', true);
      _styl( _elemId, 'bottom', _this.flexBottom?(_this.flexBottomOffset+'px'):'auto', true);
      _styl( _elemId, 'width', (_this.flexLeft&&_this.flexRight)?'auto':(_rect.width+'px'), true);
      _styl( _elemId, 'height', (_this.flexTop&&_this.flexBottom)?'auto':(_rect.height+'px'), true);
    
      if(_this.flexLeft&&_this.flexRight){
        _styl( _elemId, 'min-width', _rect.width+'px', true);
      }
      if(_this.flexTop&&_this.flexBottom){
        _styl( _elemId, 'min-height', _rect.height+'px', true);
      }
    
      // Show the rectangle once it gets created, unless visibility was set to
      // hidden in the constructor.
      if(undefined === _this.isHidden || _this.isHidden === false) {
        _styl( _elemId, 'visibility', 'inherit', true);
      }
    
      _styl( _elemId, 'display', 'block', true);
    
      _this._updateZIndex();
    
      if (_this._cachedLeft !== _rect.left || _this._cachedTop !== _rect.top) {
        _this.invalidatePositionCache();
        _this._cachedLeft = _rect.left;
        _this._cachedTop = _rect.top;
      }
    
      _this.drawn = true;
    }
    return this;
  },
  
/** --
  * This method updates the z-index property of the children of self.
  * It's essentially a wrapper for HSystem.updateZIndexOfChildren passed
  * with the viewId of self.
  * ++
  **/
  _updateZIndex: function() {
    HSystem.updateZIndexOfChildren(this.viewId);
  },
  
/** --
  * This method updates the z-index property of the siblings of self.
  * It's essentially a wrapper for HSystem.updateZIndexOfChildren passed
  * with the parent's viewId of self.
  * ++
  **/
  _updateZIndexAllSiblings: function() {
    HSystem.updateZIndexOfChildren(this.parent.viewId);
  },
  
/** = Description
  * The higher level draw wrapper for drawRect, drawMarkup and drawSubviews.
  * Finally calls refresh.
  * 
  * = Returns
  * +self+
  *
  **/
  draw: function() {
    var _isDrawn = this.drawn;
    this.drawRect();
    if(!_isDrawn){
      if(this['componentName']!==undefined){
        this.drawMarkup();
      }
      this.drawSubviews();
    }
    this.refresh();
    return this;
  },
  
/** = Description
  * Called once, when the layout of the view is initially drawn.
  * Doesn't do anything by itself, but provides an extension point for making
  * subviews.
  *
  **/
  drawSubviews: function(){
  },
  
/** --
  * Loads the markup from theme manager. If this.preserveTheme is set to true,
  * the this.theme is used for loading the markup. Otherwise the currently
  * active theme is used.
  * ++
  **/
  _loadMarkup: function() {
    var _themeName, _markup;
    if (this.preserveTheme) {
      _themeName = this.theme;
    }
    else {
      _themeName = HThemeManager.currentTheme;
    }
    _markup = HThemeManager.getMarkup( _themeName, this.componentName, this.themePath );
    if(_markup === false){
      console.log('Warning: Markup template for "'+this.componentName+'" using theme "'+_themeName+'" not loaded.');
    }
    this.markup = _markup;
    return (_markup !== false);
  },
  
/** = Description
  * Replaces the contents of the view's DOM element with html from the theme specific html file.
  *
  * = Returns
  * +self+
  **/
  markupElemNames: ['bg', 'label', 'state', 'control', 'value', 'subview'],
  drawMarkup: function() {
    ELEM.setStyle(this.elemId, 'display', 'none', true);
    
    // continue processing from here on:
    var _markupStatus = this._loadMarkup();
    
    this.bindMarkupVariables();
    ELEM.setHTML(this.elemId, this.markup);
    
    this.markupElemIds = {};
    for(var i=0; i < this.markupElemNames.length; i++ ) {
      var _partName = this.markupElemNames[ i ],
          _elemName = _partName + this.elemId,
          _htmlIdMatch = ' id="' + _elemName + '"';
      if( this.markup.indexOf( _htmlIdMatch ) !== -1 ) {
        this.markupElemIds[ _partName ] = this.bindDomElement( _elemName );
      }
    }
    
    ELEM.setStyle(this.elemId, 'display', 'block' );
    return this;
  },
  
/** = Description
  * Replaces the contents of the view's DOM element with custom html.
  *
  * = Parameters
  * +_html+:: The HTML (string-formatted) to replace the content with.
  *
  * = Returns
  * +self+
  *
  **/
  setHTML: function( _html ) {
    ELEM.setHTML( this.elemId, _html );
    return this;
  },
  
/** = Description
  *
  * This method should be extended in order to redraw only specific parts. The
  * base implementation calls optimizeWidth when optimizeWidthOnRefresh is set
  * to true.
  *
  * = Returns
  * +self+
  *
  **/
  refresh: function() {
    if(this.drawn) {
      // this.drawn is checked here so the rectangle doesn't get drawn by the
      // constructor when setRect() is initially called.
      this.drawRect();
    }
    if(this.optimizeWidthOnRefresh) {
      this.optimizeWidth();
    }
    return this;
  },

/** = Description
  * Replaces the rect of the component with a new HRect instance and
  * then refreshes the display.
  *
  * = Parameters
  * +_rect+:: The new HRect instance to replace the old rect instance with.
  * +_rect+:: Array format, see HView#constructor for further details.
  *
  * = Returns
  * +self+
  *
  **/
  setRect: function(_rect) {
    if (this.rect) {
      this.rect.release(this);
    }
    if(_rect instanceof Array){
      var _arrLen = _rect.length,
          _throwPrefix = 'HView.setRect: If the HRect instance is replaced by an array, ';
      if((_arrLen === 4) || (_arrLen === 6)){
        var _leftOffset   = _rect[0],
            _topOffset    = _rect[1],
            _width        = _rect[2],
            _height       = _rect[3],
            _rightOffset  = ((_arrLen === 6)?_rect[4]:null),
            _bottomOffset = ((_arrLen === 6)?_rect[5]:null),
            _validLeftOffset    = (typeof _leftOffset    === 'number'),
            _validTopOffset     = (typeof _topOffset     === 'number'),
            _validRightOffset   = (typeof _rightOffset   === 'number'),
            _validBottomOffset  = (typeof _bottomOffset  === 'number'),
            _validWidth   = (typeof _width   === 'number'),
            _validHeight  = (typeof _height  === 'number'),
            _right,
            _bottom;
        
        if( (!_validLeftOffset && !_validRightOffset) ||
            (!_validTopOffset && !_validBottomOffset) ){
          console.log(_throwPrefix + '(left or top) and (top or bottom) must be specified.');
        }
        else if( (!_validWidth   && !(_validLeftOffset && _validRightOffset)) ||
                 (!_validHeight  && !(_validTopOffset  && _validBottomOffset)) ){
          console.log(_throwPrefix + 'the (height or width) must be specified unless both (left and top) or (top and bottom) are specified.');
        }
        
        this.setFlexLeft(_validLeftOffset,_leftOffset);
        this.setFlexTop(_validTopOffset,_topOffset);
        this.setFlexRight(_validRightOffset,_rightOffset);
        this.setFlexBottom(_validBottomOffset,_bottomOffset);
        
        // default, makes a correct rect
        if(_validLeftOffset && _validWidth && !_validRightOffset){
          _right = _leftOffset + _width;
        }
        // can't be entirely correct rect unless parent size is calculated
        else if(!_validLeftOffset && _validWidth && _validRightOffset){
          _leftOffset = 0;
          _right = _width;
        }
        // can't be entirely correct rect unless parent size is calculated
        else if(_validLeftOffset && !_validWidth && _validRightOffset){
          _right = _leftOffset + _rightOffset;
        }
        
        // use minimum width based on the height information given
        else if(_validLeftOffset && _validWidth && _validRightOffset){
          _right = _leftOffset + _width;
        }
        
        // default, makes a correct rect
        if(_validTopOffset && _validHeight && !_validBottomOffset){
          _bottom = _topOffset + _height;
        }
        // can't be entirely correct rect unless parent size is calculated
        else if(!_validTopOffset && _validHeight && _validBottomOffset){
          _topOffset = 0;
          _bottom = _height;
        }
        // can't be entirely correct rect unless parent size is calculated
        else if(_validTopOffset && !_validHeight && _validBottomOffset){
          _bottom = _topOffset + _bottomOffset;
        }
        
        // use minimum height based on the height information given
        else if(_validTopOffset && _validHeight && _validBottomOffset){
          _bottom = _topOffset + _height;
        }
        
        this.rect = HRect.nu(_leftOffset,_topOffset,_right,_bottom);
      }
      else {
        console.log(_throwPrefix + 'the length has to be either 4 or 6.');
      }
    }
    else {
      this.rect = _rect;
    }
    this.rect.bind(this);
    this.refresh();
    return this;
  },
  
/** = Description
  * Sets any arbitary style of the main DOM element of the component.
  * Utilizes Element Manager's drawing queue/cache to perform the action.
  *
  * = Parameters
  * +_name+::          The style name (css syntax, eg. 'background-color')
  * +_value+::         The style value (css syntax, eg. 'rgb(255,0,0)')
  * +_cacheOverride+:: Cache override flag.
  *
  * = Returns
  * +self+
  *
  **/
  setStyle: function(_name, _value, _cacheOverride) {
    if (this.elemId) {
      ELEM.setStyle(this.elemId, _name, _value, _cacheOverride);
    }
    return this;
  },

/** = Description
  * Returns a style of the main DOM element of the component.
  * Utilizes +ELEM+ cache to perform the action.
  *
  * = Parameters
  * +_name+:: The style name (css syntax, eg. 'background-color')
  *
  * = Returns
  * The style property value (css syntax, eg. 'rgb(255,0,0)')
  *
  **/
  style: function(_name) {
    if (this.elemId) {
      return ELEM.getStyle(this.elemId, _name);
    }
    return '';
  },
  
/** = Description
  * Sets a style for a specified markup element that has been bound to this
  * view.
  *
  * = Parameters
  * +_partName+:: The identifier of the markup element.
  * +_name+::     The style name
  * +_value+::    The style value
  *
  * = Returns
  * +self+
  *
  **/
  setStyleOfPart: function(_partName, _name, _value, _cacheOverride) {
    if (!this.markupElemIds[_partName]) {
      console.log('Warning, setStyleOfPart: partName "'+_partName+'" does not exist for viewId '+this.viewId+'.');
    }
    else {
      ELEM.setStyle(this.markupElemIds[_partName], _name, _value, _cacheOverride);
    }
    return this;
  },
  
/** = Description
  * Returns a style of a specified markup element that has been bound to this
  * view.
  *
  * = Parameters
  * +_partName+::  The identifier of the markup element.
  * +_name+::      The style name
  *
  * = Returns
  * The style of a specified markup element.
  *
  **/
  styleOfPart: function(_partName, _name) {
    if (!this.markupElemIds[_partName]) {
      console.log('Warning, styleOfPart: partName "'+_partName+'" does not exist for viewId '+this.viewId+'.');
      return '';
    }
    return ELEM.getStyle(this.markupElemIds[_partName], _name);
  },
  
/** = Description
  * Sets a style of a specified markup element that has been bound to this
  * view.
  *
  * = Parameters
  * +_partName+::  The identifier of the markup element.
  * +_value+::     Value for markup element.
  *
  * = Returns
  * +self+
  *
  **/
  setMarkupOfPart: function( _partName, _value ) {
    if (!this.markupElemIds[_partName]) {
      console.log('Warning, setMarkupOfPart: partName "'+_partName+'" does not exist for viewId '+this.viewId+'.');
    }
    else {
      ELEM.setHTML( this.markupElemIds[_partName], _value );
    }
    return this;
  },
  
/** = Description
  * Returns a style of a specified markup element that has been bound to this
  * view.
  *
  * = Parameters
  * +_partName+::  The identifier of the markup element.
  *
  * = Returns
  * The style of a specified markup element.
  *
  **/
  markupOfPart: function(_partName) {
    if (!this.markupElemIds[_partName]) {
      console.log('Warning, markupOfPart: partName "'+_partName+'" does not exist for viewId '+this.viewId+'.');
      return '';
    }
    return ELEM.getHTML(this.markupElemIds[_partName]);
  },

/** = Description
  * Hides the component's main DOM element (and its children).
  *
  * = Returns
  * +self+
  *
  **/
  hide: function() {
    if(!this.isHidden) {
      var _setStyl = ELEM.setStyle,
          _elemId  = this.elemId;
      _setStyl(_elemId,'visibility', 'hidden');
      _setStyl(_elemId,'display', 'none');
      this.isHidden = true;
    }
    return this;
  },
  
/** = Description
  * Restores the visibility of the component's main DOM element (and its children).
  *
  * = Return
  * +self+
  *
  **/
  show: function() {
    if(this.isHidden) {
      var _setStyl = ELEM.setStyle,
          _elemId  = this.elemId;
      _setStyl(_elemId,'visibility', 'inherit');
      _setStyl(_elemId,'display', 'block');
      this.isHidden = false;
    }
    return this;
  },
  
/** = Description
  * Toggles between hide and show.
  *
  * = Returns
  * +self+
  *
  **/
  toggle: function() {
    if(this.isHidden) {
      this.show();
    } else {
      this.hide();
    }
    return this;
  },
  
/** = Description
  * Call this if you need to remove a component from its parent's views array without
  * destroying the DOM element itself, making it in effect a view without parent.
  * Useful, for example, for moving a view from one parent component to another.
  *
  * = Returns
  * +self+
  *
  **/
  remove: function() {
    if( this.parent ) {
      
      var _viewZIdx = this.parent.viewsZOrder.indexOf(this.viewId),
          _viewPIdx = this.parent.views.indexOf(this.viewId);
      
      this.parent.views.splice(_viewPIdx,1);
      HSystem.delView(this.viewId);
      this.parent.viewsZOrder.splice( _viewZIdx, 1 );
      var _sysUpdateZIndexOfChildrenBufferIndex = HSystem._updateZIndexOfChildrenBuffer.indexOf( this.viewId );
      if(_sysUpdateZIndexOfChildrenBufferIndex !== -1){
        HSystem._updateZIndexOfChildrenBuffer.splice( _sysUpdateZIndexOfChildrenBufferIndex, 1 );
      }
      
      this._updateZIndexAllSiblings();
      this.parent  = null;
      this.parents = [];
    }
    return this;
  },
  
/** = Description
  * Deletes the component and all its children.
  * Should normally be called from the parent.
  *
  **/
  die: function() {
    // hide self, makes destruction seem faster
    this.hide();
    this.drawn = false;
    this.stopAnimation();
    // Delete the children first.
    var _childViewId, i;
    while (this.views.length !== 0) {
      _childViewId = this.views[0];
      this.destroyView(_childViewId);
    }
    // Remove this object's bindings, except the DOM element.
    this.remove();
    // Remove the DOM element bindings.
    for ( i = 0; i < this._domElementBindings.length; i++) {
      ELEM.del(this._domElementBindings[i]);
    }
    this._domElementBindings = [];
    
    
    // Remove the DOM object itself
    ELEM.del(this.elemId);
    
    this.rect = null;
    var _this = this;
    for( i in _this ){
      _this[i] = null;
      delete _this[i];
    }
  },
  
/** Recursive idle poller. Should be extended if functionality is desired.
  **/
  onIdle: function() {
    for(var i = 0; i < this.views.length; i++) {
      HSystem.views[this.views[i]].onIdle();
    }
  },
  
/** Used by addView to build a parents array of parent classes.
  **/
  buildParents: function(_viewId){
    var _view = HSystem.views[_viewId];
    _view.parent = this;
    _view.parents = [];
    for(var _parentNum = 0; _parentNum < this.parents.length; _parentNum++) {
      _view.parents.push(this.parents[_parentNum]);
    }
    _view.parents.push(this);
  },
  
/** = Description
  * Adds a sub-view/component to the view. Called from inside the 
  * HView#constructor and should be automatic for all components that accept 
  * the 'parent' parameter, usually the second argument, after the HRect. May 
  * also be used to attach a freely floating component (removed with remove) 
  * to another component.
  *
  * = Parameter
  * +_view+:: Usually this inside HView derivate components.
  *
  * = Returns
  * The view id.
  *
  **/
  addView: function(_view) {
    var _viewId = HSystem.addView(_view);
    this.views.push(_viewId);
    
    this.buildParents(_viewId);
    this.viewsZOrder.push(_viewId);
    
    return _viewId;
  },
  
/** = Description
  * Call this if you need to remove a child view from this view without
  * destroying its element, making it in effect a view without parent.
  * Useful, for example, for moving a view from one parent component to another.
  *
  * = Parameters
  * +_viewId+:: The parent-specific view id. Actually an array index.
  *
  * = Returns
  * +self+
  *
  **/
  removeView: function(_viewId) {
    HSystem.views[_viewId].remove();
    return this;
  },
  
/** = Description
  * Call this if you need to remove a child view from this view, destroying its
  * child elements recursively and removing all DOM elements too.
  *
  * = Parameters
  * +_viewId+::  The parent-specific view id. Actually an array index.
  *
  * = Returns
  * +self+
  **/
  destroyView: function(_viewId) {
    HSystem.views[_viewId].die();
    return this;
  },
  
/** = Description
  * Returns bounds rectangle that defines the size and coordinate system
  * of the component. This should be identical to the rectangle used in
  * constructing the object, unless it has been changed after construction.
  *
  * = Returns
  * A new <HRect> instance with identical values to this component's rect.
  *
  **/
  bounds: function() {
    // Could be cached.
    var _bounds = new HRect(this.rect);
    
    _bounds.right -= _bounds.left;
    _bounds.left = 0;
    _bounds.bottom -= _bounds.top;
    _bounds.top = 0;
    
    return _bounds;
  },
  
  
/** = Description
  * This method resizes the view, without moving its left and top sides.
  * It adds horizontal coordinate units to the width and vertical units to
  * the height of the view.
  * Since a View's frame rectangle must be aligned on screen pixels, only
  * integral values should be passed to this method. Values with
  * fractional components will be rounded to the nearest whole integer.
  * If the View is attached to a window, this method causes its parent view
  * to be updated, so the View is immediately displayed in its new size. If it
  * doesn't have a parent or isn't attached to a window, this method
  * merely alter its frame and bounds rectangle.
  *
  * = Parameters
  * +_horizonal+:: Horizonal units to add to the width (negative units subtract)
  * +_vertical+::  Vertical units to add to the height (negative units subtract)
  *
  * = Returns
  * +self+
  *
  **/
  resizeBy: function(_horizontal, _vertical) {
    var _rect = this.rect;
    _rect.right += _horizontal;
    _rect.bottom += _vertical;
    _rect.updateSecondaryValues();
    this.drawRect();
    return this;
  },

/** = Description
  * This method makes the view width units wide
  * and height units high. This method adjust the right and bottom
  * components of the frame rectangle accordingly.
  * Since a View's frame rectangle must be aligned on screen pixels, only
  * integral values should be passed to this method. Values with
  * fractional components will be rounded to the nearest whole integer.
  * If the View is attached to a window, this method causes its parent view
  * to be updated, so the View is immediately displayed in its new size. If it
  * doesn't have a parent or isn't attached to a window, this method
  * merely alter its frame and bounds rectangle.
  *
  * = Parameters
  * +_width+::  The new width of the view.
  * +_height+:: The new height of the view.
  *
  * = Returns
  * +self+
  *
  **/
  resizeTo: function(_width, _height) {
    var _rect = this.rect;
    _rect.right = _rect.left + _width;
    _rect.bottom = _rect.top + _height;
    _rect.updateSecondaryValues();
    this.drawRect();
    return this;
  },

/** = Descripion
  * This method moves the view to a new coordinate. It adjusts the 
  * left and top components of the frame rectangle accordingly.
  * Since a View's frame rectangle must be aligned on screen pixels, only
  * integral values should be passed to this method. Values with
  * fractional components will be rounded to the nearest whole integer.
  * If the View is attached to a window, this method causes its parent view
  * to be updated, so the View is immediately displayed in its new size. If it
  * doesn't have a parent or isn't attached to a window, this method
  * merely alter its frame and bounds rectangle.
  *
  * = Parameters
  * +_x+:: The new x-coordinate of the view.
  * +_y+:: The new y-coordinate of the view.
  *
  * +_point+:: The new coordinate point of the view.
  *
  * = Returns
  * +self+
  *
  **/
  offsetTo: function() {
    this.rect.offsetTo.apply(this.rect, arguments);
    this.drawRect();
    return this;
  },
  
/** = Description
  * Alias method for offsetTo.
  * 
  * = Returns
  * +self+
  *
  **/
  moveTo: function() {
    this.offsetTo.apply(this, arguments);
    return this;
  },
  
/** = Description
  * This method re-positions the view without changing its size.
  * It adds horizontal coordinate units to the x coordinate and vertical
  * units to the y coordinate of the view.
  * Since a View's frame rectangle must be aligned on screen pixels, only
  * integral values should be passed to this method. Values with
  * fractional components will be rounded to the nearest whole integer.
  * If the View is attached to a window, this method causes its parent view
  * to be updated, so the View is immediately displayed in its new size. If it
  * doesn't have a parent or isn't attached to a window, this method
  * merely alter its frame and bounds rectangle.
  *
  * = Parameters
  * +_horizonal+::  Horizonal units to change the x coordinate (negative units subtract)
  * +_vertical+::   Vertical units to add to change the y coordinate (negative units subtract)
  *
  * = Returns
  * +self+
  *
  **/
  offsetBy: function(_horizontal, _vertical) {
    this.rect.offsetBy(_horizontal, _vertical);
    this.drawRect();
    return this;
  },
  
/** = Description
  * Alias method for offsetBy.
  *
  * = Returns
  * +self+
  *
  **/
  moveBy: function() {
    this.offsetBy.apply(this, arguments);
    return this;
  },

/** = Description
  * Brings the view to the front by changing its Z-Index.
  *
  * = Returns
  * +self+
  *
  **/
  bringToFront: function() {
    if (this.parent) {
      var _index = this.zIndex();
      this.parent.viewsZOrder.splice(_index, 1);
      this.parent.viewsZOrder.push(this.viewId);
      this._updateZIndexAllSiblings();
    }
    return this;
  },
  
/** = Description
  * Brings itself to the front of the given view by changing its Z-Index.
  * Only works on sibling views.
  *
  * = Parameters
  * +_view+::  The view to bring to the front of.
  *
  * = Returns
  * +self+
  *
  **/
  bringToFrontOf: function(_view){
    if(this.parent.viewId === _view.parent.viewId){
      this.parent.viewsZOrder.splice( this.zIndex(), 1 ); // removes selfs index from the array
      this.parent.viewsZOrder.splice( _view.zIndex()+1, 0, this.viewId); // sets itself in front of to _view
      this._updateZIndexAllSiblings();
    }
    return this;
  },
  
/** = Description
  * Sends itself to the back of the given view by changing its Z-Index.
  * Only works on sibling views.
  *
  * = Parameters
  * +_view+::  The view to send to the back of.
  *
  * = Returns
  * +self+
  *
  **/
  sendToBackOf: function(_view){
    if(this.parent.viewId === _view.parent.viewId){
      this.parent.viewsZOrder.splice( this.zIndex(), 1 ); // removes selfs index from the array
      this.parent.viewsZOrder.splice( _view.zIndex(), 0, this.viewId); // sets itself in back of to _view
      this._updateZIndexAllSiblings();
    }
    return this;
  },
  
/** = Description
  * Sends itself one step backward by changing its Z-Index.
  *
  * = Returns
  * +self+
  *
  **/
  sendBackward: function(){
    var _index = this.zIndex();
    if(_index!==0){
      this.parent.viewsZOrder.splice( _index, 1 ); // removes selfs index from the array
      this.parent.viewsZOrder.splice( _index-1, 0, this.viewId); // moves selfs position to one step less than where it was
      this._updateZIndexAllSiblings();
    }
    return this;
  },
  
/** = Description
  * Brings itself one step forward by changing its Z-Index.
  *
  * = Returns
  * +self+
  *
  **/
  bringForward: function(){
    var _index = this.zIndex();
    if(_index!==this.parent.viewsZOrder.length-1){
      this.parent.viewsZOrder.splice( _index, 1 ); // removes selfs index from the array
      this.parent.viewsZOrder.splice( _index+1, 0, this.viewId); // moves selfs position to one step more than it was
      this._updateZIndexAllSiblings();
    }
    return this;
  },
  

/** = Description
  * Sends the view to the back by changing its Z-Index.
  *
  * = Returns
  * +self+
  *
  **/
  sendToBack: function() {
    if (this.parent) {
      var _index = this.zIndex();
      this.parent.viewsZOrder.splice(_index, 1); // removes this index from the arr
      this.parent.viewsZOrder.splice(0, 0, this.viewId); // unshifts viewId
      this._updateZIndexAllSiblings();
    }
    return this;
  },

/** = Description
  * Use this method to get the Z-Index of itself.
  *
  * = Returns
  * The current Z-Index value.
  *
  **/
  zIndex: function() {
    if (!this.parent) {
      return -1;
    }
    // Returns the z-order of this item as seen by the parent.
    return this.parent.viewsZOrder.indexOf(this.viewId);
  },
  
/** = Description
  * Measures the characters encoded in length bytes of the string - or,
  * if no length is specified, the entire string up to the null character,
  * '0', which terminates it. The return value totals the width of all the
  * characters in coordinate units; it's the length of the baseline required
  * to draw the string.
  *
  * = Parameters
  * +_string+::   The string to measure.
  * +_length+::   Optional, How many characters to count.
  * +_elemId+::   Optional, The element ID where the temporary string is created
  *               in.
  * +_wrap+::     Optional boolean value, wrap whitespaces?
  * +_extraCss+:: Optional, extra css to add.
  *
  * = Returns
  * The width in pixels required to draw a string in the font.
  *
  **/
  stringSize: function(_string, _length, _elemId, _wrap, _extraCss) {
    if (_length || _length === 0) {
      _string = _string.substring(0, _length);
    }
    if (!_elemId && _elemId !== 0) {
      _elemId = 0; //this.elemId;
    }
    if (!_extraCss) {
      _extraCss = '';
    }
    if (!_wrap){
      _extraCss += 'white-space:nowrap;';
    }
    
    var _stringElem = ELEM.make(_elemId);
    ELEM.setCSS(_stringElem, "visibility:hidden;position:absolute;"+_extraCss);
    ELEM.setHTML(_stringElem, _string);
    ELEM.flushLoop();
    var _visibleSize=ELEM.getVisibleSize(_stringElem);
    ELEM.del(_stringElem);
    return _visibleSize;
  },
  
/** Returns the string width
  **/
  stringWidth: function(_string, _length, _elemId, _extraCss){
    return this.stringSize(_string, _length, _elemId, false, _extraCss)[0];
  },
  
  /** Returns the string height.
    **/
  stringHeight: function(_string, _length, _elemId, _extraCss){
    return this.stringSize(_string, _length, _elemId, true, _extraCss)[1];
  },
  
/** Returns the X coordinate that has the scrolled position calculated.
  **/
  pageX: function() {
    var _x = 0,
        _elem = this;
    while(_elem) {
      if(_elem.elemId && _elem.rect) {
        _x += ELEM.get(_elem.elemId).offsetLeft;
        _x -= ELEM.get(_elem.elemId).scrollLeft;
      }
      if(_elem.markupElemIds&&_elem.markupElemIds.subview){
        _x += ELEM.get(_elem.markupElemIds.subview).offsetLeft;
        _x -= ELEM.get(_elem.markupElemIds.subview).scrollLeft;
      }
      _elem = _elem.parent;
    }
    return _x;
  },
  
/** Returns the Y coordinate that has the scrolled position calculated.
  **/
  pageY: function() {
    var _y = 0,
        _elem = this;
    while(_elem) {
      if(_elem.elemId && _elem.rect) {
        _y += ELEM.get(_elem.elemId).offsetTop;
        _y -= ELEM.get(_elem.elemId).scrollTop;
      }
      if(_elem.markupElemIds&&_elem.markupElemIds.subview){
        _y += ELEM.get(_elem.markupElemIds.subview).offsetTop;
        _y -= ELEM.get(_elem.markupElemIds.subview).scrollTop;
      }
      _elem = _elem.parent;
    }
    return _y;
  },
  
/** Returns the HPoint that has the scrolled position calculated.
  **/
  pageLocation: function() {
    return new HPoint(this.pageX(), this.pageY());
  },
  
/** = Description
  * An abstract method that derived classes may implement, if they are able to
  * resize themselves so that their content fits nicely inside.
  * 
  **/
  optimizeWidth: function() {

  },
  
  
/** = Description
  * Invalidates event manager's element position cache for this view and its
  * subviews. Actual functionality is implemented in HControl.
  * 
  * = Returns
  * +self+
  * 
  **/
  invalidatePositionCache: function() {
    for(var i=0; i<this.views.length; i++) {
      HSystem.views[this.views[i]].invalidatePositionCache();
    }
    return this;
  },
  
  
/** = Description
  * Binds a DOM element to the +ELEM+ cache. This is a wrapper for
  * the ELEM#elem_bind that keeps track of the bound elements and
  * frees them from the element manager when the view is destroyed.
  * 
  * = Parameters
  * +_domElementId+:: The value of the DOM element's id attribute that is
  *                   to be bound to the element cache.
  * 
  * = Returns
  * The element index id of the bound element.
  * 
  **/
  bindDomElement: function(_domElementId) {
    var _cacheId = ELEM.bindId(_domElementId);
    if (_cacheId) {
      this._domElementBindings.push(_cacheId);
    }
    return _cacheId;
  },
  
  
/** = Description
  * Removes a DOM element from the +ELEM+ cache. This is a wrapper
  * for the ELEM#elem_del. This is used for safely removing DOM
  * nodes from the cache.
  * 
  * = Parameters
  * +_elementId+:: The id of the element in the element manager's cache 
  *                that is to be removed from the cache.
  * 
  **/
  unbindDomElement: function(_elementId) {
    var _indexOfElementId = this._domElementBindings.indexOf(_elementId);
    if (_indexOfElementId > -1) {
      ELEM.del(_elementId);
      this._domElementBindings.splice(_indexOfElementId, 1);
    }
  }
  
  
});

HView.implement(HMarkupView);
HView.implement(HMorphAnimation);

/*   Riassence Framework
 *   Copyright 2006 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** Automatic event responder. Defines what events HControl listens to
  ** and actions to be taken.
  **
  ** = Event handler methods
  ** Pre-defined event handler methods, extend these in your subclass.
  **
  ** +focus+::               Called when the component gets focus
  ** +blur+::                Called when the component loses focus
  ** +mouseDown+::           Called when the mouse button is pushed down
  ** +mouseUp+::             Called when the mouse button is released
  ** +mouseWheel+::          Called when the mouse wheel is used
  ** +startDrag+::           Called when the mouse button 
  **                         is pressed (and item is draggable).
  ** +endDrag+::             Called when the mouse button 
  **                         is released (and item is draggable).
  ** +drag+::                Called when the mouse is moved and mouse button 
  **                         is down (and item is draggable).
  ** +drop+::                Called when a draggable item is released 
  **                         on the droppable.
  ** +startHover+::          Called when a draggable item is moved 
  **                         over the droppable.
  ** +endHover+::            Called when a draggable item is moved out 
  **                         of the droppable.
  ** +keyDown+::             Called when the user presses a key, and 
  **                         the control is active.
  ** +keyUp+::               Called when the user releases a key, and 
  **                         the control is active.
  ** +textEnter+::           Called when the user releases a key regardless 
  **                         if the control is active or not.
  ** +gainedActiveStatus+::  Called when the component gets activated.
  ** +lostActiveStatus+::    Called when the component gets deactivated.
***/
HEventResponder = HClass.extend({
  
/** = Description
  * The event responder interface for +HControl+.
  * Registers the events defined by boolean properties of 
  * the events object to the control instance. The event manager 
  * handles the event mapping and abstraction itself.
  * NOTE startDrag vs mouseDown and endDrag vs mouseUp events 
  * conflict, if both are set simultaneously.
  *
  * = Parameters
  * +_events+::  A +{ event: state }+ Object structure, sets events based on the 
  *              keys and the flag. All states are Booleans (true or false).
  *              A true state means the event listening for the event is
  *              enabled and a false state means the event listening is disabled.
  *              See the Event Types below:
  *
  * == Event States
  * Event State::    Description
  * +mouseMove+::   The global +mouseMove+ event state. The component receives
  *                 this event regardless if it's focused or not. The event
  *                 responder method for it is +mouseMove+ and it receives the
  *                 absolute x and y coordinates of the mouse pointer when the
  *                 mouse cursor position changes. Can also be toggled
  *                 separately by using the +setMouseMove+ method.
  * +textEnter+::   The global +textEnter+ event state. The component receives
  *                 this event regardless if it's focused or not. The event
  *                 responder method for it is +textEnter+ and it receives a
  *                 every time a key on the keyboard is pressed. Can also
  *                 be toggled separately by using the +setTextEnter+ method.
  * +click+::       The local +click+ event state. The component receives
  *                 this event only if it's focused. The event responder
  *                 method for it is +click+ and it receives the absolute x
  *                 and y coordinates of the mouse pointer as well as which
  *                 mouse button was used to trigger the event. Can also be
  *                 toggled separately by using the +setClickable+ method.
  * +mouseDown+::   The local +mouseDown+ event state. The component receives
  *                 this event only if it's focused. The event responder
  *                 method for it is +mouseDown+ and it receives the absolute x
  *                 and y coordinates of the mouse pointer as well as which
  *                 mouse button was used to trigger the event. Can also be
  *                 toggled separately by using the +setMouseDown+ method.
  * +mouseUp+::     The local +mouseUp+ event state. The component receives
  *                 this event only if it's focused. The event responder
  *                 method for it is +mouseUp+ and it receives the absolute x
  *                 and y coordinates of the mouse pointer as well as which
  *                 mouse button was used to trigger the event. Can also be
  *                 toggled separately by using the +setMouseUp+ method.
  * +mouseWheel+::  The local +mouseWheel+ event state. The component receives
  *                 this event only if it's focused. The event responder
  *                 method for it is +mouseWheel+ and it receives the delta of
  *                 the amount of the mouse scroll wheel was rolled: a floating
  *                 point number, larger or smaller than 0, depending on the
  *                 direction the scroll wheel was rolled. Can also be
  *                 toggled separately by using the +setMouseWheel+ method.
  * +draggable+::   The local +draggable+ event states. The component receives
  *                 these events only if it's focused. The event responders
  *                 methods are +startDrag+, +drag+ and +endDrag+. The events
  *                 receive the mouse cursor coordinates.
  *                 Can also be toggled separately by using the +setDraggable+
  *                 method.
  * +droppable+::   The local +droppable+ event states. The component receives
  *                 this event only if another component is dragged (hovered)
  *                 or dropped with the area of this component as the target.
  *                 The event responders method for it are +hoverStart+,
  *                 +drop+ and +hoverEnd+.
  *                 Can also be toggled separately by using the
  *                 +setDroppable+ method.
  * +keyDown+::     The local +keyDown+ event state. The component receives
  *                 this event only if it's focused. The event responder
  *                 method for it is +keyDown+ and it receives the ascii key
  *                 code whenever a keyboard key is pressed. Can also be
  *                 toggled separately by using the +setKeyDown+ method.
  * +keyUp+::       The local +keyUp+ event state. The component receives
  *                 this event only if it's focused. The event responder
  *                 method for it is +keyUp+ and it receives the ascii key
  *                 code whenever a keyboard key is released. Can also be
  *                 toggled separately by using the +setKeyUp+ method.
  *
  * = Usage
  *   HControl.new(
  *     [0,0,100,20],
  *     HApplication.nu()
  *   ).setEvents({
  *     mouseUp: true,
  *     mouseDown: true
  *   });
  *
  * = Returns
  * +self+
  *
  **/
  setEvents: function(_events) {
    if(!this.events) {
      this.events = HClass.extend({
        mouseMove:  false,
        mouseDown:  false,
        mouseUp:    false,
        draggable:  false,
        droppable:  false,
        keyDown:    false,
        keyUp:      false,
        mouseWheel: false,
        textEnter:  false,
        click:      false
      }).nu();
    }
    if(_events) {
      this.events.extend( _events );
    }
    this.events.ctrl = this;
    EVENT.focusOptions[this.elemId] = this.events;
    var _mmoveStatus = this.events.mouseMove,
        _mmoveIndex  = EVENT.coordListeners.indexOf(this.elemId);
    if (_mmoveStatus && (_mmoveIndex===-1)){
      EVENT.coordListeners.push(this.elemId);
    }
    else if ((!_mmoveStatus) && (_mmoveIndex!==-1)){
      EVENT.coordListeners.splice(_mmoveIndex,1);
    }
    
    return this;
  },

/** = Description
  * Alternative flag setter for the mouseMove event type. If set to true, 
  * starts listening to mouseDown events when the component has focus.
  *
  * = Parameters
  * +_flag+:: Set the mouseDown event listening on/off (true/false) for
  *           the component instance.
  *
  * = Returns
  * +self+
  *
  **/
  setMouseMove: function(_flag) {
    this.events.mouseMove = _flag;
    this.setEvents();
    return this;
  },

/** = Description
  * Alternative flag setter for the click event type. If set to true, 
  * starts listening to click events when the component has focus.
  *
  * = Parameters
  * +_flag+:: Set the click event listening on/off (true/false) for
  *           the component instance.
  *
  * = Returns
  * +self+
  *
  **/
  setClickable: function(_flag) {
    this.events.click = _flag;
    this.setEvents();
    return this;
  },
  
/** = Description
  * Registers or releases event listening for mouseDown events depending on 
  * the value of the flag argument.
  *
  * = Parameters
  * +_flag+:: Set the mouseDown event listening on/off (true/false) for
  *           the component instance.
  *
  * = Returns
  * +self+
  *
  **/
  setMouseDown: function(_flag) {
    this.events.mouseDown = _flag;
    this.setEvents();
    return this;
  },
  
/** = Description
  * Registers or releases event listening for mouseUp events depending on the 
  * value of the flag argument.
  *
  * = Parameters
  * +_flag+:: Set the mouseUp event listening on/off (true/false) for
  *           the component instance.
  *
  * = Returns
  * +self+
  *
  **/
  setMouseUp: function(_flag) {
    this.events.mouseUp = _flag;
    this.setEvents();
    return this;
  },
  
/** = Description
  * Alternative flag setter for the mouseWheel event type. If set to true, 
  * starts listening to mouseWheel events when the component has focus.
  *
  * = Parameters
  * +_flag+:: Set the mouseWheel event listening on/off (true/false) for
  *           the component instance.
  *
  * = Returns
  * +self+
  *
  **/
  setMouseWheel: function(_flag) {
    this.events.mouseWheel = _flag;
    this.setEvents();
    return this;
  },
  
/** = Description
  * Registers or releases event listening for startDrag, drag and 
  * endDrag -events depending on the value of the flag argument.
  *
  * = Parameters
  * +_flag+:: Set the startDrag, drag and endDrag event listening 
  *           on/off (true/false) for the component instance.
  *
  * = Returns
  * +self+
  *
  **/
  setDraggable: function(_flag) {
    this.events.draggable = _flag;
    this.setEvents();
    return this;
  },
  
/** = Description
  * Registers or releases event listening for startHover, drop and 
  * endHover -events depending on the value of the flag argument.
  *
  * = Parameters
  * +_flag+:: Set the startHover, drop and endHover event listening 
  *           on/off (true/false) for the component instance.
  *
  * = Returns
  * +self+
  *
  **/
  setDroppable: function(_flag) {
    this.events.droppable = _flag;
    this.setEvents();
    return this;
  },
  
  
/** = Description
  * Registers or releases event listening for keyDown events depending on the 
  * value of the flag argument.
  *
  * = Parameters
  * +_flag+:: Set the keyDown event listening on/off (true/false) for
  *           the component instance.
  *
  * = Returns
  * +self+
  *
  **/
  setKeyDown: function(_flag) {
    this.events.keyDown = _flag;
    this.setEvents();
    return this;
  },
  
  
/** = Description
  * Registers or releases event listening for keyUp events depending on 
  * the value of the flag argument.
  *
  * = Parameters
  * +_flag+:: Set the keyUp event listening on/off (true/false) for
  *           the component instance.
  *
  * = Returns
  * +self+
  *
  **/
  setKeyUp: function(_flag) {
    this.events.keyUp = _flag;
    this.setEvents();
    return this;
  },
  
/** = Description
  * Registers or releases event listening for textEnter events 
  * depending on the value of the flag argument.
  *
  * = Returns
  * +self+
  *
  **/
  setTextEnter: function(_flag) {
    this.events.textEnter = _flag;
    this.setEvents();
    return this;
  },
  
/** Same as +setClickable+
  **/
  setClick: function(_flag) {
    return this.setClickable(_flag);
  },
  
/** = Description
  * Default focus event responder method. Does nothing by default.
  * Called when the component gets focus.
  *
  **/
  focus: function() {},
  
/** = Description
  * Default blur event responder method. Does nothing by default.
  * Called when the component loses focus.
  *
  **/
  blur: function() {},
  
  
/** = Description
  * Default gainedActiveStatus event responder method. Does nothing by default.
  * Called when the component gains active status; both focused and clicked.
  *
  * = Parameters
  * +_lastActiveControl+:: A reference to the control that was active
  *                        before this control became active. Can
  *                        be null if there was no active control.
  *
  **/
  gainedActiveStatus: function(_lastActiveControl) {
    
    if ( (HSystem.windowFocusBehaviour === 1) && ( this.parents.length > 2 ) ) {
      if ( this.parents[2].componentBehaviour.indexOf('window') !== -1 ) {
        this.parents[2].gainedActiveStatus();
      }
    }
    
  },
  
  // A low-level handler for active status, don't extend this.
  _gainedActiveStatus: function(_lastActiveControl) {
    if(this.enabled) {
      this.toggleCSSClass(this.elemId, HControl.CSS_ACTIVE, true);
    }
    this.gainedActiveStatus(_lastActiveControl);
  },
  
  
/** = Description
  * Default lostActiveStatus event responder method. Does nothing by default.
  * Called when the component loses active status; another component was
  * focused and clicked.
  *
  * = Parameters
  * +_newActiveControl+:: A reference to the control that became the currently
  *                       active control. Can be null if there is no active
  *                       control.
  *
  **/
  lostActiveStatus: function(_newActiveControl) {
    
  },
  
  // --A low-level handler for lost active status, don't extend this.++
  _lostActiveStatus: function(_newActiveControl) {
    if(this.enabled) {
      this.toggleCSSClass(this.elemId, HControl.CSS_ACTIVE, false);
    }
    this.lostActiveStatus(_newActiveControl);
  },
  
  
/** = Description
  * Default mouseMove event responder method. Does nothing by default.
  * Called whenever the mouse cursor is moved regardless if the
  * component is active or has focus.
  *
  * = Parameters
  * +x+:: The horizontal coordinate units (px) of the mouse cursor position.
  * +y+:: The vertical coordinate units (px) of the mouse cursor position.
  *
  **/
  mouseMove: function(x,y) {},
  
  
/** = Description
  * Default click event responder method. Does nothing by default.
  *
  * = Parameters
  * +x+::              The horizontal coordinate units (px) of the 
  *                    mouse cursor position.
  * +y+::              The vertical coordinate units (px) of the 
  *                    mouse cursor position.
  * +_isRightButton+:: Boolean flag; true if the right(context) mouse
  *                    button is pressed.
  *
  **/
  click: function(x,y,_isRightButton){},
  
/** = Description
  * Default mouseDown event responder method. Does nothing by default.
  *
  * = Parameters
  * +x+::              The horizontal coordinate units (px) of the 
  *                    mouse cursor position.
  * +y+::              The vertical coordinate units (px) of the 
  *                    mouse cursor position.
  * +_isRightButton+:: Boolean flag; true if the right(context) mouse
  *                    button is pressed.
  *
  **/
  mouseDown: function(x,y,_isRightButton) {},
  
  
/** = Description
  * Default mouseDown event responder method. Does nothing by default.
  *
  * = Parameters
  * +x+::              The horizontal coordinate units (px) of the 
  *                    mouse cursor position.
  * +y+::              The vertical coordinate units (px) of the 
  *                    mouse cursor position.
  * +_isRightButton+:: Boolean flag; true if the right(context) mouse
  *                    button is pressed.
  *
  **/
  mouseUp: function(x,y,_isRightButton) {},
  
/** = Description
  * Default mouseWheel event responder method. Does nothing by default.
  *
  * = Parameters
  * +_delta+:: Scrolling delta, the wheel angle change. If delta is positive,
  *            wheel was scrolled up. Otherwise, it was scrolled down.
  *
  **/
  mouseWheel: function(_delta) {},
  
/** = Description
  * Default startDrag event responder method. Sets internal flags by default.
  * This is the preferred method to extend if you want to do something
  * when a drag event starts. If you extend, remember to call +this.base();+
  *
  * = Parameters
  * +x+:: The horizontal coordinate units (px) of the mouse cursor position.
  * +y+:: The vertical coordinate units (px) of the mouse cursor position.
  *
  **/
  startDrag: function(x, y) {},
  
/** = Description
  * Default drag event responder method. Does nothing by default.
  * This is the preferred method to extend while a drag method is ongoing.
  * Called whenever the mouse cursor moves and a drag event has been started.
  *
  * = Parameters
  * +x+:: The horizontal coordinate units (px) of the mouse cursor position.
  * +y+:: The vertical coordinate units (px) of the mouse cursor position.
  *
  **/
  drag: function(x,y){
    this.doDrag(x,y);
  },
  doDrag: function(x, y) {},
  
  
/** = Description
  * Default endDrag event responder method. Sets internal flags by default.
  * This is the preferred method to extend if you want to do something
  * when a drag event ends. If you extend, remember to call +this.base();+
  *
  * = Parameters
  * +x+:: The horizontal coordinate units (px) of the mouse cursor position.
  * +y+:: The vertical coordinate units (px) of the mouse cursor position.
  *
  **/
  endDrag: function(x, y) {
    this.invalidatePositionCache();
  },

/** = Description
  * Default drop event responder method. Does nothing by default.
  * Extend the drop method, if you want to do something 
  * when this instance is the target of another instance's endDrag event.
  * Called when a dragged component instance is dropped on the target instance.
  *
  * = Parameters
  * +obj+:: The dragged component object.
  *
  **/
  drop: function(obj) {
    this.onDrop(obj);
  },
  onDrop: function(obj) {},

/** = Description
  * Default startHover event responder method. Does nothing by default.
  * Extend the startHover method, if you want to do something 
  * when this instance is the target of another instance's drag event.
  * Called when a dragged component instance is dragged over
  * the target instance.
  *
  * = Parameters
  * +obj+:: The dragged component object.
  *
  **/
  startHover: function(obj) {
    this.onHoverStart(obj);
  },
  onHoverStart: function(obj) {},
  
/** = Description
  * Default endHover event responder method. Does nothing by default.
  * Extend the endHover method, if you want to do something 
  * when this instance is no longer the target of another instance's
  * drag event. Called when a dragged component instance is dragged
  * away from the target instance.
  *
  * = Parameters
  * +obj+:: The dragged component object.
  *
  **/
  endHover: function(obj) {
    this.onHoverEnd(obj);
  },
  onHoverEnd: function(obj) {},
  
/** = Description
  * Default keyDown event responder method. Does nothing by default.
  * Extend the keyDown method, if you want to do something
  * when a key is pressed and the component is active.
  *
  * = Parameters
  * +_keycode+:: The ascii key code of the key that was pressed.
  *
  **/
  keyDown: function(_keycode) {},
  
/** = Description
  * Default keyUp event responder method. Does nothing by default.
  * Extend the keyUp method, if you want to do something
  * when a key is released and the component is active.
  *
  * = Parameters
  * +_keycode+:: The ascii key code of the key that was released.
  *
  **/
  keyUp: function(_keycode) {},
  
/** = Description
  * Default textEnter event responder method. Does nothing by default.
  * Extend the textEnter method, if you want to do something
  * when a key is released regardless if the component is active,
  * has focus or not.
  *
  * = Parameters
  * +_keycode+:: The ascii key code of the key that was released.
  *
  **/
  textEnter: function() {},
  
  /** -- DON'T TOUCH _mouseOver, IT IS A LOW-LEVEL HANDLER, use focus() instead ++ **/
  _mouseOver: function(e) {
    if (!Event.element) {
      return;
    }
    var _that = Event.element(e);
    while(_that && _that.ctrl === undefined) {
      _that = _that.parentNode;
    }
    if (!_that) {
      return;
    }
    var _this = _that.ctrl;

    EVENT.focus(_this);
    Event.stop(e);
  },
  
  /** -- DON'T TOUCH _mouseOut, IT IS A LOW-LEVEL HANDLER, use blur() instead ++ **/
  _mouseOut: function(e) {
    if (!Event.element) {
      return;
    }
    var _that = Event.element(e);
    while(_that && _that.ctrl === undefined) {
      _that = _that.parentNode;
    }
    if (!_that) {
      return;
    }
    var _this = _that.owner;
    
    EVENT.blur(_this);
    Event.stop(e);
  },
  
  
/** = Description
  * Forces retrieving this control's DOM element position directly rather than
  * using the cached version when the position is needed by +EVENT+.
  * Child controls are invalidated recursively by +HView+.
  *
  * = Returns
  * +self+
  * 
  **/
  invalidatePositionCache: function() {
    this.base();
    EVENT.coordCacheFlush(this.elemId);
    return this;
  }
  
});/*   Riassence Framework
 *   Copyright 2006 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */


/*** = Description
  ** Defines a minimal +HValue+ responder interface.
  ** It's implemented by default by +HControl+.
***/
HValueResponder = HClass.extend({
  
/** = Description
  * Binds an HValue compatible instance to the component's valueObj. Also 
  * calls +setValue+. It should not be called from user code, instead
  * use the +HValue+ instance method +bind+.
  *
  * = Parameter
  * +_aValueObj+:: The HValue instance object to bind.
  *
  * = Returns
  * +self+
  *
  **/
  setValueObj: function(_valueObj) {
    this.valueObj = _valueObj;
    this.setValue(_valueObj.value);
    return this;
  },
  
/** = Description
  * Checks, if the value given as parameter differs from +value+.
  *
  * = Parameters
  * +_value+:: The value to be tested.
  *
  * = Returns
  * A boolean true (different) or false (same).
  *
  **/
  valueDiffers: function(_value){
    return (COMM.Values.encode(_value) !== COMM.Values.encode(this.value));
  },
  
/** = Description
  * Assigns the object a new value.
  * Extend it, if your component needs to do validation of the new value.
  * For +HControl+ instances, extend HControl#refreshValue to do something when
  * the +value+ has been set.
  *
  * = Parameter
  * +_value+::  The new value. Allowed values depend on the component type
  *             and other usage of the bound +HValue+ instance +self.valueObj+.
  *
  * = Returns
  * +self+
  *
  **/
  setValue: function(_value) {
    if(_value !== undefined && this['valueObj'] && this.valueDiffers(_value)) {
      var _valueManager = COMM.Values;
      this.value = _value;
      if( _valueManager._builtins.indexOf( _valueManager.type(_value) ) === -1 ){
        this.valueObj.set( _valueManager.clone( _value ) );
      }
      else {
        this.valueObj.set( _value );
      }
      (this['refresh'] !== undefined) && (typeof this.refresh === 'function') && this.refresh();
    }
    return this;
  }

});/*   Riassence Framework
 *   Copyright 2006 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** A HDummyValue is just a placeholder for HValue values. HDummyValue
  ** is a light-weight alternative that doesn't implement any actual HValue
  ** functionality, but implements the essential methods and keeps the HControl
  ** content when an actual HValue instance isn't bound.
  ** It's the default valueObj type for components not bound to real HValue instances.
***/
HDummyValue = HClass.extend({
  
/** = Description
  * HDummyValue is initialized just like a real HValue.
  *
  * = Parameters
  * +_id+::    Any string or integer, just a placeholder for HValue.id
  * +_value+:: Any valid js object, just as for HValue.value
  *
  **/
  constructor: function(_id, _value) {
    this.id = _id;
    this.value = _value;
  },

/** Sets a new instance payload value.
  **/
  set: function(_value) {
    this.value = _value;
  },

/** Returns the instance payload value.
  **/
  get: function() {
    return this.value;
  },
  
/** Binds HControl, does actually nothing.
  **/
  bind: function( _theObj ){},
  
/** Unbinds (releases) HControl, does actually nothing.
  **/
  unbind: function( _theObj ){}
});
/*   Riassence Framework
 *   Copyright 2006 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** Define default setting here. Will be used, when no or invalid constructor
  ** options are supplied.
  ***/
HControlDefaults = HClass.extend({
  
/** The default label. A label is the "visual value" of a component that
  * operates on a "hidden" value.
  **/
  label:    "",

/** The default initial visibility of the component.
  **/
  visible:  true,
  
/** The default initial event responders to register to a component.
  * By default no events are enabled.
  **/
  events:   null,
  
  constructor: function(){
    if(!this.events){
      this.events = {};
    }
  },
  
/** The default initial value of the component.
  **/
  value:    0,
  
/** The default initial enabled state of the component.
  **/
  enabled:  true,
  
/** The default initial active state of the component.
  **/
  active:   false,
  
/** The default initial minimum value of the component.
  **/
  minValue: -2147483648,
  
/** The default initial maximum value of the component.
  **/
  maxValue:  2147483648
  
});

// Alias for backwards-compatibility.
HComponentDefaults = HControlDefaults;

/*   Riassence Framework
 *   Copyright 2006 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/** = Description
  * The foundation class for all active visual components that 
  * implement events and values.
  * 
  * Extend +HControl+ to suit your needs. See any component 
  * for extension reference.
  *
  **/
HControl = HView.extend({
  
/** Component behaviour includes 'view' and 'control' as a default.
  **/
  componentBehaviour: ['view','control'],
  
/** A flag: when true, calls the +refreshValue+ method whenever
  * +self.value+ is changed.
  **/
  refreshOnValueChange: true,
  
/** A flag: when true, calls the +refreshLabel+ method whenever
  * +self.label+ is changed.
  **/
  refreshOnLabelChange: true,
  
/** The visual value of a component, usually a String. See setLabel.
  **/
  label: null,
  
/** The event object structure that specifies which events to listen to.
  **/
  events: null,
  
/** The enabled/disabled flag. See setEnabled.
  **/
  enabled: null,
  
/** The current value of a component. See setValue.
  **/
  value: null,
  
/** The current HValue compatible object. Do not set directly.
  * Holds reference to the bound HValue instance.
  * Set with HValue.bind.
  **/
  valueObj: null,
  
/** The minimum numeric value allowed, when the component
  * utilizes value ranges. See setValueRange
  **/
  minValue: null,
  
/** The maximum allowed value, when the component
  * utilizes value ranges. See setValueRange
  **/
  maxValue: null,
  
/** A boolean value that shows whether this control is currently 
  * active or not. Control gets active when the user clicks on it.
  **/
  active: null,
  
/** An reference to the options block given as the constructor
  * parameter _options.
  **/
  options: null,
  
/** -- Use this object to specify class-specific default settings. ++
  * The controlDefaults is a HControlDefaults object that is extended
  * in the constructor with the options block given. The format of
  * it is an Object.
  **/
  controlDefaults: HControlDefaults,
  
/** = Description
  * The constructor of HControl implements the same model as HView, 
  * but accepts a third parameter: the options object, that contain 
  * optional properties, like the value, label and events.
  *
  * = Parameters
  * +_rect+::     An instance of +HRect+, defines the position and size of views.
  *               It can be also defined with an array, see below.
  * +_parent+::   The parent instance this instance will be contained within.
  *               A valid parent can be another HView compatible instance,
  *               an HApplication instance, a HControl or a similar extended
  *               HView instance. The origin of the +_rect+ is the same as the
  *               parent's offset. For HApplication instances, the web browser's
  *               window's left top corner is the origin.
  * +_options+:: Optional, all other parameters as object attributes.
  *              Defaults to an instance of +HControlDefaults+, see controlDefaults
  *
  * == The +_rect+ dimensions as arrays
  * Instead of an instance of +HRect+, dimensions can also be supplied as arrays.
  * The array length must be either 4 or 6. If the length is 4, the dimensions are
  * specified as follows: +[ x, y, width, height ]+. Note that this is different
  * from the construction parameters of +HRect+ that takes the coordinates as two
  * points, like: +( left, top, right, bottom )+.
  * Arrays with 6 items are a bit more complex (and powerful) as they can specify
  * the flexible offsets too.
  * 
  * === The array indexes for a +_rect+ configured as an 4-item array:
  * Always left/top aligned, all items must be specified.
  * Index::            Description
  * +0+::              The X-coordinate (measured from the parent's left edge)
  * +1+::              The Y-coordinate (measured from the parent's top edge)
  * +2+::              The width.
  * +3+::              The height.
  *
  * === The array indexes a +_rect+ configured as an 6-item array:
  * Can be any configuration of left/top/right/bottom alignment and supports
  * flexible widths. At least 4 items must be specified.
  * Index::            Description
  * +0+::              The left-aligned X-coordinate or +null+ if the view is
  *                    right-aligned and using a right-aligned X-coordinate at
  *                    index +4+ as well as the width specified at index +2+.
  * +1+::              The top-aligned Y-coordinate or +null+ if the view is
  *                    bottom-aligned and using a right-aligned X-coordinate at
  *                    index +5+ as well as the height specified at index +3+.
  * +2+::              The width, if only one X-coordinate specifies the
  *                    position (at indexes +0+ or +4+).
  *                    If both X-coordinates (at indexes +0+ and +4+) are
  *                    specified, the width can be specified with a +null+ for
  *                    automatic (flexible) width. If the width is specified,
  *                    it's used as the minimum width.
  * +3+::              The height, if only one Y-coordinate specifies the
  *                    position (at indexes +1+ or +5+).
  *                    If both Y-coordinates (at indexes +1+ and +5+) are
  *                    specified, the height can be specified with a +null+ for
  *                    automatic (flexible) height. if the height is specified,
  *                    it's used as the minimum height.
  * +4+::              The right-aligned X-coordinate or +null+ if the view is
  *                    left-aligned and using a left-aligned X-coordinate at
  *                    index +0+ as well as the width specified at index +2+.
  * +5+::              The bottom-aligned Y-coordinate or +null+ if the view is
  *                    top-aligned and using a top-aligned X-coordinate at
  *                    index +1+ as well as the height specified at index +3+.
  * == Usage examples of +_rect+:
  * Specified as two instances of +HPoint+,
  * x: 23, y: 75, width: 200, height: 100:
  *  HRect.nu( HPoint.nu( 23, 75 ), HPoint.nu( 223, 175 ) )
  *
  * The same as above, but without +HPoint+ instances:
  *  HRect.nu( 23, 75, 223, 175 )
  *
  * The same as above, but with an array as the constructor
  * parameter for +HRect+:
  *  HRect.nu( [ 23, 75, 223, 175 ] )
  *
  * The same as above, but with an array instead of a +HRect+ instance:
  *  [ 23, 75, 200, 100 ]
  *
  * The same as above, but with a 6-item array:
  *  [ 23, 75, 200, 100, null, null ]
  *
  * The same as above, but aligned to the right instead of left:
  *  [ null, 75, 200, 100, 23, null ]
  *
  * The same as above, but aligned to the right/bottom edges:
  *  [ null, null, 200, 100, 23, 75 ]
  *
  * The same as above, but aligned to the left/bottom edges:
  *  [ 23, null, 200, 100, null, 75 ]
  *
  * Flexible width (based on the parent's dimensions):
  *  [ 23, 75, null, 100, 23, null ]
  *
  * Flexible height (based on the parent's dimensions):
  *  [ 23, 75, 200, null, null, 75 ]
  *
  * Flexible width and height (based on the parent's dimensions):
  *  [ 23, 75, null, null, 23, 75 ]
  *
  * Flexible width and height, but limited to a minimum width
  * of 200 and a minimum height of 100 (based on the parent's dimensions):
  *  [ 23, 75, 200, 100, 23, 75 ]
  *
  * == The +_options+ Object, all options are optional and default to what's
  * defined in +controlDefaults+.
  * Key::         Description
  * +value+::     The initial value of the component. It's type and meaning
  *               differs between components.
  * +valueObj+::  An HValue instance to bind immediately. The value of the
  *               HValue instance overrides the +value+ option.
  * +label+::     The label of the component. It's usually a text (or html)
  *               String. Its meaning differs between components.
  *               See +setLabel+ and +refreshLabel+
  * +visible+::   A Boolean value defining the initial visibility of the
  *               component. A true value means visible and false means
  *               hidden.
  * +events+::    An Object containing the events to listen to.
  *               See setEvents and EVENT
  * +enabled+::   A Boolean value defining the initial enabled -state
  *               of the component. Set to false to initially disable the
  *               component. See setEnabled
  * +active+::    A Boolean value defining the initial active (clicked 
  *               or focused) state of the component.
  * +minValue+::  A Number for components utilizing value ranges.
  *               See setValueRange
  * +maxValue+::  A Number for components utilizing value ranges.
  *               See setValueRange
  * 
  *
  **/
  constructor: function(_rect, _parent, _options) {
    if(!_options) {
      _options = {};
    }
    var _this = this;
    
    _options = (_this.controlDefaults.extend(_options)).nu(this);
    _this.options = _options;
    
    if(_this.isinherited) {
      _this.base(_rect, _parent);
    }
    else {
      _this.isinherited = true;
      _this.base(_rect, _parent);
      _this.isinherited = false;
    }
    
    var _isValueRange = (_options.minValue || _options.maxValue),
        _label = _options.label,
        _events = _options.events;
    
    if(_options.visible) {
      _this.show();
    }
    else {
      _this.hide();
    }
    
    if(_isValueRange) {
      _this.minValue = _options.minValue;
      _this.maxValue = _options.maxValue;
    }
    
    _this.setLabel(_label);
    _this.setEvents(_events);
    _this.setEnabled(_options.enabled);
    
    if(_options.valueObj){
      _options.valueObj.bind(_this);
    }
    else if(!_this.valueObj) {
      _this.valueObj = HDummyValue.nu();
    }
    
    if((_this.value===null)&&(_options.value!==undefined)) {
      _this.setValue(_options.value);
    }
    if(_isValueRange) {
      _this.setValueRange(this.value, _options.minValue, _options.maxValue);
    }
    if(!_this.isinherited) {
      _this.draw();
    }
  },
  
/** = Description
  * The destructor of +HControl+ instances. 
  * Releases events and values before passing through to the base HView.die.
  * Extend it, you you allocate new instance members that need to be
  * deallocated upon destruction.
  *
  **/
  die: function() {
    var _this = this;
    if(_this.valueObj){
      _this.valueObj.unbind(_this);
      _this.valueObj = null;
    }
    EVENT.unreg(_this);
    _this.base();
  },
  
/** = Description
  * Sets the label on a control component: the text that's displayed in 
  * HControl extensions. Visual functionality is implemented in component 
  * theme templates and refreshLabel method extensions.
  *
  * Avoid extending directly, extend +refreshLabel+ instead.
  *
  * = Parameters
  * +_label+:: The text the component should display.
  *
  * = Returns
  * +self+
  *
  **/
  setLabel: function(_label) {
    var _this = this,
        _differs = (_label !== _this.label);
    if(_differs){
      _this.label = _label;
      _this.options.label = _label;
      _this.refresh();
    }
    return this;
  },
  
/** = Description
  * Enables the HControl instance, if the enabled flag is true, and disables 
  * it if enabled is false. A disabled HControl won't respond events. 
  * Component themes reflect the disabled state typically with 
  * a dimmer appearance.
  *
  * = Parameters
  * +_flag+:: Boolean; true enables, false disables.
  *
  * = Returns
  * +this+
  *
  **/
  setEnabled: function(_flag) {
    
    var _this = this,
        _elemId = this.elemId,
        _sysViews = HSystem.views,
        i = 0,
        _views = _this.views,
        _viewsLen = _views.length;
    
    // Enable/disable the children first.
    for (; i < _viewsLen; i++) {
      _sysViews[_views[i]].setEnabled(_flag);
    }
    
    if (_this.enabled === _flag) {
      // No change in enabled status, do nothing.
      return this;
    }
    
    _this.enabled = _flag;
    
    if(_flag) {
      EVENT.reg(_this, _this.events);
    }
    else {
      EVENT.unreg(this);
    }
    
    // Toggle the CSS class: enabled/disabled
    _this.toggleCSSClass(_elemId, HControl.CSS_ENABLED, _flag);
    _this.toggleCSSClass(_elemId, HControl.CSS_DISABLED, !_flag);
    return this;
  },
  
  
/** = Description
  * Assigns the object a new value range. Used for sliders, steppers etc. Calls
  * setValue with the value given.
  *
  * = Parameters
  * +_value+::    The new value to be set to the component's 
  *               HValue compatible instance.
  *
  * +_minValue+:: The new minimum value limit. See minValue.
  *
  * +_maxValue+:: The new maximum value limit. See maxValue.
  *
  * = Returns
  * +self+
  *
  **/
  setValueRange: function(_value, _minValue, _maxValue) {
    this.minValue = _minValue;
    this.maxValue = _maxValue;
    _value = (_value < _minValue) ? _minValue : _value;
    _value = (_value > _maxValue) ? _maxValue : _value;
    this.setValue(_value);
    return this;
  },
  
/** = Description
  * Called when the +self.value+ has been changed. By default
  * tries to update the value element defined in the theme of
  * the component. Of course, the HControl itself doesn't
  * define a theme, so without a theme doesn't do anything.
  *
  * = Returns
  * +self+
  *
  **/
  refreshValue: function(){
    if(this.markupElemIds){
      if(this.markupElemIds['value']){
        ELEM.setHTML(this.markupElemIds.value,this.value);
      }
    }
    return this;
  },
  
/** = Description
  * Called when the +self.label+ has been changed. By default
  * tries to update the label element defined in the theme of
  * the component. Of course, the HControl itself doesn't
  * define a theme, so without a theme doesn't do anything.
  *
  * = Returns
  * +self+
  *
  **/
  refreshLabel: function(){
    if(this.markupElemIds){
      if(this.markupElemIds['label']){
        ELEM.setHTML(this.markupElemIds.label,this.label);
      }
    }
    return this;
  },
  
/** = Description
  * Called mostly internally whenever a property change requires usually visual
  * action. It's called by methods like setLabel and setValue. 
  * Extends HView.refresh. The boolean properties refreshOnValueChange and 
  * refreshOnLabelChange control whether refreshValue or refreshLabel 
  * should be called. It's used as-is in most components. If you implement 
  * your class extension with properties similar to value or label, 
  * you are advised to extend the refresh method.
  *
  * = Returns
  * +self+
  *
  **/
  refresh: function(){
    this.base();
    if(this.drawn){
      if(this.refreshOnValueChange){
        this.refreshValue();
      }
      if(this.refreshOnLabelChange){
        this.refreshLabel();
      }
    }
    return this;
  }
},

{
  
  // The CSS class name to set when the component is disabled
  CSS_DISABLED: "disabled",
  
  // The CSS class name to set when the component is enabled
  CSS_ENABLED:  "enabled",
  
  // The CSS class name to set when the component is active (clicked/focused)
  CSS_ACTIVE:   "active"
  
});

HControl.implement( HValueResponder );
HControl.implement( HEventResponder );
/*   Riassence Framework
 *   Copyright 2008 Riassence Inc.
 *   http://riassence.com/
 *
 *   You should have received a copy of the GNU General Public License along
 *   with this software package. If not, contact licensing@riassence.com
 */

/*** = Description
  ** The +HControl+ extension that defines a draggable and
  ** resizable foundation container view.
  **
  ** It's used like a HControl, but contains pre-defined active areas
  ** for resizing and moving its view. The resizable areas are defined
  ** by the edges (only horizontal or vertical resizability) and corners
  ** (both horizontal and vertical resizability). The rest triggers
  ** the movable area.
  **
  ** The droppable events are enabled by default and
  ** a lot more constructor options can be defined: see +controlDefaults+
  **
  **
***/
HDynControl = HControl.extend({
  
  componentBehaviour: ['view','control','window'],
  
  preserveTheme: true,
  
/** = Description
  * In addition to the standard HControl#constructor options,
  * the following properties can be set:
  * 
  * Key::         Description
  * +minX+::      The minimum X-coordinate allowed to be dragged or resized to.
  *               Defaults to +0+.
  * +minY+::      The minimum Y-coordinate allowed to be dragged or resized to.
  *               Defaults to +0+.
  * +maxX+::      The maximum X-coordinate allowed to be dragged or resized to.
  *               Defaults to the browser window width.
  * +maxY+::      The maximum Y-coordinate allowed to be dragged or resized to.
  *               Defaults to the browser window height.
  * +minSize+::   An array containing exactly two values: +[ width, height ]+.
  *               Defines the minimum size allowed for resizing.
  *               Defaults to +[ 24, 54 ]+
  * +maxSize+::   An array containing exactly two values: +[ width, height ]+.
  *               Defines the maximum size allowed for resizing.
  *               Defaults to the browser window size.
  * +resizeW+::   The size of the west (left) resizable edge. Defaults to +1+.
  * +resizeE+::   The size of the east (right) resizable edge. Defaults to +1+.
  * +resizeN+::   The size of the north (top) resizable edge. Defaults to +1+.
  * +resizeS+::   The size of the south (bottom) resizable edge. Default to +1+.
  * +resizeNW+::  The size of the north-west (left top) resizable corner.
  *               Defaults to +[ 1, 1 ]+
  * +resizeNE+::  The size of the north-east (right top) resizable corner.
  *               Defaults to +[ 1, 1 ]+
  * +resizeSW+::  The size of the south-west (left bottom) resizable corner.
  *               Defaults to +[ 1, 1 ]+
  * +resizeSE+::  The size of the south-east (right bottom) resizable corner.
  *               Defaults to +[ 1, 1 ]+
  * +noResize+::  A flag (when true) disables all resizing and only allows
  *               moving.
  *
  **/
  controlDefaults: (HControlDefaults.extend({
    constructor: function(_ctrl){
      var _winSize = ELEM.windowSize(),
          _winWidth = _winSize[0],
          _winHeight = _winSize[1];
      if(!this.minSize){
        this.minSize = [24,54];
      }
      if(!this.maxSize){
        this.maxSize = _winSize;
      }
      if(!this.maxX){
        this.maxX = _winWidth-this.minSize[0];
      }
      if(!this.maxY){
        this.maxY = _winHeight-this.minSize[1];
      }
      if(!this.events){
        this.events = {
          draggable: true
        };
      }
      if(!this.resizeNW){
        this.resizeNW = [ 1, 1 ];
      }
      if(!this.resizeNE){
        this.resizeNE = [ 1, 1 ];
      }
      if(!this.resizeSW){
        this.resizeSW = [ 1, 1 ];
      }
      if(!this.resizeSE){
        this.resizeSE = [ 1, 1 ];
      }
    },
    minX:      0,
    minY:      0,
    maxX:      null,
    maxY:      null,
    minSize:   null,
    maxSize:   null,
    resizeW:   1,
    resizeE:   1,
    resizeN:   1,
    resizeS:   1,
    resizeNW:  null,
    resizeNE:  null,
    resizeSW:  null,
    resizeSE:  null,
    noResize:  false
  })),
  
  draw: function(){
    this.base();
    this._initActionFns();
    this._initActionFlag();
  },
  
  /* Method for checking the change is within the limits */
  _checkConstraints: function(_leftChange,_topChange){
    var _this = this, _rect = _this.rect,
        _options = _this.options,
        _dw, _dh;
    if(_rect.width < _options.minSize[0]){
      _dw=0-(_options.minSize[0]-_rect.width);
      _rect.setWidth( _options.minSize[0]);
      if(_leftChange){
        _rect.offsetBy( _dw, 0 );
      }
    }
    else if(_rect.width > _options.maxSize[0]){
      _dw=0-(_options.maxSize[0]-_rect.width);
      _rect.setWidth( _options.maxSize[0]);
      if(_leftChange){
        _rect.offsetBy( _dw, 0 );
      }
    }
    if(_rect.height < _options.minSize[1]){
      _dh=0-(_options.minSize[1]-_rect.height);
      _rect.setHeight(_options.minSize[1]);
      if(_topChange){
        _rect.offsetBy( 0, _dh );
      }
    }
    else if(_rect.height > _options.maxSize[1]){
      _dh=0-(_options.maxSize[1]-_rect.height);
      _rect.setHeight(_options.maxSize[1]);
      if(_topChange){
        _rect.offsetBy( 0, _dh );
      }
    }
    if(_rect.left < _options.minX){
      _rect.offsetTo( _options.minX, _rect.top );
    }
    else if(_rect.left > _options.maxX){
      _rect.offsetTo( _options.maxX, _rect.top );
    }
    if(_rect.top < _options.minY){
      _rect.offsetTo( _rect.left, _options.minY );
    }
    else if(_rect.top > _options.maxY){
      _rect.offsetTo( _rect.left, _options.maxY );
    }
    _this.drawRect();
  },
  
  /* Method for returning the coordinate difference as a HPoint instance */
  _diffPoint: function(x,y){
    return this._startPoint.subtract(x,y);
  },
  
/** = Description
  * Sub-event method responder when the north-west (left-top) corner is being
  * moved to resize the view.
  *
  * By default calculates the dimensions and checks the constraints.
  *
  * = Parameters
  * +_this+:: The instance of the class it belongs to (+self+).
  * +x+::     The current x coordinate of the mouse cursor.
  * +y+::     The current y coordinate of the mouse cursor.
  *
  **/
  dynResizeNW: function(_this,x,y){
    var _dp = _this._diffPoint(x,y);
    _this.rect.setLeftTop(_this._startRect.leftTop.subtract(_dp));
    _this._checkConstraints(1,1);
  },
  
/** = Description
  * Sub-event method responder when the north-east (right-top) corner is being
  * moved to resize the view.
  *
  * By default calculates the dimensions and checks the constraints.
  *
  * = Parameters
  * +_this+:: The instance of the class it belongs to (+self+).
  * +x+::     The current x coordinate of the mouse cursor.
  * +y+::     The current y coordinate of the mouse cursor.
  *
  **/
  dynResizeNE: function(_this,x,y){
    var _dp = _this._diffPoint(x,y);
    _this.rect.setRightTop(_this._startRect.rightTop.subtract(_dp));
    _this._checkConstraints(0,1);
  },
  
/** = Description
  * Sub-event method responder when the south-west (left-bottom) corner is being
  * moved to resize the view.
  *
  * By default calculates the dimensions and checks the constraints.
  *
  * = Parameters
  * +_this+:: The instance of the class it belongs to (+self+).
  * +x+::     The current x coordinate of the mouse cursor.
  * +y+::     The current y coordinate of the mouse cursor.
  *
  **/
  dynResizeSW: function(_this,x,y){
    var _dp = _this._diffPoint(x,y);
    _this.rect.setLeftBottom(_this._startRect.leftBottom.subtract(_dp));
    _this._checkConstraints(1,0);
  },
  
/** = Description
  * Sub-event method responder when the south-east (right-bottom) corner is being
  * moved to resize the view.
  *
  * By default calculates the dimensions and checks the constraints.
  *
  * = Parameters
  * +_this+:: The instance of the class it belongs to (+self+).
  * +x+::     The current x coordinate of the mouse cursor.
  * +y+::     The current y coordinate of the mouse cursor.
  *
  **/
  dynResizeSE: function(_this,x,y){
    var _dp = _this._diffPoint(x,y);
    _this.rect.setRightBottom(_this._startRect.rightBottom.subtract(_dp));
    _this._checkConstraints(0,0);
  },
  
/** = Description
  * Sub-event method responder when the west (left) edge is being
  * moved to resize the view.
  *
  * By default calculates the dimensions and checks the constraints.
  *
  * = Parameters
  * +_this+:: The instance of the class it belongs to (+self+).
  * +x+::     The current x coordinate of the mouse cursor.
  * +y+::     The current y coordinate of the mouse cursor.
  *
  **/
  dynResizeW: function(_this,x,y){
    var _dp = _this._diffPoint(x,y);
    _this.rect.setLeft(_this._startRect.left-_dp.x);
    _this._checkConstraints(1,0);
  },
  
/** = Description
  * Sub-event method responder when the east (right) edge is being
  * moved to resize the view.
  *
  * By default calculates the dimensions and checks the constraints.
  *
  * = Parameters
  * +_this+:: The instance of the class it belongs to (+self+).
  * +x+::     The current x coordinate of the mouse cursor.
  * +y+::     The current y coordinate of the mouse cursor.
  *
  **/
  dynResizeE: function(_this,x,y){
    var _dp = _this._diffPoint(x,y);
    _this.rect.setRight(_this._startRect.right-_dp.x);
    _this._checkConstraints(0,0);
  },
  
/** = Description
  * Sub-event method responder when the north (top) edge is being
  * moved to resize the view.
  *
  * By default calculates the dimensions and checks the constraints.
  *
  * = Parameters
  * +_this+:: The instance of the class it belongs to (+self+).
  * +x+::     The current x coordinate of the mouse cursor.
  * +y+::     The current y coordinate of the mouse cursor.
  *
  **/
  dynResizeN: function(_this,x,y){
    var _dp = _this._diffPoint(x,y);
    _this.rect.setTop(_this._startRect.top-_dp.y);
    _this._checkConstraints(0,1);
  },
  
/** = Description
  * Sub-event method responder when the south (bottom) edge is being
  * moved to resize the view.
  *
  * By default calculates the dimensions and checks the constraints.
  *
  * = Parameters
  * +_this+:: The instance of the class it belongs to (+self+).
  * +x+::     The current x coordinate of the mouse cursor.
  * +y+::     The current y coordinate of the mouse cursor.
  *
  **/
  dynResizeS: function(_this,x,y){
    var _dp = _this._diffPoint(x,y);
    _this.rect.setBottom(_this._startRect.bottom-_dp.y);
    _this._checkConstraints(0,0);
  },
  
/** = Description
  * Sub-event method responder when moving the offset of the view.
  * This is called when no resize areas are triggered.
  *
  * By default calculates the dimensions and checks the constraints.
  *
  * = Parameters
  * +_this+:: The instance of the class it belongs to (+self+).
  * +x+::     The current x coordinate of the mouse cursor.
  * +y+::     The current y coordinate of the mouse cursor.
  *
  **/
  dynDrag: function(_this,x,y){
    var _dp = _this._diffPoint(x,y);
    _this.rect.offsetTo(_this._startRect.leftTop.subtract(_dp));
    _this._checkConstraints(1,1);
  },
  
  /* Method to initialize the rules */
  _initActionFns: function(){
    this._actionFns = [];
    this._actionCrsr = [
      'nw-resize', 'ne-resize', 'sw-resize', 'se-resize',
      'w-resize', 'e-resize', 'n-resize', 's-resize', 'move'
    ];
    var i, _this = this,
    _resizeNW=0,_resizeNE=1,_resizeSW=2,_resizeSE=3,
    _resizeW =4, _resizeE=5, _resizeN=6, _resizeS=7, _drag=8,
    _actionFns=this._actionFns;
    _actionFns[_resizeNW] = _this.dynResizeNW;
    _actionFns[_resizeNE] = _this.dynResizeNE;
    _actionFns[_resizeSW] = _this.dynResizeSW;
    _actionFns[_resizeSE] = _this.dynResizeSE;
    
    _actionFns[_resizeW] = _this.dynResizeW;
    _actionFns[_resizeE] = _this.dynResizeE;
    _actionFns[_resizeN] = _this.dynResizeN;
    _actionFns[_resizeS] = _this.dynResizeS;
    
    _actionFns[_drag] = _this.dynDrag;
    
  },
  
  
/** Calculates the rectangles for all the active areas.
  **/
  makeRectRules: function(){
    var _opts=this.options, _rect=this.rect;
    return [
      // corners:
      [0,0,_opts.resizeNW[0],_opts.resizeNW[1]], // NW
      [_rect.width-_opts.resizeNE[0],0,_rect.width,_opts.resizeNE[1]], // NE
      [0,_rect.height-_opts.resizeSW[1],_opts.resizeSW[0],_rect.height], // SW
      [_rect.width-_opts.resizeSE[0],_rect.height-_opts.resizeSE[1],_rect.width,_rect.height], // SE
      // borders:
      [0,_opts.resizeN,_opts.resizeW,_rect.height-_opts.resizeS], // W
      [_rect.width-_opts.resizeE,_opts.resizeN,_rect.width,_rect.height-_opts.resizeS], // E
      [_opts.resizeW,0,_rect.width-_opts.resizeE,_opts.resizeN], // N
      [_opts.resizeW,_rect.height-_opts.resizeS,_rect.width-_opts.resizeE,_rect.height], // S
      // drag-area:
      [_opts.resizeW,_opts.resizeN,_rect.width-_opts.resizeE,_rect.height-_opts.resizeS]
    ];
  },
  
  /* Method used for initializing the action flags. */
  _initActionFlag: function(){
    this._actionFlag = -1;
    this._actionRects = [];
    var i=0,_rr,_rectRules = this.makeRectRules();
    for(;i<9;i++){
      _rr = _rectRules[i];
      this._actionRects.push( HRect.nu(_rr[0],_rr[1],_rr[2],_rr[3]) );
    }
  },
  
  /* Method used to detect the action flags. Also sets the cursor. */
  _detectActionFlag: function(){
    var i,
    _actionPoint = this._startPoint.subtract(this.rect.left,this.rect.top),
    _actionRects = this._actionRects;
    if(this.options.noResize && _actionRects[8].contains(_actionPoint)){
      this._actionFlag = 8;
      this.setStyle('cursor',this._actionCrsr[8]);
      return;
    }
    for(i=0;i!==9;i++){
      if(_actionRects[i].contains(_actionPoint)){
        this._actionFlag=i;
        this.setStyle('cursor',this._actionCrsr[i]);
        return;
      }
    }
  },
  
/** = Description
  * Event method responder that decides and initializes
  * a resize or move operation on the drag events.
  *
  * = Parameters
  * +x+::              The current x coordinate of the mouse cursor.
  * +y+::              The current y coordinate of the mouse cursor.
  * +_isRightButton+:: A flag that is true when right (or context) clicking.
  *
  * = Returns
  * +true+
  *
  **/
  startDrag: function(x,y,_isRightButton){
    var _parent = this.parent;
    if(_parent.elemId){
      x-=_parent.pageX();
      y-=_parent.pageY();
    }
    this._startPoint = new HPoint(x,y);
    this._startRect  = new HRect( this.rect );
    this._detectActionFlag();
    if(this._actionFlag!==-1){
      this._actionFns[this._actionFlag](this,x,y);
    }
    return true; // prevents text selection
  },
  
/** = Description
  * Event method responder that performs a resize or move recalculation
  * and redraw call when dragging one of the corners or edges to resize or
  * any other area to move.
  *
  * = Parameters
  * +x+::              The current x coordinate of the mouse cursor.
  * +y+::              The current y coordinate of the mouse cursor.
  *
  * = Returns
  * +true+
  *
  **/
  drag: function(x,y){
    var _parent = this.parent;
    if(_parent.elemId){
      x-=_parent.pageX();
      y-=_parent.pageY();
    }
    if(this._actionFlag!==-1){
      this._actionFns[this._actionFlag](this,x,y);
    }
    return true; // prevents text selection
  },

/** = Description
  * Event method responder that ends a resize or move operation.
  * Also restores the mouse cursor.
  *
  * = Parameters
  * +x+::              The current x coordinate of the mouse cursor.
  * +y+::              The current y coordinate of the mouse cursor.
  * +_isRightButton+:: A flag that is true when right (or context) clicking.
  *
  * = Returns
  * +true+
  *
  **/
  endDrag: function(x,y,_isRightButton){
    this.base();
    var _parent = this.parent;
    if(_parent.elemId){
      x-=_parent.pageX();
      y-=_parent.pageY();
    }
    if(this._actionFlag!==-1){
      this._actionFns[this._actionFlag](this,x,y);
    }
    this.setStyle('cursor','default');
    this._initActionFlag();
    return true; // prevents text selection
  }
});
