function scBrowser() {
  this.popups = null;
}

scBrowser.prototype.initialize = function() {
  /* excess in content editor - see if we need this in other apps */
  /* this.attachEvent(document.body, "onclick", function() { scGeckoClosePopups("body onclick"); }); */
  this.attachEvent(document.body, "ondblclick", function() { scGeckoClosePopups("body ondblclick"); });
  this.attachEvent(document.body, "oncontextmenu", function() { scGeckoClosePopups("body oncontextmenu"); });

  this.initializeFixsizeElements();  
  Event.observe(window, "resize", function() { setTimeout(scForm.browser.resizeFixsizeElements.bind(scForm.browser), 100); });
}

scBrowser.prototype.attachEvent = function(object, eventName, method) {
  eventName = eventName.replace(/on/, "");
  
  method._eventHandler = function(e) {
    window.event = e;
    return method();
  };
  
  object.addEventListener(eventName, method._eventHandler, false);
}

scBrowser.prototype.detachEvent = function(object, eventName, method) {
  eventName = eventName.replace(/on/, "");
  
  if (typeof(method._eventHandler) == "function") {
    object.removeEventListener(eventName, method._eventHandler, false);
  }
  else {
    object.removeEventListener(eventName, method, true);
  }
}

scBrowser.prototype.clearEvent = function(evt, cancelBubble, returnValue, keyCode) {
  if (evt != null) {
    if (cancelBubble == true) {
      evt.stopPropagation();
    }
    if (returnValue == false) {
      evt.preventDefault();
    }
  }
  // ignore keycode
}
    
scBrowser.prototype.closePopups = function(reason, exclusions) {
  if (this.popups != null) {
    if (reason == "mainWindowBlur") {
      return;
    }
    
    for(var n = 0; n < this.popups.length; n++) {
      if (exclusions && exclusions.indexOf(this.popups[n]) >= 0) {
        continue;
      }
      
      var ctl = $(this.popups[n]);      
      if (ctl) {
        ctl.remove();
      }
    }
  }
  
  this.popups = exclusions ? $$(".scPopup") : null;
}
    
scBrowser.prototype.createHttpRequest = function() {
  return new XMLHttpRequest();
}

scBrowser.prototype.getControl = function(id, doc) {
  return (doc != null ? doc : document).getElementById(id);
}

scBrowser.prototype.getEnumerator = function(collection) {
  return new scGeckoEnumerator(collection);
}

scBrowser.prototype.getFrameElement = function(win) {
  for(var e = scForm.browser.getEnumerator(win.parent.document.getElementsByTagName("IFRAME")); !e.atEnd(); e.moveNext()) {

    var ctl = e.item();
    
    if (ctl.contentWindow == win) {
      return ctl;
    }
  }
 
  return null;
}

scBrowser.prototype.getImageSrc = function(img) {
  return img.src;
}

scBrowser.prototype.getMouseButton = function(evt) {
  return 1;
}

scBrowser.prototype.getNextSibling = function(ctl) {
  ctl = ctl.nextSibling;
  
  while (ctl != null && ctl.nodeType != 1) {
    ctl = ctl.nextSibling;
  }
  
  return ctl;
}

scBrowser.prototype.getOffset = function(evt) {
  var result = new Object();
  
  result.x = evt.pageX != null ? evt.pageX : 0;
  result.y = evt.pageY != null ? evt.pageY : 0;
  
  return result;
}

scBrowser.prototype.getOuterHtml = function(control) {
  var attr;
  var attrs = control.attributes;
  var str = "<" + control.tagName;
  
  for(var i = 0; i < attrs.length; i++) {
    attr = attrs[i];
    if (attr.specified) {
      str += " " + attr.name + '="' + attr.value + '"';
    }
  }
  
  switch (control.tagName) {
    case "AREA":
    case "BASE":
    case "BASEFONT":
    case "COL":
    case "FRAME":
    case "HR":
    case "IMG":
    case "BR":
    case "INPUT":
    case "ISINDEX":
    case "LINK":
    case "META":
    case "PARAM":
      return str + ">";
  }
  
  return str + ">" + control.innerHTML + "</" + control.tagName + ">";
}

