1 /**
  2  * Construct a new Browser object.
  3  * @class This class is the main interface between JBrowse and embedders
  4  * @constructor
  5  * @param params a dictionary with the following keys:<br>
  6  * <ul>
  7  * <li><code>containerID</code> - ID of the HTML element that contains the browser</li>
  8  * <li><code>refSeqs</code> - list of reference sequence information items (usually from refSeqs.js)</li>
  9  * <li><code>trackData</code> - list of track data items (usually from trackInfo.js)</li>
 10  * <li><code>dataRoot</code> - (optional) URL prefix for the data directory</li>
 11  * <li><code>browserRoot</code> - (optional) URL prefix for the browser code</li>
 12  * <li><code>tracks</code> - (optional) comma-delimited string containing initial list of tracks to view</li>
 13  * <li><code>location</code> - (optional) string describing the initial location</li>
 14  * <li><code>defaultTracks</code> - (optional) comma-delimited string containing initial list of tracks to view if there are no cookies and no "tracks" parameter</li>
 15  * <li><code>defaultLocation</code> - (optional) string describing the initial location if there are no cookies and no "location" parameter</li>
 16  * </ul>
 17  */
 18 
 19 var Browser = function(params) {
 20     dojo.require("dojo.dnd.Source");
 21     dojo.require("dojo.dnd.Moveable");
 22     dojo.require("dojo.dnd.Mover");
 23     dojo.require("dojo.dnd.move");
 24     dojo.require("dijit.layout.ContentPane");
 25     dojo.require("dijit.layout.BorderContainer");
 26 
 27     var refSeqs = params.refSeqs;
 28     var trackData = params.trackData;
 29     this.deferredFunctions = [];
 30     this.dataRoot = params.dataRoot;
 31     var dataRoot;
 32     if ("dataRoot" in params)
 33         dataRoot = params.dataRoot;
 34     else
 35         dataRoot = "";
 36 
 37     this.names = new LazyTrie(dataRoot + "names/lazy-",
 38 			      dataRoot + "names/root.json");
 39     this.tracks = [];
 40     var brwsr = this;
 41     brwsr.isInitialized = false;
 42     dojo.addOnLoad(
 43         function() {
 44             //set up top nav/overview pane and main GenomeView pane
 45             dojo.addClass(document.body, "tundra");
 46             brwsr.container = dojo.byId(params.containerID);
 47             brwsr.container.genomeBrowser = brwsr;
 48             var topPane = document.createElement("div");
 49             brwsr.container.appendChild(topPane);
 50 
 51             var overview = document.createElement("div");
 52             overview.className = "overview";
 53             overview.id = "overview";
 54             topPane.appendChild(overview);
 55             //try to come up with a good estimate of how big the location box
 56             //actually has to be
 57             var maxBase = refSeqs.reduce(function(a,b) {return a.end > b.end ? a : b;}).end;
 58             var navbox = brwsr.createNavBox(topPane, (2 * (String(maxBase).length + (((String(maxBase).length / 3) | 0) / 2))) + 2, params);
 59 
 60             var viewElem = document.createElement("div");
 61             brwsr.container.appendChild(viewElem);
 62             viewElem.className = "dragWindow";
 63 
 64             var containerWidget = new dijit.layout.BorderContainer({
 65                 liveSplitters: false,
 66                 design: "sidebar",
 67                 gutters: false
 68             }, brwsr.container);
 69             var contentWidget = new dijit.layout.ContentPane({region: "top"}, topPane);
 70             var browserWidget = new dijit.layout.ContentPane({region: "center"}, viewElem);
 71 
 72             //create location trapezoid
 73             brwsr.locationTrap = document.createElement("div");
 74             brwsr.locationTrap.className = "locationTrap";
 75             topPane.appendChild(brwsr.locationTrap);
 76             topPane.style.overflow="hidden";
 77 
 78             //set up ref seqs
 79             brwsr.allRefs = {};
 80             for (var i = 0; i < refSeqs.length; i++)
 81                 brwsr.allRefs[refSeqs[i].name] = refSeqs[i];
 82 
 83             var refCookie = dojo.cookie(params.containerID + "-refseq");
 84             brwsr.refSeq = refSeqs[0];
 85             for (var i = 0; i < refSeqs.length; i++) {
 86                 brwsr.chromList.options[i] = new Option(refSeqs[i].name,
 87                                                         refSeqs[i].name);
 88                 if (refSeqs[i].name.toUpperCase() == String(refCookie).toUpperCase()) {
 89                     brwsr.refSeq = brwsr.allRefs[refSeqs[i].name];
 90                     brwsr.chromList.selectedIndex = i;
 91                 }
 92             }
 93 
 94             dojo.connect(brwsr.chromList, "onchange", function(event) {
 95                     var oldLocMap = dojo.fromJson(dojo.cookie(brwsr.container.id + "-location")) || {};
 96                     var newRef = brwsr.allRefs[brwsr.chromList.options[brwsr.chromList.selectedIndex].value];
 97 
 98                     if (oldLocMap[newRef.name])
 99                         brwsr.navigateTo(newRef.name + ":"
100                                          + oldLocMap[newRef.name]);
101                     else
102                         brwsr.navigateTo(newRef.name + ":"
103                                          + (((newRef.start + newRef.end) * 0.4) | 0)
104                                          + " .. "
105                                          + (((newRef.start + newRef.end) * 0.6) | 0));
106                         });
107 
108             //hook up GenomeView
109             var gv = new GenomeView(viewElem, 250, brwsr.refSeq, 1/200);
110             brwsr.view = gv;
111             brwsr.viewElem = viewElem;
112             //gv.setY(0);
113             viewElem.view = gv;
114 
115             dojo.connect(browserWidget, "resize", function() {
116                     gv.sizeInit();
117 
118                     brwsr.view.locationTrapHeight = dojo.marginBox(navbox).h;
119                     gv.showFine();
120                     gv.showCoarse();
121                 });
122             brwsr.view.locationTrapHeight = dojo.marginBox(navbox).h;
123 
124             dojo.connect(gv, "onFineMove", brwsr, "onFineMove");
125             dojo.connect(gv, "onCoarseMove", brwsr, "onCoarseMove");
126 
127             //set up track list
128             var trackListDiv = brwsr.createTrackList(brwsr.container, params);
129             containerWidget.startup();
130 
131 	    brwsr.isInitialized = true;
132 
133             //set initial location
134             var oldLocMap = dojo.fromJson(dojo.cookie(brwsr.container.id + "-location")) || {};
135 
136             if (params.location) {
137                 brwsr.navigateTo(params.location);
138             } else if (oldLocMap[brwsr.refSeq.name]) {
139                 brwsr.navigateTo(brwsr.refSeq.name
140                                  + ":"
141                                  + oldLocMap[brwsr.refSeq.name]);
142             } else if (params.defaultLocation){
143                 brwsr.navigateTo(params.defaultLocation);
144             } else {
145                 brwsr.navigateTo(brwsr.refSeq.name
146                                  + ":"
147                                  + ((((brwsr.refSeq.start + brwsr.refSeq.end)
148                                       * 0.4) | 0)
149                                     + " .. "
150                                     + (((brwsr.refSeq.start + brwsr.refSeq.end)
151                                         * 0.6) | 0)));
152             }
153 
154 	    //if someone calls methods on this browser object
155 	    //before it's fully initialized, then we defer
156 	    //those functions until now
157 	    for (var i = 0; i < brwsr.deferredFunctions.length; i++)
158 		brwsr.deferredFunctions[i]();
159 	    brwsr.deferredFunctions = [];
160         });
161 };
162 
163 /**
164  * @private
165  */
166 Browser.prototype.onFineMove = function(startbp, endbp) {
167     var length = this.view.ref.end - this.view.ref.start;
168     var trapLeft = Math.round((((startbp - this.view.ref.start) / length)
169                                * this.view.overviewBox.w) + this.view.overviewBox.l);
170     var trapRight = Math.round((((endbp - this.view.ref.start) / length)
171                                 * this.view.overviewBox.w) + this.view.overviewBox.l);
172     var locationTrapStyle;
173     if (dojo.isIE) {
174         //IE apparently doesn't like borders thicker than 1024px
175         locationTrapStyle =
176             "top: " + this.view.overviewBox.t + "px;"
177             + "height: " + this.view.overviewBox.h + "px;"
178             + "left: " + trapLeft + "px;"
179             + "width: " + (trapRight - trapLeft) + "px;"
180             + "border-width: 0px";
181     } else {
182         locationTrapStyle =
183             "top: " + this.view.overviewBox.t + "px;"
184             + "height: " + this.view.overviewBox.h + "px;"
185             + "left: " + this.view.overviewBox.l + "px;"
186             + "width: " + (trapRight - trapLeft) + "px;"
187             + "border-width: " + "0px "
188             + (this.view.overviewBox.w - trapRight) + "px "
189             + this.view.locationTrapHeight + "px " + trapLeft + "px;";
190     }
191 
192     this.locationTrap.style.cssText = locationTrapStyle;
193 };
194 
195 /**
196  * @private
197  */
198 Browser.prototype.createTrackList = function(parent, params) {
199     var leftPane = document.createElement("div");
200     leftPane.style.cssText="width: 10em";
201     parent.appendChild(leftPane);
202     var leftWidget = new dijit.layout.ContentPane({region: "left", splitter: true}, leftPane);
203     var trackListDiv = document.createElement("div");
204     trackListDiv.id = "tracksAvail";
205     trackListDiv.className = "container handles";
206     trackListDiv.style.cssText = "width: 100%; height: 100%;";
207     trackListDiv.innerHTML =
208         "Available Tracks:<br/>(Drag <img src=\""
209         + (params.browserRoot ? params.browserRoot : "")
210         + "img/right_arrow.png\"/> to view)<br/><br/>";
211     leftPane.appendChild(trackListDiv);
212 
213     var brwsr = this;
214 
215     var changeCallback = function() {
216         brwsr.view.showVisibleBlocks(true);
217     };
218 
219     var trackListCreate = function(track, hint) {
220         var node = document.createElement("div");
221         node.className = "tracklist-label";
222         node.innerHTML = track.key;
223         //in the list, wrap the list item in a container for
224         //border drag-insertion-point monkeying
225         if ("avatar" != hint) {
226             var container = document.createElement("div");
227             container.className = "tracklist-container";
228             container.appendChild(node);
229             node = container;
230         }
231         node.id = dojo.dnd.getUniqueId();
232         return {node: node, data: track, type: ["track"]};
233     };
234     this.trackListWidget = new dojo.dnd.Source(trackListDiv,
235                                                {creator: trackListCreate,
236 						accept: ["track"],
237 						withHandles: false});
238 
239     var trackCreate = function(track, hint) {
240         var node;
241         if ("avatar" == hint) {
242             return trackListCreate(track, hint);
243         } else {
244             var replaceData = {refseq: brwsr.refSeq.name};
245             var url = track.url.replace(/\{([^}]+)\}/g, function(match, group) {return replaceData[group];});
246             var klass = eval(track.type);
247             var newTrack = new klass(track, url, brwsr.refSeq,
248                                      {
249                                          changeCallback: changeCallback,
250                                          trackPadding: brwsr.view.trackPadding,
251                                          baseUrl: brwsr.dataRoot,
252                                          charWidth: brwsr.view.charWidth,
253                                          seqHeight: brwsr.view.seqHeight
254                                      });
255             node = brwsr.view.addTrack(newTrack);
256         }
257         return {node: node, data: track, type: ["track"]};
258     };
259     this.viewDndWidget = new dojo.dnd.Source(this.view.container,
260                                        {
261                                            creator: trackCreate,
262                                            accept: ["track"],
263                                            withHandles: true
264                                        });
265     dojo.subscribe("/dnd/drop", function(source,nodes,iscopy){
266             var trackLabels = dojo.map(brwsr.view.trackList(),
267                                        function(track) { return track.name; });
268             dojo.cookie(brwsr.container.id + "-tracks",
269                         trackLabels.join(","),
270                         {expires: 60});
271             brwsr.view.showVisibleBlocks();
272             //multi-select too confusing?
273             //brwsr.viewDndWidget.selectNone();
274         });
275 
276     this.trackListWidget.insertNodes(false, params.trackData);
277     var oldTrackList = dojo.cookie(this.container.id + "-tracks");
278     if (params.tracks) {
279         this.showTracks(params.tracks);
280     } else if (oldTrackList) {
281         this.showTracks(oldTrackList);
282     } else if (params.defaultTracks) {
283         this.showTracks(params.defaultTracks);
284     }
285 
286     return trackListDiv;
287 };
288 
289 /**
290  * @private
291  * add new tracks to the track list
292  * @param trackList list of track information items
293  * @param replace true if this list of tracks should replace any existing
294  * tracks, false to merge with the existing list of tracks
295  */
296 
297 Browser.prototype.addTracks = function(trackList, replace) {
298     if (!this.isInitialized) {
299         var brwsr = this;
300         this.deferredFunctions.push(
301             function() {brwsr.addTracks(trackList, show); }
302         );
303 	return;
304     }
305 
306     this.tracks.concat(trackList);
307     if (show || (show === undefined)) {
308         this.showTracks(dojo.map(trackList,
309                                  function(t) {return t.label;}).join(","));
310     }
311 };
312 
313 /**
314  * navigate to a given location
315  * @example
316  * gb=dojo.byId("GenomeBrowser").genomeBrowser
317  * gb.navigateTo("ctgA:100..200")
318  * gb.navigateTo("f14")
319  * @param loc can be either:<br>
320  * <chromosome>:<start> .. <end><br>
321  * <start> .. <end><br>
322  * <center base><br>
323  * <feature name/ID>
324  */
325 Browser.prototype.navigateTo = function(loc) {
326     if (!this.isInitialized) {
327         var brwsr = this;
328         this.deferredFunctions.push(function() { brwsr.navigateTo(loc); });
329 	return;
330     }
331 
332     loc = dojo.trim(loc);
333     //                                (chromosome)    (    start      )   (  sep     )     (    end   )
334     var matches = String(loc).match(/^(((\S*)\s*:)?\s*(-?[0-9,.]*[0-9])\s*(\.\.|-|\s+))?\s*(-?[0-9,.]+)$/i);
335     //matches potentially contains location components:
336     //matches[3] = chromosome (optional)
337     //matches[4] = start base (optional)
338     //matches[6] = end base (or center base, if it's the only one)
339     if (matches) {
340 	if (matches[3]) {
341 	    var refName;
342 	    for (ref in this.allRefs) {
343 		if ((matches[3].toUpperCase() == ref.toUpperCase())
344                     ||
345                     ("CHR" + matches[3].toUpperCase() == ref.toUpperCase())
346                     ||
347                     (matches[3].toUpperCase() == "CHR" + ref.toUpperCase())) {
348 
349 		    refName = ref;
350                 }
351             }
352 	    if (refName) {
353 		dojo.cookie(this.container.id + "-refseq", refName, {expires: 60});
354 		if (refName == this.refSeq.name) {
355 		    //go to given start, end on current refSeq
356 		    this.view.setLocation(this.refSeq,
357 					  parseInt(matches[4].replace(/[,.]/g, "")),
358 					  parseInt(matches[6].replace(/[,.]/g, "")));
359 		} else {
360 		    //new refseq, record open tracks and re-open on new refseq
361                     var curTracks = [];
362                     this.viewDndWidget.forInItems(function(obj, id, map) {
363                             curTracks.push(obj.data);
364                         });
365 
366 		    for (var i = 0; i < this.chromList.options.length; i++)
367 			if (this.chromList.options[i].text == refName)
368 			    this.chromList.selectedIndex = i;
369 		    this.refSeq = this.allRefs[refName];
370 		    //go to given refseq, start, end
371 		    this.view.setLocation(this.refSeq,
372 					  parseInt(matches[4].replace(/[,.]/g, "")),
373 					  parseInt(matches[6].replace(/[,.]/g, "")));
374 
375                     this.viewDndWidget.insertNodes(false, curTracks);
376 		}
377 		return;
378 	    }
379 	} else if (matches[4]) {
380 	    //go to start, end on this refseq
381 	    this.view.setLocation(this.refSeq,
382 				  parseInt(matches[4].replace(/[,.]/g, "")),
383 				  parseInt(matches[6].replace(/[,.]/g, "")));
384 	    return;
385 	} else if (matches[6]) {
386 	    //center at given base
387 	    this.view.centerAtBase(parseInt(matches[6].replace(/[,.]/g, "")));
388 	    return;
389 	}
390     }
391     //if we get here, we didn't match any expected location format
392 
393     var brwsr = this;
394     this.names.exactMatch(loc, function(nameMatches) {
395 	    var goingTo;
396 	    //first check for exact case match
397 	    for (var i = 0; i < nameMatches.length; i++) {
398 		if (nameMatches[i][1] == loc)
399 		    goingTo = nameMatches[i];
400 	    }
401 	    //if no exact case match, try a case-insentitive match
402             if (!goingTo) {
403                 for (var i = 0; i < nameMatches.length; i++) {
404                     if (nameMatches[i][1].toLowerCase() == loc.toLowerCase())
405                         goingTo = nameMatches[i];
406                 }
407             }
408             //else just pick a match
409 	    if (!goingTo) goingTo = nameMatches[0];
410 	    var startbp = goingTo[3];
411 	    var endbp = goingTo[4];
412 	    var flank = Math.round((endbp - startbp) * .2);
413 	    //go to location, with some flanking region
414 	    brwsr.navigateTo(goingTo[2]
415 			     + ":" + (startbp - flank)
416 			     + ".." + (endbp + flank));
417 	    brwsr.showTracks(brwsr.names.extra[nameMatches[0][0]]);
418 	});
419 };
420 
421 /**
422  * load and display the given tracks
423  * @example
424  * gb=dojo.byId("GenomeBrowser").genomeBrowser
425  * gb.showTracks("DNA,gene,mRNA,noncodingRNA")
426  * @param trackNameList {String} comma-delimited string containing track names,
427  * each of which should correspond to the "label" element of the track
428  * information dictionaries
429  */
430 Browser.prototype.showTracks = function(trackNameList) {
431     if (!this.isInitialized) {
432         var brwsr = this;
433         this.deferredFunctions.push(
434             function() { brwsr.showTracks(trackNameList); }
435         );
436 	return;
437     }
438 
439     var trackNames = trackNameList.split(",");
440     var removeFromList = [];
441     var brwsr = this;
442     for (var n = 0; n < trackNames.length; n++) {
443         this.trackListWidget.forInItems(function(obj, id, map) {
444                 if (trackNames[n] == obj.data.label) {
445                     brwsr.viewDndWidget.insertNodes(false, [obj.data]);
446                     removeFromList.push(id);
447                 }
448             });
449     }
450     var movedNode;
451     for (var i = 0; i < removeFromList.length; i++) {
452         this.trackListWidget.delItem(removeFromList[i]);
453         movedNode = dojo.byId(removeFromList[i]);
454         movedNode.parentNode.removeChild(movedNode);
455     }
456 };
457 
458 /**
459  * @returns {String} string representation of the current location<br>
460  * (suitable for passing to navigateTo)
461  */
462 Browser.prototype.visibleRegion = function() {
463     return this.view.ref.name + ":" + Math.round(this.view.minVisible()) + ".." + Math.round(this.view.maxVisible());
464 };
465 
466 /**
467  * @returns {String} containing comma-separated list of currently-viewed tracks<br>
468  * (suitable for passing to showTracks)
469  */
470 Browser.prototype.visibleTracks = function() {
471     var trackLabels = dojo.map(this.view.trackList(),
472                                function(track) { return track.name; });
473     return trackLabels.join(",");
474 };
475 
476 /**
477  * @private
478  */
479 Browser.prototype.onCoarseMove = function(startbp, endbp) {
480     var length = this.view.ref.end - this.view.ref.start;
481     var trapLeft = Math.round((((startbp - this.view.ref.start) / length)
482                                * this.view.overviewBox.w) + this.view.overviewBox.l);
483     var trapRight = Math.round((((endbp - this.view.ref.start) / length)
484                                 * this.view.overviewBox.w) + this.view.overviewBox.l);
485 
486     this.view.locationThumb.style.cssText =
487     "height: " + (this.view.overviewBox.h - 4) + "px; "
488     + "left: " + trapLeft + "px; "
489     + "width: " + (trapRight - trapLeft) + "px;"
490     + "z-index: 20";
491 
492     //since this method gets triggered by the initial GenomeView.sizeInit,
493     //we don't want to save whatever location we happen to start at
494     if (! this.isInitialized) return;
495     var locString = Util.addCommas(Math.round(startbp)) + " .. " + Util.addCommas(Math.round(endbp));
496     this.locationBox.value = locString;
497     this.goButton.disabled = true;
498     this.locationBox.blur();
499     var oldLocMap = dojo.fromJson(dojo.cookie(this.container.id + "-location"));
500     if ((typeof oldLocMap) != "object") oldLocMap = {};
501     oldLocMap[this.refSeq.name] = locString;
502     dojo.cookie(this.container.id + "-location",
503                 dojo.toJson(oldLocMap),
504                 {expires: 60});
505 
506     document.title = this.refSeq.name + ":" + locString;
507 };
508 
509 /**
510  * @private
511  */
512 Browser.prototype.createNavBox = function(parent, locLength, params) {
513     var brwsr = this;
514     var navbox = document.createElement("div");
515     var browserRoot = params.browserRoot ? params.browserRoot : "";
516     navbox.id = "navbox";
517     parent.appendChild(navbox);
518     navbox.style.cssText = "text-align: center; padding: 2px; z-index: 10;";
519 
520     var moveLeft = document.createElement("input");
521     moveLeft.type = "image";
522     moveLeft.src = browserRoot + "img/slide-left.png";
523     moveLeft.id = "moveLeft";
524     moveLeft.className = "icon nav";
525     moveLeft.style.height = "40px";
526     dojo.connect(moveLeft, "click",
527                  function(event) {
528                      dojo.stopEvent(event);
529                      brwsr.view.slide(0.9);
530                  });
531     navbox.appendChild(moveLeft);
532 
533     var moveRight = document.createElement("input");
534     moveRight.type = "image";
535     moveRight.src = browserRoot + "img/slide-right.png";
536     moveRight.id="moveRight";
537     moveRight.className = "icon nav";
538     moveRight.style.height = "40px";
539     dojo.connect(moveRight, "click",
540                  function(event) {
541                      dojo.stopEvent(event);
542                      brwsr.view.slide(-0.9);
543                  });
544     navbox.appendChild(moveRight);
545 
546     navbox.appendChild(document.createTextNode("\u00a0\u00a0\u00a0\u00a0"));
547 
548     var bigZoomOut = document.createElement("input");
549     bigZoomOut.type = "image";
550     bigZoomOut.src = browserRoot + "img/zoom-out-2.png";
551     bigZoomOut.id = "bigZoomOut";
552     bigZoomOut.className = "icon nav";
553     bigZoomOut.style.height = "40px";
554     navbox.appendChild(bigZoomOut);
555     dojo.connect(bigZoomOut, "click",
556                  function(event) {
557                      dojo.stopEvent(event);
558                      brwsr.view.zoomOut(undefined, undefined, 2);
559                  });
560 
561     var zoomOut = document.createElement("input");
562     zoomOut.type = "image";
563     zoomOut.src = browserRoot + "img/zoom-out-1.png";
564     zoomOut.id = "zoomOut";
565     zoomOut.className = "icon nav";
566     zoomOut.style.height = "40px";
567     dojo.connect(zoomOut, "click",
568                  function(event) {
569                      dojo.stopEvent(event);
570                      brwsr.view.zoomOut();
571                  });
572     navbox.appendChild(zoomOut);
573 
574     var zoomIn = document.createElement("input");
575     zoomIn.type = "image";
576     zoomIn.src = browserRoot + "img/zoom-in-1.png";
577     zoomIn.id = "zoomIn";
578     zoomIn.className = "icon nav";
579     zoomIn.style.height = "40px";
580     dojo.connect(zoomIn, "click",
581                  function(event) {
582                      dojo.stopEvent(event);
583                      brwsr.view.zoomIn();
584                  });
585     navbox.appendChild(zoomIn);
586 
587     var bigZoomIn = document.createElement("input");
588     bigZoomIn.type = "image";
589     bigZoomIn.src = browserRoot + "img/zoom-in-2.png";
590     bigZoomIn.id = "bigZoomIn";
591     bigZoomIn.className = "icon nav";
592     bigZoomIn.style.height = "40px";
593     dojo.connect(bigZoomIn, "click",
594                  function(event) {
595                      dojo.stopEvent(event);
596                      brwsr.view.zoomIn(undefined, undefined, 2);
597                  });
598     navbox.appendChild(bigZoomIn);
599 
600     navbox.appendChild(document.createTextNode("\u00a0\u00a0\u00a0\u00a0"));
601     this.chromList = document.createElement("select");
602     this.chromList.id="chrom";
603     navbox.appendChild(this.chromList);
604     this.locationBox = document.createElement("input");
605     this.locationBox.size=locLength;
606     this.locationBox.type="text";
607     this.locationBox.id="location";
608     dojo.connect(this.locationBox, "keydown", function(event) {
609             if (event.keyCode == dojo.keys.ENTER) {
610                 brwsr.navigateTo(brwsr.locationBox.value);
611                 //brwsr.locationBox.blur();
612                 brwsr.goButton.disabled = true;
613                 dojo.stopEvent(event);
614             } else {
615                 brwsr.goButton.disabled = false;
616             }
617         });
618     navbox.appendChild(this.locationBox);
619 
620     this.goButton = document.createElement("button");
621     this.goButton.appendChild(document.createTextNode("Go"));
622     this.goButton.disabled = true;
623     dojo.connect(this.goButton, "click", function(event) {
624             brwsr.navigateTo(brwsr.locationBox.value);
625             //brwsr.locationBox.blur();
626             brwsr.goButton.disabled = true;
627             dojo.stopEvent(event);
628         });
629     navbox.appendChild(this.goButton);
630 
631     return navbox;
632 };
633 
634 /*
635 
636 Copyright (c) 2007-2009 The Evolutionary Software Foundation
637 
638 Created by Mitchell Skinner <mitch_skinner@berkeley.edu>
639 
640 This package and its accompanying libraries are free software; you can
641 redistribute it and/or modify it under the terms of the LGPL (either
642 version 2.1, or at your option, any later version) or the Artistic
643 License 2.0.  Refer to LICENSE for the full license text.
644 
645 */
646