git-svn-id: http://code.softwarefreedom.org/svn/stet/trunk@54 55a1384f-cf82-4c22...
[stet:stet.git] / stet-intense-shorter.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 function printfire(foo) {
30   //  alert(foo);
31   /*       if (document.createEvent) {
32     printfire.args = arguments;
33     var ev = document.createEvent("Events");
34     ev.initEvent("printfire", false, true);
35     dispatchEvent(ev);
36     }  */
37 }
38
39 // a few things need to be global
40 var ticketObj = new Object;
41 ticketObj.length = 0;
42 var nodelist = new Object;
43 var drafter = '';
44 var filename;
45 var firstLoad = "1";
46
47 function initPage() {
48   // quit if this function has already been called
49   if (arguments.callee.done) return;
50   // flag this function so we don't do the same thing twice
51   arguments.callee.done = true;
52   statusbox("Please wait while we load some comments.");
53   filename = location.pathname.substring(location.pathname.lastIndexOf('/')+1,location.pathname.length); 
54   if((!filename.length) || (filename.match(/index/)) || (filename.match(/comments$/))|| (filename.match(/comments\/\?/)) || (filename.match(/comments\/#/)) || (filename.match(/debug/) || (filename.match(/classic/)))) {
55     filename = 'gplv3-draft-1';
56   }
57   if(window.location.search.length) {
58     loadXMLDoc('/comments/rt/xmlresults-intense.html',window.location.search.substring(1));
59   }
60   else {
61     loadXMLDoc('/comments/cached.xml','');
62   }
63 }
64 function loadFromHash() { // checks the location hash for bookmarked notes to show
65   if ((window.location.hash.length > 0) && (window.location.hash.match(/^#\d\d\d/))) {
66     getnotes(location.hash.substring(1,location.hash.length));
67   } 
68   else if (window.location.hash.match(/^#all/)) {
69     loadAll()
70   }
71 }
72
73 function loadAll() {
74   var getUs = '';
75   for (var rtid in ticketObj) {
76     getUs += rtid+':';
77   }
78   getnotes(getUs);
79 }
80 // XpathSel adapted from http://www.quirksmode.org/js/selected.html
81 // this is for browsers with a featureful getSelection object; others
82 // have to use an XMLHTTPRequest call.
83 function XpathSel() { 
84   if (!readCookie('__ac')) {
85     document.getElementById('login').setAttribute('style','color: red; font-weight: bold; font-size: 150%');
86     return;
87   }
88   var textObj = '';
89   var start = '';
90   var end = '';
91   if (window.getSelection)
92     {
93       textObj = window.getSelection();
94     }
95   else if (document.getSelection)
96     {
97       textObj= document.getSelection();
98     }
99   else if (document.selection)
100     {
101       textObj= document.selection.createRange().text;
102     }
103        if(textObj.length < 1) {
104          document.getElementById('selectsome').setAttribute('class','error');
105          document.getElementById('login').innerHTML(textObj.length);
106          return;
107        }
108
109     try { var sn = textObj.anchorNode; } catch(e) {}
110     if (!sn) {  // lame getSelection object has no anchorNode
111        loadXMLDoc('/comments/rt/formxml.html','selection='+encodeURI(textObj.toString()));
112         
113
114     }
115     else {  // good getSelection
116       try { var startNode = textObj.anchorNode; } catch(e) { }
117       if (startNode.parentNode.getAttribute && startNode.parentNode.getAttribute('class') && startNode.parentNode.getAttribute('class').match(/highlight/)) {
118         startNode = startNode.parentNode;
119       }
120       try { var startid = startNode.parentNode.id; } catch(e) { }
121       try { var start = startNode.parentNode.nodeName; } catch(e) { }
122       var endnode = textObj.focusNode.parentNode;
123       if (endnode.getAttribute && endnode.getAttribute('class') && endnode.getAttribute('class').match(/highlight/)) {
124         endnode = endnode.parentNode;
125       }
126       var endid = endnode.id;
127       var end = endnode.nodeName;
128       if (!readCookie('__ac')) {
129         // emphasizes that You need to log in to make comments.
130         document.getElementById('login').setAttribute('style','color: red; font-weight: bold; font-size: 150%');
131       }
132       else {
133         // create the note form
134         var oStr = getDomPath(startNode);
135         createNoteDiv(startid);
136         var textString = textObj.toString();
137         document.getElementById('DomPath').value = oStr;
138         document.getElementById('Selection').value = textString;
139         loadHTMLtoDiv('comment on: <strong>'+textString+'</strong>','SelectionTxt');
140         document.getElementById('NoteSubj').value = 'Subject/summary [required]';
141         document.getElementById('StartNodeId').value = startid;
142         // document.getElementById('EndNodeId').value = endid;
143         document.getElementById('NoteUrl').value = filename;
144         if ((i = navigator.userAgent.indexOf('MSIE')) >= 0) {
145           document.getElementById('SelectionTxt').innerHTML += "<br/>Comment submission isn't working on the copy of IE 6 we tested [if IE is really the browser you're using] but you're welcome to try.  If this box doesn't turn gray and then disappear when you click 'Submit', it probably didn't work: you can <a href=\"/comments/email.html\">email your comment</a> instead.";
146         }
147         if(startid != endid) {
148           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.  startid='+startid+', endid='+endid);
149         }
150         if((startid =='login') || (startid == undefined) || (textString == undefined) || (textString == '')) {
151           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');
152         }
153       }
154     }
155 }
156
157 function noteErr(msg) {
158   document.getElementById('NoteText').value = msg;
159   document.getElementById('submitNote').setAttribute('disabled','disabled');
160   document.getElementById('NoteSubj').setAttribute('disabled','disabled');
161 }
162
163 /* // not currently used: could invoke when calling submitComment(); 
164 function rmChildNotes(startid) {
165   node = document.getElementById(startid);
166   //  var oldChild;
167   if (node.hasChildNodes()) {
168     for (var nodeCt = 0; nodeCt < node.childNodes.length; nodeCt++) {
169       if (node.childNodes.item(nodeCt).getAttribute('class').match('notegroup')) { 
170         //oldChild.appendChild(
171         node.removeChild(node.childNodes.item(nodeCt));
172         // )
173       }
174     }
175   }
176   } */
177 function processDompath(response) {
178   currently('asking server about your selection...');
179   try { var fail = response.getElementsByTagName("nf")[0].firstChild.data; } catch(e) {}
180   if (fail) { alert(fail); } // because we don't otherwise know where to put it, argh
181   else {
182     createNoteDiv(response.getElementsByTagName("si")[0].firstChild.data);
183     document.getElementById('DomPath').value = response.getElementsByTagName("dp")[0].firstChild.data;
184     document.getElementById('Selection').value = response.getElementsByTagName("sl")[0].firstChild.data;
185     loadHTMLtoDiv('comment on: <strong>'+response.getElementsByTagName("sl")[0].firstChild.data+'</strong>','SelectionTxt');
186     document.getElementById('NoteSubj').value = 'Subject/summary [required]';
187     document.getElementById('StartNodeId').value = response.getElementsByTagName("si")[0].firstChild.data;
188
189         if ((i = navigator.userAgent.indexOf('MSIE')) >= 0) {
190           document.getElementById('SelectionTxt').innerHTML += "<br/>Comment submission isn't working on the copy of IE 6 we tested [if IE is really the browser you're using] but you're welcome to try.  If this box doesn't turn gray and then disappear when you click 'Submit', it probably didn't work: you can <a href=\"/comments/email.html\">email your comment</a> instead.";
191 }
192     //    document.getElementById('NoteText').value = "dp: "+document.getElementById('DomPath').value+"\nsl: "+document.getElementById('Selection').value+"\nsi: "+document.getElementById('StartNodeId').value;
193     // document.getElementById('EndNodeId').value = endid;
194     document.getElementById('NoteUrl').value = response.getElementsByTagName("fn")[0].firstChild.data;;
195   }
196 }
197
198 function getDomPath(startNode) {
199   // helped here by http://www.howtocreate.co.uk/emails/FlorianSauvin.html
200   for (var oStr='' ; startNode && startNode.nodeName != '#document'; startNode=startNode.parentNode) { 
201     if ((startNode.nodeName!='#text') || ((startNode.getAttribute) && (startNode.getAttribute('class').match('highlight')))) { 
202       oStr = startNode.nodeName 
203         + (startNode.id ? ('[id='+startNode.id+']') : '')
204         + (oStr ? ('/'+oStr) : '');
205     }
206   }
207   currently('Ready.');
208   return oStr;
209 }
210
211 function submitComment() {
212   // doesn't fire on IE, hmm.
213   if ((document.getElementById('NoteSubj').value == 'Subject/summary [required]') || (document.getElementById('NoteSubj').value == ' ')) {
214     loadHTMLtoDiv('<span class="error">Please enter a brief explanatory subject for this comment</span>','SelectionTxt');
215   }
216   else {
217   var form = document.getElementById('noteify');
218   //rmChildNotes(form.StartNodeId.value);
219   var noteText = form.NoteText.value;
220   loadHTMLtoDiv('Ok, submitting...','SelectionTxt');
221   form.NoteText.style.background = '#aaa';
222   form.NoteSubj.style.background = '#aaa';
223   document.getElementById('submitNote').setAttribute('disabled','disabled');
224   document.getElementById('cancel').setAttribute('disabled','disabled');
225   form.style.background = '#aaa';
226
227   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);
228
229   var theUrl = '/comments/rt/submitcomment-cachetoo.html'; 
230   loadXMLDoc(theUrl,params);
231   }
232 }
233
234 // these are all too too slow, maybe cssQuery
235  function getElementsByClass(needle)
236 {
237   var         my_array = document.getElementsByTagName('*');
238   var         retvalue = new Array();
239   var        i;
240   var        j;
241   for (i = 0, j = 0; i < my_array.length; i++)
242     {
243       var c = " " + my_array[i].className + " ";
244       if (c.indexOf(" " + needle + " ") != -1) {
245         retvalue[j++] = my_array[i];
246       }
247     }
248   return retvalue;
249
250
251 /* function getElementsByClass(searchClass,node,tag) {
252   var classElements = new Array();
253   if ( node == null )
254     node = document;
255   if ( tag == null )
256     tag = '*';
257   var els = node.getElementsByTagName(tag);
258   var elsLen = els.length;
259   var pattern = new RegExp("(^|\s)"+searchClass+"(\s|$)");
260   for (i = 0, j = 0; i < elsLen; i++) {
261     if (node != document)
262     if ( pattern.test(els[i].className) ) {
263       classElements[j] = els[i];
264       j++;
265     }
266   }
267   return classElements;
268   } */
269
270
271 // help from http://www.xml.com/pub/a/2005/02/09/xml-http-request.html
272 var req;
273 function loadXMLDoc(url,theData) 
274 {
275   if (window.XMLHttpRequest) {
276     req = new XMLHttpRequest();
277   } else if (window.ActiveXObject) {
278     req = new ActiveXObject('Microsoft.XMLHTTP');
279   }
280   var method;
281   req.onreadystatechange = processReqChange;
282   if(theData=='') { method="GET"; } 
283   else { method = "POST"; }
284   req.open(method, url, true);
285   req.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
286   req.setRequestHeader('X-Referer',document.location);
287   req.send(theData);
288 }
289
290
291 function loadHTMLtoDiv(text,targetid,caller) {
292   if(targetid) {
293     if (myElement = document.getElementById(targetid)) {
294       try {     
295         myElement.innerHTML = text;
296       }
297       catch(e) {
298         var newDiv = document.createElement('span');
299         clearNode(myElement);
300         newDiv.innerHTML = text;
301         myElement.appendChild(newDiv);
302       }
303     }
304   }
305 }
306
307 function clearNode(node) {
308   if (node.hasChildNodes()) {
309     while (node.childNodes.length > 0) {
310       node.removeChild(node.childNodes.item(0));
311     }
312   } 
313 }
314
315 // thanks to http://www.mercurytide.com/knowledge/white-papers/issues-working-with-ajax
316 function getXMLNodeSerialisation(xmlNode) {
317   //  alert(xmlNode);
318   var text = false;
319   if (xmlNode.nodeType != 3) {
320   try {
321     // Gecko-based browsers, Safari, Opera.
322     //    var serializer = new XMLSerializer();
323     text = serializer.serializeToString(xmlNode);
324   }
325   catch (e) {
326     try {
327       // Internet Explorer?  
328       text = xmlNode.xml;
329     }
330     catch (e) { // Anybody?  Bueller?
331       text = kSerialize(xmlNode);
332     }
333   }
334   if (text == undefined) { // maybe someone only pretended to succeed
335     text = kSerialize(xmlNode);
336   }
337   return text;
338   }
339   else {
340     return xmlNode;
341   }
342 }
343
344 function loadURLtoDiv(url,args,targetid) {
345   if (window.XMLHttpRequest) {
346     req = new XMLHttpRequest();
347   } else if (window.ActiveXObject) {
348     req = new ActiveXObject('Microsoft.XMLHTTP');
349   }
350   req.open('GET',url,false);
351   req.send(args);
352   loadHTMLtoDiv(req.responseText,targetid,"loadurltodiv");
353 }
354
355 function processReqChange()
356 {
357   var did = 0;
358   if (req.readyState == 4) {
359     // only if "OK" or "not modified"
360     if ((req.status == 200) || (req.status == 304)) {
361       cancelNote("noteify");
362       cancelNote("loading");
363       var response  = req.responseXML.documentElement;
364       var resp_arrN;
365       var resp_arrA;
366       var resp_arrF;
367       var resp_arrS;
368       try {
369         drafter = response.getElementsByTagName("d")[0].firstChild.data;
370       }
371       catch (e) {}
372       try {
373         cs = response.getElementsByTagName("cs");
374         if (!!cs.length && (cs.length > 0)) {
375           cstatusbox(cs[0]);
376         }
377       } catch(e) {}
378       resp_arrN = response.getElementsByTagName("ant");
379       if(!!resp_arrN.length && (resp_arrN.length > 0)) {
380         processAnt(response);
381         did++;
382       }
383       resp_arrN = response.getElementsByTagName("anf");
384       if(!!resp_arrN.length && (resp_arrN.length > 0)) {
385         processAnf(response);
386         did++;
387       }
388       resp_arrN = response.getElementsByTagName("nant");
389       if(!!resp_arrN.length && (resp_arrN.length > 0)) {
390         cacheAntVals(response);
391         processAnf(response,'top');
392         did++;
393       }
394       resp_arrA = response.getElementsByTagName("agreement");
395       if(!!resp_arrN.length && (resp_arrN.length > 0)) {
396         processAgreement(response);
397         did++;
398       }
399       resp_arrS = response.getElementsByTagName("sdp");
400       if(!!resp_arrS.length && (resp_arrS.length > 0)) {
401         processDompath(response);
402         did++;
403       }
404       
405       if (!did) {
406         statusbox("Your search didn't return anything usable: try a reload, or 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
407       }
408     }
409     if (firstLoad == 1) {
410       firstLoad = 0;
411       loadFromHash();
412     }
413   }
414 }
415   
416 function onNoteifyMouseover() {
417   //this.onblur = onNoteifyBlur;
418 }
419
420 function processAnf(response,placement) {
421   currently('loading selected annotations...');
422   var newDiv = new Object;
423   rtids_arr = response.getElementsByTagName("id");
424   annotations_arr = response.getElementsByTagName("n");
425   users_arr = response.getElementsByTagName("u");
426   uagr_arr = response.getElementsByTagName("ua");
427   agrtot_arr = response.getElementsByTagName("at");
428   startnodes_arr = response.getElementsByTagName("i");
429
430   var addCount = 0;;
431   
432   for (prI = 0; prI < annotations_arr.length; prI++) {
433     if (annotations_arr[prI].firstChild) {
434       var rtid = rtids_arr[prI].firstChild.data;
435       if (!document.getElementById('rt'+rtid)) {
436         var sn = startnodes_arr[prI].firstChild.data;
437         if (!newDiv[sn]) {
438           // why doesn't IE see my stylesheets on these?
439           newDiv[sn] = document.createElement('div');
440           newDiv[sn].style.left = '55%';
441           newDiv[sn].style.position = 'absolute';
442           newDiv[sn].style.fontSize = '10pt';
443           newDiv[sn].style.fontWeight = 'normal';
444           if ((i = navigator.userAgent.indexOf('MSIE')) >= 0) {
445             newDiv[sn].style.width = '90%';
446           }     
447           togBtn = document.createElement('div');
448           togBtn.setAttribute('id','tog'+sn);
449           togBtn.setAttribute('class','noteClose a'+annotations_arr.length);
450           togX = document.createElement('span');
451           togX.setAttribute('id','x'+sn);
452           togX.setAttribute('onclick','killNoteGroup(\''+newDiv[sn].id+'\')');
453           togX.innerHTML = "[X]";
454           togBtn.appendChild(togX);
455           newDiv[sn].appendChild(togBtn);
456           newDiv[sn].itemcount = 1;
457         }
458         newDiv[sn].setAttribute('id',newDiv[sn].id+rtid+':');
459         newDiv[sn].itemcount++;
460         newDiv[sn].setAttribute('class','notegroup');
461
462         addCount++;
463         try { var tooltipString = getXMLNodeSerialisation(annotations_arr[prI]); } catch(e) { };
464         if (!tooltipString) { var tooltipString = annotations_arr[prI].firstChild.data; }
465         tooltipString.length > 165 ? tooltipSubString = tooltipString.substr(0,199) + '...' : tooltipSubString = tooltipString;
466         if (!ticketObj[rtid]) { ticketObj[rtid] = new Object; ticketObj.length++;}
467         ticketObj[rtid].link = makeLinkObj(rtid, getXMLNodeSerialisation(uagr_arr[prI]), agrtot_arr[prI].firstChild.data);      
468         ticketObj[rtid].full = tooltipString;
469         //alert("setting "+rtid+" excerpt to "+tooltipSubString);
470         ticketObj[rtid].excerpt = tooltipSubString;
471         ticketObj[rtid].user = users_arr[prI].firstChild ? users_arr[prI].firstChild.data : "";
472         ticketObj[rtid].ua = getXMLNodeSerialisation(uagr_arr[prI]);
473         ticketObj[rtid].startnode = startnodes_arr[prI].firstChild.data.replace(/note\.[0-9]+\./,'');;
474
475         //speed: maybe use cssQuery instead
476         //nodelist[rtid] = getElementsByClass('n'+rtid);
477         var divChild = document.createElement('div');
478         divChild.setAttribute('id','rt'+rtid);
479         divChild.setAttribute('onclick','showFull(\''+rtid+'\')');
480         //speed
481         //divChild.setAttribute('onmouseover',"notemouseover(this,nodelist["+rtid+"])");
482         //divChild.setAttribute('onmouseout','notemouseout(this,nodelist['+rtid+'])');
483         divChild.innerHTML = '\
484  <span style="font-size: smaller;font-style: italic" id="rt'+rtid+'user">'+ticketObj[rtid].user+': </span><span id="rt'+rtid+'txt">'+ticketObj[rtid].excerpt +'<a href="javascript:showFull(\''+rtid+'\')">[+]</a></span>';
485         divChild.setAttribute('class','newannotation '+rtid);
486         
487         newDiv[sn].appendChild(divChild);
488         
489       }
490     }
491   }
492   if (addCount > 0) {
493     for (var sn in newDiv) {
494       newDiv[sn].id = newDiv[sn].id.replace(/:$/,'');
495       subjNode = document.getElementById(sn);
496       if (placement && (placement == 'top')) {
497         subjNode.insertBefore(newDiv[sn],subjNode.lastChild);
498       }
499       else {
500         subjNode.appendChild(newDiv[sn]);
501       }
502       document.getElementById('tog'+sn).setAttribute('class','noteClose a'+newDiv[sn].itemcount);
503       document.getElementById('tog'+sn).setAttribute('onmousedown',"dragStart(event,\'"+newDiv[sn].id+"\')");
504       document.getElementById('x'+sn).setAttribute('onclick','killNoteGroup(\''+newDiv[sn].id+'\')');
505     }
506     unOverlap("notegroup",5);
507   } 
508   currently("Ready.");
509 }
510
511 function processAnt(response) {
512   currently('highlighting this page according to your query...');
513   var rtidsBySn;
514   var rtidsByP;
515   var rtnArray;
516   rtnArray = cacheAntVals(response);
517   rtidsBySn = rtnArray[0];
518   rtidsByP = rtnArray[1];
519   highlightSelections(rtidsBySn);
520   PLinks(rtidsByP);
521   addAllLink();
522   //  unOverlap("annotation",5); // thse won't overlap anymore
523   currently('Ready.');
524 }
525
526 function addAllLink() {
527   if (ticketObj.length < 150) {
528     var sLnk = document.createElement('a');
529     sLnk.setAttribute('href','#all');
530     sLnk.setAttribute('onClick','loadAll()');
531     sLnk.appendChild(document.createTextNode('display all'));
532     document.getElementById('statustext').appendChild(document.createTextNode(' '));
533     document.getElementById('statustext').appendChild(sLnk);
534   }
535 }
536 function PLinks(rtidsByP) {
537   for (var p in rtidsByP) {
538     var pLnk = document.createElement('span');
539     pLnk.setAttribute('id','pLnk'+p);
540     pLnk.setAttribute('class','plink');
541     pLnk.style.display = 'none';
542     pLnk.innerHTML = '<a href="#'+p+'" title="paragraph id: #'+p+'">&para;</a>&nbsp;<a title="show all '+rtidsByP[p].length+' comments on this paragraph" href="javascript:getnotes(\''+rtidsByP[p].join(":")+'\')">&raquo;</a>';
543     document.getElementById(p).appendChild(pLnk);
544     document.getElementById(p).setAttribute('onmouseover','show("pLnk'+p+'")');
545     document.getElementById(p).setAttribute('onmouseout','hide("pLnk'+p+'")');
546   }
547 }
548 function show(id) {
549   document.getElementById(id).style.display = 'inline';
550 }
551 function hide(id) {
552   document.getElementById(id).style.display = 'none';
553 }
554
555
556 function cacheAntVals(response) {
557   // this used to be part of processAnt but then I needed it for process nant.
558   // populates global ticketObj[] for these rtids and returns
559   // the rtidsbysn that we need for highlighting.
560   var selections_arr = response.getElementsByTagName("s");
561   var startnodes_arr = response.getElementsByTagName("i");
562   var rtids_arr = response.getElementsByTagName("id");
563   var queues_arr = response.getElementsByTagName("qn");
564   var rtidsBySn = new Object;
565   var rtidsByP = new Object;
566   for (prI = 0; prI < selections_arr.length; prI++) {
567     if (selections_arr[prI].firstChild) {
568       rtids_arr[prI].firstChild.data ? rtid = rtids_arr[prI].firstChild.data : rtid = "error";
569       if (!ticketObj[rtid]) { ticketObj[rtid] = new Object; ticketObj.length++;}
570       ticketObj[rtid].startnode = startnodes_arr[prI].firstChild.data.replace(/note\.[0-9]+\./,'');;
571       ticketObj[rtid].queue = queues_arr[prI].firstChild.data;
572       ticketObj[rtid].selection = selections_arr[prI].firstChild.data;
573       startp = ticketObj[rtid].startnode.replace(/.s[\d+]$/, '');
574       if(!rtidsBySn[ticketObj[rtid].startnode]) {
575         rtidsBySn[ticketObj[rtid].startnode] = new Array(rtid);
576         rtidsByP[startp] = new Array(rtid);
577       }
578       else {
579         rtidsBySn[ticketObj[rtid].startnode].push(rtid);
580         rtidsByP[startp].push(rtid);
581       }
582     }
583   }
584
585   return Array(rtidsBySn, rtidsByP);
586 }
587
588 function highlightSelections(rtidsBySn) {
589   // fixme todo: p drm.0 and sent drm.0.0 get called separately, and this fucks things up.
590   // (this is GIGO: nothing should be drm.0, only drm.0.0 .  Still, should be fixed at some point)
591   // ah, now it has to be fixed so we can highlight new annots.
592   for (var section in rtidsBySn) {
593     var Selections = new Array();
594     var Rtids = new Array();
595     for (var i = 0; i < rtidsBySn[section].length; i++) {
596       var rtid = rtidsBySn[section][i];
597       Selections.push(ticketObj[rtid].selection);
598       Rtids.push(rtid);
599     }
600     intense_annot(document.getElementById(section),Selections,Rtids);         
601   }
602 }
603 function makeLinkObj(rtid,agreement,agrtot) {
604     returnLink = rtid ? '<a href="/comments/rt/readsay.html?id='+rtid+'">read/say more</a> ' : '[problem with ticket link]';
605           agreestr = agreement;
606           agreestr = agreestr.replace(/<\/?ua>/g,'');
607           agreestr = agreestr.replace(/<\/?span>/g,'');
608           agreestr = agreestr.replace(/<\/?b>/g,'');
609           if ((agreestr == "agree") || (agreestr == "unagree")) {
610             returnLink += '<a id="agree'+rtid+'" href="javascript:iAgree('+rtid+',\''+agreestr+'\')">'+agreestr+'</a> | ';
611           }
612           else {
613             returnLink += '<span id="agree'+rtid+'">'+agreestr+'</span> | ';
614           }
615           if (agrtot) {
616             returnLink += ' [<span id="agrtot'+rtid+'">'+agrtot+'</span> agree]';
617           }
618           return returnLink;
619 }
620
621 function processAgreement(response) {
622   currently('Indicating your agreement with this comment...');
623         myId = response.getElementsByTagName("id")[0].firstChild.data;
624         myOpn = response.getElementsByTagName("b")[0].firstChild.data;
625         myLink = document.getElementById('agree'+myId);
626         opOpn = document.createElement('span');
627         myOpn == "agree" ? opOpn.appendChild(document.createTextNode("unagree")) : opOpn.appendChild(document.createTextNode("agree"));
628         var agrcount = response.getElementsByTagName("ct")[0].firstChild.data;
629         loadHTMLtoDiv(agrcount,'agrtot'+myId);
630         ticketObj[myId].link = makeLinkObj(myId,getXMLNodeSerialisation(opOpn),agrcount);
631         showFull(myId);
632         myLink.href = 'javascript:iAgree('+myId+',\''+opOpn+'\')';
633         currently('Ready.');
634       }
635
636   
637 function onNoteifyBlur() {
638 //      noteNode = document.getElementById('noteify');
639 //      dump(noteNode);
640 //      submitComment();
641 }
642
643 function checkKeyPressed(keyEvent) {
644   if (!document.getElementById("noteify")) {
645     keyEvent = (keyEvent) ? keyEvent : (window.event) ? event : null;
646     if (keyEvent) {
647       var charCode = (keyEvent.charCode) ? keyEvent.charCode :
648         ((keyEvent.keyCode) ? keyEvent.keyCode :
649          ((keyEvent.which) ? keyEvent.which : 0));
650       //printfire(charCode); // to find out others
651       if (charCode == 99) { XpathSel(); }
652     }
653   }
654 }
655
656 function cancelNote(div) {
657   if (cancelme = document.getElementById(div)) {
658     cancelme.parentNode.removeChild(cancelme);
659   }
660 }
661
662 function createNoteDiv(startid) {
663
664   // this would be a lot more readable and maintainable if it were
665   // just a string.
666
667 var noteifyDiv = document.createElement('form');
668 noteifyDiv.setAttribute('action','/comments/rt/submitcomment-cachetoo.html');
669 noteifyDiv.setAttribute('class','noteify');
670 noteifyDiv.setAttribute('id','noteify');
671 noteifyDiv.setAttribute('name','noteify');
672 noteifyDiv.setAttribute('onblur','onNoteifyBlur()');
673 // noteifyDiv.style.z-index = '100';
674
675 var DomPathTxt = document.createElement('span');
676 DomPathTxt.setAttribute('id','DomPathTxt');
677 DomPathTxt.setAttribute('name','DomPathTxt');
678
679 var DomPath = document.createElement('input');
680 DomPath.setAttribute('id','DomPath');
681 DomPath.setAttribute('name','DomPath');
682 DomPath.setAttribute('type','hidden');
683 DomPath.setAttribute('class','addnote');
684
685 var SelectionTxt = document.createElement('span');
686 SelectionTxt.setAttribute('id','SelectionTxt');
687 SelectionTxt.setAttribute('name','SelectionTxt');
688
689 var Selection = document.createElement('input');
690 Selection.setAttribute('id','Selection');
691 Selection.setAttribute('name','Selection');
692 Selection.setAttribute('type','hidden');
693
694 var NoteSubj = document.createElement('input');
695 NoteSubj.setAttribute('id','NoteSubj');
696 NoteSubj.setAttribute('name','NoteSubj');
697 NoteSubj.setAttribute('type','text');
698 NoteSubj.setAttribute('size','40');
699 NoteSubj.setAttribute('onfocus','if(this.value=="Subject/summary [required]") {this.value="";}');
700 NoteSubj.setAttribute('onblur','if(this.value==""){this.value="Subject/summary [required]";}');
701 NoteSubj.setAttribute('class','addnote');
702
703 var NoteText = document.createElement('textarea');
704 NoteText.setAttribute('id','NoteText');
705 NoteText.setAttribute('name','NoteText');
706 NoteText.setAttribute('rows','10');
707 NoteText.setAttribute('cols','40');
708 NoteText.setAttribute('class','addnote');
709
710 var StartNodeId = document.createElement('input');
711 StartNodeId.setAttribute('id','StartNodeId');
712 StartNodeId.setAttribute('name','StartNodeId');
713 StartNodeId.setAttribute('type','hidden');
714
715 var NoteUrl = document.createElement('input');
716 NoteUrl.setAttribute('id','NoteUrl');
717 NoteUrl.setAttribute('name','NoteUrl');
718 NoteUrl.setAttribute('type','hidden');
719
720 var cancel = document.createElement('input');
721 cancel.setAttribute('type','button');
722 cancel.setAttribute('id','cancel');
723 cancel.setAttribute('name','cancel');
724 cancel.setAttribute('value','cancel');
725 cancel.setAttribute('onClick','cancelNote("noteify")');
726 cancel.setAttribute('class','annoteButton');
727 cancel.setAttribute('class','addnote');
728
729 var theBR = document.createElement('br');
730 var theBR1 = document.createElement('br');
731 var theBR2 = document.createElement('br');
732 var theBR3 = document.createElement('br');
733 var theBR4 = document.createElement('br');
734
735 var pickQueue;
736 if (drafter) {
737 pickQueue = document.createElement('select');
738 pickQueue.setAttribute('name','queue');
739 pickQueue.setAttribute('id','queue');
740
741 var optDrafter = document.createElement('option');
742 optDrafter.setAttribute('value','Drafter');
743 optDrafter.setAttribute('selected','selected');
744 optDrafter.appendChild(document.createTextNode('Drafter'));
745
746 var optInbox = document.createElement('option');
747 optInbox.setAttribute('value','Inbox');
748 optInbox.appendChild(document.createTextNode('Inbox'));
749
750 var optIssues = document.createElement('option');
751 optIssues.setAttribute('value','Issues');
752 optIssues.appendChild(document.createTextNode('Issues'));
753
754 pickQueue.appendChild(optDrafter);
755 pickQueue.appendChild(optInbox);
756 pickQueue.appendChild(optIssues);
757 }
758 else {
759 pickQueue = document.createElement('input');
760 pickQueue.setAttribute('type','hidden');
761 pickQueue.setAttribute('name','queue');
762 pickQueue.setAttribute('value','Inbox');
763 }
764 var submit = document.createElement('input');
765 submit.setAttribute('type','button');
766 submit.setAttribute('id','submitNote');
767 submit.setAttribute('value','submit');
768 submit.setAttribute('onClick','submitComment()');
769 submit.setAttribute('class','annoteButton');
770 submit.setAttribute('class','addnote');
771
772 noteifyDiv.appendChild(DomPathTxt);
773 noteifyDiv.appendChild(DomPath);
774 noteifyDiv.appendChild(SelectionTxt);
775 noteifyDiv.appendChild(theBR1);
776 noteifyDiv.appendChild(Selection);
777 noteifyDiv.appendChild(NoteSubj);
778 noteifyDiv.appendChild(theBR3);
779 noteifyDiv.appendChild(NoteText);
780 noteifyDiv.appendChild(StartNodeId);
781 //noteifyDiv.appendChild(EndNodeId);
782 noteifyDiv.appendChild(NoteUrl);
783 //noteifyDiv.appendChild(DocRevision);
784 noteifyDiv.appendChild(theBR4);
785 noteifyDiv.appendChild(submit);
786 noteifyDiv.appendChild(cancel);
787
788 if (drafter) {
789 pickQueue && noteifyDiv.appendChild(document.createTextNode(' Queue: ')) && noteifyDiv.appendChild(pickQueue);
790 }
791 else {
792 pickQueue && noteifyDiv.appendChild(pickQueue);
793 }
794
795  thisNode = document.getElementById(startid);
796  
797  if ((thisNode.previousSibling) && (thisNode.previousSibling.previousSibling) && (thisNode.previousSibling.previousSibling.appendChild)) {
798    thisNode.previousSibling.previousSibling.appendChild(noteifyDiv);
799  }
800  else 
801    if ((thisNode.previousSibling) && (thisNode.previousSibling.appendChild)) {
802      thisNode.previousSibling.appendChild(noteifyDiv);
803    }
804    else {
805      thisNode.appendChild(noteifyDiv);
806    }
807 }
808
809 function intense_annot(root,selections,rtids) {
810   selecObj = new Array;
811   rootObj = new Array;
812   var rootstr = '';
813   var rootid = root.id;
814   var divstart = root.innerHTML.indexOf('<div');
815   var DIVstart = root.innerHTML.indexOf('<DIV');
816   divstart = (DIVstart > divstart) ? DIVstart : divstart;
817   if (divstart == -1) {
818     rootSer = getXMLNodeSerialisation(root,'noattr');
819     var divstartSer = rootSer.indexOf('<div');
820     rootstr = root.innerHTML;
821   }
822   if (divstart > -1) {
823     var olddivs = root.innerHTML.substring(divstart,root.innerHTML.length);
824     rootstr = root.innerHTML.substring(0,divstart);
825   }
826   else if (divstartSer > -1) {
827     var olddivs = rootSer.substring(divstartSer,rootSer.length);
828     rootstr = rootSer.substring(0,divstart);
829   }
830   // couple lines of html-ignoring help from
831   // http://www.notestips.com/80256B3A007F2692/1/NAMO5RNV2S 
832   // by Mike Golding, 9/24/2003
833   //Extract HTML Tags
834   var regexp=/<[^<>]*>/ig;
835   var rootHTMLArray = rootstr.match(regexp);
836   //Replace HTML tags (may not work in Safari)
837   rootStrippedHTML = rootstr.replace(regexp,"$!$");
838   
839   var root_words = rootStrippedHTML.split(/[\s$!]+/);
840   
841   // there is one rootObj, each word in the root sentence a sub-obj
842   for (var i=0; i<root_words.length; i++) {
843     rootObj[i] = new Object;
844     rootObj[i].word = root_words[i];
845     rootObj[i].intensity = 0;
846     rootObj[i].annotations = new Array;
847   }
848   
849   // each selecObj[n] refers to a single selection, and then has a .words[] array
850   // that holds each word in the selection.
851   for (var i=0; i<selections.length; i++) {
852     selecObj[i] = new Object;
853     selecObj[i].selection = selections[i];
854     selecObj[i].words = new Array(selections[i].split(/[\s$!]+/));
855     selecObj[i].rtid = rtids[i];
856     // this initializes a handy "matchindex" attribute that tells you 
857     // which array index of rootObj has the first char of the match
858     // (so you can skip the irrelevant indices that precede it)
859     intense_doRoughMatch(selections,selecObj,root_words,i);
860   }
861   intense_incrWords(selecObj,rootObj);
862   returnstring = intense_genReturn(rootObj,rootHTMLArray);
863   if (olddivs) {
864     returnstring += olddivs;
865   }
866   loadHTMLtoDiv(returnstring,rootid);
867 }
868
869
870 function intense_doRoughMatch(selections,selecObj,root_words,i) {   
871   var rer = deParen(selections[i]);
872   var foo = rer.replace(/ /g,'[\\s\\$\\!]+');
873   var re = new RegExp(foo);
874   var roughMatch = deParen(rootStrippedHTML).match(re);
875   if (roughMatch) {
876     selecObj[i].matchindex = (roughMatch.index - selections[i].length);
877     var n,sum;
878     for (n=0,sum=0; sum<roughMatch.index&&n<root_words.length; n++) {
879       if (root_words[n]) {
880         sum += root_words[n].length + 1;
881       }
882     }
883     n == 0 ? selecObj[i].arrayIndex = 0 : selecObj[i].arrayIndex = n-2;
884   }
885 }
886
887
888 function intense_incrWords(selecObj,rootObj) {
889
890 // try: first match the whole selectstring against the whole
891 // rootstring, and find the index/offset of the first character of the
892 // selectstring in the rootstring.  then split the rootstring... but
893 // how do you know which array index corresponds to the offset?  do
894 // selecObj[i].length until you get to the offset?
895 // for each selection...
896  for (var j=0; j<selecObj.length; j++) { 
897    // for each word in the selection...
898    for (var k=0; k<selecObj[j].words.length; k++) {
899      // start at the object's predetermined arrayindex, and look only until you get to
900      // the arrayindex + the number of words in the selection...
901      var skip = 0;
902      for (var i=selecObj[j].arrayIndex; i<selecObj[j].words[k].length+selecObj[j].arrayIndex+2; i++) {
903        // I'm clueless as to why I need an additional [m] incrementer, but this so far produces
904        // my desired result
905        if (rootObj[i]) {
906          var m=skip;
907          var matched = '';
908          if (selecObj[j].words[k][m]) {
909            var re = new RegExp(''+deParen(selecObj[j].words[k][m])+'','m');
910            if (re) {
911              try { var thisMatch = rootObj[i].word.match(re); } catch(e) {}
912            }
913            if (thisMatch) {
914              rootObj[i].intensity++;
915              matched = 'true';
916              rootObj[i].annotations.push(selecObj[j].rtid);
917              skip++;
918            }
919            if (matched && ((m+1) == selecObj[j].words[k].length)) {
920              // ? fixme?
921              //skip++;
922            }
923          }
924        }
925      }
926    }
927  }
928 }
929 function killNoteGroup(divid) {
930   myDiv = document.getElementById(divid);
931   myDiv.parentNode.removeChild(myDiv);
932   // ugh, we can't set it to nothing, but this clutters it with hashes
933   window.location.hash = window.location.hash.replace(divid,'#');
934   if(window.location.hash.length >= 2) { window.location.hash = window.location.hash.replace(/[^^]#/g,''); }
935   if(window.location.hash.length >= 2) { window.location.hash = window.location.hash.replace(/::/g,':'); }
936   if(window.location.hash.length >= 2) { window.location.hash = window.location.hash.replace(/#:/g,'#'); }
937   if(window.location.hash.length >= 2) { window.location.hash = window.location.hash.replace(/##/g,'#'); }
938   if(window.location.hash.length >= 2) { window.location.hash = window.location.hash.replace(/\%23/g,''); }
939 }
940 function killnotes(ids) {
941   if (ids.indexOf(":") > -1) {
942     var id_arr = ids.split(':');
943   }
944   else {
945     var id_arr = new Array(ids);
946   }
947   for (var kn=0; kn<id_arr.length; kn++) {
948     cancelNote("rt"+id_arr[kn]);
949   }
950 }
951 function intense_genReturn(rootObj,rootHTMLArray) {  
952  var intensereturn = '';
953   for (i=0; i<rootObj.length; i++) {
954     if(rootObj[i].intensity > 0) {
955       intensereturn += '<span class="highlight n'+rootObj[i].annotations.join(" n")+' a'+((rootObj[i].intensity > 24) ? 24 : rootObj[i].intensity)+'" onclick="getnotes(\''+rootObj[i].annotations.join(":")+'\',\''+ticketObj[rootObj[i].annotations[0]].startnode+'\')" title="'+rootObj[i].annotations.length+' comments">'+rootObj[i].word+' </span>';
956     }
957     else { intensereturn += rootObj[i].word+' '; }
958   }
959   // thanks again, Mike Golding
960   for(r=0;intensereturn.indexOf("$!$") > -1;r++){
961     intensereturn = intensereturn.replace("$!$", rootHTMLArray[r]);
962   }
963   return intensereturn;
964 }
965
966 function getnotes(rtids,startid) {
967   currently("loading annotations on this bit...");
968   if (startid) {
969     var newDiv = document.createElement('div');
970     newDiv.setAttribute('id','loading');
971     newDiv.setAttribute('class','notegroup');
972     // IE doesn't seem to honor these things from the stylesheet unless 
973     // I declare them here too.
974     newDiv.style.left = '55%';
975     newDiv.style.position = 'absolute';
976     newDiv.style.width = '40%';
977     newDiv.innerHTML = '<span class="noteClose">Loading comments on this text...</span>';
978     document.getElementById(startid).appendChild(newDiv);
979   }
980   if((!window.location.hash.match(rtids) && (!window.location.hash.match(/^#all/)))) {
981     location.hash+=rtids+':';
982   }
983   loadXMLDoc('/comments/rt/getannotations.html','ids='+rtids);
984   currently("Ready.");
985 }
986
987 function getElementStyle(elem, IEStyleProp, CSSStyleProp) {
988   if (elem.currentStyle) {
989     if (elem.currentStyle[IEStyleProp] != '') {
990       return elem.currentStyle[IEStyleProp];
991     }
992   } else if (window.getComputedStyle) {
993     var compStyle = window.getComputedStyle(elem, "");
994     if (compStyle != '') {
995       return compStyle.getPropertyValue(CSSStyleProp);
996     }
997   }
998
999   // we haven't returned yet, so:
1000   // default to going back to yellow -- must sync with stet.css, augh, FIXME somehow
1001   if (CSSStyleProp == 'border') {
1002     return '#FFFF00';
1003   }
1004   if (CSSStyleProp == 'background') {
1005     return '#f0ecb3';
1006   }
1007   return "";
1008 }
1009
1010
1011 function notemouseover(ref,nodelist) {
1012     ref.style.prevBG=getElementStyle(ref,'background','background');
1013     ref.style.prevBRDT=getElementStyle(ref,'border-top','border-top');
1014     ref.style.prevBRDB=getElementStyle(ref,'border-bottom','border-bottom');
1015     ref.style.background="#f9f";
1016     // too slow to do with getElementsById here -- must pre-process
1017   for (i = 0; i<nodelist.length; i++) {
1018     nodelist[i].style.prevBG=getElementStyle(nodelist[i],'background','background');
1019     nodelist[i].style.prevBRDT=getElementStyle(nodelist[i],'border-top','border-top');
1020     nodelist[i].style.prevBRDB=getElementStyle(nodelist[i],'border-bottom','border-bottom');
1021     nodelist[i].style.background="#f9f";
1022   }
1023 }
1024 function notemouseout(ref,nodelist) {
1025     ref.style.background=ref.style.prevBG;
1026     ref.style['border-top']=ref.style.prevBRDT;
1027     ref.style['border-bottom']=ref.style.prevBRDB;
1028   for (i = 0; i<nodelist.length; i++) {
1029     nodelist[i].style.background=nodelist[i].style.prevBG;
1030     nodelist[i].style['border-top']=nodelist[i].style.prevBRDT;
1031     nodelist[i].style['border-bottom']=nodelist[i].style.prevBRDB;
1032   }
1033 }
1034
1035
1036 function showFull (rtid) {
1037   myCollapsed = document.getElementById('rt'+rtid+'txt');
1038   loadHTMLtoDiv(ticketObj[rtid].full+' '+ticketObj[rtid].link+' <a href="javascript:showExcerpt(\''+rtid+'\')">[-]</a>',myCollapsed.id);
1039   myCollapsed.parentNode.setAttribute('onclick','');
1040   unOverlap("annotation",5);
1041 }
1042
1043 function showExcerpt (rtid) {
1044   myFull = document.getElementById('rt'+rtid+'txt');
1045   loadHTMLtoDiv(ticketObj[rtid].excerpt+' <a href="javascript:showFull(\''+rtid+'\')">[+]</a>',myFull.id);
1046   myFull.parentNode.setAttribute('onclick','showFull(\''+rtid+'\')');
1047   unOverlap("annotation",5);
1048 }
1049
1050 function iAgree (rtid,opn) {
1051   myAgr = document.getElementById('agree'+rtid);
1052   myAgr.setAttribute('href','#');
1053   loadHTMLtoDiv(myAgr.innerHTML+'ing...',myAgr.id);
1054   loadXMLDoc('/comments/rt/agree.html','rtid='+rtid+'&amp;opn='+opn);
1055 }
1056
1057 function unOverlap(cls,gapDesired) {
1058 items = getElementsByClass(cls);
1059
1060  for (i = 0; i< (items.length - 1); i++) {
1061    iheight =parseInt(document.defaultView.getComputedStyle(items[i], '').getPropertyValue("height"));
1062    ibottom = parseInt(parseInt(items[i].offsetTop) + parseInt(iheight));
1063    items[i+1].style.top = items[i+1].offsetTop+"px";
1064    diff = parseInt(ibottom) - parseInt(items[i+1].style.top);
1065    if (diff >= 0) {
1066      items[i+1].style.top = parseInt(parseInt(items[i+1].style.top)+parseInt(diff)+parseInt(gapDesired))+"px";
1067    }
1068  }
1069 }
1070
1071 function last(obj) {
1072   return(this[obj][this.msg.length-1]);
1073 }
1074
1075 function statusbox(text) {
1076   loadHTMLtoDiv(text,'statustext');
1077   loginbox();
1078 }
1079 function currently(text) {
1080   //  loadHTMLtoDiv(text,'currently');
1081   //  alert(text);
1082 }
1083
1084 function loginbox() {
1085   if((!name) && (readCookie('__ac'))) {
1086         namepass = decodeBase64(readCookie('__ac'));
1087         var name = namepass.substr(0,namepass.indexOf(':'));
1088         loadHTMLtoDiv('you are '+name+': <a href="http://gplv3.fsf.org/logout">logout</a> <a href="http://gplv3.fsf.org/comments/source/stet-2006-03-14.tar.bz2">source</a> <a href=\"http://gplv3.fsf.org/comments/classic.html\">old interface</a><br/>\
1089 <span id="selectsome" class="selectsome">select some text</span> and <a class="fakelink" onmousedown="javascript:XpathSel()">add a comment</a> | <a href="http://gplv3.fsf.org/comments/email.html">email your comment</a>','login');
1090   }
1091   else {
1092     loadHTMLtoDiv('You need to <a href=\"http://gplv3.fsf.org/login_form?came_from='+location.pathname+'\">log in</a> to make comments. <a href=\"http://gplv3.fsf.org/comments/classic.html\">old interface</a>','login');
1093   }
1094 }
1095 function idLinks(cs) {
1096   try { var say = cs.getElementsByTagName('say')[0].firstChild; } catch(e) { }
1097     var id = cs.getElementsByTagName('ci')[0].firstChild; 
1098     var list = cs.getElementsByTagName('l')[0].firstChild;
1099     var newMe = document.createElement('span');
1100     newMe.appendChild(document.createTextNode(say.data+' '));
1101     newMe.appendChild(document.createTextNode(id.data+' '));
1102     newMe.appendChild(document.createTextNode(' '));
1103     Lnk = document.createElement('a');
1104     Lnk.setAttribute('href',list.data);
1105     Lnk.appendChild(document.createTextNode('[see thread]'));
1106     newMe.appendChild(Lnk);
1107     newMe.appendChild(document.createTextNode(' '));
1108     sLnk = document.createElement('a');
1109     sLnk.setAttribute('href','http://gplv3.fsf.org/comments/rt/changeshown.html?came_from=gplv3-draft-1');
1110     sLnk.appendChild(document.createTextNode('search'));
1111     newMe.appendChild(sLnk);
1112     st = document.getElementById('statustext');
1113     ch = st.childNodes;
1114     while (ch.length > 0) {
1115         st.removeChild(ch.item(0));
1116     }
1117     st.appendChild(newMe);
1118 }
1119
1120 // whoa clean me up asap, what a mess for nothing now.  fixme.
1121 function qLinks(cs) {
1122     var say = cs.getElementsByTagName('say')[0].firstChild;
1123     var q = cs.getElementsByTagName('q')[0].firstChild; 
1124     var r = cs.getElementsByTagName('r')[0].firstChild; 
1125     var list = cs.getElementsByTagName('l')[0].firstChild;
1126     var ra = cs.getElementsByTagName('ra')[0].firstChild; 
1127     var t =  cs.getElementsByTagName('t')[0].firstChild; 
1128     var newMe = document.createElement('span');
1129     var pr;
1130     var nx;
1131     try { pr = cs.getElementsByTagName('pr')[0].firstChild; } catch(e) {}
1132     try { nx = cs.getElementsByTagName('nx')[0].firstChild; } catch(e) {}
1133     //try { 
1134     newMe.appendChild(document.createTextNode(say.data+' ')); 
1135 //} catch(e) { alert("say "+say+", saydata "+say.data+", e "+e); }
1136     //try {
1137     newMe.appendChild(document.createTextNode(q.data)); 
1138     //} catch(e) { alert("q "+q+", qdata "+q.data+", e "+e); }
1139     newMe.appendChild(document.createTextNode(' '));
1140     rLnk = document.createElement('a');
1141     rLnk.setAttribute('href',r.data);
1142     rLnk.appendChild(document.createTextNode('[rss]'));
1143     try { newMe.appendChild(rLnk); } catch(e) { /* alert(e); */ }
1144     newMe.appendChild(document.createTextNode(' '));
1145     lLnk = document.createElement('a');
1146     lLnk.setAttribute('href',list.data);
1147     lLnk.appendChild(document.createTextNode('[list]'));
1148     newMe.appendChild(lLnk);
1149     newMe.appendChild(document.createTextNode(' '));
1150     rat = document.createElement('a');
1151     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");
1152     rat.appendChild(document.createTextNode('[rationale]'));
1153     newMe.appendChild(rat);
1154     newMe.appendChild(document.createElement('br'));
1155     newMe.appendChild(document.createTextNode('(found '+t.data+': click highlights to show.) ')); // , showing '+rng.data));
1156  
1157     sLnk = document.createElement('span');
1158     sLnk.setAttribute('id','searchlink');
1159     sLnkA = document.createElement('a');
1160     sLnkA.setAttribute('href','http://gplv3.fsf.org/comments/rt/changeshown.html?came_from=gplv3-draft-1');
1161     sLnkA.appendChild(document.createTextNode('search'));
1162     sLnk.appendChild(sLnkA)
1163     newMe.appendChild(sLnk);
1164
1165     st = document.getElementById('statustext');
1166     ch = st.childNodes;
1167     while (ch.length > 0) {
1168         st.removeChild(ch.item(0));
1169     }
1170     st.appendChild(newMe);
1171 }
1172
1173 function cstatusbox(cs) {
1174   try { 
1175     idLinks(cs);
1176   } catch (e) {
1177     qLinks(cs);
1178   }
1179   loginbox();
1180 }
1181 function deParen(str) {
1182   str = str.replace(/[\\]*\(/g,'\\(');
1183   str = str.replace(/[\\]*\)/g,'\\)');
1184   return str;
1185 }
1186
1187 function newQuery() {
1188   loadURLtoDiv('/comments/rt/changeshown.html','came_from='+document.location,'statustext');
1189 }
1190
1191 function cancelNewQuery() {
1192   //  loadHTMLtoDiv(status.last,'statustext');
1193 }
1194
1195 function kSerialize(node,attrflag) {
1196   var returnme = '';
1197   // really this should encompass any non-closed tag, but I'm not sure if I
1198   // can generalize it without just getting a whole list.
1199   if (node.nodeName.toLowerCase() == 'br') {
1200
1201     returnme += '<'+node.nodeName.toLowerCase()+'/>';
1202   }
1203   else if (node.nodeType == 1) {
1204     returnme += '<'+node.nodeName.toLowerCase();
1205     try { node.hasAttributes(); var hasAttr = 1; } catch (e) { 
1206       if (attrflag != 'noattr') { 
1207         return node+" [your browser doesn't support hasAttributes so this is funky: reload will fix]"; } 
1208     };
1209     if (!!hasAttr) {
1210       for (var attrCt = 0; attrCt < node.attributes.length; attrCt++) {
1211         returnme += ' '+node.attributes[attrCt].nodeName.toLowerCase()+'="'+node.attributes[attrCt].nodeValue+'"';
1212       }
1213     }
1214     returnme += '>';
1215     if (node.hasChildNodes()) {
1216       for (var nodeCt = 0; nodeCt < node.childNodes.length; nodeCt++) {
1217         returnme += kSerialize(node.childNodes[nodeCt]);
1218       }
1219     } 
1220     returnme += '</'+node.nodeName.toLowerCase()+'>';
1221   }
1222   else if (node.nodeType == 3) {
1223     returnme += node.data;
1224   }
1225   return returnme;
1226 }
1227
1228 // end of code by Orion Montoya
1229
1230 //* dragging behavior courtesy of brainjar.com.  License is GPL2+
1231 //*****************************************************************************
1232 // Do not remove this notice.
1233 // Copyright 2001 by Mike Hall.
1234 // See http://www.brainjar.com for terms of use.
1235 //*****************************************************************************
1236 // Determine browser and version.
1237 function Browser() {
1238   var ua, s, i;
1239   this.isIE    = false;
1240   this.isNS    = false;
1241   this.version = null;
1242   ua = navigator.userAgent;
1243   s = "MSIE";
1244   if ((i = ua.indexOf(s)) >= 0) {
1245     this.isIE = true;
1246     this.version = parseFloat(ua.substr(i + s.length));
1247     return;
1248   }
1249   s = "Netscape6/";
1250   if ((i = ua.indexOf(s)) >= 0) {
1251     this.isNS = true;
1252     this.version = parseFloat(ua.substr(i + s.length));
1253     return;
1254   }
1255   // Treat any other "Gecko" browser as NS 6.1.
1256   s = "Gecko";
1257   if ((i = ua.indexOf(s)) >= 0) {
1258     this.isNS = true;
1259     this.version = 6.1;
1260     return;
1261   }
1262 }
1263
1264 var browser = new Browser();
1265 // Global object to hold drag information.
1266 var dragObj = new Object();
1267 dragObj.zIndex = 0;
1268
1269 function dragStart(event, id) {
1270   var el;
1271   var x, y;
1272   // If an element id was given, find it. Otherwise use the element being
1273   // clicked on.
1274   if (id)
1275     dragObj.elNode = document.getElementById(id);
1276   else {
1277     if (browser.isIE)
1278       dragObj.elNode = window.event.srcElement;
1279     if (browser.isNS)
1280       dragObj.elNode = event.target;
1281
1282     // If this is a text node, use its parent element.
1283
1284     if (dragObj.elNode.nodeType == 3)
1285       dragObj.elNode = dragObj.elNode.parentNode;
1286   }
1287
1288   // Get cursor position with respect to the page.
1289
1290   if (browser.isIE) {
1291     x = window.event.clientX + document.documentElement.scrollLeft
1292       + document.body.scrollLeft;
1293     y = window.event.clientY + document.documentElement.scrollTop
1294       + document.body.scrollTop;
1295   }
1296   if (browser.isNS) {
1297     x = event.clientX + window.scrollX;
1298     y = event.clientY + window.scrollY;
1299   }
1300   // Save starting positions of cursor and element.
1301   dragObj.cursorStartX = x;
1302   dragObj.cursorStartY = y;
1303   dragObj.elStartLeft  = parseInt(dragObj.elNode.offsetLeft, 10);
1304   dragObj.elStartTop   = parseInt(dragObj.elNode.offsetTop,  10);
1305
1306   if (isNaN(dragObj.elStartLeft)) dragObj.elStartLeft = 0;
1307   if (isNaN(dragObj.elStartTop))  dragObj.elStartTop  = 0;
1308
1309   // Update element's z-index.
1310   dragObj.elNode.style.zIndex = ++dragObj.zIndex;
1311
1312   // Capture mousemove and mouseup events on the page.
1313   if (browser.isIE) {
1314     document.attachEvent("onmousemove", dragGo);
1315     document.attachEvent("onmouseup",   dragStop);
1316     window.event.cancelBubble = true;
1317     window.event.returnValue = false;
1318   }
1319   if (browser.isNS) {
1320     document.addEventListener("mousemove", dragGo,   true);
1321     document.addEventListener("mouseup",   dragStop, true);
1322     event.preventDefault();
1323   }
1324 }
1325
1326 function dragGo(event) {
1327   var x, y;
1328   // Get cursor position with respect to the page.
1329   if (browser.isIE) {
1330     x = window.event.clientX + document.documentElement.scrollLeft
1331       + document.body.scrollLeft;
1332     y = window.event.clientY + document.documentElement.scrollTop
1333       + document.body.scrollTop;
1334   }
1335   if (browser.isNS) {
1336     x = event.clientX + window.scrollX;
1337     y = event.clientY + window.scrollY;
1338   }
1339   // Move drag element by the same amount the cursor has moved.
1340 dragObj.elNode.style.left = (dragObj.elStartLeft + x - dragObj.cursorStartX) + "px";
1341 dragObj.elNode.style.top  = (dragObj.elStartTop + y - dragObj.cursorStartY) + "px";
1342   if (browser.isIE) {
1343     window.event.cancelBubble = true;
1344     window.event.returnValue = false;
1345   }
1346   if (browser.isNS)
1347     event.preventDefault();
1348 }
1349
1350 function dragStop(event) {
1351   // Stop capturing mousemove and mouseup events.
1352   if (browser.isIE) {
1353     document.detachEvent("onmousemove", dragGo);
1354     document.detachEvent("onmouseup",   dragStop);
1355   }
1356   if (browser.isNS) {
1357     document.removeEventListener("mousemove", dragGo,   true);
1358     document.removeEventListener("mouseup",   dragStop, true);
1359   }
1360 }
1361
1362 function readCookie(name)
1363 {
1364   var nameEQ = name + "=";
1365   var ca = document.cookie.split(';');
1366   for(var i=0;i<ca.length;i++)
1367     {
1368       var c = ca[i];
1369       while (c.charAt(0)==' ') c = c.substring(1,c.length);
1370       if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
1371     }
1372   return null;
1373 }
1374
1375
1376
1377 // http://ostermiller.org/calc/encode.html
1378 // Copyright Stephen Ostermiller 2003-2006
1379 // http://ostermiller.org/contact.pl?regarding=JavaScript+Encoding
1380 //
1381 // This program is free software; you can redistribute it and/or
1382 // modify it under the terms of the GNU General Public License as
1383 // published by the Free Software Foundation; either version 2 of the
1384 // License, or (at your option) any later version.
1385
1386 // This program is distributed in the hope that it will be useful, but
1387 // WITHOUT ANY WARRANTY; without even the implied warranty of
1388 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1389 // General Public License for more details.
1390 var END_OF_INPUT = -1;
1391 var base64Str;
1392 var base64Count;
1393 var base64Chars = new Array(
1394 'A','B','C','D','E','F','G','H',
1395 'I','J','K','L','M','N','O','P',
1396 'Q','R','S','T','U','V','W','X',
1397 'Y','Z','a','b','c','d','e','f',
1398 'g','h','i','j','k','l','m','n',
1399 'o','p','q','r','s','t','u','v',
1400 'w','x','y','z','0','1','2','3',
1401 '4','5','6','7','8','9','+','/'
1402 );
1403
1404 var reverseBase64Chars = new Array();
1405 for (var i=0; i < base64Chars.length; i++){
1406   reverseBase64Chars[base64Chars[i]] = i;
1407 }
1408
1409 function setBase64Str(str){
1410   base64Str = str;
1411   base64Count = 0;
1412 }
1413 function readBase64(){    
1414   if (!base64Str) return END_OF_INPUT;
1415   if (base64Count >= base64Str.length) return END_OF_INPUT;
1416   var c = base64Str.charCodeAt(base64Count) & 0xff;
1417   base64Count++;
1418   return c;
1419 }
1420
1421 // removed encodeBase64(str){ } since I don't use it
1422
1423 function readReverseBase64(){   
1424   if (!base64Str) return END_OF_INPUT;
1425   while (true){      
1426     if (base64Count >= base64Str.length) return END_OF_INPUT;
1427     var nextCharacter = base64Str.charAt(base64Count);
1428     base64Count++;
1429     if (reverseBase64Chars[nextCharacter]){
1430       return reverseBase64Chars[nextCharacter];
1431     }
1432     if (nextCharacter == 'A') return 0;
1433   }
1434   return END_OF_INPUT;
1435 }
1436
1437 function ntos(n){
1438   n=n.toString(16);
1439   if (n.length == 1) n="0"+n;
1440   n="%"+n;
1441   return unescape(n);
1442 }
1443
1444 function decodeBase64(str){
1445   setBase64Str(str);
1446   var result = "";
1447   var inBuffer = new Array(4);
1448   var done = false;
1449   while (!done && (inBuffer[0] = readReverseBase64()) != END_OF_INPUT
1450          && (inBuffer[1] = readReverseBase64()) != END_OF_INPUT){
1451     inBuffer[2] = readReverseBase64();
1452     inBuffer[3] = readReverseBase64();
1453     result += ntos((((inBuffer[0] << 2) & 0xff)| inBuffer[1] >> 4));
1454     if (inBuffer[2] != END_OF_INPUT){
1455       result +=  ntos((((inBuffer[1] << 4) & 0xff)| inBuffer[2] >> 2));
1456       if (inBuffer[3] != END_OF_INPUT){
1457         result +=  ntos((((inBuffer[2] << 6)  & 0xff) | inBuffer[3]));
1458       } else {
1459         done = true;
1460       }
1461     } else {
1462       done = true;
1463     }
1464   }
1465   return result;
1466 }