CLIENT SIDEXML - 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 THEBOOKS - 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 thisarticle
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 elsebtnStyle='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; %>