var xmlDOM = new Object();

//
// newXMLDOM() creates a new DOM object
// parameter selects one of three common objects:
//   xml: DOM for xml and xsl (rental model)
//   xsl: free threaded model DOM for xsl which need to be explicitly 
//        compiled to xsl processor.
//        free model is slower than the rental model
//   xslt: xslt template for the xsl processor
//
// if xmlDOM.progidverset is true, the specified version is used.
// otherwise, will test availability of each version
// and sets xmlDOM.progidver to the version found.
// return null if failed to create any.
//
// to hardcode XML version, caller should assign like this
//
//  xmlDOM.progidverset=true;
//  xmlDOM.progidver=".3.0";

function newXMLDOM(t) {
  function testver(s) {
    var e;
    try { return new ActiveXObject(s); } 
    catch(e) { return null; }
  }

  function testver2(s,t,v) {
    var doc = testver(s+v);
    if (doc) {
      if (t == "xsl" || t == "xml") {
        if (v == ".6.0") {
          // doc.setProperty("AllowXsltScript", true); I don't need this
          doc.setProperty("AllowDocumentFunction", true);
          doc.resolveExternals = true;
        } 
        if (v == ".4.0") {
          doc.setProperty("NewParser", true ); 
        }
      }
      return doc;
    } else return null;
  }
  
  var s;
  switch (t) {
    case "xml":
      s = "Msxml2.DOMDocument";
      break;
    case "xsl":
      s="Msxml2.FreeThreadedDOMDocument"; 
      // free model required if xsl will compile to xslt
      break;
    case "xslt":
      s="Msxml2.XSLTemplate";
      break;
  }
  var doc;
  if (xmlDOM.progidverset) {
    if (doc = testver2(s,t,xmlDOM.progidver)) return doc;
  }
  xmlDOM.progidverset = true;
  var progIDs = [".6.0", ".4.0", ".3.0", ".2.6", ".2.5", ".2.0"]
  for (var i = 0; i < progIDs.length; i++) {
    if (doc = testver2(s,t,progIDs[i])) {
      xmlDOM.progidver=progIDs[i];
      return doc;
    }
  }
  xmlDOM.errmsg = "Failed to create XML DOM";
  xmlDOM.progidverset=false;
  return null;
}

//
// given xml and xsl file name, transform and return the output
// during the process, window.defaultStatus is updated
//
// on error, return null
// detailed message is at xmlDOM.errmsg
//
// if xml is not a string, it is treated as an XMLDOM and used directly
//

function transformXX(xml, xsl) {
  return transformXXp(xml, xsl, null);
}

//
// XSLparm = 1 or 0
// use 0 when loading XML 
// use 0 when loading XSL without parm
// use 1 when loading XSL with parm (use free model)
//
// cache disables forced xml reload (seems not work in XML4)
// ver3 forces XML3
// seems fails with older XML3 SPs
//
function loadXML(fname, XSLparm, cache, ver3) { 

  // Parse error formatting function
  function reportParseError(error)
  {
    var s = "";
    for (var i=1; i<error.linepos; i++) {
      s += " ";
    }
    r = "<font face=Verdana size=2><font size=4>XML Error loading '" + 
        error.url + "'</font>" +
        "<P><B>" + error.reason + 
        "</B></P></font>";
    if (error.line > 0)
      r += "<font size=3><XMP>" +
      "at line " + error.line + ", character " + error.linepos +
      "\n" + error.srcText +
      "\n" + s + "^" +
      "</XMP></font>";
    return r;
  }

  if (ver3) {
    // hardcode v3.0
    xmlDOM.progidverset=true;
    xmlDOM.progidver=".3.0"
  } // else find latest as usual
  xmlDoc = newXMLDOM(XSLparm ? "xsl" : "xml");
  if (xmlDoc) {
    xmlDoc.async = false;
    if (cache) {
      var e;
      try{
        xmlDoc.setProperty("ForcedResync", false);
      } catch(e) {}
    }
    // default true will no-cache 
    defaultStatus = "Loading " + fname + "...";
    xmlDoc.load(fname);
    if (xmlDoc.parseError.errorCode) {
      xmlDOM.errmsg = reportParseError(xmlDoc.parseError);
      xmlDoc = null;
      return null;
    } else return xmlDoc;
  } else return null;
}  


