<?php
define('idDiGIR_clientJS','$Id: DiGIR_clientJS.php,v 1.4 2004/01/21 22:51:59 vieglais Exp $');
if (defined('idDIGIR_VERSION_REQUEST'))
    return;

//define('DiGIRNameSpace','http://digir.net/schema/protocol/2003/1.0');
//define('XMLSCHEMANS','http://www.w3.org/2001/XMLSchema');
//define('XMLSCHEMAINST','http://www.w3.org/2001/XMLSchema-instance');
//define('DiGIRmNameSpace','http://digir.net/schema/digirm/2003/1.0');

//define("XMLSCHEMA_NS","http://www.w3.org/2001/XMLSchema");
//define("DIGIRSCHEMA_NS","http://www.namespaceTBD.org/digir");
//define("XMLSCHEMAINSTANCE_NS","http://www.w3.org/2001/XMLSchema-instance");

define("DEFAULT_DOMAIN_DOC","http://digir.sourceforge.net/schema/conceptual/darwin/2003/1.0/darwin2Infodo.xml");

error_reporting(E_ALL
                & ~E_NOTICE
                & ~E_WARNING
                & ~E_CORE_WARNING
                & ~E_COMPILE_WARNING);
include_once('DiGIR_globals.php');
include_once('DiGIR_utils.php');
require_once('Cache/Function.php');
require_once(DIGIR_XPATH_LIBRARY);


$loadDomain = (int) getVar("metadata","1");
$domaindoc = getvar('domain',NULL);

if ($loadDomain && is_null($domaindoc))
{
	if (is_null($domaindoc))
	{
	?>
	<html>
	<head><title>DiGIR Client JS Generator</title></head>
	<body>
	<p>Enter location of domain document to use:</p>
	<form method='GET'>
	<?php
		echo "<input id='domain' name='domain' ".
			"type='text' value='".htmlentities(DEFAULT_DOMAIN_DOC)."' size='80' /><br />";
		echo "<input type='submit' value='OK' />";
		echo "</form>";
		echo "</body></html>";
		die();
	}
}
?>
/*<code><pre>
DiGIR web-browser client helper scripts.
If you're looking at this in a web browser, then you'll probably have to
view source to see everything you should be...
	
Probably the most useful ones for you to examine are:
  * DiGIRRequestMetadata()
  * DiGIRRequestSearch()
  * DiGIRRequestScan()

which are documented in the code below.

Global variables created:
	g_ClientIP = client IP address (required for DiGIR requests)
	g_DiGIRNS = Namespace of DiGIR protocol
	g_XMLSCHEMANS = namespace of XMLSchema
	g_XmlSchemaInstanceNS = namespace of XMLSchemaInstance


This page supports a couple of parameters:
  * metadata = 1|0
Indicates if information domain metadata is to be included in the
generated Javascript.

  * domain = URL to domain document.
*/

/**
* $Id: DiGIR_clientJS.php,v 1.4 2004/01/21 22:51:59 vieglais Exp $
*
* == License ==
* Copyright (c) 2002 The University of Kansas Natural History Museum and
* Biodiversity Research Center.  All rights reserved.
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* with the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* *  Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimers.
* 
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimers in the
* documentation and/or other materials provided with the distribution.
* 
* * Neither the names of The University of Kansas Natural History Museum and
* Biodiversity Research Center, The University of Kansas at Lawrence, nor the
* names of its contributors may be used to endorse or promote products derived
* from this Software without specific prior written permission.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*********************************
* Utility Functions
**********************************/
/**
* Utility function that replaces a special character with it's html escape.
* @param str string input string
* @param cpos integer index (0 based) of character to encode if necessary
* @return string the character as itself or as an encoded string.
*/
function htmlEncodeChar(str,cpos)
{
    var icc = str.charCodeAt(cpos);
    if ((icc < 0x20) && (icc > 0) ||
			(icc == 0x21) ||
			(icc == 0x22) ||
			(icc == 0x23) ||
			(icc == 0x26) ||
			(icc == 0x27) ||
			(icc == 0x3c) ||
			(icc == 0x3e) ||
			(icc == 0x3f) ||
			(icc >= 0x80))
    {
        sres = "&#x" + icc.toString(16) + ";";
    }
    else
        sres = str.charAt(cpos);
    return sres;
}

