SiteExperts.com Logo Home | Community | Developer's Paradise | Jobs
User Groups | Site Tools | Site Information | Search

Inside Technique : Web Annotator : The Annotation Functions

The Annotation Functions:

The second text command in the contents.htm frame is the "Load Saved Page" command. This loads the newly copied page in the main frame. When we load the new page, the document now contains support for our annotations we want to make.

When we re-constructed the document we added an include file that contains  the code shown below:
var annCount = 0;

function setComment() {

 var e=event.srcElement;

 if(event.srcElement.parentElement.isTextEdit)
   { 
     var retVal = window.showModalDialog("TextEdit.htm");
     var tr=document.body.createTextRange();
     if(retVal!='') 
     
     {
      annCount++;
      tr.moveToPoint(event.x, event.y);
      tr.moveEnd("word",1)
      tr.collapse(false);
      tr.pasteHTML("\n\n<i ID='COMMENT_"+annCount+"' COMMENT='"+retVal+"' CLASS=ANN  ONCLICK='showComment(this.id);' STYLE='Z-INDEX:2;CURSOR:HAND;BACKGROUND-COLOR:LIGHTGREEN;COLOR:RED;HEIGHT:2px;WIDTH:2px'>A</i>\n\n")
      
      if(null==document.all("SAVER"))
       {
         document.body.insertAdjacentHTML("afterbegin","\n>INPUT ID=SAVER TYPE=BUTTON onclick=docSave() VALUE=Save?>\n\n");
       }
     }
   }    
  
}

To add a comment or annotation to a page we invoke the setcomment function when the user clicks on an area of the document. The first line is a global variable that lets the function check a global count for referencing each new comment. This will serve as our ID counter for each element we add as an annotation. The actual id will take this form:  "COMMENT_" annCount.

For this to work out properly, we need to somehow store a number value for a comment and persist it between reloads of the document. If the document was reloaded or refreshed, all scripts would go out of scope and our annCount variable would be set back to 0.

There are several solutions to this. First, we could query each element of the document and look for attributes on each tag that have the COMMENT attribute and increment a count.

Another way, and much easier is to set an attribute in the body tag called COMMENTCOUNT.  We discussed this previously, and this is where it comes into focus. We store the latest comment count to the bodys "COMMENTCOUNT" attribrute everytime the page is saved. 

The next line is the setComment function. The checkComment function sees if the element that was clicked supports text editing. To find out we first get the object that generated the event then call the parent.isTextEdit method to know if the user can add a comment to this section all in one line of code:
            
if(event.srcElement.parentElement.isTextEdit)                       

Once we have decided the section of  the document can be annotated we open up a modal dialog window that accepts text input from a textarea element.


figure 4 Comment Box Dialog
var retVal =             
       window.showModalDialog("TextEdit.htm")  

The variable retVal is the return value from the modal dialog we want to use to add as a comment.
    
if(retVal!='') 
 {
  annCount++;
  tr.moveToPoint(event.x, event.y);
  tr.moveEnd("word",1)
  tr.collapse(false);
  tr.pasteHTML("\n\n<i ID='COMMENT_"+annCount+"' 
   COMMENT='"+retVal+"' CLASS=ANN
   ONCLICK='showComment(this.id);' 
   STYLE='Z-INDEX:2;CURSOR:HAND;
   BACKGROUND-COLOR:LIGHTGREEN;COLOR:RED;
   HEIGHT:2px;WIDTH:2px'>A</i>\n\n")

 if(null==document.all("SAVER")) 
  {
   document.body.insertAdjacentHTML("afterbegin",
   "\n>INPUT ID=SAVER TYPE=BUTTON onclick=docSave() 
   VALUE=Save?>\n\n");
  }
 }

If the return value isnt empty, we get the position of the users mouse click, the text under it, expand the text to a full word, then collapse the text range down to the end of the word. After this is done, we should have an area after the end of the word closest to the click we can put our annotation element in.

The annotation doesnt get added to any text within a word causing the word to break into pieces, rather always at the end of the word.The tr.moveEnd("word",1) method ensures we move directly to the end of the word.

The next lines use the text range object to paste in a bit of html we use to mark up a location with our annotation.
tr.pasteHTML("\n\n<i ID='COMMENT_"+annCount+"' COMMENT='"+retVal+"'"
CLASS=ANN  ONCLICK='showComment(this.id);'
STYLE='Z-INDEX:2;CURSOR:HAND;BACKGROUND-COLOR:LIGHTGREEN;
COLOR:RED;HEIGHT:2px;WIDTH:2px'>A</i>\n\n")

The tr.pasteHTML method already has the location stored for us to place the html at, so adding the html to the location only requires us to pass along the html to the method. The interesting part is the html being added itself.

The html here represents our annotation mark and consists of an <I> tag. Notice in the code above that we are using the annCount for our id with the work "COMMENT_" appended in front. Also, note that we have an attribute called "COMMENT". this is where we store the actual text of the comment the user makes. The modal dialogs return value is used to set the value for "COMMENT".

The onclick handler 'showComment(this.id)' simply calls a function to show a comment made on that element once it's been added to the document and is not shown here. It's interesting to note, that you can bind events dynamically this way without worrying the elements events won't be processed. The rest of the attributes are styles used to color the annotation and set its size. I chose to use the letter A (for annotation) for marking up the document

The last bit of code to discuss is the if statement.
             
if(null==document.all("SAVER"))
  {
   document.body.insertAdjacentHTML("afterbegin",    
   "\nINPUT ID=SAVER TYPE=BUTTON onclick=docSave() 
   VALUE=Save?>\n\n");
  }

The if statement checks for the existence of our "Save?" button. If we find that the document doesnt have one, we add it by inserting it just after the beginning body tag. This allows the user to decide whether or not they want to save the document and comments(if any) to the file system.

Next we use the HTML element <I> tag (for italicizing text) and give it some properties. We position the element inline with the text we are interested in and add our "COMMENT" attribute to it as well as some style properties. We could easily have added a Span or a DIV tag, but because we are only placing elements inline with text using a block style element is not necessary.

Using a DIV or a SPAN is perfectly acceptable of course, but for our purposes we opt for a simpler solution that gives us light weight with the added benefit of italicizing our "A" text between the opening and closing <I> tag without defining additional style properties.

Saving the changes:
There are two ways to store our annotations permanently. The first, is to save the document immediately after a change is made. The second is to show the user a button to press which saves our document.

We opt with letting the user save the document, this way you can always change your mind in case you made some clever remarks you care not to have others see. 

To save the document, we call the updateContent() method on the contents page and pass it the current count of comments as an argument. This argument gets added to the global count we were keeping in the contents page.

Instead of writing a separate function to update the page, we call on the getPageContent function once again and let it run through the process of saving the pages content.

You may want to write a separate function for updating changes because we really no longer need to parse images in our copy nor are we worried about adding event handlers.