Click here to monitor SSC
SQLServerCentral is supported by Red Gate Software Ltd.
 
Log in  ::  Register  ::  Not logged in
 
 
 

CLIENT SIDE XML - ASP on steroids !

By Leon Platt,

CLIENT SIDE XML - ASP on steroids !

In my first article XML in 20 minutes , I discussed the basics of XML, identified key properties of the XML DOM and demonstrated the ability to query a XML DOM using XPATH. I ended the article by creating a server-side Active X control which would query the titles table in the pubs sample database and return a list of books in XML format. My Second article will pick up right where I left off, demonstrating the ability to use the XML DOM on the client-side browser. Using Java script, I will load the XML string that is retrieved from the Active X control into a DOM and then use the DOM to bind the data to form controls on an ASP page. I will then use the DOM to browse through the list of books providing the ability to prune off book nodes from the DOM and save them as a new XML document. All of this functionality without ever reloading the ASP page!

Some key concepts I will go over in this article are the SelectSingleNode() method, InsertBefore() method, the RemoveChild() method and finally the CloneNode method of the XML DOM. Using these four methods, I will be able to manipulate my list of books in any manner I desire. The code presented here has taken me at least a hundred hours to research, develop and refine. I am including it as a download, free of charge with this article. All I ask is that credit be given to myself if you decide to use it or any part of it in your own code. ASP doesn't get any better than this! Try it and you'll see what I mean!

LETS GET STARTED - The BookViewer Application !

System Requirements:
Microsoft SQL Server with sample pubs database loaded
Microsoft I.E. 5.0+
Microsoft Parser version 3.0 (msxml3.dll)
Microsoft IIS - copy all ASP pages, style sheets, as well as the mybooks.xml file into a virtual directory
Place the files: bookview.asp, xml_receive.asp, mybooks.xml, default.css, and book.css in a virtual directory on your web server.
Register the WebClass.xmlcontrol on your web server (webclass.dll)
Set your sql servers name in the xml_receive.asp page
Set your sql server username and password in the webClass.xmlcontrol class

To demonstrate the client-side capabilities of the XML DOM, I will be relying extensively on an application that I developed called the BookViewer Application. I highly recommend downloading the code and running it to get a feel for its capabilities before I get into explaining the technicalities.

The Application consists of three main components. First, there is the server-side Active X control that was developed in the last article (webclass.dll). This control should be compiled as a DLL and loaded on your web server. Note: I have made two minor changes to the control since the first article. I have added a property called strVersion and set it to "xmlControl Version 1.1". This is purely for debugging purposes. I expose this version string in my ASP pages so that I can be sure the correct version of the control is being used at all times. Also, I added the following XML processing instruction (<?xml version= "1.0"?>) to the beginning of the XML string that is being created by the control. The reason for this is that, I want all of my XML strings to be consistent with each other so that I can always assume that the processing instruction will be xmldoc.childnodes.item(0) and my top level node will always be xmldoc.childnodes.item(1). Additionally, the processing instruction is necessary to identify the file as an XML file structure.

The second component, "xml_receive.asp" is what I call an ASP listener page. The purpose of this page is to sit and listen for requests to use the server-side control. Basically, this page acts as a bridge between my client-side application and my server-side Active X control. In some ways this technology is very similar to Web Services. I will be saving the explanation of this code for the topic of my next article Pseudo web service using xmlHTTP. The third component, and the main focus of this article, will be the functionality of my client-side ASP page "bookviewer.asp".

BOOKVIEWER.ASP - overview

Bookviewer.asp consists of two forms. The first form I call the "Saved Book List" and the second form I call the "Queried Book List". When the page loads, the "Saved Book List" form is displayed and the "Queried Book List" form is hidden using styles. Neither one of the forms is initially populated. Each form will have its own set of variables and its own XML DOM. Variables associated associated with the "Saved Book List" all start with a capital "D". Variables associated with the "Queried Book List" all start with a capital "S".

The fields on the forms will be directly mapped to the XML <FIELD> elements of the XML DOM. Later you will see that I walk the DOM to retrieve current <BOOK> element. Once I have the current book, I use the SelectSingleNode method to query for the data to populate each field. The book titles list box is populated by looping through all of the <BOOK> elements in the DOM and retrieving the title_id from the correct <FIELD> element. The book description textarea is created by using a floating frame over the top of a standard textarea. The reason I do this is so that the text displayed can be formatted using HTML.

The Top, Up, Down, and Bottom buttons are used to arrange the order of the books in the DOM. The VCR buttons can be used to browse through the list of books. The user may also click on the title_id of any book in the book titles box to view it. The delete button will remove the currently selected book from the DOM. The save button will write the DOM to a file and display the XML in the browser. The copy from query button will open the second form "Queried Book List" and allow books to be copied from it to our "Saved Book List". The "Saved Book List" form will be used to display and manipulate the users saved list of books and should look similar to the picture bellow:

BOOKVIEWER.ASP - "Saved Book List

For the purposes of this article the user will start off by opening a saved books file. A default file (mybooks.xml) is included with the sample code and should be copied to the web server and located in the same directory as bookviewer.asp. To open the saved book list, click on the "Open My Books" button. The DOM will be loaded and the form should look similar to the one in the picture bellow:

Open My Books - how does it work?

Lets start with the mybooks.xml file. What does the XML look like? I have shown a portion of the file bellow. The structure of the XML is the same as the XML that was generated from the Active X control I created in my first article. To refresh your memories, each <BOOK> is an element and we have seven <FIELD> elements under each book. Each <FIELD> element has several attributes (@) one of which I will use in this article (@gcolumnname). The @gcolumnname will be used to bind the <FIELD> elements text value to the form control in the ASP page.

<?xml version="1.0"?>
<TITLES>
	<BOOK>Computer Phobic AND Non-Phobic Individuals: Behavior Variations
		<FIELD prettyname="Title Identification Number" tablename="titles" gcolumnname="title_id" datatype="number" gfilter="">PS1372</FIELD>
		<FIELD prettyname="Title of the Book" tablename="titles" gcolumnname="title" datatype="text" gfilter="">Computer Phobic AND Non-Phobic Individuals: Behavior Variations</FIELD>
		<FIELD prettyname="Type of Book" tablename="titles" gcolumnname="type" datatype="text" gfilter="">psychology </FIELD>
		<FIELD prettyname="Price of the Book" tablename="titles" gcolumnname="price" datatype="number" gfilter="">21.59</FIELD>
		<FIELD prettyname="Year to date sales" tablename="titles" gcolumnname="ytd_sales" datatype="number" gfilter="">375</FIELD>
		<FIELD prettyname="Notes about the book" tablename="titles" gcolumnname="notes" datatype="memo" gfilter="">A must for the specialist, this book examines the difference between those who hate and fear computers and those who don't.</FIELD>
		<FIELD prettyname="Date Published" tablename="titles" gcolumnname="pubdate" datatype="date" gfilter="">10/21/1991</FIELD>
	</BOOK>
	<BOOK>Secrets of Silicon Valley
		<FIELD prettyname="Title Identification Number" tablename="titles" gcolumnname="title_id" datatype="number" gfilter="">PC8888</FIELD>
		<FIELD prettyname="Title of the Book" tablename="titles" gcolumnname="title" datatype="text" gfilter="">Secrets of Silicon Valley</FIELD>
		<FIELD prettyname="Type of Book" tablename="titles" gcolumnname="type" datatype="text" gfilter="">popular_comp</FIELD>
		<FIELD prettyname="Price of the Book" tablename="titles" gcolumnname="price" datatype="number" gfilter="">20</FIELD>
		<FIELD prettyname="Year to date sales" tablename="titles" gcolumnname="ytd_sales" datatype="number" gfilter="">4095</FIELD>
		<FIELD prettyname="Notes about the book" tablename="titles" gcolumnname="notes" datatype="memo" gfilter="">Muckraking reporting on the world's largest computer hardware and software manufacturers.</FIELD>
		<FIELD prettyname="Date Published" tablename="titles" gcolumnname="pubdate" datatype="date" gfilter="">6/12/1994</FIELD>
	</BOOK>
</TITLES>

LOAD THE BOOKS - function get_books()

When the user clicks on "Open My Books" the mybooks.xml files is loaded into DOM (objDallbooks) on the client. This is accomplished by sending a command in XML format to the ASP listener page xml_receive.asp, which actually does the work of opening the file on the web server, loading it into a DOM and passing it back to the client. On the client, I load the response from xml_receive.asp into the client-side DOM named objDallbooks. I then check for errors, and if everything went ok I am ready to roll. From here, all I need to do is call the function that will extract the correct <BOOK> element from the DOM and map the correct <FIELD> elements of the <BOOK> to the appropriate controls on the form.