/**
* Escapes HTML special characters in a string.
* @param string strIn String to be encoded
* @return string The encoded string
*/
function htmlEncodeString(strIn)
{
    var res = "";
    var str = new String(strIn);
    var sl = str.length;
    for (var i=0; i < sl; i++)
    {
        res += htmlEncodeChar(str,i);
    }
    return res;
}

/**
* Generates a numeric string with preceding zeros.
* @param integer n The numbeeric value
* @param integer p The total length of the resulting string (pre-filled with 0s)
* @return string The resulting string
*/
function fillNumber(n,p)
{
	var s = new String(n);
	while (s.length < p)
		s = "0" + s;
	return s;
}

/**
* Generates an ISO 8601 formatted time stamp string.
* @return string The timestamp string
*/
function TimeStampString()
{
	var dt = new Date();
	var stmp = fillNumber(dt.getUTCFullYear(),4) + fillNumber(dt.getUTCMonth()+1,2) + fillNumber(dt.getUTCDate(),2);
	stmp += "T" + fillNumber(dt.getUTCHours(),2) + fillNumber(dt.getUTCMinutes(),2) + fillNumber(dt.getUTCSeconds(),2) + "." + dt.getUTCMilliseconds() + "Z";
	return stmp;
}

/**
* Creates an xml tag
* @param string tag The name of the resulting element.
* @param string value The value of the element.
* @param array attrs An array of attributes that represent key value pairs, where
*                    the key is the index of the array.
* @param string dns The namespace handle to use for element names.
* @return string The resulting xml element
*/
function makeTag(tag,value,attrs,dns)
{
	if (dns)
		tag = dns + ":" + tag;
    var res = "<" + tag;
    if (attrs != null)
    {
        for (att in attrs)
        {
            res += " " + att + "='" + attrs[att] + "'";
        }
    }
    res += ">";
    res += value;
    res += "</" + tag + ">\n";
    return res;
}

/**
*
*/
function findNamespace(namespaces,space)
{
    for (var n in namespaces)
    {
        if (namespaces[n] == space)
            return n;
    }
    return "";
}

/*********************************************************************/
/**
* Generates a DiGIR XML filter representation of the DiGIRCOP object.
*
* @return	string	The XML representation of the object.
*/
function DiGIRCOP_toXML()
{
	var res = "<" + this.COPOP + ">";
	if (this.COPOP == 'list')
	{
		var alist = this.term.split(this.delimiter);
		for (itm in alist)
		{
			res += "<" + this.concept + ">";
			res += htmlEncodeString(alist[itm]);
			res += "</" + this.concept + ">\n";
			
		}
	}
	else
	{
		res += "<" + this.concept;
		if (this.isnullterm)
		{
			res += " xsi:nill='true'";
		}
		res += ">";
		res += htmlEncodeString(this.term);
		res += "</" + this.concept + ">";
	}
	res += "</" + this.COPOP+">\n";
	return res;
}

/**
* Creates a new DiGIRCOP object.
*
* @param 	COPop	string	The COPop.  The value and validity is not checked!!
* @param	concept	string	The concept against which the term is being compared.  This should include the namespace identifier as well.
* @param	term	string	The term that is being compared.  If COPop='list', then the term is split by this.delimiter, whicdefautls to ','
*/
function DiGIRCOP(COPop,concept,term,isnullterm)
{
	//attributes
	this.type = "COP";
	this.delimiter = ",";
	this.COPOP = COPop;
	this.concept = concept;
	this.term = new String(term);
	this.isnullterm = isnullterm
	
	//methods
	this.toXML = DiGIRCOP_toXML;
}