scBrowser.prototype.getParentWindow = function(doc) {
  var result = doc.contentWindow;
  
  if (result == null) {
    result = doc.defaultView;
  }
  
  return result;
}

scBrowser.prototype.getPreviousSibling = function(ctl) {
  ctl = ctl.previousSibling;
  
  while (ctl != null && ctl.nodeType != 1) {
    ctl = ctl.previousSibling;
  }
  
  return ctl;
}

scBrowser.prototype.getSrcElement = function(evt) {
  return evt.target;
}

scBrowser.prototype.getTableRows = function(table) {
  var result = new Array()
 
  for(var n = 0; n < table.childNodes.length; n++) {
    var ctl = table.childNodes[n];
    
    if (ctl.tagName == "TR") {
      result.push(ctl)
    }
    else if (ctl.tagName == "TBODY") {
      for(var i = 0; i < ctl.childNodes.length; i++) {
        var c = ctl.childNodes[i];
        
        if (c.tagName == "TR") {
          result.push(c);
        }
      }
    }
  } 
  
  return result;
}

scBrowser.prototype.prompt = function(text, defaultValue) {
  return prompt(text, defaultValue);
}

scBrowser.prototype.insertAdjacentHTML = function(control, where, html) {
  var df; 
  var r = control.ownerDocument.createRange();
  
  switch (String(where).toLowerCase()) {
    case "beforebegin":
      r.setStartBefore(control);
      df = r.createContextualFragment(html);
      control.parentNode.insertBefore(df, control);
      break;
      
    case "afterbegin":
      r.selectNodeContents(control);
      r.collapse(true);
      df = r.createContextualFragment(html);
      control.insertBefore(df, control.firstChild);
      break;
      
    case "beforeend":
      r.selectNodeContents(control);
      r.collapse(false);
      df = r.createContextualFragment(html);
      control.appendChild(df);
      break;
      
    case "afterend":
      r.setStartAfter(control);
      df = r.createContextualFragment(html);
      control.parentNode.insertBefore(df, control.nextSibling);
      break;
  } 
}

scBrowser.prototype.releaseCapture = function(control) {
  scGeckoCapturedControl = null;
  this.releaseCaptureWindow(window);
}

scBrowser.prototype.releaseCaptureWindow = function(win) {
  win.releaseEvents(Event.CLICK);
  win.releaseEvents(Event.MOUSEDOWN);
  win.releaseEvents(Event.MOUSEMOVE);
  win.releaseEvents(Event.MOUSEUP);

  win.onclick = null;
  win.onmousedown = null;
  win.onmousemove = null;
  win.onmouseup = null;

  for(var n = 0; n < win.frames.length; n++) {
    this.releaseCaptureWindow(win.frames[n]);
  }
}

scBrowser.prototype.scrollIntoView = function(control) {
  control.scrollIntoView();
}

scBrowser.prototype.setCapture = function(control) {
  scGeckoCapturedControl = control;
  this.setCaptureWindow(window);
}

scBrowser.prototype.setCaptureWindow = function(win) {
  win.captureEvents(Event.CLICK);
  win.captureEvents(Event.MOUSEDOWN);
  win.captureEvents(Event.MOUSEMOVE);
  win.captureEvents(Event.MOUSEUP);
  
  win.onclick = scGeckoDispatchCapturedEvent;
  win.onmousedown = scGeckoDispatchCapturedEvent;
  win.onmousemove = scGeckoDispatchCapturedEvent;
  win.onmouseup = scGeckoDispatchCapturedEvent;

  for(var n = 0; n < win.frames.length; n++) {
    this.setCaptureWindow(win.frames[n]);
  }
}

scBrowser.prototype.removeChild = function(tag) {
  if (tag.parentNode != null) {
    tag.parentNode.removeChild(tag);
  }
}

scBrowser.prototype.setImageSrc = function(img, src) {
  img.src = src;
}

