Mercurial > repos > iuc > meme_fimo
comparison test-data/dreme_output_test1.html @ 13:4eb02864e5df draft
planemo upload for repository https://github.com/galaxyproject/tools-iuc/tree/master/tools/meme commit e2cf796f991cbe8c96e0cc5a0056b7255ac3ad6b
| author | iuc |
|---|---|
| date | Thu, 17 May 2018 14:11:00 -0400 |
| parents | |
| children | 0c5239d1aa65 |
comparison
equal
deleted
inserted
replaced
| 12:02f6ad791bbf | 13:4eb02864e5df |
|---|---|
| 1 <!DOCTYPE HTML> | |
| 2 <html> | |
| 3 <head> | |
| 4 <meta charset="UTF-8"> | |
| 5 <title>DREME</title> | |
| 6 <script> | |
| 7 // @JSON_VAR data | |
| 8 var data = { | |
| 9 "program": "dreme", | |
| 10 "version": "4.12.0", | |
| 11 "release": "Tue Jun 27 16:22:50 2017 -0700", | |
| 12 "cmd": [ | |
| 13 "dreme", "-o", "./dreme_test1_out", "-p", "dreme_test_sites.fa", | |
| 14 "-norc", "-rna", "-s", "1" | |
| 15 ], | |
| 16 "options": { | |
| 17 "revcomp": false, | |
| 18 "ngen": 100, | |
| 19 "add_pv_thresh": 0.01, | |
| 20 "seed": 1, | |
| 21 "stop": { | |
| 22 "evalue": "0.05" | |
| 23 } | |
| 24 }, | |
| 25 "alphabet": { | |
| 26 "name": "RNA", | |
| 27 "like": "rna", | |
| 28 "ncore": 4, | |
| 29 "symbols": [ | |
| 30 { | |
| 31 "symbol": "A", | |
| 32 "name": "Adenine", | |
| 33 "colour": "CC0000" | |
| 34 }, { | |
| 35 "symbol": "C", | |
| 36 "name": "Cytosine", | |
| 37 "colour": "0000CC" | |
| 38 }, { | |
| 39 "symbol": "G", | |
| 40 "name": "Guanine", | |
| 41 "colour": "FFB300" | |
| 42 }, { | |
| 43 "symbol": "U", | |
| 44 "aliases": "T", | |
| 45 "name": "Uracil", | |
| 46 "colour": "008000" | |
| 47 }, { | |
| 48 "symbol": "N", | |
| 49 "aliases": "X.", | |
| 50 "name": "Any base", | |
| 51 "equals": "ACGU" | |
| 52 }, { | |
| 53 "symbol": "V", | |
| 54 "name": "Not U", | |
| 55 "equals": "ACG" | |
| 56 }, { | |
| 57 "symbol": "H", | |
| 58 "name": "Not G", | |
| 59 "equals": "ACU" | |
| 60 }, { | |
| 61 "symbol": "D", | |
| 62 "name": "Not C", | |
| 63 "equals": "AGU" | |
| 64 }, { | |
| 65 "symbol": "B", | |
| 66 "name": "Not A", | |
| 67 "equals": "CGU" | |
| 68 }, { | |
| 69 "symbol": "M", | |
| 70 "name": "Amino", | |
| 71 "equals": "AC" | |
| 72 }, { | |
| 73 "symbol": "R", | |
| 74 "name": "Purine", | |
| 75 "equals": "AG" | |
| 76 }, { | |
| 77 "symbol": "W", | |
| 78 "name": "Weak", | |
| 79 "equals": "AU" | |
| 80 }, { | |
| 81 "symbol": "S", | |
| 82 "name": "Strong", | |
| 83 "equals": "CG" | |
| 84 }, { | |
| 85 "symbol": "Y", | |
| 86 "name": "Pyrimidine", | |
| 87 "equals": "CU" | |
| 88 }, { | |
| 89 "symbol": "K", | |
| 90 "name": "Keto", | |
| 91 "equals": "GU" | |
| 92 } | |
| 93 ] | |
| 94 }, | |
| 95 "background": { | |
| 96 "freqs": [0.221, 0.245, 0.221, 0.312] | |
| 97 }, | |
| 98 "sequence_db": { | |
| 99 "name": "dreme test sites", | |
| 100 "file": "dreme_test_sites.fa", | |
| 101 "lmod": "Thu Apr 26 15:09:03 CEST 2018", | |
| 102 "count": 1000 | |
| 103 }, | |
| 104 "control_db": { | |
| 105 "name": "shuffled positive sequences", | |
| 106 "from": "shuffled", | |
| 107 "count": 1000, | |
| 108 "freqs": [0.221, 0.245, 0.221, 0.312] | |
| 109 }, | |
| 110 "motifs": [ | |
| 111 { | |
| 112 "db": 0, | |
| 113 "id": "UUYUCY", | |
| 114 "alt": "DREME-1", | |
| 115 "len": 6, | |
| 116 "nsites": 459, | |
| 117 "evalue": "1.2e-013", | |
| 118 "p": 387, | |
| 119 "n": 210, | |
| 120 "pvalue": "2.6e-018", | |
| 121 "unerased_evalue": "1.2e-013", | |
| 122 "pwm": [ | |
| 123 [0.000000, 0.000000, 0.000000, 1.000000], | |
| 124 [0.000000, 0.000000, 0.000000, 1.000000], | |
| 125 [0.000000, 0.294118, 0.000000, 0.705882], | |
| 126 [0.000000, 0.000000, 0.000000, 1.000000], | |
| 127 [0.000000, 1.000000, 0.000000, 0.000000], | |
| 128 [0.000000, 0.474946, 0.000000, 0.525054] | |
| 129 ], | |
| 130 "matches": [ | |
| 131 { | |
| 132 "seq": "UUUUCC", | |
| 133 "p": 147, | |
| 134 "n": 75, | |
| 135 "pvalue": "1.8e-007", | |
| 136 "evalue": "8.1e-003" | |
| 137 }, { | |
| 138 "seq": "UUUUCU", | |
| 139 "p": 155, | |
| 140 "n": 94, | |
| 141 "pvalue": "2.2e-005", | |
| 142 "evalue": "1.0e+000" | |
| 143 }, { | |
| 144 "seq": "UUCUCU", | |
| 145 "p": 94, | |
| 146 "n": 51, | |
| 147 "pvalue": "1.3e-004", | |
| 148 "evalue": "6.1e+000" | |
| 149 }, { | |
| 150 "seq": "UUCUCC", | |
| 151 "p": 75, | |
| 152 "n": 42, | |
| 153 "pvalue": "1.1e-003", | |
| 154 "evalue": "5.0e+001" | |
| 155 } | |
| 156 ] | |
| 157 }, { | |
| 158 "db": 0, | |
| 159 "id": "YAGG", | |
| 160 "alt": "DREME-2", | |
| 161 "len": 4, | |
| 162 "nsites": 793, | |
| 163 "evalue": "5.1e-012", | |
| 164 "p": 600, | |
| 165 "n": 416, | |
| 166 "pvalue": "1.1e-016", | |
| 167 "unerased_evalue": "2.4e-012", | |
| 168 "pwm": [ | |
| 169 [0.000000, 0.692308, 0.000000, 0.307692], | |
| 170 [1.000000, 0.000000, 0.000000, 0.000000], | |
| 171 [0.000000, 0.000000, 1.000000, 0.000000], | |
| 172 [0.000000, 0.000000, 1.000000, 0.000000] | |
| 173 ], | |
| 174 "matches": [ | |
| 175 { | |
| 176 "seq": "CAGG", | |
| 177 "p": 441, | |
| 178 "n": 304, | |
| 179 "pvalue": "1.5e-010", | |
| 180 "evalue": "6.6e-006" | |
| 181 }, { | |
| 182 "seq": "UAGG", | |
| 183 "p": 232, | |
| 184 "n": 165, | |
| 185 "pvalue": "1.1e-004", | |
| 186 "evalue": "4.7e+000" | |
| 187 } | |
| 188 ] | |
| 189 }, { | |
| 190 "db": 0, | |
| 191 "id": "GAAGAW", | |
| 192 "alt": "DREME-3", | |
| 193 "len": 6, | |
| 194 "nsites": 89, | |
| 195 "evalue": "3.4e-005", | |
| 196 "p": 81, | |
| 197 "n": 22, | |
| 198 "pvalue": "8.2e-010", | |
| 199 "unerased_evalue": "3.5e-004", | |
| 200 "pwm": [ | |
| 201 [0.000000, 0.000000, 1.000000, 0.000000], | |
| 202 [1.000000, 0.000000, 0.000000, 0.000000], | |
| 203 [1.000000, 0.000000, 0.000000, 0.000000], | |
| 204 [0.000000, 0.000000, 1.000000, 0.000000], | |
| 205 [1.000000, 0.000000, 0.000000, 0.000000], | |
| 206 [0.494382, 0.000000, 0.000000, 0.505618] | |
| 207 ], | |
| 208 "matches": [ | |
| 209 { | |
| 210 "seq": "GAAGAU", | |
| 211 "p": 45, | |
| 212 "n": 7, | |
| 213 "pvalue": "2.4e-008", | |
| 214 "evalue": "9.9e-004" | |
| 215 }, { | |
| 216 "seq": "GAAGAA", | |
| 217 "p": 40, | |
| 218 "n": 16, | |
| 219 "pvalue": "7.9e-004", | |
| 220 "evalue": "3.3e+001" | |
| 221 } | |
| 222 ] | |
| 223 }, { | |
| 224 "db": 0, | |
| 225 "id": "SMUGGA", | |
| 226 "alt": "DREME-4", | |
| 227 "len": 6, | |
| 228 "nsites": 119, | |
| 229 "evalue": "3.7e-003", | |
| 230 "p": 110, | |
| 231 "n": 47, | |
| 232 "pvalue": "9.1e-008", | |
| 233 "unerased_evalue": "2.6e-005", | |
| 234 "pwm": [ | |
| 235 [0.000000, 0.529412, 0.470588, 0.000000], | |
| 236 [0.428571, 0.571429, 0.000000, 0.000000], | |
| 237 [0.000000, 0.000000, 0.000000, 1.000000], | |
| 238 [0.000000, 0.000000, 1.000000, 0.000000], | |
| 239 [0.000000, 0.000000, 1.000000, 0.000000], | |
| 240 [1.000000, 0.000000, 0.000000, 0.000000] | |
| 241 ], | |
| 242 "matches": [ | |
| 243 { | |
| 244 "seq": "GAUGGA", | |
| 245 "p": 22, | |
| 246 "n": 6, | |
| 247 "pvalue": "1.7e-003", | |
| 248 "evalue": "7.1e+001" | |
| 249 }, { | |
| 250 "seq": "GCUGGA", | |
| 251 "p": 33, | |
| 252 "n": 14, | |
| 253 "pvalue": "3.6e-003", | |
| 254 "evalue": "1.5e+002" | |
| 255 }, { | |
| 256 "seq": "CCUGGA", | |
| 257 "p": 32, | |
| 258 "n": 15, | |
| 259 "pvalue": "8.6e-003", | |
| 260 "evalue": "3.5e+002" | |
| 261 }, { | |
| 262 "seq": "CAUGGA", | |
| 263 "p": 29, | |
| 264 "n": 13, | |
| 265 "pvalue": "9.1e-003", | |
| 266 "evalue": "3.7e+002" | |
| 267 } | |
| 268 ] | |
| 269 } | |
| 270 ], | |
| 271 "runtime": { | |
| 272 "host": "ThinkPad-T450s", | |
| 273 "when": "Thu May 03 13:22:29 CEST 2018", | |
| 274 "cpu": 13.95, | |
| 275 "real": 13.95, | |
| 276 "stop": "evalue" | |
| 277 } | |
| 278 }; | |
| 279 </script> | |
| 280 <script> | |
| 281 var site_url = "http://meme-suite.org"; | |
| 282 </script> | |
| 283 <script> | |
| 284 | |
| 285 /* | |
| 286 * $ | |
| 287 * | |
| 288 * Shorthand function for getElementById | |
| 289 */ | |
| 290 function $(el) { | |
| 291 return document.getElementById(el); | |
| 292 } | |
| 293 | |
| 294 | |
| 295 /* | |
| 296 * See http://stackoverflow.com/a/5450113/66387 | |
| 297 * Does string multiplication like the perl x operator. | |
| 298 */ | |
| 299 function string_mult(pattern, count) { | |
| 300 if (count < 1) return ''; | |
| 301 var result = ''; | |
| 302 while (count > 1) { | |
| 303 if (count & 1) result += pattern; | |
| 304 count >>= 1, pattern += pattern; | |
| 305 } | |
| 306 return result + pattern; | |
| 307 } | |
| 308 | |
| 309 /* | |
| 310 * See http://stackoverflow.com/questions/814613/how-to-read-get-data-from-a-url-using-javascript | |
| 311 * Slightly modified with information from | |
| 312 * https://developer.mozilla.org/en/DOM/window.location | |
| 313 */ | |
| 314 function parse_params() { | |
| 315 "use strict"; | |
| 316 var search, queryStart, queryEnd, query, params, nvPairs, i, nv, n, v; | |
| 317 search = window.location.search; | |
| 318 queryStart = search.indexOf("?") + 1; | |
| 319 queryEnd = search.indexOf("#") + 1 || search.length + 1; | |
| 320 query = search.slice(queryStart, queryEnd - 1); | |
| 321 | |
| 322 if (query === search || query === "") return {}; | |
| 323 | |
| 324 params = {}; | |
| 325 nvPairs = query.replace(/\+/g, " ").split("&"); | |
| 326 | |
| 327 for (i = 0; i < nvPairs.length; i++) { | |
| 328 nv = nvPairs[i].split("="); | |
| 329 n = decodeURIComponent(nv[0]); | |
| 330 v = decodeURIComponent(nv[1]); | |
| 331 // allow a name to be used multiple times | |
| 332 // storing each value in the array | |
| 333 if (!(n in params)) { | |
| 334 params[n] = []; | |
| 335 } | |
| 336 params[n].push(nv.length === 2 ? v : null); | |
| 337 } | |
| 338 return params; | |
| 339 } | |
| 340 | |
| 341 /* | |
| 342 * coords | |
| 343 * | |
| 344 * Calculates the x and y offset of an element. | |
| 345 * From http://www.quirksmode.org/js/findpos.html | |
| 346 * with alterations to take into account scrolling regions | |
| 347 */ | |
| 348 function coords(elem) { | |
| 349 var myX = myY = 0; | |
| 350 if (elem.getBoundingClientRect) { | |
| 351 var rect; | |
| 352 rect = elem.getBoundingClientRect(); | |
| 353 myX = rect.left + ((typeof window.pageXOffset !== "undefined") ? | |
| 354 window.pageXOffset : document.body.scrollLeft); | |
| 355 myY = rect.top + ((typeof window.pageYOffset !== "undefined") ? | |
| 356 window.pageYOffset : document.body.scrollTop); | |
| 357 } else { | |
| 358 // this fall back doesn't properly handle absolutely positioned elements | |
| 359 // inside a scrollable box | |
| 360 var node; | |
| 361 if (elem.offsetParent) { | |
| 362 // subtract all scrolling | |
| 363 node = elem; | |
| 364 do { | |
| 365 myX -= node.scrollLeft ? node.scrollLeft : 0; | |
| 366 myY -= node.scrollTop ? node.scrollTop : 0; | |
| 367 } while (node = node.parentNode); | |
| 368 // this will include the page scrolling (which is unwanted) so add it back on | |
| 369 myX += (typeof window.pageXOffset !== "undefined") ? window.pageXOffset : document.body.scrollLeft; | |
| 370 myY += (typeof window.pageYOffset !== "undefined") ? window.pageYOffset : document.body.scrollTop; | |
| 371 // sum up offsets | |
| 372 node = elem; | |
| 373 do { | |
| 374 myX += node.offsetLeft; | |
| 375 myY += node.offsetTop; | |
| 376 } while (node = node.offsetParent); | |
| 377 } | |
| 378 } | |
| 379 return [myX, myY]; | |
| 380 } | |
| 381 | |
| 382 /* | |
| 383 * position_popup | |
| 384 * | |
| 385 * Positions a popup relative to an anchor element. | |
| 386 * | |
| 387 * The avaliable positions are: | |
| 388 * 0 - Centered below the anchor. | |
| 389 */ | |
| 390 function position_popup(anchor, popup, position) { | |
| 391 "use strict"; | |
| 392 var a_x, a_y, a_w, a_h, p_x, p_y, p_w, p_h; | |
| 393 var a_xy, spacer, margin, scrollbar, page_w; | |
| 394 // define constants | |
| 395 spacer = 5; | |
| 396 margin = 15; | |
| 397 scrollbar = 15; | |
| 398 // define the positions and widths | |
| 399 a_xy = coords(anchor); | |
| 400 a_x = a_xy[0]; | |
| 401 a_y = a_xy[1]; | |
| 402 a_w = anchor.offsetWidth; | |
| 403 a_h = anchor.offsetHeight; | |
| 404 p_w = popup.offsetWidth; | |
| 405 p_h = popup.offsetHeight; | |
| 406 page_w = null; | |
| 407 if (window.innerWidth) { | |
| 408 page_w = window.innerWidth; | |
| 409 } else if (document.body) { | |
| 410 page_w = document.body.clientWidth; | |
| 411 } | |
| 412 // check the position type is defined | |
| 413 if (typeof position !== "number") { | |
| 414 position = 0; | |
| 415 } | |
| 416 // calculate the popup position | |
| 417 switch (position) { | |
| 418 case 1: | |
| 419 p_x = a_x + a_w + spacer; | |
| 420 p_y = a_y + (a_h / 2) - (p_h / 2); | |
| 421 break; | |
| 422 case 0: | |
| 423 default: | |
| 424 p_x = a_x + (a_w / 2) - (p_w / 2); | |
| 425 p_y = a_y + a_h + spacer; | |
| 426 break; | |
| 427 } | |
| 428 // constrain the popup position | |
| 429 if (p_x < margin) { | |
| 430 p_x = margin; | |
| 431 } else if (page_w != null && (p_x + p_w) > (page_w - margin - scrollbar)) { | |
| 432 p_x = page_w - margin - scrollbar - p_w; | |
| 433 } | |
| 434 if (p_y < margin) { | |
| 435 p_y = margin; | |
| 436 } | |
| 437 // position the popup | |
| 438 popup.style.left = p_x + "px"; | |
| 439 popup.style.top = p_y + "px"; | |
| 440 } | |
| 441 | |
| 442 function lookup_help_popup(popup_id) { | |
| 443 var _body, pop, info; | |
| 444 pop = document.getElementById(popup_id); | |
| 445 if (pop == null) { | |
| 446 _body = document.getElementsByTagName("body")[0]; | |
| 447 pop = document.createElement("div"); | |
| 448 pop.className = "pop_content"; | |
| 449 pop.id = popup_id; | |
| 450 pop.style.backgroundColor = "#FFC"; | |
| 451 pop.style.borderColor = "black"; | |
| 452 info = document.createElement("p"); | |
| 453 info.style.fontWeight = "bold"; | |
| 454 info.appendChild(document.createTextNode("Error: No popup for topic \"" + popup_id + "\".")); | |
| 455 pop.appendChild(info); | |
| 456 // this might cause problems with the menu, but as this only happens | |
| 457 // when something is already wrong I don't think that's too much of a problem | |
| 458 _body.insertBefore(pop, _body.firstChild); | |
| 459 } | |
| 460 if (document.getElementsByTagName('body')[0].hasAttribute("data-autobtns")) { | |
| 461 if (!/\bauto_buttons\b/.test(pop.className)) { | |
| 462 pop.className += " auto_buttons"; | |
| 463 var back_btn_sec = document.createElement("div"); | |
| 464 back_btn_sec.className = "nested_only pop_back_sec"; | |
| 465 var back_btn = document.createElement("span"); | |
| 466 back_btn.className = "pop_back"; | |
| 467 back_btn.appendChild(document.createTextNode("<< back")); | |
| 468 back_btn.addEventListener("click", function(e) { | |
| 469 help_return(); | |
| 470 }, false); | |
| 471 back_btn_sec.appendChild(back_btn); | |
| 472 pop.insertBefore(back_btn_sec, pop.firstChild); | |
| 473 var close_btn_sec = document.createElement("div"); | |
| 474 close_btn_sec.className = "pop_close_sec"; | |
| 475 var close_btn = document.createElement("span"); | |
| 476 close_btn.className = "pop_close"; | |
| 477 close_btn.appendChild(document.createTextNode("close")); | |
| 478 close_btn.addEventListener("click", function(e) { | |
| 479 help_popup(); | |
| 480 }, false); | |
| 481 close_btn_sec.appendChild(close_btn); | |
| 482 pop.appendChild(close_btn_sec); | |
| 483 } | |
| 484 } | |
| 485 return pop; | |
| 486 } | |
| 487 | |
| 488 /* | |
| 489 * help_popup | |
| 490 * | |
| 491 * Moves around help pop-ups so they appear | |
| 492 * below an activator. | |
| 493 */ | |
| 494 function help_popup(activator, popup_id) { | |
| 495 "use strict"; | |
| 496 var pop; | |
| 497 // set default values | |
| 498 if (typeof help_popup.popup === "undefined") { | |
| 499 help_popup.popup = []; | |
| 500 } | |
| 501 if (typeof help_popup.activator === "undefined") { | |
| 502 help_popup.activator = null; | |
| 503 } | |
| 504 var last_pop = (help_popup.popup.length > 0 ? help_popup.popup[help_popup.popup.length - 1] : null); | |
| 505 if (typeof(activator) == "undefined") { // no activator so hide | |
| 506 if (last_pop != null) { | |
| 507 last_pop.style.display = 'none'; | |
| 508 help_popup.popup = []; | |
| 509 } | |
| 510 return; | |
| 511 } | |
| 512 pop = lookup_help_popup(popup_id); | |
| 513 if (pop == last_pop) { | |
| 514 if (activator == help_popup.activator) { | |
| 515 //hide popup (as we've already shown it for the current help button) | |
| 516 last_pop.style.display = 'none'; | |
| 517 help_popup.popup = []; | |
| 518 return; // toggling complete! | |
| 519 } | |
| 520 } else if (last_pop != null) { | |
| 521 //activating different popup so hide current one | |
| 522 last_pop.style.display = 'none'; | |
| 523 } | |
| 524 help_popup.popup = [pop]; | |
| 525 help_popup.activator = activator; | |
| 526 toggle_class(pop, "nested", false); | |
| 527 //must make the popup visible to measure it or it has zero width | |
| 528 pop.style.display = 'block'; | |
| 529 position_popup(activator, pop); | |
| 530 } | |
| 531 | |
| 532 /* | |
| 533 * help_refine | |
| 534 * | |
| 535 * Intended for links within a help popup. Stores a stack of state so | |
| 536 * you can go back. | |
| 537 */ | |
| 538 function help_refine(popup_id) { | |
| 539 if (help_popup.popup == null || help_popup.popup.length == 0 || help_popup.activator == null) { | |
| 540 throw new Error("Can not refine a help popup when one is not shown!"); | |
| 541 } | |
| 542 var pop = lookup_help_popup(popup_id); | |
| 543 var last_pop = help_popup.popup[help_popup.popup.length - 1]; | |
| 544 if (pop == last_pop) return; // slightly odd, but no real cause for alarm | |
| 545 help_popup.popup.push(pop); | |
| 546 toggle_class(pop, "nested", true); | |
| 547 last_pop.style.display = "none"; | |
| 548 //must make the popup visible to measure it or it has zero width | |
| 549 pop.style.display = "block"; | |
| 550 position_popup(help_popup.activator, pop); | |
| 551 } | |
| 552 | |
| 553 /* | |
| 554 * help_return | |
| 555 * | |
| 556 * Intended for links within a help popup. Stores a stack of state so | |
| 557 * you can go back. | |
| 558 */ | |
| 559 function help_return() { | |
| 560 if (help_popup.popup == null || help_popup.popup.length == 0 || help_popup.activator == null) { | |
| 561 throw new Error("Can not return to a earlier help popup when one is not shown!"); | |
| 562 } | |
| 563 var last_pop = help_popup.popup.pop(); | |
| 564 last_pop.style.display = "none"; | |
| 565 var pop = (help_popup.popup.length > 0 ? help_popup.popup[help_popup.popup.length - 1] : null); | |
| 566 if (pop != null) { | |
| 567 toggle_class(pop, "nested", help_popup.popup.length > 1); | |
| 568 pop.style.display = "block"; | |
| 569 position_popup(help_popup.activator, pop); | |
| 570 } else { | |
| 571 help_popup.activator = null; | |
| 572 } | |
| 573 } | |
| 574 | |
| 575 /* | |
| 576 * update_scroll_pad | |
| 577 * | |
| 578 * Creates padding at the bottom of the page to allow | |
| 579 * scrolling of anything into view. | |
| 580 */ | |
| 581 function update_scroll_pad() { | |
| 582 var page, pad; | |
| 583 page = (document.compatMode === "CSS1Compat") ? document.documentElement : document.body; | |
| 584 pad = $("scrollpad"); | |
| 585 if (pad === null) { | |
| 586 pad = document.createElement("div"); | |
| 587 pad.id = 'scrollpad'; | |
| 588 document.getElementsByTagName('body')[0].appendChild(pad); | |
| 589 } | |
| 590 pad.style.height = Math.abs(page.clientHeight - 100) + "px"; | |
| 591 } | |
| 592 | |
| 593 function substitute_classes(node, remove, add) { | |
| 594 "use strict"; | |
| 595 var list, all, i, cls, classes; | |
| 596 list = node.className.split(/\s+/); | |
| 597 all = {}; | |
| 598 for (i = 0; i < list.length; i++) { | |
| 599 if (list[i].length > 0) all[list[i]] = true; | |
| 600 } | |
| 601 for (i = 0; i < remove.length; i++) { | |
| 602 if (all.hasOwnProperty(remove[i])) { | |
| 603 delete all[remove[i]]; | |
| 604 } | |
| 605 } | |
| 606 for (i = 0; i < add.length; i++) { | |
| 607 all[add[i]] = true; | |
| 608 } | |
| 609 classes = ""; | |
| 610 for (cls in all) { | |
| 611 classes += cls + " "; | |
| 612 } | |
| 613 node.className = classes; | |
| 614 } | |
| 615 | |
| 616 /* | |
| 617 * toggle_class | |
| 618 * | |
| 619 * Adds or removes a class from the node. If the parameter 'enabled' is not | |
| 620 * passed then the existence of the class will be toggled, otherwise it will be | |
| 621 * included if enabled is true. | |
| 622 */ | |
| 623 function toggle_class(node, cls, enabled) { | |
| 624 var classes = node.className; | |
| 625 var list = classes.replace(/^\s+/, '').replace(/\s+$/, '').split(/\s+/); | |
| 626 var found = false; | |
| 627 for (var i = 0; i < list.length; i++) { | |
| 628 if (list[i] == cls) { | |
| 629 list.splice(i, 1); | |
| 630 i--; | |
| 631 found = true; | |
| 632 } | |
| 633 } | |
| 634 if (typeof enabled == "undefined") { | |
| 635 if (!found) list.push(cls); | |
| 636 } else { | |
| 637 if (enabled) list.push(cls); | |
| 638 } | |
| 639 node.className = list.join(" "); | |
| 640 } | |
| 641 | |
| 642 /* | |
| 643 * find_child | |
| 644 * | |
| 645 * Searches child nodes in depth first order and returns the first it finds | |
| 646 * with the className specified. | |
| 647 * TODO replace with querySelector | |
| 648 */ | |
| 649 function find_child(node, className) { | |
| 650 var pattern; | |
| 651 if (node == null || typeof node !== "object") { | |
| 652 return null; | |
| 653 } | |
| 654 if (typeof className === "string") { | |
| 655 pattern = new RegExp("\\b" + className + "\\b"); | |
| 656 } else { | |
| 657 pattern = className; | |
| 658 } | |
| 659 if (node.nodeType == Node.ELEMENT_NODE && | |
| 660 pattern.test(node.className)) { | |
| 661 return node; | |
| 662 } else { | |
| 663 var result = null; | |
| 664 for (var i = 0; i < node.childNodes.length; i++) { | |
| 665 result = find_child(node.childNodes[i], pattern); | |
| 666 if (result != null) break; | |
| 667 } | |
| 668 return result; | |
| 669 } | |
| 670 } | |
| 671 | |
| 672 /* | |
| 673 * find_parent | |
| 674 * | |
| 675 * Searches parent nodes outwards from the node and returns the first it finds | |
| 676 * with the className specified. | |
| 677 */ | |
| 678 function find_parent(node, className) { | |
| 679 var pattern; | |
| 680 pattern = new RegExp("\\b" + className + "\\b"); | |
| 681 do { | |
| 682 if (node.nodeType == Node.ELEMENT_NODE && | |
| 683 pattern.test(node.className)) { | |
| 684 return node; | |
| 685 } | |
| 686 } while (node = node.parentNode); | |
| 687 return null; | |
| 688 } | |
| 689 | |
| 690 /* | |
| 691 * find_parent_tag | |
| 692 * | |
| 693 * Searches parent nodes outwards from the node and returns the first it finds | |
| 694 * with the tag name specified. HTML tags should be specified in upper case. | |
| 695 */ | |
| 696 function find_parent_tag(node, tag_name) { | |
| 697 do { | |
| 698 if (node.nodeType == Node.ELEMENT_NODE && node.tagName == tag_name) { | |
| 699 return node; | |
| 700 } | |
| 701 } while (node = node.parentNode); | |
| 702 return null; | |
| 703 } | |
| 704 | |
| 705 /* | |
| 706 * __toggle_help | |
| 707 * | |
| 708 * Uses the 'topic' property of the this object to | |
| 709 * toggle display of a help topic. | |
| 710 * | |
| 711 * This function is not intended to be called directly. | |
| 712 */ | |
| 713 function __toggle_help(e) { | |
| 714 if (!e) e = window.event; | |
| 715 if (e.type === "keydown") { | |
| 716 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
| 717 return; | |
| 718 } | |
| 719 // stop a submit or something like that | |
| 720 e.preventDefault(); | |
| 721 } | |
| 722 | |
| 723 help_popup(this, this.getAttribute("data-topic")); | |
| 724 } | |
| 725 | |
| 726 function setup_help_button(button) { | |
| 727 "use strict"; | |
| 728 var topic; | |
| 729 if (button.hasAttribute("data-topic")) { | |
| 730 topic = button.getAttribute("data-topic"); | |
| 731 if (document.getElementById(topic) != null) { | |
| 732 button.tabIndex = "0"; // make keyboard selectable | |
| 733 button.addEventListener("click", function() { | |
| 734 help_popup(button, topic); | |
| 735 }, false); | |
| 736 button.addEventListener("keydown", function(e) { | |
| 737 // toggle only on Enter or Spacebar, let other keys do their thing | |
| 738 if (e.keyCode !== 13 && e.keyCode !== 32) return; | |
| 739 // stop a submit or something like that | |
| 740 e.preventDefault(); | |
| 741 help_popup(button, topic); | |
| 742 }, false); | |
| 743 } else { | |
| 744 button.style.visibility = "hidden"; | |
| 745 } | |
| 746 } | |
| 747 button.className += " active"; | |
| 748 } | |
| 749 | |
| 750 /* | |
| 751 * help_button | |
| 752 * | |
| 753 * Makes a help button for the passed topic. | |
| 754 */ | |
| 755 function help_button(topic) { | |
| 756 var btn = document.createElement("div"); | |
| 757 btn.className = "help"; | |
| 758 btn.setAttribute("data-topic", topic); | |
| 759 setup_help_button(btn); | |
| 760 return btn; | |
| 761 } | |
| 762 | |
| 763 /* | |
| 764 * prepare_download | |
| 765 * | |
| 766 * Sets the attributes of a link to setup a file download using the given content. | |
| 767 * If no link is provided then create one and click it. | |
| 768 */ | |
| 769 function prepare_download(content, mimetype, filename, link) { | |
| 770 "use strict"; | |
| 771 // if no link is provided then create one and click it | |
| 772 var click_link = false; | |
| 773 if (!link) { | |
| 774 link = document.createElement("a"); | |
| 775 click_link = true; | |
| 776 } | |
| 777 try { | |
| 778 // Use a BLOB to convert the text into a data URL. | |
| 779 // We could do this manually with a base 64 conversion. | |
| 780 // This will only be supported on modern browsers, | |
| 781 // hence the try block. | |
| 782 var blob = new Blob([content], {type: mimetype}); | |
| 783 var reader = new FileReader(); | |
| 784 reader.onloadend = function() { | |
| 785 // If we're lucky the browser will also support the download | |
| 786 // attribute which will let us suggest a file name to save the link. | |
| 787 // Otherwise it is likely that the filename will be unintelligible. | |
| 788 link.setAttribute("download", filename); | |
| 789 link.href = reader.result; | |
| 790 if (click_link) { | |
| 791 // must add the link to click it | |
| 792 document.body.appendChild(link); | |
| 793 link.click(); | |
| 794 document.body.removeChild(link); | |
| 795 } | |
| 796 } | |
| 797 reader.readAsDataURL(blob); | |
| 798 } catch (error) { | |
| 799 if (console && console.log) console.log(error); | |
| 800 // probably an old browser | |
| 801 link.href = ""; | |
| 802 link.visible = false; | |
| 803 } | |
| 804 } | |
| 805 | |
| 806 /* | |
| 807 * add_cell | |
| 808 * | |
| 809 * Add a cell to the table row. | |
| 810 */ | |
| 811 function add_cell(row, node, cls, click_action) { | |
| 812 var cell = row.insertCell(row.cells.length); | |
| 813 if (node) cell.appendChild(node); | |
| 814 if (cls && cls !== "") cell.className = cls; | |
| 815 if (click_action) cell.addEventListener("click", click_action, false); | |
| 816 } | |
| 817 | |
| 818 /* | |
| 819 * add_header_cell | |
| 820 * | |
| 821 * Add a header cell to the table row. | |
| 822 */ | |
| 823 function add_header_cell(row, node, help_topic, cls, colspan) { | |
| 824 var th = document.createElement("th"); | |
| 825 if (node) th.appendChild(node); | |
| 826 if (help_topic && help_topic !== "") th.appendChild(help_button(help_topic)); | |
| 827 if (cls && cls !== "") th.className = cls; | |
| 828 if (typeof colspan == "number" && colspan > 1) th.colSpan = colspan; | |
| 829 row.appendChild(th); | |
| 830 } | |
| 831 | |
| 832 /* | |
| 833 * add_text_cell | |
| 834 * | |
| 835 * Add a text cell to the table row. | |
| 836 */ | |
| 837 function add_text_cell(row, text, cls, action) { | |
| 838 var node = null; | |
| 839 if (typeof(text) != 'undefined') node = document.createTextNode(text); | |
| 840 add_cell(row, node, cls, action); | |
| 841 } | |
| 842 | |
| 843 /* | |
| 844 * add_text_header_cell | |
| 845 * | |
| 846 * Add a text header cell to the table row. | |
| 847 */ | |
| 848 function add_text_header_cell(row, text, help_topic, cls, action, colspan) { | |
| 849 var node = null; | |
| 850 if (typeof(text) != 'undefined') { | |
| 851 var nbsp = (help_topic ? "\u00A0" : ""); | |
| 852 var str = "" + text; | |
| 853 var parts = str.split(/\n/); | |
| 854 if (parts.length === 1) { | |
| 855 if (action) { | |
| 856 node = document.createElement("span"); | |
| 857 node.appendChild(document.createTextNode(str + nbsp)); | |
| 858 } else { | |
| 859 node = document.createTextNode(str + nbsp); | |
| 860 } | |
| 861 } else { | |
| 862 node = document.createElement("span"); | |
| 863 for (var i = 0; i < parts.length; i++) { | |
| 864 if (i !== 0) { | |
| 865 node.appendChild(document.createElement("br")); | |
| 866 } | |
| 867 node.appendChild(document.createTextNode(parts[i])); | |
| 868 } | |
| 869 } | |
| 870 if (action) { | |
| 871 node.addEventListener("click", action, false); | |
| 872 node.style.cursor = "pointer"; | |
| 873 } | |
| 874 } | |
| 875 add_header_cell(row, node, help_topic, cls, colspan); | |
| 876 } | |
| 877 | |
| 878 function setup_help() { | |
| 879 "use strict"; | |
| 880 var help_buttons, i; | |
| 881 help_buttons = document.querySelectorAll(".help:not(.active)"); | |
| 882 for (i = 0; i < help_buttons.length; i++) { | |
| 883 setup_help_button(help_buttons[i]); | |
| 884 } | |
| 885 } | |
| 886 | |
| 887 function setup_scrollpad() { | |
| 888 "use strict"; | |
| 889 if (document.getElementsByTagName('body')[0].hasAttribute("data-scrollpad") && document.getElementById("scrollpad") == null) { | |
| 890 window.addEventListener("resize", update_scroll_pad, false); | |
| 891 update_scroll_pad(); | |
| 892 } | |
| 893 } | |
| 894 | |
| 895 // anon function to avoid polluting global scope | |
| 896 (function() { | |
| 897 "use strict"; | |
| 898 window.addEventListener("load", function load(evt) { | |
| 899 window.removeEventListener("load", load, false); | |
| 900 setup_help(); | |
| 901 setup_scrollpad(); | |
| 902 }, false); | |
| 903 })(); | |
| 904 | |
| 905 /* | |
| 906 * make_link | |
| 907 * | |
| 908 * Creates a text node and if a URL is specified it surrounds it with a link. | |
| 909 * If the URL doesn't begin with "http://" it automatically adds it, as | |
| 910 * relative links don't make much sense in this context. | |
| 911 */ | |
| 912 function make_link(text, url) { | |
| 913 var textNode = null; | |
| 914 var link = null; | |
| 915 if (typeof text !== "undefined" && text !== null) textNode = document.createTextNode(text); | |
| 916 if (typeof url === "string") { | |
| 917 if (url.indexOf("//") == -1) { | |
| 918 url = "http://" + url; | |
| 919 } | |
| 920 link = document.createElement('a'); | |
| 921 link.href = url; | |
| 922 if (textNode) link.appendChild(textNode); | |
| 923 return link; | |
| 924 } | |
| 925 return textNode; | |
| 926 } | |
| 927 </script> | |
| 928 <script> | |
| 929 function motif_logo_template(inputs) { | |
| 930 function _input(name) { | |
| 931 if (typeof inputs[name] === "undefined") { | |
| 932 throw new Error("Missing template variable: " + name); | |
| 933 } | |
| 934 return inputs[name]; | |
| 935 } | |
| 936 return ( | |
| 937 "%!PS-Adobe-3.0 EPSF-3.0\n" + | |
| 938 "%%Title: Sequence Logo : " + _input("TITLE") + "\n" + | |
| 939 "%%Creator: " + _input("CREATOR") + "\n" + | |
| 940 "%%CreationDate: " + _input("CREATIONDATE") + "\n" + | |
| 941 "%%BoundingBox: 0 0 " + _input("BOUNDINGWIDTH") + " " + _input("BOUNDINGHEIGHT") + " \n" + | |
| 942 "%%Pages: 0\n" + | |
| 943 "%%DocumentFonts: \n" + | |
| 944 "%%EndComments\n" + | |
| 945 "\n" + | |
| 946 "% ---- CONSTANTS ----\n" + | |
| 947 "\/cmfactor 72 2.54 div def % defines points -> cm conversion\n" + | |
| 948 "\/cm {cmfactor mul} bind def % defines centimeters\n" + | |
| 949 "\n" + | |
| 950 "% ---- VARIABLES ----\n" + | |
| 951 "\n" + | |
| 952 "% NA = Nucleic Acid, AA = Amino Acid\n" + | |
| 953 "\/logoType (" + _input("LOGOTYPE") + ") def \n" + | |
| 954 "\n" + | |
| 955 "\/logoTitle (" + _input("TITLE") + ") def\n" + | |
| 956 "\n" + | |
| 957 "% Dimensions in cm\n" + | |
| 958 "\/logoWidth " + _input("LOGOWIDTH") + " cm def\n" + | |
| 959 "\/logoHeight " + _input("LOGOLINEHEIGHT") + " cm def\n" + | |
| 960 "\/totalHeight " + _input("LOGOHEIGHT") + " cm def\n" + | |
| 961 "\n" + | |
| 962 "\/yaxis " + _input("YAXIS") + " def\n" + | |
| 963 "\/yaxisLabel (" + _input("YAXISLABEL") + ") def\n" + | |
| 964 "\/yaxisBits " + _input("BARBITS") + " def % bits\n" + | |
| 965 "\/yaxisTicBits " + _input("TICBITS") + " def\n" + | |
| 966 "\n" + | |
| 967 "\/xaxis " + _input("NUMBERING") + " def\n" + | |
| 968 "\/xaxisLabel (" + _input("XAXISLABEL") + ") def\n" + | |
| 969 "\/showEnds (" + _input("SHOWENDS") + ") def \n" + | |
| 970 "\n" + | |
| 971 "\/showFineprint true def\n" + | |
| 972 "\/fineprint (" + _input("FINEPRINT") + ") def\n" + | |
| 973 "\n" + | |
| 974 "\/charsPerLine " + _input("CHARSPERLINE") + " def\n" + | |
| 975 "\n" + | |
| 976 "\/showingBox " + _input("SHOWINGBOX") + " def \n" + | |
| 977 "\/shrinking false def % true falses\n" + | |
| 978 "\/shrink 1.0 def\n" + | |
| 979 "\/outline " + _input("OUTLINE") + " def\n" + | |
| 980 "\n" + | |
| 981 "\/IbeamFraction " + _input("ERRORBARFRACTION") + " def\n" + | |
| 982 "\/IbeamGray 0.50 def\n" + | |
| 983 "\/IbeamLineWidth 0.5 def\n" + | |
| 984 "\n" + | |
| 985 "\/fontsize " + _input("FONTSIZE") + " def\n" + | |
| 986 "\/titleFontsize " + _input("TITLEFONTSIZE") + " def\n" + | |
| 987 "\/smallFontsize " + _input("SMALLFONTSIZE") + " def\n" + | |
| 988 "\n" + | |
| 989 "\/topMargin " + _input("TOPMARGIN") + " cm def\n" + | |
| 990 "\/bottomMargin " + _input("BOTTOMMARGIN") + " cm def\n" + | |
| 991 "\n" + | |
| 992 "\/defaultColor [0 0 0] def \n" + | |
| 993 "\n" + | |
| 994 _input("COLORDICT") + "\n" + | |
| 995 "\n" + | |
| 996 "\/colorDict fullColourDict def\n" + | |
| 997 "\n" + | |
| 998 "% ---- DERIVED PARAMETERS ----\n" + | |
| 999 "\n" + | |
| 1000 "\/leftMargin\n" + | |
| 1001 " fontsize 3.5 mul\n" + | |
| 1002 "\n" + | |
| 1003 "def \n" + | |
| 1004 "\n" + | |
| 1005 "\/rightMargin \n" + | |
| 1006 " %Add extra room if showing ends\n" + | |
| 1007 " showEnds (false) eq { fontsize}{fontsize 1.5 mul} ifelse\n" + | |
| 1008 "def\n" + | |
| 1009 "\n" + | |
| 1010 "\/yaxisHeight \n" + | |
| 1011 " logoHeight \n" + | |
| 1012 " bottomMargin sub \n" + | |
| 1013 " topMargin sub\n" + | |
| 1014 "def\n" + | |
| 1015 "\n" + | |
| 1016 "\/ticWidth fontsize 2 div def\n" + | |
| 1017 "\n" + | |
| 1018 "\/pointsPerBit yaxisHeight yaxisBits div def\n" + | |
| 1019 "\n" + | |
| 1020 "\/stackMargin 1 def\n" + | |
| 1021 "\n" + | |
| 1022 "% Do not add space aroung characters if characters are boxed\n" + | |
| 1023 "\/charRightMargin \n" + | |
| 1024 " showingBox { 0.0 } {stackMargin} ifelse\n" + | |
| 1025 "def\n" + | |
| 1026 "\n" + | |
| 1027 "\/charTopMargin \n" + | |
| 1028 " showingBox { 0.0 } {stackMargin} ifelse\n" + | |
| 1029 "def\n" + | |
| 1030 "\n" + | |
| 1031 "\/charWidth\n" + | |
| 1032 " logoWidth\n" + | |
| 1033 " leftMargin sub\n" + | |
| 1034 " rightMargin sub\n" + | |
| 1035 " charsPerLine div\n" + | |
| 1036 " charRightMargin sub\n" + | |
| 1037 "def\n" + | |
| 1038 "\n" + | |
| 1039 "\/charWidth4 charWidth 4 div def\n" + | |
| 1040 "\/charWidth2 charWidth 2 div def\n" + | |
| 1041 "\n" + | |
| 1042 "\/stackWidth \n" + | |
| 1043 " charWidth charRightMargin add\n" + | |
| 1044 "def\n" + | |
| 1045 " \n" + | |
| 1046 "\/numberFontsize \n" + | |
| 1047 " fontsize charWidth lt {fontsize}{charWidth} ifelse\n" + | |
| 1048 "def\n" + | |
| 1049 "\n" + | |
| 1050 "% movements to place 5'\/N and 3'\/C symbols\n" + | |
| 1051 "\/leftEndDeltaX fontsize neg def\n" + | |
| 1052 "\/leftEndDeltaY fontsize 1.5 mul neg def\n" + | |
| 1053 "\/rightEndDeltaX fontsize 0.25 mul def\n" + | |
| 1054 "\/rightEndDeltaY leftEndDeltaY def\n" + | |
| 1055 "\n" + | |
| 1056 "% Outline width is proporional to charWidth, \n" + | |
| 1057 "% but no less that 1 point\n" + | |
| 1058 "\/outlinewidth \n" + | |
| 1059 " charWidth 32 div dup 1 gt {}{pop 1} ifelse\n" + | |
| 1060 "def\n" + | |
| 1061 "\n" + | |
| 1062 "\n" + | |
| 1063 "% ---- PROCEDURES ----\n" + | |
| 1064 "\n" + | |
| 1065 "\/StartLogo { \n" + | |
| 1066 " % Save state\n" + | |
| 1067 " save \n" + | |
| 1068 " gsave \n" + | |
| 1069 "\n" + | |
| 1070 " % Print Logo Title, top center \n" + | |
| 1071 " gsave \n" + | |
| 1072 " SetStringFont\n" + | |
| 1073 "\n" + | |
| 1074 " logoWidth 2 div\n" + | |
| 1075 " logoTitle\n" + | |
| 1076 " stringwidth pop 2 div sub\n" + | |
| 1077 " totalHeight\n" + | |
| 1078 " titleFontsize sub\n" + | |
| 1079 " moveto\n" + | |
| 1080 "\n" + | |
| 1081 " logoTitle\n" + | |
| 1082 " show\n" + | |
| 1083 " grestore\n" + | |
| 1084 "\n" + | |
| 1085 " % Print X-axis label, bottom center\n" + | |
| 1086 " gsave\n" + | |
| 1087 " SetStringFont\n" + | |
| 1088 "\n" + | |
| 1089 " logoWidth 2 div\n" + | |
| 1090 " xaxisLabel\n" + | |
| 1091 " stringwidth pop 2 div sub\n" + | |
| 1092 " 0\n" + | |
| 1093 " titleFontsize 3 div\n" + | |
| 1094 " add\n" + | |
| 1095 " moveto\n" + | |
| 1096 "\n" + | |
| 1097 " xaxisLabel\n" + | |
| 1098 " show\n" + | |
| 1099 " grestore\n" + | |
| 1100 "\n" + | |
| 1101 " % Show Fine Print\n" + | |
| 1102 " showFineprint {\n" + | |
| 1103 " gsave\n" + | |
| 1104 " SetSmallFont\n" + | |
| 1105 " logoWidth\n" + | |
| 1106 " fineprint stringwidth pop sub\n" + | |
| 1107 " smallFontsize sub\n" + | |
| 1108 " smallFontsize 3 div\n" + | |
| 1109 " moveto\n" + | |
| 1110 " \n" + | |
| 1111 " fineprint show\n" + | |
| 1112 " grestore\n" + | |
| 1113 " } if\n" + | |
| 1114 "\n" + | |
| 1115 " % Move to lower left corner of last line, first stack\n" + | |
| 1116 " leftMargin bottomMargin translate\n" + | |
| 1117 "\n" + | |
| 1118 " % Move above first line ready for StartLine \n" + | |
| 1119 " 0 totalHeight translate\n" + | |
| 1120 "\n" + | |
| 1121 " SetLogoFont\n" + | |
| 1122 "} bind def\n" + | |
| 1123 "\n" + | |
| 1124 "\/EndLogo { \n" + | |
| 1125 " grestore \n" + | |
| 1126 " showpage \n" + | |
| 1127 " restore \n" + | |
| 1128 "} bind def\n" + | |
| 1129 "\n" + | |
| 1130 "\n" + | |
| 1131 "\/StartLine { \n" + | |
| 1132 " % move down to the bottom of the line:\n" + | |
| 1133 " 0 logoHeight neg translate\n" + | |
| 1134 " \n" + | |
| 1135 " gsave \n" + | |
| 1136 " yaxis { MakeYaxis } if\n" + | |
| 1137 " xaxis { showEnds (true) eq {ShowLeftEnd} if } if\n" + | |
| 1138 "} bind def\n" + | |
| 1139 "\n" + | |
| 1140 "\/EndLine{ \n" + | |
| 1141 " xaxis { showEnds (true) eq {ShowRightEnd} if } if\n" + | |
| 1142 " grestore \n" + | |
| 1143 "} bind def\n" + | |
| 1144 "\n" + | |
| 1145 "\n" + | |
| 1146 "\/MakeYaxis {\n" + | |
| 1147 " gsave \n" + | |
| 1148 " stackMargin neg 0 translate\n" + | |
| 1149 " ShowYaxisBar\n" + | |
| 1150 " ShowYaxisLabel\n" + | |
| 1151 " grestore\n" + | |
| 1152 "} bind def\n" + | |
| 1153 "\n" + | |
| 1154 "\n" + | |
| 1155 "\/ShowYaxisBar { \n" + | |
| 1156 " gsave \n" + | |
| 1157 " SetStringFont\n" + | |
| 1158 "\n" + | |
| 1159 " \/str 10 string def % string to hold number \n" + | |
| 1160 " \/smallgap stackMargin 2 div def\n" + | |
| 1161 "\n" + | |
| 1162 " % Draw first tic and bar\n" + | |
| 1163 " gsave \n" + | |
| 1164 " ticWidth neg 0 moveto \n" + | |
| 1165 " ticWidth 0 rlineto \n" + | |
| 1166 " 0 yaxisHeight rlineto\n" + | |
| 1167 " stroke\n" + | |
| 1168 " grestore\n" + | |
| 1169 "\n" + | |
| 1170 " \n" + | |
| 1171 " % Draw the tics\n" + | |
| 1172 " % initial increment limit proc for\n" + | |
| 1173 " 0 yaxisTicBits yaxisBits abs %cvi\n" + | |
| 1174 " {\/loopnumber exch def\n" + | |
| 1175 "\n" + | |
| 1176 " % convert the number coming from the loop to a string\n" + | |
| 1177 " % and find its width\n" + | |
| 1178 " loopnumber 10 str cvrs\n" + | |
| 1179 " \/stringnumber exch def % string representing the number\n" + | |
| 1180 "\n" + | |
| 1181 " stringnumber stringwidth pop\n" + | |
| 1182 " \/numberwidth exch def % width of number to show\n" + | |
| 1183 "\n" + | |
| 1184 " \/halfnumberheight\n" + | |
| 1185 " stringnumber CharBoxHeight 2 div\n" + | |
| 1186 " def\n" + | |
| 1187 "\n" + | |
| 1188 " numberwidth % move back width of number\n" + | |
| 1189 " neg loopnumber pointsPerBit mul % shift on y axis\n" + | |
| 1190 " halfnumberheight sub % down half the digit\n" + | |
| 1191 "\n" + | |
| 1192 " moveto % move back the width of the string\n" + | |
| 1193 "\n" + | |
| 1194 " ticWidth neg smallgap sub % Move back a bit more \n" + | |
| 1195 " 0 rmoveto % move back the width of the tic \n" + | |
| 1196 "\n" + | |
| 1197 " stringnumber show\n" + | |
| 1198 " smallgap 0 rmoveto % Make a small gap \n" + | |
| 1199 "\n" + | |
| 1200 " % now show the tic mark\n" + | |
| 1201 " 0 halfnumberheight rmoveto % shift up again\n" + | |
| 1202 " ticWidth 0 rlineto\n" + | |
| 1203 " stroke\n" + | |
| 1204 " } for\n" + | |
| 1205 " grestore\n" + | |
| 1206 "} bind def\n" + | |
| 1207 "\n" + | |
| 1208 "\/ShowYaxisLabel {\n" + | |
| 1209 " gsave\n" + | |
| 1210 " SetStringFont\n" + | |
| 1211 "\n" + | |
| 1212 " % How far we move left depends on the size of\n" + | |
| 1213 " % the tic labels.\n" + | |
| 1214 " \/str 10 string def % string to hold number \n" + | |
| 1215 " yaxisBits yaxisTicBits div cvi yaxisTicBits mul \n" + | |
| 1216 " str cvs stringwidth pop\n" + | |
| 1217 " ticWidth 1.5 mul add neg \n" + | |
| 1218 "\n" + | |
| 1219 "\n" + | |
| 1220 " yaxisHeight\n" + | |
| 1221 " yaxisLabel stringwidth pop\n" + | |
| 1222 " sub 2 div\n" + | |
| 1223 "\n" + | |
| 1224 " translate\n" + | |
| 1225 " 90 rotate\n" + | |
| 1226 " 0 0 moveto\n" + | |
| 1227 " yaxisLabel show\n" + | |
| 1228 " grestore\n" + | |
| 1229 "} bind def\n" + | |
| 1230 "\n" + | |
| 1231 "\n" + | |
| 1232 "\/StartStack { % <stackNumber> startstack\n" + | |
| 1233 " xaxis {MakeNumber}{pop} ifelse\n" + | |
| 1234 " gsave\n" + | |
| 1235 "} bind def\n" + | |
| 1236 "\n" + | |
| 1237 "\/EndStack {\n" + | |
| 1238 " grestore\n" + | |
| 1239 " stackWidth 0 translate\n" + | |
| 1240 "} bind def\n" + | |
| 1241 "\n" + | |
| 1242 "\n" + | |
| 1243 "% Draw a character whose height is proportional to symbol bits\n" + | |
| 1244 "\/MakeSymbol{ % charbits character MakeSymbol\n" + | |
| 1245 " gsave\n" + | |
| 1246 " \/char exch def\n" + | |
| 1247 " \/bits exch def\n" + | |
| 1248 "\n" + | |
| 1249 " \/bitsHeight \n" + | |
| 1250 " bits pointsPerBit mul \n" + | |
| 1251 " def\n" + | |
| 1252 "\n" + | |
| 1253 " \/charHeight \n" + | |
| 1254 " bitsHeight charTopMargin sub\n" + | |
| 1255 " dup \n" + | |
| 1256 " 0.0 gt {}{pop 0.0} ifelse % if neg replace with zero \n" + | |
| 1257 " def \n" + | |
| 1258 " \n" + | |
| 1259 " charHeight 0.0 gt {\n" + | |
| 1260 " char SetColor\n" + | |
| 1261 " charWidth charHeight char ShowChar\n" + | |
| 1262 "\n" + | |
| 1263 " showingBox { % Unfilled box\n" + | |
| 1264 " 0 0 charWidth charHeight false ShowBox\n" + | |
| 1265 " } if\n" + | |
| 1266 "\n" + | |
| 1267 "\n" + | |
| 1268 " } if\n" + | |
| 1269 "\n" + | |
| 1270 " grestore\n" + | |
| 1271 "\n" + | |
| 1272 " 0 bitsHeight translate \n" + | |
| 1273 "} bind def\n" + | |
| 1274 "\n" + | |
| 1275 "\n" + | |
| 1276 "\/ShowChar { % <width> <height> <char> ShowChar\n" + | |
| 1277 " gsave\n" + | |
| 1278 " \/tc exch def % The character\n" + | |
| 1279 " \/ysize exch def % the y size of the character\n" + | |
| 1280 " \/xsize exch def % the x size of the character\n" + | |
| 1281 "\n" + | |
| 1282 " \/xmulfactor 1 def \n" + | |
| 1283 " \/ymulfactor 1 def\n" + | |
| 1284 " \/limmulfactor 0.01 def\n" + | |
| 1285 " \/drawable true def\n" + | |
| 1286 "\n" + | |
| 1287 " \n" + | |
| 1288 " % if ysize is negative, make everything upside down!\n" + | |
| 1289 " ysize 0 lt {\n" + | |
| 1290 " % put ysize normal in this orientation\n" + | |
| 1291 " \/ysize ysize abs def\n" + | |
| 1292 " xsize ysize translate\n" + | |
| 1293 " 180 rotate\n" + | |
| 1294 " } if\n" + | |
| 1295 "\n" + | |
| 1296 " shrinking {\n" + | |
| 1297 " xsize 1 shrink sub 2 div mul\n" + | |
| 1298 " ysize 1 shrink sub 2 div mul translate \n" + | |
| 1299 "\n" + | |
| 1300 " shrink shrink scale\n" + | |
| 1301 " } if\n" + | |
| 1302 "\n" + | |
| 1303 " % Calculate the font scaling factors\n" + | |
| 1304 " % Loop twice to catch small correction due to first scaling\n" + | |
| 1305 " 2 {\n" + | |
| 1306 " gsave\n" + | |
| 1307 " xmulfactor ymulfactor scale\n" + | |
| 1308 " \n" + | |
| 1309 " ysize % desired size of character in points\n" + | |
| 1310 " tc CharBoxHeight \n" + | |
| 1311 " dup 0.0 ne {\n" + | |
| 1312 " div % factor by which to scale up the character\n" + | |
| 1313 " \/ymulfactor exch def\n" + | |
| 1314 " } % end if\n" + | |
| 1315 " {pop pop}\n" + | |
| 1316 " ifelse\n" + | |
| 1317 "\n" + | |
| 1318 " xsize % desired size of character in points\n" + | |
| 1319 " tc CharBoxWidth \n" + | |
| 1320 " dup 0.0 ne {\n" + | |
| 1321 " div % factor by which to scale up the character\n" + | |
| 1322 " \/xmulfactor exch def\n" + | |
| 1323 " } % end if\n" + | |
| 1324 " {pop pop}\n" + | |
| 1325 " ifelse\n" + | |
| 1326 " grestore\n" + | |
| 1327 " % if the multiplication factors get too small we need to avoid a crash\n" + | |
| 1328 " xmulfactor limmulfactor lt {\n" + | |
| 1329 " \/xmulfactor 1 def\n" + | |
| 1330 " \/drawable false def\n" + | |
| 1331 " } if\n" + | |
| 1332 " ymulfactor limmulfactor lt {\n" + | |
| 1333 " \/ymulfactor 1 def\n" + | |
| 1334 " \/drawable false def\n" + | |
| 1335 " } if\n" + | |
| 1336 " } repeat\n" + | |
| 1337 "\n" + | |
| 1338 " % Adjust horizontal position if the symbol is an I\n" + | |
| 1339 " tc (I) eq {\n" + | |
| 1340 " charWidth 2 div % half of requested character width\n" + | |
| 1341 " tc CharBoxWidth 2 div % half of the actual character\n" + | |
| 1342 " sub 0 translate\n" + | |
| 1343 " % Avoid x scaling for I \n" + | |
| 1344 " \/xmulfactor 1 def \n" + | |
| 1345 " } if\n" + | |
| 1346 "\n" + | |
| 1347 "\n" + | |
| 1348 " % ---- Finally, draw the character\n" + | |
| 1349 " drawable { \n" + | |
| 1350 " newpath\n" + | |
| 1351 " xmulfactor ymulfactor scale\n" + | |
| 1352 "\n" + | |
| 1353 " % Move lower left corner of character to start point\n" + | |
| 1354 " tc CharBox pop pop % llx lly : Lower left corner\n" + | |
| 1355 " exch neg exch neg\n" + | |
| 1356 " moveto\n" + | |
| 1357 "\n" + | |
| 1358 " outline { % outline characters:\n" + | |
| 1359 " outlinewidth setlinewidth\n" + | |
| 1360 " tc true charpath\n" + | |
| 1361 " gsave 1 setgray fill grestore\n" + | |
| 1362 " clip stroke\n" + | |
| 1363 " } { % regular characters\n" + | |
| 1364 " tc show\n" + | |
| 1365 " } ifelse\n" + | |
| 1366 " } if\n" + | |
| 1367 "\n" + | |
| 1368 " grestore\n" + | |
| 1369 "} bind def\n" + | |
| 1370 "\n" + | |
| 1371 "\n" + | |
| 1372 "\/ShowBox { % x1 y1 x2 y2 filled ShowBox\n" + | |
| 1373 " gsave\n" + | |
| 1374 " \/filled exch def \n" + | |
| 1375 " \/y2 exch def\n" + | |
| 1376 " \/x2 exch def\n" + | |
| 1377 " \/y1 exch def\n" + | |
| 1378 " \/x1 exch def\n" + | |
| 1379 " newpath\n" + | |
| 1380 " x1 y1 moveto\n" + | |
| 1381 " x2 y1 lineto\n" + | |
| 1382 " x2 y2 lineto\n" + | |
| 1383 " x1 y2 lineto\n" + | |
| 1384 " closepath\n" + | |
| 1385 "\n" + | |
| 1386 " clip\n" + | |
| 1387 " \n" + | |
| 1388 " filled {\n" + | |
| 1389 " fill\n" + | |
| 1390 " }{ \n" + | |
| 1391 " 0 setgray stroke \n" + | |
| 1392 " } ifelse\n" + | |
| 1393 "\n" + | |
| 1394 " grestore\n" + | |
| 1395 "} bind def\n" + | |
| 1396 "\n" + | |
| 1397 "\n" + | |
| 1398 "\/MakeNumber { % number MakeNumber\n" + | |
| 1399 " gsave\n" + | |
| 1400 " SetNumberFont\n" + | |
| 1401 " stackWidth 0 translate\n" + | |
| 1402 " 90 rotate % rotate so the number fits\n" + | |
| 1403 " dup stringwidth pop % find the length of the number\n" + | |
| 1404 " neg % prepare for move\n" + | |
| 1405 " stackMargin sub % Move back a bit\n" + | |
| 1406 " charWidth (0) CharBoxHeight % height of numbers\n" + | |
| 1407 " sub 2 div %\n" + | |
| 1408 " moveto % move back to provide space\n" + | |
| 1409 " show\n" + | |
| 1410 " grestore\n" + | |
| 1411 "} bind def\n" + | |
| 1412 "\n" + | |
| 1413 "\n" + | |
| 1414 "\/Ibeam{ % heightInBits Ibeam\n" + | |
| 1415 " gsave\n" + | |
| 1416 " % Make an Ibeam of twice the given height in bits\n" + | |
| 1417 " \/height exch pointsPerBit mul def \n" + | |
| 1418 " \/heightDRAW height IbeamFraction mul def\n" + | |
| 1419 "\n" + | |
| 1420 " IbeamLineWidth setlinewidth\n" + | |
| 1421 " IbeamGray setgray \n" + | |
| 1422 "\n" + | |
| 1423 " charWidth2 height neg translate\n" + | |
| 1424 " ShowIbar\n" + | |
| 1425 " newpath\n" + | |
| 1426 " 0 0 moveto\n" + | |
| 1427 " 0 heightDRAW rlineto\n" + | |
| 1428 " stroke\n" + | |
| 1429 " newpath\n" + | |
| 1430 " 0 height moveto\n" + | |
| 1431 " 0 height rmoveto\n" + | |
| 1432 " currentpoint translate\n" + | |
| 1433 " ShowIbar\n" + | |
| 1434 " newpath\n" + | |
| 1435 " 0 0 moveto\n" + | |
| 1436 " 0 heightDRAW neg rlineto\n" + | |
| 1437 " currentpoint translate\n" + | |
| 1438 " stroke\n" + | |
| 1439 " grestore\n" + | |
| 1440 "} bind def\n" + | |
| 1441 "\n" + | |
| 1442 "\n" + | |
| 1443 "\/ShowIbar { % make a horizontal bar\n" + | |
| 1444 " gsave\n" + | |
| 1445 " newpath\n" + | |
| 1446 " charWidth4 neg 0 moveto\n" + | |
| 1447 " charWidth4 0 lineto\n" + | |
| 1448 " stroke\n" + | |
| 1449 " grestore\n" + | |
| 1450 "} bind def\n" + | |
| 1451 "\n" + | |
| 1452 "\n" + | |
| 1453 "\/ShowLeftEnd {\n" + | |
| 1454 " gsave\n" + | |
| 1455 " SetStringFont\n" + | |
| 1456 " leftEndDeltaX leftEndDeltaY moveto\n" + | |
| 1457 " logoType (NA) eq {(5) show ShowPrime} if\n" + | |
| 1458 " logoType (AA) eq {(N) show} if\n" + | |
| 1459 " grestore\n" + | |
| 1460 "} bind def\n" + | |
| 1461 "\n" + | |
| 1462 "\n" + | |
| 1463 "\/ShowRightEnd { \n" + | |
| 1464 " gsave\n" + | |
| 1465 " SetStringFont\n" + | |
| 1466 " rightEndDeltaX rightEndDeltaY moveto\n" + | |
| 1467 " logoType (NA) eq {(3) show ShowPrime} if\n" + | |
| 1468 " logoType (AA) eq {(C) show} if\n" + | |
| 1469 " grestore\n" + | |
| 1470 "} bind def\n" + | |
| 1471 "\n" + | |
| 1472 "\n" + | |
| 1473 "\/ShowPrime {\n" + | |
| 1474 " gsave\n" + | |
| 1475 " SetPrimeFont\n" + | |
| 1476 " (\\242) show \n" + | |
| 1477 " grestore\n" + | |
| 1478 "} bind def\n" + | |
| 1479 "\n" + | |
| 1480 " \n" + | |
| 1481 "\/SetColor{ % <char> SetColor\n" + | |
| 1482 " dup colorDict exch known {\n" + | |
| 1483 " colorDict exch get aload pop setrgbcolor\n" + | |
| 1484 " } {\n" + | |
| 1485 " pop\n" + | |
| 1486 " defaultColor aload pop setrgbcolor\n" + | |
| 1487 " } ifelse \n" + | |
| 1488 "} bind def\n" + | |
| 1489 "\n" + | |
| 1490 "% define fonts\n" + | |
| 1491 "\/SetTitleFont {\/Times-Bold findfont titleFontsize scalefont setfont} bind def\n" + | |
| 1492 "\/SetLogoFont {\/Helvetica-Bold findfont charWidth scalefont setfont} bind def\n" + | |
| 1493 "\/SetStringFont{\/Helvetica-Bold findfont fontsize scalefont setfont} bind def\n" + | |
| 1494 "\/SetPrimeFont {\/Symbol findfont fontsize scalefont setfont} bind def\n" + | |
| 1495 "\/SetSmallFont {\/Helvetica findfont smallFontsize scalefont setfont} bind def\n" + | |
| 1496 "\n" + | |
| 1497 "\/SetNumberFont {\n" + | |
| 1498 " \/Helvetica-Bold findfont \n" + | |
| 1499 " numberFontsize\n" + | |
| 1500 " scalefont\n" + | |
| 1501 " setfont\n" + | |
| 1502 "} bind def\n" + | |
| 1503 "\n" + | |
| 1504 "%Take a single character and return the bounding box\n" + | |
| 1505 "\/CharBox { % <char> CharBox <lx> <ly> <ux> <uy>\n" + | |
| 1506 " gsave\n" + | |
| 1507 " newpath\n" + | |
| 1508 " 0 0 moveto\n" + | |
| 1509 " % take the character off the stack and use it here:\n" + | |
| 1510 " true charpath \n" + | |
| 1511 " flattenpath \n" + | |
| 1512 " pathbbox % compute bounding box of 1 pt. char => lx ly ux uy\n" + | |
| 1513 " % the path is here, but toss it away ...\n" + | |
| 1514 " grestore\n" + | |
| 1515 "} bind def\n" + | |
| 1516 "\n" + | |
| 1517 "\n" + | |
| 1518 "% The height of a characters bounding box\n" + | |
| 1519 "\/CharBoxHeight { % <char> CharBoxHeight <num>\n" + | |
| 1520 " CharBox\n" + | |
| 1521 " exch pop sub neg exch pop\n" + | |
| 1522 "} bind def\n" + | |
| 1523 "\n" + | |
| 1524 "\n" + | |
| 1525 "% The width of a characters bounding box\n" + | |
| 1526 "\/CharBoxWidth { % <char> CharBoxHeight <num>\n" + | |
| 1527 " CharBox\n" + | |
| 1528 " pop exch pop sub neg \n" + | |
| 1529 "} bind def\n" + | |
| 1530 "\n" + | |
| 1531 "% Set the colour scheme to be faded to indicate trimming\n" + | |
| 1532 "\/MuteColour {\n" + | |
| 1533 " \/colorDict mutedColourDict def\n" + | |
| 1534 "} def\n" + | |
| 1535 "\n" + | |
| 1536 "% Restore the colour scheme to the normal colours\n" + | |
| 1537 "\/RestoreColour {\n" + | |
| 1538 " \/colorDict fullColourDict def\n" + | |
| 1539 "} def\n" + | |
| 1540 "\n" + | |
| 1541 "% Draw the background for a trimmed section\n" + | |
| 1542 "% takes the number of columns as a parameter\n" + | |
| 1543 "\/DrawTrimBg { % <num> DrawTrimBox\n" + | |
| 1544 " \/col exch def\n" + | |
| 1545 " \n" + | |
| 1546 " \/boxwidth \n" + | |
| 1547 " col stackWidth mul \n" + | |
| 1548 " def\n" + | |
| 1549 " \n" + | |
| 1550 " gsave\n" + | |
| 1551 " 0.97 setgray\n" + | |
| 1552 "\n" + | |
| 1553 " newpath\n" + | |
| 1554 " 0 0 moveto\n" + | |
| 1555 " boxwidth 0 rlineto\n" + | |
| 1556 " 0 yaxisHeight rlineto\n" + | |
| 1557 " 0 yaxisHeight lineto\n" + | |
| 1558 " closepath\n" + | |
| 1559 " \n" + | |
| 1560 " fill\n" + | |
| 1561 " grestore\n" + | |
| 1562 "} def\n" + | |
| 1563 "\n" + | |
| 1564 "\/DrawTrimEdge {\n" + | |
| 1565 " gsave\n" + | |
| 1566 " 0.2 setgray\n" + | |
| 1567 " [2] 0 setdash\n" + | |
| 1568 "\n" + | |
| 1569 " newpath\n" + | |
| 1570 " 0 0 moveto\n" + | |
| 1571 " 0 yaxisHeight lineto\n" + | |
| 1572 " \n" + | |
| 1573 " stroke\n" + | |
| 1574 "\n" + | |
| 1575 "} def\n" + | |
| 1576 "\n" + | |
| 1577 "\n" + | |
| 1578 "% Deprecated names\n" + | |
| 1579 "\/startstack {StartStack} bind def\n" + | |
| 1580 "\/endstack {EndStack} bind def\n" + | |
| 1581 "\/makenumber {MakeNumber} bind def\n" + | |
| 1582 "\/numchar { MakeSymbol } bind def\n" + | |
| 1583 "\n" + | |
| 1584 "%%EndProlog\n" + | |
| 1585 "\n" + | |
| 1586 "%%Page: 1 1\n" + | |
| 1587 "StartLogo\n" + | |
| 1588 "\n" + | |
| 1589 _input("DATA") + "\n" + | |
| 1590 "\n" + | |
| 1591 "EndLogo\n" + | |
| 1592 "\n" + | |
| 1593 "%%EOF\n" | |
| 1594 ); | |
| 1595 }</script> | |
| 1596 <script> | |
| 1597 //====================================================================== | |
| 1598 // start Alphabet object | |
| 1599 //====================================================================== | |
| 1600 var Alphabet = function(alphabet, background) { | |
| 1601 "use strict"; | |
| 1602 var i, j, sym, aliases, complement, comp_e_sym, ambigs, generate_background; | |
| 1603 generate_background = (background == null); | |
| 1604 if (generate_background) { | |
| 1605 background = []; | |
| 1606 for (i = 0; i < alphabet.ncore; i++) background[i] = 1.0 / alphabet.ncore; | |
| 1607 } else if (alphabet.ncore != background.length) { | |
| 1608 throw new Error("The background length does not match the alphabet length."); | |
| 1609 } | |
| 1610 this.name = alphabet.name; | |
| 1611 this.like = (alphabet.like != null ? alphabet.like.toUpperCase() : null); | |
| 1612 this.ncore = alphabet.ncore; | |
| 1613 this.symbols = alphabet.symbols; | |
| 1614 this.background = background; | |
| 1615 this.genbg = generate_background; | |
| 1616 this.encode = {}; | |
| 1617 this.encode2core = {}; | |
| 1618 this.complement = {}; | |
| 1619 // check if all symbols are same case | |
| 1620 var seen_uc = false; | |
| 1621 var seen_lc = false; | |
| 1622 var check_case = function (syms) { | |
| 1623 var s, sym; | |
| 1624 if (typeof syms === "string") { | |
| 1625 for (s = 0; s < syms.length; s++) { | |
| 1626 sym = syms.charAt(s); | |
| 1627 if (sym >= 'a' && sym <= 'z') seen_lc = true; | |
| 1628 else if (sym >= 'A' && sym <= 'Z') seen_uc = true; | |
| 1629 } | |
| 1630 } | |
| 1631 }; | |
| 1632 for (i = 0; i < this.symbols.length; i++) { | |
| 1633 check_case(this.symbols[i].symbol); | |
| 1634 check_case(this.symbols[i].aliases); | |
| 1635 } | |
| 1636 // now map symbols to indexes | |
| 1637 var update_array = function(array, syms, index) { | |
| 1638 var s, sym; | |
| 1639 if (typeof syms === "string") { | |
| 1640 for (s = 0; s < syms.length; s++) { | |
| 1641 sym = syms.charAt(s); | |
| 1642 array[sym] = index; | |
| 1643 // when only a single case is used, then encode as case insensitive | |
| 1644 if (seen_uc != seen_lc) { | |
| 1645 if (sym >= 'a' && sym <= 'z') { | |
| 1646 array[sym.toUpperCase()] = index; | |
| 1647 } else if (sym >= 'A' && sym <= 'Z') { | |
| 1648 array[sym.toLowerCase()] = index; | |
| 1649 } | |
| 1650 } | |
| 1651 } | |
| 1652 } | |
| 1653 } | |
| 1654 // map core symbols to index | |
| 1655 for (i = 0; i < this.ncore; i++) { | |
| 1656 update_array(this.encode2core, this.symbols[i].symbol, i); | |
| 1657 update_array(this.encode, this.symbols[i].symbol, i); | |
| 1658 update_array(this.encode2core, this.symbols[i].aliases, i); | |
| 1659 update_array(this.encode, this.symbols[i].aliases, i); | |
| 1660 } | |
| 1661 // map ambigous symbols to index | |
| 1662 ambigs = {}; | |
| 1663 for (i = this.ncore; i < this.symbols.length; i++) { | |
| 1664 update_array(this.encode, this.symbols[i].symbol, i); | |
| 1665 update_array(this.encode, this.symbols[i].aliases, i); | |
| 1666 ambigs[this.symbols[i].equals] = i; | |
| 1667 } | |
| 1668 // determine complements | |
| 1669 for (i = 0; i < this.ncore; i++) { | |
| 1670 complement = this.symbols[i].complement; | |
| 1671 if (typeof complement === "string") { | |
| 1672 this.complement[i] = this.encode2core[complement]; | |
| 1673 } | |
| 1674 } | |
| 1675 next_symbol: | |
| 1676 for (i = this.ncore; i < this.symbols.length; i++) { | |
| 1677 complement = ""; | |
| 1678 for (j = 0; j < this.symbols[i].equals.length; j++) { | |
| 1679 comp_e_sym = this.complement[this.encode2core[this.symbols[i].equals.charAt(j)]]; | |
| 1680 if (typeof comp_e_sym !== "number") continue next_symbol; | |
| 1681 complement += this.symbols[comp_e_sym].symbol; | |
| 1682 } | |
| 1683 complement = complement.split("").sort().join(""); | |
| 1684 if (typeof ambigs[complement] === "number") { | |
| 1685 this.complement[i] = ambigs[complement]; | |
| 1686 } | |
| 1687 } | |
| 1688 // determine case insensitivity | |
| 1689 this.case_insensitive = true; | |
| 1690 if (seen_uc == seen_lc) { | |
| 1691 // when there is a mixture of cases it probably won't | |
| 1692 // be case insensitive but we still need to check | |
| 1693 loop: | |
| 1694 for (i = 0; i < this.symbols.length; i++) { | |
| 1695 sym = this.symbols[i].symbol; | |
| 1696 if (sym >= 'A' && sym <= 'Z') { | |
| 1697 if (this.encode[sym.toLowerCase()] != i) { | |
| 1698 this.case_insensitive = false; | |
| 1699 break loop; | |
| 1700 } | |
| 1701 } else if (sym >= 'a' && sym <= 'z') { | |
| 1702 if (this.encode[sym.toUpperCase()] != i) { | |
| 1703 this.case_insensitive = false; | |
| 1704 break loop; | |
| 1705 } | |
| 1706 } | |
| 1707 aliases = this.symbols[i].aliases; | |
| 1708 if (aliases != null) { | |
| 1709 for (j = 0; j < aliases.length; j++) { | |
| 1710 sym = aliases.charAt(j); | |
| 1711 if (sym >= 'A' && sym <= 'Z') { | |
| 1712 if (this.encode[sym.toLowerCase()] != i) { | |
| 1713 this.case_insensitive = false; | |
| 1714 break loop; | |
| 1715 } | |
| 1716 } else if (sym >= 'a' && sym <= 'z') { | |
| 1717 if (this.encode[sym.toUpperCase()] != i) { | |
| 1718 this.case_insensitive = false; | |
| 1719 break loop; | |
| 1720 } | |
| 1721 } | |
| 1722 } | |
| 1723 } | |
| 1724 } | |
| 1725 } | |
| 1726 // normalise aliases to remove the prime symbol and eliminate | |
| 1727 // the alternate cases when the alphabet is case insensitive | |
| 1728 var seen, out; | |
| 1729 for (i = 0; i < this.symbols.length; i++) { | |
| 1730 sym = this.symbols[i].symbol; | |
| 1731 aliases = this.symbols[i].aliases; | |
| 1732 if (typeof aliases != "string") aliases = ""; | |
| 1733 seen = {}; | |
| 1734 out = []; | |
| 1735 if (this.case_insensitive) { | |
| 1736 sym = sym.toUpperCase(); | |
| 1737 aliases = aliases.toUpperCase(); | |
| 1738 } | |
| 1739 seen[sym] = true; | |
| 1740 for (j = 0; j < aliases.length; j++) { | |
| 1741 if (!seen[aliases.charAt(j)]) { | |
| 1742 seen[aliases.charAt(j)] = true; | |
| 1743 out.push(aliases.charAt(j)); | |
| 1744 } | |
| 1745 } | |
| 1746 this.symbols[i].aliases = out.sort().join(""); | |
| 1747 } | |
| 1748 }; | |
| 1749 // return the name of the alphabet | |
| 1750 Alphabet.prototype.get_alphabet_name = function() { | |
| 1751 return this.name; | |
| 1752 }; | |
| 1753 // return if the alphabet can be complemented | |
| 1754 Alphabet.prototype.has_complement = function() { | |
| 1755 return (typeof this.symbols[0].complement === "string"); | |
| 1756 }; | |
| 1757 // return true if an uppercase letter has the same meaning as the lowercase form | |
| 1758 Alphabet.prototype.is_case_insensitive = function() { | |
| 1759 return this.case_insensitive; | |
| 1760 }; | |
| 1761 // return the information content of an alphabet letter | |
| 1762 Alphabet.prototype.get_ic = function() { | |
| 1763 return Math.log(this.ncore) / Math.LN2; | |
| 1764 }; | |
| 1765 // return the count of the core alphabet symbols | |
| 1766 Alphabet.prototype.get_size_core = function() { | |
| 1767 return this.ncore; | |
| 1768 }; | |
| 1769 // return the count of all alphabet symbols | |
| 1770 Alphabet.prototype.get_size_full = function() { | |
| 1771 return this.symbols.length; | |
| 1772 }; | |
| 1773 // return the symbol for the given alphabet index | |
| 1774 Alphabet.prototype.get_symbol = function(alph_index) { | |
| 1775 "use strict"; | |
| 1776 if (alph_index < 0 || alph_index >= this.symbols.length) { | |
| 1777 throw new Error("Alphabet index out of bounds"); | |
| 1778 } | |
| 1779 return this.symbols[alph_index].symbol; | |
| 1780 }; | |
| 1781 // return the aliases for the given alphabet index | |
| 1782 Alphabet.prototype.get_aliases = function(alph_index) { | |
| 1783 "use strict"; | |
| 1784 if (alph_index < 0 || alph_index >= this.symbols.length) { | |
| 1785 throw new Error("Alphabet index out of bounds"); | |
| 1786 } | |
| 1787 var sym_obj = this.symbols[alph_index]; | |
| 1788 return (sym_obj.aliases != null ? sym_obj.aliases : ""); | |
| 1789 }; | |
| 1790 // return the name for the given alphabet index | |
| 1791 Alphabet.prototype.get_name = function(alph_index) { | |
| 1792 "use strict"; | |
| 1793 var sym; | |
| 1794 if (alph_index < 0 || alph_index >= this.symbols.length) { | |
| 1795 throw new Error("Alphabet index out of bounds"); | |
| 1796 } | |
| 1797 sym = this.symbols[alph_index]; | |
| 1798 return (typeof sym.name === "string" ? sym.name : sym.symbol); | |
| 1799 }; | |
| 1800 // return the alphabet it is like or null | |
| 1801 Alphabet.prototype.get_like = function() { | |
| 1802 "use strict"; | |
| 1803 return this.like; | |
| 1804 }; | |
| 1805 // return the index of the complement for the given alphabet index | |
| 1806 Alphabet.prototype.get_complement = function(alph_index) { | |
| 1807 var comp_e_sym = this.complement[alph_index]; | |
| 1808 if (typeof comp_e_sym === "number") { | |
| 1809 return comp_e_sym; | |
| 1810 } else { | |
| 1811 return -1; | |
| 1812 } | |
| 1813 }; | |
| 1814 // return a string containing the core symbols | |
| 1815 Alphabet.prototype.get_symbols = function() { | |
| 1816 "use strict"; | |
| 1817 var i, core_symbols; | |
| 1818 core_symbols = ""; | |
| 1819 for (i = 0; i < this.ncore; i++) { | |
| 1820 core_symbols += this.symbols[i].symbol; | |
| 1821 } | |
| 1822 return core_symbols; | |
| 1823 }; | |
| 1824 // return if the background was not a uniform generated background | |
| 1825 Alphabet.prototype.has_bg = function() { | |
| 1826 "use strict"; | |
| 1827 return !this.genbg; | |
| 1828 }; | |
| 1829 // get the background frequency for the index | |
| 1830 Alphabet.prototype.get_bg_freq = function(alph_index) { | |
| 1831 "use strict"; | |
| 1832 var freq, i, symbols; | |
| 1833 if (alph_index >= 0) { | |
| 1834 if (alph_index < this.ncore) { | |
| 1835 return this.background[alph_index]; | |
| 1836 } else if (alph_index < this.symbols.length) { | |
| 1837 freq = 0; | |
| 1838 symbols = this.symbols[alph_index].equals; | |
| 1839 for (i = 0; i < symbols.length; i++) { | |
| 1840 freq += this.background[this.encode2core[symbols.charAt(i)]]; | |
| 1841 } | |
| 1842 return freq; | |
| 1843 } | |
| 1844 } | |
| 1845 throw new Error("The alphabet index is out of range."); | |
| 1846 }; | |
| 1847 // get the colour of the index | |
| 1848 Alphabet.prototype.get_colour = function(alph_index) { | |
| 1849 "use strict"; | |
| 1850 if (alph_index < 0 || alph_index >= this.symbols.length) { | |
| 1851 throw new Error("BAD_ALPHABET_INDEX"); | |
| 1852 } | |
| 1853 if (typeof this.symbols[alph_index].colour != "string") { | |
| 1854 return "black"; | |
| 1855 } | |
| 1856 return "#" + this.symbols[alph_index].colour; | |
| 1857 }; | |
| 1858 // get the rgb componets of the colour at the index | |
| 1859 Alphabet.prototype.get_rgb = function(alph_index) { | |
| 1860 "use strict"; | |
| 1861 if (alph_index < 0 || alph_index >= this.symbols.length) { | |
| 1862 throw new Error("BAD_ALPHABET_INDEX"); | |
| 1863 } | |
| 1864 if (typeof this.symbols[alph_index].colour != "string") { | |
| 1865 return {"red": 0, "green": 0, "blue": 0}; | |
| 1866 } | |
| 1867 var colour = this.symbols[alph_index].colour; | |
| 1868 var red = parseInt(colour.substr(0, 2), 16) / 255; | |
| 1869 var green = parseInt(colour.substr(2, 2), 16) / 255; | |
| 1870 var blue = parseInt(colour.substr(4, 2), 16) / 255; | |
| 1871 return {"red": red, "green": green, "blue": blue}; | |
| 1872 }; | |
| 1873 // convert a symbol into the index | |
| 1874 Alphabet.prototype.get_index = function(letter) { | |
| 1875 "use strict"; | |
| 1876 var alph_index; | |
| 1877 alph_index = this.encode[letter]; | |
| 1878 if (typeof alph_index === "undefined") { | |
| 1879 return -1; | |
| 1880 } | |
| 1881 return alph_index; | |
| 1882 }; | |
| 1883 // convert a symbol into the list of core indexes that it equals | |
| 1884 Alphabet.prototype.get_indexes = function(letter) { | |
| 1885 "use strict"; | |
| 1886 var alph_index, comprise_str, i, comprise_list; | |
| 1887 alph_index = this.encode[letter]; | |
| 1888 if (typeof alph_index === "undefined") { | |
| 1889 throw new Error("Unknown letter"); | |
| 1890 } | |
| 1891 comprise_str = this.symbols[alph_index].equals; | |
| 1892 comprise_list = []; | |
| 1893 if (typeof comprise_str == "string") { | |
| 1894 for (i = 0; i < comprise_str.length; i++) { | |
| 1895 comprise_list.push(this.encode2core[comprise_str.charAt(i)]); | |
| 1896 } | |
| 1897 } else { | |
| 1898 comprise_list.push(alph_index); | |
| 1899 } | |
| 1900 return comprise_list; | |
| 1901 }; | |
| 1902 // check if a symbol is the primary way of representing the symbol in the alphabet | |
| 1903 Alphabet.prototype.is_prime_symbol = function(letter) { | |
| 1904 var alph_index; | |
| 1905 alph_index = this.encode[letter]; | |
| 1906 if (alph_index == null) return false; | |
| 1907 if (this.is_case_insensitive()) { | |
| 1908 return (this.symbols[alph_index].symbol.toUpperCase() == letter.toUpperCase()); | |
| 1909 } else { | |
| 1910 return (this.symbols[alph_index].symbol == letter); | |
| 1911 } | |
| 1912 }; | |
| 1913 // compare 2 alphabets | |
| 1914 Alphabet.prototype.equals = function(other) { | |
| 1915 "use strict"; | |
| 1916 var i, sym1, sym2; | |
| 1917 // first check that it's actually an alphabet object | |
| 1918 if (!(typeof other === "object" && other != null && other instanceof Alphabet)) { | |
| 1919 return false; | |
| 1920 } | |
| 1921 // second shortcircuit if it's the same object | |
| 1922 if (this === other) return true; | |
| 1923 // compare | |
| 1924 if (this.name !== other.name) return false; | |
| 1925 if (this.ncore !== other.ncore) return false; | |
| 1926 if (this.symbols.length !== other.symbols.length) return false; | |
| 1927 for (i = 0; i < this.symbols.length; i++) { | |
| 1928 sym1 = this.symbols[i]; | |
| 1929 sym2 = other.symbols[i]; | |
| 1930 if (sym1.symbol !== sym2.symbol) return false; | |
| 1931 if (sym1.aliases !== sym2.aliases) return false; | |
| 1932 if (sym1.name !== sym2.name) return false; | |
| 1933 if (typeof sym1.colour !== typeof sym2.colour || | |
| 1934 (typeof sym1.colour === "string" && typeof sym2.colour === "string" && | |
| 1935 parseInt(sym1.colour, 16) != parseInt(sym2.colour, 16))) { | |
| 1936 return false; | |
| 1937 } | |
| 1938 if (sym1.complement !== sym2.complement) return false; | |
| 1939 if (sym1.equals !== sym2.equals) return false; | |
| 1940 } | |
| 1941 return true; | |
| 1942 }; | |
| 1943 Alphabet.prototype.check_core_subset = function(super_alph) { | |
| 1944 var complement_same = true; | |
| 1945 var seen_set = {}; | |
| 1946 var sub_i, sub_symbol, super_i, super_symbol; | |
| 1947 for (sub_i = 0; sub_i < this.ncore; sub_i++) { | |
| 1948 sub_symbol = this.symbols[sub_i]; | |
| 1949 super_i = super_alph.encode[sub_symbol.symbol]; | |
| 1950 if (super_i == null) return 0; | |
| 1951 super_symbol = super_alph.symbols[super_i]; | |
| 1952 if (seen_set[super_i]) return 0; | |
| 1953 seen_set[super_i] = true; | |
| 1954 // check complement | |
| 1955 if (sub_symbol.complement != null && super_symbol.complement != null) { | |
| 1956 if (super_alph.encode[sub_symbol.complement] != super_alph.encode[super_symbol.complement]) { | |
| 1957 complement_same = false; | |
| 1958 } | |
| 1959 } else if (sub_symbol.complement != null || super_symbol.complement != null) { | |
| 1960 complement_same = false; | |
| 1961 } | |
| 1962 } | |
| 1963 return (complement_same ? 1 : -1); | |
| 1964 }; | |
| 1965 // convert a sequence to its reverse complement | |
| 1966 Alphabet.prototype.invcomp_seq = function(seq) { | |
| 1967 "use strict"; | |
| 1968 var syms, i, e_sym, comp_e_sym; | |
| 1969 if (!this.has_complement()) throw new Error("Alphabet must be complementable"); | |
| 1970 syms = seq.split(""); | |
| 1971 for (i = 0; i < syms.length; i++) { | |
| 1972 e_sym = this.encode[syms[i]]; | |
| 1973 if (typeof e_sym === "undefined") { | |
| 1974 e_sym = this.ncore; // wildcard | |
| 1975 } | |
| 1976 comp_e_sym = this.complement[e_sym]; | |
| 1977 if (typeof comp_e_sym === "undefined") { | |
| 1978 comp_e_sym = e_sym; // not complementable | |
| 1979 } | |
| 1980 syms[i] = this.symbols[comp_e_sym].symbol; | |
| 1981 } | |
| 1982 return syms.reverse().join(""); | |
| 1983 }; | |
| 1984 // convert the alphabet to the text version | |
| 1985 Alphabet.prototype.as_text = function() { | |
| 1986 "use strict"; | |
| 1987 function name_as_text(name) { | |
| 1988 var i, c, out; | |
| 1989 out = "\""; | |
| 1990 for (i = 0; i < name.length; i++) { | |
| 1991 c = name.charAt(i); | |
| 1992 if (c == "\"") { | |
| 1993 out += "\\\""; | |
| 1994 } else if (c == "/") { | |
| 1995 out += "\\/"; | |
| 1996 } else if (c == "\\") { | |
| 1997 out += "\\\\"; | |
| 1998 } else { | |
| 1999 out += c; | |
| 2000 } | |
| 2001 } | |
| 2002 out += "\""; | |
| 2003 return out; | |
| 2004 } | |
| 2005 function symbol_as_text(sym) { | |
| 2006 var out; | |
| 2007 out = sym.symbol; | |
| 2008 if (typeof sym.name === "string" && sym.name != sym.symbol) { | |
| 2009 out += " " + name_as_text(sym.name); | |
| 2010 } | |
| 2011 if (typeof sym.colour === "string") { | |
| 2012 out += " " + sym.colour; | |
| 2013 } | |
| 2014 return out; | |
| 2015 } | |
| 2016 var out, i, j, c, sym; | |
| 2017 out = ""; | |
| 2018 // output core symbols with 2 way complements | |
| 2019 for (i = 0; i < this.ncore; i++) { | |
| 2020 c = this.complement[i]; | |
| 2021 if (typeof c === "number" && i < c && this.complement[c] === i) { | |
| 2022 out += symbol_as_text(this.symbols[i]) + " ~ " + symbol_as_text(this.symbols[c]) + "\n"; | |
| 2023 } | |
| 2024 } | |
| 2025 // output core symbols with no complement | |
| 2026 for (i = 0; i < this.ncore; i++) { | |
| 2027 if (typeof this.complement[i] === "undefined") { | |
| 2028 out += symbol_as_text(this.symbols[i]) + "\n"; | |
| 2029 } | |
| 2030 } | |
| 2031 // output ambiguous symbols that have comprising characters | |
| 2032 for (i = this.ncore; i < this.symbols.length; i++) { | |
| 2033 if (this.symbols[i].equals.length == 0) break; | |
| 2034 out += symbol_as_text(this.symbols[i]) + " = " + this.symbols[i].equals + "\n"; | |
| 2035 if (typeof this.symbols[i].aliases === "string") { | |
| 2036 for (j = 0; j < this.symbols[i].aliases.length; j++) { | |
| 2037 if (this.symbols[i].aliases.charAt(j) == this.symbols[i].symbol) continue; | |
| 2038 out += this.symbols[i].aliases.charAt(j) + " = " + this.symbols[i].equals + "\n"; | |
| 2039 } | |
| 2040 } | |
| 2041 } | |
| 2042 // output aliases of core symbols | |
| 2043 for (i = 0; i < this.ncore; i++) { | |
| 2044 if (typeof this.symbols[i].aliases === "string") { | |
| 2045 for (j = 0; j < this.symbols[i].aliases.length; j++) { | |
| 2046 if (this.symbols[i].aliases.charAt(j) == this.symbols[i].symbol) continue; | |
| 2047 out += this.symbols[i].aliases.charAt(j) + " = " + this.symbols[i].symbol + "\n"; | |
| 2048 } | |
| 2049 } | |
| 2050 } | |
| 2051 // output gap symbols | |
| 2052 i = this.symbols.length - 1; | |
| 2053 if (this.symbols[i].equals.length == 0) { | |
| 2054 out += symbol_as_text(this.symbols[i]) + " =\n"; | |
| 2055 if (typeof this.symbols[i].aliases === "string") { | |
| 2056 for (j = 0; j < this.symbols[i].aliases.length; j++) { | |
| 2057 if (this.symbols[i].aliases.charAt(j) == this.symbols[i].symbol) continue; | |
| 2058 out += this.symbols[i].aliases.charAt(j) + " =\n"; | |
| 2059 } | |
| 2060 } | |
| 2061 } | |
| 2062 return out; | |
| 2063 }; | |
| 2064 // output the alphabet as it appears in minimal MEME format | |
| 2065 Alphabet.prototype.as_meme = function() { | |
| 2066 "use strict"; | |
| 2067 function name_as_text(name) { | |
| 2068 var i, c, out; | |
| 2069 out = "\""; | |
| 2070 for (i = 0; i < name.length; i++) { | |
| 2071 c = name.charAt(i); | |
| 2072 if (c == "\"") { | |
| 2073 out += "\\\""; | |
| 2074 } else if (c == "/") { | |
| 2075 out += "\\/"; | |
| 2076 } else if (c == "\\") { | |
| 2077 out += "\\\\"; | |
| 2078 } else { | |
| 2079 out += c; | |
| 2080 } | |
| 2081 } | |
| 2082 out += "\""; | |
| 2083 return out; | |
| 2084 } | |
| 2085 if (this.equals(AlphStd.DNA)) { | |
| 2086 return "ALPHABET= ACGT\n"; | |
| 2087 } else if (this.equals(AlphStd.PROTEIN)) { | |
| 2088 return "ALPHABET= ACDEFGHIKLMNPQRSTVWY\n"; | |
| 2089 } else { | |
| 2090 return "ALPHABET" + | |
| 2091 (this.name != null ? " " + name_as_text(this.name) : "") + | |
| 2092 (this.like != null ? " " + this.like + "-LIKE" : "") + "\n" + | |
| 2093 this.as_text() + "END ALPHABET\n"; | |
| 2094 } | |
| 2095 }; | |
| 2096 | |
| 2097 // Returns a table showing all the letters in the alphabet | |
| 2098 Alphabet.prototype.as_table = function() { | |
| 2099 "use strict"; | |
| 2100 var i, j, row, th, td, aliases, equals, sym; | |
| 2101 var table = document.createElement("table"); | |
| 2102 // create the core symbol header | |
| 2103 row = table.insertRow(table.rows.length); | |
| 2104 th = document.createElement("th"); | |
| 2105 th.appendChild(document.createTextNode("Symbol(s)")); | |
| 2106 row.appendChild(th); | |
| 2107 th = document.createElement("th"); | |
| 2108 th.appendChild(document.createTextNode("Name")); | |
| 2109 row.appendChild(th); | |
| 2110 th = document.createElement("th"); | |
| 2111 if (this.has_complement()) { | |
| 2112 th.appendChild(document.createTextNode("Complement")); | |
| 2113 } | |
| 2114 row.appendChild(th); | |
| 2115 // list the core symbols | |
| 2116 for (i = 0; i < this.ncore; i++) { | |
| 2117 row = table.insertRow(table.rows.length); | |
| 2118 td = document.createElement("td"); | |
| 2119 if (this.symbols[i].colour != null) { | |
| 2120 td.style.color = '#' + this.symbols[i].colour; | |
| 2121 } | |
| 2122 td.appendChild(document.createTextNode(this.symbols[i].symbol)); | |
| 2123 aliases = this.get_aliases(i); | |
| 2124 if (aliases.length > 0) { | |
| 2125 td.appendChild(document.createTextNode(' ' + aliases.split('').join(' '))); | |
| 2126 } | |
| 2127 row.appendChild(td); | |
| 2128 td = document.createElement("td"); | |
| 2129 if (this.symbols[i].name != null) { | |
| 2130 td.appendChild(document.createTextNode(this.symbols[i].name)); | |
| 2131 } | |
| 2132 row.appendChild(td); | |
| 2133 td = document.createElement("td"); | |
| 2134 if (this.symbols[i].complement != null) { | |
| 2135 td.style.color = this.get_colour(this.get_index(this.symbols[i].complement)); | |
| 2136 td.appendChild(document.createTextNode(this.symbols[i].complement)); | |
| 2137 } | |
| 2138 row.appendChild(td); | |
| 2139 } | |
| 2140 // create the ambiguous symbol header | |
| 2141 row = table.insertRow(table.rows.length); | |
| 2142 th = document.createElement("th"); | |
| 2143 th.appendChild(document.createTextNode("Symbol(s)")); | |
| 2144 row.appendChild(th); | |
| 2145 th = document.createElement("th"); | |
| 2146 th.appendChild(document.createTextNode("Name")); | |
| 2147 row.appendChild(th); | |
| 2148 th = document.createElement("th"); | |
| 2149 th.appendChild(document.createTextNode("Matches")); | |
| 2150 row.appendChild(th); | |
| 2151 // list the ambiguous symbols | |
| 2152 for (i = this.ncore; i < this.symbols.length; i++) { | |
| 2153 row = table.insertRow(table.rows.length); | |
| 2154 td = document.createElement("td"); | |
| 2155 if (this.symbols[i].colour != null) { | |
| 2156 td.style.color = '#' + this.symbols[i].colour; | |
| 2157 } | |
| 2158 td.appendChild(document.createTextNode(this.symbols[i].symbol)); | |
| 2159 aliases = this.get_aliases(i); | |
| 2160 if (aliases.length > 0) { | |
| 2161 td.appendChild(document.createTextNode(' ' + aliases.split('').join(' '))); | |
| 2162 } | |
| 2163 row.appendChild(td); | |
| 2164 td = document.createElement("td"); | |
| 2165 if (this.symbols[i].name != null) { | |
| 2166 td.appendChild(document.createTextNode(this.symbols[i].name)); | |
| 2167 } | |
| 2168 row.appendChild(td); | |
| 2169 td = document.createElement("td"); | |
| 2170 equals = this.symbols[i].equals.split(''); | |
| 2171 for (j = 0; j < equals.length; j++) { | |
| 2172 if (j != 0) td.appendChild(document.createTextNode(' ')); | |
| 2173 sym = document.createElement("span"); | |
| 2174 sym.style.color = this.get_colour(this.get_index(equals[j])); | |
| 2175 sym.appendChild(document.createTextNode(equals[j])); | |
| 2176 td.appendChild(sym); | |
| 2177 } | |
| 2178 row.appendChild(td); | |
| 2179 } | |
| 2180 return table; | |
| 2181 }; | |
| 2182 | |
| 2183 // returns a dictionary of the colours for EPS | |
| 2184 Alphabet.prototype._as_eps_dict = function() { | |
| 2185 "use strict"; | |
| 2186 var i, sym, rgb; | |
| 2187 var out = "/fullColourDict <<\n"; | |
| 2188 for (i = 0; i < this.ncore; i++) { | |
| 2189 sym = this.get_symbol(i); | |
| 2190 sym = sym.replace(/\\/g, "\\\\"); | |
| 2191 sym = sym.replace(/\(/g, "\\("); | |
| 2192 sym = sym.replace(/\)/g, "\\)"); | |
| 2193 rgb = this.get_rgb(i); | |
| 2194 out += " (" + sym + ") [" + rgb.red.toFixed(4) + " " + rgb.green.toFixed(4) + " " + rgb.blue.toFixed(4) + "]\n"; | |
| 2195 } | |
| 2196 out += ">> def\n"; | |
| 2197 out += "/mutedColourDict <<\n"; | |
| 2198 for (i = 0; i < this.ncore; i++) { | |
| 2199 sym = this.get_symbol(i); | |
| 2200 sym = sym.replace(/\\/g, "\\\\"); | |
| 2201 sym = sym.replace(/\(/g, "\\("); | |
| 2202 sym = sym.replace(/\)/g, "\\)"); | |
| 2203 rgb = Alphabet.lighten_colour(this.get_rgb(i)); | |
| 2204 out += " (" + sym + ") [" + rgb.red.toFixed(4) + " " + rgb.green.toFixed(4) + " " + rgb.blue.toFixed(4) + "]\n"; | |
| 2205 } | |
| 2206 out += ">> def\n"; | |
| 2207 return out; | |
| 2208 }; | |
| 2209 | |
| 2210 // return the alphabet name or a list of primary symbols | |
| 2211 Alphabet.prototype.toString = function() { | |
| 2212 "use strict"; | |
| 2213 if (this.name != null) { | |
| 2214 return this.name; | |
| 2215 } else { | |
| 2216 return this.get_symbols(); | |
| 2217 } | |
| 2218 }; | |
| 2219 | |
| 2220 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| 2221 // Helper functions | |
| 2222 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| 2223 | |
| 2224 // Convert a colour specified in RGB colourspace values into LAB colourspace | |
| 2225 Alphabet.rgb2lab = function(rgb) { | |
| 2226 "use strict"; | |
| 2227 var xyzHelper, labHelper; | |
| 2228 // XYZ helper | |
| 2229 xyzHelper = function(value) { | |
| 2230 if (value > 0.0445) { | |
| 2231 value = (value + 0.055) / 1.055; | |
| 2232 value = Math.pow(value, 2.4); | |
| 2233 } else { | |
| 2234 value /= 12.92; | |
| 2235 } | |
| 2236 value *= 100; | |
| 2237 return value; | |
| 2238 }; | |
| 2239 // lab helper | |
| 2240 labHelper = function(value) { | |
| 2241 if (value > 0.008856) { | |
| 2242 value = Math.pow(value, 1.0 / 3.0); | |
| 2243 } else { | |
| 2244 value = (7.787 * value) + (16.0 / 116.0); | |
| 2245 } | |
| 2246 return value; | |
| 2247 }; | |
| 2248 // convert into XYZ colourspace | |
| 2249 var c1, c2, c3; | |
| 2250 if (typeof rgb == "number") { | |
| 2251 c1 = xyzHelper(((rgb >> 16) & 0xFF) / 255.0); | |
| 2252 c2 = xyzHelper(((rgb >> 8) & 0xFF) / 255.0); | |
| 2253 c3 = xyzHelper((rgb & 0xFF) / 255.0); | |
| 2254 } else { | |
| 2255 c1 = xyzHelper(rgb.red); | |
| 2256 c2 = xyzHelper(rgb.green); | |
| 2257 c3 = xyzHelper(rgb.blue); | |
| 2258 } | |
| 2259 var x = (c1 * 0.4124) + (c2 * 0.3576) + (c3 * 0.1805); | |
| 2260 var y = (c1 * 0.2126) + (c2 * 0.7152) + (c3 * 0.0722); | |
| 2261 var z = (c1 * 0.0193) + (c2 * 0.1192) + (c3 * 0.9505); | |
| 2262 // convert into Lab colourspace | |
| 2263 c1 = labHelper(x / 95.047); | |
| 2264 c2 = labHelper(y / 100.0); | |
| 2265 c3 = labHelper(z / 108.883); | |
| 2266 var l = (116.0 * c2) - 16; | |
| 2267 var a = 500.0 * (c1 - c2); | |
| 2268 var b = 200.0 * (c2 - c3); | |
| 2269 return {"l": l, "a": a, "b": b}; | |
| 2270 }; | |
| 2271 | |
| 2272 // Convert a colour specified in HSV colourspace into RGB colourspace | |
| 2273 Alphabet.hsv2rgb = function(hue, sat, value, output_object) { | |
| 2274 // achromatic (grey) | |
| 2275 var r = value; | |
| 2276 var g = value; | |
| 2277 var b = value; | |
| 2278 if (sat != 0) { | |
| 2279 var h = hue / 60.0; | |
| 2280 var i = Math.floor(h); | |
| 2281 var f = h - i; | |
| 2282 var p = value * (1.0 - sat); | |
| 2283 var q = value * (1.0 - (sat * f)); | |
| 2284 var t = value * (1.0 - (sat * (1.0 - f))); | |
| 2285 if (i == 0) { | |
| 2286 r = value; | |
| 2287 g = t; | |
| 2288 b = p; | |
| 2289 } else if (i == 1) { | |
| 2290 r = q; | |
| 2291 g = value; | |
| 2292 b = p; | |
| 2293 } else if (i == 2) { | |
| 2294 r = p; | |
| 2295 g = value; | |
| 2296 b = t; | |
| 2297 } else if (i == 3) { | |
| 2298 r = p; | |
| 2299 g = q; | |
| 2300 b = value; | |
| 2301 } else if (i == 4) { | |
| 2302 r = t; | |
| 2303 g = p; | |
| 2304 b = value; | |
| 2305 } else { | |
| 2306 r = value; | |
| 2307 g = p; | |
| 2308 b = q; | |
| 2309 } | |
| 2310 } | |
| 2311 if (output_object) { | |
| 2312 return {"red": r, "green": g, "blue": b}; | |
| 2313 } else { | |
| 2314 return (Math.floor(r * 255) << 15) | (Math.floor(g * 255) << 8) | (Math.floor(b * 255)); | |
| 2315 } | |
| 2316 }; | |
| 2317 | |
| 2318 // Calculate a distance score between two colours in LAB colourspace | |
| 2319 Alphabet.lab_dist = function(lab1, lab2) { | |
| 2320 var c1 = Math.sqrt((lab1.l * lab1.l) + (lab1.a * lab1.a)); | |
| 2321 var c2 = Math.sqrt((lab2.l * lab2.l) + (lab2.a * lab2.a)); | |
| 2322 var dc = c1 - c2; | |
| 2323 var dl = lab1.l - lab2.l; | |
| 2324 var da = lab1.a - lab2.a; | |
| 2325 var db = lab1.b - lab2.b; | |
| 2326 // we don't want NaN due to rounding errors so fudge things a bit... | |
| 2327 var dh = 0; | |
| 2328 var dh_squared = (da * da) + (db * db) - (dc * dc); | |
| 2329 if (dh_squared > 0) { | |
| 2330 dh = Math.sqrt(dh_squared); | |
| 2331 } | |
| 2332 var first = dl; | |
| 2333 var second = dc / (1.0 + (0.045 * c1)); | |
| 2334 var third = dh / (1.0 + (0.015 * c1)); | |
| 2335 return Math.sqrt((first * first) + (second * second) + (third * third)); | |
| 2336 }; | |
| 2337 | |
| 2338 // convert an RGB value into a HSL value | |
| 2339 Alphabet.rgb2hsl = function(rgb) { | |
| 2340 "use strict"; | |
| 2341 var min, max, delta, h, s, l, r, g, b; | |
| 2342 if (typeof rgb == "number") { | |
| 2343 r = ((rgb >> 16) & 0xFF) / 255.0; | |
| 2344 g = ((rgb >> 8) & 0xFF) / 255.0; | |
| 2345 b = (rgb & 0xFF) / 255.0; | |
| 2346 } else { | |
| 2347 r = rgb.red; | |
| 2348 g = rgb.green; | |
| 2349 b = rgb.blue; | |
| 2350 } | |
| 2351 min = Math.min(r, g, b); | |
| 2352 max = Math.max(r, g, b); | |
| 2353 delta = max - min; | |
| 2354 l = min + (delta / 2); | |
| 2355 if (max == min) { | |
| 2356 h = 0; // achromatic (grayscale) | |
| 2357 s = 0; | |
| 2358 } else { | |
| 2359 if (l > 0.5) { | |
| 2360 s = delta / (2 - max - min); | |
| 2361 } else { | |
| 2362 s = delta / (max + min); | |
| 2363 } | |
| 2364 if (max == r) { | |
| 2365 h = (g - b) / delta; | |
| 2366 if (g < b) h += 6; | |
| 2367 } else if (max == g) { | |
| 2368 h = ((b - r) / delta) + 2; | |
| 2369 } else { | |
| 2370 h = ((r - g) / delta) + 4; | |
| 2371 } | |
| 2372 h /= 6; | |
| 2373 } | |
| 2374 return {"h": h, "s": s, "l": l}; | |
| 2375 }; | |
| 2376 | |
| 2377 // convert a HSL value into an RGB value | |
| 2378 Alphabet.hsl2rgb = function(hsl, output_object) { | |
| 2379 "use strict"; | |
| 2380 function _hue(p, q, t) { | |
| 2381 "use strict"; | |
| 2382 if (t < 0) t += 1; | |
| 2383 else if (t > 1) t -= 1; | |
| 2384 if (t < (1.0 / 6.0)) { | |
| 2385 return p + ((q - p) * 6.0 * t); | |
| 2386 } else if (t < 0.5) { | |
| 2387 return q; | |
| 2388 } else if (t < (2.0 / 3.0)) { | |
| 2389 return p + ((q - p) * ((2.0 / 3.0) - t) * 6.0); | |
| 2390 } else { | |
| 2391 return p; | |
| 2392 } | |
| 2393 } | |
| 2394 var r, g, b, p, q; | |
| 2395 if (hsl.s == 0) { | |
| 2396 // achromatic (grayscale) | |
| 2397 r = hsl.l; | |
| 2398 g = hsl.l; | |
| 2399 b = hsl.l; | |
| 2400 } else { | |
| 2401 if (hsl.l < 0.5) { | |
| 2402 q = hsl.l * (1 + hsl.s); | |
| 2403 } else { | |
| 2404 q = hsl.l + hsl.s - (hsl.l * hsl.s); | |
| 2405 } | |
| 2406 p = (2 * hsl.l) - q; | |
| 2407 r = _hue(p, q, hsl.h + (1.0 / 3.0)); | |
| 2408 g = _hue(p, q, hsl.h); | |
| 2409 b = _hue(p, q, hsl.h - (1.0 / 3.0)); | |
| 2410 } | |
| 2411 if (output_object) { | |
| 2412 return {"red": r, "green": g, "blue": b}; | |
| 2413 } else { | |
| 2414 return (Math.floor(r * 255) << 15) | (Math.floor(g * 255) << 8) | (Math.floor(b * 255)); | |
| 2415 } | |
| 2416 }; | |
| 2417 | |
| 2418 Alphabet.lighten_colour = function(rgb) { | |
| 2419 "use strict"; | |
| 2420 var hsl = Alphabet.rgb2hsl(rgb); | |
| 2421 hsl.l += (1.0 - hsl.l) * 2 / 3; | |
| 2422 return Alphabet.hsl2rgb(hsl, typeof rgb != "number"); | |
| 2423 }; | |
| 2424 | |
| 2425 //====================================================================== | |
| 2426 // end Alphabet object | |
| 2427 //====================================================================== | |
| 2428 | |
| 2429 //====================================================================== | |
| 2430 // start StandardAlphabet object | |
| 2431 //====================================================================== | |
| 2432 | |
| 2433 // an extension of the alphabet object to support some additional fields | |
| 2434 // only present in standard alphabets. | |
| 2435 var StandardAlphabet = function(enum_code, enum_name, alphabet_data) { | |
| 2436 Alphabet.apply(this, [alphabet_data]); | |
| 2437 this.enum_code = enum_code; | |
| 2438 this.enum_name = enum_name; | |
| 2439 }; | |
| 2440 StandardAlphabet.prototype = Alphabet.prototype; | |
| 2441 StandardAlphabet.prototype.constructor = StandardAlphabet; | |
| 2442 | |
| 2443 // A unique code for this standard alphabet. | |
| 2444 // This code will be a power of 2 to enable creation of bitsets for | |
| 2445 // a selection of standard alphabets. | |
| 2446 StandardAlphabet.prototype.get_code = function() { | |
| 2447 return this.enum_code; | |
| 2448 }; | |
| 2449 | |
| 2450 // A unique name for this standard alphabet. | |
| 2451 // this name will be all upper case and the same as the property that | |
| 2452 // refers to this alphabet in the AlphStd collection. | |
| 2453 StandardAlphabet.prototype.get_enum = function() { | |
| 2454 return this.enum_name; | |
| 2455 }; | |
| 2456 | |
| 2457 //====================================================================== | |
| 2458 // end StandardAlphabet object | |
| 2459 //====================================================================== | |
| 2460 | |
| 2461 // A collection of standard alphabets. | |
| 2462 var AlphStd = { | |
| 2463 RNA: new StandardAlphabet(1, "RNA", { | |
| 2464 "name": "RNA", | |
| 2465 "like": "RNA", | |
| 2466 "ncore": 4, | |
| 2467 "symbols": [ | |
| 2468 {"symbol": "A", "name": "Adenine", "colour": "CC0000"}, | |
| 2469 {"symbol": "C", "name": "Cytosine", "colour": "0000CC"}, | |
| 2470 {"symbol": "G", "name": "Guanine", "colour": "FFB300"}, | |
| 2471 {"symbol": "U", "name": "Uracil", "colour": "008000", | |
| 2472 "aliases": "T"}, | |
| 2473 {"symbol": "N", "name": "Any base", "equals": "ACGU", "aliases": "X."}, | |
| 2474 {"symbol": "V", "name": "Not U", "equals": "ACG"}, | |
| 2475 {"symbol": "H", "name": "Not G", "equals": "ACU"}, | |
| 2476 {"symbol": "D", "name": "Not C", "equals": "AGU"}, | |
| 2477 {"symbol": "B", "name": "Not A", "equals": "CGU"}, | |
| 2478 {"symbol": "M", "name": "Amino", "equals": "AC"}, | |
| 2479 {"symbol": "R", "name": "Purine", "equals": "AG"}, | |
| 2480 {"symbol": "W", "name": "Weak", "equals": "AU"}, | |
| 2481 {"symbol": "S", "name": "Strong", "equals": "CG"}, | |
| 2482 {"symbol": "Y", "name": "Pyrimidine", "equals": "CU"}, | |
| 2483 {"symbol": "K", "name": "Keto", "equals": "GU"} | |
| 2484 ] | |
| 2485 }), | |
| 2486 DNA: new StandardAlphabet(2, "DNA", { | |
| 2487 "name": "DNA", | |
| 2488 "like": "DNA", | |
| 2489 "ncore": 4, | |
| 2490 "symbols": [ | |
| 2491 {"symbol": "A", "name": "Adenine", "colour": "CC0000", "complement": "T"}, | |
| 2492 {"symbol": "C", "name": "Cytosine", "colour": "0000CC", "complement": "G"}, | |
| 2493 {"symbol": "G", "name": "Guanine", "colour": "FFB300", "complement": "C"}, | |
| 2494 {"symbol": "T", "name": "Thymine", "colour": "008000", "complement": "A", | |
| 2495 "aliases": "U"}, | |
| 2496 {"symbol": "N", "name": "Any base", "equals": "ACGT", "aliases": "X."}, | |
| 2497 {"symbol": "V", "name": "Not T", "equals": "ACG"}, | |
| 2498 {"symbol": "H", "name": "Not G", "equals": "ACT"}, | |
| 2499 {"symbol": "D", "name": "Not C", "equals": "AGT"}, | |
| 2500 {"symbol": "B", "name": "Not A", "equals": "CGT"}, | |
| 2501 {"symbol": "M", "name": "Amino", "equals": "AC"}, | |
| 2502 {"symbol": "R", "name": "Purine", "equals": "AG"}, | |
| 2503 {"symbol": "W", "name": "Weak", "equals": "AT"}, | |
| 2504 {"symbol": "S", "name": "Strong", "equals": "CG"}, | |
| 2505 {"symbol": "Y", "name": "Pyrimidine", "equals": "CT"}, | |
| 2506 {"symbol": "K", "name": "Keto", "equals": "GT"} | |
| 2507 ] | |
| 2508 }), | |
| 2509 PROTEIN: new StandardAlphabet(4, "PROTEIN", { | |
| 2510 "name": "Protein", | |
| 2511 "like": "PROTEIN", | |
| 2512 "ncore": 20, | |
| 2513 "symbols": [ | |
| 2514 {"symbol": "A", "name": "Alanine", "colour": "0000CC"}, | |
| 2515 {"symbol": "C", "name": "Cysteine", "colour": "0000CC"}, | |
| 2516 {"symbol": "D", "name": "Aspartic acid", "colour": "FF00FF"}, | |
| 2517 {"symbol": "E", "name": "Glutamic acid", "colour": "FF00FF"}, | |
| 2518 {"symbol": "F", "name": "Phenylalanine", "colour": "0000CC"}, | |
| 2519 {"symbol": "G", "name": "Glycine", "colour": "FFB300"}, | |
| 2520 {"symbol": "H", "name": "Histidine", "colour": "FFCCCC"}, | |
| 2521 {"symbol": "I", "name": "Isoleucine", "colour": "0000CC"}, | |
| 2522 {"symbol": "K", "name": "Lysine", "colour": "CC0000"}, | |
| 2523 {"symbol": "L", "name": "Leucine", "colour": "0000CC"}, | |
| 2524 {"symbol": "M", "name": "Methionine", "colour": "0000CC"}, | |
| 2525 {"symbol": "N", "name": "Asparagine", "colour": "008000"}, | |
| 2526 {"symbol": "P", "name": "Proline", "colour": "FFFF00"}, | |
| 2527 {"symbol": "Q", "name": "Glutamine", "colour": "008000"}, | |
| 2528 {"symbol": "R", "name": "Arginine", "colour": "CC0000"}, | |
| 2529 {"symbol": "S", "name": "Serine", "colour": "008000"}, | |
| 2530 {"symbol": "T", "name": "Threonine", "colour": "008000"}, | |
| 2531 {"symbol": "V", "name": "Valine", "colour": "0000CC"}, | |
| 2532 {"symbol": "W", "name": "Tryptophan", "colour": "0000CC"}, | |
| 2533 {"symbol": "Y", "name": "Tyrosine", "colour": "33E6CC"}, | |
| 2534 {"symbol": "X", "name": "Any amino acid", "equals": "ACDEFGHIKLMNPQRSTVWY", "aliases": "*."}, | |
| 2535 {"symbol": "B", "name": "Asparagine or Aspartic acid", "equals": "DN"}, | |
| 2536 {"symbol": "Z", "name": "Glutamine or Glutamic acid", "equals": "EQ"}, | |
| 2537 {"symbol": "J", "name": "Leucine or Isoleucine", "equals": "IL"} | |
| 2538 ] | |
| 2539 }) | |
| 2540 }; | |
| 2541 | |
| 2542 //====================================================================== | |
| 2543 // start Symbol object | |
| 2544 //====================================================================== | |
| 2545 var Symbol = function(alph_index, scale, alphabet) { | |
| 2546 "use strict"; | |
| 2547 //variable prototype | |
| 2548 this.symbol = alphabet.get_symbol(alph_index); | |
| 2549 this.scale = scale; | |
| 2550 this.colour = alphabet.get_colour(alph_index); | |
| 2551 }; | |
| 2552 | |
| 2553 Symbol.prototype.get_symbol = function() { | |
| 2554 "use strict"; | |
| 2555 return this.symbol; | |
| 2556 }; | |
| 2557 | |
| 2558 Symbol.prototype.get_scale = function() { | |
| 2559 "use strict"; | |
| 2560 return this.scale; | |
| 2561 }; | |
| 2562 | |
| 2563 Symbol.prototype.get_colour = function() { | |
| 2564 "use strict"; | |
| 2565 return this.colour; | |
| 2566 }; | |
| 2567 | |
| 2568 Symbol.prototype.toString = function() { | |
| 2569 "use strict"; | |
| 2570 return this.symbol + " " + (Math.round(this.scale*1000)/10) + "%"; | |
| 2571 }; | |
| 2572 | |
| 2573 function compare_symbol(sym1, sym2) { | |
| 2574 "use strict"; | |
| 2575 if (sym1.get_scale() < sym2.get_scale()) { | |
| 2576 return -1; | |
| 2577 } else if (sym1.get_scale() > sym2.get_scale()) { | |
| 2578 return 1; | |
| 2579 } else { | |
| 2580 return 0; | |
| 2581 } | |
| 2582 } | |
| 2583 //====================================================================== | |
| 2584 // end Symbol object | |
| 2585 //====================================================================== | |
| 2586 | |
| 2587 //====================================================================== | |
| 2588 // start Pspm object | |
| 2589 //====================================================================== | |
| 2590 var Pspm = function(matrix, name, ltrim, rtrim, nsites, evalue, pssm, alt) { | |
| 2591 "use strict"; | |
| 2592 var row, col, data, row_sum, delta, evalue_re; | |
| 2593 if (typeof name !== "string") { | |
| 2594 name = ""; | |
| 2595 } | |
| 2596 this.name = name; | |
| 2597 //construct | |
| 2598 if (matrix instanceof Pspm) { | |
| 2599 // copy constructor | |
| 2600 this.alph_length = matrix.alph_length; | |
| 2601 this.motif_length = matrix.motif_length; | |
| 2602 this.name = matrix.name; | |
| 2603 this.alt = matrix.alt; | |
| 2604 this.nsites = matrix.nsites; | |
| 2605 this.evalue = matrix.evalue; | |
| 2606 this.ltrim = matrix.ltrim; | |
| 2607 this.rtrim = matrix.rtrim; | |
| 2608 this.pspm = []; | |
| 2609 for (row = 0; row < matrix.motif_length; row++) { | |
| 2610 this.pspm[row] = []; | |
| 2611 for (col = 0; col < matrix.alph_length; col++) { | |
| 2612 this.pspm[row][col] = matrix.pspm[row][col]; | |
| 2613 } | |
| 2614 } | |
| 2615 if (matrix.pssm != null) { | |
| 2616 this.pssm = []; | |
| 2617 for (row = 0; row < matrix.motif_length; row++) { | |
| 2618 this.pspm[row] = []; | |
| 2619 for (col = 0; col < matrix.alph_length; col++) { | |
| 2620 this.pssm[row][col] = matrix.pssm[row][col]; | |
| 2621 } | |
| 2622 } | |
| 2623 } | |
| 2624 } else { | |
| 2625 // check parameters | |
| 2626 if (ltrim == null) { | |
| 2627 ltrim = 0; | |
| 2628 } else if (typeof ltrim !== "number" || ltrim % 1 !== 0 || ltrim < 0) { | |
| 2629 throw new Error("ltrim must be a non-negative integer, got: " + ltrim); | |
| 2630 } | |
| 2631 if (rtrim == null) { | |
| 2632 rtrim = 0; | |
| 2633 } else if (typeof rtrim !== "number" || rtrim % 1 !== 0 || rtrim < 0) { | |
| 2634 throw new Error("rtrim must be a non-negative integer, got: " + rtrim); | |
| 2635 } | |
| 2636 if (nsites != null) { | |
| 2637 if (typeof nsites !== "number" || nsites < 0) { | |
| 2638 throw new Error("nsites must be a positive number, got: " + nsites); | |
| 2639 } else if (nsites == 0) { | |
| 2640 nsites = null; | |
| 2641 } | |
| 2642 } | |
| 2643 if (evalue != null) { | |
| 2644 if (typeof evalue === "number") { | |
| 2645 if (evalue < 0) { | |
| 2646 throw new Error("evalue must be a non-negative number, got: " + evalue); | |
| 2647 } | |
| 2648 } else if (typeof evalue === "string") { | |
| 2649 evalue_re = /^((?:[+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)|inf)$/; | |
| 2650 if (!evalue_re.test(evalue)) { | |
| 2651 throw new Error("evalue must be a non-negative number, got: " + evalue); | |
| 2652 } | |
| 2653 } else { | |
| 2654 throw new Error("evalue must be a non-negative number, got: " + evalue); | |
| 2655 } | |
| 2656 } | |
| 2657 // set properties | |
| 2658 this.name = name; | |
| 2659 this.alt = alt; | |
| 2660 this.nsites = nsites; | |
| 2661 this.evalue = evalue; | |
| 2662 this.ltrim = ltrim; | |
| 2663 this.rtrim = rtrim; | |
| 2664 if (typeof matrix === "string") { | |
| 2665 // string constructor | |
| 2666 data = parse_pspm_string(matrix); | |
| 2667 this.alph_length = data["alph_length"]; | |
| 2668 this.motif_length = data["motif_length"]; | |
| 2669 this.pspm = data["pspm"]; | |
| 2670 if (this.evalue == null) { | |
| 2671 if (data["evalue"] != null) { | |
| 2672 this.evalue = data["evalue"]; | |
| 2673 } else { | |
| 2674 this.evalue = 0; | |
| 2675 } | |
| 2676 } | |
| 2677 if (this.nsites == null) { | |
| 2678 if (typeof data["nsites"] === "number") { | |
| 2679 this.nsites = data["nsites"]; | |
| 2680 } else { | |
| 2681 this.nsites = 20; | |
| 2682 } | |
| 2683 } | |
| 2684 } else { | |
| 2685 // assume pspm is a nested array | |
| 2686 this.motif_length = matrix.length; | |
| 2687 this.alph_length = (matrix.length > 0 ? matrix[0].length : 0); | |
| 2688 if (this.nsites == null) { | |
| 2689 this.nsites = 20; | |
| 2690 } | |
| 2691 if (this.evalue == null) { | |
| 2692 this.evalue = 0; | |
| 2693 } | |
| 2694 this.pspm = []; | |
| 2695 // copy pspm and check | |
| 2696 for (row = 0; row < this.motif_length; row++) { | |
| 2697 if (this.alph_length != matrix[row].length) { | |
| 2698 throw new Error("COLUMN_MISMATCH"); | |
| 2699 } | |
| 2700 this.pspm[row] = []; | |
| 2701 row_sum = 0; | |
| 2702 for (col = 0; col < this.alph_length; col++) { | |
| 2703 this.pspm[row][col] = matrix[row][col]; | |
| 2704 row_sum += this.pspm[row][col]; | |
| 2705 } | |
| 2706 delta = 0.1; | |
| 2707 if (isNaN(row_sum) || (row_sum > 1 && (row_sum - 1) > delta) || | |
| 2708 (row_sum < 1 && (1 - row_sum) > delta)) { | |
| 2709 throw new Error("INVALID_SUM"); | |
| 2710 } | |
| 2711 } | |
| 2712 // copy pssm | |
| 2713 if (pssm != null) { | |
| 2714 this.pssm = []; | |
| 2715 for (row = 0; row < this.motif_length; row++) { | |
| 2716 this.pssm[row] = []; | |
| 2717 for (col = 0; col < this.alph_length; col++) { | |
| 2718 this.pssm[row][col] = pssm[row][col]; | |
| 2719 } | |
| 2720 } | |
| 2721 } | |
| 2722 } | |
| 2723 } | |
| 2724 }; | |
| 2725 | |
| 2726 Pspm.prototype.copy = function() { | |
| 2727 "use strict"; | |
| 2728 return new Pspm(this); | |
| 2729 }; | |
| 2730 | |
| 2731 Pspm.prototype.reverse = function() { | |
| 2732 "use strict"; | |
| 2733 var x, y, temp, temp_trim; | |
| 2734 //reverse | |
| 2735 x = 0; | |
| 2736 y = this.motif_length-1; | |
| 2737 while (x < y) { | |
| 2738 temp = this.pspm[x]; | |
| 2739 this.pspm[x] = this.pspm[y]; | |
| 2740 this.pspm[y] = temp; | |
| 2741 x++; | |
| 2742 y--; | |
| 2743 } | |
| 2744 // reverse pssm (if defined) | |
| 2745 if (typeof this.pssm !== "undefined") { | |
| 2746 //reverse | |
| 2747 x = 0; | |
| 2748 y = this.motif_length-1; | |
| 2749 while (x < y) { | |
| 2750 temp = this.pssm[x]; | |
| 2751 this.pspm[x] = this.pssm[y]; | |
| 2752 this.pssm[y] = temp; | |
| 2753 x++; | |
| 2754 y--; | |
| 2755 } | |
| 2756 } | |
| 2757 //swap triming | |
| 2758 temp_trim = this.ltrim; | |
| 2759 this.ltrim = this.rtrim; | |
| 2760 this.rtrim = temp_trim; | |
| 2761 return this; //allow function chaining... | |
| 2762 }; | |
| 2763 | |
| 2764 Pspm.prototype.reverse_complement = function(alphabet) { | |
| 2765 "use strict"; | |
| 2766 var x, y, temp, i, row, c, temp_trim; | |
| 2767 if (this.alph_length != alphabet.get_size_core()) { | |
| 2768 throw new Error("The alphabet size does not match the size of the pspm."); | |
| 2769 } | |
| 2770 if (!alphabet.has_complement()) { | |
| 2771 throw new Error("The specified alphabet can not be complemented."); | |
| 2772 } | |
| 2773 // reverse motif | |
| 2774 this.reverse(); | |
| 2775 //complement | |
| 2776 for (x = 0; x < this.motif_length; x++) { | |
| 2777 row = this.pspm[x]; | |
| 2778 for (i = 0; i < row.length; i++) { | |
| 2779 c = alphabet.get_complement(i); | |
| 2780 if (c < i) continue; | |
| 2781 temp = row[i]; | |
| 2782 row[i] = row[c]; | |
| 2783 row[c] = temp; | |
| 2784 } | |
| 2785 } | |
| 2786 // complement pssm (if defined) | |
| 2787 if (typeof this.pssm !== "undefined") { | |
| 2788 //complement | |
| 2789 for (x = 0; x < this.motif_length; x++) { | |
| 2790 row = this.pssm[x]; | |
| 2791 for (i = 0; i < row.length; i++) { | |
| 2792 c = alphabet.get_complement(i); | |
| 2793 if (c < i) continue; | |
| 2794 temp = row[i]; | |
| 2795 row[i] = row[c]; | |
| 2796 row[c] = temp; | |
| 2797 } | |
| 2798 } | |
| 2799 } | |
| 2800 return this; //allow function chaining... | |
| 2801 }; | |
| 2802 | |
| 2803 Pspm.prototype.get_stack = function(position, alphabet, ssc) { | |
| 2804 "use strict"; | |
| 2805 var row, stack_ic, alphabet_ic, stack, i, sym; | |
| 2806 if (this.alph_length != alphabet.get_size_core()) { | |
| 2807 throw new Error("The alphabet size does not match the size of the pspm."); | |
| 2808 } | |
| 2809 row = this.pspm[position]; | |
| 2810 stack_ic = this.get_stack_ic(position, alphabet); | |
| 2811 if (ssc) stack_ic -= this.get_error(alphabet); | |
| 2812 alphabet_ic = alphabet.get_ic(); | |
| 2813 stack = []; | |
| 2814 for (i = 0; i < this.alph_length; i++) { | |
| 2815 sym = new Symbol(i, row[i]*stack_ic/alphabet_ic, alphabet); | |
| 2816 if (sym.get_scale() <= 0) { | |
| 2817 continue; | |
| 2818 } | |
| 2819 stack.push(sym); | |
| 2820 } | |
| 2821 stack.sort(compare_symbol); | |
| 2822 return stack; | |
| 2823 }; | |
| 2824 | |
| 2825 Pspm.prototype.get_stack_ic = function(position, alphabet) { | |
| 2826 "use strict"; | |
| 2827 var row, H, i; | |
| 2828 if (this.alph_length != alphabet.get_size_core()) { | |
| 2829 throw new Error("The alphabet size does not match the size fo the pspm."); | |
| 2830 } | |
| 2831 row = this.pspm[position]; | |
| 2832 H = 0; | |
| 2833 for (i = 0; i < this.alph_length; i++) { | |
| 2834 if (row[i] === 0) { | |
| 2835 continue; | |
| 2836 } | |
| 2837 H -= (row[i] * (Math.log(row[i]) / Math.LN2)); | |
| 2838 } | |
| 2839 return alphabet.get_ic() - H; | |
| 2840 }; | |
| 2841 | |
| 2842 Pspm.prototype.get_error = function(alphabet) { | |
| 2843 "use strict"; | |
| 2844 if (this.nsites === 0) { | |
| 2845 return 0; | |
| 2846 } | |
| 2847 return (alphabet.get_size_core()-1) / (2 * Math.LN2 * this.nsites); | |
| 2848 }; | |
| 2849 | |
| 2850 Pspm.prototype.get_motif_length = function() { | |
| 2851 "use strict"; | |
| 2852 return this.motif_length; | |
| 2853 }; | |
| 2854 | |
| 2855 Pspm.prototype.get_alph_length = function() { | |
| 2856 "use strict"; | |
| 2857 return this.alph_length; | |
| 2858 }; | |
| 2859 | |
| 2860 Pspm.prototype.get_left_trim = function() { | |
| 2861 "use strict"; | |
| 2862 return this.ltrim; | |
| 2863 }; | |
| 2864 | |
| 2865 Pspm.prototype.get_right_trim = function() { | |
| 2866 "use strict"; | |
| 2867 return this.rtrim; | |
| 2868 }; | |
| 2869 | |
| 2870 Pspm.prototype.as_best_match = function(alphabet) { | |
| 2871 "use strict"; | |
| 2872 var match, odds, best_odds, best_index; | |
| 2873 var i, j; | |
| 2874 match = ""; | |
| 2875 for (i = 0; i < this.motif_length; i++) { | |
| 2876 best_index = 0; | |
| 2877 best_odds = this.pspm[i][0] / alphabet.get_bg_freq(0); | |
| 2878 for (j = 1; j < this.alph_length; j++) { | |
| 2879 odds = this.pspm[i][j] / alphabet.get_bg_freq(j); | |
| 2880 if (odds > best_odds) { | |
| 2881 best_odds = odds; | |
| 2882 best_index = j; | |
| 2883 } | |
| 2884 } | |
| 2885 match += alphabet.get_symbol(best_index); | |
| 2886 } | |
| 2887 return match; | |
| 2888 }; | |
| 2889 | |
| 2890 Pspm.prototype.as_count_matrix = function() { | |
| 2891 "use strict"; | |
| 2892 var count, count_text, text; | |
| 2893 var i, j; | |
| 2894 text = ""; | |
| 2895 for (i = 0; i < this.motif_length; i++) { | |
| 2896 if (i !== 0) { | |
| 2897 text += "\n"; | |
| 2898 } | |
| 2899 for (j = 0; j < this.alph_length; j++) { | |
| 2900 if (j !== 0) { | |
| 2901 text += " "; | |
| 2902 } | |
| 2903 count = Math.round(this.nsites * this.pspm[i][j]); | |
| 2904 count_text = "" + count; | |
| 2905 // pad up to length of 4 | |
| 2906 if (count_text.length < 4) { | |
| 2907 text += (new Array(5 - count_text.length)).join(" ") + count_text; | |
| 2908 } else { | |
| 2909 text += count_text; | |
| 2910 } | |
| 2911 } | |
| 2912 } | |
| 2913 return text; | |
| 2914 }; | |
| 2915 | |
| 2916 Pspm.prototype.as_probability_matrix = function() { | |
| 2917 "use strict"; | |
| 2918 var text; | |
| 2919 var i, j; | |
| 2920 text = ""; | |
| 2921 for (i = 0; i < this.motif_length; i++) { | |
| 2922 if (i !== 0) { | |
| 2923 text += "\n"; | |
| 2924 } | |
| 2925 for (j = 0; j < this.alph_length; j++) { | |
| 2926 if (j !== 0) { | |
| 2927 text += " "; | |
| 2928 } | |
| 2929 text += this.pspm[i][j].toFixed(6); | |
| 2930 } | |
| 2931 } | |
| 2932 return text; | |
| 2933 }; | |
| 2934 | |
| 2935 Pspm.prototype.as_score_matrix = function(alphabet, pseudo) { | |
| 2936 "use strict"; | |
| 2937 var me, score, out, row, col, score_text; | |
| 2938 me = this; | |
| 2939 if (typeof this.pssm === "undefined") { | |
| 2940 if (!(typeof alphabet === "object" && alphabet != null && alphabet instanceof Alphabet)) { | |
| 2941 throw new Error("The alphabet is required to generate the pssm."); | |
| 2942 } | |
| 2943 if (typeof pseudo === "undefined") { | |
| 2944 pseudo = 0.01; | |
| 2945 } else if (typeof pseudo !== "number" || pseudo < 0) { | |
| 2946 throw new Error("Expected positive number for pseudocount"); | |
| 2947 } | |
| 2948 score = function(row, col) { | |
| 2949 "use strict"; | |
| 2950 var p, bg, p2; | |
| 2951 p = me.pspm[row][col]; | |
| 2952 bg = alphabet.get_bg_freq(col); | |
| 2953 p2 = (p * me.nsites + bg * pseudo) / (me.nsites + pseudo); | |
| 2954 return (p2 > 0 ? Math.round((Math.log(p2 / bg) / Math.LN2) * 100) : -10000); | |
| 2955 }; | |
| 2956 } else { | |
| 2957 score = function(row, col) { | |
| 2958 "use strict"; | |
| 2959 return me.pssm[row][col]; | |
| 2960 }; | |
| 2961 } | |
| 2962 out = ""; | |
| 2963 for (row = 0; row < this.motif_length; row++) { | |
| 2964 for (col = 0; col < this.alph_length; col++) { | |
| 2965 if (col !== 0) { | |
| 2966 out += " "; | |
| 2967 } | |
| 2968 score_text = "" + score(row, col); | |
| 2969 // pad out to 6 characters | |
| 2970 if (score_text.length < 6) { | |
| 2971 out += (new Array(7 - score_text.length)).join(" ") + score_text; | |
| 2972 } else { | |
| 2973 out += score_text; | |
| 2974 } | |
| 2975 } | |
| 2976 out += "\n"; | |
| 2977 } | |
| 2978 return out; | |
| 2979 } | |
| 2980 | |
| 2981 Pspm.prototype.as_pspm = function() { | |
| 2982 "use strict"; | |
| 2983 return "letter-probability matrix: alength= " + this.alph_length + | |
| 2984 " w= " + this.motif_length + " nsites= " + this.nsites + | |
| 2985 " E= " + (typeof this.evalue === "number" ? | |
| 2986 this.evalue.toExponential() : this.evalue) + "\n" + | |
| 2987 this.as_probability_matrix(); | |
| 2988 }; | |
| 2989 | |
| 2990 Pspm.prototype.as_pssm = function(alphabet, pseudo) { | |
| 2991 "use strict"; | |
| 2992 return "log-odds matrix: alength= " + this.alph_length + | |
| 2993 " w= " + this.motif_length + | |
| 2994 " E= " + (typeof this.evalue == "number" ? | |
| 2995 this.evalue.toExponential() : this.evalue) + "\n" + | |
| 2996 this.as_score_matrix(alphabet, pseudo); | |
| 2997 }; | |
| 2998 | |
| 2999 Pspm.prototype.as_meme = function(options) { | |
| 3000 var with_header, with_pspm, with_pssm, version, alphabet, bg_source, pseudocount, strands; | |
| 3001 var out, alen, i; | |
| 3002 // get the options | |
| 3003 if (typeof options !== "object" || options === null) { | |
| 3004 options = {}; | |
| 3005 } | |
| 3006 with_header = (typeof options["with_header"] === "boolean" ? options["with_header"] : false); | |
| 3007 with_pspm = (typeof options["with_pspm"] === "boolean" ? options["with_pspm"] : false); | |
| 3008 with_pssm = (typeof options["with_pssm"] === "boolean" ? options["with_pssm"] : false); | |
| 3009 if (!with_pspm && !with_pssm) with_pspm = true; | |
| 3010 if (with_header) { | |
| 3011 if (typeof options["version"] === "string" && /^\d+(?:\.\d+){0,2}$/.test(options["version"])) { | |
| 3012 version = options["version"]; | |
| 3013 } else if (typeof options["version"] === "number") { | |
| 3014 version = options["version"].toFixed(0); | |
| 3015 } else { | |
| 3016 version = "4"; | |
| 3017 } | |
| 3018 if (typeof options["strands"] === "number" && options["strands"] === 1) { | |
| 3019 strands = 1; | |
| 3020 } else { | |
| 3021 strands = 2; | |
| 3022 } | |
| 3023 if (typeof options["bg_source"] === "string") { | |
| 3024 bg_source = options["bg_source"]; | |
| 3025 } else { | |
| 3026 bg_source = "unknown source"; | |
| 3027 } | |
| 3028 if (typeof options["alphabet"] === "object" && options["alphabet"] != null | |
| 3029 && options["alphabet"] instanceof Alphabet) { | |
| 3030 alphabet = options["alphabet"]; | |
| 3031 } else { | |
| 3032 throw new Error("The alphabet is required to generate the header."); | |
| 3033 } | |
| 3034 } | |
| 3035 // now create the output | |
| 3036 out = ""; | |
| 3037 if (with_header) { | |
| 3038 out = "MEME version " + version + "\n\n"; | |
| 3039 out += alphabet.as_meme() + "\n"; | |
| 3040 if (alphabet.has_complement()) { // assume DNA has both strands unless otherwise specified | |
| 3041 out += "strands: " + (strands === 1 ? "+" : "+ -") + "\n\n"; | |
| 3042 } | |
| 3043 out += "Background letter frequencies (from " + bg_source + "):\n"; | |
| 3044 alen = alphabet.get_size_core(); | |
| 3045 for (i = 0; i < alen; i++) { | |
| 3046 if (i !== 0) { | |
| 3047 if (i % 9 === 0) { // maximum of nine entries per line | |
| 3048 out += "\n"; | |
| 3049 } else { | |
| 3050 out += " "; | |
| 3051 } | |
| 3052 } | |
| 3053 out += alphabet.get_symbol(i) + " " + alphabet.get_bg_freq(i).toFixed(3); | |
| 3054 } | |
| 3055 } | |
| 3056 out += "\n\n"; | |
| 3057 out += "MOTIF " + this.name + (this.alt == null ? "" : " " + this.alt); | |
| 3058 if (with_pssm) { | |
| 3059 out += "\n\n"; | |
| 3060 out += this.as_pssm(options["alphabet"], options["pseudocount"]); | |
| 3061 } | |
| 3062 if (with_pspm) { | |
| 3063 out += "\n\n"; | |
| 3064 out += this.as_pspm(); | |
| 3065 } | |
| 3066 return out; | |
| 3067 } | |
| 3068 | |
| 3069 Pspm.prototype.toString = function() { | |
| 3070 "use strict"; | |
| 3071 var str, i, row; | |
| 3072 str = ""; | |
| 3073 for (i = 0; i < this.pspm.length; i++) { | |
| 3074 row = this.pspm[i]; | |
| 3075 str += row.join("\t") + "\n"; | |
| 3076 } | |
| 3077 return str; | |
| 3078 }; | |
| 3079 | |
| 3080 function parse_pspm_properties(str) { | |
| 3081 "use strict"; | |
| 3082 var parts, i, eqpos, before, after, properties, prop, num, num_re; | |
| 3083 num_re = /^((?:[+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)|inf)$/; | |
| 3084 parts = trim(str).split(/\s+/); | |
| 3085 // split up words containing = | |
| 3086 for (i = 0; i < parts.length;) { | |
| 3087 eqpos = parts[i].indexOf("="); | |
| 3088 if (eqpos != -1) { | |
| 3089 before = parts[i].substr(0, eqpos); | |
| 3090 after = parts[i].substr(eqpos+1); | |
| 3091 if (before.length > 0 && after.length > 0) { | |
| 3092 parts.splice(i, 1, before, "=", after); | |
| 3093 i += 3; | |
| 3094 } else if (before.length > 0) { | |
| 3095 parts.splice(i, 1, before, "="); | |
| 3096 i += 2; | |
| 3097 } else if (after.length > 0) { | |
| 3098 parts.splice(i, 1, "=", after); | |
| 3099 i += 2; | |
| 3100 } else { | |
| 3101 parts.splice(i, 1, "="); | |
| 3102 i++; | |
| 3103 } | |
| 3104 } else { | |
| 3105 i++; | |
| 3106 } | |
| 3107 } | |
| 3108 properties = {}; | |
| 3109 for (i = 0; i < parts.length; i += 3) { | |
| 3110 if (parts.length - i < 3) { | |
| 3111 throw new Error("Expected PSPM property was incomplete. "+ | |
| 3112 "Remaing parts are: " + parts.slice(i).join(" ")); | |
| 3113 } | |
| 3114 if (parts[i+1] !== "=") { | |
| 3115 throw new Error("Expected '=' in PSPM property between key and " + | |
| 3116 "value but got " + parts[i+1]); | |
| 3117 } | |
| 3118 prop = parts[i].toLowerCase(); | |
| 3119 num = parts[i+2]; | |
| 3120 if (!num_re.test(num)) { | |
| 3121 throw new Error("Expected numeric value for PSPM property '" + | |
| 3122 prop + "' but got '" + num + "'"); | |
| 3123 } | |
| 3124 properties[prop] = num; | |
| 3125 } | |
| 3126 return properties; | |
| 3127 } | |
| 3128 | |
| 3129 function parse_pspm_string(pspm_string) { | |
| 3130 "use strict"; | |
| 3131 var header_re, lines, first_line, line_num, col_num, alph_length, | |
| 3132 motif_length, nsites, evalue, pspm, i, line, match, props, parts, | |
| 3133 j, prob; | |
| 3134 header_re = /^letter-probability\s+matrix:(.*)$/i; | |
| 3135 lines = pspm_string.split(/\n/); | |
| 3136 first_line = true; | |
| 3137 line_num = 0; | |
| 3138 col_num = 0; | |
| 3139 alph_length; | |
| 3140 motif_length; | |
| 3141 nsites; | |
| 3142 evalue; | |
| 3143 pspm = []; | |
| 3144 for (i = 0; i < lines.length; i++) { | |
| 3145 line = trim(lines[i]); | |
| 3146 if (line.length === 0) { | |
| 3147 continue; | |
| 3148 } | |
| 3149 // check the first line for a header though allow matrices without it | |
| 3150 if (first_line) { | |
| 3151 first_line = false; | |
| 3152 match = header_re.exec(line); | |
| 3153 if (match !== null) { | |
| 3154 props = parse_pspm_properties(match[1]); | |
| 3155 if (props.hasOwnProperty("alength")) { | |
| 3156 alph_length = parseFloat(props["alength"]); | |
| 3157 if (alph_length != 4 && alph_length != 20) { | |
| 3158 throw new Error("PSPM property alength should be 4 or 20" + | |
| 3159 " but got " + alph_length); | |
| 3160 } | |
| 3161 } | |
| 3162 if (props.hasOwnProperty("w")) { | |
| 3163 motif_length = parseFloat(props["w"]); | |
| 3164 if (motif_length % 1 !== 0 || motif_length < 1) { | |
| 3165 throw new Error("PSPM property w should be an integer larger " + | |
| 3166 "than zero but got " + motif_length); | |
| 3167 } | |
| 3168 } | |
| 3169 if (props.hasOwnProperty("nsites")) { | |
| 3170 nsites = parseFloat(props["nsites"]); | |
| 3171 if (nsites <= 0) { | |
| 3172 throw new Error("PSPM property nsites should be larger than " + | |
| 3173 "zero but got " + nsites); | |
| 3174 } | |
| 3175 } | |
| 3176 if (props.hasOwnProperty("e")) { | |
| 3177 evalue = props["e"]; | |
| 3178 if (evalue < 0) { | |
| 3179 throw new Error("PSPM property evalue should be " + | |
| 3180 "non-negative but got " + evalue); | |
| 3181 } | |
| 3182 } | |
| 3183 continue; | |
| 3184 } | |
| 3185 } | |
| 3186 pspm[line_num] = []; | |
| 3187 col_num = 0; | |
| 3188 parts = line.split(/\s+/); | |
| 3189 for (j = 0; j < parts.length; j++) { | |
| 3190 prob = parseFloat(parts[j]); | |
| 3191 if (prob != parts[j] || prob < 0 || prob > 1) { | |
| 3192 throw new Error("Expected probability but got '" + parts[j] + "'"); | |
| 3193 } | |
| 3194 pspm[line_num][col_num] = prob; | |
| 3195 col_num++; | |
| 3196 } | |
| 3197 line_num++; | |
| 3198 } | |
| 3199 if (typeof motif_length === "number") { | |
| 3200 if (pspm.length != motif_length) { | |
| 3201 throw new Error("Expected PSPM to have a motif length of " + | |
| 3202 motif_length + " but it was actually " + pspm.length); | |
| 3203 } | |
| 3204 } else { | |
| 3205 motif_length = pspm.length; | |
| 3206 } | |
| 3207 if (typeof alph_length !== "number") { | |
| 3208 alph_length = pspm[0].length; | |
| 3209 if (alph_length != 4 && alph_length != 20) { | |
| 3210 throw new Error("Expected length of first row in the PSPM to be " + | |
| 3211 "either 4 or 20 but got " + alph_length); | |
| 3212 } | |
| 3213 } | |
| 3214 for (i = 0; i < pspm.length; i++) { | |
| 3215 if (pspm[i].length != alph_length) { | |
| 3216 throw new Error("Expected PSPM row " + i + " to have a length of " + | |
| 3217 alph_length + " but the length was " + pspm[i].length); | |
| 3218 } | |
| 3219 } | |
| 3220 return {"pspm": pspm, "motif_length": motif_length, | |
| 3221 "alph_length": alph_length, "nsites": nsites, "evalue": evalue}; | |
| 3222 } | |
| 3223 //====================================================================== | |
| 3224 // end Pspm object | |
| 3225 //====================================================================== | |
| 3226 | |
| 3227 //====================================================================== | |
| 3228 // start Logo object | |
| 3229 //====================================================================== | |
| 3230 | |
| 3231 var Logo = function(alphabet, options) { | |
| 3232 "use strict"; | |
| 3233 this.alphabet = alphabet; | |
| 3234 this.fine_text = ""; | |
| 3235 this.x_axis = 1; | |
| 3236 this.y_axis = true; | |
| 3237 this.xlate_nsyms = 1; | |
| 3238 this.xlate_start = null; | |
| 3239 this.xlate_end = null; | |
| 3240 this.pspm_list = []; | |
| 3241 this.pspm_column = []; | |
| 3242 this.rows = 0; | |
| 3243 this.columns = 0; | |
| 3244 if (typeof options === "string") { | |
| 3245 // the old method signature had fine_text here so we support that | |
| 3246 this.fine_text = options; | |
| 3247 } else if (typeof options === "object" && options != null) { | |
| 3248 this.fine_text = (typeof options.fine_text === "string" ? options.fine_text : ""); | |
| 3249 this.x_axis = (typeof options.x_axis === "boolean" ? (options.x_axis ? 1 : 0) : 1); | |
| 3250 if (options.x_axis_hidden != null && options.x_axis_hidden) this.x_axis = -1; | |
| 3251 this.y_axis = (typeof options.y_axis === "boolean" ? options.y_axis : true); | |
| 3252 this.xlate_nsyms = (typeof options.xlate_nsyms === "number" ? options.xlate_nsyms : this.xlate_nsyms); | |
| 3253 this.xlate_start = (typeof options.xlate_start === "number" ? options.xlate_start : this.xlate_start); | |
| 3254 this.xlate_end = (typeof options.xlate_end === "number" ? options.xlate_end : this.xlate_end); | |
| 3255 } | |
| 3256 }; | |
| 3257 | |
| 3258 Logo.prototype.add_pspm = function(pspm, column) { | |
| 3259 "use strict"; | |
| 3260 var col; | |
| 3261 if (typeof column === "undefined") { | |
| 3262 column = 0; | |
| 3263 } else if (column < 0) { | |
| 3264 throw new Error("Column index out of bounds."); | |
| 3265 } | |
| 3266 this.pspm_list[this.rows] = pspm; | |
| 3267 this.pspm_column[this.rows] = column; | |
| 3268 this.rows++; | |
| 3269 col = column + pspm.get_motif_length(); | |
| 3270 if (col > this.columns) { | |
| 3271 this.columns = col; | |
| 3272 } | |
| 3273 }; | |
| 3274 | |
| 3275 Logo.prototype.get_columns = function() { | |
| 3276 "use strict"; | |
| 3277 return this.columns; | |
| 3278 }; | |
| 3279 | |
| 3280 Logo.prototype.get_xlate_nsyms = function() { | |
| 3281 "use strict"; | |
| 3282 return this.xlate_nsyms; | |
| 3283 }; | |
| 3284 | |
| 3285 Logo.prototype.get_xlate_start = function() { | |
| 3286 "use strict"; | |
| 3287 return (this.xlate_start != null ? this.xlate_start : 0); | |
| 3288 }; | |
| 3289 | |
| 3290 Logo.prototype.get_xlate_end = function() { | |
| 3291 "use strict"; | |
| 3292 return (this.xlate_end != null ? this.xlate_end : this.columns * this.xlate_nsyms); | |
| 3293 }; | |
| 3294 | |
| 3295 Logo.prototype.get_xlate_columns = function() { | |
| 3296 "use strict"; | |
| 3297 return this.get_xlate_end() - this.get_xlate_start(); | |
| 3298 }; | |
| 3299 | |
| 3300 Logo.prototype.get_rows = function() { | |
| 3301 "use strict"; | |
| 3302 return this.rows; | |
| 3303 }; | |
| 3304 | |
| 3305 Logo.prototype.get_pspm = function(row_index) { | |
| 3306 "use strict"; | |
| 3307 if (row_index < 0 || row_index >= this.rows) { | |
| 3308 throw new Error("INDEX_OUT_OF_BOUNDS"); | |
| 3309 } | |
| 3310 return this.pspm_list[row_index]; | |
| 3311 }; | |
| 3312 | |
| 3313 Logo.prototype.get_offset = function(row_index) { | |
| 3314 "use strict"; | |
| 3315 if (row_index < 0 || row_index >= this.rows) { | |
| 3316 throw new Error("INDEX_OUT_OF_BOUNDS"); | |
| 3317 } | |
| 3318 return this.pspm_column[row_index]; | |
| 3319 }; | |
| 3320 | |
| 3321 Logo.prototype._as_eps_data = function(ssc, errbars) { | |
| 3322 var i, j, pos, stack_pos, pspm, stack, sym, out; | |
| 3323 out = ""; | |
| 3324 for (i = 0; i < this.rows; i++) { | |
| 3325 out += "\nStartLine\n"; | |
| 3326 // Indent | |
| 3327 for (j = 0; j < this.pspm_column[i]; j++) { | |
| 3328 out += "() startstack\nendstack\n\n"; | |
| 3329 } | |
| 3330 pspm = this.pspm_list[i]; | |
| 3331 if (pspm.get_left_trim() > 0) { | |
| 3332 out += "MuteColour\nDrawTrimEdge\n" + pspm.get_left_trim() + " DrawTrimBg\n"; | |
| 3333 } | |
| 3334 for (pos = 0; pos < pspm.get_motif_length(); pos++) { | |
| 3335 if (pos != 0 && pos == pspm.get_left_trim()) { // enable full colour | |
| 3336 out += "DrawTrimEdge\nRestoreColour\n"; | |
| 3337 } else if (pos == (pspm.get_motif_length() - pspm.get_right_trim())) { | |
| 3338 out += "MuteColour\n" + pspm.get_right_trim() + " DrawTrimBg\n"; | |
| 3339 } | |
| 3340 out += "(" + (pos + 1) + ") startstack\n"; | |
| 3341 stack = pspm.get_stack(pos, this.alphabet, ssc); | |
| 3342 for (stack_pos = 0; stack_pos < stack.length; stack_pos++) { | |
| 3343 sym = stack[stack_pos]; | |
| 3344 out += " " + (sym.get_scale() * this.alphabet.get_ic()) + " (" + sym.get_symbol() + ") numchar\n"; | |
| 3345 } | |
| 3346 if (errbars) { | |
| 3347 out += " " + pspm.get_error(this.alphabet) + " Ibeam\n"; | |
| 3348 } | |
| 3349 out += "endstack\n\n"; | |
| 3350 } | |
| 3351 if (pspm.get_right_trim() > 0 || pspm.get_left_trim() == pspm.get_motif_length()) { | |
| 3352 out += "RestoreColour\n"; | |
| 3353 } | |
| 3354 out += "EndLine\n"; | |
| 3355 } | |
| 3356 return out; | |
| 3357 }; | |
| 3358 | |
| 3359 Logo.prototype.as_eps = function(options) { | |
| 3360 "use strict"; | |
| 3361 if (this.xlate_nsyms != 1) throw new Error("Unsupported setting xlate_nsyms for EPS"); | |
| 3362 if (this.xlate_start != null) throw new Error("Unsupported setting xlate_start for EPS"); | |
| 3363 if (this.xlate_end != null) throw new Error("Unsupported setting xlate_end for EPS"); | |
| 3364 | |
| 3365 var LOGOHEIGHT = 7.5; // default height of line in cm | |
| 3366 var cm2pts, height, width, now, ssc, errbars; | |
| 3367 if (typeof options === "undefined") { | |
| 3368 options = {}; | |
| 3369 } | |
| 3370 cm2pts = 72 / 2.54; | |
| 3371 if (typeof options.logo_height == "number") { | |
| 3372 height = options.logo_height; | |
| 3373 } else { | |
| 3374 height = LOGOHEIGHT * this.rows; | |
| 3375 } | |
| 3376 if (typeof options.logo_width == "number") { | |
| 3377 width = options.logo_width; | |
| 3378 } else { | |
| 3379 width = this.columns + 2; | |
| 3380 } | |
| 3381 now = new Date(); | |
| 3382 ssc = (typeof options.ssc == "boolean" ? options.ssc : false); | |
| 3383 errbars = (typeof options.show_error_bar == "boolean" ? options.show_error_bar : ssc); | |
| 3384 var values = { | |
| 3385 "LOGOHEIGHT": height, | |
| 3386 "LOGOWIDTH": width, | |
| 3387 "BOUNDINGHEIGHT": Math.round(height * cm2pts), | |
| 3388 "BOUNDINGWIDTH": Math.round(width * cm2pts), | |
| 3389 "LOGOLINEHEIGHT": (height / this.rows), | |
| 3390 "CHARSPERLINE": this.columns, | |
| 3391 "BARBITS": this.alphabet.get_ic(), | |
| 3392 "LOGOTYPE": (this.alphabet.has_complement() ? "NA" : "AA"), | |
| 3393 "CREATIONDATE": now.getDate() + "." + (now.getMonth() + 1) + "." + now.getFullYear() + " " + now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds(), | |
| 3394 "ERRORBARFRACTION": (typeof options.error_bar_fraction == "number" ? options.error_bar_fraction : 1.0), | |
| 3395 "TICBITS": (typeof options.ticbits == "number" ? options.ticbits : 1.0), | |
| 3396 "TITLE": (typeof options.title == "string" ? options.title : ""), | |
| 3397 "FINEPRINT": (typeof options.fineprint == "string" ? options.fineprint : this.fine_text), | |
| 3398 "XAXISLABEL": (typeof options.xaxislabel == "string" ? options.xaxislabel : ""), | |
| 3399 "YAXISLABEL": (typeof options.yaxislabel == "string" ? options.yaxislabel : "bits"), | |
| 3400 "SSC": ssc, | |
| 3401 "YAXIS": (typeof options.show_y_axis == "boolean" ? options.show_y_axis : this.y_axis), | |
| 3402 "SHOWENDS": (typeof options.show_ends == "boolean" ? options.show_ends : false), | |
| 3403 "ERRBAR": errbars, | |
| 3404 "OUTLINE": (typeof options.show_outline == "boolean" ? options.show_outline : false), | |
| 3405 "NUMBERING": (typeof options.show_numbering == "boolean" ? options.show_numbering : this.x_axis != 0), | |
| 3406 "SHOWINGBOX": (typeof options.show_box == "boolean" ? options.show_box : false), | |
| 3407 "CREATOR": (typeof options.creator == "string" ? options.creator : "motif_logo.js"), | |
| 3408 "FONTSIZE": (typeof options.label_font_size == "number" ? options.label_font_size : 12), | |
| 3409 "TITLEFONTSIZE": (typeof options.title_font_size == "number" ? options.title_font_size : 12), | |
| 3410 "SMALLFONTSIZE": (typeof options.small_font_size == "number" ? options.small_font_size : 6), | |
| 3411 "TOPMARGIN" : (typeof options.top_margin == "number" ? options.top_margin : 0.9), | |
| 3412 "BOTTOMMARGIN": (typeof options.bottom_margin == "number" ? options.bottom_margin : 0.9), | |
| 3413 "COLORDICT": this.alphabet._as_eps_dict(), | |
| 3414 "DATA": this._as_eps_data(ssc, errbars) | |
| 3415 }; | |
| 3416 // now this requires that the script containing the template has been imported! | |
| 3417 return motif_logo_template(values); | |
| 3418 }; | |
| 3419 | |
| 3420 //====================================================================== | |
| 3421 // end Logo object | |
| 3422 //====================================================================== | |
| 3423 | |
| 3424 // calculate the exact size (in pixels) of an object drawn on the | |
| 3425 // canvas assuming that the background of the canvas is transparent. | |
| 3426 function canvas_bounds(ctx, cwidth, cheight) { | |
| 3427 "use strict"; | |
| 3428 var data, r, c, top_line, bottom_line, left_line, right_line, | |
| 3429 txt_width, txt_height; | |
| 3430 | |
| 3431 // extract the image data | |
| 3432 data = ctx.getImageData(0, 0, cwidth, cheight).data; | |
| 3433 | |
| 3434 // set initial values | |
| 3435 top_line = -1; bottom_line = -1; left_line = -1; right_line = -1; | |
| 3436 txt_width = 0; txt_height = 0; | |
| 3437 | |
| 3438 // Find the top-most line with a non-transparent pixel | |
| 3439 for (r = 0; r < cheight; r++) { | |
| 3440 for (c = 0; c < cwidth; c++) { | |
| 3441 if (data[r * cwidth * 4 + c * 4 + 3]) { | |
| 3442 top_line = r; | |
| 3443 break; | |
| 3444 } | |
| 3445 } | |
| 3446 if (top_line != -1) { | |
| 3447 break; | |
| 3448 } | |
| 3449 } | |
| 3450 | |
| 3451 // Only bother looking if we found at least one set pixel... | |
| 3452 if (top_line != -1) { | |
| 3453 | |
| 3454 //find the last line with a non-transparent pixel | |
| 3455 for (r = cheight-1; r >= top_line; r--) { | |
| 3456 for(c = 0; c < cwidth; c++) { | |
| 3457 if(data[r * cwidth * 4 + c * 4 + 3]) { | |
| 3458 bottom_line = r; | |
| 3459 break; | |
| 3460 } | |
| 3461 } | |
| 3462 if (bottom_line != -1) { | |
| 3463 break; | |
| 3464 } | |
| 3465 } | |
| 3466 // calculate height | |
| 3467 txt_height = bottom_line - top_line + 1; | |
| 3468 | |
| 3469 // Find the left-most line with a non-transparent pixel | |
| 3470 for (c = 0; c < cwidth; c++) { | |
| 3471 for (r = top_line; r <= bottom_line; r++) { | |
| 3472 if (data[r * cwidth * 4 + c * 4 + 3]) { | |
| 3473 left_line = c; | |
| 3474 break; | |
| 3475 } | |
| 3476 } | |
| 3477 if (left_line != -1) { | |
| 3478 break; | |
| 3479 } | |
| 3480 } | |
| 3481 | |
| 3482 //find the right most line with a non-transparent pixel | |
| 3483 for (c = cwidth-1; c >= left_line; c--) { | |
| 3484 for(r = top_line; r <= bottom_line; r++) { | |
| 3485 if(data[r * cwidth * 4 + c * 4 + 3]) { | |
| 3486 right_line = c; | |
| 3487 break; | |
| 3488 } | |
| 3489 } | |
| 3490 if (right_line != -1) { | |
| 3491 break; | |
| 3492 } | |
| 3493 } | |
| 3494 txt_width = right_line - left_line + 1; | |
| 3495 } | |
| 3496 | |
| 3497 //return the bounds | |
| 3498 return {bound_top: top_line, bound_bottom: bottom_line, | |
| 3499 bound_left: left_line, bound_right: right_line, width: txt_width, | |
| 3500 height: txt_height}; | |
| 3501 } | |
| 3502 | |
| 3503 //====================================================================== | |
| 3504 // start RasterizedAlphabet | |
| 3505 //====================================================================== | |
| 3506 | |
| 3507 // Rasterize Alphabet | |
| 3508 // 1) Measure width of text at default font for all symbols in alphabet | |
| 3509 // 2) sort in width ascending | |
| 3510 // 3) Drop the top and bottom 10% (designed to ignore outliers like 'W' and 'I') | |
| 3511 // 4) Calculate the average as the maximum scaling factor (designed to stop I becoming a rectangular blob). | |
| 3512 // 5) Assume scale of zero would result in width of zero, interpolate scale required to make perfect width font | |
| 3513 // 6) Draw text onto temp canvas at calculated scale | |
| 3514 // 7) Find bounds of drawn text | |
| 3515 // 8) Paint on to another canvas at the desired height (but only scaling width to fit if larger). | |
| 3516 var RasterizedAlphabet = function(alphabet, logo_scale, font, width) { | |
| 3517 "use strict"; | |
| 3518 var default_size, safety_pad, canvas, ctx, middle, baseline, widths, sizes, | |
| 3519 i, sym, size, tenpercent, avg_width, scale, | |
| 3520 target_width, target_height; | |
| 3521 //variable prototypes | |
| 3522 this.alphabet = alphabet; | |
| 3523 this.scale = logo_scale; | |
| 3524 this.sym_cache = {}; | |
| 3525 this.stack_num_cache = []; | |
| 3526 this.scale_num_cache = []; | |
| 3527 // size of canvas | |
| 3528 default_size = 60; // size of measuring canvas | |
| 3529 safety_pad = 20; // pixels to pad around so we don't miss the edges | |
| 3530 // create a canvas to do our measuring | |
| 3531 canvas = document.createElement("canvas"); | |
| 3532 if (!canvas.getContext) throw new Error("No canvas support"); | |
| 3533 canvas.width = default_size + 2 * safety_pad; | |
| 3534 canvas.height = default_size + 2 * safety_pad; | |
| 3535 middle = Math.round(canvas.width / 2); | |
| 3536 baseline = Math.round(canvas.height - safety_pad); | |
| 3537 ctx = canvas.getContext('2d'); | |
| 3538 if (!supports_text(ctx)) throw new Error("Canvas does not support text"); | |
| 3539 ctx.font = font; | |
| 3540 ctx.textAlign = "center"; | |
| 3541 ctx.translate(middle, baseline); | |
| 3542 // list of widths | |
| 3543 widths = []; | |
| 3544 sizes = []; | |
| 3545 //now measure each letter in the alphabet | |
| 3546 for (i = 0; i < alphabet.get_size_core(); ++i) { | |
| 3547 // reset the canvas | |
| 3548 ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| 3549 ctx.fillStyle = alphabet.get_colour(i); | |
| 3550 // draw the test text | |
| 3551 ctx.fillText(alphabet.get_symbol(i), 0, 0); | |
| 3552 //measure | |
| 3553 size = canvas_bounds(ctx, canvas.width, canvas.height); | |
| 3554 if (size.width === 0) throw new Error("Invisible symbol!"); | |
| 3555 widths.push(size.width); | |
| 3556 sizes[i] = size; | |
| 3557 } | |
| 3558 //sort the widths | |
| 3559 widths.sort(function(a,b) {return a - b;}); | |
| 3560 //drop 10% of the items off each end | |
| 3561 tenpercent = Math.floor(widths.length / 10); | |
| 3562 for (i = 0; i < tenpercent; ++i) { | |
| 3563 widths.pop(); | |
| 3564 widths.shift(); | |
| 3565 } | |
| 3566 //calculate average width | |
| 3567 avg_width = 0; | |
| 3568 for (i = 0; i < widths.length; ++i) { | |
| 3569 avg_width += widths[i]; | |
| 3570 } | |
| 3571 avg_width /= widths.length; | |
| 3572 // calculate the target width | |
| 3573 target_width = width * this.scale * 2; | |
| 3574 // calculate scales | |
| 3575 for (i = 0; i < alphabet.get_size_core(); ++i) { | |
| 3576 sym = alphabet.get_symbol(i); | |
| 3577 size = sizes[i]; | |
| 3578 // calculate scale | |
| 3579 scale = target_width / Math.max(avg_width, size.width); | |
| 3580 // estimate scaled height | |
| 3581 target_height = size.height * scale; | |
| 3582 // create an appropriately sized canvas | |
| 3583 canvas = document.createElement("canvas"); | |
| 3584 canvas.width = target_width; | |
| 3585 canvas.height = target_height + safety_pad * 2; | |
| 3586 // calculate the middle | |
| 3587 middle = Math.round(canvas.width / 2); | |
| 3588 // calculate the baseline | |
| 3589 baseline = Math.round(canvas.height - safety_pad); | |
| 3590 // get the context and prepare to draw the rasterized text | |
| 3591 ctx = canvas.getContext('2d'); | |
| 3592 ctx.font = font; | |
| 3593 ctx.fillStyle = alphabet.get_colour(i); | |
| 3594 ctx.textAlign = "center"; | |
| 3595 ctx.translate(middle, baseline); | |
| 3596 ctx.save(); | |
| 3597 ctx.scale(scale, scale); | |
| 3598 // draw the text | |
| 3599 ctx.fillText(sym, 0, 0); | |
| 3600 ctx.restore(); | |
| 3601 this.sym_cache[sym] = {"image": canvas, "size": canvas_bounds(ctx, canvas.width, canvas.height)}; | |
| 3602 } | |
| 3603 }; | |
| 3604 | |
| 3605 RasterizedAlphabet.prototype.get_alphabet = function() { | |
| 3606 return this.alphabet; | |
| 3607 }; | |
| 3608 | |
| 3609 RasterizedAlphabet.prototype.get_scale = function() { | |
| 3610 return this.scale; | |
| 3611 }; | |
| 3612 | |
| 3613 RasterizedAlphabet.prototype.draw_stack_sym = function(ctx, letter, dx, dy, dWidth, dHeight) { | |
| 3614 "use strict"; | |
| 3615 var entry, image, size; | |
| 3616 entry = this.sym_cache[letter]; | |
| 3617 image = entry.image; | |
| 3618 size = entry.size; | |
| 3619 ctx.drawImage(image, 0, size.bound_top -1, image.width, size.height+1, dx, dy, dWidth, dHeight); | |
| 3620 }; | |
| 3621 | |
| 3622 RasterizedAlphabet.prototype.draw_stack_num = function(ctx, font, stack_width, index) { | |
| 3623 var image, image_ctx, text_length; | |
| 3624 if (index >= this.stack_num_cache.length) { | |
| 3625 image = document.createElement("canvas"); | |
| 3626 // measure the text | |
| 3627 image_ctx = image.getContext('2d'); | |
| 3628 image_ctx.save(); | |
| 3629 image_ctx.font = font; | |
| 3630 text_length = image_ctx.measureText("" + (index + 1)).width; | |
| 3631 image_ctx.restore(); | |
| 3632 // resize the canvas to fit | |
| 3633 image.width = Math.ceil(stack_width); | |
| 3634 image.height = Math.ceil(text_length); | |
| 3635 // draw the text | |
| 3636 image_ctx = image.getContext('2d'); | |
| 3637 image_ctx.translate(Math.round(stack_width / 2), 0); | |
| 3638 image_ctx.font = font; | |
| 3639 image_ctx.textBaseline = "middle"; | |
| 3640 image_ctx.textAlign = "right"; | |
| 3641 image_ctx.rotate(-(Math.PI / 2)); | |
| 3642 image_ctx.fillText("" + (index + 1), 0, 0); | |
| 3643 this.stack_num_cache[index] = image; | |
| 3644 } else { | |
| 3645 image = this.stack_num_cache[index]; | |
| 3646 } | |
| 3647 ctx.drawImage(image, 0, 0); | |
| 3648 } | |
| 3649 | |
| 3650 RasterizedAlphabet.prototype.draw_scale_num = function(ctx, font, num) { | |
| 3651 var image, image_ctx, text_size, m_length; | |
| 3652 if (num >= this.scale_num_cache.length) { | |
| 3653 image = document.createElement("canvas"); | |
| 3654 // measure the text | |
| 3655 image_ctx = image.getContext('2d'); | |
| 3656 image_ctx.font = font; | |
| 3657 text_size = image_ctx.measureText("" + num); | |
| 3658 if (text_size.actualBoundingBoxAscent && text_size.actualBoundingBoxDesent) { | |
| 3659 // resize the canvas to fit | |
| 3660 image.width = Math.ceil(text_size.width); | |
| 3661 image.height = Math.ceil(text_size.actualBoundingBoxAscent + text_size.actualBoundingBoxDesent); | |
| 3662 // draw the text | |
| 3663 image_ctx = image.getContext('2d'); | |
| 3664 image_ctx.font = font; | |
| 3665 image_ctx.textAlign = "right"; | |
| 3666 image_ctx.fillText("" + num, image.width, text_size.actualBoundingBoxAscent); | |
| 3667 } else { | |
| 3668 // measure width of 'm' to approximate height, we double it later anyway | |
| 3669 m_length = image_ctx.measureText("m").width; | |
| 3670 // resize the canvas to fit | |
| 3671 image.width = Math.ceil(text_size.width); | |
| 3672 image.height = Math.ceil(2 * m_length); | |
| 3673 // draw the text | |
| 3674 image_ctx = image.getContext('2d'); | |
| 3675 image_ctx.font = font; | |
| 3676 image_ctx.textAlign = "right"; | |
| 3677 image_ctx.textBaseline = "middle"; | |
| 3678 image_ctx.fillText("" + num, image.width, m_length); | |
| 3679 } | |
| 3680 this.scale_num_cache[num] = image; | |
| 3681 } else { | |
| 3682 image = this.scale_num_cache[num]; | |
| 3683 } | |
| 3684 ctx.drawImage(image, -image.width, -Math.round(image.height / 2)) | |
| 3685 } | |
| 3686 | |
| 3687 //====================================================================== | |
| 3688 // end RasterizedAlphabet | |
| 3689 //====================================================================== | |
| 3690 | |
| 3691 //====================================================================== | |
| 3692 // start LogoMetrics object | |
| 3693 //====================================================================== | |
| 3694 | |
| 3695 var LogoMetrics = function(ctx, logo_columns, logo_rows, has_names, has_finetext, x_axis, y_axis) { | |
| 3696 "use strict"; | |
| 3697 var i, row_height; | |
| 3698 //variable prototypes | |
| 3699 this.pad_top = (has_names ? 5 : 0); | |
| 3700 this.pad_left = (y_axis ? 10 : 0); | |
| 3701 this.pad_right = (has_finetext ? 15 : 0); | |
| 3702 this.pad_bottom = 0; | |
| 3703 this.pad_middle = 20; | |
| 3704 this.name_height = 14; | |
| 3705 this.name_font = "bold " + this.name_height + "px Times, sans-serif"; | |
| 3706 this.name_spacer = 0; | |
| 3707 this.y_axis = y_axis; | |
| 3708 this.y_label = "bits"; | |
| 3709 this.y_label_height = 12; | |
| 3710 this.y_label_font = "bold " + this.y_label_height + "px Helvetica, sans-serif"; | |
| 3711 this.y_label_spacer = 3; | |
| 3712 this.y_num_height = 12; | |
| 3713 this.y_num_width = 0; | |
| 3714 this.y_num_font = "bold " + this.y_num_height + "px Helvetica, sans-serif"; | |
| 3715 this.y_tic_width = 5; | |
| 3716 this.stack_pad_left = 0; | |
| 3717 this.stack_font = "bold 25px Helvetica, sans-serif"; | |
| 3718 this.stack_height = 90; | |
| 3719 this.stack_width = 26; | |
| 3720 this.stacks_pad_right = 5; | |
| 3721 this.x_axis = x_axis; | |
| 3722 this.x_num_above = 2; | |
| 3723 this.x_num_height = 12; | |
| 3724 this.x_num_width = 0; | |
| 3725 this.x_num_font = "bold " + this.x_num_height + "px Helvetica, sans-serif"; | |
| 3726 this.fine_txt_height = 6; | |
| 3727 this.fine_txt_above = 2; | |
| 3728 this.fine_txt_font = "normal " + this.fine_txt_height + "px Helvetica, sans-serif"; | |
| 3729 this.letter_metrics = new Array(); | |
| 3730 this.summed_width = 0; | |
| 3731 this.summed_height = 0; | |
| 3732 //calculate the width of the y axis numbers | |
| 3733 ctx.font = this.y_num_font; | |
| 3734 for (i = 0; i <= 2; i++) { | |
| 3735 this.y_num_width = Math.max(this.y_num_width, ctx.measureText("" + i).width); | |
| 3736 } | |
| 3737 //calculate the width of the x axis numbers (but they are rotated so it becomes height) | |
| 3738 if (x_axis == 1) { | |
| 3739 ctx.font = this.x_num_font; | |
| 3740 for (i = 1; i <= logo_columns; i++) { | |
| 3741 this.x_num_width = Math.max(this.x_num_width, ctx.measureText("" + i).width); | |
| 3742 } | |
| 3743 } else if (x_axis == 0) { | |
| 3744 this.x_num_height = 4; | |
| 3745 this.x_num_width = 4; | |
| 3746 } else { | |
| 3747 this.x_num_height = 0; | |
| 3748 this.x_num_width = 0; | |
| 3749 } | |
| 3750 | |
| 3751 //calculate how much vertical space we want to draw this | |
| 3752 //first we add the padding at the top and bottom since that's always there | |
| 3753 this.summed_height += this.pad_top + this.pad_bottom; | |
| 3754 //all except the last row have the same amount of space allocated to them | |
| 3755 if (logo_rows > 1) { | |
| 3756 row_height = this.stack_height + this.pad_middle; | |
| 3757 if (has_names) { | |
| 3758 row_height += this.name_height; | |
| 3759 //the label is allowed to overlap into the spacer | |
| 3760 row_height += Math.max(this.y_num_height/2, this.name_spacer); | |
| 3761 //the label is allowed to overlap the space used by the other label | |
| 3762 row_height += Math.max(this.y_num_height/2, this.x_num_height + this.x_num_above); | |
| 3763 } else { | |
| 3764 row_height += this.y_num_height/2; | |
| 3765 //the label is allowed to overlap the space used by the other label | |
| 3766 row_height += Math.max(this.y_num_height/2, this.x_num_height + this.x_num_above); | |
| 3767 } | |
| 3768 this.summed_height += row_height * (logo_rows - 1); | |
| 3769 } | |
| 3770 //the last row has the name and fine text below it but no padding | |
| 3771 this.summed_height += this.stack_height + (this.y_axis ? this.y_num_height/2 : 0); | |
| 3772 | |
| 3773 var fine_txt_total = (has_finetext ? this.fine_txt_height + this.fine_txt_above : 0); | |
| 3774 if (has_names) { | |
| 3775 this.summed_height += fine_txt_total + this.name_height; | |
| 3776 this.summed_height += Math.max((this.y_axis ? this.y_num_height/2 : 0), | |
| 3777 this.x_num_height + this.x_num_above + this.name_spacer); | |
| 3778 } else { | |
| 3779 this.summed_height += Math.max((this.y_axis ? this.y_num_height/2 : 0), | |
| 3780 this.x_num_height + this.x_num_above + fine_txt_total); | |
| 3781 } | |
| 3782 | |
| 3783 //calculate how much horizontal space we want to draw this | |
| 3784 //first add the padding at the left and right since that's always there | |
| 3785 this.summed_width += this.pad_left + this.pad_right; | |
| 3786 if (this.y_axis) { | |
| 3787 //add on the space for the y-axis label | |
| 3788 this.summed_width += this.y_label_height + this.y_label_spacer; | |
| 3789 //add on the space for the y-axis | |
| 3790 this.summed_width += this.y_num_width + this.y_tic_width; | |
| 3791 } | |
| 3792 //add on the space for the stacks | |
| 3793 this.summed_width += (this.stack_pad_left + this.stack_width) * logo_columns; | |
| 3794 //add on the padding after the stacks (an offset from the fine text) | |
| 3795 this.summed_width += this.stacks_pad_right; | |
| 3796 | |
| 3797 }; | |
| 3798 | |
| 3799 //====================================================================== | |
| 3800 // end LogoMetrics object | |
| 3801 //====================================================================== | |
| 3802 | |
| 3803 //found this trick at http://talideon.com/weblog/2005/02/detecting-broken-images-js.cfm | |
| 3804 function image_ok(img) { | |
| 3805 "use strict"; | |
| 3806 // During the onload event, IE correctly identifies any images that | |
| 3807 // weren't downloaded as not complete. Others should too. Gecko-based | |
| 3808 // browsers act like NS4 in that they report this incorrectly. | |
| 3809 if (!img.complete) { | |
| 3810 return false; | |
| 3811 } | |
| 3812 // However, they do have two very useful properties: naturalWidth and | |
| 3813 // naturalHeight. These give the true size of the image. If it failed | |
| 3814 // to load, either of these should be zero. | |
| 3815 if (typeof img.naturalWidth !== "undefined" && img.naturalWidth === 0) { | |
| 3816 return false; | |
| 3817 } | |
| 3818 // No other way of checking: assume it's ok. | |
| 3819 return true; | |
| 3820 } | |
| 3821 | |
| 3822 function supports_text(ctx) { | |
| 3823 "use strict"; | |
| 3824 if (!ctx.fillText) { | |
| 3825 return false; | |
| 3826 } | |
| 3827 if (!ctx.measureText) { | |
| 3828 return false; | |
| 3829 } | |
| 3830 return true; | |
| 3831 } | |
| 3832 | |
| 3833 //draws the scale, returns the width | |
| 3834 function draw_scale(ctx, metrics, alphabet_ic, raster) { | |
| 3835 "use strict"; | |
| 3836 var tic_height, i; | |
| 3837 tic_height = metrics.stack_height / alphabet_ic; | |
| 3838 ctx.save(); | |
| 3839 ctx.translate(metrics.y_label_height, metrics.y_num_height/2); | |
| 3840 //draw the axis label | |
| 3841 ctx.save(); | |
| 3842 ctx.font = metrics.y_label_font; | |
| 3843 ctx.translate(0, metrics.stack_height/2); | |
| 3844 ctx.rotate(-(Math.PI / 2)); | |
| 3845 ctx.textAlign = "center"; | |
| 3846 ctx.fillText("bits", 0, 0); | |
| 3847 ctx.restore(); | |
| 3848 | |
| 3849 ctx.translate(metrics.y_label_spacer + metrics.y_num_width, 0); | |
| 3850 | |
| 3851 //draw the axis tics | |
| 3852 ctx.save(); | |
| 3853 ctx.translate(0, metrics.stack_height); | |
| 3854 for (i = 0; i <= alphabet_ic; i++) { | |
| 3855 //draw the number | |
| 3856 ctx.save(); | |
| 3857 ctx.translate(-1, 0); | |
| 3858 raster.draw_scale_num(ctx, metrics.y_num_font, i); | |
| 3859 ctx.restore(); | |
| 3860 //draw the tic | |
| 3861 ctx.fillRect(0, -1, metrics.y_tic_width, 2); | |
| 3862 //prepare for next tic | |
| 3863 ctx.translate(0, -tic_height); | |
| 3864 } | |
| 3865 ctx.restore(); | |
| 3866 | |
| 3867 ctx.fillRect(metrics.y_tic_width - 2, 0, 2, metrics.stack_height) | |
| 3868 | |
| 3869 ctx.restore(); | |
| 3870 } | |
| 3871 | |
| 3872 function draw_stack_num(ctx, metrics, row_index, raster) { | |
| 3873 "use strict"; | |
| 3874 ctx.save(); | |
| 3875 ctx.translate(0, Math.round(metrics.stack_height + metrics.x_num_above)); | |
| 3876 if (metrics.x_axis == 1) { | |
| 3877 raster.draw_stack_num(ctx, metrics.x_num_font, metrics.stack_width, row_index); | |
| 3878 } else if (metrics.x_axis == 0) { | |
| 3879 // draw dots instead of the numbers (good for small logos) | |
| 3880 ctx.beginPath(); | |
| 3881 var radius = Math.round(metrics.x_num_height / 2); | |
| 3882 ctx.arc(Math.round(metrics.stack_width / 2), radius, radius, 0, 2 * Math.PI, false); | |
| 3883 ctx.fill(); | |
| 3884 } | |
| 3885 ctx.restore(); | |
| 3886 } | |
| 3887 | |
| 3888 function draw_stack(ctx, metrics, symbols, raster) { | |
| 3889 "use strict"; | |
| 3890 var preferred_pad, sym_min, i, sym, sym_height, pad; | |
| 3891 preferred_pad = 0; | |
| 3892 sym_min = 5; | |
| 3893 | |
| 3894 ctx.save();//1 | |
| 3895 ctx.translate(0, metrics.stack_height); | |
| 3896 for (i = 0; i < symbols.length; i++) { | |
| 3897 sym = symbols[i]; | |
| 3898 sym_height = metrics.stack_height * sym.get_scale(); | |
| 3899 | |
| 3900 pad = preferred_pad; | |
| 3901 if (sym_height - pad < sym_min) { | |
| 3902 pad = Math.min(pad, Math.max(0, sym_height - sym_min)); | |
| 3903 } | |
| 3904 sym_height -= pad; | |
| 3905 | |
| 3906 //translate to the correct position | |
| 3907 ctx.translate(0, -(pad/2 + sym_height)); | |
| 3908 | |
| 3909 //draw | |
| 3910 raster.draw_stack_sym(ctx, sym.get_symbol(), 0, 0, metrics.stack_width, sym_height); | |
| 3911 //translate past the padding | |
| 3912 ctx.translate(0, -(pad/2)); | |
| 3913 } | |
| 3914 ctx.restore();//1 | |
| 3915 } | |
| 3916 | |
| 3917 function draw_dashed_line(ctx, pattern, start, x1, y1, x2, y2) { | |
| 3918 "use strict"; | |
| 3919 var x, y, len, i, dx, dy, tlen, theta, mulx, muly, lx, ly; | |
| 3920 dx = x2 - x1; | |
| 3921 dy = y2 - y1; | |
| 3922 tlen = Math.pow(dx*dx + dy*dy, 0.5); | |
| 3923 theta = Math.atan2(dy,dx); | |
| 3924 mulx = Math.cos(theta); | |
| 3925 muly = Math.sin(theta); | |
| 3926 lx = []; | |
| 3927 ly = []; | |
| 3928 for (i = 0; i < pattern; ++i) { | |
| 3929 lx.push(pattern[i] * mulx); | |
| 3930 ly.push(pattern[i] * muly); | |
| 3931 } | |
| 3932 i = start; | |
| 3933 x = x1; | |
| 3934 y = y1; | |
| 3935 len = 0; | |
| 3936 ctx.beginPath(); | |
| 3937 while (len + pattern[i] < tlen) { | |
| 3938 ctx.moveTo(x, y); | |
| 3939 x += lx[i]; | |
| 3940 y += ly[i]; | |
| 3941 ctx.lineTo(x, y); | |
| 3942 len += pattern[i]; | |
| 3943 i = (i + 1) % pattern.length; | |
| 3944 x += lx[i]; | |
| 3945 y += ly[i]; | |
| 3946 len += pattern[i]; | |
| 3947 i = (i + 1) % pattern.length; | |
| 3948 } | |
| 3949 if (len < tlen) { | |
| 3950 ctx.moveTo(x, y); | |
| 3951 x += mulx * (tlen - len); | |
| 3952 y += muly * (tlen - len); | |
| 3953 ctx.lineTo(x, y); | |
| 3954 } | |
| 3955 ctx.stroke(); | |
| 3956 } | |
| 3957 | |
| 3958 function draw_trim_background(ctx, metrics, left_start, left_end, left_divider, right_start, right_end, right_divider) { | |
| 3959 "use strict"; | |
| 3960 var left_size = left_end - left_start; | |
| 3961 var right_size = right_end - right_start; | |
| 3962 var line_x; | |
| 3963 | |
| 3964 ctx.save();//s8 | |
| 3965 ctx.fillStyle = "rgb(240, 240, 240)"; | |
| 3966 if (left_size > 0) { | |
| 3967 ctx.fillRect(left_start * metrics.stack_width, 0, left_size * metrics.stack_width, metrics.stack_height); | |
| 3968 } | |
| 3969 if (right_size > 0) { | |
| 3970 ctx.fillRect(right_start * metrics.stack_width, 0, right_size * metrics.stack_width, metrics.stack_height); | |
| 3971 } | |
| 3972 ctx.fillStyle = "rgb(51, 51, 51)"; | |
| 3973 if (left_size > 0 && left_divider) { | |
| 3974 line_x = (left_end * metrics.stack_width) - 0.5; | |
| 3975 draw_dashed_line(ctx, [3], 0, line_x, 0, line_x, metrics.stack_height); | |
| 3976 } | |
| 3977 if (right_size > 0 && right_divider) { | |
| 3978 line_x = (right_start * metrics.stack_width) + 0.5; | |
| 3979 draw_dashed_line(ctx, [3], 0, line_x, 0, line_x, metrics.stack_height); | |
| 3980 } | |
| 3981 ctx.restore();//s8 | |
| 3982 } | |
| 3983 | |
| 3984 function size_logo_on_canvas(logo, canvas, show_names, scale) { | |
| 3985 "use strict"; | |
| 3986 var draw_name, draw_finetext, metrics; | |
| 3987 draw_name = (typeof show_names === "boolean" ? show_names : (logo.get_rows() > 1)); | |
| 3988 draw_finetext = (logo.fine_text.length > 0); | |
| 3989 if (canvas.width !== 0 && canvas.height !== 0) { | |
| 3990 return; | |
| 3991 } | |
| 3992 metrics = new LogoMetrics(canvas.getContext('2d'), | |
| 3993 logo.get_xlate_columns(), logo.get_rows(), draw_name, draw_finetext, logo.x_axis, logo.y_axis); | |
| 3994 if (typeof scale == "number") { | |
| 3995 //resize the canvas to fit the scaled logo | |
| 3996 canvas.width = metrics.summed_width * scale; | |
| 3997 canvas.height = metrics.summed_height * scale; | |
| 3998 } else { | |
| 3999 if (canvas.width === 0 && canvas.height === 0) { | |
| 4000 canvas.width = metrics.summed_width; | |
| 4001 canvas.height = metrics.summed_height; | |
| 4002 } else if (canvas.width === 0) { | |
| 4003 canvas.width = metrics.summed_width * (canvas.height / metrics.summed_height); | |
| 4004 } else if (canvas.height === 0) { | |
| 4005 canvas.height = metrics.summed_height * (canvas.width / metrics.summed_width); | |
| 4006 } | |
| 4007 } | |
| 4008 } | |
| 4009 | |
| 4010 function draw_logo_on_canvas(logo, canvas, show_names, scale) { | |
| 4011 "use strict"; | |
| 4012 var i, draw_name, draw_finetext, ctx, metrics, raster, pspm_i, pspm, | |
| 4013 offset, col_index, motif_position, ssc; | |
| 4014 ssc = false; | |
| 4015 draw_name = (typeof show_names === "boolean" ? show_names : (logo.get_rows() > 1)); | |
| 4016 draw_finetext = (logo.fine_text.length > 0); | |
| 4017 ctx = canvas.getContext('2d'); | |
| 4018 //assume that the user wants the canvas scaled equally so calculate what the best width for this image should be | |
| 4019 metrics = new LogoMetrics(ctx, logo.get_xlate_columns(), logo.get_rows(), draw_name, draw_finetext, logo.x_axis, logo.y_axis); | |
| 4020 if (typeof scale == "number") { | |
| 4021 //resize the canvas to fit the scaled logo | |
| 4022 canvas.width = metrics.summed_width * scale; | |
| 4023 canvas.height = metrics.summed_height * scale; | |
| 4024 } else { | |
| 4025 if (canvas.width === 0 && canvas.height === 0) { | |
| 4026 scale = 1; | |
| 4027 canvas.width = metrics.summed_width; | |
| 4028 canvas.height = metrics.summed_height; | |
| 4029 } else if (canvas.width === 0) { | |
| 4030 scale = canvas.height / metrics.summed_height; | |
| 4031 canvas.width = metrics.summed_width * scale; | |
| 4032 } else if (canvas.height === 0) { | |
| 4033 scale = canvas.width / metrics.summed_width; | |
| 4034 canvas.height = metrics.summed_height * scale; | |
| 4035 } else { | |
| 4036 scale = Math.min(canvas.width / metrics.summed_width, canvas.height / metrics.summed_height); | |
| 4037 } | |
| 4038 } | |
| 4039 // cache the raster based on the assumption that we will be drawing a lot | |
| 4040 // of logos the same size and alphabet | |
| 4041 if (typeof draw_logo_on_canvas.raster_cache === "undefined") { | |
| 4042 draw_logo_on_canvas.raster_cache = []; | |
| 4043 } | |
| 4044 for (i = 0; i < draw_logo_on_canvas.raster_cache.length; i++) { | |
| 4045 raster = draw_logo_on_canvas.raster_cache[i]; | |
| 4046 if (raster.get_alphabet().equals(logo.alphabet) && | |
| 4047 Math.abs(raster.get_scale() - scale) < 0.1) break; | |
| 4048 raster = null; | |
| 4049 } | |
| 4050 if (raster == null) { | |
| 4051 raster = new RasterizedAlphabet(logo.alphabet, scale, metrics.stack_font, metrics.stack_width); | |
| 4052 draw_logo_on_canvas.raster_cache.push(raster); | |
| 4053 } | |
| 4054 ctx = canvas.getContext('2d'); | |
| 4055 ctx.save();//s1 | |
| 4056 ctx.scale(scale, scale); | |
| 4057 ctx.save();//s2 | |
| 4058 ctx.save();//s7 | |
| 4059 //create margin | |
| 4060 ctx.translate(Math.round(metrics.pad_left), Math.round(metrics.pad_top)); | |
| 4061 for (pspm_i = 0; pspm_i < logo.get_rows(); ++pspm_i) { | |
| 4062 pspm = logo.get_pspm(pspm_i); | |
| 4063 offset = logo.get_offset(pspm_i); | |
| 4064 //optionally draw name if this isn't the last row or is the only row | |
| 4065 if (draw_name && (logo.get_rows() == 1 || pspm_i != (logo.get_rows()-1))) { | |
| 4066 ctx.save();//s4 | |
| 4067 ctx.translate(Math.round(metrics.summed_width/2), Math.round(metrics.name_height)); | |
| 4068 ctx.font = metrics.name_font; | |
| 4069 ctx.textAlign = "center"; | |
| 4070 ctx.fillText(pspm.name, 0, 0); | |
| 4071 ctx.restore();//s4 | |
| 4072 ctx.translate(0, Math.round(metrics.name_height + | |
| 4073 Math.min(0, metrics.name_spacer - metrics.y_num_height/2))); | |
| 4074 } | |
| 4075 //draw scale | |
| 4076 if (logo.y_axis) draw_scale(ctx, metrics, logo.alphabet.get_ic(), raster); | |
| 4077 ctx.save();//s5 | |
| 4078 //translate across past the scale | |
| 4079 if (logo.y_axis) { | |
| 4080 ctx.translate(Math.round(metrics.y_label_height + metrics.y_label_spacer + | |
| 4081 metrics.y_num_width + metrics.y_tic_width), Math.round(metrics.y_num_height / 2)); | |
| 4082 } | |
| 4083 //draw the trimming background | |
| 4084 if (pspm.get_left_trim() > 0 || pspm.get_right_trim() > 0) { | |
| 4085 var left_start = offset * logo.get_xlate_nsyms(); | |
| 4086 var left_end = (offset + pspm.get_left_trim()) * logo.get_xlate_nsyms(); | |
| 4087 var left_divider = true; | |
| 4088 if (left_end < logo.get_xlate_start() || left_start > logo.get_xlate_end()) { | |
| 4089 // no overlap | |
| 4090 left_start = 0; | |
| 4091 left_end = 0; | |
| 4092 left_divider = false; | |
| 4093 } else { | |
| 4094 if (left_start < logo.get_xlate_start()) { | |
| 4095 left_start = logo.get_xlate_start(); | |
| 4096 } | |
| 4097 if (left_end > logo.get_xlate_end()) { | |
| 4098 left_end = logo.get_xlate_end(); | |
| 4099 left_divider = false; | |
| 4100 } | |
| 4101 left_start -= logo.get_xlate_start(); | |
| 4102 left_end -= logo.get_xlate_start(); | |
| 4103 if (left_end < left_start) { | |
| 4104 left_start = 0; | |
| 4105 left_end = 0; | |
| 4106 left_divider = false; | |
| 4107 } | |
| 4108 } | |
| 4109 var right_end = (offset + pspm.get_motif_length()) * logo.get_xlate_nsyms(); | |
| 4110 //var right_start = right_end - (pspm.get_left_trim() * logo.get_xlate_nsyms()); | |
| 4111 var right_start = right_end - (pspm.get_right_trim() * logo.get_xlate_nsyms()); | |
| 4112 var right_divider = true; | |
| 4113 if (right_end < logo.get_xlate_start() || right_start > logo.get_xlate_end()) { | |
| 4114 // no overlap | |
| 4115 right_start = 0; | |
| 4116 right_end = 0; | |
| 4117 right_divider = false; | |
| 4118 } else { | |
| 4119 if (right_start < logo.get_xlate_start()) { | |
| 4120 right_start = logo.get_xlate_start(); | |
| 4121 right_divider = false; | |
| 4122 } | |
| 4123 if (right_end > logo.get_xlate_end()) { | |
| 4124 right_end = logo.get_xlate_end(); | |
| 4125 } | |
| 4126 right_start -= logo.get_xlate_start(); | |
| 4127 right_end -= logo.get_xlate_start(); | |
| 4128 if (right_end < right_start) { | |
| 4129 right_start = 0; | |
| 4130 right_end = 0; | |
| 4131 right_divider = false; | |
| 4132 } | |
| 4133 } | |
| 4134 draw_trim_background(ctx, metrics, left_start, left_end, left_divider, right_start, right_end, right_divider); | |
| 4135 } | |
| 4136 //draw letters | |
| 4137 var xlate_col; | |
| 4138 for (xlate_col = logo.get_xlate_start(); xlate_col < logo.get_xlate_end(); xlate_col++) { | |
| 4139 ctx.translate(metrics.stack_pad_left,0); | |
| 4140 col_index = Math.floor(xlate_col / logo.get_xlate_nsyms()); | |
| 4141 if (xlate_col % logo.get_xlate_nsyms() == 0) { | |
| 4142 if (col_index >= offset && col_index < (offset + pspm.get_motif_length())) { | |
| 4143 motif_position = col_index - offset; | |
| 4144 draw_stack_num(ctx, metrics, motif_position, raster); | |
| 4145 draw_stack(ctx, metrics, pspm.get_stack(motif_position, logo.alphabet, ssc), raster); | |
| 4146 } | |
| 4147 } else { | |
| 4148 if (col_index >= offset && col_index < (offset + pspm.get_motif_length())) { | |
| 4149 ctx.save();// s5.1 | |
| 4150 ctx.translate(0, Math.round(metrics.stack_height)); | |
| 4151 // TODO draw a dot or dash or something to indicate continuity of the motif | |
| 4152 ctx.restore(); //s5.1 | |
| 4153 } | |
| 4154 } | |
| 4155 ctx.translate(Math.round(metrics.stack_width), 0); | |
| 4156 } | |
| 4157 ctx.restore();//s5 | |
| 4158 ////optionally draw name if this is the last row but isn't the only row | |
| 4159 if (draw_name && (logo.get_rows() != 1 && pspm_i == (logo.get_rows()-1))) { | |
| 4160 //translate vertically past the stack and axis's | |
| 4161 ctx.translate(0, metrics.y_num_height/2 + metrics.stack_height + | |
| 4162 Math.max(metrics.y_num_height/2, metrics.x_num_above + metrics.x_num_width + metrics.name_spacer)); | |
| 4163 | |
| 4164 ctx.save();//s6 | |
| 4165 ctx.translate(metrics.summed_width/2, metrics.name_height); | |
| 4166 ctx.font = metrics.name_font; | |
| 4167 ctx.textAlign = "center"; | |
| 4168 ctx.fillText(pspm.name, 0, 0); | |
| 4169 ctx.restore();//s6 | |
| 4170 ctx.translate(0, metrics.name_height); | |
| 4171 } else { | |
| 4172 //translate vertically past the stack and axis's | |
| 4173 ctx.translate(0, metrics.y_num_height/2 + metrics.stack_height + | |
| 4174 Math.max(metrics.y_num_height/2, metrics.x_num_above + metrics.x_num_width)); | |
| 4175 } | |
| 4176 //if not the last row then add middle padding | |
| 4177 if (pspm_i != (logo.get_rows() -1)) { | |
| 4178 ctx.translate(0, metrics.pad_middle); | |
| 4179 } | |
| 4180 } | |
| 4181 ctx.restore();//s7 | |
| 4182 if (logo.fine_text.length > 0) { | |
| 4183 ctx.translate(metrics.summed_width - metrics.pad_right, metrics.summed_height - metrics.pad_bottom); | |
| 4184 ctx.font = metrics.fine_txt_font; | |
| 4185 ctx.textAlign = "right"; | |
| 4186 ctx.fillText(logo.fine_text, 0,0); | |
| 4187 } | |
| 4188 ctx.restore();//s2 | |
| 4189 ctx.restore();//s1 | |
| 4190 } | |
| 4191 | |
| 4192 function create_canvas(c_width, c_height, c_id, c_title, c_display) { | |
| 4193 "use strict"; | |
| 4194 var canvas = document.createElement("canvas"); | |
| 4195 //check for canvas support before attempting anything | |
| 4196 if (!canvas.getContext) { | |
| 4197 return null; | |
| 4198 } | |
| 4199 var ctx = canvas.getContext('2d'); | |
| 4200 //check for html5 text drawing support | |
| 4201 if (!supports_text(ctx)) { | |
| 4202 return null; | |
| 4203 } | |
| 4204 //size the canvas | |
| 4205 canvas.width = c_width; | |
| 4206 canvas.height = c_height; | |
| 4207 canvas.id = c_id; | |
| 4208 canvas.title = c_title; | |
| 4209 canvas.style.display = c_display; | |
| 4210 return canvas; | |
| 4211 } | |
| 4212 | |
| 4213 function logo_1(alphabet, fine_text, pspm) { | |
| 4214 "use strict"; | |
| 4215 var logo = new Logo(alphabet, fine_text); | |
| 4216 logo.add_pspm(pspm); | |
| 4217 return logo; | |
| 4218 } | |
| 4219 | |
| 4220 function logo_2(alphabet, fine_text, target, query, query_offset) { | |
| 4221 "use strict"; | |
| 4222 var logo = new Logo(alphabet, fine_text); | |
| 4223 if (query_offset < 0) { | |
| 4224 logo.add_pspm(target, -query_offset); | |
| 4225 logo.add_pspm(query); | |
| 4226 } else { | |
| 4227 logo.add_pspm(target); | |
| 4228 logo.add_pspm(query, query_offset); | |
| 4229 } | |
| 4230 return logo; | |
| 4231 } | |
| 4232 | |
| 4233 /* | |
| 4234 * Specifies an alternate source for an image. | |
| 4235 * If the image with the image_id specified has | |
| 4236 * not loaded then a generated logo will be used | |
| 4237 * to replace it. | |
| 4238 * | |
| 4239 * Note that the image must either have dimensions | |
| 4240 * or a scale must be set. | |
| 4241 */ | |
| 4242 function alternate_logo(logo, image_id, scale) { | |
| 4243 "use strict"; | |
| 4244 var image = document.getElementById(image_id); | |
| 4245 if (!image) { | |
| 4246 alert("Can't find specified image id (" + image_id + ")"); | |
| 4247 return; | |
| 4248 } | |
| 4249 //if the image has loaded then there is no reason to use the canvas | |
| 4250 if (image_ok(image)) { | |
| 4251 return; | |
| 4252 } | |
| 4253 //the image has failed to load so replace it with a canvas if we can. | |
| 4254 var canvas = create_canvas(image.width, image.height, image_id, image.title, image.style.display); | |
| 4255 if (canvas === null) { | |
| 4256 return; | |
| 4257 } | |
| 4258 //draw the logo on the canvas | |
| 4259 draw_logo_on_canvas(logo, canvas, null, scale); | |
| 4260 //replace the image with the canvas | |
| 4261 image.parentNode.replaceChild(canvas, image); | |
| 4262 } | |
| 4263 | |
| 4264 /* | |
| 4265 * Specifes that the element with the specified id | |
| 4266 * should be replaced with a generated logo. | |
| 4267 */ | |
| 4268 function replace_logo(logo, replace_id, scale, title_txt, display_style) { | |
| 4269 "use strict"; | |
| 4270 var element = document.getElementById(replace_id); | |
| 4271 if (!replace_id) { | |
| 4272 alert("Can't find specified id (" + replace_id + ")"); | |
| 4273 return; | |
| 4274 } | |
| 4275 //found the element! | |
| 4276 var canvas = create_canvas(50, 120, replace_id, title_txt, display_style); | |
| 4277 if (canvas === null) { | |
| 4278 return; | |
| 4279 } | |
| 4280 //draw the logo on the canvas | |
| 4281 draw_logo_on_canvas(logo, canvas, null, scale); | |
| 4282 //replace the element with the canvas | |
| 4283 element.parentNode.replaceChild(canvas, element); | |
| 4284 } | |
| 4285 | |
| 4286 /* | |
| 4287 * Fast string trimming implementation found at | |
| 4288 * http://blog.stevenlevithan.com/archives/faster-trim-javascript | |
| 4289 * | |
| 4290 * Note that regex is good at removing leading space but | |
| 4291 * bad at removing trailing space as it has to first go through | |
| 4292 * the whole string. | |
| 4293 */ | |
| 4294 function trim (str) { | |
| 4295 "use strict"; | |
| 4296 var ws, i; | |
| 4297 str = str.replace(/^\s\s*/, ''); | |
| 4298 ws = /\s/; i = str.length; | |
| 4299 while (ws.test(str.charAt(--i))); | |
| 4300 return str.slice(0, i + 1); | |
| 4301 } | |
| 4302 </script> | |
| 4303 <script> | |
| 4304 var current_motif = 0; | |
| 4305 var dreme_alphabet = new Alphabet(data.alphabet, data.control_db.freqs); | |
| 4306 | |
| 4307 /* | |
| 4308 * Create a pspm for the given motif data | |
| 4309 */ | |
| 4310 function motif_pspm(m) { | |
| 4311 return new Pspm(m.pwm, m.id, 0, 0, m.nsites, m.evalue); | |
| 4312 } | |
| 4313 | |
| 4314 /* | |
| 4315 * Create a count matrix from the given motif data | |
| 4316 */ | |
| 4317 function motif_count_matrix(motif) { | |
| 4318 return motif_pspm(motif).as_count_matrix(); | |
| 4319 } | |
| 4320 | |
| 4321 /* | |
| 4322 * Create a probablity matrix from the given motif data | |
| 4323 */ | |
| 4324 function motif_prob_matrix(motif) { | |
| 4325 return motif_pspm(motif).as_probability_matrix(); | |
| 4326 } | |
| 4327 | |
| 4328 /* | |
| 4329 * Create a minimal meme format motif from the given motif data | |
| 4330 */ | |
| 4331 function motif_minimal_meme(motif) { | |
| 4332 return motif_pspm(motif).as_meme({ | |
| 4333 "with_header": true, | |
| 4334 "with_pspm": true, | |
| 4335 "with_pssm": false, | |
| 4336 "version": data["version"], | |
| 4337 "alphabet": dreme_alphabet, | |
| 4338 "strands": (data.options.revcomp ? 2 : 1) | |
| 4339 }); | |
| 4340 } | |
| 4341 | |
| 4342 /* | |
| 4343 * Fill in a template variable | |
| 4344 */ | |
| 4345 function set_tvar(template, tvar, value) { | |
| 4346 var node; | |
| 4347 node = find_child(template, tvar); | |
| 4348 if (node === null) { | |
| 4349 throw new Error("Template does not contain variable " + tvar); | |
| 4350 } | |
| 4351 node.innerHTML = ""; | |
| 4352 if (typeof value !== "object") { | |
| 4353 node.appendChild(document.createTextNode(value)); | |
| 4354 } else { | |
| 4355 node.appendChild(value); | |
| 4356 } | |
| 4357 } | |
| 4358 | |
| 4359 /* | |
| 4360 * Make a canvas with the motif logo drawn on it. | |
| 4361 */ | |
| 4362 function make_logo(motif, height, rc) { | |
| 4363 var pspm = new Pspm(motif["pwm"]); | |
| 4364 if (rc) pspm = pspm.copy().reverse_complement(dreme_alphabet); | |
| 4365 var logo = new Logo(dreme_alphabet); | |
| 4366 logo.add_pspm(pspm, 0); | |
| 4367 var canvas = document.createElement('canvas'); | |
| 4368 canvas.height = height; | |
| 4369 canvas.width = 0; | |
| 4370 draw_logo_on_canvas(logo, canvas, false); | |
| 4371 return canvas; | |
| 4372 } | |
| 4373 | |
| 4374 /* | |
| 4375 * Create a button designed to contain a single symbol | |
| 4376 */ | |
| 4377 function make_sym_btn(symbol, title, action) { | |
| 4378 var box, sbox; | |
| 4379 box = document.createElement("div"); | |
| 4380 box.tabIndex = 0; | |
| 4381 box.className = "sym_btn"; | |
| 4382 sbox = document.createElement("span"); | |
| 4383 if (typeof symbol == "string") { | |
| 4384 sbox.appendChild(document.createTextNode(symbol)); | |
| 4385 } else { | |
| 4386 sbox.appendChild(symbol); | |
| 4387 } | |
| 4388 box.appendChild(sbox); | |
| 4389 box.title = title; | |
| 4390 box.addEventListener('click', action, false); | |
| 4391 box.addEventListener('keydown', action, false); | |
| 4392 return box; | |
| 4393 } | |
| 4394 | |
| 4395 /* | |
| 4396 * Create a pair of text spans with different classes. | |
| 4397 * This is useful when using CSS to only display one of them. | |
| 4398 */ | |
| 4399 function text_pair(txt1, cls1, txt2, cls2) { | |
| 4400 var container, part1, part2; | |
| 4401 container = document.createElement("span"); | |
| 4402 part1 = document.createElement("span"); | |
| 4403 part1.appendChild(document.createTextNode(txt1)); | |
| 4404 part1.className = cls1; | |
| 4405 container.appendChild(part1); | |
| 4406 part2 = document.createElement("span"); | |
| 4407 part2.appendChild(document.createTextNode(txt2)); | |
| 4408 part2.className = cls2; | |
| 4409 container.appendChild(part2); | |
| 4410 return container; | |
| 4411 } | |
| 4412 | |
| 4413 /* | |
| 4414 * Make a colourised sequence. | |
| 4415 */ | |
| 4416 function make_seq(seq) { | |
| 4417 var i, j, letter, lbox, sbox; | |
| 4418 sbox = document.createElement("span"); | |
| 4419 for (i = 0; i < seq.length; i = j) { | |
| 4420 letter = seq.charAt(i); | |
| 4421 for (j = i+1; j < seq.length; j++) { | |
| 4422 if (seq.charAt(j) !== letter) { | |
| 4423 break; | |
| 4424 } | |
| 4425 } | |
| 4426 lbox = document.createElement("span"); | |
| 4427 lbox.style.color = dreme_alphabet.get_colour(dreme_alphabet.get_index(letter)); | |
| 4428 lbox.appendChild(document.createTextNode(seq.substring(i, j))); | |
| 4429 sbox.appendChild(lbox); | |
| 4430 } | |
| 4431 return sbox; | |
| 4432 } | |
| 4433 | |
| 4434 /* | |
| 4435 * Create a description element taking into account the newlines in the source text. | |
| 4436 */ | |
| 4437 function make_description(text) { | |
| 4438 var i, j, lines, p; | |
| 4439 var container = document.createElement("div"); | |
| 4440 var paragraphs = text.split(/\n\n+/); | |
| 4441 for (i = 0; i < paragraphs.length; i++) { | |
| 4442 lines = paragraphs[i].split(/\n/); | |
| 4443 p = document.createElement("p"); | |
| 4444 p.appendChild(document.createTextNode(lines[0])); | |
| 4445 for (j = 1; j < lines.length; j++) { | |
| 4446 p.appendChild(document.createElement("br")); | |
| 4447 p.appendChild(document.createTextNode(lines[j])); | |
| 4448 } | |
| 4449 container.appendChild(p); | |
| 4450 } | |
| 4451 return container; | |
| 4452 } | |
| 4453 | |
| 4454 /* | |
| 4455 * Make the table header for the discovered motifs. | |
| 4456 */ | |
| 4457 function make_motif_header() { | |
| 4458 var row = document.createElement("tr"); | |
| 4459 add_text_header_cell(row, "", "", "motif_ordinal"); | |
| 4460 add_text_header_cell(row, "Motif", "pop_motifs_word", "motif_word"); | |
| 4461 add_text_header_cell(row, "Logo", "pop_motifs_logo", "motif_logo"); | |
| 4462 if (data.options.revcomp) { | |
| 4463 add_text_header_cell(row, "RC Logo", "pop_motifs_rc_logo", "motif_logo"); | |
| 4464 } | |
| 4465 add_text_header_cell(row, "E-value", "pop_motifs_evalue", "motif_evalue"); | |
| 4466 add_text_header_cell(row, "Unerased E-value", "pop_motifs_uevalue", "motif_evalue"); | |
| 4467 add_text_header_cell(row, "More", "pop_more", "motif_more"); | |
| 4468 add_text_header_cell(row, "Submit/Download", "pop_submit_dl", "motif_submit"); | |
| 4469 row.className = "more"; | |
| 4470 return row; | |
| 4471 } | |
| 4472 | |
| 4473 /* | |
| 4474 * Make a compact motif summary row for the discovered motifs. | |
| 4475 */ | |
| 4476 function make_motif_row(tbody, ordinal, motif) { | |
| 4477 var row = document.createElement("tr"); | |
| 4478 add_text_cell(row, "" + ordinal + ".", "motif_ordinal"); | |
| 4479 add_text_cell(row, motif["id"], "motif_word"); | |
| 4480 add_cell(row, make_logo(motif, 50, false), "motif_logo"); | |
| 4481 if (data.options.revcomp) { | |
| 4482 add_cell(row, make_logo(motif, 50, true), "motif_logo"); | |
| 4483 } | |
| 4484 add_text_cell(row, motif["evalue"], "motif_evalue"); | |
| 4485 add_text_cell(row, motif["unerased_evalue"], "motif_evalue"); | |
| 4486 add_cell(row, make_sym_btn(text_pair("\u21A7", "less", "\u21A5", "more"), "Show more information.", function(e) { toggle_class(tbody, "collapsed"); }, "\u21A5", ""), "motif_more"); | |
| 4487 add_cell(row, make_sym_btn("\u21E2", "Submit the motif to another MEME Suite program or download it.", function(e) { action_show_outpop(e, ordinal); }), "motif_submit"); | |
| 4488 return row; | |
| 4489 } | |
| 4490 | |
| 4491 /* | |
| 4492 * Make a sortable table of enriched matching rows. | |
| 4493 */ | |
| 4494 function make_motif_words(motif) { | |
| 4495 var row, i, match; | |
| 4496 var table = document.createElement("table"); | |
| 4497 var thead = document.createElement("thead"); | |
| 4498 row = document.createElement("tr"); | |
| 4499 add_text_header_cell(row, "Word", "pop_match_word", "match_word", function(e) {sort_table(this, compare_words);}); | |
| 4500 add_text_header_cell(row, "Positives", "pop_match_pos", "match_count", function(e) {sort_table(this, compare_counts);}); | |
| 4501 add_text_header_cell(row, "Negatives", "pop_match_neg", "match_count", function(e) {sort_table(this, compare_counts);}); | |
| 4502 add_text_header_cell(row, "P-value", "pop_match_pval", "match_evalue", function(e) {sort_table(this, compare_evalues);}); | |
| 4503 add_text_header_cell(row, "E-value", "pop_match_eval", "match_evalue", function(e) {sort_table(this, compare_evalues);}); | |
| 4504 thead.appendChild(row); | |
| 4505 table.appendChild(thead); | |
| 4506 var tbody = document.createElement("tbody"); | |
| 4507 for (i = 0; i < motif.matches.length; i++) { | |
| 4508 match = motif.matches[i]; | |
| 4509 row = document.createElement("tr"); | |
| 4510 add_cell(row, make_seq(match.seq), "match_word"); | |
| 4511 add_text_cell(row, match.p + " / " + data.sequence_db.count, "match_count"); | |
| 4512 add_text_cell(row, match.n + " / " + data.control_db.count, "match_count"); | |
| 4513 add_text_cell(row, match.pvalue, "match_evalue"); | |
| 4514 add_text_cell(row, match.evalue, "match_evalue"); | |
| 4515 tbody.appendChild(row); | |
| 4516 } | |
| 4517 table.appendChild(tbody); | |
| 4518 return table; | |
| 4519 } | |
| 4520 | |
| 4521 /* | |
| 4522 * Make an expanded view of a discovered motif. | |
| 4523 */ | |
| 4524 function make_motif_exp(tbody, ordinal, motif) { | |
| 4525 "use strict"; | |
| 4526 var box, pspm, logo_box; | |
| 4527 box = $("tmpl_motif_expanded").cloneNode(true); | |
| 4528 toggle_class(box, "template", false); | |
| 4529 box.id = ""; | |
| 4530 find_child(box, "tvar_logo").appendChild(make_logo(motif, 150, false)); | |
| 4531 if (data.options.revcomp) { | |
| 4532 find_child(box, "tvar_rclogo").appendChild(make_logo(motif, 150, true)); | |
| 4533 } | |
| 4534 set_tvar(box, "tvar_p", motif["p"]); | |
| 4535 set_tvar(box, "tvar_p_total", data.sequence_db.count); | |
| 4536 set_tvar(box, "tvar_n", motif["n"]); | |
| 4537 set_tvar(box, "tvar_n_total", data.control_db.count); | |
| 4538 set_tvar(box, "tvar_pvalue", motif["pvalue"]); | |
| 4539 set_tvar(box, "tvar_evalue", motif["evalue"]); | |
| 4540 set_tvar(box, "tvar_uevalue", motif["unerased_evalue"]); | |
| 4541 set_tvar(box, "tvar_words", make_motif_words(motif)); | |
| 4542 var cell = document.createElement("td"); | |
| 4543 cell.colSpan = 8; | |
| 4544 cell.appendChild(box); | |
| 4545 var row = document.createElement("tr"); | |
| 4546 row.className = "more"; | |
| 4547 row.appendChild(cell); | |
| 4548 return row; | |
| 4549 } | |
| 4550 | |
| 4551 /* | |
| 4552 * Convert a string containing a scientific number into the log of that number | |
| 4553 * without having an intermediate representation of the number. | |
| 4554 * This is intended to avoid underflow problems with the tiny evalues that | |
| 4555 * MEME and DREME can create. | |
| 4556 */ | |
| 4557 function sci2log(scinum) { | |
| 4558 "use strict"; | |
| 4559 var ev_re, match, sig, exp; | |
| 4560 ev_re = /^(.*)e(.*)$/; | |
| 4561 if (match = ev_re.exec(scinum)) { | |
| 4562 sig = parseFloat(match[1]); | |
| 4563 exp = parseInt(match[2]); | |
| 4564 return Math.log(sig) + (exp * Math.log(10)); | |
| 4565 } | |
| 4566 return 0; | |
| 4567 } | |
| 4568 | |
| 4569 /* | |
| 4570 * Create a table of discovered motifs. A fresh table body is used for each | |
| 4571 * motif to make hiding/showing rows with css easier. | |
| 4572 */ | |
| 4573 function make_motifs() { | |
| 4574 "use strict"; | |
| 4575 var i, row, tbody, motif, ordinal; | |
| 4576 // make the motifs table | |
| 4577 var container = $("motifs"); | |
| 4578 container.innerHTML = ""; // clear content | |
| 4579 var table = document.createElement("table"); | |
| 4580 // add a header that is always shown | |
| 4581 var thead = document.createElement("thead"); | |
| 4582 thead.appendChild(make_motif_header()); | |
| 4583 table.appendChild(thead); | |
| 4584 for (i = 0; i < data.motifs.length; i++) { | |
| 4585 ordinal = i + 1; | |
| 4586 motif = data.motifs[i]; | |
| 4587 tbody = document.createElement("tbody"); | |
| 4588 tbody.className = "collapsed"; | |
| 4589 tbody.appendChild(make_motif_row(tbody, ordinal, motif)); | |
| 4590 tbody.appendChild(make_motif_exp(tbody, ordinal, motif)); | |
| 4591 // create a following header for every row except the last one | |
| 4592 if ((i + 1) < data.motifs.length) tbody.appendChild(make_motif_header()); | |
| 4593 table.appendChild(tbody); | |
| 4594 } | |
| 4595 container.appendChild(table); | |
| 4596 } | |
| 4597 | |
| 4598 /* | |
| 4599 * Create a table showing all the alphabet symbols, their names and frequencies. | |
| 4600 */ | |
| 4601 function make_alpha_bg(alph, freqs) { | |
| 4602 function colour_symbol(index) { | |
| 4603 var span = document.createElement("span"); | |
| 4604 span.appendChild(document.createTextNode(alph.get_symbol(index))); | |
| 4605 span.style.color = alph.get_colour(index); | |
| 4606 span.className = "alpha_symbol"; | |
| 4607 return span; | |
| 4608 } | |
| 4609 var table, thead, tbody, row, th, span, i; | |
| 4610 // create table | |
| 4611 table = document.createElement("table"); | |
| 4612 table.className = "inputs"; | |
| 4613 // create header | |
| 4614 thead = document.createElement("thead"); | |
| 4615 table.appendChild(thead); | |
| 4616 row = thead.insertRow(thead.rows.length); | |
| 4617 if (alph.has_complement()) { | |
| 4618 add_text_header_cell(row, "Name", "pop_alph_name"); | |
| 4619 add_text_header_cell(row, "Bg.", "pop_alph_control"); | |
| 4620 add_text_header_cell(row, ""); | |
| 4621 add_text_header_cell(row, ""); | |
| 4622 add_text_header_cell(row, ""); | |
| 4623 add_text_header_cell(row, "Bg.", "pop_alph_control"); | |
| 4624 add_text_header_cell(row, "Name", "pop_alph_name"); | |
| 4625 } else { | |
| 4626 add_text_header_cell(row, ""); | |
| 4627 add_text_header_cell(row, "Name", "pop_alph_name"); | |
| 4628 add_text_header_cell(row, "Bg.", "pop_alph_control"); | |
| 4629 } | |
| 4630 // add alphabet entries | |
| 4631 tbody = document.createElement("tbody"); | |
| 4632 table.appendChild(tbody); | |
| 4633 if (alph.has_complement()) { | |
| 4634 for (i = 0; i < alph.get_size_core(); i++) { | |
| 4635 var c = alph.get_complement(i); | |
| 4636 if (i > c) continue; | |
| 4637 row = tbody.insertRow(tbody.rows.length); | |
| 4638 add_text_cell(row, alph.get_name(i)); | |
| 4639 add_text_cell(row, "" + freqs[i].toFixed(3)); | |
| 4640 add_cell(row, colour_symbol(i)); | |
| 4641 add_text_cell(row, "~"); | |
| 4642 add_cell(row, colour_symbol(c)); | |
| 4643 add_text_cell(row, "" + freqs[c].toFixed(3)); | |
| 4644 add_text_cell(row, alph.get_name(c)); | |
| 4645 } | |
| 4646 } else { | |
| 4647 for (i = 0; i < alph.get_size_core(); i++) { | |
| 4648 row = tbody.insertRow(tbody.rows.length); | |
| 4649 add_cell(row, colour_symbol(i)); | |
| 4650 add_text_cell(row, alph.get_name(i)); | |
| 4651 add_text_cell(row, "" + freqs[i].toFixed(3)); | |
| 4652 } | |
| 4653 } | |
| 4654 return table; | |
| 4655 } | |
| 4656 | |
| 4657 /* | |
| 4658 * Updates the format download text in the popup. | |
| 4659 * This is called when either the format or current motif changes. | |
| 4660 */ | |
| 4661 function update_outpop_format(index) { | |
| 4662 var motif = data.motifs[index]; | |
| 4663 var fn = [motif_count_matrix, motif_prob_matrix, motif_minimal_meme]; | |
| 4664 var suffix = ["_counts.txt", "_freqs.txt", ".meme"]; | |
| 4665 var format = parseInt($("text_format").value); | |
| 4666 var text = fn[format](motif); | |
| 4667 prepare_download(text, "text/plain", motif.id + suffix[format], $("outpop_text_dl")); | |
| 4668 $("outpop_text").value = text; | |
| 4669 } | |
| 4670 | |
| 4671 /* | |
| 4672 * Updates the motif logos and format download text in the popup. | |
| 4673 * This is called whenever the current motif changes. | |
| 4674 */ | |
| 4675 function update_outpop_motif(index) { | |
| 4676 "use strict"; | |
| 4677 var motifs, motif, pspm, logo, canvas, num; | |
| 4678 motifs = data["motifs"]; | |
| 4679 if (index < 0 || index >= motifs.length) {return;} | |
| 4680 current_motif = index; | |
| 4681 motif = motifs[index]; | |
| 4682 pspm = new Pspm(motif["pwm"]); | |
| 4683 logo = new Logo(dreme_alphabet, ""); | |
| 4684 logo.add_pspm(pspm, 0); | |
| 4685 canvas = $("outpop_logo"); | |
| 4686 canvas.width = canvas.width; // clear canvas | |
| 4687 draw_logo_on_canvas(logo, canvas, false); | |
| 4688 canvas = $("outpop_logo_rc"); | |
| 4689 canvas.width = canvas.width; // clear rc canvas | |
| 4690 if (data.options.revcomp) { | |
| 4691 pspm.reverse_complement(dreme_alphabet); | |
| 4692 logo = new Logo(dreme_alphabet, ""); | |
| 4693 logo.add_pspm(pspm, 0); | |
| 4694 draw_logo_on_canvas(logo, canvas, false); | |
| 4695 } | |
| 4696 num = $("outpop_num"); | |
| 4697 num.innerHTML = ""; | |
| 4698 num.appendChild(document.createTextNode("" + (index + 1))); | |
| 4699 update_outpop_format(index); | |
| 4700 } | |
| 4701 | |
| 4702 | |
| 4703 /* | |
| 4704 * Initialise and display the download popup. | |
| 4705 */ | |
| 4706 function action_show_outpop(e, ordinal) { | |
| 4707 "use strict"; | |
| 4708 function init() { | |
| 4709 "use strict"; | |
| 4710 var close_btn, next_btn, prev_btn, cancel_btn, do_btn; | |
| 4711 var tab1, tab2, tab3; | |
| 4712 var pnl1, pnl2, pnl3; | |
| 4713 var format_list; | |
| 4714 var tbl_submit, inputs, i, default_prog; | |
| 4715 close_btn = $("outpop_close"); | |
| 4716 close_btn.addEventListener("click", action_hide_outpop, false); | |
| 4717 close_btn.addEventListener("keydown", action_hide_outpop, false); | |
| 4718 next_btn = $("outpop_next"); | |
| 4719 next_btn.addEventListener("click", action_outpop_next, false); | |
| 4720 next_btn.addEventListener("keydown", action_outpop_next, false); | |
| 4721 prev_btn = $("outpop_prev"); | |
| 4722 prev_btn.addEventListener("click", action_outpop_prev, false); | |
| 4723 prev_btn.addEventListener("keydown", action_outpop_prev, false); | |
| 4724 cancel_btn = $("outpop_cancel"); | |
| 4725 cancel_btn.addEventListener("click", action_hide_outpop, false); | |
| 4726 do_btn = $("outpop_do"); | |
| 4727 do_btn.addEventListener("click", action_outpop_submit, false); | |
| 4728 tab1 = $("outpop_tab_1"); | |
| 4729 tab1.tabIndex = 0; | |
| 4730 tab1.addEventListener("click", action_outpop_tab, false); | |
| 4731 tab1.addEventListener("keydown", action_outpop_tab, false); | |
| 4732 tab2 = $("outpop_tab_2"); | |
| 4733 tab2.tabIndex = 0; | |
| 4734 tab2.addEventListener("click", action_outpop_tab, false); | |
| 4735 tab2.addEventListener("keydown", action_outpop_tab, false); | |
| 4736 tab3 = $("outpop_tab_3"); | |
| 4737 tab3.tabIndex = 0; | |
| 4738 tab3.addEventListener("click", action_outpop_tab, false); | |
| 4739 tab3.addEventListener("keydown", action_outpop_tab, false); | |
| 4740 pnl1 = $("outpop_pnl_1"); | |
| 4741 pnl2 = $("outpop_pnl_2"); | |
| 4742 pnl3 = $("outpop_pnl_3"); | |
| 4743 toggle_class(tab1, "activeTab", true); | |
| 4744 toggle_class(tab2, "activeTab", false); | |
| 4745 toggle_class(tab3, "activeTab", false); | |
| 4746 pnl1.style.display = "block"; | |
| 4747 pnl2.style.display = "none"; | |
| 4748 pnl3.style.display = "none"; | |
| 4749 format_list = $("text_format"); | |
| 4750 format_list.addEventListener("change", action_outpop_format, false); | |
| 4751 // setup program selection | |
| 4752 tbl_submit = $("programs"); | |
| 4753 // when not dna, hide the inputs for programs that require dna motifs | |
| 4754 toggle_class(tbl_submit, "alphabet_dna", dreme_alphabet.has_complement());//TODO FIXME alphabet_dna is a bad name for a field when allowing custom alphabets | |
| 4755 // add a click listener for the radio buttons | |
| 4756 inputs = tbl_submit.querySelectorAll("input[type='radio']"); | |
| 4757 for (i = 0; i < inputs.length; i++) { | |
| 4758 inputs[i].addEventListener("click", action_outpop_program, false); | |
| 4759 } | |
| 4760 // ensure that a default program option is selected for DNA and Protein | |
| 4761 default_prog = document.getElementById(dreme_alphabet.has_complement() ? "submit_tomtom" : "submit_fimo"); | |
| 4762 default_prog.checked = true; | |
| 4763 action_outpop_program.call(default_prog); | |
| 4764 // disable reverse-complement when not DNA | |
| 4765 $("logo_rc_option").disabled = !dreme_alphabet.has_complement(); | |
| 4766 // set errorbars on when ssc is on | |
| 4767 $("logo_ssc").addEventListener("change", action_outpop_ssc, false); | |
| 4768 } | |
| 4769 // store the focused element | |
| 4770 action_hide_outpop.last_active = document.activeElement; | |
| 4771 if (!e) e = window.event; | |
| 4772 if (e.type === "keydown") { | |
| 4773 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
| 4774 return; | |
| 4775 } | |
| 4776 // stop a submit or something like that | |
| 4777 e.preventDefault(); | |
| 4778 } | |
| 4779 // hide the help popup | |
| 4780 help_popup(); | |
| 4781 // on first load initilize the popup | |
| 4782 if (!action_show_outpop.ready) { | |
| 4783 init(); | |
| 4784 action_show_outpop.ready = true; | |
| 4785 } | |
| 4786 update_outpop_motif(ordinal - 1); | |
| 4787 // display the download popup | |
| 4788 $("grey_out_page").style.display = "block"; | |
| 4789 $("download").style.display = "block"; | |
| 4790 $("outpop_close").focus(); | |
| 4791 } | |
| 4792 | |
| 4793 /* | |
| 4794 * Hide the download popup. | |
| 4795 */ | |
| 4796 function action_hide_outpop(e) { | |
| 4797 if (!e) e = window.event; | |
| 4798 if (e.type === "keydown") { | |
| 4799 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
| 4800 return; | |
| 4801 } | |
| 4802 // stop a submit or something like that | |
| 4803 e.preventDefault(); | |
| 4804 } | |
| 4805 $("download").style.display = "none"; | |
| 4806 $("grey_out_page").style.display = "none"; | |
| 4807 if (typeof action_hide_outpop.last_active !== "undefined") { | |
| 4808 action_hide_outpop.last_active.focus(); | |
| 4809 } | |
| 4810 } | |
| 4811 | |
| 4812 /* | |
| 4813 * Show the next motif in the download popup. | |
| 4814 */ | |
| 4815 function action_outpop_next(e) { | |
| 4816 if (!e) e = window.event; | |
| 4817 if (e.type === "keydown") { | |
| 4818 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
| 4819 return; | |
| 4820 } | |
| 4821 // stop a submit or something like that | |
| 4822 e.preventDefault(); | |
| 4823 } | |
| 4824 update_outpop_motif(current_motif + 1); | |
| 4825 } | |
| 4826 | |
| 4827 /* | |
| 4828 * Show the previous motif in the download popup. | |
| 4829 */ | |
| 4830 function action_outpop_prev(e) { | |
| 4831 if (!e) e = window.event; | |
| 4832 if (e.type === "keydown") { | |
| 4833 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
| 4834 return; | |
| 4835 } | |
| 4836 // stop a submit or something like that | |
| 4837 e.preventDefault(); | |
| 4838 } | |
| 4839 update_outpop_motif(current_motif - 1); | |
| 4840 } | |
| 4841 | |
| 4842 /* | |
| 4843 * Highlight the selected row in the program list. | |
| 4844 */ | |
| 4845 function action_outpop_program() { | |
| 4846 "use strict"; | |
| 4847 var table, tr, rows, i; | |
| 4848 tr = find_parent_tag(this, "TR"); | |
| 4849 table = find_parent_tag(tr, "TABLE"); | |
| 4850 rows = table.querySelectorAll("tr"); | |
| 4851 for (i = 0; i < rows.length; i++) { | |
| 4852 toggle_class(rows[i], "selected", rows[i] === tr); | |
| 4853 } | |
| 4854 } | |
| 4855 | |
| 4856 /* | |
| 4857 * Enable error bars when small sample correction is enabled. | |
| 4858 */ | |
| 4859 function action_outpop_ssc() { | |
| 4860 "use strict"; | |
| 4861 $("logo_err").value = $("logo_ssc").value; | |
| 4862 } | |
| 4863 | |
| 4864 /* | |
| 4865 * Submit the motif to the selected program. | |
| 4866 */ | |
| 4867 function action_outpop_submit(e) { | |
| 4868 "use strict"; | |
| 4869 var form, input, program, motifs; | |
| 4870 // find out which program is selected | |
| 4871 var radios, i; | |
| 4872 radios = document.getElementsByName("program"); | |
| 4873 program = "fimo"; // default to fimo, since it works with all alphabet types | |
| 4874 for (i = 0; i < radios.length; i++) { | |
| 4875 if (radios[i].checked) program = radios[i].value; | |
| 4876 } | |
| 4877 | |
| 4878 motifs = motif_minimal_meme(data.motifs[current_motif]); | |
| 4879 form = document.createElement("form"); | |
| 4880 form.setAttribute("method", "post"); | |
| 4881 form.setAttribute("action", site_url + "/tools/" + program); | |
| 4882 | |
| 4883 input = document.createElement("input"); | |
| 4884 input.setAttribute("type", "hidden"); | |
| 4885 input.setAttribute("name", "motifs_embed"); | |
| 4886 input.setAttribute("value", motifs); | |
| 4887 form.appendChild(input); | |
| 4888 | |
| 4889 document.body.appendChild(form); | |
| 4890 form.submit(); | |
| 4891 document.body.removeChild(form); | |
| 4892 } | |
| 4893 | |
| 4894 /* | |
| 4895 * Download the format text. | |
| 4896 * Wire the link containing the data URI text to a download button so it looks | |
| 4897 * the same as the server submit stuff. | |
| 4898 */ | |
| 4899 function action_outpop_download_motif(e) { | |
| 4900 $("outpop_text_dl").click(); | |
| 4901 } | |
| 4902 | |
| 4903 /* | |
| 4904 * Download the motif logo. | |
| 4905 * The EPS format can be calculated locally in Javascript | |
| 4906 */ | |
| 4907 function action_outpop_download_logo(e) { | |
| 4908 "use strict"; | |
| 4909 var pspm, logo, eps; | |
| 4910 var logo_rc, logo_ssc, logo_width, logo_height; | |
| 4911 var motif = data.motifs[current_motif]; | |
| 4912 if ($("logo_format").value == "0") { // EPS | |
| 4913 logo_rc = ($("logo_rc").value == "1"); | |
| 4914 logo_ssc = ($("logo_ssc").value == "1"); | |
| 4915 logo_width = parseFloat($("logo_width").value); | |
| 4916 if (isNaN(logo_width) || !isFinite(logo_width) || logo_width <= 0) logo_width = null; | |
| 4917 logo_height = parseFloat($("logo_height").value); | |
| 4918 if (isNaN(logo_height) || !isFinite(logo_height) || logo_height <= 0) logo_height = null; | |
| 4919 // create a PSPM from the motif | |
| 4920 pspm = motif_pspm(motif); | |
| 4921 if (logo_rc) pspm.reverse_complement(dreme_alphabet); | |
| 4922 logo = new Logo(dreme_alphabet); | |
| 4923 logo.add_pspm(pspm, 0); | |
| 4924 eps = logo.as_eps({"ssc": logo_ssc, "logo_width": logo_width, "logo_height": logo_height}); | |
| 4925 prepare_download(eps, "application/postscript", motif.id + ".eps"); | |
| 4926 } else { | |
| 4927 $("logo_motifs").value = motif_minimal_meme(motif); | |
| 4928 $("logo_form").submit(); | |
| 4929 } | |
| 4930 } | |
| 4931 | |
| 4932 /* | |
| 4933 * Change the selected tab in the download popup. | |
| 4934 */ | |
| 4935 function action_outpop_tab(e) { | |
| 4936 "use strict"; | |
| 4937 var tab1, tab2, tab3, pnl1, pnl2, pnl3, do_btn; | |
| 4938 if (!e) e = window.event; | |
| 4939 if (e.type === "keydown") { | |
| 4940 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
| 4941 return; | |
| 4942 } | |
| 4943 // stop a submit or something like that | |
| 4944 e.preventDefault(); | |
| 4945 } | |
| 4946 tab1 = $("outpop_tab_1"); | |
| 4947 tab2 = $("outpop_tab_2"); | |
| 4948 tab3 = $("outpop_tab_3"); | |
| 4949 pnl1 = $("outpop_pnl_1"); | |
| 4950 pnl2 = $("outpop_pnl_2"); | |
| 4951 pnl3 = $("outpop_pnl_3"); | |
| 4952 do_btn = $("outpop_do"); | |
| 4953 | |
| 4954 toggle_class(tab1, "activeTab", (this === tab1)); | |
| 4955 toggle_class(tab2, "activeTab", (this === tab2)); | |
| 4956 toggle_class(tab3, "activeTab", (this === tab3)); | |
| 4957 pnl1.style.display = ((this === tab1) ? "block" : "none"); | |
| 4958 pnl2.style.display = ((this === tab2) ? "block" : "none"); | |
| 4959 pnl3.style.display = ((this === tab3) ? "block" : "none"); | |
| 4960 do_btn.value = ((this === tab1) ? "Submit" : "Download"); | |
| 4961 do_btn.removeEventListener("click", action_outpop_submit, false); | |
| 4962 do_btn.removeEventListener("click", action_outpop_download_logo, false); | |
| 4963 do_btn.removeEventListener("click", action_outpop_download_motif, false); | |
| 4964 if (this === tab1) { | |
| 4965 do_btn.addEventListener("click", action_outpop_submit, false); | |
| 4966 } else if (this === tab2) { | |
| 4967 do_btn.addEventListener("click", action_outpop_download_motif, false); | |
| 4968 } else { | |
| 4969 do_btn.addEventListener("click", action_outpop_download_logo, false); | |
| 4970 } | |
| 4971 } | |
| 4972 | |
| 4973 /* | |
| 4974 * Update the text in the download format popup. | |
| 4975 */ | |
| 4976 function action_outpop_format() { | |
| 4977 update_outpop_format(current_motif); | |
| 4978 } | |
| 4979 | |
| 4980 /* | |
| 4981 * Find all text nodes in the given container. | |
| 4982 */ | |
| 4983 function text_nodes(container) { | |
| 4984 var textNodes = []; | |
| 4985 var stack = [container]; | |
| 4986 // depth first search to maintain ordering when flattened | |
| 4987 while (stack.length > 0) { | |
| 4988 var node = stack.pop(); | |
| 4989 if (node.nodeType == Node.TEXT_NODE) { | |
| 4990 textNodes.push(node); | |
| 4991 } else { | |
| 4992 for (var i = node.childNodes.length-1; i >= 0; i--) { | |
| 4993 stack.push(node.childNodes[i]); | |
| 4994 } | |
| 4995 } | |
| 4996 } | |
| 4997 return textNodes; | |
| 4998 } | |
| 4999 | |
| 5000 /* | |
| 5001 * Get the text out of a specific text node. | |
| 5002 */ | |
| 5003 function node_text(node) { | |
| 5004 if (node === undefined) { | |
| 5005 return ''; | |
| 5006 } else if (node.textContent) { | |
| 5007 return node.textContent; | |
| 5008 } else if (node.innerText) { | |
| 5009 return node.innerText; | |
| 5010 } else { | |
| 5011 return ''; | |
| 5012 } | |
| 5013 } | |
| 5014 | |
| 5015 /* | |
| 5016 * Get the text contained within the element. | |
| 5017 */ | |
| 5018 function elem_text(elem, separator) { | |
| 5019 if (separator === undefined) separator = ''; | |
| 5020 return text_nodes(elem).map(node_text).join(separator); | |
| 5021 } | |
| 5022 | |
| 5023 /* | |
| 5024 * Sort all rows in the first table body based on the column of the given element and the comparison function. | |
| 5025 * The sort is not very fast and is intended for small tables only. | |
| 5026 */ | |
| 5027 function sort_table(colEle, compare_function) { | |
| 5028 //find the parent of colEle that is either a td or th | |
| 5029 var i, j; | |
| 5030 var cell = colEle; | |
| 5031 while (true) { | |
| 5032 if (cell == null) return; | |
| 5033 if (cell.nodeType == Node.ELEMENT_NODE && | |
| 5034 (cell.tagName.toLowerCase() == "td" || cell.tagName.toLowerCase() == "th")) { | |
| 5035 break; | |
| 5036 } | |
| 5037 cell = cell.parentNode; | |
| 5038 } | |
| 5039 //find the parent of cell that is a tr | |
| 5040 var row = cell; | |
| 5041 while (true) { | |
| 5042 if (row == null) return; | |
| 5043 if (row.nodeType == Node.ELEMENT_NODE && row.tagName.toLowerCase() == "tr") { | |
| 5044 break; | |
| 5045 } | |
| 5046 row = row.parentNode; | |
| 5047 } | |
| 5048 //find the parent of row that is a table | |
| 5049 var table = row; | |
| 5050 while (true) { | |
| 5051 if (table == null) return; | |
| 5052 if (table.nodeType == Node.ELEMENT_NODE && table.tagName.toLowerCase() == "table") { | |
| 5053 break; | |
| 5054 } | |
| 5055 table = table.parentNode; | |
| 5056 } | |
| 5057 var column_index = cell.cellIndex; | |
| 5058 // do a bubble sort, because the tables are so small it doesn't matter | |
| 5059 var change; | |
| 5060 var trs = table.tBodies[0].getElementsByTagName('tr'); | |
| 5061 var already_sorted = true; | |
| 5062 var reverse = false; | |
| 5063 while (true) { | |
| 5064 do { | |
| 5065 change = false; | |
| 5066 for (i = 0; i < trs.length -1; i++) { | |
| 5067 var v1 = elem_text(trs[i].cells[column_index]); | |
| 5068 var v2 = elem_text(trs[i+1].cells[column_index]); | |
| 5069 if (reverse) { | |
| 5070 var tmp = v1; | |
| 5071 v1 = v2; | |
| 5072 v2 = tmp; | |
| 5073 } | |
| 5074 if (compare_function(v1, v2) > 0) { | |
| 5075 exchange(trs[i], trs[i+1], table); | |
| 5076 change = true; | |
| 5077 already_sorted = false; | |
| 5078 trs = table.tBodies[0].getElementsByTagName('tr'); | |
| 5079 } | |
| 5080 } | |
| 5081 } while (change); | |
| 5082 if (reverse) break;// we've sorted twice so exit | |
| 5083 if (!already_sorted) break;// sort did something so exit | |
| 5084 // when it's sorted one way already then sort the opposite way | |
| 5085 reverse = true; | |
| 5086 } | |
| 5087 // put arrows on the headers | |
| 5088 var headers = table.tHead.getElementsByTagName('tr'); | |
| 5089 for (i = 0; i < headers.length; i++) { | |
| 5090 for (j = 0; j < headers[i].cells.length; j++) { | |
| 5091 var cell = headers[i].cells[j]; | |
| 5092 var arrows = cell.getElementsByClassName("sort_arrow"); | |
| 5093 var arrow; | |
| 5094 if (arrows.length == 0) { | |
| 5095 arrow = document.createElement("span"); | |
| 5096 arrow.className = "sort_arrow"; | |
| 5097 cell.insertBefore(arrow, cell.firstChild); | |
| 5098 } else { | |
| 5099 arrow = arrows[0]; | |
| 5100 } | |
| 5101 arrow.innerHTML = ""; | |
| 5102 if (j == column_index) { | |
| 5103 arrow.appendChild(document.createTextNode(reverse ? "\u25B2" : "\u25BC")); | |
| 5104 } | |
| 5105 } | |
| 5106 } | |
| 5107 } | |
| 5108 | |
| 5109 /* | |
| 5110 * Swap two rows in a table. | |
| 5111 */ | |
| 5112 function exchange(oRowI, oRowJ, oTable) { | |
| 5113 var i = oRowI.rowIndex; | |
| 5114 var j = oRowJ.rowIndex; | |
| 5115 if (i == j+1) { | |
| 5116 oTable.tBodies[0].insertBefore(oRowI, oRowJ); | |
| 5117 } if (j == i+1) { | |
| 5118 oTable.tBodies[0].insertBefore(oRowJ, oRowI); | |
| 5119 } else { | |
| 5120 var tmpNode = oTable.tBodies[0].replaceChild(oRowI, oRowJ); | |
| 5121 if(typeof(oRowI) != "undefined") { | |
| 5122 oTable.tBodies[0].insertBefore(tmpNode, oRowI); | |
| 5123 } else { | |
| 5124 oTable.appendChild(tmpNode); | |
| 5125 } | |
| 5126 } | |
| 5127 } | |
| 5128 | |
| 5129 /* | |
| 5130 * Compare two E-values which may be very small. | |
| 5131 */ | |
| 5132 function compare_evalues(v1, v2) { | |
| 5133 var e1 = sci2log(v1); | |
| 5134 var e2 = sci2log(v2); | |
| 5135 if (e1 < e2) return -1; | |
| 5136 else if (e1 > e2) return 1; | |
| 5137 return 0; | |
| 5138 } | |
| 5139 | |
| 5140 /* | |
| 5141 * Compare two counts. | |
| 5142 */ | |
| 5143 function compare_counts(v1, v2) { | |
| 5144 var re = /(\d+)\s*\/\s*\d+/; | |
| 5145 var m1 = re.exec(v1); | |
| 5146 var m2 = re.exec(v2); | |
| 5147 if (m1 == null && m2 == null) return 0; | |
| 5148 if (m1 == null) return -1; | |
| 5149 if (m2 == null) return 1; | |
| 5150 return parseInt(m2[1]) - parseInt(m1[1]); | |
| 5151 } | |
| 5152 | |
| 5153 /* | |
| 5154 * Compare two sequence words. | |
| 5155 */ | |
| 5156 function compare_words(v1, v2) { | |
| 5157 return v1.localeCompare(v2); | |
| 5158 } | |
| 5159 | |
| 5160 | |
| 5161 </script> | |
| 5162 <style> | |
| 5163 /* The following is the content of meme.css */ | |
| 5164 body { background-color:white; font-size: 12px; font-family: Verdana, Arial, Helvetica, sans-serif;} | |
| 5165 | |
| 5166 div.help { | |
| 5167 display: inline-block; | |
| 5168 margin: 0px; | |
| 5169 padding: 0px; | |
| 5170 width: 12px; | |
| 5171 height: 13px; | |
| 5172 cursor: pointer; | |
| 5173 background-image: url(data:image/gif;base64,R0lGODlhDAANAIABANR0AP///yH5BAEAAAEALAAAAAAMAA0AAAIdhI8Xy22MIFgv1DttrrJ7mlGNNo4c+aFg6SQuUAAAOw==); | |
| 5174 } | |
| 5175 | |
| 5176 div.help:hover { | |
| 5177 background-image: url(data:image/gif;base64,R0lGODlhDAANAKEAANR0AP///9R0ANR0ACH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEAAAIALAAAAAAMAA0AAAIdDGynCe3PgoxONntvwqz2/z2K2ImjR0KhmSIZUgAAOw==); | |
| 5178 } | |
| 5179 | |
| 5180 p.spaced { line-height: 1.8em;} | |
| 5181 | |
| 5182 span.citation { font-family: "Book Antiqua", "Palatino Linotype", serif; color: #004a4d;} | |
| 5183 | |
| 5184 p.pad { padding-left: 30px; padding-top: 5px; padding-bottom: 10px;} | |
| 5185 | |
| 5186 td.jump { font-size: 13px; color: #ffffff; background-color: #00666a; | |
| 5187 font-family: Georgia, "Times New Roman", Times, serif;} | |
| 5188 | |
| 5189 a.jump { margin: 15px 0 0; font-style: normal; font-variant: small-caps; | |
| 5190 font-weight: bolder; font-family: Georgia, "Times New Roman", Times, serif;} | |
| 5191 | |
| 5192 h2.mainh {font-size: 1.5em; font-style: normal; margin: 15px 0 0; | |
| 5193 font-variant: small-caps; font-family: Georgia, "Times New Roman", Times, serif;} | |
| 5194 | |
| 5195 h2.line {border-bottom: 1px solid #CCCCCC; font-size: 1.5em; font-style: normal; | |
| 5196 margin: 15px 0 0; padding-bottom: 3px; font-variant: small-caps; | |
| 5197 font-family: Georgia, "Times New Roman", Times, serif;} | |
| 5198 | |
| 5199 h4 {border-bottom: 1px solid #CCCCCC; font-size: 1.2em; font-style: normal; | |
| 5200 margin: 10px 0 0; padding-bottom: 3px; font-family: Georgia, "Times New Roman", Times, serif;} | |
| 5201 | |
| 5202 h5 {margin: 0px} | |
| 5203 | |
| 5204 a.help { font-size: 9px; font-style: normal; text-transform: uppercase; | |
| 5205 font-family: Georgia, "Times New Roman", Times, serif;} | |
| 5206 | |
| 5207 div.pad { padding-left: 30px; padding-top: 5px; padding-bottom: 10px;} | |
| 5208 | |
| 5209 div.pad1 { margin: 10px 5px;} | |
| 5210 | |
| 5211 div.pad2 { margin: 25px 5px 5px;} | |
| 5212 h2.pad2 { padding: 25px 5px 5px;} | |
| 5213 | |
| 5214 div.pad3 { padding: 5px 0px 10px 30px;} | |
| 5215 | |
| 5216 div.box { border: 2px solid #CCCCCC; padding:10px; overflow: hidden;} | |
| 5217 | |
| 5218 div.bar { border-left: 7px solid #00666a; padding:5px; margin-top:25px; } | |
| 5219 | |
| 5220 div.subsection {margin:25px 0px;} | |
| 5221 | |
| 5222 img {border:0px none;} | |
| 5223 | |
| 5224 th.majorth {text-align:left;} | |
| 5225 th.minorth {font-weight:normal; text-align:left; width:8em; padding: 3px 0px;} | |
| 5226 th.actionth {font-weight:normal; text-align:left;} | |
| 5227 | |
| 5228 .explain h5 {font-size:1em; margin-left: 1em;} | |
| 5229 | |
| 5230 div.doc {margin-left: 2em; margin-bottom: 3em;} | |
| 5231 | |
| 5232 th.trainingset { | |
| 5233 border-bottom: thin dashed black; | |
| 5234 font-weight:normal; | |
| 5235 padding:0px 10px; | |
| 5236 } | |
| 5237 div.pop_content { | |
| 5238 position:absolute; | |
| 5239 z-index:50; | |
| 5240 width:300px; | |
| 5241 padding: 5px; | |
| 5242 background: #E4ECEC; | |
| 5243 font-size: 12px; | |
| 5244 font-family: Arial; | |
| 5245 border-style: double; | |
| 5246 border-width: 3px; | |
| 5247 border-color: #AA2244; | |
| 5248 display:none; | |
| 5249 } | |
| 5250 | |
| 5251 div.pop_content > *:first-child { | |
| 5252 margin-top: 0px; | |
| 5253 } | |
| 5254 | |
| 5255 div.pop_content h1, div.pop_content h2, div.pop_content h3, div.pop_content h4, | |
| 5256 div.pop_content h5, div.pop_content h6, div.pop_content p { | |
| 5257 margin: 0px; | |
| 5258 } | |
| 5259 | |
| 5260 div.pop_content p + h1, div.pop_content p + h2, div.pop_content p + h3, | |
| 5261 div.pop_content p + h4, div.pop_content p + h5, div.pop_content p + h6 { | |
| 5262 margin-top: 5px; | |
| 5263 } | |
| 5264 | |
| 5265 div.pop_content p + p { | |
| 5266 margin-top: 5px; | |
| 5267 } | |
| 5268 | |
| 5269 div.pop_content > *:last-child { | |
| 5270 margin-bottom: 0px; | |
| 5271 } | |
| 5272 | |
| 5273 div.pop_content div.pop_close { | |
| 5274 /* old definition */ | |
| 5275 float:right; | |
| 5276 bottom: 0; | |
| 5277 } | |
| 5278 | |
| 5279 div.pop_content span.pop_close, div.pop_content span.pop_back { | |
| 5280 display: inline-block; | |
| 5281 border: 2px outset #661429; | |
| 5282 background-color: #CCC; | |
| 5283 padding-left: 1px; | |
| 5284 padding-right: 1px; | |
| 5285 padding-top: 0px; | |
| 5286 padding-bottom: 0px; | |
| 5287 cursor: pointer; | |
| 5288 color: #AA2244; /*#661429;*/ | |
| 5289 font-weight: bold; | |
| 5290 } | |
| 5291 | |
| 5292 div.pop_content span.pop_close:active, div.pop_content span.pop_back:active { | |
| 5293 border-style: inset; | |
| 5294 } | |
| 5295 | |
| 5296 div.pop_content span.pop_close { | |
| 5297 float:right; | |
| 5298 /*border: 2px outset #AA002B;*/ | |
| 5299 /*color: #AA2244;*/ | |
| 5300 } | |
| 5301 | |
| 5302 div.pop_content:not(.nested) .nested_only { | |
| 5303 display: none; | |
| 5304 } | |
| 5305 | |
| 5306 div.pop_back_sec { | |
| 5307 margin-bottom: 5px; | |
| 5308 } | |
| 5309 | |
| 5310 div.pop_close_sec { | |
| 5311 margin-top: 5px; | |
| 5312 } | |
| 5313 | |
| 5314 table.hide_advanced tr.advanced { | |
| 5315 display: none; | |
| 5316 } | |
| 5317 span.show_more { | |
| 5318 display: none; | |
| 5319 } | |
| 5320 table.hide_advanced span.show_more { | |
| 5321 display: inline; | |
| 5322 } | |
| 5323 table.hide_advanced span.show_less { | |
| 5324 display: none; | |
| 5325 } | |
| 5326 | |
| 5327 | |
| 5328 /***************************************************************************** | |
| 5329 * Program logo styling | |
| 5330 ****************************************************************************/ | |
| 5331 div.prog_logo { | |
| 5332 border-bottom: 0.25em solid #0f5f60; | |
| 5333 height: 4.5em; | |
| 5334 width: 24em; | |
| 5335 display:inline-block; | |
| 5336 } | |
| 5337 div.prog_logo img { | |
| 5338 float:left; | |
| 5339 width: 4em; | |
| 5340 border-style: none; | |
| 5341 margin-right: 0.2em; | |
| 5342 } | |
| 5343 div.prog_logo h1, div.prog_logo h1:hover, div.prog_logo h1:active, div.prog_logo h1:visited { | |
| 5344 margin:0; | |
| 5345 padding:0; | |
| 5346 font-family: Arial, Helvetica, sans-serif; | |
| 5347 font-size: 3.2em; | |
| 5348 line-height: 1em; | |
| 5349 vertical-align: top; | |
| 5350 display: block; | |
| 5351 color: #026666; | |
| 5352 letter-spacing: -0.06em; | |
| 5353 text-shadow: 0.04em 0.06em 0.05em #666; | |
| 5354 } | |
| 5355 div.prog_logo h2, div.prog_logo h2:hover, div.prog_logo h2:active, div.prog_logo h2:visited { | |
| 5356 display: block; | |
| 5357 margin:0; | |
| 5358 padding:0; | |
| 5359 font-family: Helvetica, sans-serif; | |
| 5360 font-size: 0.9em; | |
| 5361 line-height: 1em; | |
| 5362 letter-spacing: -0.06em; | |
| 5363 color: black; | |
| 5364 } | |
| 5365 | |
| 5366 div.big.prog_logo { | |
| 5367 font-size: 18px; | |
| 5368 } | |
| 5369 | |
| 5370 </style> | |
| 5371 <style> | |
| 5372 /* dreme output specific css */ | |
| 5373 div.header { | |
| 5374 position: relative; | |
| 5375 overflow: hidden; | |
| 5376 margin-top: 15px; | |
| 5377 margin-bottom: 5px; | |
| 5378 margin-right: 3px; | |
| 5379 margin-left: 3px; | |
| 5380 } | |
| 5381 div.header > h2 { | |
| 5382 font-size: 1.5em; | |
| 5383 font-style: normal; | |
| 5384 margin: 0; | |
| 5385 font-variant: small-caps; | |
| 5386 font-family: Georgia, "Times New Roman", Times, serif; | |
| 5387 } | |
| 5388 div.header > span { | |
| 5389 position: absolute; | |
| 5390 right: 0; | |
| 5391 bottom: 0; | |
| 5392 } | |
| 5393 | |
| 5394 div.template { | |
| 5395 position: absolute; | |
| 5396 z-index: 1; | |
| 5397 left: 0; | |
| 5398 top: 0; | |
| 5399 visibility: hidden; | |
| 5400 } | |
| 5401 | |
| 5402 div.sym_btn { | |
| 5403 display:inline-block; | |
| 5404 text-decoration: underline; | |
| 5405 cursor: pointer; | |
| 5406 font-size: 20px; | |
| 5407 line-height:20px; | |
| 5408 text-align: center; | |
| 5409 width: 20px; | |
| 5410 height: 20px; | |
| 5411 color: blue; | |
| 5412 } | |
| 5413 div.sym_btn:hover { | |
| 5414 color: white; | |
| 5415 background-color: blue; | |
| 5416 } | |
| 5417 | |
| 5418 div.sym_btn.positioned { | |
| 5419 position: absolute; | |
| 5420 top: 0px; | |
| 5421 } | |
| 5422 | |
| 5423 div.box + div.box { | |
| 5424 margin-top: 5px; | |
| 5425 } | |
| 5426 | |
| 5427 th.motif_ordinal { | |
| 5428 | |
| 5429 } | |
| 5430 td.motif_ordinal { | |
| 5431 text-align: right; | |
| 5432 padding-right: 10px; | |
| 5433 font-weight: bold; | |
| 5434 font-size: large; | |
| 5435 } | |
| 5436 th.motif_word { | |
| 5437 padding-right: 10px; | |
| 5438 } | |
| 5439 td.motif_word { | |
| 5440 font-size:15px; | |
| 5441 font-family: 'Courier New', Courier, monospace; | |
| 5442 padding-right: 10px; | |
| 5443 } | |
| 5444 th.motif_logo { | |
| 5445 padding-right: 10px; | |
| 5446 } | |
| 5447 td.motif_logo { | |
| 5448 padding-right: 10px; | |
| 5449 } | |
| 5450 th.motif_evalue { | |
| 5451 text-align:right; | |
| 5452 padding-right: 10px; | |
| 5453 } | |
| 5454 td.motif_evalue { | |
| 5455 text-align: right; | |
| 5456 white-space: nowrap; | |
| 5457 padding-right: 20px; | |
| 5458 } | |
| 5459 th.motif_more { | |
| 5460 padding: 0 5px; | |
| 5461 } | |
| 5462 td.motif_more { | |
| 5463 text-align: center; | |
| 5464 padding: 0 5px; | |
| 5465 } | |
| 5466 th.motif_submit { | |
| 5467 padding: 0 5px; | |
| 5468 } | |
| 5469 td.motif_submit { | |
| 5470 text-align: center; | |
| 5471 padding: 0 5px; | |
| 5472 } | |
| 5473 th.match_word { | |
| 5474 padding-right: 10px; | |
| 5475 } | |
| 5476 td.match_word { | |
| 5477 padding-right: 10px; | |
| 5478 font-weight: bold; | |
| 5479 font-size: large; | |
| 5480 font-family: 'Courier New', Courier, monospace; | |
| 5481 } | |
| 5482 th.match_evalue, th.match_count { | |
| 5483 text-align: right; | |
| 5484 padding-right: 10px; | |
| 5485 } | |
| 5486 td.match_evalue, td.match_count { | |
| 5487 text-align: right; | |
| 5488 white-space: nowrap; | |
| 5489 padding-right: 20px; | |
| 5490 } | |
| 5491 | |
| 5492 div.tabArea { | |
| 5493 font-size: 80%; | |
| 5494 font-weight: bold; | |
| 5495 } | |
| 5496 | |
| 5497 .norc div.tabArea { | |
| 5498 display: none; | |
| 5499 } | |
| 5500 | |
| 5501 span.tab, span.tab:visited { | |
| 5502 cursor: pointer; | |
| 5503 color: #888; | |
| 5504 background-color: #ddd; | |
| 5505 border: 2px solid #ccc; | |
| 5506 padding: 2px 1em; | |
| 5507 text-decoration: none; | |
| 5508 } | |
| 5509 span.tab.middle { | |
| 5510 border-left-width: 0px; | |
| 5511 } | |
| 5512 div.tabArea.base span.tab { | |
| 5513 border-top-width: 0px; | |
| 5514 } | |
| 5515 div.tabArea.top span.tab { | |
| 5516 border-bottom-width: 0px; | |
| 5517 } | |
| 5518 | |
| 5519 span.tab:hover { | |
| 5520 background-color: #bbb; | |
| 5521 border-color: #bbb; | |
| 5522 color: #666; | |
| 5523 } | |
| 5524 span.tab.activeTab, span.tab.activeTab:hover, span.tab.activeTab:visited { | |
| 5525 background-color: white; | |
| 5526 color: black; | |
| 5527 cursor: default; | |
| 5528 } | |
| 5529 div.tabMain { | |
| 5530 border: 2px solid #ccc; | |
| 5531 background-color: white; | |
| 5532 padding: 10px; | |
| 5533 } | |
| 5534 div.tabMain.base { | |
| 5535 margin-top: 5px; | |
| 5536 display: inline-block; | |
| 5537 max-width: 98%; | |
| 5538 } | |
| 5539 | |
| 5540 div.tabMain.top { | |
| 5541 margin-bottom: 5px; | |
| 5542 } | |
| 5543 | |
| 5544 div.grey_background { | |
| 5545 position:fixed; | |
| 5546 z-index: 8; | |
| 5547 background-color: #000; | |
| 5548 -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; | |
| 5549 opacity: 0.5; | |
| 5550 left: 0; | |
| 5551 top: 0; | |
| 5552 width: 100%; | |
| 5553 height: 100%; | |
| 5554 } | |
| 5555 | |
| 5556 div.popup_wrapper { | |
| 5557 position:fixed; | |
| 5558 z-index:9; | |
| 5559 width:100%; | |
| 5560 height:0; | |
| 5561 top:50%; | |
| 5562 left:0; | |
| 5563 } | |
| 5564 | |
| 5565 div.popup { | |
| 5566 width: 600px; | |
| 5567 z-index:9; | |
| 5568 margin-left: auto; | |
| 5569 margin-right: auto; | |
| 5570 padding: 5px; | |
| 5571 background-color: #FFF; | |
| 5572 border-style: double; | |
| 5573 border-width: 5px; | |
| 5574 border-color: #00666a; | |
| 5575 position:relative; | |
| 5576 } | |
| 5577 div.close { | |
| 5578 cursor: pointer; | |
| 5579 border: 1px solid black; | |
| 5580 width:15px; | |
| 5581 height:15px; | |
| 5582 line-height:15px; /* this causes vertical centering */ | |
| 5583 text-align:center; | |
| 5584 background-color:#FFF; | |
| 5585 color:#000; | |
| 5586 font-size:15px; | |
| 5587 font-family:monospace; | |
| 5588 } | |
| 5589 | |
| 5590 div.close:hover { | |
| 5591 color:#FFF; | |
| 5592 background-color:#000; | |
| 5593 } | |
| 5594 | |
| 5595 div.navnum { | |
| 5596 width:100%; | |
| 5597 height:20px; | |
| 5598 line-height:20px; | |
| 5599 text-align:center; | |
| 5600 font-size:medium; | |
| 5601 } | |
| 5602 | |
| 5603 div.navarrow { | |
| 5604 font-size: 30px; | |
| 5605 text-decoration:none; | |
| 5606 cursor: pointer; | |
| 5607 -moz-user-select: none; | |
| 5608 -webkit-user-select: none; | |
| 5609 -ms-user-select: none; | |
| 5610 } | |
| 5611 | |
| 5612 div.navarrow > span.inactive { | |
| 5613 display: inline; | |
| 5614 } | |
| 5615 div.navarrow > span.active { | |
| 5616 display: none; | |
| 5617 } | |
| 5618 | |
| 5619 div.navarrow:hover > span.active { | |
| 5620 display: inline; | |
| 5621 } | |
| 5622 div.navarrow:hover > span.inactive { | |
| 5623 display: none; | |
| 5624 } | |
| 5625 | |
| 5626 table.programs { | |
| 5627 width: 100%; | |
| 5628 } | |
| 5629 | |
| 5630 table.programs tr { | |
| 5631 background-color: #EFE; | |
| 5632 } | |
| 5633 | |
| 5634 table.programs tr.selected { | |
| 5635 background-color: #262; | |
| 5636 color: #FFF; | |
| 5637 } | |
| 5638 | |
| 5639 table.programs tr.dna_only { | |
| 5640 display: none; | |
| 5641 } | |
| 5642 | |
| 5643 table.programs.alphabet_dna tr.dna_only { | |
| 5644 display: table-row; | |
| 5645 } | |
| 5646 | |
| 5647 table.inputs { | |
| 5648 margin-top: 20px; | |
| 5649 border-collapse:collapse; | |
| 5650 } | |
| 5651 table.inputs * td, table.inputs * th { | |
| 5652 padding-left: 15px; | |
| 5653 padding-right: 15px; | |
| 5654 padding-top: 1px; | |
| 5655 padding-bottom: 1px; | |
| 5656 } | |
| 5657 | |
| 5658 /* program settings */ | |
| 5659 span.strand_none, span.strand_given, span.strand_both { | |
| 5660 display: none; | |
| 5661 } | |
| 5662 td.none span.strand_none, td.given span.strand_given, td.both span.strand_both { | |
| 5663 display: inline; | |
| 5664 } | |
| 5665 | |
| 5666 /* show the expanded motif only when the collapsed one is hidden */ | |
| 5667 tbody *.less, tbody.collapsed *.more { | |
| 5668 display: none; | |
| 5669 } | |
| 5670 | |
| 5671 tbody.collapsed *.less { | |
| 5672 display: inline; | |
| 5673 } | |
| 5674 | |
| 5675 </style> | |
| 5676 </head> | |
| 5677 <body data-scrollpad="true"> | |
| 5678 <!-- --> | |
| 5679 <div id="grey_out_page" class="grey_background" style="display:none;"> | |
| 5680 </div> | |
| 5681 | |
| 5682 <!-- Help popups --> | |
| 5683 <div class="pop_content" id="pop_"> | |
| 5684 <p>Help poup.</p> | |
| 5685 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5686 </div> | |
| 5687 | |
| 5688 <div class="pop_content" id="pop_motifs_word"> | |
| 5689 <p> | |
| 5690 The name of the motif uses the IUPAC codes for nucleotides which has | |
| 5691 a different letter to represent each of the 15 possible combinations. | |
| 5692 </p> | |
| 5693 <p> | |
| 5694 The name is itself a representation of the motif though the position | |
| 5695 weight matrix is not directly equalivant as it is generated from the | |
| 5696 sites found that matched the letters given in the name. | |
| 5697 </p> | |
| 5698 <p> | |
| 5699 <a id="doc_alphabets_url" href="#"> | |
| 5700 Read more about the MEME suite's use of the IUPAC alphabets. | |
| 5701 </a> | |
| 5702 <script>$("doc_alphabets_url").href = site_url + "/doc/alphabets.html";</script> | |
| 5703 </p> | |
| 5704 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5705 </div> | |
| 5706 <div class="pop_content" id="pop_motifs_logo"> | |
| 5707 <p>The logo of the motif.</p> | |
| 5708 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5709 </div> | |
| 5710 <div class="pop_content" id="pop_motifs_rc_logo"> | |
| 5711 <p>The logo of the reverse complement motif.</p> | |
| 5712 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5713 </div> | |
| 5714 <div class="pop_content" id="pop_motifs_evalue"> | |
| 5715 <p>The E-value is the enrichment p-value times the number of candidate | |
| 5716 motifs tested.</p> | |
| 5717 <p>The enrichment p-value is calculated using Fisher's Exact Test for | |
| 5718 enrichment of the motif in the positive sequences.</p> | |
| 5719 <p>Note that the counts used in Fisher's Exact Test are made after | |
| 5720 erasing sites that match previously found motifs.</p> | |
| 5721 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5722 </div> | |
| 5723 <div class="pop_content" id="pop_motifs_uevalue"> | |
| 5724 <p>The E-value of the motif calculated without erasing the sites of | |
| 5725 previously found motifs.</p> | |
| 5726 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5727 </div> | |
| 5728 <div class="pop_content" id="pop_more"> | |
| 5729 <p>Show more information on the motif.</p> | |
| 5730 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5731 </div> | |
| 5732 <div class="pop_content" id="pop_submit_dl"> | |
| 5733 <p>Submit your motif to another MEME Suite program or download your motif.</p> | |
| 5734 <h5>Supported Programs</h5> | |
| 5735 <dl> | |
| 5736 <dt>Tomtom</dt> | |
| 5737 <dd>Tomtom is a tool for searching for similar known motifs. | |
| 5738 </dd> | |
| 5739 <dt>MAST</dt> | |
| 5740 <dd>MAST is a tool for searching biological sequence databases for | |
| 5741 sequences that contain one or more of a group of known motifs. | |
| 5742 </dd> | |
| 5743 <dt>FIMO</dt> | |
| 5744 <dd>FIMO is a tool for searching biological sequence databases for | |
| 5745 sequences that contain one or more known motifs. | |
| 5746 </dd> | |
| 5747 <dt>GOMO</dt> | |
| 5748 <dd>GOMO is a tool for identifying possible roles (Gene Ontology | |
| 5749 terms) for DNA binding motifs. | |
| 5750 </dd> | |
| 5751 <dt>SpaMo</dt> | |
| 5752 <dd>SpaMo is a tool for inferring possible transcription factor | |
| 5753 complexes by finding motifs with enriched spacings. | |
| 5754 </dd> | |
| 5755 </dl> | |
| 5756 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5757 </div> | |
| 5758 <div class="pop_content" id="pop_motif_positives"> | |
| 5759 <p># positive sequences matching the motif / # positive sequences.</p> | |
| 5760 <p>Note these counts are made after erasing sites that match previously | |
| 5761 found motifs.</p> | |
| 5762 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5763 </div> | |
| 5764 <div class="pop_content" id="pop_motif_negatives"> | |
| 5765 <p># negative sequences matching the motif / # negative sequences.</p> | |
| 5766 <p>Note these counts are made after erasing sites that match previously | |
| 5767 found motifs.</p> | |
| 5768 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5769 </div> | |
| 5770 <div class="pop_content" id="pop_motif_pvalue"> | |
| 5771 <p>The p-value of Fisher's Exact Test for enrichment of the motif in | |
| 5772 the positive sequences.</p> | |
| 5773 <p>Note that the counts used in Fisher's Exact Test are made after | |
| 5774 erasing sites that match previously found motifs.</p> | |
| 5775 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5776 </div> | |
| 5777 <div class="pop_content" id="pop_motif_evalue"> | |
| 5778 <p>The E-value is the motif p-value times the number of candidate motifs | |
| 5779 tested.</p> | |
| 5780 <p>Note that the p-value was calculated with counts made after | |
| 5781 erasing sites that match previously found motifs.</p> | |
| 5782 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5783 </div> | |
| 5784 <div class="pop_content" id="pop_motif_uevalue"> | |
| 5785 <p>The E-value of the motif calculated without erasing the sites of | |
| 5786 previously found motifs.</p> | |
| 5787 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5788 </div> | |
| 5789 <div class="pop_content" id="pop_match_word"> | |
| 5790 <p>All words matching the motif whose uncorrected p-value is less than | |
| 5791 <span id="help_add_pv_thresh"></span>.</p> | |
| 5792 <script>$("help_add_pv_thresh").innerHTML = data.options.add_pv_thresh;</script> | |
| 5793 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5794 </div> | |
| 5795 <div class="pop_content" id="pop_match_pos"> | |
| 5796 <p># positive sequences with matches to the word / # positive sequences.</p> | |
| 5797 <p>Note these counts are made after erasing sites that match previously | |
| 5798 found motifs.</p> | |
| 5799 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5800 </div> | |
| 5801 <div class="pop_content" id="pop_match_neg"> | |
| 5802 <p># negative sequences with matches to the word / # negative sequences.</p> | |
| 5803 <p>Note these counts are made after erasing sites that match previously | |
| 5804 found motifs.</p> | |
| 5805 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5806 </div> | |
| 5807 <div class="pop_content" id="pop_match_pval"> | |
| 5808 <p>The p-value of Fisher's Exact Test for enrichment of the word in | |
| 5809 the positive sequences.</p> | |
| 5810 <p>Note that the counts used in Fisher's Exact Test are made after | |
| 5811 erasing sites that match previously found motifs.</p> | |
| 5812 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5813 </div> | |
| 5814 <div class="pop_content" id="pop_match_eval"> | |
| 5815 <p>The word p-value times the number of candidates tested.</p> | |
| 5816 <p>Note that the p-value was calculated with counts made after | |
| 5817 erasing sites that match previously found motifs.</p> | |
| 5818 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5819 </div> | |
| 5820 | |
| 5821 <div class="pop_content" id="pop_seq_source"> | |
| 5822 <p>The sequence file used by DREME to find the motifs.</p> | |
| 5823 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5824 </div> | |
| 5825 <div class="pop_content" id="pop_seq_alph"> | |
| 5826 <p>The alphabet of the sequences.</p> | |
| 5827 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5828 </div> | |
| 5829 <div class="pop_content" id="pop_seq_count"> | |
| 5830 <p>The count of the sequences.</p> | |
| 5831 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5832 </div> | |
| 5833 | |
| 5834 <div class="pop_content" id="pop_alph_name"> | |
| 5835 <p>The name of the alphabet symbol.</p> | |
| 5836 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5837 </div> | |
| 5838 | |
| 5839 <div class="pop_content" id="pop_alph_control"> | |
| 5840 <p>The frequency of the alphabet symbol in the control dataset.</p> | |
| 5841 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 5842 </div> | |
| 5843 | |
| 5844 <!-- templates --> | |
| 5845 | |
| 5846 <div class="template box expanded_motif" id="tmpl_motif_expanded"> | |
| 5847 <div> | |
| 5848 <span class="tvar_logo"></span> | |
| 5849 <span class="tvar_rclogo"></span> | |
| 5850 </div> | |
| 5851 <h4>Details</h4> | |
| 5852 <table class="details"> | |
| 5853 <thead> | |
| 5854 <tr> | |
| 5855 <th class="match_count">Positives <div class="help" data-topic="pop_motif_positives"></div></th> | |
| 5856 <th class="match_count">Negatives <div class="help" data-topic="pop_motif_negatives"></div></th> | |
| 5857 <th class="match_evalue">P-value <div class="help" data-topic="pop_motif_pvalue"></div></th> | |
| 5858 <th class="match_evalue">E-value <div class="help" data-topic="pop_motif_evalue"></div></th> | |
| 5859 <th class="match_evalue">Unerased E-value <div class="help" data-topic="pop_motif_uevalue"></div></th> | |
| 5860 </tr> | |
| 5861 </thead> | |
| 5862 <tbody> | |
| 5863 <tr> | |
| 5864 <td class="match_count"> | |
| 5865 <span class="tvar_p"></span> / <span class="tvar_p_total"></span> | |
| 5866 </td> | |
| 5867 <td class="match_count"> | |
| 5868 <span class="tvar_n"></span> / <span class="tvar_n_total"></span> | |
| 5869 </td> | |
| 5870 <td class="tvar_pvalue match_evalue"></td> | |
| 5871 <td class="tvar_evalue match_evalue"></td> | |
| 5872 <td class="tvar_uevalue match_evalue"></td> | |
| 5873 </tr> | |
| 5874 </tbody> | |
| 5875 </table> | |
| 5876 <h4>Enriched Matching Words</h4> | |
| 5877 <div class="tvar_words"></div> | |
| 5878 </div> | |
| 5879 | |
| 5880 | |
| 5881 <div class="popup_wrapper"> | |
| 5882 <div class="popup" style="display:none; top: -150px;" id="download"> | |
| 5883 <div> | |
| 5884 <div style="float:right; "> | |
| 5885 <div id="outpop_close" class="close" tabindex="0">x</div> | |
| 5886 </div> | |
| 5887 <h2 class="mainh" style="margin:0; padding:0;">Submit or Download</h2> | |
| 5888 <div style="clear:both"></div> | |
| 5889 </div> | |
| 5890 <div style="height:100px"> | |
| 5891 <div style="float:right; width: 30px;"> | |
| 5892 <div id="outpop_prev" class="navarrow" tabindex="0"> | |
| 5893 <span class="inactive">⇧</span><span class="active">⬆</span> | |
| 5894 </div> | |
| 5895 <div id="outpop_num" class="navnum"></div> | |
| 5896 <div id="outpop_next" class="navarrow" tabindex="0"> | |
| 5897 <span class="inactive">⇩</span><span class="active">⬇</span> | |
| 5898 </div> | |
| 5899 </div> | |
| 5900 <div id="logo_box" style="height: 100px; margin-right: 40px;"> | |
| 5901 <canvas id="outpop_logo" height="100" width="250"></canvas> | |
| 5902 <canvas id="outpop_logo_rc" height="100" width="250"></canvas> | |
| 5903 </div> | |
| 5904 </div> | |
| 5905 <div> | |
| 5906 <!-- tabs start --> | |
| 5907 <div class="tabArea top"> | |
| 5908 <span id="outpop_tab_1" class="tab">Submit Motif</span><span | |
| 5909 id="outpop_tab_2" class="tab middle">Download Motif</span><span | |
| 5910 id="outpop_tab_3" class="tab middle">Download Logo</span> | |
| 5911 </div> | |
| 5912 <div class="tabMain top"> | |
| 5913 <!-- Submit to another program --> | |
| 5914 <div id="outpop_pnl_1"> | |
| 5915 <h4 class="compact">Submit to program</h4> | |
| 5916 <table id="programs" class="programs"> | |
| 5917 <tr class="dna_only"> | |
| 5918 <td><input type="radio" name="program" value="tomtom" id="submit_tomtom"></td> | |
| 5919 <td><label for="submit_tomtom">Tomtom</label></td> | |
| 5920 <td><label for="submit_tomtom">Find similar motifs in | |
| 5921 published libraries or a library you supply.</label></td> | |
| 5922 </tr> | |
| 5923 <tr> | |
| 5924 <td><input type="radio" name="program" value="fimo" id="submit_fimo"></td> | |
| 5925 <td><label for="submit_fimo">FIMO</label></td> | |
| 5926 <td><label for="submit_fimo">Find motif occurrences in | |
| 5927 sequence data.</label></td> | |
| 5928 </tr> | |
| 5929 <tr> | |
| 5930 <td><input type="radio" name="program" value="mast" id="submit_mast"></td> | |
| 5931 <td><label for="submit_mast">MAST</label></td> | |
| 5932 <td><label for="submit_mast">Rank sequences by affinity to | |
| 5933 groups of motifs.</label></td> | |
| 5934 </tr> | |
| 5935 <tr class="dna_only"> | |
| 5936 <td><input type="radio" name="program" value="gomo" id="submit_gomo"></td> | |
| 5937 <td><label for="submit_gomo">GOMo</label></td> | |
| 5938 <td><label for="submit_gomo">Identify possible roles (Gene | |
| 5939 Ontology terms) for motifs.</label></td> | |
| 5940 </tr> | |
| 5941 <tr class="dna_only"> | |
| 5942 <td><input type="radio" name="program" value="spamo" id="submit_spamo"></td> | |
| 5943 <td><label for="submit_spamo">SpaMo</label></td> | |
| 5944 <td><label for="submit_spamo">Find other motifs that are | |
| 5945 enriched at specific close spacings which might imply the existance of a complex.</label></td> | |
| 5946 </tr> | |
| 5947 </table> | |
| 5948 </div> | |
| 5949 <!-- download text format --> | |
| 5950 <div id="outpop_pnl_2"> | |
| 5951 <div> | |
| 5952 <label for="text_format">Format:</label> | |
| 5953 <select id="text_format"> | |
| 5954 <option value="0">Count Matrix</option> | |
| 5955 <option value="1">Probability Matrix</option> | |
| 5956 <option value="2">Minimal MEME</option> | |
| 5957 </select> | |
| 5958 </div> | |
| 5959 <textarea id="outpop_text" name="content" | |
| 5960 style="width:99%; white-space: pre; word-wrap: normal; overflow-x: scroll;" | |
| 5961 rows="8" readonly="readonly" wrap="off"></textarea> | |
| 5962 <a id="outpop_text_dl" download="meme.txt" href=""></a> | |
| 5963 </div> | |
| 5964 <!-- download logo format --> | |
| 5965 <div id="outpop_pnl_3"> | |
| 5966 <form id="logo_form" method="post" action=""> | |
| 5967 <script>$("logo_form").action = site_url + "/utilities/generate_logo";</script> | |
| 5968 <input type="hidden" name="program" value="DREME"/> | |
| 5969 <input type="hidden" id="logo_motifs" name="motifs" value=""/> | |
| 5970 <table> | |
| 5971 <tr> | |
| 5972 <td><label for="logo_format">Format:</label></td> | |
| 5973 <td> | |
| 5974 <select id="logo_format" name="png"> | |
| 5975 <option value="1">PNG (for web)</option> | |
| 5976 <option value="0">EPS (for publication)</option> | |
| 5977 </select> | |
| 5978 </td> | |
| 5979 </tr> | |
| 5980 <tr> | |
| 5981 <td><label for="logo_rc">Orientation:</label></td> | |
| 5982 <td> | |
| 5983 <select id="logo_rc" name="rc1"> | |
| 5984 <option value="0">Normal</option> | |
| 5985 <option value="1" id="logo_rc_option">Reverse Complement</option> | |
| 5986 </select> | |
| 5987 </td> | |
| 5988 </tr> | |
| 5989 <tr> | |
| 5990 <td><label for="logo_ssc">Small Sample Correction:</label></td> | |
| 5991 <td> | |
| 5992 <input type="hidden" id="logo_err" name="errbars" value="0"/> | |
| 5993 <select id="logo_ssc" name="ssc"> | |
| 5994 <option value="0">Off</option> | |
| 5995 <option value="1">On</option> | |
| 5996 </select> | |
| 5997 </td> | |
| 5998 </tr> | |
| 5999 <tr> | |
| 6000 <td><label for="logo_width">Width:</label></td> | |
| 6001 <td> | |
| 6002 <input type="text" id="logo_width" size="4" placeholder="default" name="width"/> cm | |
| 6003 </td> | |
| 6004 </tr> | |
| 6005 <tr> | |
| 6006 <td><label for="logo_height">Height:</label></td> | |
| 6007 <td> | |
| 6008 <input type="text" id="logo_height" size="4" placeholder="default" name="height"/> cm | |
| 6009 </td> | |
| 6010 </tr> | |
| 6011 </table> | |
| 6012 </form> | |
| 6013 </div> | |
| 6014 <!-- Buttons --> | |
| 6015 <div> | |
| 6016 <div style="float:left;"> | |
| 6017 <input type="button" id="outpop_do" value="Submit" /> | |
| 6018 </div> | |
| 6019 <div style="float:right;"> | |
| 6020 <input id="outpop_cancel" type="button" value="Cancel" /> | |
| 6021 </div> | |
| 6022 <div style="clear:both;"></div> | |
| 6023 </div> | |
| 6024 </div> | |
| 6025 </div> | |
| 6026 </div> | |
| 6027 </div> | |
| 6028 | |
| 6029 | |
| 6030 | |
| 6031 <!-- Page starts here --> | |
| 6032 <div id="top" class="pad1"> | |
| 6033 <div class="prog_logo big"> | |
| 6034 <img src="data:image/png;base64,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" alt="DREME Logo"/> | |
| 6035 <h1>DREME</h1> | |
| 6036 <h2>Discriminative Regular Expression Motif Elicitation</h2> | |
| 6037 </div> | |
| 6038 <p class="spaced"> | |
| 6039 For further information on how to interpret these results or to get a | |
| 6040 copy of the MEME software please access | |
| 6041 <a href="http://meme.nbcr.net/">http://meme.nbcr.net</a>. | |
| 6042 </p> | |
| 6043 <p> | |
| 6044 If you use DREME in your research please cite the following paper:<br /> | |
| 6045 <span class="citation"> | |
| 6046 Timothy L. Bailey, "DREME: Motif discovery in transcription factor ChIP-seq data", <i>Bioinformatics</i>, <b>27</b>(12):1653-1659, 2011. | |
| 6047 <a href="http://bioinformatics.oxfordjournals.org/content/27/12/1653">[full text]</a> | |
| 6048 </span> | |
| 6049 </p> | |
| 6050 </div> | |
| 6051 <!-- navigation --> | |
| 6052 <div class="pad2"> | |
| 6053 <a class="jump" href="#motifs_sec">Discovered Motifs</a> | |
| 6054 | | |
| 6055 <a class="jump" href="#inputs_sec">Inputs & Settings</a> | |
| 6056 | | |
| 6057 <a class="jump" href="#info_sec">Program information</a> | |
| 6058 </div> | |
| 6059 <!-- alert the user when their browser is not up to the task --> | |
| 6060 <noscript><h1 style="color:red">Javascript is required to view these results!</h1></noscript> | |
| 6061 <h1 id="html5_warning" style="color:red; display:none;">Your browser does not support canvas!</h1> | |
| 6062 <script> | |
| 6063 if (!window.HTMLCanvasElement) $("html5_warning").style.display = "block"; | |
| 6064 </script> | |
| 6065 <!-- description --> | |
| 6066 <div id="description_section" style="display:none"> | |
| 6067 <div id="description" class="header"> | |
| 6068 <h2>Description</h2> | |
| 6069 </div> | |
| 6070 <div id="description_text" class="box"> | |
| 6071 </div> | |
| 6072 </div> | |
| 6073 <script> | |
| 6074 if (data.description) { | |
| 6075 $("description_text").innerHTML = ""; | |
| 6076 $("description_text").appendChild(make_description(data.description)); | |
| 6077 $("description_section").style.display = "block"; | |
| 6078 } | |
| 6079 </script> | |
| 6080 <!-- motifs --> | |
| 6081 <div id="motifs_sec" class="header"> | |
| 6082 <h2>Discovered Motifs</h2> | |
| 6083 <span><a href="#inputs_sec">Next</a> <a href="#">Top</a></span> | |
| 6084 </div> | |
| 6085 <div id="motifs" class="box"> | |
| 6086 <p>No motifs were discovered!</p> | |
| 6087 </div> | |
| 6088 <script>make_motifs();</script> | |
| 6089 <!-- inputs and settings --> | |
| 6090 <div id="inputs_sec" class="header"> | |
| 6091 <h2>Inputs & Settings</h2> | |
| 6092 <span><a href="#motifs_sec">Previous</a> <a href="#info_sec">Next</a> <a href="#">Top</a></span> | |
| 6093 </div> | |
| 6094 <div class="box"> | |
| 6095 <h4>Sequences</h4> | |
| 6096 <table id="seq_info" class="inputs"> | |
| 6097 <tr><th>Source <div class="help" data-topic="pop_seq_source"></div></th> | |
| 6098 <th>Alphabet <div class="help" data-topic="pop_seq_alph"></div></th> | |
| 6099 <th>Sequence Count <div class="help" data-topic="pop_seq_count"></div></th> | |
| 6100 </tr> | |
| 6101 <tr> | |
| 6102 <td id="ins_seq_source"></td> | |
| 6103 <td id="ins_seq_alphabet"></td> | |
| 6104 <td id="ins_seq_count"></td> | |
| 6105 </tr> | |
| 6106 </table> | |
| 6107 <script> | |
| 6108 { | |
| 6109 var db = data.sequence_db; | |
| 6110 $("ins_seq_source").innerHTML = db.file; | |
| 6111 $("ins_seq_alphabet").innerHTML = dreme_alphabet.get_alphabet_name(); | |
| 6112 $("ins_seq_count").innerHTML = db.count; | |
| 6113 } | |
| 6114 </script> | |
| 6115 <h4>Control Sequences</h4> | |
| 6116 <table id="seq_info" class="inputs"> | |
| 6117 <tr><th>Source <div class="help" data-topic="pop_seq_source"></div></th> | |
| 6118 <th>Sequence Count <div class="help" data-topic="pop_seq_count"></div></th> | |
| 6119 </tr> | |
| 6120 <tr> | |
| 6121 <td id="ins_cseq_source"></td> | |
| 6122 <td id="ins_cseq_count"></td> | |
| 6123 </tr> | |
| 6124 </table> | |
| 6125 <script> | |
| 6126 { | |
| 6127 var db = data.control_db; | |
| 6128 if (db.from == "shuffled") { | |
| 6129 $("ins_cseq_source").innerHTML = "Shuffled Sequences"; | |
| 6130 } else { | |
| 6131 $("ins_cseq_source").innerHTML = db.file; | |
| 6132 } | |
| 6133 $("ins_cseq_count").innerHTML = db.count; | |
| 6134 } | |
| 6135 </script> | |
| 6136 <h4>Background</h4> | |
| 6137 <span id="alpha_bg"></span> | |
| 6138 <script> | |
| 6139 { | |
| 6140 $("alpha_bg").appendChild(make_alpha_bg(dreme_alphabet, data.control_db.freqs)); | |
| 6141 } | |
| 6142 </script> | |
| 6143 <h4>Other Settings</h4> | |
| 6144 <table id="tbl_settings" class="inputs hide_advanced"> | |
| 6145 <tr> | |
| 6146 <th>Strand Handling</th> | |
| 6147 <td id="opt_strand"> | |
| 6148 <span class="strand_none">This alphabet only has one strand</span> | |
| 6149 <span class="strand_given">Only the given strand is processed</span> | |
| 6150 <span class="strand_both">Both the given and reverse complement strands are processed</span> | |
| 6151 </td> | |
| 6152 </tr> | |
| 6153 <tr><th># REs to Generalize</th><td id="opt_ngen"></td></tr> | |
| 6154 <tr><th>Shuffle Seed</th><td id="opt_seed"></td></tr> | |
| 6155 <tr><th>E-value Threshold</th><td id="opt_stop_evalue"></td></tr> | |
| 6156 <tr><th>Max Motif Count</th><td id="opt_stop_count"></td></tr> | |
| 6157 <tr><th>Max Run Time</th><td id="opt_stop_time"></td></tr> | |
| 6158 </table> | |
| 6159 <script> | |
| 6160 { | |
| 6161 $("opt_strand").className = (dreme_alphabet.has_complement() ? (data.options.revcomp ? "both" : "given") : "none"); | |
| 6162 $("opt_ngen").innerHTML = data.options.ngen; | |
| 6163 $("opt_seed").innerHTML = data.options.seed; | |
| 6164 $("opt_stop_evalue").innerHTML = data.options.stop.evalue; | |
| 6165 $("opt_stop_count").innerHTML = (typeof data.options.stop.count == "number" ? data.options.stop.count : "No maximum motif count."); | |
| 6166 $("opt_stop_time").innerHTML = (typeof data.options.stop.time == "number" ? data.options.stop.time + " seconds." : "No maximum running time."); | |
| 6167 } | |
| 6168 </script> | |
| 6169 </div> | |
| 6170 <!-- list information on this program --> | |
| 6171 <div id="info_sec" class="bar" style="position:relative"> | |
| 6172 <div style="position: absolute; right: 0;"><a href="#inputs_sec">Previous</a> <a href="#">Top</a></div> | |
| 6173 <div class="subsection"> | |
| 6174 <h5 id="version">DREME version</h5> | |
| 6175 <span id="ins_version"></span> | |
| 6176 (Release date: <span id="ins_release"></span>)<br> | |
| 6177 </div> | |
| 6178 <script> | |
| 6179 $("ins_version").innerHTML = data["version"]; | |
| 6180 $("ins_release").innerHTML = data["release"]; | |
| 6181 </script> | |
| 6182 <div class="subsection"> | |
| 6183 <h5 id="reference">Reference</h5> | |
| 6184 <span class="citation"> | |
| 6185 Timothy L. Bailey, "DREME: Motif discovery in transcription factor ChIP-seq data", <i>Bioinformatics</i>, <b>27</b>(12):1653-1659, 2011. | |
| 6186 <a href="http://bioinformatics.oxfordjournals.org/content/27/12/1653">[full text]</a> | |
| 6187 </span> | |
| 6188 </div> | |
| 6189 <div class="subsection"> | |
| 6190 <h5 id="command">Command line</h5> | |
| 6191 <textarea id="cmd" rows="3" style="width:100%;" readonly="readonly"> | |
| 6192 </textarea> | |
| 6193 <script>$("cmd").value = data["cmd"].join(" ");</script> | |
| 6194 </div> | |
| 6195 </div> | |
| 6196 | |
| 6197 </body> | |
| 6198 </html> |