/*********************************************************************/

/**
* Adds a node to the structure.  "node" can be a DiGIRCOP or DiGIRLOP structure.
* The nodes is added to the next available slot in the hierarchy so order of addition
* is important.
*
* @param node	DiGIRCOP | DiGIRLOP 	The node to be added.
* @return	integer	0 = failure, 1=ok.
*/
function DiGIRLOP_addNode(node)
{
    if (this.left == null)
    {
        this.left = node;
        return 1;
    }
    if (this.right == null)
    {
        this.right = node;
        return 1;
    }
    //try and add to child of this.
    if (this.left.type == 'LOP')
    {
    	if (this.left.addNode(node) != 0)
    		return 1;
    }
    if (this.right.type == 'LOP')
    {
    	if (this.right.addNode(node) != 0)
    		return 1;
    }
    //indicate that node could not be added
    return 0;
}

/**
* Generates a DiGIR XML filter representation of the DiGIRLOP object.
*
* @return	string	The XML representation of the object.
*/
function DiGIRLOP_toXML()
{
    if ((this.left == null) || (this.right == null))
        return "";
    var res = "<" + this.LOPOP + ">\n";
    res += this.left.toXML();
    res += this.right.toXML();
    res += "</" + this.LOPOP + ">\n";
    return res;
}

/**
* Creates a new DiGIRLOP object.
*
* @param 	lopop 	string	The logical operator.  Can be "and" or "or" or "andnot" or "ornot".
*/
function DiGIRLOP(lopop)
{
    //attributes
    this.type='LOP';
    this.LOPOP = lopop;
    this.left = null;
    this.right = null;

    //methods
    this.toXML = DiGIRLOP_toXML;
    this.addNode = DiGIRLOP_addNode;
}

/*********************************************************************/
/**
* class DiGIRFilter
* Implements an object representation of a DiGIR Filter
*/

/**
* Generates a DiGIR filter Structure in XML
*/
function DiGIRFilter_toXML()
{
	if (this.root == null)
		return "";
	var res = "<filter";
	for (n in this.namespaces)
	{
		if (n == 0)
		{
			res += " xmlns='" + this.namespaces[n] + "'";
		}
		else
		{
			res += " xmlns:" + n + "='" + this.namespaces[n] + "'";
		}
	}
	res += ">";
	res += this.root.toXML();
	res += "</filter>";
	return res;
}

/**
* Adds a node to the filter.  The node is appended to the
* next available slot in the filter structure so order of
* construction is important.
*
* @params node DiGIRLOP | DiGIRCOP	The filter node to be added to the structure
* @returns integer	0 on failure, 1 = ok.
*/
function DiGIRFilter_addNode(node)
{
	if (this.root == null)
	{
		this.root = node;
		return 0;
	}
	//can not add nodes to a COP
	if (this.root.type == 'COP')
		return 0;
	//the root is a LOP- it will add node to next available
	//branch and will traverse to children if necessary.
	return this.root.addNode(node);
}

/**
* Adds a namespace to the list of namespaces for the Filter object
*
* @param 	name	string	The namespace handle, ie, the zzz of "xmlns:zzz='abc'"
* @param	space	string	The namespace, is the abc of "xmlns:zzz='abc'"
* @return	integer	0 = handle conflicts with existing handle, 1 = already exists, 2 = added
*/
function DiGIRFilter_addNamespace(name,space)
{
	for (var i in this.namespaces)
	{
		if ((i == name) && (this.namespaces[i] == space))
			return 1;
		if (i == name)
			return 0;
	}
	this.namespaces[name] = space;
	return 2;
}

/**
* Constructor for a DiGIRFilter object.
*/
function DiGIRFilter()
{
    //methods
    this.toXML = DiGIRFilter_toXML;
    this.addNode = DiGIRFilter_addNode;
    this.addNamespace = DiGIRFilter_addNamespace;

    //attributes
    this.namespaces = new Array();
    this.root = null;
}

