function _getOwnerDocument (param)
{
  return param.ownerDocument ? param.ownerDocument : param.document;
}

function _constructHtmlWindow (win, init)
{
  _waitAndCustomizeDocument (
    win,
    function (w) { return w.document; },
    init
  );
}

function _constructHtmlFrame (frame, init)
{
  _waitAndCustomizeDocument (
    frame,
    function (fr) { return fr.contentWindow.document; },
    function (doc)
    {
      if (!doc.frameElement)
        doc.frameElement = frame;

      if (init)
        init (doc);
    }
  );
}

function _waitAndCustomizeDocument (obj, extractDoc, init)
{
  var f = function()
  {
    var doc = _customizeHtmlDocument (extractDoc (obj));
    if (init)
      init (doc);

    _customizeHtmlWindow (doc.parentWindow);

    if (obj.detachEvent)
      obj.detachEvent ("onload", f);
    else if (obj.removeEventListener)
      obj.removeEventListener ("load", f, false);
  };

  if (obj.attachEvent)
    obj.attachEvent ("onload", f);
  else if (obj.addEventListener)
    obj.addEventListener ("load", f, false);
  else
    alert ("You browser doesn't support 'onload' event.");
}


function _customizeHtmlDocument (doc)
{
  doc._frame                    = HtmlDocument__frame;
  doc._table                    = HtmlDocument__table;
  doc._form                     = HtmlDocument__form;
  doc._button                   = HtmlDocument__button;
  doc._imageButton              = HtmlDocument__imageButton;
  doc._radioGroup               = HtmlDocument__radioGroup;
  doc._checkBox                 = HtmlDocument__checkBox;
  doc._label                    = HtmlDocument__label;
  doc._textArea                 = HtmlDocument__textArea;
  doc._select                   = HtmlDocument__select;
  doc._input                    = HtmlDocument__input;
  doc._icon                     = HtmlDocument__icon;
  doc._makeFrameset             = HtmlDocument__makeFrameset;
  doc._makeControlPane          = HtmlDocument__makeControlPane;
  doc._makeToolBar              = HtmlDocument__makeToolBar;

  doc._getWidth   = HtmlDocument__getWidth;
  doc._getHeight  = HtmlDocument__getHeight;

  //doc.charset = "UTF-8";

  doc._body = doc.body;
  doc._setBody = function (newBody)
  {
    this._body.parentNode.replaceChild (newBody, this._body);
    this._body = newBody;
  };

  //hack
  doc._isIE = doc.createStyleSheet;

  if (!doc.createStyleSheet) {
    doc.createStyleSheet = function (href)
    {
      var ss = this.createElement ("link");
      ss.rel  = "stylesheet";
      ss.href = href;
      this.getElementsByTagName("head")[0].appendChild (ss);
    }
  }

  if (!doc.parentWindow)
    doc.parentWindow = doc.defaultView;


  return doc;
}


function _customizeHtmlWindow (win)
{
  win._ready = true;

  win.onfocus = window.top.__win_onfocus__;
  win.onblur  = window.top.__win_onblur__;

  if (!win.showModelessDialog) {
    win.showModelessDialog = function (url, args, features, id)
    {
      return this.open (url, "dialog_" + id, features);
    }
  }

  if (!win.clipboardData) {
    win.clipboardData = new _ClipboardData();
  }

  win._isIE = win.document._isIE;
}

function _ClipboardData ()
{
  this.setData = function (format, data)
  {
    netscape.security.PrivilegeManager.enablePrivilege ('UniversalXPConnect');

    var clip = Components.classes ['@mozilla.org/widget/clipboard;1']
      .createInstance (Components.interfaces.nsIClipboard);
    var trans = Components.classes ['@mozilla.org/widget/transferable;1']
      .createInstance (Components.interfaces.nsITransferable);
    var str = Components.classes["@mozilla.org/supports-string;1"]
      .createInstance(Components.interfaces.nsISupportsString);

    str.data = data;

    trans.addDataFlavor ("text/unicode");
    trans.setTransferData ("text/unicode", str, data.length*2);

    var clipid = Components.interfaces.nsIClipboard;
    clip.setData (trans, null, clipid.kGlobalClipboard);
  }
}