//
// if xparam not null, it is an array of parm/value pairs
//

function transformXXp(xml, xsl, xparam) {
  // Runtime error formatting function
  function reportRuntimeError(exception)
  {
    return "<font face=Verdana size=2><font size=4>XSL Runtime Error</font>" +
        "<P><B>" + exception.description + "</B></P></font>";
  }
  
  xmlDOM.errmsg = "";

  var xmlDoc;
  if (typeof xml == "string") {
    defaultStatus = "Initialising XML document...";
    if (!(xmlDoc=loadXML(xml, false))) return null;
  } else xmlDoc = xml;

  defaultStatus = "Initialising XSL document...";
  var xslDoc, xslProc;
  if (xparam) {
    if (typeof xsl == "string") {
      if (!(xslDoc=loadXML(xsl, true))) return null; 
      // must use free model to issue parm
      defaultStatus = "Initialising XSLT processor...";
      var xslt = newXMLDOM("xslt"); // use template to compile xslt processor
  
      defaultStatus = "Compiling XSLT...";
      xslt.stylesheet = xslDoc;
      xslProc = xslt.createProcessor();
    } else xslProc = xsl; // caller must input a free model xslt processor
  
    defaultStatus = "Assigning parameters...";
    for (var i = 0; i<xparam.length; i++) {
      xslProc.addParameter(xparam[i].name, xparam[i].value);
    }

  } else {
    if (typeof xsl == "string") {
      if (!(xslDoc=loadXML(xsl, false))) return null; 
      // no need to compile xslt processor
    } else xslDoc = xsl; // caller input a normal xmlDoc
  }

  defaultStatus = "Transforming XML by XSLT...";

  var outHTML;
  try { 
    if (xparam) {
      xslProc.input = xmlDoc;
      xslProc.transform();
      outHTML=xslProc.output;         
    } else {
      outHTML=xmlDoc.transformNode(xslDoc);
    }
  } catch (exception) { 
    xmlDOM.errmsg = reportRuntimeError(exception); 
    defaultStatus = "Transformation failed.";
    return null;
  } 
  defaultStatus = xmlDOM.errmsg = "";
  xmlDoc = xslDoc = null;
  return outHTML;
}



//
// similar to (and calls) transformXX()
// but writes result/errmsg to the win handle (3rd argument)
// if win is null, creates a new window for it
// if win is string, open window with this name
// returns the win handle in all cases
//
// win is optional if a name is not required.
// when win is string and contains any "=", will be treated as arg to open()
//
// if win is window handle, will ignore warg
// can't do anything about it
//

function transformXXW(xml, xsl, win, warg) {
  return transformXXpW(xml, xsl, null, win, warg);
}

function transformXXpW(xml, xsl, param, win, warg) {
  var out = transformXXp(xml,xsl, param);
  var winOpenArg = 'links=no, address=no, menubar=no, location=no, toolbar=no, status=yes, resizable=yes, scrollbars=yes';
  if (warg) winOpenArg += "," + warg;
  var w;
  if (!win) {
    w= window.open("","_blank",winOpenArg);
  } else if (typeof win == "string") {
    if (win.indexOf("=") >= 0) {
      w= window.open("","_blank",winOpenArg + "," + win);
    } else {
      w= window.open("",win,winOpenArg);
    }
  } else w=win;
  if (out) {
    var d = w.document.open();
    d.write(out);
    d.close();
  } else {
    var d = w.document.open();
    d.write(xmlDOM.errmsg);
    d.close();
  }
  return w;
}