/*********************************************************************/
/*
example code for DiGIRFilter:
	var flt = new DiGIRFilter();
	flt.addNamespace('ns1','http://www.namespaceTBD.org/digir');
	var nd = new DiGIRLOP('and');
	flt.addNode(nd);
	nd = new DiGIRCOP('like','ns1:scientificName','test%');
	flt.addNode(nd);
	nd = new DiGIRCOP('list','ns1:collector','test,joe,frank');
	flt.addNode(nd);
	var XML = flt.toXML();
	alert(XML);
*/
/*********************************************************************/

/*********************************************************************/
/**
* Returns xml representation of a record element.
* @param 	xsd	string	The namespace handle for XMLSchema used in the containing document.
* @return	string	The XML representation of the record structure.
*/
function DiGIRRecordElement_toXML(xsd)
{
	var res = "<"+xsd+":element ref='" + this.concept + "' />\n";
	return res;
}

function DiGIRRecordElement(concept)
{
	//attributes
	this.concept = concept;
	
	//methods
	this.toXML = DiGIRRecordElement_toXML;
}

/*********************************************************************/

function DiGIRRecordNode_setRecordRef(ref)
{
	this.recordRef = ref;
}

function DiGIRRecordNode_addNode(node)
{
	return this.nodes.push(node);
}

/**
* Returns xml representation of a record node, which can consist of multiple elements or a reference to an xml schema document.
* @param 	xsd	string	The namespace handle for XMLSchema used in the containing document.
* @return	string	The XML representation of the record structure.
*/
function DiGIRRecordNode_toXML(xsd)
{
	var res = "<structure"
	if (this.recordRef != null)
	{
		res += " schemaLocation='" + this.recordRef + "'";
		res += " />";
		return res;
	}
	res += ">";
	res += "<"+xsd+":element name='record'>\n";
	res += "<"+xsd+":complexType>\n";
	res += "<"+xsd+":sequence>\n";
	for (i in this.nodes)
	{
		res += this.nodes[i].toXML(xsd);
	}
	res += "</"+xsd+":sequence>\n";
	res += "</"+xsd+":complexType>\n";
	res += "</"+xsd+":element>\n";
	res += "</structure>\n";
	return res;
}

function DiGIRRecordNode()
{
	//attributes
	this.nodes = new Array();
	this.recordRef = null;
	
	//methods
	this.setRecordRef = DiGIRRecordNode_setRecordRef;
	this.toXML = DiGIRRecordNode_toXML;
	this.addNode = DiGIRRecordNode_addNode;
}

/*********************************************************************/

/**
* Returns xml representation of a record structure.
* @param 	xsd	string	The namespace handle for XMLSchema used in the containing document.
* @return	string	The XML representation of the record structure.
*/
function DiGIRRecordStruct_toXML(xsd)
{
	if (this.bGetRecords)
	{
		var res = "<records";
	for (n in this.namespaces)
	{
		if (n == 0)
		{
			res += " xmlns='" + this.namespaces[n] + "'";
		}
		else
		{
			res += " xmlns:" + n + "='" + this.namespaces[n] + "'";
		}
	}
		res += " start='" + this.start + "'";
		res += " limit='" + this.max + "'";
		res += ">\n";
		res += this.recordNodes.toXML(xsd);
		res += "</records>\n";
	}
	res += "<count>";
	if (this.bcount)
		res += "true";
	else
		res += "false";
	res += "</count>\n";
	return res;
}

/**
* Adds a node to the root record structure element.
*/
function DiGIRRecordStruct_addNode(node)
{
	this.recordNodes.addNode(node);
}

function DiGIRRecordStruct_setRef(ref)
{
	this.recordNodes.setRecordRef(ref);
}