function _customizeBody (body)
{
  body.onclick       = window.top.__onclick__;
  body.onkeydown     = window.top.__onkeydown__;
  body.oncontextmenu = window.top.__oncontextmenu__;
  return body;
}


function HtmlDocument__getWidth ()
{
  if (this._isIE)
    return this._body.clientWidth;
  else
    return this.documentElement.offsetWidth - 2;
}

function HtmlDocument__getHeight ()
{
  if (this._isIE)
    return this._body.clientHeight;
  else
    return this.documentElement.offsetHeight - 2;
}


function HtmlDocument__makeFrameset (rows, cols, border)
{
  var fs = this.createElement ("frameset");
  if (border != null)
    fs.setAttribute ("border", border);

  fs.framespacing = 0;
  fs.frameborder  = 0;

  fs.rows = rows;
  fs.cols = cols;
  return fs;
}


function HtmlDocument__frame (id, name, scrolling)
{
  if (!name)
    name = id;

  var f;
  if (this._isIE) {
    f = this.createElement (
      '<frame id="' + id + '" name="' + name +
      '" src="/siberservlet/blank/blank' + id + '.html">'
    );
  }
  else {
    f = this.createElement ("frame");
    f.id = id;
    f.name = name;
    f.src = "/siberservlet/blank/blank" + id + ".html";
  }

  if (scrolling)
    f.scrolling = scrolling;
  f.onfocus = window.top.__frame_onfocus__;
  f.onblur  = window.top.__frame_onblur__;
  window.top._readyAll = false;
  return f;
}

function HtmlDocument__table ()
{
  var tbl = this.createElement ("table");
  tbl._tBody = tbl.appendChild (this.createElement ("tbody"));
  return tbl;
}

function HtmlDocument__form (id)
{
  var frm = this.createElement ("form");
  frm.id = id;
  frm.method = "POST";
  frm.target = "__sbltarget__";
  frm.action = window.top._currentLocation();
  frm.acceptCharset = "UTF-8";
  frm.encoding = "multipart/form-data";
  frm.onsubmit = Form__onsubmit;

  var rq = this._input ("__request__", "hidden", "__request__", "");
  frm.appendChild (rq);
  var top = window.top;
  if (!top._forms)
    top._forms = [];
  top._forms[id] = frm;
  return frm;
}


function HtmlDocument__input (id, type, name, value)
{
  var input = this.createElement ("input");
  input.id = id;
  input.name = name;
  input.type = type;

  if (value)
    input.setAttribute ('value', value);
  return input;
}

function HtmlDocument__icon (iconref)
{
  var img = this.createElement ("img");
  img.src = iconref;
  img.border = 0;
  img.hspace = 2;
  img.style.verticalAlign = "middle";
  return img;
}

function Form__onsubmit()
{
  return false;
}

//------------------------------------------------------------------------

function _download (url)
{
  var frame = this.document.getElementById ('__sbldownload__');
  frame.src = url;
}


function _endModal (result)
{
  this.returnValue = result;
  this.close();
}

function _finishDialog()
{
  this._endModal ([this.id, this._getState().toString()]);
}


function _execRequest (name, formName)
{
  if (this._readyAll || (this._ready && _childFramesReady (this))) {
    this.clearInterval (this._execRequestInterval);
    this._execRequestInterval = null;
    this._readyAll = true;
    _executeRequest (name, formName);
  }
  else if (!this._execRequestInterval) {
    this._execRequestInterval = this.setInterval (
      "this._execRequest('" + name + "');",
      50
    );
  }
}

function _childFramesReady (win)
{
  var frames = win.frames;
  for (var i = 0; i < frames.length; ++i) {
    var frm = frames[i];
    if (!frm._ready && frm.name != "__sbltarget__")
      return false;
    if (!_childFramesReady (frm))
      return false;
  }
  return true;
}


function _executeRequest (name, formName, closedWindowState)
{
  var form = formName ? this._forms['frm' + formName] :
                        this._forms['frm__service__'];
  var request = this._createRequest (name, closedWindowState);
  form.elements['__request__'].value = request.toString();
  form.submit();
}

