CLIENT SIDE XML - ASP on steroids !

,

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.text=objDallbooks.childNodes.item(1).childNodes.item(i).selectSingleNode("FIELD[@gcolumnname=
			'title_id']").text;document.frmFields.Dallbooks.options.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.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.text=objDallbooks.childNodes.item(1).childNodes.item(i).selectSingleNode("FIELD[@gcolumnname='title_id']").text;
			document.frmFields.Dallbooks.options.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.text=objSallbooks.childNodes.item(1).childNodes.item(i).selectSingleNode("FIELD[@gcolumnname='title_id']").text;
			document.frmFields.Sallbooks.options.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.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

Rate

2 (1)

Share

Share

Rate

2 (1)