function DiGIRRecordStruct_addNameSpace(name,space)
{
	for (var i in this.namespaces)
	{
		if ((i == name) && (this.namespaces[i] == space))
			return 1;
		if (i == name)
			return 0;
	}
	this.namespaces[name] = space;
	return 2;
}

/**
* Create a DiGIR record structure definition object.
*
* @param istart	int	The zero-based index of the first record.
* @param imax	int	The maximum number of records to be returned.
* @param bCount	boolean	True if a count of the total number of matching records is to be returned.
*/
function DiGIRRecordStruct(istart,imax,bCount)
{
	//attributes
	this.start = istart;
	this.max = imax;
	this.bcount = bCount;
	this.bGetRecords = true;
	this.recordNodes = new DiGIRRecordNode();
    this.namespaces = new Array();
	
	//methods
	this.toXML = DiGIRRecordStruct_toXML;
	this.addNode = DiGIRRecordStruct_addNode;
	this.setRef = DiGIRRecordStruct_setRef;
    this.addNamespace = DiGIRRecordStruct_addNameSpace;
}

/*********************************************************************/
/*
example code for DiGIRRecordStruct:

function test()
{
	var rs = new DiGIRRecordStruct(0,100,false);
	var nd = new DiGIRRecordElement("dwc:ScientificName");
	rs.addNode(nd);
	rs.addNode(new DiGIRRecordElement("dwc:YearCollected"));
	alert = rs.toXML("xsd");
}

*/
/*********************************************************************/



/********************************************************/
/**  Global variables **/
/**
* The version stamp to use for the DiGIR header structure.
*/
var DiGIR_version = "1.0.0";

/**
* Generates a DiGIR header structure
* @param URL endPoint The target URL.
* @param string opType One of "metadata", "search", or "scan" that identifies the
*                      Intended type of operation.
* @param string dns The namespace handle to use.  If null or sero length string then
*                   no namespace handle is applied to the elements.
* @return string the XML fragment that is the resulting DiGIR header structure.
*/
function DiGIRRequestHeader(endPoint,opType,dns,resource)
{
	if (dns != null) 
		if (dns.length <= 0)
			dns = null;
	var htag = "header";
	if (dns)
		htag= dns + ":" + htag;
    destAttrs = new Array();
	if (resource != null)
	{
	    destAttrs['resource'] = resource;
	}
    var res = "<"+htag+">\n";
    res += makeTag("version",DiGIR_version,null,dns);
    res += makeTag("sendTime",TimeStampString(),null,dns);
    res += makeTag("source",g_ClientIP,null,dns);
    res += makeTag("destination",endPoint,destAttrs,dns);
    res += makeTag("type",opType,null),dns;
    res += "</"+htag+">\n";
    return res;
}

/**
* Generates a DiGIR Metadata request envelope for the specified endpoint.
*
* @param endPoint   string  The target that the request will be sent to.
* @return string    The DiGIR request envelope for a metadata request.
*/
function DiGIRRequestMetadata(endPoint)
{
    var env = "<request xmlns='" + g_DiGIRNS + "'>\n";
    env += DiGIRRequestHeader(endPoint,'metadata',null,null);
    env += "</request>";
    return env;
}