scBrowser.prototype.setOuterHtml = function(control, html) {
  var range = control.ownerDocument.createRange();
  
  range.setStartBefore(control);
  
  var fragment = range.createContextualFragment(html);
  
  control.parentNode.replaceChild(fragment, control);
}

scBrowser.prototype.showModalDialog = function(url, arguments, features, request) {
  features = features.replace(/;/gi, ",").replace(/\:/gi, "=").replace(/dialog/gi, "").replace(/Height\=px/gi,"Height=450px").replace(/Width\=px/gi,"Width=600px");
  
  features += ",scrollbars=yes,dependent=yes";
  
  var win = window.open(url, "_blank", features);
  
  win.dialogArguments = arguments;
  
  if (request != null) {
    win.scGeckoModalDialogRequest = request;
    request.suspend = true;
  }
}

scBrowser.prototype.showPopup = function(data) {
  var id = data.id;

  var evt = (scForm.lastEvent != null ? scForm.lastEvent : event);
  
  this.clearEvent(evt, true, false);

  var doc = document;
  if (scForm.lastEvent != null && scForm.lastEvent.target != null) {
    doc = scForm.lastEvent.target.ownerDocument;
  }
  
  var popup = document.createElement("div");
  
  popup.id = "Popup" + (this.popups != null ? this.popups.length + 1 : 0);
  popup.className = "scPopup";
  popup.style.position = "absolute";
  popup.style.left = "0px";
  popup.style.top = "0px";
  popup.onblur = "scForm.browser.removeChild(this)";
  
  var html = "";
    
  if (typeof(data.value) == "string") {
    html = data.value; 
  }
  else {
    html = this.getOuterHtml(data.value); 
    
    var p = html.indexOf(">");
    if (p > 0) {
      html = html.substring(0, p).replace(/display[\s]*\:[\s]*none/gi, "") + html.substr(p);
      html = html.substring(0, p).replace(/position[\s]*\:[\s]*absolute/gi, "") + html.substr(p);
    }
  }
  
  popup.innerHTML = html;
  
  document.body.appendChild(popup);
  var width = popup.offsetWidth;
  var height = popup.offsetHeight;
  
  var ctl = null;
  var x = evt.clientX != null ? evt.clientX : 0;
  var y = evt.clientY != null ? evt.clientY : 0;
  
  if (id != null && id != "") {
    ctl = scForm.browser.getControl(id, doc);
    
    if (ctl != null && ctl.offsetWidth > 0) {
      switch(data.where) {
        case "contextmenu":
          x = evt.pageX;
          y = evt.pageY;
          break;
          
        case "left":
          x = -width;
          y = 0;
          break;

        case "right":
          x = ctl.offsetWidth - 3;
          y = 0;
          break;
          
        case "above":
          x = 0;
          y = -height + 1;
          break;
          
        case "below-right":
          x = ctl.offsetWidth - width;
          y = ctl.offsetHeight;
          break;
          
        case "dropdown":
          x = 0;
          y = ctl.offsetHeight;
          width = ctl.offsetWidth;
          break;
          
        default:
          x = 0;
          y = ctl.offsetHeight;
      }
      
      while (ctl.offsetParent != null) {
        x += ctl.offsetLeft;
        y += ctl.offsetTop;
        ctl = ctl.offsetParent;
      }
    }
  }
  
  var viewport = document.body;
  if (viewport.clientHeight == 0) {
    var form = $$("form")[0];
    if (form && form.clientHeight > 0) {
      viewport = form;
    }
  }
  
  if (x + width > viewport.clientWidth) {
    x = document.body.clientWidth - width;
  }
  if (y + height > viewport.clientHeight) {
    y = viewport.clientHeight - height;
  }
  if (x < 0) {
    x = 0;
  }
  if (y < 0) {
    y = 0;
  }
  
  popup.style.width = "" + width + "px";
  popup.style.height = "" + height + "px";
  popup.style.top = "" + y + "px";
  popup.style.left = "" + x + "px";
  popup.style.zIndex = (this.popups == null ? 1000 : 1000 + this.popups.length);

  if (this.popups != null) {
    this.popups.push(popup);
  }
  else {
    this.popups = new Array(popup);
  }
  
  var parentPopup = this.findParentPopup(evt);
  if (parentPopup) {
    popup.scParentPopup = parentPopup;
    
    var exclusions = new Array();
    var iterator = popup;
    
    while(iterator) {
      exclusions.push(iterator);
      iterator = $(iterator.scParentPopup);
    }
  }  
  
  this.closePopups("show popup", exclusions || new Array(popup));
  
  scForm.focus(popup);
}