/*<%
'//---------------------------get_books()--------------------------
'//
'//	CALLED BY: load_books
'//
'//	INPUT:	whichone - 1=Saved Book list
'//						2=Queried Book list
'//					
'//	PURPOSE:  Get the xml representation of the book list and load it into the dom
'//				build the xml command to retreive the book list
'//				send the xml command using a http request to xml_receive
'//				load the http response into a dom
'//				initialize variables
'//
'//	OUTPUT:	  Xnumberofbooks - number of books in the dom
'//				needtosave - false
'//				
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000 
'//	
'//%>*/
function get_books(whichone)
{  
	if (whichone==1)  //Saved Book List
	{  
		var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
		var filename='mybooks.xml';
		//build the command to get my saved books
		var xmlcmd='<?xml version="1.0"?><ENVELOPE><XMLCMD c1="getmybooks" p1="'+filename+'" p2="" p3=""></XMLCMD></ENVELOPE>';
		xmlhttp.Open("POST", "xml_receive.asp", false); //use http to send a command to xml_receive.asp
		xmlhttp.Send(xmlcmd); //send the xml command
		objDallbooks.load(xmlhttp.responseXML); //put the response into a dom
		var xmlhttp = null; 
		//check for errors in the response
		if (objDallbooks.childNodes.item(1).childNodes.item(0).nodeValue=="FALSE") Dnumberofbooks=0;
		else Dnumberofbooks=(objDallbooks.childNodes.item(1).childNodes.length); //set number of books
		 if(Dnumberofbooks>0) 			display_book(1,1); //display the first book
		needtosave=false;
	}

DISPLAY A BOOK - function display_book()

The function display book is called to display the first book in the "Saved Book List". This is accomplished using XPATH to extract the book information. Once extracted the data is mapped to a control on the form. This is a good example of the way in which XPATH works. Remember that an XPATH query is run from the current context node. In the code bellow, note that I set my context node using the objDbook node object.

The XPATH query is run using the SelectSingleNode method of the context node (objDbook.SelectSingleNode). The next interesting thing to note here is that I am using the gcolumnname attribute of the <FIELD> element to extract the correct <FIELD> element containing the specific data I am looking for. The book notes are written to a floating frame that is created using a <DIV> tag. The reason I do this is so that the text can be formatted with HTML. This could allow me to be very creative with the text I display here.

The last thing done in this code is to walk through each book in the DOM and put its title_id into the list box on the form. In this situation it is easier to get the data by walking the DOM rather than trying to query for it using XPATH.

/*<%
'//---------------------------display_book()--------------------------
'//
'//	CALLED BY: get_books(),vcr(),allbooks_onclick(),movebook(),Ddelete_onclick(),Spaste_onclick()
'//
'//	INPUT:	whichone - 1=saved book list  0=queried book list
'//			tmpbookIndex - the books position in the list
'//		
'//	PURPOSE:  Select the current book from the dom containing all books
'//				query the book object and get the info to populate the form controls
'//				update the listbox containing the IDs of all books in the dom
'//
'//	OUTPUT:	 Xbooknumber - the current books position in the list
'//				objXbook - an object containing all info on the current selected book
'//
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000  
'//
'//%<*/
function display_book(whichone,tmpbookIndex)
  {  
  if (tmpbookIndex<1) return;
  if (whichone==1)  //Saved book list
	{
		Dbooknumber = tmpbookIndex;
		//the position of a book in the DOM is always Dbooknumber - 1 
		objDbook= objDallbooks.childNodes.item(1).childNodes.item(Dbooknumber-1);
		var DbookName= objDbook.selectSingleNode("FIELD[@gcolumnname='title']").text.toUpperCase();
		/*<% '//display the book name%>*/
		document.all.Dbookname.innerText=
		 DbookName;varDbookType=objDbook.selectSingleNode("FIELD[@gcolumnname='type']").text;
		/*<% '//display type of book%>*/
		document.frmFields.Dtype.value=
		 DbookType;varDbookWhen=objDbook.selectSingleNode("FIELD[@gcolumnname='pubdate']").text;
		/*<% '//display when the book was published%>*/
		document.frmFields.Dwhen.value=
		 DbookWhen;varDbookPrice=objDbook.selectSingleNode("FIELD[@gcolumnname='price']").text;
		/*<% '//display the book price%>*/
		document.frmFields.Dprice.value=
		 DbookPrice;varDbookSales=objDbook.selectSingleNode("FIELD[@gcolumnname='ytd_sales']").text;
		/*<% '//display the books year to date sales%>*/
		document.frmFields.Dsales.value=
		 DbookSales;varDbookid=objDbook.selectSingleNode("FIELD[@gcolumnname='title_id']").text;
		/*<% '//display the book id%>*/
		document.frmFields.Dtid.value=Dbookid;
		/*<% '//display book xx of xx %>*/
		document.all.Dbookid.innerText="[ "+Dbooknumber+" of "+Dnumberofbooks+" ]";
		/*<% '//set the length of the list box to the number of books%> */
		document.frmFields.Dallbooks.length=Dnumberofbooks;
		/*<%'//get the notes about the book%> */
		var Denglish = objDbook.selectSingleNode("FIELD[@gcolumnname='notes']").text;
		/*<%'//display the books notes%>*/
		document.all.Denglish.children(1).innerHTML=Denglish;
		/*<%'//fill in the available books list box with the current books from the xml doc%>*/
		for (i=
		
			0;i<Dnumberofbooks;i++){document.frmFields.Dallbooks.options[i].text=objDallbooks.childNodes.item(1).childNodes.item(i).selectSingleNode("FIELD[@gcolumnname=
			'title_id']").text;document.frmFields.Dallbooks.options[i].value=objDallbooks.childNodes.item(1).childNodes.item(i).selectSingleNode("FIELD[@gcolumnname='title_id']").text;
		}
		document.frmFields.Dallbooks.selectedIndex=Dbooknumber-1;
	}

DELETE A BOOK - function removebook()

The function removebook() demonstrates the ability to prune a node off of the DOM. To accomplish this, I use the RemoveChild() method of the DOM. This function is called when the user clicks on the "delete a book" button. The first thing I do is call a function setNeedToSave(). All this does is change the color of the save button to red and set a global variable indicating the DOM has changed. Next, I set my context by referencing a the <TITLES> element (objTitlesNode). Then I set a reference to the <BOOK> element I want to delete using the bookpos variable that was passed into the function. The reference to the book node is created using the objBookNode object.

The last step is to remove the node from the DOM. This is accomplished by calling the removeChild() method of my context node (objTitlesNode). In English, I am telling the <TITLES> element to delete the <BOOK> element that is in position bookpos. In the example bellow I am actually saving the removed node (book) in an object named objRemovedNode. The reason for this is because this same function will be used later when I talk about re-ordering books or copying books from the "Queried Books List". That's all there is to it to delete a node from the DOM. The important thing to remember is to set your context node, call the RemoveChild() method from the context node, passing it a reference to the node that will be deleted.

Syntax: ContextNode.RemoveChild(reference to node that will be deleted).

/*<%
'//--------------------------removebook()--------------------------
'//
'//	CALLED BY: Ddelete_onclick(), movenode()
'//
'//	INPUT:	bookpos - the position of the book that we are removing in the list box
'//				Note: this is the same as the books position in the dom
'//				
'//	PURPOSE:  removes a node (book) from the dom and stores it in an object (objRemovedNode)
'//
'//	OUTPUT:	  objRemovedNode - contains the removed node and all it descendents 
'//				
'//
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000
'//	
'//%>*/
function removebook(bookpos) 
{	
	setNeedToSave();  //dom has been modified we need to save
	//set a reference to the node that contains all books
	var objTitlesNode = objDallbooks.childNodes.item(1)
	//set a reference to the node that is currently in the position we wish to place the inserted node
	var objBookNode = objTitlesNode.childNodes.item(bookpos);
	objRemovedNode = objTitlesNode.removeChild(objBookNode); //save the node in objRemovedNode variable
	objBookNode = null;
	objTitlesNode = null;
}

RE-ARANGE THE BOOKS - function movebook()

Now that you know how to remove a book lets use this knowledge to provide the capabilities of arranging the books in any order desired. Changing the order of the books consists of two steps. First the selected book must be removed from the DOM. Second the removed book must be placed back into the DOM at its new position. I have already shown you how the remove the book from the DOM. The previous function removebook() will be used to do this.

Now you see why the removebook() function keeps the removed node in a global object called objRemovedNode. The next step is to call the function insertbook() passing it the index value of the books new position in the DOM. Lastly, I call the display_book() function to refresh the form reflecting the books new position in the DOM. The only thing new here is the insertbook() function which I will explain next.

/*<%
'//--------------------------movebook()--------------------------
'//
'//	CALLED BY: (Dmoveup,Dmovetop,Dmovedown,Dmovebottom) button onclick
'//
'//	INPUT:	movewhere - the caption of the button that was clicked
'//				
'//	PURPOSE:  Allows the user to re-order the Saved Book list
'//
'//	OUTPUT:	  Dbooknumber - the current book in the Saved Book list
'//
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000
'//	
'//%>*/
function movebook(movewhere) 
{
	if (Dnumberofbooks==0) return;
	oldpos=(document.frmFields.Dallbooks.selectedIndex);  //index in list box
	if (movewhere=='previous')
	{
		if (Dbooknumber==1) return;  //cant move up if youre at the top
		 newpos=oldpos-1; //new position in list box
		 Dbooknumber-=1; //new book number will be one less that current 
	}
	if (movewhere=='next') 
	{
		if (Dbooknumber==Dnumberofbooks) return; //cant move down if youre at the bottom
		newpos=oldpos+1;  //new position in list box
		 Dbooknumber+=1;  //new book number will be one more than current
	}
	if (movewhere=='top') 
	{
		if (Dbooknumber==1) return; //cant move to top if youre at the top
		 newpos=0; //top is position 0 in list box
		 Dbooknumber=1; //top is book number 1
	}
	if (movewhere=='bottom')
	{
		if (Dbooknumber==Dnumberofbooks) return;  //cant move to bottom if youre at the bottom
		 newpos=Dnumberofbooks-1;  //bottom is position number of books minus one
		 Dbooknumber=Dnumberofbooks;  //bottom book number is number of books
	}
	removebook(oldpos);  //remove the current node
	insertbook(newpos);  //insert it back into the dom in its new position
	display_book(1,Dbooknumber);  //display the current rule at its new location
}

The insertbook() function is not too different from the removebook() function. Just like before, the first thing I do is call the setNeedToSave() function which enables the save button, changes its color to red and sets a global variable indicating that the DOM has changed. The next two lines are the same as they were in removebook(). I am setting the context node to the <TITLES> element. I am setting a reference to the node in the DOM where I want to insert the objRemovedNode. Then I call the insertBefore() method of my context node passing it the node that I want inserted as well as the reference to the node specifying where I would like it inserted. Its not too hard to figure out that since the method is named insertBefore(), the node objRemovedNode will be inserted immediately before the reference <BOOK> node.

In English, I am telling the <TITLES> element to insert a <BOOK> element into the DOM in the position (bookpos) currently held by the reference <BOOK> node, and then increment the position of all following-siblings under <TITLES> by one. That's all there is to inserting a node into the DOM. The important thing to remember is to set your context node, call the InsertBefore() method from the context node, passing it two references. The first reference is to the node that will be inserted. The second reference is to an existing node in the DOM where I will insert the new node. In effect what happens is the new node takes the position of the referenced node and then all other nodes are pushed up one position in the DOM or down one position in the tree (however you want to look at it).

Syntax: ContextNode.InsertBefore(reference to node that will be inserted, reference to the node that currently exists in the position that the new node will be inserted into).


/*<%
'//--------------------------insertbook()--------------------------
'//
'//	CALLED BY: Spaste_onclick(), movebook()
'//
'//	INPUT:	bookpos - new position of the book in the list box
'//				note: this is the same as nodes position in the dom
'//			objRemovedNode - global object holding the node that will be inserted
'//					
'//	PURPOSE:  takes a node that has been removed from the dom and inserts it at a new location
'//
'//	OUTPUT:		none
'//				
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000 
'//	
'//%>*/
function insertbook(bookpos) 
{
	setNeedToSave();  //dom has been modified we need to save
	//set a reference to the node that contains all books
	var objTitlesNode = objDallbooks.childNodes.item(1)
	//set a reference to the node that is currently in the position we wish to place the inserted node
	var objBookNode = objTitlesNode.childNodes.item(bookpos);
	//  note: we do a insertBefore which effectively puts our inserted node in desired location
	//       and pushes all the other nodes up one in the dom
	objInsertNode = objTitlesNode.insertBefore(objRemovedNode, objBookNode);
	//clean up objects
	objTitlesNode=null;
	objBookNode=null;
	objRemovedNode=null;
	objInsertNode=null;
}

BOOKVIEWER.ASP - "Queried Book List"

The second form in the bookviewer application is the "Queried Book List" and it is normally hidden using styles. By dynamically changing the style, the form can be displayed or hidden at the click of a button. When the user clicks the "Copy from query" button on the "Saved Book List" form, the "Queried Book List form will be displayed in the browser. Now the user can click the "Query Titles Database" button to load the form. When the button is clicked, the function get_books() will be called, which will create an XML command, send the command to the ASP listener page (xml_receive.asp) using a HTTP request.

The listener page will disassemble the XML command it receives and use the results to instantiate the WebClass.xmlcontrol object, which will query the titles table in the pubs database and return the results in XML back to the ASP listener page. The ASP listener page will then check for errors and return the results back as a HTTP response to the client function get_books(), where the XML will be loaded into a client side DOM.

Once the client-side DOM has been populated, the function display_book() will be called to populate the form controls with the data from the first book in the list. The results of this, should produce a form that looks similar to the one bellow: This form provides the same browsing functionality as the "Saved Books List" form. However, its main purpose is to allow the user to copy a book from the queried results into his/her "Saved Books List". I will explain the code that makes this work next.



xml_receive.asp - querying the titles table in the pubs database.

The code bellow is a snippet from the ASP listener page xml_receive.ASP. The code is responsible for instantiating the xmlControl class, calling the GetTitlesXML function and returning the results of the query as XML.

/*<%  
<%@ language=JavaScript %>
<%
    Response.Expires = -1000;
    // Load the posted XML Command
    //Command format is <XMLCMD c1="command" p1="parameter" p2="parameter" p3="parameter" ></XMLCMD>
	//var xmlcmd='<?xml version="1.0"?><ENVELOPE><XMLCMD c1="getmybooks" p1="" p2="" p3=""></XMLCMD></ENVELOPE>';
    var doc = Server.CreateObject("Msxml2.DOMDocument");
    doc.load(Request);
	//doc.loadXML(xmlcmd);
	var c1=doc.childNodes.item(1).childNodes.item(0).attributes.item(0).text; //command
    var p1=doc.childNodes.item(1).childNodes.item(0).attributes.item(1).text; //parameter 1
    var p2=doc.childNodes.item(1).childNodes.item(0).attributes.item(2).text; //parameter 2
    var p3=doc.childNodes.item(1).childNodes.item(0).attributes.item(3).text; //parameter 3
    if (doc.childNodes.item(1).childNodes.length==2) //check for passed xml
    var passedXML='<?xml version="1.0"?>'+doc.childNodes.item(1).childNodes.item(1).xml;
    var xmlreturn=''; //initialize return string
    var doc=null;
    var resultsXML=true; //true means results object will load a xml string
						//false means results object will load a xml file
    var sqlServer='LANSBS';  //put your sql servers name Here
	//
    //************* Function gettitlesxml ***********************
    if (c1=="gettitlesxml") 
    { 
		//Response.Write("gettitlesxml");
		resultsXML=true; //results object will load a xml string
	    var objWC= Server.CreateObject("WebClass.xmlControl"); //instantiate vb control
	    objWC.strDataBase=p1; //which database 
	    objWC.strServer=sqlServer; //which sql server
		xmlreturn=objWC.GetTitlesXML(); //vb function to get titles and return as xml string
		var objWC=null; //destroy the vb control
	}
	//
    var result = Server.CreateObject("Msxml2.DOMDocument"); //create the results object
    //Response.Write(resultsXML);
    if (resultsXML) //load a xml string
    {
		result.loadXML(xmlreturn);
		if (c1=="savemybooks") result.save(Server.MapPath(p1)); //save the xml if necessary
	}
	else //load a file 
	{
		result.load(Server.MapPath(p1)); //load the saved book list from a xml file
	}
	//Response.Write('Error code: ' +result.parseError.errorCode);
	//Response.Write('Error description: ' +result.parseError.reason);
	//Response.End;
	if ( result.parseError.errorCode!=0 ) //error parsing document
	{
		xmlreturn='<?xml version="1.0"?><RESULTS error="true">FALSE</RESULTS>'; //return for errors
		result.loadXML(xmlreturn); //load the xml error string into the resutls object
	}
    // Now process the order and build the result document.
    Response.ContentType = "text/xml"; //we are sending xml back
	//Response.Write(result.xml);
	//Response.End;
    result.save(Response); //save the dom to the HTTP Response object
    var result = null;
    %>

Bookview.asp - function get_books()

The code bellow is a snippet from the get_books() function that is responsible for loading the query results into a client-side DOM (objSallbooks) and then calling the function to display the first book in the DOM.

/*<%
'//---------------------------get_books()--------------------------
'//
'//	CALLED BY: load_books
'//
'//	INPUT:	whichone - 1=Saved Book list
'//						2=Queried Book list
'//					
'//	PURPOSE:  Get the xml representation of the book list and load it into the DOM
'//				build the xml command to retrieve the book list
'//				send the xml command using a http request to xml_receive
'//				load the http response into a DOM
'//				initialize variables
'//
'//	OUTPUT:	  Xnumberofbooks - number of books in the DOM
'//				needtosave - false
'//				
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000 
'//	
'//%>*/
function get_books(whichone)
{  
	if (whichone==1)  //Saved Book List
	{
	}
	else  //Queried Book List
	{  
		var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
		//build the command to get my saved books
		var xmlcmd='<?xml version="1.0"?><ENVELOPE><XMLCMD c1="gettitlesxml" p1="" p2="" p3=""></XMLCMD></ENVELOPE>';
		xmlhttp.Open("POST", "xml_receive.asp", false);  //use http to send a command to xml_receive.asp
		xmlhttp.Send(xmlcmd); //send the xml command
		objSallbooks.load(xmlhttp.responseXML); //put the response into a dom
		var xmlhttp = null;
		//check for errors in the response
		if (objSallbooks.childNodes.item(1).childNodes.item(0).nodeValue=="FALSE") Snumberofbooks=0;
		else Snumberofbooks=(objSallbooks.childNodes.item(1).childNodes.length); //set number of books
		if (Snumberofbooks>0) 			display_book(0,1); //display the first book
	}
  }

BOOKVIEWER.ASP - copy a book from "Queried Book List" to "Saved Book List"

So far I have demonstrated removing a node and inserting a node into a XML DOM. The process of copying a node from one DOM to another is very similar to the movebook() function with two exceptions. First, I don't want to actually remove the node from the "Queried Book List" DOM. I just want to duplicate it. Second, I don't want to insert the node back into the same DOM. I want to insert it into the "Saved Book List" DOM. It just so happens that the node object has a method to help us do this called cloneNode().

Once the node has been cloned, it is just a matter of inserting it into the "Saved Book List" DOM in the correct position. The execution works like this: the users selects a book in the "Queried Book List" list box, the user then clicks on the "Paste" button and Spaste_onclick() is called, then the function copybook() is called which uses the cloneNode() method of the "Queried Books List" DOM to place a duplicated copy of the selected book into the objRemovedNode object, next I use the insertbook() function again which uses the insertBefore() method of the "Saved Book List" DOM to insert the objRemovedNode into the "Saved Book List" DOM.

Note the book will be copied into the "Saved Book List" immediately before the currently selected node. After the book has been copied, the "Queried Book List" hidden, the copied book is made the currently selected book and display_book() is called to refresh the "Saved Book List" form.


/*<%
'//--------------------------Spaste_onClick()--------------------------
'//
'//	CALLED BY: Spaste button onclick
'//
'//	INPUT:	no
'//				
'//	PURPOSE:  when the paste button is clicked the currently selected book(s)
'//				from the Queried Book List will be copied one by one to the Saved Book List.
'//				Books will be inserted into the Saved Book List before the currently
'//				selected book.  When done copying the Queried Book List will be hidden.
'//
'//	OUTPUT:  Dnumberofbooks = Dnumberofbooks + number of copied books		
'//				
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000  
'//	
'//%>*/
function Spaste_onclick()
{
	for (i=0;i<Snumberofbooks;i++) //go through each book in the queried book list
	{
		if (document.frmFields.Sallbooks.options[i].selected) //if selected then copy it
		{
			copybook(i); //copy the book
			insertbook(Dbooknumber-1);  //insert the book before the current selected book
			Dnumberofbooks+=1; //increment the number of books in the saved book list
		}
	}
	toggleSource(document.frmFields.Dcopy.value);  //hide the Queried Book List
	display_book(1,Dbooknumber); //display the last 
}


The copybook() function is fairly simple. First, I create a reference to the selected book and store it using the node object objBookNode. Then I call the cloneNode() method of the objBookNode (objBookNode.cloneNode(true)). Note that I am passing the parameter (true). This instructs the method to clone the node along will all of its descendants. The resulting node is stored in the global object, objRemovedNode.

Note: the node is not actually removed from the DOM, I am just reusing the global object objRemovedNode instead of creating another global node object. In my example, the results that will be stored in objRemovedNode is the <BOOK> element along with all of its <FIELD> elements, attributes and text nodes. If I passed in a parameter of (false) to the cloneNode() method, the only thing stored in objRemovedNode would have been the <BOOK> element itself.


/*<%
'//--------------------------copybook()--------------------------
'//
'//	CALLED BY: Spaste_onclick()
'//
'//	INPUT:	bookpos - the position in the list box of the book that we are copying
'//				Note: this is the same as the books position in the dom
'//				
'//	PURPOSE:  clone a node along with all of its descendants and store it in objRemovedNode
'//
'//	OUTPUT:	  objRemovedNode - a clone of the node that we are copying
'//				
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000
'//
'//%>*/
function copybook(bookpos) 
{	
	//create a reference to the node that we would like to copy
	var objBookNode = objSallbooks.childNodes.item(1).childNodes.item(bookpos);
	//use the cloneNode method to copy the node
	//  include true to get the descendants along with the node
	objRemovedNode = objBookNode.cloneNode(true);
	objBookNode = null;
}

BOOKVIEWER.ASP - save the "Saved Book List" to a file

The last thing that needs to be done is to save the "Saved Book List" to a XML file (mybooks.xml) on the web server. This is done using the ASP listener page (xml_receive.asp). The way I accomplish this is to build an XML command called "savemybooks", append the xml representation of the "Saved Book List" DOM (objDallbooks.childNodes.item(1).xml) to the command and pass everything to the ASP listener page.

When the listener page detects this command, it extracts the book list xml and loads it into a DOM on the server and then calls the save() method of the DOM passing it a filename of "mybooks.xml". This writes the XML out to a file on the web server. If everything works correctly, the ASP listener page passes back an XML result indicating a successful save.


/*<%
'//---------------------------save_book()--------------------------
'//
'//	CALLED BY: close_books(), Dsave button onclick
'//
'//	INPUT:	none
'//		
'//	PURPOSE:  Build a command to save the current list of books
'//				send the command using a http request to xml_receive
'//				Redirect to the xml document that was just saved
'//
'//	OUTPUT: an xml file (mybooks.xml) in the root of the web directory
'//
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000  
'//
'//%>*/
function save_books()
  {    
	if (!confirm('Click OK to save your changes?'))	return;
	document.frmFields.Dsave.disabled=true;
	document.frmFields.Dsave.className='greenbutton';
	needtosave=false;
	var filename='mybooks.xml';  //name of file that will be created
    var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    //build command that will save the book list
    var xmlcmd='<?xml version="1.0"?><ENVELOPE><XMLCMD c1="savemybooks" p1="'+filename+'" p2="" p3=""></XMLCMD>';
	xmlcmd+=objDallbooks.childNodes.item(1).xml+'</ENVELOPE>';   //append book list to command
    xmlhttp.Open("POST", "xml_receive.asp", false); //use http to send command to xml_receive
    xmlhttp.Send(xmlcmd);  //send the command using http
	var objResults = new ActiveXObject("Msxml2.DOMDocument");  
    objResults.async=false;
    objResults.load(xmlhttp.responseXML);  //put the results in a dom
    //check for errors in the response
    if (objDallbooks.childNodes.item(1).childNodes.item(0).nodeValue=="FALSE") alert('Save UnSuccessful!');
		else window.location.href="mybooks.xml";  //redirect to the xml file
    var xmlhttp = null;
    objResults = null;
  }

CONCLUSION - don't forget to come back next month

This ends my demonstration of client-side XML. In this article, I demonstrated the ability to populate two client-side DOMs and bind the data from each DOM to specific form controls. I showed how to create several generic functions to handle removing nodes, inserting nodes, arranging nodes, and copying nodes from one DOM to another. In some cases I "walked" the DOM to get the information I needed and in other cases I used XPATH to query for specific data in the DOM. Could I have accomplished this same functionality using client-side recordset? Probably, but XML is the ultimate tool for manipulating heirachical data.

So will this code scale to millions of users? Well yes and no. Since most of the work is preformed on the client we really don't see a big performance impact by adding users. However, you should be aware that the XML DOM is a memory hog. If anyone knows of a light weight DOM let me know. If not it may be a good task for some of you C++ programmers out there. The Microsoft DOM provides a lot of fluff but as you can see in my articles I only need a few capabilities to get a lot of functionality.

In this example I kept two copies of the DOM object active at all times. This solution works fine for small documents but I wouldn't try using this application to browse your local yellow pages! I seen a demo once where a 3.5 meg file was loaded into a client-side DOM and IE ended up using close to 100Megs of RAM to load it! Wow..., the moral is keep your XML files small or find a different method than this. My sqlServerCentral friend Andy Warren and I are thinking about doing something in the future regarding the advantages/disadvantages of XML -vs- SQL.

Using the techniques provided in this article and my previous article XML in 20 minutes will allow you to stretch the functionality of ASP to it's extreme limits. Like I said earlier, "It doesn't get any better than this!" I can't promise all my articles will amaze you like this one, but I do still have a few tricks up my sleeve, so keep checking my home page for more ideas on how you can benefit from using XML in your web applications.

Coming up next month... Pseudo Web Services using xmlHTTP, where I will delve into the mysterious ASP listener page (xml_receive.asp). Also don't forget to check out the Tip of the Month, each and every month. This months tip: Invisible Comments - Now you see'em, Now you don't. "

Download the source code for this article

Listing 1 - Bookviewer.asp

<%@ Language=VBScript %>

<%
Response.Expires =-1
' --------------------------------------------------------------------------------
'									BookViewer.ASP
'-----------------------------------------------------------------------------------
'	
'	CALLED BY: Browser IE5+ compatible
'
'	INPUT:  none
'		
'	PURPOSE:  Query the titles table in the pubs database and display the results
'				Allow the user to save titles in their personal file
'
'	OUTPUT:   XML output of the users saved titles
'
'	DEVELOPER:	Leon Platt
'
'	DATE:    Feb 18, 2000
'
' --------------------------------------------------------------------------------

dim objBV

' ****************************************************************************
' Create the BV Control    "WebClass.xmlControl"
' ****************************************************************************

set objBV = Server.CreateObject("WebClass.xmlControl")
control_version=objBV.strVersion
%>
<!--Control Version:  <%=control_version%>     -->
<%
' ****************************************************************************
'                                           HEAD                                                                                        
' ****************************************************************************
%>
<html>
<head>
<%
' ****************************************************************************
'                                           STYLES                                                                                        
' ****************************************************************************
%>
<link rel="stylesheet" type="text/css" href="default.css">
<link rel="stylesheet" type="text/css" href="books.css">
<STYLE ID="localstyles" TYPE="text/css">
<%
	Response.Write ".Dbook {display:block}"	
	Response.Write ".Sbook {display:none}"
	Response.Write ".global {display:none}"
%>
</STYLE>
<title>Book Viewer</title>
<meta NAME="GENERATOR" Content="Microsoft FrontPage 4.0">
<%
' ****************************************************************************
'                                           JAVA  SCRIPT CLIENT SIDE FUNCTIONS                                           
' ****************************************************************************
%>
<script ID="bookviewer" LANGUAGE="javascript">
<!--
//----------------------global variables------------------
//
//  all variables that begin with D correspond to the saved book list
//
//  all variables that begin with S correspond to the queried book list
//
var Dnumberofbooks=0;  //total number of books in the saved book list
var Snumberofbooks=0;  //total number of books in the queried book list
var Dbooknumber=0;  //position of the current book in the saved book list
var Sbooknumber=0;  //position of the current book in the queried book list
var objSallbooks = new ActiveXObject("Msxml2.DOMDocument");  //dom to hold query results
objSallbooks.async=false;
var objSbook;  //object to hold the current book in the queried book list 
var objDallbooks = new ActiveXObject("Msxml2.DOMDocument");  //dom to hold saved book list
objDallbooks.async=false;
var objDbook; //object to hold the current book in the saved book list
var objRemovedNode; //used to move books and copy books
var needtosave=false; //keep track of wheter or not users book list has changed
/*<%
'//---------------------------initialize()--------------------------
'//
'//	CALLED BY: document onload
'//
'//	INPUT:	none	
'//				
'//	PURPOSE:  Set form controls to be enabled or disabled
'//
'//	OUTPUT:	 none
'//				
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000  
'//	
'//%>*/
function initialize()
{
	setControls(1,true);
	setControls(0,true);
	return;
}
/*<%
'//---------------------------toggleSource()--------------------------
'//
'//	CALLED BY: spaste_onclick(), Dcopy button onclick
'//
'//	INPUT:	curValue - the current captin on the Dcopy button
'//		
'//	PURPOSE:  Toggles the caption of the Dcopy button
'//				Hides and Shows the Query results book list
'//
'//	OUTPUT:	none	
'//				
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000 
'//	
'//%>*/
function toggleSource(curValue)
  {    
  if (curValue=="Copy From Query")
  {
		document.frmFields.Dcopy.value="Hide Query Set";
		document.styleSheets.localstyles.rules[1].style.display="block";
		document.location.href="#S_TABLE";
	}
	else
    {
		document.frmFields.Dcopy.value="Copy From Query";
		document.styleSheets.localstyles.rules[1].style.display="none";
		document.location.href="#D_TABLE";
	}
}
/*<%
'//---------------------------get_books()--------------------------
'//
'//	CALLED BY: load_books
'//
'//	INPUT:	whichone - 1=Saved Book list
'//						2=Queried Book list
'//					
'//	PURPOSE:  Get the xml representation of the book list and load it into the dom
'//				build the xml command to retreive the book list
'//				send the xml command using a http request to xml_receive
'//				load the http response into a dom
'//				initialize variables
'//
'//	OUTPUT:	  Xnumberofbooks - number of books in the dom
'//				needtosave - false
'//				
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000 
'//	
'//%>*/
function get_books(whichone)
{  
	if (whichone==1)  //Saved Book List
	{  
		var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
		var filename='mybooks.xml';
		//build the command to get my saved books
		var xmlcmd='<?xml version="1.0"?><ENVELOPE><XMLCMD c1="getmybooks" p1="'+filename+'" p2="" p3=""></XMLCMD></ENVELOPE>';
		xmlhttp.Open("POST", "xml_receive.asp", false); //use http to send a command to xml_receive.asp
		xmlhttp.Send(xmlcmd); //send the xml command
    	objDallbooks.load(xmlhttp.responseXML); //put the response into a dom
		var xmlhttp = null; 
		//check for errors in the response
		if (objDallbooks.childNodes.item(1).childNodes.item(0).nodeValue=="FALSE") Dnumberofbooks=0;
		else Dnumberofbooks=(objDallbooks.childNodes.item(1).childNodes.length); //set number of books
		if (Dnumberofbooks>0) 			display_book(1,1); //display the first book
		needtosave=false;
	}
	else  //Queried Book List
	{  
		var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
		//build the command to get my saved books
		var xmlcmd='<?xml version="1.0"?><ENVELOPE><XMLCMD c1="gettitlesxml" p1="" p2="" p3=""></XMLCMD></ENVELOPE>';
		xmlhttp.Open("POST", "xml_receive.asp", false);  //use http to send a command to xml_receive.asp
		xmlhttp.Send(xmlcmd); //send the xml command
    	objSallbooks.load(xmlhttp.responseXML); //put the response into a dom
		var xmlhttp = null;
		//check for errors in the response
		if (objSallbooks.childNodes.item(1).childNodes.item(0).nodeValue=="FALSE") Snumberofbooks=0;
		else Snumberofbooks=(objSallbooks.childNodes.item(1).childNodes.length); //set number of books
		if (Snumberofbooks>0) 			display_book(0,1); //display the first book
	}
  }
/*<%
'//---------------------------save_book()--------------------------
'//
'//	CALLED BY: close_books(), Dsave button onclick
'//
'//	INPUT:	none
'//		
'//	PURPOSE:  Build a command to save the current list of books
'//				send the command using a http request to xml_receive
'//				Redirect to the xml document that was just saved
'//
'//	OUTPUT: an xml file (mybooks.xml) in the root of the web directory
'//
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000  
'//
'//%>*/
function save_books()
  {    
	if (!confirm('Click OK to save your changes?'))	return;
	document.frmFields.Dsave.disabled=true;
	document.frmFields.Dsave.className='greenbutton';
	needtosave=false;
	var filename='mybooks.xml';  //name of file that will be created
    var xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    //build command that will save the book list
    var xmlcmd='<?xml version="1.0"?><ENVELOPE><XMLCMD c1="savemybooks" p1="'+filename+'" p2="" p3=""></XMLCMD>';
	xmlcmd+=objDallbooks.childNodes.item(1).xml+'</ENVELOPE>';   //append book list to command
    xmlhttp.Open("POST", "xml_receive.asp", false); //use http to send command to xml_receive
    xmlhttp.Send(xmlcmd);  //send the command using http
	var objResults = new ActiveXObject("Msxml2.DOMDocument");  
    objResults.async=false;
    objResults.load(xmlhttp.responseXML);  //put the results in a dom
    //check for errors in the response
    if (objDallbooks.childNodes.item(1).childNodes.item(0).nodeValue=="FALSE") alert('Save UnSuccessful!');
		else window.location.href="mybooks.xml";  //redirect to the xml file
    var xmlhttp = null;
    objResults = null;
  }
/*<%
'//---------------------------display_book()--------------------------
'//
'//	CALLED BY: get_books(),vcr(),allbooks_onclick(),movebook(),Ddelete_onclick(),Spaste_onclick()
'//
'//	INPUT:	whichone - 1=saved book list  0=queried book list
'//			tmpbookIndex - the books position in the list
'//		
'//	PURPOSE:  Select the current book from the dom containing all books
'//				query the book object and get the info to populate the form controls
'//				update the listbox containing the IDs of all books in the dom
'//
'//	OUTPUT:	 Xbooknumber - the current books position in the list
'//				objXbook - an object containing all info on the current selected book
'//
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000  
'//
'//%>*/
function display_book(whichone,tmpbookIndex)
  {  
  if (tmpbookIndex<1) return;
  if (whichone==1)  //Saved book list
	{
		Dbooknumber = tmpbookIndex;
		//the position of a book in the DOM is always Dbooknumber - 1
		objDbook= objDallbooks.childNodes.item(1).childNodes.item(Dbooknumber-1);
		var DbookName= objDbook.selectSingleNode("FIELD[@gcolumnname='title']").text.toUpperCase();
		/*<% '//display the book name%>*/
		document.all.Dbookname.innerText=DbookName;
		var DbookType=objDbook.selectSingleNode("FIELD[@gcolumnname='type']").text;
		/*<% '//display type of book%>*/
		document.frmFields.Dtype.value=DbookType;
		var DbookWhen=objDbook.selectSingleNode("FIELD[@gcolumnname='pubdate']").text;
		/*<% '//display when the book was published%>*/
		document.frmFields.Dwhen.value=DbookWhen;
		var DbookPrice=objDbook.selectSingleNode("FIELD[@gcolumnname='price']").text;
		/*<% '//display the book price%>*/
		document.frmFields.Dprice.value=DbookPrice;
		var DbookSales=objDbook.selectSingleNode("FIELD[@gcolumnname='ytd_sales']").text;
		/*<% '//display the books year to date sales%>*/
		document.frmFields.Dsales.value=DbookSales;
		var Dbookid=objDbook.selectSingleNode("FIELD[@gcolumnname='title_id']").text;
		/*<% '//display the book id%>*/
		document.frmFields.Dtid.value=Dbookid;
		/*<% '//display book xx of xx %>*/
		document.all.Dbookid.innerText="[ "+Dbooknumber+" of "+Dnumberofbooks+" ]";
		/*<% '//set the length of the list box to the number of books%> */
		document.frmFields.Dallbooks.length=Dnumberofbooks;
		/*<%'//get the notes about the book%> */
		var Denglish = objDbook.selectSingleNode("FIELD[@gcolumnname='notes']").text;
		/*<%'//display the books notes%>*/
		document.all.Denglish.children(1).innerHTML=Denglish;
		/*<%'//fill in the available books list box with the current books from the xml doc%>*/
		for (i=0;i<Dnumberofbooks;i++)
		{
			document.frmFields.Dallbooks.options[i].text=objDallbooks.childNodes.item(1).childNodes.item(i).selectSingleNode("FIELD[@gcolumnname='title_id']").text;
			document.frmFields.Dallbooks.options[i].value=objDallbooks.childNodes.item(1).childNodes.item(i).selectSingleNode("FIELD[@gcolumnname='title_id']").text;
		}
		document.frmFields.Dallbooks.selectedIndex=Dbooknumber-1;
	}
	else  //Queried Book List
	{
		Sbooknumber = tmpbookIndex;
		//the position of a book in the DOM is always Sbooknumber - 1
		objSbook= objSallbooks.childNodes.item(1).childNodes.item(Sbooknumber-1);
		var SbookName= objSbook.selectSingleNode("FIELD[@gcolumnname='title']").text.toUpperCase();
		/*<% '//display the book name%>*/
		document.all.Sbookname.innerText=SbookName;
		var SbookType=objSbook.selectSingleNode("FIELD[@gcolumnname='type']").text;
		/*<% '//display type of book%>*/
		document.frmFields.Stype.value=SbookType;
		var SbookWhen=objSbook.selectSingleNode("FIELD[@gcolumnname='pubdate']").text;
		/*<% '//display when the book was published%>*/
		document.frmFields.Swhen.value=SbookWhen;
		var SbookPrice=objSbook.selectSingleNode("FIELD[@gcolumnname='price']").text;
		/*<% '//display the book price%>*/
		document.frmFields.Sprice.value=SbookPrice;
		var SbookSales=objSbook.selectSingleNode("FIELD[@gcolumnname='ytd_sales']").text;
		/*<% '//display the books year to date sales%>*/
		document.frmFields.Ssales.value=SbookSales;
		var Sbookid=objSbook.selectSingleNode("FIELD[@gcolumnname='title_id']").text;
		/*<% '//display the book id%>*/
		document.frmFields.Stid.value=Sbookid;
		/*<% '//display book xx of xx %>*/
		document.all.Sbookid.innerText="[ "+Sbooknumber+" of "+Snumberofbooks+" ]";
		/*<% '//set the length of the list box to the number of books%> */
		document.frmFields.Sallbooks.length=Snumberofbooks;
		/*<%'//get the books notes from the dom%> */
		var Senglish = objSbook.selectSingleNode("FIELD[@gcolumnname='notes']").text;
		/*<%'//display the books notes%>*/
		document.all.Senglish.children(1).innerHTML=Senglish;
		/*<%'//fill in the available books list box with the current books from the xml doc%>*/
		for (i=0;i<Snumberofbooks;i++)
		{
			document.frmFields.Sallbooks.options[i].text=objSallbooks.childNodes.item(1).childNodes.item(i).selectSingleNode("FIELD[@gcolumnname='title_id']").text;
			document.frmFields.Sallbooks.options[i].value=objSallbooks.childNodes.item(1).childNodes.item(i).selectSingleNode("FIELD[@gcolumnname='title_id']").text;
		}
		document.frmFields.Sallbooks.selectedIndex=Sbooknumber-1;
	}		
  }
/*<%
'//--------------------------vcr()--------------------------
'//
'//	CALLED BY: (Dtop, Dprevious, Dnext, Dbottom, Stop, Sprevious, Snext, Sbottom) buttons onclick
'//
'//	INPUT:	whichone - 1=Saved book list   0=Queried book list	
'//			param - caption of the button that was clicked
'//		
'//	PURPOSE:  used to manuever through the list of books
'//
'//	OUTPUT:	  Xbooknumber - the current selected books position in the list
'//
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000  
'//
'//%>*/
function vcr(whichone,param)
{    
	if (whichone==1) //Saved book list
	{
		if (Dnumberofbooks==0) return;
		if (param=="next") Dbooknumber+=1;
		if (param=="previous") Dbooknumber-=1;
		if (param=='top') Dbooknumber=1;
		if (param=='bottom') Dbooknumber=Dnumberofbooks;
		if (Dbooknumber>=Dnumberofbooks)
		{
			 document.frmFields.Dnext.disabled=true;
			 Dbooknumber=Dnumberofbooks;
		}
		else document.frmFields.Dnext.disabled=false;
		if (Dbooknumber<=1)
		{
			 document.frmFields.Dprevious.disabled=true;
			 Dbooknumber=1;
		}
		else document.frmFields.Dprevious.disabled=false;		
		display_book(1,Dbooknumber);  //display the new book
	}
	else  //Queried book list
	{
		if (Snumberofbooks==0) return;
		if (param=="next") Sbooknumber+=1;
		if (param=="previous") Sbooknumber-=1;
		if (param=='top') Sbooknumber=1;
		if (param=='bottom') Sbooknumber=Snumberofbooks;
		if (Sbooknumber>=Snumberofbooks)
		{
			 document.frmFields.Snext.disabled=true;
			 Sbooknumber=Snumberofbooks;
		}
		else document.frmFields.Snext.disabled=false;
		if (Sbooknumber<=1)
		{
			 document.frmFields.Sprevious.disabled=true;
			 Sbooknumber=1;
		}
		else document.frmFields.Sprevious.disabled=false;		
		display_book(0,Sbooknumber);  //display the new book
	}
}
/*<%
'//--------------------------allbooks_onClick()--------------------------
'//
'//	CALLED BY: (Dallbooks,Sallbooks) button onclick
'//
'//	INPUT:	whichone - 1=Saved Book List   0=Saved Book List
'//			myindex - is the index of the selected item in the Xallbooks drop down
'//		
'//	PURPOSE:  Allows the user to click on the book id in the Xallbooks list
'//				clicking on an id will display the book clicked on
'//
'//	OUTPUT:	  Xbooknumber - the position of the current selected book in the list	
'//
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000  
'//	
'//%>*/
function allbooks_onclick(whichone,myindex) 
{
	if (myindex<0) return;
	if (whichone==1)  //Saved book List
	{
		Dbooknumber=myindex+1;  //position of book in the list is the index + 1 
								//the index starts at 0 whereas the list starts with 1
		display_book(1,Dbooknumber);  //display the book
	}
	else  //Queried Book List
	{
		Sbooknumber=myindex+1;  //position of book in the list is the index + 1 
								//the index starts at 0 whereas the list starts with 1
		if (Sbooknumber<1) Sbooknumber=1;  //this is here for handle multi select capability
		display_book(0,Sbooknumber);  //display the book
	}
}
/*<%
'//--------------------------movebook()--------------------------
'//
'//	CALLED BY: (Dmoveup,Dmovetop,Dmovedown,Dmovebottom) button onclick
'//
'//	INPUT:	movewhere - the caption of the button that was clicked
'//				
'//	PURPOSE:  Allows the user to re-order the Saved Book list
'//
'//	OUTPUT:	  Dbooknumber - the current book in the Saved Book list
'//
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000
'//	
'//%>*/
function movebook(movewhere) 
{
	if (Dnumberofbooks==0) return;
	oldpos=(document.frmFields.Dallbooks.selectedIndex);  //index in list box
	if (movewhere=='previous')
	{
		if (Dbooknumber==1) return;  //cant move up if youre at the top
		 newpos=oldpos-1; //new position in list box
		 Dbooknumber-=1; //new book number will be one less that current 
	}
	if (movewhere=='next') 
	{
		if (Dbooknumber==Dnumberofbooks) return; //cant move down if youre at the bottom
		newpos=oldpos+1;  //new position in list box
		 Dbooknumber+=1;  //new book number will be one more than current
	}
	if (movewhere=='top') 
	{
		if (Dbooknumber==1) return; //cant move to top if youre at the top
		 newpos=0; //top is position 0 in list box
		 Dbooknumber=1; //top is book number 1
	}
	if (movewhere=='bottom')
	{
		if (Dbooknumber==Dnumberofbooks) return;  //cant move to bottom if youre at the bottom
		 newpos=Dnumberofbooks-1;  //bottom is position number of books minus one
		 Dbooknumber=Dnumberofbooks;  //bottom book number is number of books
	}
	removebook(oldpos);  //remove the current node
	insertbook(newpos);  //insert it back into the dom in its new position
	display_book(1,Dbooknumber);  //display the current rule at its new location
}
/*<%
'//--------------------------insertbook()--------------------------
'//
'//	CALLED BY: Spaste_onclick(), movebook()
'//
'//	INPUT:	bookpos - new position of the book in the list box
'//				note: this is the same as nodes position in the dom
'//			objRemovedNode - global object holding the node that will be inserted
'//					
'//	PURPOSE:  takes a node that has been removed from the dom and inserts it at a new location
'//
'//	OUTPUT:		none
'//				
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000 
'//	
'//%>*/
function insertbook(bookpos) 
{
	setNeedToSave();  //dom has been modified we need to save
	//set a reference to the node that contains all books
	var objTitlesNode = objDallbooks.childNodes.item(1)
	//set a reference to the node that is currently in the position we wish to place the inserted node
	var objBookNode = objTitlesNode.childNodes.item(bookpos);
	//  note: we do a insertBefore which effectively puts our inserted node in desired location
	//       and pushes all the other nodes up one in the dom
	objInsertNode = objTitlesNode.insertBefore(objRemovedNode, objBookNode);
	//clean up objects
	objTitlesNode=null;
	objBookNode=null;
	objRemovedNode=null;
	objInsertNode=null;
}
/*<%
'//--------------------------removebook()--------------------------
'//
'//	CALLED BY: Ddelete_onclick(), movenode()
'//
'//	INPUT:	bookpos - the position of the book that we are removing in the list box
'//				Note: this is the same as the books position in the dom
'//				
'//	PURPOSE:  removes a node (book) from the dom and stores it in an object (objRemovedNode)
'//
'//	OUTPUT:	  objRemovedNode - contains the removed node and all it descendents 
'//				
'//
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000
'//	
'//%>*/
function removebook(bookpos) 
{	
	setNeedToSave();  //dom has been modified we need to save
	//set a reference to the node that contains all books
	var objTitlesNode = objDallbooks.childNodes.item(1)
	//set a reference to the node that is currently in the position we wish to place the inserted node
	var objBookNode = objTitlesNode.childNodes.item(bookpos);
	objRemovedNode = objTitlesNode.removeChild(objBookNode); //save the node in objRemovedNode variable
	objBookNode = null;
	objTitlesNode = null;
}
/*<%
'//--------------------------copybook()--------------------------
'//
'//	CALLED BY: Spaste_onclick()
'//
'//	INPUT:	bookpos - the position in the list box of the book that we are copying
'//				Note: this is the same as the books position in the dom
'//				
'//	PURPOSE:  clone a node along with all of its descendants and store it in objRemovedNode
'//
'//	OUTPUT:	  objRemovedNode - a clone of the node that we are copying
'//				
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000
'//
'//%>*/
function copybook(bookpos) 
{	
	//create a reference to the node that we would like to copy
	var objBookNode = objSallbooks.childNodes.item(1).childNodes.item(bookpos);
	//use the cloneNode method to copy the node
	//  include true to get the descendants along with the node
	objRemovedNode = objBookNode.cloneNode(true);
	objBookNode = null;
}
/*<%
'//--------------------------setNeedToSave()--------------------------
'//
'//	CALLED BY: insertbook(), removebook()
'//
'//	INPUT:	none	
'//				
'//	PURPOSE:  set the global variable needtosave to true indicating dom changed
'//				enable the save button and turn the color red
'//
'//	OUTPUT:		needtosave = true
'//				 
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000 
'//
'//%>*/
function setNeedToSave() 
{	
	needtosave=true; //set flag indicating dom has changed
	document.frmFields.Dsave.disabled=false; //enable save button
	document.frmFields.Dsave.className='redbutton';  //change color of button to red
}
/*<%
'//--------------------------Ddelete_onClick()--------------------------
'//
'//	CALLED BY: Ddelete button onclick
'//
'//	INPUT:	none	
'//				
'//	PURPOSE:  removes the currently selected book from the Saved Book List
'//
'//	OUTPUT:	Dbooknumber = Dbooknumber -1
'//			Dnumberofbooks = Dnumberofbooks -1
'//				
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000
'//
'//%>*/
function Ddelete_onclick() 
{
	if (Dnumberofbooks==0) return; //cant delete if there are none
	var bookpos=Dbooknumber-1;  //position in dom is equal to Dbooknumber - 1
	removebook(bookpos);  //remove the node
	Dbooknumber-=1; //make the previous book the current book
	Dnumberofbooks-=1; //decrement Number of books
	if (Dnumberofbooks<=0) //account for deleting last book in the list
	{
		Dnumberofbooks=0;
		Dbooknumber=0;
		alert("resetting");
		clearForm(1); //no more rules clean up form
	}
	else 	
	{
		if (Dbooknumber<1) Dbooknumber=1; //account for deleting the first book
		display_book(1,Dbooknumber); //display the new currently selected book
	}
}
/*<%
'//--------------------------clearForm()--------------------------
'//
'//	CALLED BY: Ddelete_onclick(),closebooks()
'//
'//	INPUT:	whichone - 1=Saved Book List   0=Saved Book List
'//				
'//	PURPOSE:  clear the form text fields when the last rule has been deleted
'//
'//	OUTPUT:		none
'//				
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000 
'//
'//%>*/
function clearForm(whichone) 
{
	if (whichone==1) //Saved Book List
	{
		/*<% '//clear the book name%>*/
		document.all.Dbookname.innerText='BOOK      NAME';
		/*<% '//clear the book type field%>*/
		document.frmFields.Dtype.value='';
		/*<% '//clear the book publish date field%>*/
		document.frmFields.Dwhen.value='';
		/*<% '//display book 0 of 0 %>*/
		document.all.Dbookid.innerText='[0 of 0]';
		/*<% '//clear the book price field%>*/
		document.frmFields.Dprice.value='';
		/*<% '//clear the books year to date sales field%>*/
		document.frmFields.Dsales.value='';
		/*<% '//clear the book id field%>*/
		document.frmFields.Dtid.value='';
		/*<% '//set the length of the list box to the number of books%> */
		document.frmFields.Dallbooks.length=0;
		/*<%'//Clear the book notes field%>*/
		var Denglish='<b>YOU CURRENTLY HAVE NO BOOKS LOADED. <BR>CLICK COPY FROM TO LOAD BOOK SET.</b>'
		document.all.Denglish.children(1).innerHTML=Denglish;
	}
	else  //Queried Book List
	{
		/*<% '//clear the book name%>*/
		document.all.Sbookname.innerText='BOOK      NAME';
		/*<% '//clear the book type field%>*/
		document.frmFields.Stype.value='';
		/*<% '//clear the book publish date field%>*/
		document.frmFields.Swhen.value='';
		/*<% '//display book 0 of 0 %>*/
		document.all.Sbookid.innerText='[0 of 0]';
		/*<% '//clear the book price field%>*/
		document.frmFields.Sprice.value='';
		/*<% '//clear the books year to date sales field%>*/
		document.frmFields.Ssales.value='';
		/*<% '//clear the book id field%>*/
		document.frmFields.Stid.value='';
		/*<% '//set the length of the list box to the number of books%> */
		document.frmFields.Sallbooks.length=0;
		/*<%'//Clear the book notes field%>*/
		var Senglish='<b>YOU CURRENTLY HAVE NO BOOKS LOADED. <BR>CLICK QUERY TO LOAD BOOK SET.</b>'
		document.all.Senglish.children(1).innerHTML=Senglish;
	}
}
/*<%
'//--------------------------setControls()--------------------------
'//
'//	CALLED BY: close_books(),load_books(), initialize()
'//
'//	INPUT:	whichone - 1=Saved Book List   0=Saved Book List
'//			lhow - true=disable controls   false=enable controls
'//				
'//	PURPOSE:  certain controls should be enabled when books exits
'//				and disabled when books do not exist.
'//				enabled buttons are set to blue, disabled buttons are green
'//
'//	OUTPUT:	none
'//				
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000 
'//
'//%>*/
function setControls(whichone,lhow) 
{
if (!lhow) var enabling=true; //lhow=false we are enabling the controls
if (lhow) btnStyle='greenbutton'; //if disabling then set the buttons green
else	btnStyle='bluebutton'; //if enabling then set the buttons blue
	if (whichone==1) //Save Book List
	{
		if (Dnumberofbooks==0) var nobooks=true; //chek to see if there are books
		with (document.frmFields)
		{
			Dallbooks.disabled=lhow; //enable or disable the list box of books
			/*<%'//do not enable these buttons bellow if we have no books (an empty book set)%>*/
			if (!(enabling && nobooks))  
			{
				Ddelete.disabled=lhow;
				Ddelete.className=btnStyle;
				Dcopy.disabled=lhow;
				Dcopy.className=btnStyle;
				Dsave.disabled=lhow;
				Dsave.className=btnStyle;
				Dmovebottom.disabled=lhow;
				Dmovebottom.className=btnStyle;
				Dmovedown.disabled=lhow;
				Dmovedown.className=btnStyle;
				Dmovetop.disabled=lhow;
				Dmovetop.className=btnStyle;
				Dmoveup.disabled=lhow;
				Dmoveup.className=btnStyle;
			}
		}
	}
	else //Queried Book List
	{
		if (Snumberofbooks==0) var nobooks=true;
		with (document.frmFields)
		{
			Sallbooks.disabled=lhow; //enable or disable the list box of books
			/*<%'//do not enable these buttons bellow if we have no books (an empty book set)%>*/
			if (!(enabling && nobooks))  
			{
				Spaste.disabled=lhow;
				Spaste.className=btnStyle;
			}
		}
	}
}
/*<%
'//--------------------------close_books()--------------------------
'//
'//	CALLED BY: load_onclick()
'//
'//	INPUT:	whichone - 1=Saved Book List   0=Saved Book List
'//				
'//	PURPOSE:  when a book list is closed the dom is cleared and no books exist
'//				the form needs to be cleared of the info that remains
'//				controls need to be disabled 
'//
'//	OUTPUT:		Xnumberofbooks = 0
'//				
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000 
'//
'//%>*/
function close_books(whichone) 
{
	if (whichone==1) //Save Book List
	{
		if (needtosave)  save_books();  //dont close if dom has been modified, save first
		clearForm(1); //get rid of the info that is currently displayed 
		setControls(1,true); //disable the controls that should not be used when book list is empty
		Dnumberofbooks=0; //intialize number of books to 0
		with(document.frmFields)
		{
			Dsave.disabled=true;  //disable the save button
			Dsave.className='greenbutton'; //make save button green
		}
	}
	else //Queried book list
	{
		clearForm(0);  //get rid of the info that is currently displayed 
		setControls(0,true); //disable the controls that should not be used when book list is empty
		Snumberofbooks=0; //intialize number of books to 0
	}
}
/*<%
'//--------------------------load_books()--------------------------
'//
'//	CALLED BY: load_onclick()
'//
'//	INPUT:	whichone - 1=Saved Book List   0=Saved Book List
'//				
'//	PURPOSE:  calls the functions to get the book list and load it in the dom
'//				then the controls need to be enabled
'//
'//	OUTPUT:		none
'//				
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000 
'//
'//%>*/
function load_books(whichone)
{
	if (whichone==1) //Save Book List
	{
		get_books(1); //load Saved Book List into dom
		setControls(1,false); //enable controls
	}
	else //Queried Book List
	{
		get_books(0); //load Queried Book List into dom
		setControls(0,false); //enable controls
	}
}
/*<%
'//--------------------------load_onClick()--------------------------
'//
'//	CALLED BY: (Dload, Sload) button onclick
'//
'//	INPUT:	whichone - 1=Saved Book List   0=Saved Book List
'//			caption - the caption of the button when it was clicked
'//				
'//	PURPOSE:  this function is used to toggle the caption of the Xload buttons
'//				between (open and close)(close and open)
'//				depending on the current button caption either the load_books
'//				or close_books function will be called.
'//
'//	OUTPUT:		
'//				
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000  
'//	
'//%>*/
function load_onclick(whichone,caption)
{
	if (whichone==1)  //Saved Book List
	{
		if (caption=="Open My Books") //list currently closed
		{
			document.frmFields.Dload.value="Close My Books";  //change the caption
			load_books(1); //load the books into the dom
		}
		else  //list currently open
		{
			document.frmFields.Dload.value="Open My Books"; //change the caption
			close_books(1); //close the book set 
		}
	}
	else  //Queried Book List
	{
		if (caption=="Query Titles Database") //list currently closed
		{
			document.frmFields.Sload.value="Close Query List";  //change the caption
			load_books(0); //load the books into the dom
		}
		else  //list currently open
		{
			document.frmFields.Sload.value="Query Titles Database"; //change the caption
			close_books(0); //close the book set
		}
	}
}
/*<%
'//--------------------------Spaste_onClick()--------------------------
'//
'//	CALLED BY: Spaste button onclick
'//
'//	INPUT:	no
'//				
'//	PURPOSE:  when the paste button is clicked the currently selected book(s)
'//				from the Queried Book List will be copied one by one to the Saved Book List.
'//				Books will be inserted into the Saved Book List before the currently
'//				selected book.  When done copying the Queried Book List will be hidden.
'//
'//	OUTPUT:  Dnumberofbooks = Dnumberofbooks + number of copied books		
'//				
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000  
'//	
'//%>*/
function Spaste_onclick()
{
	for (i=0;i<Snumberofbooks;i++) //go through each book in the queried book list
	{
		if (document.frmFields.Sallbooks.options[i].selected) //if selected then copy it
		{
			copybook(i); //copy the book
			insertbook(Dbooknumber-1);  //insert the book before the current selected book
			Dnumberofbooks+=1; //increment the number of books in the saved book list
		}
	}
	toggleSource(document.frmFields.Dcopy.value);  //hide the Queried Book List
	display_book(1,Dbooknumber); //display the last 
}
/*<%
'//--------------------------destroy()--------------------------
'//
'//	CALLED BY: body onunload
'//
'//	INPUT:	none
'//				
'//	PURPOSE:  clean up by destroying all of the objects
'//
'//	OUTPUT:		none
'//				
'//	DEVELOPER:	Leon Platt
'//
'//	DATE:    Feb 18, 2000 
'//	
'//%>*/
function destroy()
{
	Dnumberofbooks=null;
	Dbooknumber=null;
	objSallbooks = null;
	objSrull=null;
	objDallbooks = null;
	objDbook=null;
	objRemovedNode=null;
	needtosave=null;
}
//-->
</script>
</head>
<%
' ****************************************************************************
'                                           HTML   BODY                                                                              
' ****************************************************************************
section= "Book Manager"
%>
<table>
	<tr>
	<td  class="companyHeader" align="left">Client Side XML </td>
	</TR>
	<TR>
	<TD class="programHeader" align="left"><%=section%></TD>
	</tr>
</table>
<HR>
<body onload="initialize();" onunload="destroy();">
<form onsubmit="return(Ready_Output());" ID="frmFields" NAME="frmFields" METHOD="post" ACTION="setfilters.asp"> 
<%
' ****************************************************************************
'                        My book Table  (Saved Book List)                                                                             
' ****************************************************************************
%>
<A NAME="D_TABLE"></A>
<table BORDER="1" WIDTH="100%">
<tr>
<th>
<span class="Dbook">
      <TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
        <TR><TH align="left">SAVED BOOK LIST</TH>
		<th ALIGN="middle" >
			<span name="Dbookname" id="Dbookname">BOOK      NAME</span>
		</th>
		<th ALIGN="right">
			<span name="Dbookid" id="Dbookid">[xx of xx]</span>
		</th>
	</TR>
	</TABLE></span>
</th>
</tr>
<tr>
<td class="destbk"><span class="Dbook">
      <INPUT id="Dload" class="bluebutton" type=button value="Open My Books" name="DLoad" LANGUAGE=javascript onclick="return load_onclick(1,this.value)">
      </span>
</td>
</tr>
<tr>
	<td class="destbk" ALIGN="left">
    <span class="Dbook">
      <span class="frmhilite"><BR>Publish Date:</span>
      <INPUT id="Dwhen" size=15 name="Dwhen" readOnly>    
      <span class="frmhilite">Type of Book:</span>
      <INPUT id="Dtype" size=10 name="Dtype" readOnly>     
      <span class="frmhilite">Price:</span>
      <INPUT id="Dprice" size=5 name="Dprice" readOnly>     
      <span class="frmhilite">Year to Date Sales:</span>
      <INPUT id="Dsales" size=5 name="Dsales" readOnly>     
      <span class="frmhilite">Title ID:</span>
      <INPUT id="Dtid" size=5 name="Dtid" readOnly> 
      <TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
        <TR>
          <TD width="75%" valign=top><br><br>
				<span class="frmhilite">This book Description</span><br>
 				<%' Set up a layer for book notes%>
				<div id="Denglish" name="Denglish" onMouseOver="window.status='This field displays an english representation of all entered filter conditions'; return true" onMouseOut="window.status=' '" style="position:absolute; width:100%; height:40mm; top:85mm; left:5mm; background-color:white; font-family:Verdana; font-size:10pt; color:navy; text-align:left; overflow:scroll">
				<br><p><b>
				YOU CURRENTLY HAVE NO BOOKS LOADED. <BR>CLICK COPY FROM TO LOAD BOOK SET.</b></p>
				</div>
         </TD>
          <TD><!--Available books List Box--> 
            <span class="frmhilite">Book Tiltes</span><BR>
			<SELECT class="smfrm" id="Dallbooks" name="Dallbooks" size=13 style="WIDTH: 170px; HEIGHT: 246px" LANGUAGE=javascript onclick="return allbooks_onclick(1,this.selectedIndex)"> 
            <OPTION value=""></OPTION>
            </SELECT>
            </TD>
            <TD align="center">
				 <INPUT id="Dmovetop" type=button value="Top"  name=  "Dmovetop" class=bluebutton onClick="movebook('top')"><BR><BR>
				<INPUT id="Dmoveup" type=button value=" Up " name="Dmoveup" class=bluebutton onClick="movebook('previous')"> 
            <BR><BR>
				<INPUT id="Dmovedown" type=button value="Down" name ="Dmovedown" class=bluebutton onClick="movebook('next')"><BR><BR>
				<INPUT id="Dmovebottom" type=button value="Bottom"  name="Dmovebottom" class=bluebutton onClick="movebook('bottom')"> 
			</TD>  
        </TR>
       </TABLE></span>
	</td>
</tr>
<tr>
<td class="destbk">
	<span class="Dbook">
      <INPUT id="Dtop" type=button value="  |<  " name="Dtop" onclick="vcr(1,'top');">
      <INPUT id="Dprevious" type=button value="   <  " name="Dprevious" onclick="vcr(1,'previous');">
      <INPUT id="Dnext" type=button value="   >  " name="Dnext" onclick="vcr(1,'next');">
      <INPUT id="Dbottom" type=button value="  >|  " name="Dbottom" onclick="vcr(1,'bottom');">  
      <INPUT id="Ddelete" type=button value="Delete a book" name="Ddelete" class=bluebutton LANGUAGE=javascript onclick="return Ddelete_onclick()">  
      <INPUT id="Dsave" type=button value="Save books" name="Dsave" class=redbutton onClick="save_books()">
      <INPUT id="Dcopy" type=button value="Copy From Query" name="Dcopy" disabled class=greenbutton onclick="toggleSource(this.value)">
      </span>
  </td>
</tr>
<table>
<%
' ****************************************************************************
'                         Copy From book Table   (Queried Book List)  
' ****************************************************************************
%>
<table BORDER="1" WIDTH="100%">
<tr>
<th>
<span class="Sbook">
      <TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
        <TR><TH align="left">QUERIED BOOK LIST</TH>
		<th ALIGN="middle" >
			<span name="Sbookname" id="Sbookname">BOOK      NAME</span>
		</th>
		<th ALIGN="right">
			<span name="Sbookid" id="Sbookid">[xx of xx]</span>
		</th>
	</TR>
	</TABLE></span>
</th>
</tr>
<tr>
<td class="sourcebk"><span class="Sbook">
      <INPUT id="Sload" class="bluebutton" type=button value="Query Titles Database" name="SLoad" LANGUAGE=javascript onclick="return load_onclick(0,this.value)">
	</span> 
</td>
</tr>
<tr>
	<td class="sourcebk" ALIGN="left">
	<span class="Sbook">
     <span class="frmhilite"><BR>Publish Date:</span>
     <INPUT id="Swhen" size=15 name="Swhen" readOnly>    
      <span class="frmhilite">Type of Book:</span>
      <INPUT id="Stype" size=10 name="Stype" readOnly>     
      <span class="frmhilite">Price:</span>
      <INPUT  id="Sprice" size=5 name="Sprice" readOnly>     
      <span class="frmhilite">Year to Date Sales:</span>
      <INPUT id="Ssales" size=5 name="Ssales" readOnly>     
      <span class="frmhilite">Title ID:</span>
      <INPUT id="Stid" size=5 name="Stid" readOnly> 
      <TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
        <TR>
          <TD width=75% valign=top><BR><br>
				<span class="frmhilite">This book Description</span><BR>
 				<%' Set up a layer for book notes%>
				<div id="Senglish" name="Senglish" onMouseOver="window.status='This field displays an english representation of all entered filter conditions'; return true" onMouseOut="window.status=' '" style="position:relative; width:100%; height:40mm; top:20; left:0mm; background-color:white; font-family:Verdana; font-size:10pt; color:navy; text-align:left; overflow:scroll">
				<br><p><b>
				YOU CURRENTLY HAVE NO BOOKS LOADED. <BR>CLICK QUERY TO LOAD BOOK SET</b></p>
				</div>
          </TD>
          <TD>
            <span class="frmhilite">Select book to be copied</span><BR>
			<SELECT class="smfrm" id="Sallbooks" name="Sallbooks" size=13 style="WIDTH: 170px; HEIGHT: 246px" LANGUAGE=javascript onclick="return allbooks_onclick(0,this.selectedIndex)"> 
					<OPTION></OPTION>
				</SELECT> 
		</TD></TR>
        </TABLE>
	</span>
  </td>
</tr>
<tr>
<td class="sourcebk">
	<span class="Sbook">
      <INPUT id="Stop" type=button value="  |<  " name="Stop" onclick="vcr(0,'top');">
      <INPUT id="Sprevious" type=button value="   <  " name="Sprevious" onclick="vcr(0,'previous');">
      <INPUT id="Snext" type=button value="   >  " name="Snext" onclick="vcr(0,'next');">
      <INPUT id="Sbottom" type=button value="  >|  " name="Sbottom" onclick="vcr(0,'bottom');">  
      <INPUT id="Spaste" type=button value="Paste To" name="Spaste" class=bluebutton LANGUAGE=javascript onclick="return Spaste_onclick()">
      </span>
</td>
</tr>
</table>
</form>
<A NAME="S_TABLE"></A>
<% 
' ****************************************************************************
' Destroy the BV Control
' ****************************************************************************
set objBV = nothing
%>
</body>
</html>

Listing 2 - ActiveX Control: ADO to XML (WebClass.dll)(xmlControl.cls)

Option Explicit

'Declare Database variables
Private m_dbConnection As New ADODB.Connection
Private m_dbCommand As ADODB.Command
Private m_adoRs As ADODB.Recordset
Private m_adoErrors As ADODB.Errors
Private m_adoErr As Error
Public nCommandTimeOut As Variant
Public nConnectionTimeOut As Variant
Public strConnect As Variant
Public strAppName As String
Public strLogPath As String
Public strDatabase As String
Public strUser As String
Public strPassword As String
Public strServer As String
Public strVersion as String
Public lMSADO As Boolean
'Private Global Variables
Private gnErrNum As Variant
Private gstrErrDesc As Variant
Private gstrErrSrc As Variant
Private gstrDB As String
Private gstrADOError As String
Private Const adLeonNoRecordset As Integer = 129
Private gtableName(6) As String
Private gcolumnName(6) As String
Private gprettyName(6) As String
Private gdatatype(6) As String
Private gfilter(6) As String

Private Function OpenDatabase()

If Len(strConnect) = 0 Then  'set up defaults if they were not passed
    If Len(strDatabase) = 0 Then
        strDatabase = "pubs"
    End If
    
    If nConnectionTimeOut = 0 Then
        nConnectionTimeOut = 600
    End If
    
    If nCommandTimeOut = 0 Then
        nCommandTimeOut = 600
    End If
    
    If Len(strAppName) = 0 Then
        strAppName = "xmlControl"
    End If
    
    If Len(strUser) = 0 Then
        strUser = "sa"
    End If
    
    If Len(strPassword) = 0 Then
        strPassword = ""
    End If
    
        strConnect = "Provider=SQLOLEDB.1; " & _
           "Application Name=" & strAppName & _
           "; Data Source=" & strServer & "; Initial Catalog=" & strDatabase & "; " & _
           " User ID=" & strUser & "; Password=" & strPassword & ";"
End If

 'connect to SQL Server and open the database
On Error GoTo SQLErr  'turn on error handler
With m_dbConnection
    .ConnectionTimeout = nConnectionTimeOut
    .CommandTimeout = nCommandTimeOut
    .Open strConnect  'open the database using the connection string
End With
On Error GoTo 0  'turn off error handler

OpenDatabase = True  'database opened successfully

Exit Function

SQLErr:
Call logerror("OPEN")
OpenDatabase = False

End Function

Private Function BuildSQLwhere(tmpWhere) As String

 'This is for the future

End Function

Public Function GetTitlesXML(Optional xmlWhere As Variant) As String

Dim whereClause As String
Dim strSQL As String

Call OpenDatabase  'open the pubs database

If IsMissing(xmlWhere) Then  'a query was not passed in
    whereClause = ""
Else
    whereClause = BuildSQLwhere(xmlWhere)  'convert the query to valid sql
End If

 'initialize the sql statement that will query for the titles
strSQL = "select title_id,title,type,price,ytd_sales,notes,pubdate from titles " & whereClause

Call NewRecordSet  'create a record set
	 'Set the cursorlocation
    m_adoRs.CursorLocation = adUseClient
     'open the recordset
    m_adoRs.Open strSQL, m_dbConnection, adOpenForwardOnly, adLockReadOnly, adCmdText

 'disconnect the recordset
Set m_adoRs.ActiveConnection = Nothing

On Error GoTo 0  'turn off error handler
    
 'Close the database to free the connection
Call CloseDatabase

If m_adoRs.EOF Then
    GetTitlesXML = ""  'query did not return any titles
Else
    If lMSADO Then
        GetTitlesXML =  msado(m_adoRs)  'convert the recordset to Microsoftado-->xml
    Else
        GetTitlesXML = ADOtoXML(m_adoRs, True)  'convert the ado recordset to custom xml
    End If
End If

 'Close the recordset
Call CloseRecordset
    
Exit Function

SQLErr:
    Call logerror(strSQL)

End Function

Private Function ADOtoXML(tmprs As ADODB.Recordset, tmpMP As Boolean) As String

Dim adoFields As ADODB.Fields 'set up a collectio to hold the fields
Dim adoField As ADODB.Field  'used to retrieve each field from the collection
Dim xmlDoc As msxml2.DOMDocument30
Dim tmpLine As String  'holds the xml representation of each book
Dim tmpXML As String  'used to concatenate each line of xml
Dim i As Integer

If tmprs.EOF Then  'no titles returned by query
    ADOtoXML = ""
    Exit Function
Else
    Set adoFields = tmprs.Fields  'create the collection of fields
End If

tmpXML = "<?xml version=""1.0""?><TITLES>"  'all books will be wrapped in a <TITLES> tag

Do Until tmprs.EOF  'loop through each title in the recordset
    i = 0  ' i is an index to the ado field its initialized to 0 so that first field will be field(0)
    tmpLine = "<BOOK>" & tmprs("title") & vbCrLf
    For Each adoField In adoFields   'loop through all the fields
         'build the xml <FIELD> tag and its attributes for the current field
        tmpLine = tmpLine & "<FIELD " 
        tmpLine = tmpLine & "prettyname=""" & gprettyName(i) & """ "
        tmpLine = tmpLine & "tablename=""" & gtableName(i) & """ gcolumnname=""" & adoField.Name & """ "
        tmpLine = tmpLine & "datatype=""" & gdatatype(i) & """ gfilter="""""
        tmpLine = tmpLine & ">" & adoField.Value
        tmpLine = tmpLine & "</FIELD>" & vbCrLf
        i = i + 1  'point index to next field
    Next
    tmpXML = tmpXML & tmpLine & "</BOOK>" & vbCrLf  'end the book tag after last field
    tmprs.MoveNext  'next title
Loop
Set adoField = Nothing  'destroy the field object
Set adoFields = Nothing  'destroy the fields collection

tmpXML= tmpXML & "</TITLES>" & vbCrLf  'end the string with the ending </TITLES> tag
'at this point I could just return this string back 
'load the xml string into a DOM just toshow the loadxml method
'also this will test to make sure the string is well-formed
Set xmlDoc = New msxml2.DOMDocument30  'create a xmlDOM 
xmlDoc.async = False  'wait for document  to load
xmlDoc.validateOnParse = False  'do not validate  against a schema
xmlDoc.loadXML(tmpXML)  'load the string  into the DOM
On Error Resume Next  'if  the file bellow  does not exist I will get an error  when I try to kill  it
Kill("c:\temp\custom.xml")   'erase the file if it exists 
On Error GoTo 0  'seterror handler to abort on error 
xmlDoc.save ("c:\temp\custom.xml")  'save the xml to a file
ADOtoXML=xmlDoc.xml  'return the xml string
Set xmlDoc=Nothing  'destroy the xml DOM

End Function 

Private Function msado(tmprs As ADODB.Recordset) As String 

Dim xmlDoc As msxml2.DOMDocument30
On Error Resume Next  'if the file bellow does not exist I will get an error when I try to kill it
Kill ("c:\temp\msado.xml")  'erase the file if it  exists
On Error  GoTo 0  'set error handler to abort on error
tmprs.save "c:\temp\msado.xml", adPersistXML  'save the xml to a file
Set xmlDoc = New msxml2.DOMDocument30  'create a xml DOM
xmlDoc.async = False  'wait for document to load
xmlDoc.validateOnParse = False  'do not validate  against a schema
xmlDoc.Load ("C:\temp\msado.xml") 'load the file into the DOM
msado =   xmlDoc.xml  'return the xml string
Set xmlDoc = Nothing   'destroy the xml DOM

End Function 

Private SubCloseRecordset()      
     
 'Close recordset objects and dereference them m_adoRs.Close Set
m_adoRs =Nothing  

End Sub 

Private Sub NewRecordSet() 

Set m_adoRs= Nothing  'Close recordset objects and dereference them
Set m_adoRs=New ADODB.Recordset

End Sub 

Private Sub CloseDatabase()               

 'Close database  objects and dereference them
m_dbConnection.Close
Set m_dbConnection =Nothing 

End Sub 

Private Sub logerror(errSQL As String)        
       
Dim hFile As Integer
Dim expFile As String
 
On Error GoTo 0
gnErrNum = Err.Number 
gstrErrDesc =Err.Description 
gstrErrSrc = Err.Source Set
m_adoErrors = m_dbConnection.Errors 

For Each m_adoErr In m_adoErrors
	gstrADOError =   m_adoErr.Description & "," & CStr(m_adoErr.NativeError)
	_ & "," & CStr(m_adoErr.Number) & "," &
	m_adoErr.Source  _ & "," & CStr(m_adoErr.SQLState)
Next 

hFile =FreeFile
If Len(strLogPath) = 0 Then 
    strLogPath = "C:\temp\" 
End If 
expFile = strLogPath & strAppName & ".err" 
Open expFile For Append As  #hFile
    
Print #hFile,"**********************************" 
Print #hFile, Now() 
Print#hFile, "**********************************" 
Print #hFile,"Subroutine: " & tmpPro
Print #hFile, "Error Number:"  & gnErrNum  
Print#hFile,  "Error Description: "  & gstrErrDesc 
Print #hFile, "Error Source:"  & gstrErrSrc 
Print #hFile, "Ado error String: " & gstrADOError 
Print #hFile, "Bad SQL: " & errSQL 
Close #hFile
End Sub 

 Private Sub Class_Initialize() 
     
 'title_id,title,type,price,ytd_sales,notes,pubdate
strVersion="xmlControl Version 1.1"
gtableName(0) = "titles"
gcolumnName(0) = "title_id"
gprettyName(0) = "Title Identification Number"
gdatatype(0) = "number"
gfilter(0) = ""
gtableName(1) = "titles"
gcolumnName(1) = "title"
gprettyName(1) = "Title of the Book"
gdatatype(1) = "text"
gfilter(1) = ""
gtableName(2) = "titles"
gcolumnName(2) = "type"
gprettyName(2) = "Type of Book"
gdatatype(2) = "text"
gfilter(2) = ""
gtableName(3) = "titles"
gcolumnName(3) = "price"
gprettyName(3) = "Price of the Book"
gdatatype(3) = "number"
gfilter(3) = ""
gtableName(4) = "titles"
gcolumnName(4) = "ytd_sales"
gprettyName(4) = "Year to date sales"
gdatatype(4) = "number"
gfilter(4) = ""
gtableName(5) = "titles"
gcolumnName(5) = "notes"
gprettyName(5) = "Notes about the book"
gdatatype(5) = "memo"
gfilter(5) = ""
gtableName(6) = "titles"
gcolumnName(6) = "pubdate"
gprettyName(6) = "Date Published"
gdatatype(6) = "date"
gfilter(6) = ""

End Sub

Listing 3 - xmlReceive.asp

<%@ language=javascript %>
<%
    Response.Expires = -1000;
    // Load the posted XML Command
    //Command format is <XMLCMD c1="command" p1="parameter" p2="parameter" p3="parameter" ></XMLCMD>
	//var xmlcmd='<?xml version="1.0"?><ENVELOPE><XMLCMD c1="getmybooks" p1="" p2="" p3=""></XMLCMD></ENVELOPE>';
    var doc = Server.CreateObject("Msxml2.DOMDocument");
    doc.load(Request);
	//doc.loadXML(xmlcmd);
	var c1=doc.childNodes.item(1).childNodes.item(0).attributes.item(0).text; //command
    var p1=doc.childNodes.item(1).childNodes.item(0).attributes.item(1).text; //parameter 1
    var p2=doc.childNodes.item(1).childNodes.item(0).attributes.item(2).text; //parameter 2
    var p3=doc.childNodes.item(1).childNodes.item(0).attributes.item(3).text; //parameter 3
    if (doc.childNodes.item(1).childNodes.length==2) //check for passed xml
    var passedXML='<?xml version="1.0"?>'+doc.childNodes.item(1).childNodes.item(1).xml;
    var xmlreturn=''; //initialize return string
    var doc=null;
    var resultsXML=true; //true means results object will load a xml string
						//false means results object will load a xml file
    var sqlServer='LANSBS';  //put your sql servers name Here
    //************* Function getMyBooks ***********************
    if (c1=="getmybooks") 
    { 
		//Response.Write("getmybooks");
		resultsXML=false; //results will load a file
	}
    //************* Function saveBooks ***********************
    if (c1=="savemybooks") 
    { 
		//Response.Write("getmybooks");
		resultsXML=true; //results object will load a xml string
		xmlreturn=passedXML; //save the xml that was passed
	}
	//
    //************* Function gettitlesxml ***********************
    if (c1=="gettitlesxml") 
    { 
		//Response.Write("gettitlesxml");
		resultsXML=true; //results object will load a xml string
	    var objWC= Server.CreateObject("WebClass.xmlControl"); //instantiate vb control
	    objWC.strDataBase=p1; //which database 
	    objWC.strServer=sqlServer; //which sql server
		xmlreturn=objWC.GetTitlesXML(); //vb function to get titles and return as xml string
		var objWC=null; //destroy the vb control
	}
	//
    var result = Server.CreateObject("Msxml2.DOMDocument"); //create the results object
    //Response.Write(resultsXML);
    if (resultsXML) //load a xml string
    {
		result.loadXML(xmlreturn);
		if (c1=="savemybooks") result.save(Server.MapPath(p1)); //save the xml if necessary
	}
	else //load a file 
	{
		result.load(Server.MapPath(p1)); //load the saved book list from a xml file
	}
	//Response.Write('Error code: ' +result.parseError.errorCode);
	//Response.Write('Error description: ' +result.parseError.reason);
	//Response.End;
	if ( result.parseError.errorCode!=0 ) //error parsing document
	{
		xmlreturn='<?xml version="1.0"?><RESULTS error="true">FALSE</RESULTS>'; //return for errors
		result.loadXML(xmlreturn); //load the xml error string into the resutls object
	}
    // Now process the order and build the result document.
    Response.ContentType = "text/xml"; //we are sending xml back
	//Response.Write(result.xml);
	//Response.End;
    result.save(Response); //save the dom to the HTTP Response object
    var result = null;
    %>

Download the source code for this article

Total article views: 16656 | Views in the last 30 days: 4
 
Related Articles
FORUM

Average of current and previous record Field

Average of current and previous record Field

FORUM

how to display previous month data along with current month data

I am using stored procedure to display current month data using query. Now i want to display previou...

FORUM

Changing Format of field for display

Changing Format of field for display

FORUM

Displaying dates in Name and dd-mm-yyyy

Query on Displaying dates

FORUM

SSRS 2005 Display .jpg files based on query result field holding a URL for the file

I want to display external jpg files in the report body, by using a query reference to a table field...

Tags
asp    
basics    
programming    
xml    
 
Contribute

Join the most active online SQL Server Community

SQL knowledge, delivered daily, free:

Email address:  

You make SSC a better place

As a member of SQLServerCentral, you get free access to loads of fresh content: thousands of articles and SQL scripts, a library of free eBooks, a weekly database news roundup, a great Q & A platform… And it’s our huge, buzzing community of SQL Server Professionals that makes it such a success.

Join us!

Steve Jones
Editor, SQLServerCentral.com

Already a member? Jump in:

Email address:   Password:   Remember me: Forgotten your password?
Steve Jones