/**
* Generates a DiGIR Scan request envelope for the specified endpoint.
*
* @param    endPoint    string  The target that the request will be sent to
* @param	namespaces	array	Associative array of namespaces for the request envelope
* @param    concept string  The concept that is being scanned.
* @param    filter  structure   A DiGIRFilter object.  Defaults to null which means no filter is included with the request.
*
* @return   string  The DiGIR scan request envelope.
*/
function DiGIRRequestScan(endPoint,resource,namespaces,concept,filter)
{
    var env = "<request";
    for (var n in namespaces)
    {
        if (n==0)
        {
            env += " xmlns='" + namespaces[n] + "'";
        }
        else
        {
            env += " xmlns:" + n + "='" + namespaces[n] + "'";
        }
    }
    env += ">\n";
    env += DiGIRRequestHeader(endPoint,'scan',null,resource);
    env += "<scan>";
    if (filter != null)
    {
        env += filter.toXML();
    }
    if (concept != null)
    {
    	if ((concept.max == null) || (concept.max=='undefined'))
    	{
	        //create a record structure to hold the concept
    		var rs = new DiGIRRecordStruct(0,100,false);
        	var nd = new DiGIRRecordElement(concept);
		    rs.addNode(nd);

    	    //find the xmlschema namespace.
        	var xsd = findNamespace(namespaces,g_XMLSCHEMANS);
	        if (xsd == "")
    	    {
        	    //uh oh.  guess and use "xsd" for xmlschema namespace handle
	            xsd = "xsd";
    	    }
    		env += rs.toXML(xsd);
    	}
    	else
    	{
        	var xsd = findNamespace(namespaces,g_XMLSCHEMANS);
	        if (xsd == "")
    	    {
        	    //uh oh.  guess and use "xsd" for xmlschema namespace handle
	            xsd = "xsd";
    	    }
    	    env += concept.toXML(xsd);
    	}
    }
    env += "</scan></request>";
    return env;
}

/**
* Generates a DiGIR Search request envelope for the specified endpoint.
*
* @param    endPoint    string  The target that the request will be sent to
* @param	namespaces	array	Associative array of namespaces for the request envelope
* @param    recordStruct structre  The structure of the response records defiend as a DiGIRRecord object.
* @param    filter  structure   A DiGIRFilter object.
*
* @return   string  The DiGIR scan request envelope.
*/
function DiGIRRequestSearch(endPoint,resource,namespaces,recordStruct,filter)
{
    var env = "<request";
    for (var n in namespaces)
    {
        if (n==0)
        {
            env += " xmlns='" + namespaces[n] + "'";
        }
        else
        {
            env += " xmlns:" + n + "='" + namespaces[n] + "'";
        }
    }
    env += ">\n";
    env += DiGIRRequestHeader(endPoint,'search',null,resource);
    env += "<search>";
    if (filter != null)
    {
        env += filter.toXML();
    }
    if (recordStruct != null)
    {
        //find the xmlschema namespace.
        var xsd = findNamespace(namespaces,g_XMLSCHEMANS);
        if (xsd == "")
        {
            //uh oh.  guess and use "xsd" for xmlschema namespace handle
            xsd = "xsd";
        }
    	env += recordStruct.toXML(xsd);
    }
    env += "</search></request>";
    return env;
}

<?php

class conceptSet
{
    var $location;
    var $targetNamespace;
    var $namespaces;
    var $concepts;
    var $annotation;
    var $nsPrefix;
    
    function conceptSet($sURL)
    {
        $this->location = $sURL;
        $this->targetNamespace = "";
        $this->schemaNamespace = array();
        $this->namespaces = array();
        $this->concepts = array();
        $this->annotation = array();
        $this->nsPrefix = "sl_";
    }
    
