with (classes) {
//
with (GUI) {
//
with (Common) {
//
Common.Alignable = function (this_alignment, this_size, minimum_width, minimum_height)
{
 if (this.Alignable) return this; this.Alignable = Alignable; // RTTI

 // classes.Common.Watchable.call(this);

 with (Alignable)
 {
  this.position = position;
  this.positionChildren = positionChildren;
  this.getPosition = getPosition;
  this.getBorder_position = getBorder_position;

  this.calculateIntrinsics = calculateIntrinsics;

  this._Top =
  this._Left =
  this._Width =
  this._Height = null;

  this.marginTop_size =
  this.marginLeft_size =
  this.marginBottom_size =
  this.marginRight_size =

  this.paddingTop_size =
  this.paddingLeft_size =
  this.paddingBottom_size =
  this.paddingRight_size =

  this.borderTop_size =
  this.borderLeft_size =
  this.borderBottom_size =
  this.borderRight_size = null;

  this.Alignment = this._Alignment = this_alignment? this_alignment : AlignNone;

  this.requested_size = this_size? size(this_size) : 0;
  this.minimum_width = minimum_width? size(minimum_width) : 0;
  this.minimum_height = minimum_height? size(minimum_height) : 0;

  this.introspect = true;

  this.positionChildren_timeout = null;

  with (this)
  {
   style.position = "absolute";

   // calculateIntrinsics();

   // watch("Alignment", set);
  }
 }

 return null;
}

Alignable.size = Number;

Alignable.AlignNone = 7; // 1;
Alignable.AlignTop = 2;
Alignable.AlignBottom = 3;
Alignable.AlignLeft = 4;
Alignable.AlignRight = 5;
Alignable.AlignClient = 6;

Alignable.top_border = 1;
Alignable.bottom_border = 2;
Alignable.left_border = 3;
Alignable.right_border = 4;

Alignable.construct = function (Positionable_HTMLElement, _Alignment, this_size, minimum_width, minimum_height)
{
 return Alignable.call(Positionable_HTMLElement, _Alignment, this_size, minimum_width, minimum_height);
}

Alignable.position = function (excludeChildren) { with (Alignable) with (this)
{
 introspect = false;

 switch (_Alignment)
 {
  case AlignTop:
  case AlignBottom: _Width = 100; _Height = size(requested_size);
   break;
  case AlignLeft:
  case AlignRight: _Height = 100; _Width = size(requested_size);
   break;
  case AlignClient: _Width = _Height = 100;
   break;
  default: if (getPosition == Alignable.getPosition) return;
 }

 var parent_width = parentNode.clientWidth; // offsetWidth

 var minimumWidth_size = minimum_width? (100 * minimum_width / parent_width) : 0;

 if (_Width < minimumWidth_size) _Width = minimumWidth_size;

 var parent_height = parentNode.clientHeight; // offsetHeight

 var minimumHeight_size = (minimum_height && parent_height)? (100 * minimum_height / parent_height) : 0;

 if (_Height < minimumHeight_size) _Height = minimumHeight_size;

 getPosition();

 calculateIntrinsics();

 style.top = String(_Top) + "%";
 style.left = String(_Left) + "%";

 var widthOffset_size = paddingLeft_size + paddingRight_size + borderLeft_size + borderRight_size;

 if (_Width > minimumWidth_size)
 {
  if (! parentNode.Alignable || _Width == 100) style.width = "100%";
  else style.width = String(parent_width * _Width / 100 - widthOffset_size) + "px";
 }
 else
 {
  _Width = minimumWidth_size;

  style.width = (minimum_width - paddingLeft_size - paddingRight_size - borderLeft_size - borderRight_size) + "px";
 }

 var heightOffset_size = paddingTop_size + paddingBottom_size + borderTop_size + borderBottom_size;

 if (_Height > minimumHeight_size)
 {
  if (! parentNode.Alignable || _Height == 100) style.height = "100%";
  else style.height = String(parent_height * _Height / 100 - heightOffset_size) + "px";
 }
 else
 {
  _Height = minimumHeight_size;

  style.height = (minimum_height - heightOffset_size) + "px";
 }

 if (! (excludeChildren || positionChildren_timeout)) positionChildren_timeout = setTimeout(function () { positionChildren_timeout = null; positionChildren(); }, 0);

 introspect = true;
}}
//
Alignable.positionChildren = function () { with (this)
{
 var This_HTMLElement = lastChild, Previous_HTMLElement;

 while (This_HTMLElement != firstChild)
 {
  Previous_HTMLElement = This_HTMLElement.previousSibling;

  if (This_HTMLElement.Alignable) // nodeType == dom.Node.ELEMENT_NODE)
  {
   var This_Node = This_HTMLElement, Next_Node;

   do
   {
    if (This_Node.Alignable) Next_Node = This_Node;

    This_Node = This_Node.previousSibling;

   } while (This_Node && (! This_Node.Alignable || This_Node.Alignment > This_HTMLElement.Alignment));

   if (This_HTMLElement != Next_Node) insertBefore(This_HTMLElement, Next_Node);
  }

  This_HTMLElement = Previous_HTMLElement;
 }

 while (This_HTMLElement)
 {
  if (This_HTMLElement.Alignable) This_HTMLElement.position();

  This_HTMLElement = This_HTMLElement.nextSibling;
 }
}}
//
Alignable.getPosition = function () { with (Alignable) with (this)
{
 switch (_Alignment)
 {
  case AlignTop:
  {
   _Top = getBorder_position(top_border, _Alignment);
   _Left = 0;
   _Width = 100;
  }
   break;
  case AlignBottom:
  {
   _Top = getBorder_position(bottom_border, _Alignment) - _Height;
   _Left = 0;
   _Width = 100;
  }
   break;
  case AlignLeft:
  {
   _Top = getBorder_position(top_border, AlignTop);
   _Height = getBorder_position(bottom_border, AlignBottom) - _Top;
   _Left = getBorder_position(left_border, _Alignment);
  }
   break;
  case AlignRight:
  {
   _Top = getBorder_position(top_border, AlignTop);
   _Height = getBorder_position(bottom_border, AlignBottom) - _Top;
   _Left = getBorder_position(right_border, _Alignment) - _Width;
  }
   break;
  case AlignClient:
  {
   _Top = getBorder_position(top_border, AlignTop);
   _Height = getBorder_position(bottom_border, AlignBottom) - _Top;
   _Left = getBorder_position(left_border, AlignLeft);
   _Width = getBorder_position(right_border, AlignRight) - _Left;
  }
   break;
  default:;
 }
}}

Alignable.getBorder_position = function (this_border, this_alignment) { with (Alignable) with (this)
{
 if (this_alignment == AlignNone || this_alignment == AlignClient) return null; // throw

 var This_HTMLElement = previousSibling;

 while (This_HTMLElement && This_HTMLElement._Alignment != this_alignment)
  This_HTMLElement = This_HTMLElement.previousSibling;

 if (! This_HTMLElement) return (this_border == top_border || this_border == left_border)? 0 : 100;

 var border_position = 0;

 with (This_HTMLElement) switch (this_border)
 {
  case top_border: border_position = size(_Top) + size(_Height);
   break;
  case right_border: border_position = size(_Left);
   break;
  case bottom_border: border_position = size(_Top);
   break;
  case left_border: border_position = size(_Left) + size(_Width);
   break;
  default:; // throw
 }

 return border_position;
}}
//
Alignable.calculateIntrinsics = function () { with (this)
{
 var Result_Array;

 with (/\d+(?:\.\d{1,})?/) with (ownerDocument.defaultView.getComputedStyle(this, null))
 {
  marginTop_size = (Result_Array = exec(getPropertyValue("margin-top")))? Number(Result_Array[0]) : 0;
  marginRight_size = (Result_Array = exec(getPropertyValue("margin-right")))? Number(Result_Array[0]) : 0;
  marginBottom_size = (Result_Array = exec(getPropertyValue("margin-bottom")))? Number(Result_Array[0]) : 0;
  marginLeft_size = (Result_Array = exec(getPropertyValue("margin-left")))? Number(Result_Array[0]) : 0;

  paddingTop_size = (Result_Array = exec(getPropertyValue("padding-top")))? Number(Result_Array[0]) : 0;
  paddingRight_size = (Result_Array = exec(getPropertyValue("padding-right")))? Number(Result_Array[0]) : 0;
  paddingBottom_size = (Result_Array = exec(getPropertyValue("padding-bottom")))? Number(Result_Array[0]) : 0;
  paddingLeft_size = (Result_Array = exec(getPropertyValue("padding-left")))? Number(Result_Array[0]) : 0;

  borderTop_size = (Result_Array = exec(getPropertyValue("border-top-width")))? Number(Result_Array[0]) : 0;
  borderRight_size = (Result_Array = exec(getPropertyValue("border-right-width")))? Number(Result_Array[0]) : 0;
  borderBottom_size = (Result_Array = exec(getPropertyValue("border-bottom-width")))? Number(Result_Array[0]) : 0;
  borderLeft_size = (Result_Array = exec(getPropertyValue("border-left-width")))? Number(Result_Array[0]) : 0;
 }
}}
//
}
//
}
//
}

