git-svn-id: http://code.softwarefreedom.org/svn/stet/trunk@54 55a1384f-cf82-4c22...
[stet:stet.git] / stet-compat.js
1 // Copyright (C) 2005, 2006   Software Freedom Law Center, Inc.
2 // Author: Orion Montoya <orion@mdcclv.com>
3 //
4 // This software gives you freedom; it is licensed to you under version
5 // 3 of the GNU Affero General Public License, along with the
6 // additional permission in the following paragraph.
7 //
8 // This notice constitutes a grant of such permission as is necessary
9 // to combine or link this software, or a modified version of it, with
10 // Request Tracker (RT), published by Jesse Vincent and Best Practical
11 // Solutions, LLC, or a derivative work of RT, and to copy, modify, and
12 // distribute the resulting work.  RT is licensed under version 2 of
13 // the GNU General Public License.
14 // 
15 // This software is distributed WITHOUT ANY WARRANTY, without even the
16 // implied warranties of MERCHANTABILITY and FITNESS FOR A PARTICULAR
17 // PURPOSE.  See the GNU Affero General Public License for further
18 // details.
19 //  
20 // You should have received a copy of the GNU Affero General Public
21 // License, version 3, and the GNU General Public License, version 2,
22 // along with this software.  If not, see <http://www.gnu.org/licenses/>.
23
24 // Based on some work from http://www.quirksmode.org/js/selected.html
25 // That work was copyrighted by Peter-Paul Koch.
26 //  His license is:
27 //   You may copy, tweak, rewrite, sell or lease any code example on this site.
28
29
30 //if (!dump) {
31 //function dump() { }
32 //}
33
34 var ticketObj = new Object;
35 ticketObj.rtidsBySn = new Object;
36 var drafter = '';
37 var filename;
38
39 var base64Chars = new Array(
40 'A','B','C','D','E','F','G','H',
41 'I','J','K','L','M','N','O','P',
42 'Q','R','S','T','U','V','W','X',
43 'Y','Z','a','b','c','d','e','f',
44 'g','h','i','j','k','l','m','n',
45 'o','p','q','r','s','t','u','v',
46 'w','x','y','z','0','1','2','3',
47 '4','5','6','7','8','9','+','/'
48 );
49
50 var reverseBase64Chars = new Array();
51 for (var i=0; i < base64Chars.length; i++){
52   reverseBase64Chars[base64Chars[i]] = i;
53 }
54
55
56 function initPage() {
57   statusbox("Please wait while we load some comments.");
58   filename = location.pathname.substring(location.pathname.lastIndexOf('/')+1,location.pathname.length); 
59   if((!filename.length) || (filename.match(/index/)) || (filename.match(/comments$/))|| (filename.match(/comments\/\?/)) || (filename.match(/comments\/#/)) || (filename.match(/debug/) || (filename.match(/classic/)))) {
60     filename = 'gplv3-draft-1';
61   }
62 if(window.location.search.length) {
63   // loadXMLDoc('/comments/rt/xmlresults.html',window.location.search.substring(1));
64  loadXMLDoc('/comments/rt/xmlresults-compat.html',window.location.search.substring(1));
65 }
66  else {
67    loadXMLDoc('/comments/rt/xmlresults-compat.html','Query=+%27CF.NoteUrl%27+LIKE+%27'+filename+'%27+&RowsPerPage=25&Order=DESC');
68  }
69 }
70
71 // XpathSel adapted from http://www.quirksmode.org/js/selected.html
72 function XpathSel() {
73   //alert("firing XpathSel");
74   if (!readCookie('__ac')) {
75     document.getElementById('login').setAttribute('style','color: red; font-weight: bold; font-size: 150%');
76     return;
77   }
78   var textObj = '';
79   var start = '';
80   var end = '';
81   if (window.getSelection)
82     {
83       textObj = window.getSelection();
84     }
85   else if (document.getSelection)
86     {
87       textObj= document.getSelection();
88     }
89   else if (document.selection)
90     {
91       textObj= document.selection.createRange().text;
92     }
93   //alert(textObj.toString());
94        if(textObj.length < 1) {
95          document.getElementById('selectsome').setAttribute('class','error');
96          document.getElementById('login').innerHTML(textObj.length);
97          return;
98        }
99
100     try { sn = textObj.anchorNode; } catch(e) {
101       alert(e);
102     }
103     if (!sn) {
104        //alert('trying to load /comments/rt/formxml3.html?'+encodeURI(textObj.toString()));
105        loadXMLDoc('/comments/rt/formxml3.html','selection='+encodeURI(textObj.toString()));
106     }
107
108     else {
109       try { startNode = textObj.anchorNode; } catch(e) { }
110       try { startid = startNode.parentNode.id; } catch(e) { }
111       try { start = startNode.parentNode.nodeName; } catch(e) { }
112       endnode = textObj.focusNode.parentNode;
113       endid = endnode.id;
114       end = endnode.nodeName;
115       if (!readCookie('__ac')) {
116         document.getElementById('login').setAttribute('style','color: red; font-weight: bold; font-size: 150%');
117       }
118       else {
119         oStr = getDomPath(startNode);
120         createNoteDiv(startid);
121         textString = textObj.toString();
122         document.getElementById('DomPath').value = oStr;
123         document.getElementById('Selection').value = textString;
124         loadHTMLtoDiv('comment on: <strong>'+textString+'</strong>','SelectionTxt');
125         // document.getElementById('SelectionTxt').innerHTML = 'comment on: <strong>'+textString+'</strong>';
126         document.getElementById('NoteSubj').value = 'Subject/summary [required]';
127         document.getElementById('StartNodeId').value = startid;
128         // document.getElementById('EndNodeId').value = endid;
129         document.getElementById('NoteUrl').value = filename;
130         
131         if(startid != endid) {
132           noteErr('Your selection does not begin and end in the same sentence.  Please cancel and try again with a shorter selection.  If you want to comment on a whole section, please highlight the section title instead');
133         }
134         if((startid =='login') || (startid == undefined) || (textString == undefined) || (textString == '')) {
135           noteErr('You\'ve found a browser-compatibility bug that we\'re trying to fix; we would appreciate an email to stet@gplv3.fsf.org saying exactly what browser you\'re using (though we know about Safari).  For the moment the best way to send your comment would be via email: see http://gplv3.fsf.org/comments/email.html');
136         }
137       }
138     }
139 }
140
141 function noteErr(msg) {
142   document.getElementById('NoteText').value = msg;
143   document.getElementById('submitNote').setAttribute('disabled','disabled');
144   document.getElementById('NoteSubj').setAttribute('disabled','disabled');
145   //document.getElementById('NoteText').setAttribute('disabled','disabled');
146 }
147
148 function processDompath(response) {
149   //  alert("got to processDompath");
150   try { var fail = response.getElementsByTagName("nf")[0].firstChild.data; } catch(e) {}
151   if (fail) { noteErr(fail); }
152   else {
153     createNoteDiv(response.getElementsByTagName("si")[0].firstChild.data);
154     //    document.getElementById('noteify').setAttribute('action','/comments/rt/simpleform.html');
155     document.getElementById('DomPath').value = response.getElementsByTagName("dp")[0].firstChild.data;
156     document.getElementById('Selection').value = response.getElementsByTagName("sl")[0].firstChild.data;
157     loadHTMLtoDiv('comment on: <strong>'+response.getElementsByTagName("sl")[0].firstChild.data+'</strong>','SelectionTxt');
158     // document.getElementById('SelectionTxt').innerHTML = 'comment on: <strong>'+textString+'</strong>';
159     document.getElementById('NoteSubj').value = 'Subject/summary [required]';
160     document.getElementById('StartNodeId').value = response.getElementsByTagName("si")[0].firstChild.data;
161     //    document.getElementById('NoteText').value = "dp: "+document.getElementById('DomPath').value+"\nsl: "+document.getElementById('Selection').value+"\nsi: "+document.getElementById('StartNodeId').value;
162     // document.getElementById('EndNodeId').value = endid;
163     document.getElementById('NoteUrl').value = response.getElementsByTagName("fn")[0].firstChild.data;;
164   }
165 }
166
167 function getDomPath(startNode) {
168   // helped here by http://www.howtocreate.co.uk/emails/FlorianSauvin.html
169   for (var oStr='' ; startNode && startNode.nodeName != '#document'; startNode=startNode.parentNode) { 
170     if (startNode.nodeName!='#text') { 
171       oStr = startNode.nodeName 
172         + (startNode.id ? ('[id='+startNode.id+']') : '')
173         + (oStr ? ('/'+oStr) : '');
174     }
175   }
176   return oStr;
177 }
178
179 function submitComment() {
180   
181   if ((document.getElementById('NoteSubj').value == 'Subject/summary [required]') || (document.getElementById('NoteSubj').value == ' ')) {
182     loadHTMLtoDiv('<span class="error">Please enter a brief explanatory subject for this comment</span>','SelectionTxt');
183   }
184   else {
185   
186   var form = document.getElementById('noteify');
187   var noteText = form.NoteText.value;
188   loadHTMLtoDiv('Ok, submitting...','SelectionTxt');
189   form.NoteText.style.background = '#aaa';
190   form.NoteSubj.style.background = '#aaa';
191   document.getElementById('submitNote').setAttribute('disabled','disabled');
192   document.getElementById('cancel').setAttribute('disabled','disabled');
193   form.style.background = '#aaa';
194
195   var params = encodeURI('DomPath='+form.DomPath.value+'&amp;Selection='+encodeURIComponent(form.Selection.value)+'&amp;NoteSubj='+encodeURIComponent(form.NoteSubj.value)+'&amp;NoteText='+encodeURIComponent(noteText)+'&amp;StartNodeId='+form.StartNodeId.value+'&amp;NoteUrl='+location.href+'&map;queue='+form.queue.value);
196
197   var theUrl = '/comments/rt/submitcomment.html'; 
198   //highlightWord(form.StartNode.value,form.Selection.value,noteText,'');   
199   loadXMLDoc(theUrl,params);
200   }
201 }
202
203 function getElementsByClass(needle)
204 {
205   var         my_array = document.getElementsByTagName('*');
206   var         retvalue = new Array();
207   var        i;
208   var        j;
209   for (i = 0, j = 0; i < my_array.length; i++)
210     {
211       var c = " " + my_array[i].className + " ";
212       if (c.indexOf(" " + needle + " ") != -1) {
213         retvalue[j++] = my_array[i];
214       }
215     }
216   return retvalue;
217 }
218
219
220 // help from http://www.xml.com/pub/a/2005/02/09/xml-http-request.html
221 var req;
222 function loadXMLDoc(url,theData) 
223 {
224   //dump('loading '+url+'?'+theData+'\n');
225   //alert('loading '+url+'?'+theData+'\n');
226   // branch for native XMLHttpRequest object
227   if (window.XMLHttpRequest) {
228     req = new XMLHttpRequest();
229   } else if (window.ActiveXObject) {
230     req = new ActiveXObject('Microsoft.XMLHTTP');
231   }
232   req.onreadystatechange = processReqChange;
233   
234   req.open("POST", url, true);
235   //  req.open("GET", url, false);
236   req.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
237   req.setRequestHeader('X-Referer',document.location);
238   //  theData = encodeURI(theData);
239   req.send(theData);
240 }
241
242 function loadHTMLtoDiv(text,targetid,caller) {
243   // this won't work in Safari so we need a Safari case:
244   if(targetid) {
245     //  dump('targetid is '+targetid);
246   //  if(caller) { dump(', caller is '+caller);}
247     //  dump('\n');
248   if (myElement = document.getElementById(targetid)) {
249         //      dump(e);
250         try {   
251           myElement.innerHTML = text;
252         }
253         catch(e) {
254           var newDiv = document.createElement('span');
255           newDiv.innerHTML = text;
256           myElement.appendChild(newDiv);
257         }
258   }
259   }
260 }
261
262 function glueXMLTogether(xmlToAdd, parentToAddItTo) {
263   childrenOfParent = parentToAddItTo.childNodes;
264   while (childrenOfParent.length > 0) {
265     parentToAddItTo.removeChild(childrenOfParent.item(0));
266   }
267   parentToAddItTo.appendChild(xmlToAdd);
268 }
269
270 // thanks to http://www.mercurytide.com/knowledge/white-papers/issues-working-with-ajax
271 function getXMLNodeSerialisation(xmlNode) {
272   //  alert(xmlNode);
273   var text = false;
274   try {
275     // Gecko-based browsers, Safari, Opera.
276     //    var serializer = new XMLSerializer();
277     text = serializer.serializeToString(xmlNode);
278   }
279   catch (e) {
280     try {
281       // Internet Explorer.
282       text = xmlNode.xml;
283     }
284     catch (e) {
285       text = kSerialize(xmlNode);
286       //      alert("xmlnode.xml failed: serialized to "+text);
287       
288     }
289   }
290   if (text == undefined) {
291     text = kSerialize(xmlNode);
292     //    alert("undefined serialized to "+text);
293   }
294   //  alert("serialized to "+text);
295   return text;
296 }
297
298 function loadURLtoDiv(url,args,targetid) {
299   // branch for native XMLHttpRequest object
300   if (window.XMLHttpRequest) {
301     req = new XMLHttpRequest();
302   } else if (window.ActiveXObject) {
303     req = new ActiveXObject('Microsoft.XMLHTTP');
304   }
305   req.open('GET',url,false);
306   req.send(args);
307   loadHTMLtoDiv(req.responseText,targetid,"loadurltodiv");
308 }
309
310 function processReqChange() 
311 {
312   var did = 0;
313   //  alert("doing PRC");
314   // only if req shows "complete"
315   if (req.readyState == 4) {
316     // only if "OK"
317     if (req.status == 200) {
318       cancelNote("noteify");
319       //alert("response: "+req.responseText);
320       response  = req.responseXML.documentElement;
321       var resp_arrN;
322       var resp_arrA;
323       var resp_arrF;
324       var resp_arrS;
325         try {
326               drafter = response.getElementsByTagName("d")[0].firstChild.data;
327         }
328         catch (e) {}
329         //alert("about to call cstatusbox");
330         try {
331           cs = response.getElementsByTagName("cs");
332           if (cs.length > 0) {
333             cstatusbox(cs[0]);
334           }
335           } catch(e) {}
336
337
338       resp_arrN = response.getElementsByTagName("annotation");
339       if(resp_arrN.length > 0) {
340         //dump("processAnnot starting from "+resp_arrN[0].firstChild.data+"\n")
341         processAnnotation(response);
342         did++;
343       }
344 /*       else if (resp_arrF = response.getElementsByTagName("form")) { */
345 /*      dump("pre processNQ\n") */
346 /*        if(resp_arrF.length > 0) { */
347 /*          dump("processNQ starting from "+resp_arrF[0].firstChild.data+"\n") */
348 /*            processNewQuery(response); */
349 /*        } */
350 /*       } */
351
352       resp_arrA = response.getElementsByTagName("agreement");
353       if(resp_arrA.length > 0) {
354         //dump("pre processAgree from"+resp_arrA[0].firstChild.data+"\n");
355         //dump("processAgree\n");
356         processAgreement(response);
357         did++;
358       }
359
360       resp_arrS = response.getElementsByTagName("sdp");
361     if(resp_arrS.length > 0) {
362       //dump("pre processDompath from"+resp_arrA[0].firstChild.data+"\n");
363       //dump("processDompath\n");
364       processDompath(response);
365       did++;
366     }
367     
368     if (!did) {
369         statusbox("Your search didn't return anything usable: maybe there are no comments:  <a href=\"http://gplv3.fsf.org/comments/rt/changeshown.html\">search again</a>\n\n<br/>HTTP Status: " + req.statusText+""); //debug
370       }
371     }
372   }
373   //    else { dump("readyState was "+req.readyState+"\n"); } // debug
374 }
375   
376 function onNoteifyMouseover() {
377   //this.onblur = onNoteifyBlur;
378   
379 }
380
381
382 function processAnnotation(response) {
383   selections_arr = response.getElementsByTagName("s");
384   // dump(selections_arr + ' ' + selections_arr.length + ' ' + selections_arr.item(0)+"\n");
385   startnodes_arr = response.getElementsByTagName("i");
386   annotations_arr = response.getElementsByTagName("n");
387   rtids_arr = response.getElementsByTagName("id");
388   users_arr = response.getElementsByTagName("u");
389   uagr_arr = response.getElementsByTagName("ua");
390   queues_arr = response.getElementsByTagName("qn");
391   agrtot_arr = response.getElementsByTagName("at");
392
393         for (prI = 0; prI < selections_arr.length; prI++) {
394           if (selections_arr[prI].firstChild) {
395             noteSelection = selections_arr[prI].firstChild.data;
396             
397             rtids_arr[prI].firstChild.data ? rtid = rtids_arr[prI].firstChild.data : rtid = "error";
398             //alert("calling gXNS at 350");
399             
400             try { var tooltipString = getXMLNodeSerialisation(annotations_arr[prI]); } catch(e) { alert(e); }
401             if (!tooltipString) { var tooltipString = annotations_arr[prI].firstChild.data; }
402             //      alert("got here"+tooltipString+" "+annotations_arr[prI].firstChild.data);
403             tooltipString.length > 165 ? tooltipSubString = tooltipString.substr(0,199) + '...' : tooltipSubString = tooltipString;
404
405
406             
407             ticketObj[rtid] = new Object;
408
409             ticketObj[rtid].link = makeLinkObj(rtid, uagr_arr[prI], agrtot_arr[prI].firstChild.data);
410
411             ticketObj[rtid].full = tooltipString;
412             //alert("setting "+rtid+" excerpt to "+tooltipSubString);
413             ticketObj[rtid].excerpt = tooltipSubString;
414             ticketObj[rtid].user = users_arr[prI].firstChild ? users_arr[prI].firstChild.data : "";
415             ticketObj[rtid].startnode = startnodes_arr[prI].firstChild.data;
416             ticketObj[rtid].queue = queues_arr[prI].firstChild.data;
417
418
419             if(!ticketObj.rtidsBySn[ticketObj[rtid].startnode]) {
420               ticketObj.rtidsBySn[ticketObj[rtid].startnode] = new Array(rtid);
421             }
422             else {
423               ticketObj.rtidsBySn[ticketObj[rtid].startnode].push(rtid);
424             }
425             
426             //alert("calling gXNS at 376");
427             ticketObj[rtid].ua = getXMLNodeSerialisation(uagr_arr[prI]);
428             startNode = startnodes_arr[prI].firstChild.data;
429             startNode = startNode.replace(/note\.[0-9]+\./,'');
430             annoteText = annotations_arr[prI];
431             rtid = rtids_arr[prI] ? rtids_arr[prI].firstChild.data : "";
432             user = users_arr[prI].firstChild ? users_arr[prI].firstChild.data : "";
433             //          dump(users_arr[prI].firstChild.data);
434             //alert("startNode is '"+startNode+"'");
435             highlightWord(startNode,noteSelection,annoteText,rtid,user);
436           }
437           unOverlap("annotation",5);
438         }
439 }
440
441 function makeLinkObj(rtid,agreement,agrtot) {
442     returnLink = rtid ? '<a href="/comments/rt/readsay.html?id='+rtid+'">read/say more</a> ' : '[problem with ticket link]';
443     
444     //alert("calling gXNS at 395");
445           agreestr = agreement.firstChild.data;
446           //alert("agreestr is'"+agreestr+"'");
447           if ((agreestr == "agree") || (agreestr == "unagree")) {
448             returnLink += '<a id="agree'+rtid+'" href="javascript:iAgree('+rtid+',\''+agreestr+'\')">'+agreestr+'</a> | ';
449           }
450           else {
451             returnLink += '<span id="agree'+rtid+'">'+agreestr+'</span> | ';
452           }
453           if (agrtot) {
454             returnLink += ' [<span id="agrtot'+rtid+'">'+agrtot+'</span> agree]';
455           }
456           return returnLink;
457 }
458
459 function processAgreement(response) {
460 // dump("got an agreement\n");
461         myId = response.getElementsByTagName("id")[0].firstChild.data;
462         myOpn = response.getElementsByTagName("b")[0].firstChild.data;
463 // dump('id is '+rtids_arr[0].firstChild.data);
464         myLink = document.getElementById('agree'+myId);
465         opOpn = document.createElement('span');
466         myOpn == "agree" ? opOpn.appendChild(document.createTextNode("unagree")) : opOpn.appendChild(document.createTextNode("agree"));
467         //loadHTMLtoDiv(opOpn.firstChild.data,'agree'+myId,"processagreement");
468         var agrcount = response.getElementsByTagName("ct")[0].firstChild.data;
469         //alert("agrcount: "+agrcount);
470         loadHTMLtoDiv(agrcount,'agrtot'+myId);
471         ticketObj[myId].link = makeLinkObj(myId,opOpn,agrcount);
472         showFull(myId);
473 //      myLink.innerHTML = opOpn;
474         myLink.href = 'javascript:iAgree('+myId+',\''+opOpn+'\')';
475       }
476
477   
478 function onNoteifyBlur() {
479 //      noteNode = document.getElementById('noteify');
480 //      dump(noteNode);
481         submitComment();
482 }
483
484 function checkKeyPressed(keyEvent) {
485   if (!document.getElementById("noteify")) {
486           keyEvent = (keyEvent) ? keyEvent : (window.event) ? event : null;
487           if (keyEvent) {
488                 var charCode = (keyEvent.charCode) ? keyEvent.charCode :
489                                            ((keyEvent.keyCode) ? keyEvent.keyCode :
490                                            ((keyEvent.which) ? keyEvent.which : 0));
491                 //dump(charCode);
492                 if (charCode == 99) { XpathSel(); }
493         
494           }
495   }
496 }
497
498 function cancelNote(div) {
499   if (cancelme = document.getElementById(div)) {
500     cancelme.parentNode.removeChild(cancelme);
501   }
502 }
503
504 function createNoteDiv(startid) {
505
506   // this would be a lot more readable and maintainable if it were
507   // just a string.
508
509 var noteifyDiv = document.createElement('form');
510 noteifyDiv.setAttribute('action','/comments/rt/submitcomment.html');
511 noteifyDiv.setAttribute('class','noteify');
512 noteifyDiv.setAttribute('id','noteify');
513 noteifyDiv.setAttribute('name','noteify');
514 noteifyDiv.setAttribute('onblur','onNoteifyBlur()');
515 // noteifyDiv.style.z-index = '100';
516
517 var DomPathTxt = document.createElement('span');
518 DomPathTxt.setAttribute('id','DomPathTxt');
519 DomPathTxt.setAttribute('name','DomPathTxt');
520
521 var DomPath = document.createElement('input');
522 DomPath.setAttribute('id','DomPath');
523 DomPath.setAttribute('name','DomPath');
524 DomPath.setAttribute('type','hidden');
525 DomPath.setAttribute('class','addnote');
526
527 var SelectionTxt = document.createElement('span');
528 SelectionTxt.setAttribute('id','SelectionTxt');
529 SelectionTxt.setAttribute('name','SelectionTxt');
530
531 var Selection = document.createElement('input');
532 Selection.setAttribute('id','Selection');
533 Selection.setAttribute('name','Selection');
534 Selection.setAttribute('type','hidden');
535
536 var NoteSubj = document.createElement('input');
537 NoteSubj.setAttribute('id','NoteSubj');
538 NoteSubj.setAttribute('name','NoteSubj');
539 NoteSubj.setAttribute('type','text');
540 NoteSubj.setAttribute('size','40');
541 NoteSubj.setAttribute('onfocus','if(this.value=="Subject/summary [required]") {this.value="";}');
542 NoteSubj.setAttribute('onblur','if(this.value==""){this.value="Subject/summary [required]";}');
543 NoteSubj.setAttribute('class','addnote');
544
545 var NoteText = document.createElement('textarea');
546 NoteText.setAttribute('id','NoteText');
547 NoteText.setAttribute('name','NoteText');
548 NoteText.setAttribute('rows','10');
549 NoteText.setAttribute('cols','40');
550 NoteText.setAttribute('class','addnote');
551
552 var StartNodeId = document.createElement('input');
553 StartNodeId.setAttribute('id','StartNodeId');
554 StartNodeId.setAttribute('name','StartNodeId');
555 StartNodeId.setAttribute('type','hidden');
556
557 var NoteUrl = document.createElement('input');
558 NoteUrl.setAttribute('id','NoteUrl');
559 NoteUrl.setAttribute('name','NoteUrl');
560 NoteUrl.setAttribute('type','hidden');
561
562 var cancel = document.createElement('input');
563 cancel.setAttribute('type','button');
564 cancel.setAttribute('id','cancel');
565 cancel.setAttribute('name','cancel');
566 cancel.setAttribute('value','cancel');
567 cancel.setAttribute('onClick','cancelNote("noteify")');
568 cancel.setAttribute('class','annoteButton');
569 cancel.setAttribute('class','addnote');
570
571 var theBR = document.createElement('br');
572 var theBR1 = document.createElement('br');
573 var theBR2 = document.createElement('br');
574 var theBR3 = document.createElement('br');
575 var theBR4 = document.createElement('br');
576
577 var pickQueue;
578 if (drafter) {
579 pickQueue = document.createElement('select');
580 pickQueue.setAttribute('name','queue');
581 pickQueue.setAttribute('id','queue');
582
583 var optDrafter = document.createElement('option');
584 optDrafter.setAttribute('value','Drafter');
585 optDrafter.setAttribute('selected','selected');
586 optDrafter.appendChild(document.createTextNode('Drafter'));
587
588 var optInbox = document.createElement('option');
589 optInbox.setAttribute('value','Inbox');
590 optInbox.appendChild(document.createTextNode('Inbox'));
591
592 var optIssues = document.createElement('option');
593 optIssues.setAttribute('value','Issues');
594 optIssues.appendChild(document.createTextNode('Issues'));
595
596 pickQueue.appendChild(optDrafter);
597 pickQueue.appendChild(optInbox);
598 pickQueue.appendChild(optIssues);
599 }
600 else {
601 pickQueue = document.createElement('input');
602 pickQueue.setAttribute('type','hidden');
603 pickQueue.setAttribute('name','queue');
604 pickQueue.setAttribute('value','Inbox');
605 }
606 var submit = document.createElement('input');
607 submit.setAttribute('type','button');
608 submit.setAttribute('id','submitNote');
609 submit.setAttribute('value','submit');
610 submit.setAttribute('onClick','submitComment()');
611 submit.setAttribute('class','annoteButton');
612 submit.setAttribute('class','addnote');
613
614 noteifyDiv.appendChild(DomPathTxt);
615 noteifyDiv.appendChild(DomPath);
616 noteifyDiv.appendChild(SelectionTxt);
617 noteifyDiv.appendChild(theBR1);
618 noteifyDiv.appendChild(Selection);
619 noteifyDiv.appendChild(NoteSubj);
620 noteifyDiv.appendChild(theBR3);
621 noteifyDiv.appendChild(NoteText);
622 noteifyDiv.appendChild(StartNodeId);
623 //noteifyDiv.appendChild(EndNodeId);
624 noteifyDiv.appendChild(NoteUrl);
625 //noteifyDiv.appendChild(DocRevision);
626 noteifyDiv.appendChild(theBR4);
627 noteifyDiv.appendChild(submit);
628 noteifyDiv.appendChild(cancel);
629
630 if (drafter) {
631 pickQueue && noteifyDiv.appendChild(document.createTextNode(' Queue: ')) && noteifyDiv.appendChild(pickQueue);
632 }
633 else {
634 pickQueue && noteifyDiv.appendChild(pickQueue);
635 }
636
637  thisNode = document.getElementById(startid);
638  
639  if ((thisNode.previousSibling) && (thisNode.previousSibling.previousSibling) && (thisNode.previousSibling.previousSibling.appendChild)) {
640    thisNode.previousSibling.previousSibling.appendChild(noteifyDiv);
641  }
642  else 
643    if ((thisNode.previousSibling) && (thisNode.previousSibling.appendChild)) {
644      thisNode.previousSibling.appendChild(noteifyDiv);
645    }
646    else {
647      thisNode.appendChild(noteifyDiv);
648    }
649 }
650
651 function highlightWord(nodeId,word,tooltip,rtid,user) {
652     var node = document.getElementById(''+nodeId+'');
653     //alert("entering hlw with "+nodeId+":"+node+","+word+","+tooltip.firstChild.data+","+rtid+","+user);
654
655 /* this code is basically no longer descended from: */
656 /* http://www.kryogenix.org/code/browser/searchhi/  */
657 /* but that got me started ...                      */
658 // it's also too rickety and will be replaced by intense_annot as soon
659 // as that is a little less buggy.
660 // but so far the only thing worse than this function, is everything I've
661 // tried to replace it with.
662 //if (node) { dump("my node is "+node.id+"\n"); }
663   var haveHighlighted = false;
664   if ((node) && (node.hasChildNodes)) {
665         paragraph = node;
666         //      try { alert(node.children[0].firstChild.data); } catch(e) {}
667         //alert("calling gXNS at 628");
668         var paragraphString = getXMLNodeSerialisation(paragraph);
669         if (!paragraphString) {var paragraphString = node.firstChild.data; }
670         if (!paragraphString) {var paragraphString = node.firstChild.firstChild.data; }
671         paragraphString.replace(/\s+/gm,' ');
672         //alert("pString is "+paragraphString);
673         tempWordVal = word;
674         tempWordVal = tempWordVal.replace(/\)/g,"\\)");
675         tempWordVal = tempWordVal.replace(/\(/g,"\\(");
676         tempWordVal = tempWordVal.replace(/\b\W+\b/g,'\\W[^<>]*(?:<[^<]+>)*');
677         //tempWordVal = tempWordVal.replace(/^\\W/,'');
678         //tempWordVal = tempWordVal.replace(/\\W$/,'');
679         //tempWordVal = tempWordVal.replace(/\\W[<>^*()+\]]+$/,'');
680         tempWordVal = '('+tempWordVal+')(.*)';
681         //alert('t: '+tempWordVal);
682         try { var re = new RegExp(tempWordVal, 'm'); } catch (e) {};
683         //      dump("re is "+re+"\n");
684         if (re) {
685
686 paragraphString = paragraphString.replace(re,'<span class="highlight '+rtid+' '+ticketObj[rtid].queue+'" id="'+node.id+'">'+"$1"+'</span>'+"$2"+'\
687 <span class="annotation '+rtid+' '+ticketObj[rtid].queue+'" id="rt'+rtid+'" onmousedown="dragStart(event,\'rt'+rtid+'\')" onclick="showFull(\''+rtid+'\')" onmouseover="notemouseover('+rtid+')" onmouseout="notemouseout('+rtid+')">\
688   <span style="font-size: smaller;font-style: italic" id="rt'+rtid+'user">'+ticketObj[rtid].user+'</span>: \
689   <span id="rt'+rtid+'txt">'+ticketObj[rtid].excerpt+'\
690   <a href="javascript:showFull(\''+rtid+'\')">[+]</a></span>\
691 </span>');
692 //dump("replaced on "+$&+"\n");
693 //alert("pString is now "+paragraphString+"\n\n");
694  loadHTMLtoDiv(paragraphString,node.id,"highlightword 742"); 
695 }
696     }
697   }
698
699 function getElementStyle(elem, IEStyleProp, CSSStyleProp) {
700   //  var elem = document.getElementById(elemID); // we're passing the element instead of its id
701   if (elem.currentStyle) {
702     if (elem.currentStyle[IEStyleProp] != '') {
703       return elem.currentStyle[IEStyleProp];
704     }
705   } else if (window.getComputedStyle) {
706     var compStyle = window.getComputedStyle(elem, "");
707     if (compStyle != '') {
708       return compStyle.getPropertyValue(CSSStyleProp);
709     }
710   }
711   // default to going back to yellow -- must sync with stet.css, augh, FIXME
712   if (CSSStyleProp == 'border') {
713     return '#FFFF00';
714   }
715   if (CSSStyleProp == 'background') {
716     return '#f0ecb3';
717   }
718   return "";
719 }
720   
721 function notemouseover(rtid) {
722   var noteArr = getElementsByClass(rtid);
723   for (i = 0; i<noteArr.length; i++) {
724         noteArr[i].style.prevBG=getElementStyle(noteArr[i],'background','background');
725         noteArr[i].style.prevBRD=getElementStyle(noteArr[i],'border','border');
726         //dump('s prevBG '+noteArr[i].style.prevBG+'\n');
727         //dump('s prevBRD '+noteArr[i].style.prevBRD+'\n');
728     noteArr[i].style.background="#f9f";
729     noteArr[i].style.border="1px solid #f0f";
730
731   }
732 }
733 function notemouseout(rtid) {
734   var noteArr = getElementsByClass(rtid);
735   for (i = 0; i<noteArr.length; i++) {
736     //dump('r prevBG '+noteArr[i].style.prevBG+'\n');
737     //dump('r prevBRD '+noteArr[i].style.prevBRD+'\n');
738     noteArr[i].style.background=noteArr[i].style.prevBG;
739     noteArr[i].style.border=noteArr[i].style.prevBRD;
740   }
741 }
742
743
744 function showFull (rtid) {
745   myCollapsed = document.getElementById('rt'+rtid+'txt');
746   loadHTMLtoDiv(ticketObj[rtid].full+' '+ticketObj[rtid].link+' <a href="javascript:showExcerpt(\''+rtid+'\')">[-]</a>',myCollapsed.id);
747   myCollapsed.parentNode.setAttribute('onclick','');
748   unOverlap("annotation",5);
749 }
750
751 function showExcerpt (rtid) {
752   myFull = document.getElementById('rt'+rtid+'txt');
753   loadHTMLtoDiv(ticketObj[rtid].excerpt+' <a href="javascript:showFull(\''+rtid+'\')">[+]</a>',myFull.id);
754   myFull.parentNode.setAttribute('onclick','showFull(\''+rtid+'\')');
755   unOverlap("annotation",5);
756 }
757
758 function iAgree (rtid,opn) {
759   myAgr = document.getElementById('agree'+rtid);
760   myAgr.setAttribute('href','#');
761   loadHTMLtoDiv(myAgr.innerHTML+'ing...',myAgr.id);
762   loadXMLDoc('/comments/rt/agree.html','rtid='+rtid+'&amp;opn='+opn);
763 }
764
765 function unOverlap(cls,gapDesired) {
766 items = getElementsByClass(cls);
767
768  for (i = 0; i< (items.length - 1); i++) {
769    iheight =parseInt(document.defaultView.getComputedStyle(items[i], '').getPropertyValue("height"));
770    ibottom = parseInt(parseInt(items[i].offsetTop) + parseInt(iheight));
771    items[i+1].style.top = items[i+1].offsetTop+"px";
772    diff = parseInt(ibottom) - parseInt(items[i+1].style.top);
773    if (diff >= 0) {
774      items[i+1].style.top = parseInt(parseInt(items[i+1].style.top)+parseInt(diff)+parseInt(gapDesired))+"px";
775    }
776  }
777 }
778
779 function last(obj) {
780   return(this[obj][this.msg.length-1]);
781 }
782
783 function statusbox(text) {
784   loadHTMLtoDiv(text,'statustext');
785   loginbox();
786 }
787 function loginbox() {
788   if((!name) && (readCookie('__ac'))) {
789         namepass = decodeBase64(readCookie('__ac'));
790         var name = namepass.substr(0,namepass.indexOf(':'));
791         loadHTMLtoDiv('you are '+name+': <a href="http://gplv3.fsf.org/logout">logout</a> <a href="http://gplv3.fsf.org/comments/stet-2006-01-20.tar.gz">source</a><br/><span id="selectsome" class="selectsome">select some text</span> and <a href="#" onmousedown="javascript:XpathSel()">add a comment</a> | <a href="http://gplv3.fsf.org/comments/email.html">email your comment</a>','login');
792   }
793   else {
794     loadHTMLtoDiv('You need to <a href=\"http://gplv3.fsf.org/login_form?came_from='+location.pathname+'\">log in</a> to make comments.','login');
795   }
796 }
797 function idLinks(cs) {
798   //alert("entering idlinks");
799   try { var say = cs.getElementsByTagName('say')[0].firstChild; } catch(e) { }
800     var id = cs.getElementsByTagName('ci')[0].firstChild; 
801     var list = cs.getElementsByTagName('l')[0].firstChild;
802     var newMe = document.createElement('span');
803     //try { 
804     newMe.appendChild(document.createTextNode(say.data+' '));
805     //} catch(e) { alert("say "+say+", saydata "+say.data+", e "+e); }
806     //alert("say "+say+", saydata "+say.data);
807     newMe.appendChild(document.createTextNode(id.data+' '));
808     newMe.appendChild(document.createTextNode(' '));
809     Lnk = document.createElement('a');
810     Lnk.setAttribute('href',list.data);
811     Lnk.appendChild(document.createTextNode('[see thread]'));
812     newMe.appendChild(Lnk);
813     newMe.appendChild(document.createTextNode(' '));
814     sLnk = document.createElement('a');
815     sLnk.setAttribute('href','http://gplv3.fsf.org/comments/rt/changeshown.html?came_from=gplv3-draft-1');
816     sLnk.appendChild(document.createTextNode('search'));
817     newMe.appendChild(sLnk);
818
819     st = document.getElementById('statustext');
820     ch = st.childNodes;
821     while (ch.length > 0) {
822         st.removeChild(ch.item(0));
823     }
824     st.appendChild(newMe);
825 }
826 function qLinks(cs) {
827     var say = cs.getElementsByTagName('say')[0].firstChild;
828     var q = cs.getElementsByTagName('q')[0].firstChild; 
829     var r = cs.getElementsByTagName('r')[0].firstChild; 
830     var list = cs.getElementsByTagName('l')[0].firstChild;
831     var ra = cs.getElementsByTagName('ra')[0].firstChild; 
832     var t =  cs.getElementsByTagName('t')[0].firstChild; 
833     var rng =  cs.getElementsByTagName('rng')[0].firstChild; 
834     //    var newMe = document.createElement('span');
835     var newMe = document.createElement('span');
836     //    alert(newMe);
837     var pr;
838     var nx;
839     try { pr = cs.getElementsByTagName('pr')[0].firstChild; } catch(e) {}
840     try { nx = cs.getElementsByTagName('nx')[0].firstChild; } catch(e) {}
841     //try { 
842     newMe.appendChild(document.createTextNode(say.data+' ')); 
843 //} catch(e) { alert("say "+say+", saydata "+say.data+", e "+e); }
844     //try {
845     newMe.appendChild(document.createTextNode(q.data)); 
846     //} catch(e) { alert("q "+q+", qdata "+q.data+", e "+e); }
847     newMe.appendChild(document.createTextNode(' '));
848     rLnk = document.createElement('a');
849     rLnk.setAttribute('href',r.data);
850     rLnk.appendChild(document.createTextNode('[rss]'));
851     try { newMe.appendChild(rLnk); } catch(e) {alert(e);}
852     newMe.appendChild(document.createTextNode(' '));
853     lLnk = document.createElement('a');
854     lLnk.setAttribute('href',list.data);
855     lLnk.appendChild(document.createTextNode('[list]'));
856     newMe.appendChild(lLnk);
857     newMe.appendChild(document.createTextNode(' '));
858     rat = document.createElement('a');
859     rat.setAttribute('href',"http://gplv3.fsf.org/comments/gplv3-draft-1?Query=%20Creator%20=%20'ratiodoc'%20%20AND%20'CF.NoteUrl'%20LIKE%20'gplv3-draft-1'%20&Order=DESC&OrderBy=id&StartAt=1&Rows=80");
860     rat.appendChild(document.createTextNode('[rationale]'));
861     newMe.appendChild(rat);
862     newMe.appendChild(document.createElement('br'));
863     newMe.appendChild(document.createTextNode('(found '+t.data+', showing '+rng.data));
864     if (pr || nx) { newMe.appendChild(document.createTextNode(': ')); }
865     if(pr) { 
866     pLnk = document.createElement('a');
867     pLnk.setAttribute('href',pr.data);
868     pLnk.appendChild(document.createTextNode('prev'));
869     newMe.appendChild(pLnk);
870     }
871     if (pr && nx) {
872       newMe.appendChild(document.createTextNode(' | '));
873     }
874     if (nx) {
875     nLnk = document.createElement('a');
876     nLnk.setAttribute('href',nx.data);
877     nLnk.appendChild(document.createTextNode('next'));
878     newMe.appendChild(nLnk);
879     }
880
881     newMe.appendChild(document.createTextNode(') '));
882
883     sLnk = document.createElement('a');
884     sLnk.setAttribute('href','http://gplv3.fsf.org/comments/rt/changeshown.html?came_from=gplv3-draft-1');
885     sLnk.appendChild(document.createTextNode('search'));
886     newMe.appendChild(sLnk);
887
888     st = document.getElementById('statustext');
889     ch = st.childNodes;
890     while (ch.length > 0) {
891         st.removeChild(ch.item(0));
892     }
893     st.appendChild(newMe);
894 }
895
896 function cstatusbox(cs) {
897   try { 
898     //alert("about to call idlinks");
899     idLinks(cs);
900   } catch (e) {
901     //    try {
902       qLinks(cs);
903       //    } catch (e) { statusbox("There is a problem with either our browser compatibility or the server's response. <a href=\"http://gplv3.fsf.org/comments/rt/changeshown.html?came_from=gplv3-draft-1\">search again</a>"); }
904   }
905   loginbox();
906 }
907 function deParen(str) {
908   str = str.replace(/\(/g,'\\(');
909   str = str.replace(/\)/g,'\\)');
910   return str;
911 }
912
913 function newQuery() {
914   loadURLtoDiv('/comments/rt/changeshown.html','came_from='+document.location,'statustext');
915 }
916
917 function cancelNewQuery() {
918   //  loadHTMLtoDiv(status.last,'statustext');
919 }
920
921 function kSerialize(node) {
922   var returnme = '';
923   // really this should encompass any non-closed tag, but I'm not sure if I
924   // can generalize it without just getting a whole list.
925   if (node.nodeName.toLowerCase() == 'br') {
926     returnme += '<'+node.nodeName.toLowerCase()+'/>';
927   }
928   else if (node.nodeType == 1) {
929     returnme += '<'+node.nodeName.toLowerCase();
930     if (node.hasAttributes()) {
931       for (var attrCt = 0; attrCt < node.attributes.length; attrCt++) {
932         returnme += ' '+node.attributes[attrCt].nodeName.toLowerCase()+'="'+node.attributes[attrCt].nodeValue+'"';
933       }
934     }
935     returnme += '>';
936     if (node.hasChildNodes()) {
937       for (var nodeCt = 0; nodeCt < node.childNodes.length; nodeCt++) {
938         returnme += kSerialize(node.childNodes[nodeCt]);
939       }
940     } 
941     returnme += '</'+node.nodeName.toLowerCase()+'>';
942   }
943   else if (node.nodeType == 3) {
944     returnme += node.data;
945   }
946   return returnme;
947 }
948
949 //* dragging behavior courtesy of brainjar.com.  License is GPL2+
950
951 //*****************************************************************************
952 // Do not remove this notice.
953 //
954 // Copyright 2001 by Mike Hall.
955 // See http://www.brainjar.com for terms of use.
956 //*****************************************************************************
957
958 // Determine browser and version.
959
960 function Browser() {
961
962   var ua, s, i;
963
964   this.isIE    = false;
965   this.isNS    = false;
966   this.version = null;
967
968   ua = navigator.userAgent;
969
970   s = "MSIE";
971   if ((i = ua.indexOf(s)) >= 0) {
972     this.isIE = true;
973     this.version = parseFloat(ua.substr(i + s.length));
974     return;
975   }
976
977   s = "Netscape6/";
978   if ((i = ua.indexOf(s)) >= 0) {
979     this.isNS = true;
980     this.version = parseFloat(ua.substr(i + s.length));
981     return;
982   }
983
984   // Treat any other "Gecko" browser as NS 6.1.
985
986   s = "Gecko";
987   if ((i = ua.indexOf(s)) >= 0) {
988     this.isNS = true;
989     this.version = 6.1;
990     return;
991   }
992 }
993
994 var browser = new Browser();
995
996 // Global object to hold drag information.
997
998 var dragObj = new Object();
999 dragObj.zIndex = 0;
1000
1001 function dragStart(event, id) {
1002
1003   var el;
1004   var x, y;
1005
1006   // If an element id was given, find it. Otherwise use the element being
1007   // clicked on.
1008
1009   if (id)
1010     dragObj.elNode = document.getElementById(id);
1011   else {
1012     if (browser.isIE)
1013       dragObj.elNode = window.event.srcElement;
1014     if (browser.isNS)
1015       dragObj.elNode = event.target;
1016
1017     // If this is a text node, use its parent element.
1018
1019     if (dragObj.elNode.nodeType == 3)
1020       dragObj.elNode = dragObj.elNode.parentNode;
1021   }
1022
1023   // Get cursor position with respect to the page.
1024
1025   if (browser.isIE) {
1026     x = window.event.clientX + document.documentElement.scrollLeft
1027       + document.body.scrollLeft;
1028     y = window.event.clientY + document.documentElement.scrollTop
1029       + document.body.scrollTop;
1030   }
1031   if (browser.isNS) {
1032     x = event.clientX + window.scrollX;
1033     y = event.clientY + window.scrollY;
1034   }
1035
1036   // Save starting positions of cursor and element.
1037
1038   dragObj.cursorStartX = x;
1039   dragObj.cursorStartY = y;
1040
1041   dragObj.elStartLeft  = parseInt(dragObj.elNode.offsetLeft, 10);
1042   dragObj.elStartTop   = parseInt(dragObj.elNode.offsetTop,  10);
1043
1044   if (isNaN(dragObj.elStartLeft)) dragObj.elStartLeft = 0;
1045   if (isNaN(dragObj.elStartTop))  dragObj.elStartTop  = 0;
1046
1047   // Update element's z-index.
1048
1049   dragObj.elNode.style.zIndex = ++dragObj.zIndex;
1050
1051   // Capture mousemove and mouseup events on the page.
1052
1053   if (browser.isIE) {
1054     document.attachEvent("onmousemove", dragGo);
1055     document.attachEvent("onmouseup",   dragStop);
1056     window.event.cancelBubble = true;
1057     window.event.returnValue = false;
1058   }
1059   if (browser.isNS) {
1060     document.addEventListener("mousemove", dragGo,   true);
1061     document.addEventListener("mouseup",   dragStop, true);
1062     event.preventDefault();
1063   }
1064 }
1065
1066 function dragGo(event) {
1067
1068   var x, y;
1069
1070   // Get cursor position with respect to the page.
1071
1072   if (browser.isIE) {
1073     x = window.event.clientX + document.documentElement.scrollLeft
1074       + document.body.scrollLeft;
1075     y = window.event.clientY + document.documentElement.scrollTop
1076       + document.body.scrollTop;
1077   }
1078   if (browser.isNS) {
1079     x = event.clientX + window.scrollX;
1080     y = event.clientY + window.scrollY;
1081   }
1082
1083   // Move drag element by the same amount the cursor has moved.
1084   //  dump("left starts as "+dragObj.elStartLeft+"; top starts as "+dragObj.elStartTop+"\n");
1085   //  dump("x is "+x+" and y is "+y+"\n");
1086
1087 //  dragObj.elNode.style.left = (dragObj.elStartLeft + x - dragObj.cursorStartX) + "px";
1088 //  dragObj.elNode.style.top  = (dragObj.elStartTop  + y - dragObj.cursorStartY) + "px";
1089
1090 dragObj.elNode.style.left = (dragObj.elStartLeft + x - dragObj.cursorStartX) + "px";
1091 dragObj.elNode.style.top  = (dragObj.elStartTop + y - dragObj.cursorStartY) + "px";
1092
1093
1094
1095 //  dump("left is now "+dragObj.elNode.style.left+"; top is now "+dragObj.elNode.style.top+"\n");
1096
1097   if (browser.isIE) {
1098     window.event.cancelBubble = true;
1099     window.event.returnValue = false;
1100   }
1101   if (browser.isNS)
1102     event.preventDefault();
1103 }
1104
1105 function dragStop(event) {
1106
1107   // Stop capturing mousemove and mouseup events.
1108
1109   if (browser.isIE) {
1110     document.detachEvent("onmousemove", dragGo);
1111     document.detachEvent("onmouseup",   dragStop);
1112   }
1113   if (browser.isNS) {
1114     document.removeEventListener("mousemove", dragGo,   true);
1115     document.removeEventListener("mouseup",   dragStop, true);
1116   }
1117 }
1118
1119 function readCookie(name)
1120 {
1121         var nameEQ = name + "=";
1122         var ca = document.cookie.split(';');
1123         for(var i=0;i<ca.length;i++)
1124         {
1125                 var c = ca[i];
1126                 while (c.charAt(0)==' ') c = c.substring(1,c.length);
1127                 if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
1128         }
1129         return null;
1130 }
1131
1132
1133
1134 // http://ostermiller.org/calc/encode.html
1135 // Copyright Stephen Ostermiller 2003-2006
1136 // http://ostermiller.org/contact.pl?regarding=JavaScript+Encoding
1137 //
1138 // This program is free software; you can redistribute it and/or
1139 // modify it under the terms of the GNU General Public License as
1140 // published by the Free Software Foundation; either version 2 of the
1141 // License, or (at your option) any later version.
1142
1143 // This program is distributed in the hope that it will be useful, but
1144 // WITHOUT ANY WARRANTY; without even the implied warranty of
1145 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1146 // General Public License for more details.
1147 var END_OF_INPUT = -1;
1148
1149 var base64Str;
1150 var base64Count;
1151 function setBase64Str(str){
1152   base64Str = str;
1153   base64Count = 0;
1154 }
1155 function readBase64(){    
1156   if (!base64Str) return END_OF_INPUT;
1157   if (base64Count >= base64Str.length) return END_OF_INPUT;
1158   var c = base64Str.charCodeAt(base64Count) & 0xff;
1159   base64Count++;
1160   return c;
1161 }
1162 function encodeBase64(str){
1163   setBase64Str(str);
1164   var result = '';
1165   var inBuffer = new Array(3);
1166   var lineCount = 0;
1167   var done = false;
1168   while (!done && (inBuffer[0] = readBase64()) != END_OF_INPUT){
1169     inBuffer[1] = readBase64();
1170     inBuffer[2] = readBase64();
1171     result += (base64Chars[ inBuffer[0] >> 2 ]);
1172     if (inBuffer[1] != END_OF_INPUT){
1173       result += (base64Chars [(( inBuffer[0] << 4 ) & 0x30) | (inBuffer[1] >> 4) ]);
1174       if (inBuffer[2] != END_OF_INPUT){
1175         result += (base64Chars [((inBuffer[1] << 2) & 0x3c) | (inBuffer[2] >> 6) ]);
1176         result += (base64Chars [inBuffer[2] & 0x3F]);
1177       } else {
1178         result += (base64Chars [((inBuffer[1] << 2) & 0x3c)]);
1179         result += ('=');
1180         done = true;
1181       }
1182     } else {
1183       result += (base64Chars [(( inBuffer[0] << 4 ) & 0x30)]);
1184       result += ('=');
1185       result += ('=');
1186       done = true;
1187     }
1188     lineCount += 4;
1189     if (lineCount >= 76){
1190       result += ('\n');
1191       lineCount = 0;
1192     }
1193   }
1194   return result;
1195 }
1196 function readReverseBase64(){   
1197   if (!base64Str) return END_OF_INPUT;
1198   while (true){      
1199     if (base64Count >= base64Str.length) return END_OF_INPUT;
1200     var nextCharacter = base64Str.charAt(base64Count);
1201     base64Count++;
1202     if (reverseBase64Chars[nextCharacter]){
1203       return reverseBase64Chars[nextCharacter];
1204     }
1205     if (nextCharacter == 'A') return 0;
1206   }
1207   return END_OF_INPUT;
1208 }
1209
1210 function ntos(n){
1211   n=n.toString(16);
1212   if (n.length == 1) n="0"+n;
1213   n="%"+n;
1214   return unescape(n);
1215 }
1216
1217 function decodeBase64(str){
1218   setBase64Str(str);
1219   var result = "";
1220   var inBuffer = new Array(4);
1221   var done = false;
1222   while (!done && (inBuffer[0] = readReverseBase64()) != END_OF_INPUT
1223          && (inBuffer[1] = readReverseBase64()) != END_OF_INPUT){
1224     inBuffer[2] = readReverseBase64();
1225     inBuffer[3] = readReverseBase64();
1226     result += ntos((((inBuffer[0] << 2) & 0xff)| inBuffer[1] >> 4));
1227     if (inBuffer[2] != END_OF_INPUT){
1228       result +=  ntos((((inBuffer[1] << 4) & 0xff)| inBuffer[2] >> 2));
1229       if (inBuffer[3] != END_OF_INPUT){
1230         result +=  ntos((((inBuffer[2] << 6)  & 0xff) | inBuffer[3]));
1231       } else {
1232         done = true;
1233       }
1234     } else {
1235       done = true;
1236     }
1237   }
1238   return result;
1239 }
1240
1241 // from rt:
1242 function hideshow(num) {
1243     idstring = "element-" + num;
1244     chunk = document.getElementById(idstring);
1245     if ( chunk.style.display == "none")  {
1246     chunk.style.display = chunk.style.tag;
1247     } else {
1248         chunk.style.tag = chunk.style.display;
1249         chunk.style.display = "none";
1250     }
1251 }   
1252