    /**
    * Renders this as a set of JavaScript arrays
    * @return string Javascript rendering
    */
    function toJavaScript()
    {
        $res = "/////////////////////////////////////////////\n";
        $res = "//Concept set loaded from ".$this->location."\n";
        $res .= "var g_conceptSet = new Array();\n";
        $res .= "var tmp = new Array();\n";
        foreach ($this->namespaces as $nm=>$sp)
        {
            $res .= "tmp['$nm']='$sp';\n";
        }
        $res .= "g_conceptSet['namespaces'] = tmp;\n";
        $res .= "var tmp = new Array();\n";
        foreach ($this->schemaNamespace as $nm=>$sp)
        {
            $res .= "tmp['$nm']='$sp';\n";
        }
        $res .= "g_conceptSet['schemaNamespaces'] = tmp;\n";
        $res .= "g_conceptSet['concepts'] = new Array();\n";

        $digirHandle = "digir";
        foreach ($this->namespaces as $k=>$v)
        {
            if ($v == DiGIRNameSpace)
                $digirHandle = $k;
        }

        foreach ($this->concepts as $cn=>$cptset)
        {
            $res .= "var tmp = new Array();\n";
            foreach ($cptset as $key=>$valu)
            {
                $res .= "tmp['$key'] = new Array();\n";
                $res .= "tmp['$key']['name'] = '".$valu['name']."';\n";
                $res .= "tmp['$key']['type'] = '".$valu['type']."';\n";
                $res .= "tmp['$key']['searchable'] = ".$valu['searchable'].";\n";
                $res .= "tmp['$key']['returnable'] = ".$valu['returnable'].";\n";

            //TODO:
            //$res .= "tmp['$cn']['zid'] = '".$cpt['name']."';\n";
                $res .= "tmp['$key']['annotation'] = '".str_replace("'","\'",implode("\n",$valu['annotation']))."';\n";
            }
            $res .= "g_conceptSet['concepts']['$cn'] = tmp;\n";
        }
        return $res;
    }
    
    function loadSchema($nsHandle = 1, $sURL = NULL)
    {
        if (!is_null($sURL))
        {
            $this->location = $sURL;
        }
        $xp = new XPath();
        $xp->setVerbose(0);
        $xp->setXmlOption(XML_OPTION_CASE_FOLDING,false);
        $xp->setXmlOption(XML_OPTION_SKIP_WHITE,true);
        $res = $xp->importFromFile($this->location);
        if (!$res)
             return;

        //load the schema namespaces
        $this->namespaces = getNamespaces($xp,$this->namespaces);

        //get the target namespace of this schema
        $nodes = $xp->match("//xsd:schema[@targetNamespace]");
        $this->targetNamespace = $xp->getAttributes($nodes[0],'targetNamespace');

        $this->schemaNamespace[$this->nsPrefix.$nsHandle] = $this->targetNamespace;

        //find the xmlschema prefix
        $xsdHandle = "xsd";
        foreach ($this->namespaces as $k=>$v)
        {
            if ($v == XMLSCHEMANS)
                $xsdHandle = $k;
        }
        $digirHandle = "digir";
        foreach ($this->namespaces as $k=>$v)
        {
            if ($v == DiGIRNameSpace)
                $digirHandle = $k;
        }
        
        //get the schema annotation
        $aq = "/$xsdHandle:schema/$xsdHandle:annotation/$xsdHandle:documentation";
        $nodes = $xp->match($aq);
        $thisdoc = array();
        foreach ($nodes as $apath)
        {
            array_push($thisdoc,$xp->getData($apath));
        }
        $this->annotation[$this->location] = $thisdoc;
        
        //Load the concepts from the schema.
        $cq = "//$xsdHandle:element";
        $nodes = $xp->match($cq);
        $tmpConcepts = array();
        foreach ($nodes as $apath)
        {
            $attrs = $xp->getAttributes($apath);
            if (isset($attrs['substitutionGroup']))
            {
                if (($attrs['substitutionGroup'] == "$digirHandle:searchableData") ||
                    ($attrs['substitutionGroup'] == "$digirHandle:searchableReturnableData") ||
                    ($attrs['substitutionGroup'] == "$digirHandle:returnableData"))
                {
                    //get annotations
                    $aq = $apath."/$xsdHandle:annotation/$xsdHandle:documentation";
                    $anodes = $xp->match($aq);
                    $annotations = array();
                    foreach ($anodes as $dpath)
                    {
                        array_push($annotations,$xp->getData($dpath));
                    }
                    $attrs['annotation'] = $annotations;
                    $cname = $this->nsPrefix.$nsHandle.":".$attrs['name'];
                    if ($attrs['substitutionGroup'] == "$digirHandle:searchableReturnableData")
                    {
                        $attrs['searchable'] = 1;
                        $attrs['returnable'] = 1;
                    }
                    elseif ($attrs['substitutionGroup'] == "$digirHandle:searchableData")
                    {
                        $attrs['searchable'] = 1;
                        $attrs['returnable'] = 0;
                    }
                    elseif ($attrs['substitutionGroup'] == "$digirHandle:returnableData")
                    {
                        $attrs['searchable'] = 0;
                        $attrs['returnable'] = 1;
                    }
                    else
                    {
                        $attrs['searchable'] = 0;
                        $attrs['returnable'] = 0;
                    }
                    $tmpConcepts[$cname] = $attrs;
                }
            }
        }
        $this->concepts[$this->nsPrefix.$nsHandle]=$tmpConcepts;

        //Load referenced schemas if necessary
        $nds = $xp->match("//$xsdHandle:import");
        foreach ($nds as $npath)
        {
            $attrs = $xp->getAttributes($npath);
            if (count($attrs) > 0)
            {
//                if (array_search($attrs['namespace'],$this->namespaces) !== FALSE)
                {
                    $this->loadSchema($nsHandle+1,$attrs['schemaLocation']);

                }
            }
        }
    }
}