scBrowser.prototype.findParentPopup = function(evt) {
  if (!this.popups || !evt || !evt.target || !evt.target.descendantOf) {
    return;
  }
  
  var target = evt.target;
  
  return this.popups.find(function(popup) { return target.descendantOf(popup); });
}

scBrowser.prototype.swapNode = function(control, withControl) {
  var parent = control.parentNode;

  var clone = control.cloneNode(true);
  
  withControl = parent.replaceChild(clone, withControl);
  
  parent.replaceChild(withControl, control);
  parent.replaceChild(control, clone);
  
  clone = null;
}

/* fixsize elements */
scBrowser.prototype.initializeFixsizeElements = function() {
  this.fixsizeElements = $$(".scFixSize").concat($$(".scFixSizeInitialized"));
  var form = $$("form")[0];
  
  this.fixsizeElements.each(function(element) {
    element.setStyle({ height: "100%" });
    
    var elementHeight = element.getHeight();    
    if (elementHeight == 0) {
      return;
    }
    
    if (element.hasClassName("scScrollbox")) {
      elementHeight -= 4;
    }
    
    element.scHeightAdjustment = form.getHeight() - elementHeight;
    element.setStyle({ height: elementHeight + "px" });
    
    element.removeClassName("scFixSize").addClassName("scFixSizeInitialized");
  });
}

scBrowser.prototype.resizeFixsizeElements = function() {
  var form = $$("form")[0];
  
  this.fixsizeElements.each(function(element) {
    var height = form.getHeight() - element.scHeightAdjustment + "px";
    element.setStyle({ height: height });
  });
  
  /* trigger re-layouting to fix the firefox bug: table is not shrinking itself down on resize */
  scGeckoRelayout();
}

function scGeckoEnumerator(collection) {
  this.m_collection = collection;
  this.m_current = 0;
}

scGeckoEnumerator.prototype.atEnd = function() {
  return (this.m_collection == null) || (this.m_current >= this.m_collection.length);
}

scGeckoEnumerator.prototype.item = function() {
  return this.m_collection[this.m_current];
}

scGeckoEnumerator.prototype.moveNext = function() {
  this.m_current++;
}

scGeckoEnumerator.prototype.moveFirst = function() {
  this.m_current = 0;
}

var scGeckoCapturedControl = null;
var scGeckoCapturedEventExecuting = false;

function scGeckoDispatchCapturedEvent(evt) {
  if (scGeckoCapturedControl != null && !scGeckoCapturedEventExecuting) {
    scGeckoCapturedEventExecuting = true;

    // FireFox 1.0.6 work-around
    var e = document.createEvent("MouseEvents");
    
    e.initMouseEvent(evt.type,
        evt.bubbles, evt.cancelable, evt.view, evt.detail,
        evt.screenX, evt.screenY, evt.clientX, evt.clientY,
        evt.ctrlKey, evt.altKey, evt.shiftKey, evt.metaKey,
        evt.button, evt.relatedTarget);
        
    scGeckoCapturedControl.dispatchEvent(e);  
    
    scGeckoCapturedEventExecuting = false;
  }
}

function scGeckoClosePopups(reason) {
  scForm.browser.closePopups(reason || "geckoClosePopups");
}

function scGeckoRelayout() {
  var form = $$("form")[0];
  
  /* trigger re-layouting to fix the firefox bug: table is not shrinking itself down on resize */
  form.setStyle({ opacity: "0.999" });
  
  setTimeout(function() {
    form.setStyle({ opacity: "1" });
  }, 100);
}