function _createRequest (name, closedWindowState)
{
  var request = new XmlElementNode ('request');
  request.setAttribute ('action', name);
  if (this.navigator._global)
    request.addChild (this.navigator._global._getState());

  request.setAttribute ('window', this.id);
  request.addChild (this._getState());

  if (closedWindowState) {
    request.setAttribute ('closed-window', closedWindowState[0]);
    request.addLiteral (closedWindowState[1]);
  }
  return request;
}

//------------------------------------------------------------------------

function __win_onfocus__ (event)
{
  var fr = this.frameElement;
  while (fr && fr.parentNode) {
    fr.parentNode._focus = fr;
    fr = _getOwnerDocument (fr).frameElement;
  }

  var doc = this.document;
  doc._focused = true;
  var body = doc._body;
  if (body && body.__processOnFocusChanged__)
    body.__processOnFocusChanged__ (true);
}


function __win_onblur__ (event)
{
  var doc = this.document;
  doc._focused = false;
  var body = doc._body;
  if (body && body.__processOnFocusChanged__)
    return body.__processOnFocusChanged__ (false);
}


function __frame_onfocus__ ()
{
  if (this.parentNode)
    this.parentNode._focus = this;
  var doc = this.contentWindow.document;
  doc._focused = true;
  var body = doc._body;
  if (body && body.__processOnFocusChanged__)
    body.__processOnFocusChanged__ (true);
}


function __frame_onblur__ ()
{
  var doc = this.contentWindow.document;
  doc._focused = false;
  var body = doc._body;
  if (body && body.__processOnFocusChanged__)
    return body.__processOnFocusChanged__ (false);
}


function __onclick__ (event)
{
  var doc = _getOwnerDocument (this);
  if (!event)
    event = doc.parentWindow.event;
  if (event.stopPropagation)
    event.stopPropagation();

  var elm = event.srcElement ? event.srcElement : event.target;
  while (elm && !elm.__processOnClick__)
    elm = elm.parentNode;

  if (elm)
    return elm.__processOnClick__ (doc, event);
  else
    return false;
}


function __onkeydown__ (event)
{
  var doc = _getOwnerDocument (this);
  if (!event)
    event = doc.parentWindow.event;
  if (event.stopPropagation)
    event.stopPropagation();

  if (event.ctrlKey && event.shiftKey && event.altKey && event.keyCode == 123)
    alert (doc.documentElement.innerHTML);
}


function __oncontextmenu__ (event)
{
  var doc = _getOwnerDocument (this);
  if (!event)
    event = doc.parentWindow.event;
  if (event.stopPropagation)
    event.stopPropagation();

  var elm = event.srcElement ? event.srcElement : event.target;
  while (elm && !elm.__processOnContextMenu__)
    elm = elm.parentNode;

  if (elm)
    return elm.__processOnContextMenu__ (doc, event);
  else
    return false;
}


//------------------------------------------------------------------------
// child windows
//------------------------------------------------------------------------

function _currentLocation()
{
  var loc = window.top.document.location;
  var pathname = loc.pathname;
  if (pathname.charAt(0) != '/')
    pathname = '/' + pathname;
  return loc.protocol + "//" + loc.host + pathname;
}

function _preparedWindowUrl (redirect, id)
{
  return (redirect ? redirect : _currentLocation()) + "?window-id=" + id;
}

function _makeFeatures (width, height, isResizable, haveStatus)
{
  if (window._isIE) {
    return "dialogWidth:" + (width+8)
           + "px;dialogHeight:" + (height + 24)
           + "px;resizable:" + (isResizable? 'yes' : 'no')
           + ";status:" + (haveStatus? 'yes' : 'no')
           + ";help:no";
  }
  else {
    return "width=" + width
           + "px,height=" + height
           + "px,top=" + ((screen.height - height) / 2)
           + "px,left=" + ((screen.width - width) / 2)
           + "px,resizable=" + (isResizable? 'yes' : 'no')
           + ",status=" + (haveStatus? 'yes' : 'no')
           + ",help=no,dialog=yes,modal=yes";
  }
}

function _showPreparedModalDialog (id, actionToResume, width, height, isResizable)
{
  var dialog = new _ModalDialog (
    id, this,
    function (state)
    {
      if (state)
        this._executeRequest (actionToResume, null, state);

      delete this.__properties__["resumedAction"];
      delete this.__properties__["context"];
    }
  );
  dialog._open (
    _preparedWindowUrl (null, id),
    this,
    _makeFeatures (width, height, isResizable, false)
  );
}

