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