function generateScript($domainDoc)
{
    echo "//Domain Doc=".$domainDoc."\n";
    $xp = new XPath();
    $xp->setVerbose(0);
    $xp->setXmlOption(XML_OPTION_CASE_FOLDING,false);
    $xp->setXmlOption(XML_OPTION_SKIP_WHITE,true);
    $res = $xp->importFromFile($domainDoc);
    $pths = $xp->match("//conceptualSchema/location");
    if (count($pths) <= 0)
    {
		echo "//ERROR: No location elements in /conceptualschema";
        return;
    }
    $conceptURL = $xp->getData($pths[0]);
    echo "//DEBUG: FOUND URL:".$conceptURL."\n";
    //$conceptURL = "http://128.32.214.123/MaNIS/DwC/darwin2jrw030315.xsd";
    $defaultFormats = array();
    $pths = $xp->match("//recordSchemas/resultSchema");
    foreach ($pths as $pth)
    {
        $handle = $xp->getData($pth."/handle[1]");
        $location = $xp->getData($pth."/location[1]");
        //$nm = $xp->getAttributes($pth,"name");
        //$defaultFormats[$nm] = $xp->getData($pth);
        $defaultFormats[$handle] = $location;
    }
    echo "//concept URL=$conceptURL\n";
    $concepts = new conceptSet($conceptURL);
    $concepts->loadSchema();
    echo $concepts->toJavaScript();
    
    echo "//default record formats\n";
    echo "var g_defaultRecordFormats = new Array();\n";
    foreach ($defaultFormats as $k=>$v)
    {
        echo "g_defaultRecordFormats['$k']='$v';\n";
    }
}

//Set the IP address of the calling application - necessary for the
//request to the DiGIR provider
echo "var g_ClientIP='".$_SERVER['REMOTE_ADDR']."';\n";
echo "var g_DiGIRNS='".DiGIRNameSpace."';\n";
echo "var g_XMLSCHEMANS='".XMLSCHEMANS."';\n";
echo "var g_XmlSchemaInstanceNS='".XMLSCHEMAINST."';\n";

if ($loadDomain)
{
    if (DIGIR_USE_CACHE)
	{
		$cacheParams = array('cache_dir'=>DIGIR_CACHE_DIRECTORY,
                         'filename_prefix'=>'JS_');
		$cache = new Cache_Function('file',
                                 $cacheParams,
                                 DIGIR_SCRIPT_CACHE_LIFE_SECONDS);
		$domainDoc = getVar("domain",DEFAULT_DOMAIN_DOC);
		$cache->call('generateScript',$domainDoc);
	}
	else
	{
		$domainDoc = getVar("domain",DEFAULT_DOMAIN_DOC);
		generateScript($domainDoc);
	}
}

?>//end of generated code</code></pre>