function _showPreparedWindow (id, width, height, isResizable, redirect)
{
  this.showModelessDialog (
    _preparedWindowUrl (redirect, id),
    this,
    _makeFeatures (width, height, isResizable, false),
    id
  );
}


function _ModalDialog (id, win, onclose)
{
  this._open = function (url, args, features)
  {
    if (win.showModalDialog)
      onclose (win.showModalDialog (url, args, features));
    else {
      win.document._getArgs = function() { return args; };
      var dialog = win.open (url, "dialog_" + id, features);
      dialog.addEventListener (
        "load",
        function () {
          dialog.onunload = function() 
          {
            onclose (this.returnValue); 
            delete win.document._getArgs;
          };
        },
        false
      );
    }
  }
}

//------------------------------------------------------------------------
// getState()
//------------------------------------------------------------------------

function _getState()
{
  var state = GuiObject__getState.call (this, 'window');
  state.addChild (this._rootPane._getState());
  return state;
}

function GuiObject__getState (tag)
{
  var state = new XmlElementNode (tag);
  state.setAttribute ('name', this.name);
  state.setAttribute ('id', this.id);
  state.setAttribute ('classid', this._classid);
  var properties =  this.__properties__;
  for (var p in properties) {
    var val = properties[p];
    if (val || val == '') {
      var prop = new XmlElementNode ("property");
      prop.setAttribute ('name', p);
      prop.addText (val);
      state.addChild (prop);
    }
  }
  return state;
}

function Widget__getState()
{
  var state = GuiObject__getState.call (this, "widget");
  return state;
}


function ComplexPane__getState()
{
  var state = Widget__getState.call (this);
  var body = this.contentWindow.document._body;
  var borderAttr = body.attributes["border"];
  state.setAttribute ("border", borderAttr ? borderAttr.value : null);
  var focus = body._focus;
  if (!focus)
    focus = body.firstChild;

  state.setAttribute ('focus', focus.id);
  var frames = body.childNodes;
  for (var i = 0; i < frames.length; ++i) {
    var pane = frames[i];
    if (pane._getState)
      state.addChild (pane._getState());
  }
  return state;
}

function HtmlPane__getState()
{
  return Widget__getState.call (this);
}

function _findPane (parent, id)
{
  if (parent.id == id)
    return parent;

  var doc = parent.contentWindow.document;
  var bodyTag = doc._body.tagName;
  var pane = null;
  if (bodyTag == 'frameset' || bodyTag == 'FRAMESET') {
    pane = doc.getElementById (id);
    if (!pane) {
      var frames = doc._body.getElementsByTagName ('frame');
      for (var i = 0; !pane && i < frames.length; ++i)
        pane = _findPane (frames[i], id);
    }
  }
  return pane;
}

function Global__findWidget (name)
{
  var ref = this._widgetRefs[name];
  var pane = _findPane (ref[0]._rootPane, ref[1]);
  return [ref[0], pane];
}

function Global__getState()
{
  var state = GuiObject__getState.call (this, "global");
  var refs = this._widgetRefs;
  for (var name in refs) {
    var ref = refs[name];
    var pane = _findPane (ref[0]._rootPane, ref[1]);
    if (pane && pane._getState) {
      var paneState = pane._getState();
      paneState.setAttribute ("regname", name);
      state.addChild (paneState);
    }
  }
  return state;
}


// [TODO: do console changes honestly]
function _changeConsole (consolePath)
{
  window.top.location = RESOURCE_PATH + "servlet/" + consolePath;
  return false;
}


// Deskband helper
function _getGlobalProperty (name)
{
  var global = window.top.navigator._global;
  return global ? global.__properties__[name] : null;
}


function _scrollIntoView (element, container)
{
  var offset = _calcOffset (element, container);
  var scrollTop = container.scrollTop;
  if (offset < scrollTop)
    element.scrollIntoView (true);

  if (offset + element.offsetHeight > scrollTop + container.offsetHeight)
    element.scrollIntoView (false);
}

function _calcOffset (element, container)
{
  if (!container || !element || element == container)
    return 0;

  return element.offsetTop + _calcOffset (element.offsetParent, container);
}

