Mercurial > repos > iuc > meme_psp_gen
comparison test-data/meme_output_test2.html @ 8:8436beac5eed draft default tip
planemo upload for repository https://github.com/galaxyproject/tools-iuc/tree/master/tools/meme commit 89ee0af6e955ff964b7984d77ad536e0a9154278
| author | iuc |
|---|---|
| date | Mon, 14 Jul 2025 21:32:53 +0000 |
| parents | 5f95d385a33c |
| children |
comparison
equal
deleted
inserted
replaced
| 7:47e7ae719765 | 8:8436beac5eed |
|---|---|
| 1 * See http://stackoverflow.com/a/5450113/66387 | |
| 2 * Does string multiplication like the perl x operator. | |
| 3 */ | |
| 4 function string_mult(pattern, count) { | |
| 5 if (count < 1) return ''; | |
| 6 var result = ''; | |
| 7 while (count > 1) { | |
| 8 if (count & 1) result += pattern; | |
| 9 count >>= 1, pattern += pattern; | |
| 10 } | |
| 11 return result + pattern; | |
| 12 } | |
| 13 | |
| 14 /* | |
| 15 * See http://stackoverflow.com/questions/814613/how-to-read-get-data-from-a-url-using-javascript | |
| 16 * Slightly modified with information from | |
| 17 * https://developer.mozilla.org/en/DOM/window.location | |
| 18 */ | |
| 19 function parse_params() { | |
| 20 "use strict"; | |
| 21 var search, queryStart, queryEnd, query, params, nvPairs, i, nv, n, v; | |
| 22 search = window.location.search; | |
| 23 queryStart = search.indexOf("?") + 1; | |
| 24 queryEnd = search.indexOf("#") + 1 || search.length + 1; | |
| 25 query = search.slice(queryStart, queryEnd - 1); | |
| 26 | |
| 27 if (query === search || query === "") return {}; | |
| 28 | |
| 29 params = {}; | |
| 30 nvPairs = query.replace(/\+/g, " ").split("&"); | |
| 31 | |
| 32 for (i = 0; i < nvPairs.length; i++) { | |
| 33 nv = nvPairs[i].split("="); | |
| 34 n = decodeURIComponent(nv[0]); | |
| 35 v = decodeURIComponent(nv[1]); | |
| 36 // allow a name to be used multiple times | |
| 37 // storing each value in the array | |
| 38 if (!(n in params)) { | |
| 39 params[n] = []; | |
| 40 } | |
| 41 params[n].push(nv.length === 2 ? v : null); | |
| 42 } | |
| 43 return params; | |
| 44 } | |
| 45 | |
| 46 /* | |
| 47 * coords | |
| 48 * | |
| 49 * Calculates the x and y offset of an element. | |
| 50 * From http://www.quirksmode.org/js/findpos.html | |
| 51 * with alterations to take into account scrolling regions | |
| 52 */ | |
| 53 function coords(elem) { | |
| 54 var myX = myY = 0; | |
| 55 if (elem.getBoundingClientRect) { | |
| 56 var rect; | |
| 57 rect = elem.getBoundingClientRect(); | |
| 58 myX = rect.left + ((typeof window.pageXOffset !== "undefined") ? | |
| 59 window.pageXOffset : document.body.scrollLeft); | |
| 60 myY = rect.top + ((typeof window.pageYOffset !== "undefined") ? | |
| 61 window.pageYOffset : document.body.scrollTop); | |
| 62 } else { | |
| 63 // this fall back doesn't properly handle absolutely positioned elements | |
| 64 // inside a scrollable box | |
| 65 var node; | |
| 66 if (elem.offsetParent) { | |
| 67 // subtract all scrolling | |
| 68 node = elem; | |
| 69 do { | |
| 70 myX -= node.scrollLeft ? node.scrollLeft : 0; | |
| 71 myY -= node.scrollTop ? node.scrollTop : 0; | |
| 72 } while (node = node.parentNode); | |
| 73 // this will include the page scrolling (which is unwanted) so add it back on | |
| 74 myX += (typeof window.pageXOffset !== "undefined") ? window.pageXOffset : document.body.scrollLeft; | |
| 75 myY += (typeof window.pageYOffset !== "undefined") ? window.pageYOffset : document.body.scrollTop; | |
| 76 // sum up offsets | |
| 77 node = elem; | |
| 78 do { | |
| 79 myX += node.offsetLeft; | |
| 80 myY += node.offsetTop; | |
| 81 } while (node = node.offsetParent); | |
| 82 } | |
| 83 } | |
| 84 return [myX, myY]; | |
| 85 } | |
| 86 | |
| 87 /* | |
| 88 * position_popup | |
| 89 * | |
| 90 * Positions a popup relative to an anchor element. | |
| 91 * | |
| 92 * The available positions are: | |
| 93 * 0 - Centered below the anchor. | |
| 94 */ | |
| 95 function position_popup(anchor, popup, position) { | |
| 96 "use strict"; | |
| 97 var a_x, a_y, a_w, a_h, p_x, p_y, p_w, p_h; | |
| 98 var a_xy, spacer, margin, scrollbar, page_w; | |
| 99 // define constants | |
| 100 spacer = 5; | |
| 101 margin = 15; | |
| 102 scrollbar = 15; | |
| 103 // define the positions and widths | |
| 104 a_xy = coords(anchor); | |
| 105 a_x = a_xy[0]; | |
| 106 a_y = a_xy[1]; | |
| 107 a_w = anchor.offsetWidth; | |
| 108 a_h = anchor.offsetHeight; | |
| 109 p_w = popup.offsetWidth; | |
| 110 p_h = popup.offsetHeight; | |
| 111 page_w = null; | |
| 112 if (window.innerWidth) { | |
| 113 page_w = window.innerWidth; | |
| 114 } else if (document.body) { | |
| 115 page_w = document.body.clientWidth; | |
| 116 } | |
| 117 // check the position type is defined | |
| 118 if (typeof position !== "number") { | |
| 119 position = 0; | |
| 120 } | |
| 121 // calculate the popup position | |
| 122 switch (position) { | |
| 123 case 1: | |
| 124 p_x = a_x + a_w + spacer; | |
| 125 p_y = a_y + (a_h / 2) - (p_h / 2); | |
| 126 break; | |
| 127 case 0: | |
| 128 default: | |
| 129 p_x = a_x + (a_w / 2) - (p_w / 2); | |
| 130 p_y = a_y + a_h + spacer; | |
| 131 break; | |
| 132 } | |
| 133 // constrain the popup position | |
| 134 if (p_x < margin) { | |
| 135 p_x = margin; | |
| 136 } else if (page_w != null && (p_x + p_w) > (page_w - margin - scrollbar)) { | |
| 137 p_x = page_w - margin - scrollbar - p_w; | |
| 138 } | |
| 139 if (p_y < margin) { | |
| 140 p_y = margin; | |
| 141 } | |
| 142 // position the popup | |
| 143 popup.style.left = p_x + "px"; | |
| 144 popup.style.top = p_y + "px"; | |
| 145 } | |
| 146 | |
| 147 function lookup_help_popup(popup_id) { | |
| 148 var _body, pop, info; | |
| 149 pop = document.getElementById(popup_id); | |
| 150 if (pop == null) { | |
| 151 _body = document.getElementsByTagName("body")[0]; | |
| 152 pop = document.createElement("div"); | |
| 153 pop.className = "pop_content"; | |
| 154 pop.id = popup_id; | |
| 155 pop.style.backgroundColor = "#FFC"; | |
| 156 pop.style.borderColor = "black"; | |
| 157 info = document.createElement("p"); | |
| 158 info.style.fontWeight = "bold"; | |
| 159 info.appendChild(document.createTextNode("Error: No popup for topic \"" + popup_id + "\".")); | |
| 160 pop.appendChild(info); | |
| 161 // this might cause problems with the menu, but as this only happens | |
| 162 // when something is already wrong I don't think that's too much of a problem | |
| 163 _body.insertBefore(pop, _body.firstChild); | |
| 164 } | |
| 165 if (document.getElementsByTagName('body')[0].hasAttribute("data-autobtns")) { | |
| 166 if (!/\bauto_buttons\b/.test(pop.className)) { | |
| 167 pop.className += " auto_buttons"; | |
| 168 var back_btn_sec = document.createElement("div"); | |
| 169 back_btn_sec.className = "nested_only pop_back_sec"; | |
| 170 var back_btn = document.createElement("span"); | |
| 171 back_btn.className = "pop_back"; | |
| 172 back_btn.appendChild(document.createTextNode("<< back")); | |
| 173 back_btn.addEventListener("click", function(e) { | |
| 174 help_return(); | |
| 175 }, false); | |
| 176 back_btn_sec.appendChild(back_btn); | |
| 177 pop.insertBefore(back_btn_sec, pop.firstChild); | |
| 178 var close_btn_sec = document.createElement("div"); | |
| 179 close_btn_sec.className = "pop_close_sec"; | |
| 180 var close_btn = document.createElement("span"); | |
| 181 close_btn.className = "pop_close"; | |
| 182 close_btn.appendChild(document.createTextNode("close")); | |
| 183 close_btn.addEventListener("click", function(e) { | |
| 184 help_popup(); | |
| 185 }, false); | |
| 186 close_btn_sec.appendChild(close_btn); | |
| 187 pop.appendChild(close_btn_sec); | |
| 188 } | |
| 189 } | |
| 190 return pop; | |
| 191 } | |
| 192 | |
| 193 /* | |
| 194 * help_popup | |
| 195 * | |
| 196 * Moves around help pop-ups so they appear | |
| 197 * below an activator. | |
| 198 */ | |
| 199 function help_popup(activator, popup_id) { | |
| 200 "use strict"; | |
| 201 var pop; | |
| 202 // set default values | |
| 203 if (typeof help_popup.popup === "undefined") { | |
| 204 help_popup.popup = []; | |
| 205 } | |
| 206 if (typeof help_popup.activator === "undefined") { | |
| 207 help_popup.activator = null; | |
| 208 } | |
| 209 var last_pop = (help_popup.popup.length > 0 ? help_popup.popup[help_popup.popup.length - 1] : null); | |
| 210 if (typeof(activator) == "undefined") { // no activator so hide | |
| 211 if (last_pop != null) { | |
| 212 last_pop.style.display = 'none'; | |
| 213 help_popup.popup = []; | |
| 214 } | |
| 215 return; | |
| 216 } | |
| 217 pop = lookup_help_popup(popup_id); | |
| 218 if (pop == last_pop) { | |
| 219 if (activator == help_popup.activator) { | |
| 220 //hide popup (as we've already shown it for the current help button) | |
| 221 last_pop.style.display = 'none'; | |
| 222 help_popup.popup = []; | |
| 223 return; // toggling complete! | |
| 224 } | |
| 225 } else if (last_pop != null) { | |
| 226 //activating different popup so hide current one | |
| 227 last_pop.style.display = 'none'; | |
| 228 } | |
| 229 help_popup.popup = [pop]; | |
| 230 help_popup.activator = activator; | |
| 231 toggle_class(pop, "nested", false); | |
| 232 //must make the popup visible to measure it or it has zero width | |
| 233 pop.style.display = 'block'; | |
| 234 position_popup(activator, pop); | |
| 235 } | |
| 236 | |
| 237 /* | |
| 238 * help_refine | |
| 239 * | |
| 240 * Intended for links within a help popup. Stores a stack of state so | |
| 241 * you can go back. | |
| 242 */ | |
| 243 function help_refine(popup_id) { | |
| 244 if (help_popup.popup == null || help_popup.popup.length == 0 || help_popup.activator == null) { | |
| 245 //throw new Error("Cannot refine a help popup when one is not shown!"); | |
| 246 var pop = lookup_help_popup(popup_id); | |
| 247 var act_id = popup_id + '_act'; | |
| 248 var activator = document.getElementById(act_id); | |
| 249 help_popup(activator, popup_id); | |
| 250 } | |
| 251 var pop = lookup_help_popup(popup_id); | |
| 252 var last_pop = help_popup.popup[help_popup.popup.length - 1]; | |
| 253 if (pop == last_pop) return; // slightly odd, but no real cause for alarm | |
| 254 help_popup.popup.push(pop); | |
| 255 toggle_class(pop, "nested", true); | |
| 256 last_pop.style.display = "none"; | |
| 257 //must make the popup visible to measure it or it has zero width | |
| 258 pop.style.display = "block"; | |
| 259 position_popup(help_popup.activator, pop); | |
| 260 } | |
| 261 | |
| 262 /* | |
| 263 * help_return | |
| 264 * | |
| 265 * Intended for links within a help popup. Stores a stack of state so | |
| 266 * you can go back. | |
| 267 */ | |
| 268 function help_return() { | |
| 269 if (help_popup.popup == null || help_popup.popup.length == 0 || help_popup.activator == null) { | |
| 270 throw new Error("Can not return to a earlier help popup when one is not shown!"); | |
| 271 } | |
| 272 var last_pop = help_popup.popup.pop(); | |
| 273 last_pop.style.display = "none"; | |
| 274 var pop = (help_popup.popup.length > 0 ? help_popup.popup[help_popup.popup.length - 1] : null); | |
| 275 if (pop != null) { | |
| 276 toggle_class(pop, "nested", help_popup.popup.length > 1); | |
| 277 pop.style.display = "block"; | |
| 278 position_popup(help_popup.activator, pop); | |
| 279 } else { | |
| 280 help_popup.activator = null; | |
| 281 } | |
| 282 } | |
| 283 | |
| 284 /* | |
| 285 * update_scroll_pad | |
| 286 * | |
| 287 * Creates padding at the bottom of the page to allow | |
| 288 * scrolling of anything into view. | |
| 289 */ | |
| 290 function update_scroll_pad() { | |
| 291 var page, pad; | |
| 292 page = (document.compatMode === "CSS1Compat") ? document.documentElement : document.body; | |
| 293 pad = $("scrollpad"); | |
| 294 if (pad === null) { | |
| 295 pad = document.createElement("div"); | |
| 296 pad.id = 'scrollpad'; | |
| 297 document.getElementsByTagName('body')[0].appendChild(pad); | |
| 298 } | |
| 299 pad.style.height = Math.abs(page.clientHeight - 100) + "px"; | |
| 300 } | |
| 301 | |
| 302 function substitute_classes(node, remove, add) { | |
| 303 "use strict"; | |
| 304 var list, all, i, cls, classes; | |
| 305 list = node.className.split(/\s+/); | |
| 306 all = {}; | |
| 307 for (i = 0; i < list.length; i++) { | |
| 308 if (list[i].length > 0) all[list[i]] = true; | |
| 309 } | |
| 310 for (i = 0; i < remove.length; i++) { | |
| 311 if (all.hasOwnProperty(remove[i])) { | |
| 312 delete all[remove[i]]; | |
| 313 } | |
| 314 } | |
| 315 for (i = 0; i < add.length; i++) { | |
| 316 all[add[i]] = true; | |
| 317 } | |
| 318 classes = ""; | |
| 319 for (cls in all) { | |
| 320 classes += cls + " "; | |
| 321 } | |
| 322 node.className = classes; | |
| 323 } | |
| 324 | |
| 325 /* | |
| 326 * toggle_class | |
| 327 * | |
| 328 * Adds or removes a class from the node. If the parameter 'enabled' is not | |
| 329 * passed then the existence of the class will be toggled, otherwise it will be | |
| 330 * included if enabled is true. | |
| 331 */ | |
| 332 function toggle_class(node, cls, enabled) { | |
| 333 var classes = node.className; | |
| 334 var list = classes.replace(/^\s+/, '').replace(/\s+$/, '').split(/\s+/); | |
| 335 var found = false; | |
| 336 for (var i = 0; i < list.length; i++) { | |
| 337 if (list[i] == cls) { | |
| 338 list.splice(i, 1); | |
| 339 i--; | |
| 340 found = true; | |
| 341 } | |
| 342 } | |
| 343 if (typeof enabled == "undefined") { | |
| 344 if (!found) list.push(cls); | |
| 345 } else { | |
| 346 if (enabled) list.push(cls); | |
| 347 } | |
| 348 node.className = list.join(" "); | |
| 349 } | |
| 350 | |
| 351 /* | |
| 352 * find_child | |
| 353 * | |
| 354 * Searches child nodes in depth first order and returns the first it finds | |
| 355 * with the className specified. | |
| 356 * TODO replace with querySelector | |
| 357 */ | |
| 358 function find_child(node, className) { | |
| 359 var pattern; | |
| 360 if (node == null || typeof node !== "object") { | |
| 361 return null; | |
| 362 } | |
| 363 if (typeof className === "string") { | |
| 364 pattern = new RegExp("\\b" + className + "\\b"); | |
| 365 } else { | |
| 366 pattern = className; | |
| 367 } | |
| 368 if (node.nodeType == Node.ELEMENT_NODE && | |
| 369 pattern.test(node.className)) { | |
| 370 return node; | |
| 371 } else { | |
| 372 var result = null; | |
| 373 for (var i = 0; i < node.childNodes.length; i++) { | |
| 374 result = find_child(node.childNodes[i], pattern); | |
| 375 if (result != null) break; | |
| 376 } | |
| 377 return result; | |
| 378 } | |
| 379 } | |
| 380 | |
| 381 /* | |
| 382 * find_parent | |
| 383 * | |
| 384 * Searches parent nodes outwards from the node and returns the first it finds | |
| 385 * with the className specified. | |
| 386 */ | |
| 387 function find_parent(node, className) { | |
| 388 var pattern; | |
| 389 pattern = new RegExp("\\b" + className + "\\b"); | |
| 390 do { | |
| 391 if (node.nodeType == Node.ELEMENT_NODE && | |
| 392 pattern.test(node.className)) { | |
| 393 return node; | |
| 394 } | |
| 395 } while (node = node.parentNode); | |
| 396 return null; | |
| 397 } | |
| 398 | |
| 399 /* | |
| 400 * find_parent_tag | |
| 401 * | |
| 402 * Searches parent nodes outwards from the node and returns the first it finds | |
| 403 * with the tag name specified. HTML tags should be specified in upper case. | |
| 404 */ | |
| 405 function find_parent_tag(node, tag_name) { | |
| 406 do { | |
| 407 if (node.nodeType == Node.ELEMENT_NODE && node.tagName == tag_name) { | |
| 408 return node; | |
| 409 } | |
| 410 } while (node = node.parentNode); | |
| 411 return null; | |
| 412 } | |
| 413 | |
| 414 /* | |
| 415 * __toggle_help | |
| 416 * | |
| 417 * Uses the 'topic' property of the this object to | |
| 418 * toggle display of a help topic. | |
| 419 * | |
| 420 * This function is not intended to be called directly. | |
| 421 */ | |
| 422 function __toggle_help(e) { | |
| 423 if (!e) e = window.event; | |
| 424 if (e.type === "keydown") { | |
| 425 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
| 426 return; | |
| 427 } | |
| 428 // stop a submit or something like that | |
| 429 e.preventDefault(); | |
| 430 } | |
| 431 | |
| 432 help_popup(this, this.getAttribute("data-topic")); | |
| 433 } | |
| 434 | |
| 435 function setup_help_button(button) { | |
| 436 "use strict"; | |
| 437 var topic; | |
| 438 if (button.hasAttribute("data-topic")) { | |
| 439 topic = button.getAttribute("data-topic"); | |
| 440 if (document.getElementById(topic) != null) { | |
| 441 button.tabIndex = "0"; // make keyboard selectable | |
| 442 button.addEventListener("click", function() { | |
| 443 help_popup(button, topic); | |
| 444 }, false); | |
| 445 button.addEventListener("keydown", function(e) { | |
| 446 // toggle only on Enter or Spacebar, let other keys do their thing | |
| 447 if (e.keyCode !== 13 && e.keyCode !== 32) return; | |
| 448 // stop a submit or something like that | |
| 449 e.preventDefault(); | |
| 450 help_popup(button, topic); | |
| 451 }, false); | |
| 452 } else { | |
| 453 button.style.visibility = "hidden"; | |
| 454 } | |
| 455 } | |
| 456 button.className += " active"; | |
| 457 } | |
| 458 | |
| 459 /* | |
| 460 * help_button | |
| 461 * | |
| 462 * Makes a help button for the passed topic. | |
| 463 */ | |
| 464 function help_button(topic) { | |
| 465 var btn = document.createElement("div"); | |
| 466 btn.className = "help"; | |
| 467 btn.setAttribute("data-topic", topic); | |
| 468 setup_help_button(btn); | |
| 469 return btn; | |
| 470 } | |
| 471 | |
| 472 /* | |
| 473 * prepare_download | |
| 474 * | |
| 475 * Sets the attributes of a link to setup a file download using the given content. | |
| 476 * If no link is provided then create one and click it. | |
| 477 */ | |
| 478 function prepare_download(content, mimetype, filename, link) { | |
| 479 "use strict"; | |
| 480 // if no link is provided then create one and click it | |
| 481 var click_link = false; | |
| 482 if (!link) { | |
| 483 link = document.createElement("a"); | |
| 484 click_link = true; | |
| 485 } | |
| 486 try { | |
| 487 // Use a BLOB to convert the text into a data URL. | |
| 488 // We could do this manually with a base 64 conversion. | |
| 489 // This will only be supported on modern browsers, | |
| 490 // hence the try block. | |
| 491 var blob = new Blob([content], {type: mimetype}); | |
| 492 var reader = new FileReader(); | |
| 493 reader.onloadend = function() { | |
| 494 // If we're lucky the browser will also support the download | |
| 495 // attribute which will let us suggest a file name to save the link. | |
| 496 // Otherwise it is likely that the filename will be unintelligible. | |
| 497 link.setAttribute("download", filename); | |
| 498 link.href = reader.result; | |
| 499 if (click_link) { | |
| 500 // must add the link to click it | |
| 501 document.body.appendChild(link); | |
| 502 link.click(); | |
| 503 document.body.removeChild(link); | |
| 504 } | |
| 505 } | |
| 506 reader.readAsDataURL(blob); | |
| 507 } catch (error) { | |
| 508 if (console && console.log) console.log(error); | |
| 509 // probably an old browser | |
| 510 link.href = ""; | |
| 511 link.visible = false; | |
| 512 } | |
| 513 } | |
| 514 | |
| 515 /* | |
| 516 * add_cell | |
| 517 * | |
| 518 * Add a cell to the table row. | |
| 519 */ | |
| 520 function add_cell(row, node, cls, click_action) { | |
| 521 var cell = row.insertCell(row.cells.length); | |
| 522 if (node) cell.appendChild(node); | |
| 523 if (cls && cls !== "") cell.className = cls; | |
| 524 if (click_action) cell.addEventListener("click", click_action, false); | |
| 525 } | |
| 526 | |
| 527 /* | |
| 528 * add_header_cell | |
| 529 * | |
| 530 * Add a header cell to the table row. | |
| 531 */ | |
| 532 function add_header_cell(row, node, help_topic, cls, colspan, is_new) { | |
| 533 var th = document.createElement("th"); | |
| 534 if (node) th.appendChild(node); | |
| 535 if (help_topic && help_topic !== "") th.appendChild(help_button(help_topic)); | |
| 536 if (is_new && is_new !== "") { | |
| 537 var br = document.createElement("span"); | |
| 538 br.innerHTML = "<br>"; | |
| 539 th.appendChild(br); | |
| 540 var new_icon = document.createElement("img"); | |
| 541 new_icon.src = new_icon_src; | |
| 542 new_icon.alt = "NEW"; | |
| 543 th.appendChild(new_icon); | |
| 544 } | |
| 545 if (cls && cls !== "") th.className = cls; | |
| 546 if (typeof colspan == "number" && colspan > 1) th.colSpan = colspan; | |
| 547 row.appendChild(th); | |
| 548 } | |
| 549 | |
| 550 /* | |
| 551 * add_text_cell | |
| 552 * | |
| 553 * Add a text cell to the table row. | |
| 554 */ | |
| 555 function add_text_cell(row, text, cls, action) { | |
| 556 var node = null; | |
| 557 if (typeof(text) != 'undefined') node = document.createTextNode(text); | |
| 558 add_cell(row, node, cls, action); | |
| 559 } | |
| 560 | |
| 561 /* | |
| 562 * add_text_header_cell | |
| 563 * | |
| 564 * Add a text header cell to the table row. | |
| 565 */ | |
| 566 function add_text_header_cell(row, text, help_topic, cls, action, colspan, is_new) { | |
| 567 var node = null; | |
| 568 if (typeof(text) != 'undefined') { | |
| 569 var nbsp = (help_topic ? "\u00A0" : ""); | |
| 570 var str = "" + text; | |
| 571 var parts = str.split(/\n/); | |
| 572 if (parts.length === 1) { | |
| 573 if (action) { | |
| 574 node = document.createElement("span"); | |
| 575 node.appendChild(document.createTextNode(str + nbsp)); | |
| 576 } else { | |
| 577 node = document.createTextNode(str + nbsp); | |
| 578 } | |
| 579 } else { | |
| 580 node = document.createElement("span"); | |
| 581 for (var i = 0; i < parts.length; i++) { | |
| 582 if (i !== 0) { | |
| 583 node.appendChild(document.createElement("br")); | |
| 584 } | |
| 585 node.appendChild(document.createTextNode(parts[i])); | |
| 586 } | |
| 587 } | |
| 588 if (action) { | |
| 589 node.addEventListener("click", action, false); | |
| 590 node.style.cursor = "pointer"; | |
| 591 } | |
| 592 } | |
| 593 add_header_cell(row, node, help_topic, cls, colspan, is_new); | |
| 594 } | |
| 595 | |
| 596 function setup_help() { | |
| 597 "use strict"; | |
| 598 var help_buttons, i; | |
| 599 help_buttons = document.querySelectorAll(".help:not(.active)"); | |
| 600 for (i = 0; i < help_buttons.length; i++) { | |
| 601 setup_help_button(help_buttons[i]); | |
| 602 } | |
| 603 } | |
| 604 | |
| 605 function setup_scrollpad() { | |
| 606 "use strict"; | |
| 607 if (document.getElementsByTagName('body')[0].hasAttribute("data-scrollpad") && document.getElementById("scrollpad") == null) { | |
| 608 window.addEventListener("resize", update_scroll_pad, false); | |
| 609 update_scroll_pad(); | |
| 610 } | |
| 611 } | |
| 612 | |
| 613 // anon function to avoid polluting global scope | |
| 614 (function() { | |
| 615 "use strict"; | |
| 616 window.addEventListener("load", function load(evt) { | |
| 617 window.removeEventListener("load", load, false); | |
| 618 setup_help(); | |
| 619 setup_scrollpad(); | |
| 620 }, false); | |
| 621 })(); | |
| 622 | |
| 623 /* | |
| 624 * make_link | |
| 625 * | |
| 626 * Creates a text node and if a URL is specified it surrounds it with a link. | |
| 627 * If the URL doesn't begin with "http://" it automatically adds it, as | |
| 628 * relative links don't make much sense in this context. | |
| 629 */ | |
| 630 function make_link(text, url) { | |
| 631 var textNode = null; | |
| 632 var link = null; | |
| 633 if (typeof text !== "undefined" && text !== null) textNode = document.createTextNode(text); | |
| 634 if (typeof url === "string") { | |
| 635 if (url.indexOf("//") == -1) { | |
| 636 url = "http://" + url; | |
| 637 } | |
| 638 link = document.createElement('a'); | |
| 639 link.href = url; | |
| 640 if (textNode) link.appendChild(textNode); | |
| 641 return link; | |
| 642 } | |
| 643 return textNode; | |
| 644 } | |
| 645 | |
| 646 // | |
| 647 // Function to create an HTML paragraph describing the | |
| 648 // MEME Suite background model source. | |
| 649 // | |
| 650 function make_background_source(title, source, text) { | |
| 651 var paraNode = document.createElement("P"); | |
| 652 var titleNode = document.createElement("B"); | |
| 653 var textNode1 = document.createTextNode("\u00A0\u00A0\u00A0\u00A0" + title + ": "); | |
| 654 titleNode.appendChild(textNode1); | |
| 655 var source_text = ((source == "--motif--") ? "the (first) motif file" : (source == "--nrdb--") ? "an old version of the NCBI non-redundant database" : (source == "--uniform--") ? "the uniform model" : (source == "--query--") ? "the query file" : (source == "--sequences--") ? "built from the (primary) sequences" : (source == "--control--") ? "built from the control (negative) sequences" : ((source == "--negatives--") ? "built from the negative (control) sequences" : "the file '" + source + "'")); | |
| 656 if (text) { return source_text; } | |
| 657 var textNode2 = document.createTextNode(source_text); | |
| 658 paraNode.appendChild(titleNode); | |
| 659 paraNode.appendChild(textNode2); | |
| 660 return paraNode; | |
| 661 } | |
| 662 | |
| 663 // Function to create a help button | |
| 664 function make_help_button(container, help_topic) { | |
| 665 container.appendChild(help_button(help_topic)); | |
| 666 } | |
| 667 | |
| 668 // Function to toggle display. | |
| 669 function change_display(id) { | |
| 670 var element=document.getElementById(id); | |
| 671 element.style.display=(element.style.display=='none') ? element.style.display='inline' : element.style.display='none'; | |
| 672 } | |
| 673 </script> | |
| 674 <script> | |
| 675 // | |
| 676 // return true if any part of the passed element is visible in the viewport | |
| 677 // | |
| 678 function element_in_viewport(elem) { | |
| 679 var rect; | |
| 680 try { | |
| 681 rect = elem.getBoundingClientRect(); | |
| 682 } catch (e) { | |
| 683 return false; | |
| 684 } | |
| 685 return ( | |
| 686 rect.top < (window.innerHeight || document.body.clientHeight) && | |
| 687 rect.bottom > 0 && | |
| 688 rect.left < (window.innerWidth || document.body.clientWidth) && | |
| 689 rect.right > 0 | |
| 690 ); | |
| 691 } | |
| 692 | |
| 693 // | |
| 694 // Functions to delay a drawing task until it is required or it would not lag the display to do so | |
| 695 // | |
| 696 | |
| 697 // a list of items still to be drawn | |
| 698 var drawable_list = []; | |
| 699 // the delay between drawing objects that are not currently visible | |
| 700 var draw_delay = 1; | |
| 701 // the delay after a user interaction | |
| 702 var user_delay = 300; | |
| 703 // the delay after a user has stopped scrolling and is viewing the stuff drawn on the current page | |
| 704 var stop_delay = 300; | |
| 705 // the timer handle; allows resetting of the timer after user interactions | |
| 706 var draw_timer = null; | |
| 707 | |
| 708 // | |
| 709 // Drawable | |
| 710 // | |
| 711 // elem - a page element which defines the position on the page that drawing is to be done | |
| 712 // task - an object with the method run which takes care of painting the object | |
| 713 // | |
| 714 var Drawable = function(elem, task) { | |
| 715 this.elem = elem; | |
| 716 this.task = task; | |
| 717 } | |
| 718 | |
| 719 // | |
| 720 // Drawable.is_visible | |
| 721 // | |
| 722 // Determines if the element is visible in the viewport | |
| 723 // | |
| 724 Drawable.prototype.is_visible = function() { | |
| 725 return element_in_viewport(this.elem); | |
| 726 } | |
| 727 | |
| 728 // | |
| 729 // Drawable.run | |
| 730 // | |
| 731 // Run the task held by the drawable | |
| 732 Drawable.prototype.run = function() { | |
| 733 if (this.task) this.task.run(); | |
| 734 this.task = null; | |
| 735 } | |
| 736 | |
| 737 // | |
| 738 // Drawable.run | |
| 739 // | |
| 740 // Run the task iff visible | |
| 741 // returns true if the task ran or has already run | |
| 742 Drawable.prototype.run_visible = function() { | |
| 743 if (this.task) { | |
| 744 if (element_in_viewport(this.elem)) { | |
| 745 this.task.run(); | |
| 746 this.task = null; | |
| 747 return true; | |
| 748 } | |
| 749 return false; | |
| 750 } else { | |
| 751 return true; | |
| 752 } | |
| 753 } | |
| 754 | |
| 755 // | |
| 756 // draw_on_screen | |
| 757 // | |
| 758 // Checks each drawable object and draws those on screen. | |
| 759 // | |
| 760 function draw_on_screen() { | |
| 761 var found = false; | |
| 762 for (var i = 0; i < drawable_list.length; i++) { | |
| 763 if (drawable_list[i].run_visible()) { | |
| 764 drawable_list.splice(i--, 1); | |
| 765 found = true; | |
| 766 } | |
| 767 } | |
| 768 return found; | |
| 769 } | |
| 770 | |
| 771 // | |
| 772 // process_draw_tasks | |
| 773 // | |
| 774 // Called on a delay to process the next available | |
| 775 // draw task. | |
| 776 // | |
| 777 function process_draw_tasks() { | |
| 778 var delay = draw_delay; | |
| 779 draw_timer = null; | |
| 780 if (drawable_list.length == 0) return; //no more tasks | |
| 781 if (draw_on_screen()) { | |
| 782 delay = stop_delay; //give the user a chance to scroll | |
| 783 } else { | |
| 784 //get next task | |
| 785 var drawable = drawable_list.shift(); | |
| 786 drawable.task.run(); | |
| 787 } | |
| 788 //allow UI updates between tasks | |
| 789 draw_timer = window.setTimeout("process_draw_tasks()", delay); | |
| 790 } | |
| 791 | |
| 792 // | |
| 793 // delayed_process_draw_tasks | |
| 794 // | |
| 795 // Call process_draw_tasks after a short delay. | |
| 796 // The delay serves to group multiple redundant events. | |
| 797 // Should be set as event handler for onscroll and onresize. | |
| 798 // | |
| 799 function delayed_process_draw_tasks() { | |
| 800 //reset the timer | |
| 801 if (drawable_list.length > 0) { | |
| 802 if (draw_timer != null) clearTimeout(draw_timer); | |
| 803 draw_timer = window.setTimeout("process_draw_tasks()", user_delay); | |
| 804 } | |
| 805 } | |
| 806 | |
| 807 // | |
| 808 // add_draw_task | |
| 809 // | |
| 810 // Add a drawing task to be called immediately if it is | |
| 811 // visible, or to be called on a delay to reduce stuttering | |
| 812 // effect on the web browser. | |
| 813 function add_draw_task(elem, task) { | |
| 814 drawable = new Drawable(elem, task); | |
| 815 if (drawable.is_visible()) { | |
| 816 task.run(); | |
| 817 } else { | |
| 818 drawable_list.push(drawable); | |
| 819 //reset timer | |
| 820 if (draw_timer != null) clearTimeout(draw_timer); | |
| 821 draw_timer = window.setTimeout("process_draw_tasks()", user_delay); | |
| 822 } | |
| 823 } | |
| 824 | |
| 825 </script> | |
| 826 <script> | |
| 827 function motif_logo_template(inputs) { | |
| 828 function _input(name) { | |
| 829 if (typeof inputs[name] === "undefined") { | |
| 830 throw new Error("Missing template variable: " + name); | |
| 831 } | |
| 832 return inputs[name]; | |
| 833 } | |
| 834 return ( | |
| 835 "%!PS-Adobe-3.0 EPSF-3.0\n" + | |
| 836 "%%Title: Sequence Logo : " + _input("TITLE") + "\n" + | |
| 837 "%%Creator: " + _input("CREATOR") + "\n" + | |
| 838 "%%CreationDate: " + _input("CREATIONDATE") + "\n" + | |
| 839 "%%BoundingBox: 0 0 " + _input("BOUNDINGWIDTH") + " " + _input("BOUNDINGHEIGHT") + " \n" + | |
| 840 "%%Pages: 0\n" + | |
| 841 "%%DocumentFonts: \n" + | |
| 842 "%%EndComments\n" + | |
| 843 "\n" + | |
| 844 "% ---- CONSTANTS ----\n" + | |
| 845 "\/cmfactor 72 2.54 div def % defines points -> cm conversion\n" + | |
| 846 "\/cm {cmfactor mul} bind def % defines centimeters\n" + | |
| 847 "\n" + | |
| 848 "% ---- VARIABLES ----\n" + | |
| 849 "\n" + | |
| 850 "% NA = Nucleic Acid, AA = Amino Acid\n" + | |
| 851 "\/logoType (" + _input("LOGOTYPE") + ") def \n" + | |
| 852 "\n" + | |
| 853 "\/logoTitle (" + _input("TITLE") + ") def\n" + | |
| 854 "\n" + | |
| 855 "% Dimensions in cm\n" + | |
| 856 "\/logoWidth " + _input("LOGOWIDTH") + " cm def\n" + | |
| 857 "\/logoHeight " + _input("LOGOLINEHEIGHT") + " cm def\n" + | |
| 858 "\/totalHeight " + _input("LOGOHEIGHT") + " cm def\n" + | |
| 859 "\n" + | |
| 860 "\/yaxis " + _input("YAXIS") + " def\n" + | |
| 861 "\/yaxisLabel (" + _input("YAXISLABEL") + ") def\n" + | |
| 862 "\/yaxisBits " + _input("BARBITS") + " def % bits\n" + | |
| 863 "\/yaxisTicBits " + _input("TICBITS") + " def\n" + | |
| 864 "\n" + | |
| 865 "\/xaxis " + _input("NUMBERING") + " def\n" + | |
| 866 "\/xaxisLabel (" + _input("XAXISLABEL") + ") def\n" + | |
| 867 "\/showEnds (" + _input("SHOWENDS") + ") def \n" + | |
| 868 "\n" + | |
| 869 "\/showFineprint true def\n" + | |
| 870 "\/fineprint (" + _input("FINEPRINT") + ") def\n" + | |
| 871 "\n" + | |
| 872 "\/charsPerLine " + _input("CHARSPERLINE") + " def\n" + | |
| 873 "\n" + | |
| 874 "\/showingBox " + _input("SHOWINGBOX") + " def \n" + | |
| 875 "\/shrinking false def % true falses\n" + | |
| 876 "\/shrink 1.0 def\n" + | |
| 877 "\/outline " + _input("OUTLINE") + " def\n" + | |
| 878 "\n" + | |
| 879 "\/IbeamFraction " + _input("ERRORBARFRACTION") + " def\n" + | |
| 880 "\/IbeamGray 0.50 def\n" + | |
| 881 "\/IbeamLineWidth 0.5 def\n" + | |
| 882 "\n" + | |
| 883 "\/fontsize " + _input("FONTSIZE") + " def\n" + | |
| 884 "\/titleFontsize " + _input("TITLEFONTSIZE") + " def\n" + | |
| 885 "\/smallFontsize " + _input("SMALLFONTSIZE") + " def\n" + | |
| 886 "\n" + | |
| 887 "\/topMargin " + _input("TOPMARGIN") + " cm def\n" + | |
| 888 "\/bottomMargin " + _input("BOTTOMMARGIN") + " cm def\n" + | |
| 889 "\n" + | |
| 890 "\/defaultColor [0 0 0] def \n" + | |
| 891 "\n" + | |
| 892 _input("COLORDICT") + "\n" + | |
| 893 "\n" + | |
| 894 "\/colorDict fullColourDict def\n" + | |
| 895 "\n" + | |
| 896 "% ---- DERIVED PARAMETERS ----\n" + | |
| 897 "\n" + | |
| 898 "\/leftMargin\n" + | |
| 899 " fontsize 3.5 mul\n" + | |
| 900 "\n" + | |
| 901 "def \n" + | |
| 902 "\n" + | |
| 903 "\/rightMargin \n" + | |
| 904 " %Add extra room if showing ends\n" + | |
| 905 " showEnds (false) eq { fontsize}{fontsize 1.5 mul} ifelse\n" + | |
| 906 "def\n" + | |
| 907 "\n" + | |
| 908 "\/yaxisHeight \n" + | |
| 909 " logoHeight \n" + | |
| 910 " bottomMargin sub \n" + | |
| 911 " topMargin sub\n" + | |
| 912 "def\n" + | |
| 913 "\n" + | |
| 914 "\/ticWidth fontsize 2 div def\n" + | |
| 915 "\n" + | |
| 916 "\/pointsPerBit yaxisHeight yaxisBits div def\n" + | |
| 917 "\n" + | |
| 918 "\/stackMargin 1 def\n" + | |
| 919 "\n" + | |
| 920 "% Do not add space aroung characters if characters are boxed\n" + | |
| 921 "\/charRightMargin \n" + | |
| 922 " showingBox { 0.0 } {stackMargin} ifelse\n" + | |
| 923 "def\n" + | |
| 924 "\n" + | |
| 925 "\/charTopMargin \n" + | |
| 926 " showingBox { 0.0 } {stackMargin} ifelse\n" + | |
| 927 "def\n" + | |
| 928 "\n" + | |
| 929 "\/charWidth\n" + | |
| 930 " logoWidth\n" + | |
| 931 " leftMargin sub\n" + | |
| 932 " rightMargin sub\n" + | |
| 933 " charsPerLine div\n" + | |
| 934 " charRightMargin sub\n" + | |
| 935 "def\n" + | |
| 936 "\n" + | |
| 937 "\/charWidth4 charWidth 4 div def\n" + | |
| 938 "\/charWidth2 charWidth 2 div def\n" + | |
| 939 "\n" + | |
| 940 "\/stackWidth \n" + | |
| 941 " charWidth charRightMargin add\n" + | |
| 942 "def\n" + | |
| 943 " \n" + | |
| 944 "\/numberFontsize \n" + | |
| 945 " fontsize charWidth lt {fontsize}{charWidth} ifelse\n" + | |
| 946 "def\n" + | |
| 947 "\n" + | |
| 948 "% movements to place 5'\/N and 3'\/C symbols\n" + | |
| 949 "\/leftEndDeltaX fontsize neg def\n" + | |
| 950 "\/leftEndDeltaY fontsize 1.5 mul neg def\n" + | |
| 951 "\/rightEndDeltaX fontsize 0.25 mul def\n" + | |
| 952 "\/rightEndDeltaY leftEndDeltaY def\n" + | |
| 953 "\n" + | |
| 954 "% Outline width is proporional to charWidth, \n" + | |
| 955 "% but no less that 1 point\n" + | |
| 956 "\/outlinewidth \n" + | |
| 957 " charWidth 32 div dup 1 gt {}{pop 1} ifelse\n" + | |
| 958 "def\n" + | |
| 959 "\n" + | |
| 960 "\n" + | |
| 961 "% ---- PROCEDURES ----\n" + | |
| 962 "\n" + | |
| 963 "\/StartLogo { \n" + | |
| 964 " % Save state\n" + | |
| 965 " save \n" + | |
| 966 " gsave \n" + | |
| 967 "\n" + | |
| 968 " % Print Logo Title, top center \n" + | |
| 969 " gsave \n" + | |
| 970 " SetStringFont\n" + | |
| 971 "\n" + | |
| 972 " logoWidth 2 div\n" + | |
| 973 " logoTitle\n" + | |
| 974 " stringwidth pop 2 div sub\n" + | |
| 975 " totalHeight\n" + | |
| 976 " titleFontsize sub\n" + | |
| 977 " moveto\n" + | |
| 978 "\n" + | |
| 979 " logoTitle\n" + | |
| 980 " show\n" + | |
| 981 " grestore\n" + | |
| 982 "\n" + | |
| 983 " % Print X-axis label, bottom center\n" + | |
| 984 " gsave\n" + | |
| 985 " SetStringFont\n" + | |
| 986 "\n" + | |
| 987 " logoWidth 2 div\n" + | |
| 988 " xaxisLabel\n" + | |
| 989 " stringwidth pop 2 div sub\n" + | |
| 990 " 0\n" + | |
| 991 " titleFontsize 3 div\n" + | |
| 992 " add\n" + | |
| 993 " moveto\n" + | |
| 994 "\n" + | |
| 995 " xaxisLabel\n" + | |
| 996 " show\n" + | |
| 997 " grestore\n" + | |
| 998 "\n" + | |
| 999 " % Show Fine Print\n" + | |
| 1000 " showFineprint {\n" + | |
| 1001 " gsave\n" + | |
| 1002 " SetSmallFont\n" + | |
| 1003 " logoWidth\n" + | |
| 1004 " fineprint stringwidth pop sub\n" + | |
| 1005 " smallFontsize sub\n" + | |
| 1006 " smallFontsize 3 div\n" + | |
| 1007 " moveto\n" + | |
| 1008 " \n" + | |
| 1009 " fineprint show\n" + | |
| 1010 " grestore\n" + | |
| 1011 " } if\n" + | |
| 1012 "\n" + | |
| 1013 " % Move to lower left corner of last line, first stack\n" + | |
| 1014 " leftMargin bottomMargin translate\n" + | |
| 1015 "\n" + | |
| 1016 " % Move above first line ready for StartLine \n" + | |
| 1017 " 0 totalHeight translate\n" + | |
| 1018 "\n" + | |
| 1019 " SetLogoFont\n" + | |
| 1020 "} bind def\n" + | |
| 1021 "\n" + | |
| 1022 "\/EndLogo { \n" + | |
| 1023 " grestore \n" + | |
| 1024 " showpage \n" + | |
| 1025 " restore \n" + | |
| 1026 "} bind def\n" + | |
| 1027 "\n" + | |
| 1028 "\n" + | |
| 1029 "\/StartLine { \n" + | |
| 1030 " % move down to the bottom of the line:\n" + | |
| 1031 " 0 logoHeight neg translate\n" + | |
| 1032 " \n" + | |
| 1033 " gsave \n" + | |
| 1034 " yaxis { MakeYaxis } if\n" + | |
| 1035 " xaxis { showEnds (true) eq {ShowLeftEnd} if } if\n" + | |
| 1036 "} bind def\n" + | |
| 1037 "\n" + | |
| 1038 "\/EndLine{ \n" + | |
| 1039 " xaxis { showEnds (true) eq {ShowRightEnd} if } if\n" + | |
| 1040 " grestore \n" + | |
| 1041 "} bind def\n" + | |
| 1042 "\n" + | |
| 1043 "\n" + | |
| 1044 "\/MakeYaxis {\n" + | |
| 1045 " gsave \n" + | |
| 1046 " stackMargin neg 0 translate\n" + | |
| 1047 " ShowYaxisBar\n" + | |
| 1048 " ShowYaxisLabel\n" + | |
| 1049 " grestore\n" + | |
| 1050 "} bind def\n" + | |
| 1051 "\n" + | |
| 1052 "\n" + | |
| 1053 "\/ShowYaxisBar { \n" + | |
| 1054 " gsave \n" + | |
| 1055 " SetStringFont\n" + | |
| 1056 "\n" + | |
| 1057 " \/str 10 string def % string to hold number \n" + | |
| 1058 " \/smallgap stackMargin 2 div def\n" + | |
| 1059 "\n" + | |
| 1060 " % Draw first tic and bar\n" + | |
| 1061 " gsave \n" + | |
| 1062 " ticWidth neg 0 moveto \n" + | |
| 1063 " ticWidth 0 rlineto \n" + | |
| 1064 " 0 yaxisHeight rlineto\n" + | |
| 1065 " stroke\n" + | |
| 1066 " grestore\n" + | |
| 1067 "\n" + | |
| 1068 " \n" + | |
| 1069 " % Draw the tics\n" + | |
| 1070 " % initial increment limit proc for\n" + | |
| 1071 " 0 yaxisTicBits yaxisBits abs %cvi\n" + | |
| 1072 " {\/loopnumber exch def\n" + | |
| 1073 "\n" + | |
| 1074 " % convert the number coming from the loop to a string\n" + | |
| 1075 " % and find its width\n" + | |
| 1076 " loopnumber 10 str cvrs\n" + | |
| 1077 " \/stringnumber exch def % string representing the number\n" + | |
| 1078 "\n" + | |
| 1079 " stringnumber stringwidth pop\n" + | |
| 1080 " \/numberwidth exch def % width of number to show\n" + | |
| 1081 "\n" + | |
| 1082 " \/halfnumberheight\n" + | |
| 1083 " stringnumber CharBoxHeight 2 div\n" + | |
| 1084 " def\n" + | |
| 1085 "\n" + | |
| 1086 " numberwidth % move back width of number\n" + | |
| 1087 " neg loopnumber pointsPerBit mul % shift on y axis\n" + | |
| 1088 " halfnumberheight sub % down half the digit\n" + | |
| 1089 "\n" + | |
| 1090 " moveto % move back the width of the string\n" + | |
| 1091 "\n" + | |
| 1092 " ticWidth neg smallgap sub % Move back a bit more \n" + | |
| 1093 " 0 rmoveto % move back the width of the tic \n" + | |
| 1094 "\n" + | |
| 1095 " stringnumber show\n" + | |
| 1096 " smallgap 0 rmoveto % Make a small gap \n" + | |
| 1097 "\n" + | |
| 1098 " % now show the tic mark\n" + | |
| 1099 " 0 halfnumberheight rmoveto % shift up again\n" + | |
| 1100 " ticWidth 0 rlineto\n" + | |
| 1101 " stroke\n" + | |
| 1102 " } for\n" + | |
| 1103 " grestore\n" + | |
| 1104 "} bind def\n" + | |
| 1105 "\n" + | |
| 1106 "\/ShowYaxisLabel {\n" + | |
| 1107 " gsave\n" + | |
| 1108 " SetStringFont\n" + | |
| 1109 "\n" + | |
| 1110 " % How far we move left depends on the size of\n" + | |
| 1111 " % the tic labels.\n" + | |
| 1112 " \/str 10 string def % string to hold number \n" + | |
| 1113 " yaxisBits yaxisTicBits div cvi yaxisTicBits mul \n" + | |
| 1114 " str cvs stringwidth pop\n" + | |
| 1115 " ticWidth 1.5 mul add neg \n" + | |
| 1116 "\n" + | |
| 1117 "\n" + | |
| 1118 " yaxisHeight\n" + | |
| 1119 " yaxisLabel stringwidth pop\n" + | |
| 1120 " sub 2 div\n" + | |
| 1121 "\n" + | |
| 1122 " translate\n" + | |
| 1123 " 90 rotate\n" + | |
| 1124 " 0 0 moveto\n" + | |
| 1125 " yaxisLabel show\n" + | |
| 1126 " grestore\n" + | |
| 1127 "} bind def\n" + | |
| 1128 "\n" + | |
| 1129 "\n" + | |
| 1130 "\/StartStack { % <stackNumber> startstack\n" + | |
| 1131 " xaxis {MakeNumber}{pop} ifelse\n" + | |
| 1132 " gsave\n" + | |
| 1133 "} bind def\n" + | |
| 1134 "\n" + | |
| 1135 "\/EndStack {\n" + | |
| 1136 " grestore\n" + | |
| 1137 " stackWidth 0 translate\n" + | |
| 1138 "} bind def\n" + | |
| 1139 "\n" + | |
| 1140 "\n" + | |
| 1141 "% Draw a character whose height is proportional to symbol bits\n" + | |
| 1142 "\/MakeSymbol{ % charbits character MakeSymbol\n" + | |
| 1143 " gsave\n" + | |
| 1144 " \/char exch def\n" + | |
| 1145 " \/bits exch def\n" + | |
| 1146 "\n" + | |
| 1147 " \/bitsHeight \n" + | |
| 1148 " bits pointsPerBit mul \n" + | |
| 1149 " def\n" + | |
| 1150 "\n" + | |
| 1151 " \/charHeight \n" + | |
| 1152 " bitsHeight charTopMargin sub\n" + | |
| 1153 " dup \n" + | |
| 1154 " 0.0 gt {}{pop 0.0} ifelse % if neg replace with zero \n" + | |
| 1155 " def \n" + | |
| 1156 " \n" + | |
| 1157 " charHeight 0.0 gt {\n" + | |
| 1158 " char SetColor\n" + | |
| 1159 " charWidth charHeight char ShowChar\n" + | |
| 1160 "\n" + | |
| 1161 " showingBox { % Unfilled box\n" + | |
| 1162 " 0 0 charWidth charHeight false ShowBox\n" + | |
| 1163 " } if\n" + | |
| 1164 "\n" + | |
| 1165 "\n" + | |
| 1166 " } if\n" + | |
| 1167 "\n" + | |
| 1168 " grestore\n" + | |
| 1169 "\n" + | |
| 1170 " 0 bitsHeight translate \n" + | |
| 1171 "} bind def\n" + | |
| 1172 "\n" + | |
| 1173 "\n" + | |
| 1174 "\/ShowChar { % <width> <height> <char> ShowChar\n" + | |
| 1175 " gsave\n" + | |
| 1176 " \/tc exch def % The character\n" + | |
| 1177 " \/ysize exch def % the y size of the character\n" + | |
| 1178 " \/xsize exch def % the x size of the character\n" + | |
| 1179 "\n" + | |
| 1180 " \/xmulfactor 1 def \n" + | |
| 1181 " \/ymulfactor 1 def\n" + | |
| 1182 " \/limmulfactor 0.01 def\n" + | |
| 1183 " \/drawable true def\n" + | |
| 1184 "\n" + | |
| 1185 " \n" + | |
| 1186 " % if ysize is negative, make everything upside down!\n" + | |
| 1187 " ysize 0 lt {\n" + | |
| 1188 " % put ysize normal in this orientation\n" + | |
| 1189 " \/ysize ysize abs def\n" + | |
| 1190 " xsize ysize translate\n" + | |
| 1191 " 180 rotate\n" + | |
| 1192 " } if\n" + | |
| 1193 "\n" + | |
| 1194 " shrinking {\n" + | |
| 1195 " xsize 1 shrink sub 2 div mul\n" + | |
| 1196 " ysize 1 shrink sub 2 div mul translate \n" + | |
| 1197 "\n" + | |
| 1198 " shrink shrink scale\n" + | |
| 1199 " } if\n" + | |
| 1200 "\n" + | |
| 1201 " % Calculate the font scaling factors\n" + | |
| 1202 " % Loop twice to catch small correction due to first scaling\n" + | |
| 1203 " 2 {\n" + | |
| 1204 " gsave\n" + | |
| 1205 " xmulfactor ymulfactor scale\n" + | |
| 1206 " \n" + | |
| 1207 " ysize % desired size of character in points\n" + | |
| 1208 " tc CharBoxHeight \n" + | |
| 1209 " dup 0.0 ne {\n" + | |
| 1210 " div % factor by which to scale up the character\n" + | |
| 1211 " \/ymulfactor exch def\n" + | |
| 1212 " } % end if\n" + | |
| 1213 " {pop pop}\n" + | |
| 1214 " ifelse\n" + | |
| 1215 "\n" + | |
| 1216 " xsize % desired size of character in points\n" + | |
| 1217 " tc CharBoxWidth \n" + | |
| 1218 " dup 0.0 ne {\n" + | |
| 1219 " div % factor by which to scale up the character\n" + | |
| 1220 " \/xmulfactor exch def\n" + | |
| 1221 " } % end if\n" + | |
| 1222 " {pop pop}\n" + | |
| 1223 " ifelse\n" + | |
| 1224 " grestore\n" + | |
| 1225 " % if the multiplication factors get too small we need to avoid a crash\n" + | |
| 1226 " xmulfactor limmulfactor lt {\n" + | |
| 1227 " \/xmulfactor 1 def\n" + | |
| 1228 " \/drawable false def\n" + | |
| 1229 " } if\n" + | |
| 1230 " ymulfactor limmulfactor lt {\n" + | |
| 1231 " \/ymulfactor 1 def\n" + | |
| 1232 " \/drawable false def\n" + | |
| 1233 " } if\n" + | |
| 1234 " } repeat\n" + | |
| 1235 "\n" + | |
| 1236 " % Adjust horizontal position if the symbol is an I\n" + | |
| 1237 " tc (I) eq {\n" + | |
| 1238 " charWidth 2 div % half of requested character width\n" + | |
| 1239 " tc CharBoxWidth 2 div % half of the actual character\n" + | |
| 1240 " sub 0 translate\n" + | |
| 1241 " % Avoid x scaling for I \n" + | |
| 1242 " \/xmulfactor 1 def \n" + | |
| 1243 " } if\n" + | |
| 1244 "\n" + | |
| 1245 "\n" + | |
| 1246 " % ---- Finally, draw the character\n" + | |
| 1247 " drawable { \n" + | |
| 1248 " newpath\n" + | |
| 1249 " xmulfactor ymulfactor scale\n" + | |
| 1250 "\n" + | |
| 1251 " % Move lower left corner of character to start point\n" + | |
| 1252 " tc CharBox pop pop % llx lly : Lower left corner\n" + | |
| 1253 " exch neg exch neg\n" + | |
| 1254 " moveto\n" + | |
| 1255 "\n" + | |
| 1256 " outline { % outline characters:\n" + | |
| 1257 " outlinewidth setlinewidth\n" + | |
| 1258 " tc true charpath\n" + | |
| 1259 " gsave 1 setgray fill grestore\n" + | |
| 1260 " clip stroke\n" + | |
| 1261 " } { % regular characters\n" + | |
| 1262 " tc show\n" + | |
| 1263 " } ifelse\n" + | |
| 1264 " } if\n" + | |
| 1265 "\n" + | |
| 1266 " grestore\n" + | |
| 1267 "} bind def\n" + | |
| 1268 "\n" + | |
| 1269 "\n" + | |
| 1270 "\/ShowBox { % x1 y1 x2 y2 filled ShowBox\n" + | |
| 1271 " gsave\n" + | |
| 1272 " \/filled exch def \n" + | |
| 1273 " \/y2 exch def\n" + | |
| 1274 " \/x2 exch def\n" + | |
| 1275 " \/y1 exch def\n" + | |
| 1276 " \/x1 exch def\n" + | |
| 1277 " newpath\n" + | |
| 1278 " x1 y1 moveto\n" + | |
| 1279 " x2 y1 lineto\n" + | |
| 1280 " x2 y2 lineto\n" + | |
| 1281 " x1 y2 lineto\n" + | |
| 1282 " closepath\n" + | |
| 1283 "\n" + | |
| 1284 " clip\n" + | |
| 1285 " \n" + | |
| 1286 " filled {\n" + | |
| 1287 " fill\n" + | |
| 1288 " }{ \n" + | |
| 1289 " 0 setgray stroke \n" + | |
| 1290 " } ifelse\n" + | |
| 1291 "\n" + | |
| 1292 " grestore\n" + | |
| 1293 "} bind def\n" + | |
| 1294 "\n" + | |
| 1295 "\n" + | |
| 1296 "\/MakeNumber { % number MakeNumber\n" + | |
| 1297 " gsave\n" + | |
| 1298 " SetNumberFont\n" + | |
| 1299 " stackWidth 0 translate\n" + | |
| 1300 " 90 rotate % rotate so the number fits\n" + | |
| 1301 " dup stringwidth pop % find the length of the number\n" + | |
| 1302 " neg % prepare for move\n" + | |
| 1303 " stackMargin sub % Move back a bit\n" + | |
| 1304 " charWidth (0) CharBoxHeight % height of numbers\n" + | |
| 1305 " sub 2 div %\n" + | |
| 1306 " moveto % move back to provide space\n" + | |
| 1307 " show\n" + | |
| 1308 " grestore\n" + | |
| 1309 "} bind def\n" + | |
| 1310 "\n" + | |
| 1311 "\n" + | |
| 1312 "\/Ibeam{ % heightInBits Ibeam\n" + | |
| 1313 " gsave\n" + | |
| 1314 " % Make an Ibeam of twice the given height in bits\n" + | |
| 1315 " \/height exch pointsPerBit mul def \n" + | |
| 1316 " \/heightDRAW height IbeamFraction mul def\n" + | |
| 1317 "\n" + | |
| 1318 " IbeamLineWidth setlinewidth\n" + | |
| 1319 " IbeamGray setgray \n" + | |
| 1320 "\n" + | |
| 1321 " charWidth2 height neg translate\n" + | |
| 1322 " ShowIbar\n" + | |
| 1323 " newpath\n" + | |
| 1324 " 0 0 moveto\n" + | |
| 1325 " 0 heightDRAW rlineto\n" + | |
| 1326 " stroke\n" + | |
| 1327 " newpath\n" + | |
| 1328 " 0 height moveto\n" + | |
| 1329 " 0 height rmoveto\n" + | |
| 1330 " currentpoint translate\n" + | |
| 1331 " ShowIbar\n" + | |
| 1332 " newpath\n" + | |
| 1333 " 0 0 moveto\n" + | |
| 1334 " 0 heightDRAW neg rlineto\n" + | |
| 1335 " currentpoint translate\n" + | |
| 1336 " stroke\n" + | |
| 1337 " grestore\n" + | |
| 1338 "} bind def\n" + | |
| 1339 "\n" + | |
| 1340 "\n" + | |
| 1341 "\/ShowIbar { % make a horizontal bar\n" + | |
| 1342 " gsave\n" + | |
| 1343 " newpath\n" + | |
| 1344 " charWidth4 neg 0 moveto\n" + | |
| 1345 " charWidth4 0 lineto\n" + | |
| 1346 " stroke\n" + | |
| 1347 " grestore\n" + | |
| 1348 "} bind def\n" + | |
| 1349 "\n" + | |
| 1350 "\n" + | |
| 1351 "\/ShowLeftEnd {\n" + | |
| 1352 " gsave\n" + | |
| 1353 " SetStringFont\n" + | |
| 1354 " leftEndDeltaX leftEndDeltaY moveto\n" + | |
| 1355 " logoType (NA) eq {(5) show ShowPrime} if\n" + | |
| 1356 " logoType (AA) eq {(N) show} if\n" + | |
| 1357 " grestore\n" + | |
| 1358 "} bind def\n" + | |
| 1359 "\n" + | |
| 1360 "\n" + | |
| 1361 "\/ShowRightEnd { \n" + | |
| 1362 " gsave\n" + | |
| 1363 " SetStringFont\n" + | |
| 1364 " rightEndDeltaX rightEndDeltaY moveto\n" + | |
| 1365 " logoType (NA) eq {(3) show ShowPrime} if\n" + | |
| 1366 " logoType (AA) eq {(C) show} if\n" + | |
| 1367 " grestore\n" + | |
| 1368 "} bind def\n" + | |
| 1369 "\n" + | |
| 1370 "\n" + | |
| 1371 "\/ShowPrime {\n" + | |
| 1372 " gsave\n" + | |
| 1373 " SetPrimeFont\n" + | |
| 1374 " (\\242) show \n" + | |
| 1375 " grestore\n" + | |
| 1376 "} bind def\n" + | |
| 1377 "\n" + | |
| 1378 " \n" + | |
| 1379 "\/SetColor{ % <char> SetColor\n" + | |
| 1380 " dup colorDict exch known {\n" + | |
| 1381 " colorDict exch get aload pop setrgbcolor\n" + | |
| 1382 " } {\n" + | |
| 1383 " pop\n" + | |
| 1384 " defaultColor aload pop setrgbcolor\n" + | |
| 1385 " } ifelse \n" + | |
| 1386 "} bind def\n" + | |
| 1387 "\n" + | |
| 1388 "% define fonts\n" + | |
| 1389 "\/SetTitleFont {\/Times-Bold findfont titleFontsize scalefont setfont} bind def\n" + | |
| 1390 "\/SetLogoFont {\/Helvetica-Bold findfont charWidth scalefont setfont} bind def\n" + | |
| 1391 "\/SetStringFont{\/Helvetica-Bold findfont fontsize scalefont setfont} bind def\n" + | |
| 1392 "\/SetPrimeFont {\/Symbol findfont fontsize scalefont setfont} bind def\n" + | |
| 1393 "\/SetSmallFont {\/Helvetica findfont smallFontsize scalefont setfont} bind def\n" + | |
| 1394 "\n" + | |
| 1395 "\/SetNumberFont {\n" + | |
| 1396 " \/Helvetica-Bold findfont \n" + | |
| 1397 " numberFontsize\n" + | |
| 1398 " scalefont\n" + | |
| 1399 " setfont\n" + | |
| 1400 "} bind def\n" + | |
| 1401 "\n" + | |
| 1402 "%Take a single character and return the bounding box\n" + | |
| 1403 "\/CharBox { % <char> CharBox <lx> <ly> <ux> <uy>\n" + | |
| 1404 " gsave\n" + | |
| 1405 " newpath\n" + | |
| 1406 " 0 0 moveto\n" + | |
| 1407 " % take the character off the stack and use it here:\n" + | |
| 1408 " true charpath \n" + | |
| 1409 " flattenpath \n" + | |
| 1410 " pathbbox % compute bounding box of 1 pt. char => lx ly ux uy\n" + | |
| 1411 " % the path is here, but toss it away ...\n" + | |
| 1412 " grestore\n" + | |
| 1413 "} bind def\n" + | |
| 1414 "\n" + | |
| 1415 "\n" + | |
| 1416 "% The height of a characters bounding box\n" + | |
| 1417 "\/CharBoxHeight { % <char> CharBoxHeight <num>\n" + | |
| 1418 " CharBox\n" + | |
| 1419 " exch pop sub neg exch pop\n" + | |
| 1420 "} bind def\n" + | |
| 1421 "\n" + | |
| 1422 "\n" + | |
| 1423 "% The width of a characters bounding box\n" + | |
| 1424 "\/CharBoxWidth { % <char> CharBoxHeight <num>\n" + | |
| 1425 " CharBox\n" + | |
| 1426 " pop exch pop sub neg \n" + | |
| 1427 "} bind def\n" + | |
| 1428 "\n" + | |
| 1429 "% Set the colour scheme to be faded to indicate trimming\n" + | |
| 1430 "\/MuteColour {\n" + | |
| 1431 " \/colorDict mutedColourDict def\n" + | |
| 1432 "} def\n" + | |
| 1433 "\n" + | |
| 1434 "% Restore the colour scheme to the normal colours\n" + | |
| 1435 "\/RestoreColour {\n" + | |
| 1436 " \/colorDict fullColourDict def\n" + | |
| 1437 "} def\n" + | |
| 1438 "\n" + | |
| 1439 "% Draw the background for a trimmed section\n" + | |
| 1440 "% takes the number of columns as a parameter\n" + | |
| 1441 "\/DrawTrimBg { % <num> DrawTrimBox\n" + | |
| 1442 " \/col exch def\n" + | |
| 1443 " \n" + | |
| 1444 " \/boxwidth \n" + | |
| 1445 " col stackWidth mul \n" + | |
| 1446 " def\n" + | |
| 1447 " \n" + | |
| 1448 " gsave\n" + | |
| 1449 " 0.97 setgray\n" + | |
| 1450 "\n" + | |
| 1451 " newpath\n" + | |
| 1452 " 0 0 moveto\n" + | |
| 1453 " boxwidth 0 rlineto\n" + | |
| 1454 " 0 yaxisHeight rlineto\n" + | |
| 1455 " 0 yaxisHeight lineto\n" + | |
| 1456 " closepath\n" + | |
| 1457 " \n" + | |
| 1458 " fill\n" + | |
| 1459 " grestore\n" + | |
| 1460 "} def\n" + | |
| 1461 "\n" + | |
| 1462 "\/DrawTrimEdge {\n" + | |
| 1463 " gsave\n" + | |
| 1464 " 0.2 setgray\n" + | |
| 1465 " [2] 0 setdash\n" + | |
| 1466 "\n" + | |
| 1467 " newpath\n" + | |
| 1468 " 0 0 moveto\n" + | |
| 1469 " 0 yaxisHeight lineto\n" + | |
| 1470 " \n" + | |
| 1471 " stroke\n" + | |
| 1472 "\n" + | |
| 1473 "} def\n" + | |
| 1474 "\n" + | |
| 1475 "\n" + | |
| 1476 "% Deprecated names\n" + | |
| 1477 "\/startstack {StartStack} bind def\n" + | |
| 1478 "\/endstack {EndStack} bind def\n" + | |
| 1479 "\/makenumber {MakeNumber} bind def\n" + | |
| 1480 "\/numchar { MakeSymbol } bind def\n" + | |
| 1481 "\n" + | |
| 1482 "%%EndProlog\n" + | |
| 1483 "\n" + | |
| 1484 "%%Page: 1 1\n" + | |
| 1485 "StartLogo\n" + | |
| 1486 "\n" + | |
| 1487 _input("DATA") + "\n" + | |
| 1488 "\n" + | |
| 1489 "EndLogo\n" + | |
| 1490 "\n" + | |
| 1491 "%%EOF\n" | |
| 1492 ); | |
| 1493 }</script> | |
| 1494 <script> | |
| 1495 //====================================================================== | |
| 1496 // start Alphabet object | |
| 1497 //====================================================================== | |
| 1498 var Alphabet = function(alphabet, background) { | |
| 1499 "use strict"; | |
| 1500 var i, j, sym, aliases, complement, comp_e_sym, ambigs, generate_background; | |
| 1501 generate_background = (background == null); | |
| 1502 if (generate_background) { | |
| 1503 background = []; | |
| 1504 for (i = 0; i < alphabet.ncore; i++) background[i] = 1.0 / alphabet.ncore; | |
| 1505 } else if (alphabet.ncore != background.length) { | |
| 1506 throw new Error("The background length does not match the alphabet length."); | |
| 1507 } | |
| 1508 this.name = alphabet.name; | |
| 1509 this.like = (alphabet.like != null ? alphabet.like.toUpperCase() : null); | |
| 1510 this.ncore = alphabet.ncore; | |
| 1511 this.symbols = alphabet.symbols; | |
| 1512 this.background = background; | |
| 1513 this.genbg = generate_background; | |
| 1514 this.encode = {}; | |
| 1515 this.encode2core = {}; | |
| 1516 this.complement = {}; | |
| 1517 // check if all symbols are same case | |
| 1518 var seen_uc = false; | |
| 1519 var seen_lc = false; | |
| 1520 var check_case = function (syms) { | |
| 1521 var s, sym; | |
| 1522 if (typeof syms === "string") { | |
| 1523 for (s = 0; s < syms.length; s++) { | |
| 1524 sym = syms.charAt(s); | |
| 1525 if (sym >= 'a' && sym <= 'z') seen_lc = true; | |
| 1526 else if (sym >= 'A' && sym <= 'Z') seen_uc = true; | |
| 1527 } | |
| 1528 } | |
| 1529 }; | |
| 1530 for (i = 0; i < this.symbols.length; i++) { | |
| 1531 check_case(this.symbols[i].symbol); | |
| 1532 check_case(this.symbols[i].aliases); | |
| 1533 } | |
| 1534 // now map symbols to indexes | |
| 1535 var update_array = function(array, syms, index) { | |
| 1536 var s, sym; | |
| 1537 if (typeof syms === "string") { | |
| 1538 for (s = 0; s < syms.length; s++) { | |
| 1539 sym = syms.charAt(s); | |
| 1540 array[sym] = index; | |
| 1541 // when only a single case is used, then encode as case insensitive | |
| 1542 if (seen_uc != seen_lc) { | |
| 1543 if (sym >= 'a' && sym <= 'z') { | |
| 1544 array[sym.toUpperCase()] = index; | |
| 1545 } else if (sym >= 'A' && sym <= 'Z') { | |
| 1546 array[sym.toLowerCase()] = index; | |
| 1547 } | |
| 1548 } | |
| 1549 } | |
| 1550 } | |
| 1551 } | |
| 1552 // map core symbols to index | |
| 1553 for (i = 0; i < this.ncore; i++) { | |
| 1554 update_array(this.encode2core, this.symbols[i].symbol, i); | |
| 1555 update_array(this.encode, this.symbols[i].symbol, i); | |
| 1556 update_array(this.encode2core, this.symbols[i].aliases, i); | |
| 1557 update_array(this.encode, this.symbols[i].aliases, i); | |
| 1558 } | |
| 1559 // map ambiguous symbols to index | |
| 1560 ambigs = {}; | |
| 1561 for (i = this.ncore; i < this.symbols.length; i++) { | |
| 1562 update_array(this.encode, this.symbols[i].symbol, i); | |
| 1563 update_array(this.encode, this.symbols[i].aliases, i); | |
| 1564 ambigs[this.symbols[i].equals] = i; | |
| 1565 } | |
| 1566 // determine complements | |
| 1567 for (i = 0; i < this.ncore; i++) { | |
| 1568 complement = this.symbols[i].complement; | |
| 1569 if (typeof complement === "string") { | |
| 1570 this.complement[i] = this.encode2core[complement]; | |
| 1571 } | |
| 1572 } | |
| 1573 next_symbol: | |
| 1574 for (i = this.ncore; i < this.symbols.length; i++) { | |
| 1575 complement = ""; | |
| 1576 for (j = 0; j < this.symbols[i].equals.length; j++) { | |
| 1577 comp_e_sym = this.complement[this.encode2core[this.symbols[i].equals.charAt(j)]]; | |
| 1578 if (typeof comp_e_sym !== "number") continue next_symbol; | |
| 1579 complement += this.symbols[comp_e_sym].symbol; | |
| 1580 } | |
| 1581 complement = complement.split("").sort().join(""); | |
| 1582 if (typeof ambigs[complement] === "number") { | |
| 1583 this.complement[i] = ambigs[complement]; | |
| 1584 } | |
| 1585 } | |
| 1586 // determine case insensitivity | |
| 1587 this.case_insensitive = true; | |
| 1588 if (seen_uc == seen_lc) { | |
| 1589 // when there is a mixture of cases it probably won't | |
| 1590 // be case insensitive but we still need to check | |
| 1591 loop: | |
| 1592 for (i = 0; i < this.symbols.length; i++) { | |
| 1593 sym = this.symbols[i].symbol; | |
| 1594 if (sym >= 'A' && sym <= 'Z') { | |
| 1595 if (this.encode[sym.toLowerCase()] != i) { | |
| 1596 this.case_insensitive = false; | |
| 1597 break loop; | |
| 1598 } | |
| 1599 } else if (sym >= 'a' && sym <= 'z') { | |
| 1600 if (this.encode[sym.toUpperCase()] != i) { | |
| 1601 this.case_insensitive = false; | |
| 1602 break loop; | |
| 1603 } | |
| 1604 } | |
| 1605 aliases = this.symbols[i].aliases; | |
| 1606 if (aliases != null) { | |
| 1607 for (j = 0; j < aliases.length; j++) { | |
| 1608 sym = aliases.charAt(j); | |
| 1609 if (sym >= 'A' && sym <= 'Z') { | |
| 1610 if (this.encode[sym.toLowerCase()] != i) { | |
| 1611 this.case_insensitive = false; | |
| 1612 break loop; | |
| 1613 } | |
| 1614 } else if (sym >= 'a' && sym <= 'z') { | |
| 1615 if (this.encode[sym.toUpperCase()] != i) { | |
| 1616 this.case_insensitive = false; | |
| 1617 break loop; | |
| 1618 } | |
| 1619 } | |
| 1620 } | |
| 1621 } | |
| 1622 } | |
| 1623 } | |
| 1624 // normalise aliases to remove the prime symbol and eliminate | |
| 1625 // the alternate cases when the alphabet is case insensitive | |
| 1626 var seen, out; | |
| 1627 for (i = 0; i < this.symbols.length; i++) { | |
| 1628 sym = this.symbols[i].symbol; | |
| 1629 aliases = this.symbols[i].aliases; | |
| 1630 if (typeof aliases != "string") aliases = ""; | |
| 1631 seen = {}; | |
| 1632 out = []; | |
| 1633 if (this.case_insensitive) { | |
| 1634 sym = sym.toUpperCase(); | |
| 1635 aliases = aliases.toUpperCase(); | |
| 1636 } | |
| 1637 seen[sym] = true; | |
| 1638 for (j = 0; j < aliases.length; j++) { | |
| 1639 if (!seen[aliases.charAt(j)]) { | |
| 1640 seen[aliases.charAt(j)] = true; | |
| 1641 out.push(aliases.charAt(j)); | |
| 1642 } | |
| 1643 } | |
| 1644 this.symbols[i].aliases = out.sort().join(""); | |
| 1645 } | |
| 1646 }; | |
| 1647 // return the name of the alphabet | |
| 1648 Alphabet.prototype.get_alphabet_name = function() { | |
| 1649 return this.name; | |
| 1650 }; | |
| 1651 // return if the alphabet can be complemented | |
| 1652 Alphabet.prototype.has_complement = function() { | |
| 1653 return (typeof this.symbols[0].complement === "string"); | |
| 1654 }; | |
| 1655 // return true if an uppercase letter has the same meaning as the lowercase form | |
| 1656 Alphabet.prototype.is_case_insensitive = function() { | |
| 1657 return this.case_insensitive; | |
| 1658 }; | |
| 1659 // return the information content of an alphabet letter | |
| 1660 Alphabet.prototype.get_ic = function() { | |
| 1661 return Math.log(this.ncore) / Math.LN2; | |
| 1662 }; | |
| 1663 // return the count of the core alphabet symbols | |
| 1664 Alphabet.prototype.get_size_core = function() { | |
| 1665 return this.ncore; | |
| 1666 }; | |
| 1667 // return the count of all alphabet symbols | |
| 1668 Alphabet.prototype.get_size_full = function() { | |
| 1669 return this.symbols.length; | |
| 1670 }; | |
| 1671 // return the symbol for the given alphabet index | |
| 1672 Alphabet.prototype.get_symbol = function(alph_index) { | |
| 1673 "use strict"; | |
| 1674 if (alph_index < 0 || alph_index >= this.symbols.length) { | |
| 1675 throw new Error("Alphabet index out of bounds"); | |
| 1676 } | |
| 1677 return this.symbols[alph_index].symbol; | |
| 1678 }; | |
| 1679 // return the aliases for the given alphabet index | |
| 1680 Alphabet.prototype.get_aliases = function(alph_index) { | |
| 1681 "use strict"; | |
| 1682 if (alph_index < 0 || alph_index >= this.symbols.length) { | |
| 1683 throw new Error("Alphabet index out of bounds"); | |
| 1684 } | |
| 1685 var sym_obj = this.symbols[alph_index]; | |
| 1686 return (sym_obj.aliases != null ? sym_obj.aliases : ""); | |
| 1687 }; | |
| 1688 // return the name for the given alphabet index | |
| 1689 Alphabet.prototype.get_name = function(alph_index) { | |
| 1690 "use strict"; | |
| 1691 var sym; | |
| 1692 if (alph_index < 0 || alph_index >= this.symbols.length) { | |
| 1693 throw new Error("Alphabet index out of bounds"); | |
| 1694 } | |
| 1695 sym = this.symbols[alph_index]; | |
| 1696 return (typeof sym.name === "string" ? sym.name : sym.symbol); | |
| 1697 }; | |
| 1698 // return the alphabet it is like or null | |
| 1699 Alphabet.prototype.get_like = function() { | |
| 1700 "use strict"; | |
| 1701 return this.like; | |
| 1702 }; | |
| 1703 // return the index of the complement for the given alphabet index | |
| 1704 Alphabet.prototype.get_complement = function(alph_index) { | |
| 1705 var comp_e_sym = this.complement[alph_index]; | |
| 1706 if (typeof comp_e_sym === "number") { | |
| 1707 return comp_e_sym; | |
| 1708 } else { | |
| 1709 return -1; | |
| 1710 } | |
| 1711 }; | |
| 1712 // return a string containing the core symbols | |
| 1713 Alphabet.prototype.get_symbols = function() { | |
| 1714 "use strict"; | |
| 1715 var i, core_symbols; | |
| 1716 core_symbols = ""; | |
| 1717 for (i = 0; i < this.ncore; i++) { | |
| 1718 core_symbols += this.symbols[i].symbol; | |
| 1719 } | |
| 1720 return core_symbols; | |
| 1721 }; | |
| 1722 // return if the background was not a uniform generated background | |
| 1723 Alphabet.prototype.has_bg = function() { | |
| 1724 "use strict"; | |
| 1725 return !this.genbg; | |
| 1726 }; | |
| 1727 // get the background frequency for the index | |
| 1728 Alphabet.prototype.get_bg_freq = function(alph_index) { | |
| 1729 "use strict"; | |
| 1730 var freq, i, symbols; | |
| 1731 if (alph_index >= 0) { | |
| 1732 if (alph_index < this.ncore) { | |
| 1733 return this.background[alph_index]; | |
| 1734 } else if (alph_index < this.symbols.length) { | |
| 1735 freq = 0; | |
| 1736 symbols = this.symbols[alph_index].equals; | |
| 1737 for (i = 0; i < symbols.length; i++) { | |
| 1738 freq += this.background[this.encode2core[symbols.charAt(i)]]; | |
| 1739 } | |
| 1740 return freq; | |
| 1741 } | |
| 1742 } | |
| 1743 throw new Error("The alphabet index is out of range."); | |
| 1744 }; | |
| 1745 // get the colour of the index | |
| 1746 Alphabet.prototype.get_colour = function(alph_index) { | |
| 1747 "use strict"; | |
| 1748 if (alph_index < 0 || alph_index >= this.symbols.length) { | |
| 1749 throw new Error("BAD_ALPHABET_INDEX"); | |
| 1750 } | |
| 1751 if (typeof this.symbols[alph_index].colour != "string") { | |
| 1752 return "black"; | |
| 1753 } | |
| 1754 return "#" + this.symbols[alph_index].colour; | |
| 1755 }; | |
| 1756 // get the rgb components of the colour at the index | |
| 1757 Alphabet.prototype.get_rgb = function(alph_index) { | |
| 1758 "use strict"; | |
| 1759 if (alph_index < 0 || alph_index >= this.symbols.length) { | |
| 1760 throw new Error("BAD_ALPHABET_INDEX"); | |
| 1761 } | |
| 1762 if (typeof this.symbols[alph_index].colour != "string") { | |
| 1763 return {"red": 0, "green": 0, "blue": 0}; | |
| 1764 } | |
| 1765 var colour = this.symbols[alph_index].colour; | |
| 1766 var red = parseInt(colour.substr(0, 2), 16) / 255; | |
| 1767 var green = parseInt(colour.substr(2, 2), 16) / 255; | |
| 1768 var blue = parseInt(colour.substr(4, 2), 16) / 255; | |
| 1769 return {"red": red, "green": green, "blue": blue}; | |
| 1770 }; | |
| 1771 // convert a symbol into the index | |
| 1772 Alphabet.prototype.get_index = function(letter) { | |
| 1773 "use strict"; | |
| 1774 var alph_index; | |
| 1775 alph_index = this.encode[letter]; | |
| 1776 if (typeof alph_index === "undefined") { | |
| 1777 return -1; | |
| 1778 } | |
| 1779 return alph_index; | |
| 1780 }; | |
| 1781 // convert a symbol into the list of core indexes that it equals | |
| 1782 Alphabet.prototype.get_indexes = function(letter) { | |
| 1783 "use strict"; | |
| 1784 var alph_index, comprise_str, i, comprise_list; | |
| 1785 alph_index = this.encode[letter]; | |
| 1786 if (typeof alph_index === "undefined") { | |
| 1787 throw new Error("Unknown letter"); | |
| 1788 } | |
| 1789 comprise_str = this.symbols[alph_index].equals; | |
| 1790 comprise_list = []; | |
| 1791 if (typeof comprise_str == "string") { | |
| 1792 for (i = 0; i < comprise_str.length; i++) { | |
| 1793 comprise_list.push(this.encode2core[comprise_str.charAt(i)]); | |
| 1794 } | |
| 1795 } else { | |
| 1796 comprise_list.push(alph_index); | |
| 1797 } | |
| 1798 return comprise_list; | |
| 1799 }; | |
| 1800 // check if a symbol is the primary way of representing the symbol in the alphabet | |
| 1801 Alphabet.prototype.is_prime_symbol = function(letter) { | |
| 1802 var alph_index; | |
| 1803 alph_index = this.encode[letter]; | |
| 1804 if (alph_index == null) return false; | |
| 1805 if (this.is_case_insensitive()) { | |
| 1806 return (this.symbols[alph_index].symbol.toUpperCase() == letter.toUpperCase()); | |
| 1807 } else { | |
| 1808 return (this.symbols[alph_index].symbol == letter); | |
| 1809 } | |
| 1810 }; | |
| 1811 // compare 2 alphabets | |
| 1812 Alphabet.prototype.equals = function(other) { | |
| 1813 "use strict"; | |
| 1814 var i, sym1, sym2; | |
| 1815 // first check that it's actually an alphabet object | |
| 1816 if (!(typeof other === "object" && other != null && other instanceof Alphabet)) { | |
| 1817 return false; | |
| 1818 } | |
| 1819 // second shortcircuit if it's the same object | |
| 1820 if (this === other) return true; | |
| 1821 // compare | |
| 1822 if (this.name !== other.name) return false; | |
| 1823 if (this.ncore !== other.ncore) return false; | |
| 1824 if (this.symbols.length !== other.symbols.length) return false; | |
| 1825 for (i = 0; i < this.symbols.length; i++) { | |
| 1826 sym1 = this.symbols[i]; | |
| 1827 sym2 = other.symbols[i]; | |
| 1828 if (sym1.symbol !== sym2.symbol) return false; | |
| 1829 if (sym1.aliases !== sym2.aliases) return false; | |
| 1830 if (sym1.name !== sym2.name) return false; | |
| 1831 if (typeof sym1.colour !== typeof sym2.colour || | |
| 1832 (typeof sym1.colour === "string" && typeof sym2.colour === "string" && | |
| 1833 parseInt(sym1.colour, 16) != parseInt(sym2.colour, 16))) { | |
| 1834 return false; | |
| 1835 } | |
| 1836 if (sym1.complement !== sym2.complement) return false; | |
| 1837 if (sym1.equals !== sym2.equals) return false; | |
| 1838 } | |
| 1839 return true; | |
| 1840 }; | |
| 1841 Alphabet.prototype.check_core_subset = function(super_alph) { | |
| 1842 var complement_same = true; | |
| 1843 var seen_set = {}; | |
| 1844 var sub_i, sub_symbol, super_i, super_symbol; | |
| 1845 for (sub_i = 0; sub_i < this.ncore; sub_i++) { | |
| 1846 sub_symbol = this.symbols[sub_i]; | |
| 1847 super_i = super_alph.encode[sub_symbol.symbol]; | |
| 1848 if (super_i == null) return 0; | |
| 1849 super_symbol = super_alph.symbols[super_i]; | |
| 1850 if (seen_set[super_i]) return 0; | |
| 1851 seen_set[super_i] = true; | |
| 1852 // check complement | |
| 1853 if (sub_symbol.complement != null && super_symbol.complement != null) { | |
| 1854 if (super_alph.encode[sub_symbol.complement] != super_alph.encode[super_symbol.complement]) { | |
| 1855 complement_same = false; | |
| 1856 } | |
| 1857 } else if (sub_symbol.complement != null || super_symbol.complement != null) { | |
| 1858 complement_same = false; | |
| 1859 } | |
| 1860 } | |
| 1861 return (complement_same ? 1 : -1); | |
| 1862 }; | |
| 1863 // convert a sequence to its reverse complement | |
| 1864 Alphabet.prototype.invcomp_seq = function(seq) { | |
| 1865 "use strict"; | |
| 1866 var syms, i, e_sym, comp_e_sym; | |
| 1867 if (!this.has_complement()) throw new Error("Alphabet must be complementable"); | |
| 1868 syms = seq.split(""); | |
| 1869 for (i = 0; i < syms.length; i++) { | |
| 1870 e_sym = this.encode[syms[i]]; | |
| 1871 if (typeof e_sym === "undefined") { | |
| 1872 e_sym = this.ncore; // wildcard | |
| 1873 } | |
| 1874 comp_e_sym = this.complement[e_sym]; | |
| 1875 if (typeof comp_e_sym === "undefined") { | |
| 1876 comp_e_sym = e_sym; // not complementable | |
| 1877 } | |
| 1878 syms[i] = this.symbols[comp_e_sym].symbol; | |
| 1879 } | |
| 1880 return syms.reverse().join(""); | |
| 1881 }; | |
| 1882 // convert the alphabet to the text version | |
| 1883 Alphabet.prototype.as_text = function() { | |
| 1884 "use strict"; | |
| 1885 function name_as_text(name) { | |
| 1886 var i, c, out; | |
| 1887 out = "\""; | |
| 1888 for (i = 0; i < name.length; i++) { | |
| 1889 c = name.charAt(i); | |
| 1890 if (c == "\"") { | |
| 1891 out += "\\\""; | |
| 1892 } else if (c == "/") { | |
| 1893 out += "\\/"; | |
| 1894 } else if (c == "\\") { | |
| 1895 out += "\\\\"; | |
| 1896 } else { | |
| 1897 out += c; | |
| 1898 } | |
| 1899 } | |
| 1900 out += "\""; | |
| 1901 return out; | |
| 1902 } | |
| 1903 function symbol_as_text(sym) { | |
| 1904 var out; | |
| 1905 out = sym.symbol; | |
| 1906 if (typeof sym.name === "string" && sym.name != sym.symbol) { | |
| 1907 out += " " + name_as_text(sym.name); | |
| 1908 } | |
| 1909 if (typeof sym.colour === "string") { | |
| 1910 out += " " + sym.colour; | |
| 1911 } | |
| 1912 return out; | |
| 1913 } | |
| 1914 var out, i, j, c, sym; | |
| 1915 out = ""; | |
| 1916 // output core symbols with 2 way complements | |
| 1917 for (i = 0; i < this.ncore; i++) { | |
| 1918 c = this.complement[i]; | |
| 1919 if (typeof c === "number" && i < c && this.complement[c] === i) { | |
| 1920 out += symbol_as_text(this.symbols[i]) + " ~ " + symbol_as_text(this.symbols[c]) + "\n"; | |
| 1921 } | |
| 1922 } | |
| 1923 // output core symbols with no complement | |
| 1924 for (i = 0; i < this.ncore; i++) { | |
| 1925 if (typeof this.complement[i] === "undefined") { | |
| 1926 out += symbol_as_text(this.symbols[i]) + "\n"; | |
| 1927 } | |
| 1928 } | |
| 1929 // output ambiguous symbols that have comprising characters | |
| 1930 for (i = this.ncore; i < this.symbols.length; i++) { | |
| 1931 if (this.symbols[i].equals.length == 0) break; | |
| 1932 out += symbol_as_text(this.symbols[i]) + " = " + this.symbols[i].equals + "\n"; | |
| 1933 if (typeof this.symbols[i].aliases === "string") { | |
| 1934 for (j = 0; j < this.symbols[i].aliases.length; j++) { | |
| 1935 if (this.symbols[i].aliases.charAt(j) == this.symbols[i].symbol) continue; | |
| 1936 out += this.symbols[i].aliases.charAt(j) + " = " + this.symbols[i].equals + "\n"; | |
| 1937 } | |
| 1938 } | |
| 1939 } | |
| 1940 // output aliases of core symbols | |
| 1941 for (i = 0; i < this.ncore; i++) { | |
| 1942 if (typeof this.symbols[i].aliases === "string") { | |
| 1943 for (j = 0; j < this.symbols[i].aliases.length; j++) { | |
| 1944 if (this.symbols[i].aliases.charAt(j) == this.symbols[i].symbol) continue; | |
| 1945 out += this.symbols[i].aliases.charAt(j) + " = " + this.symbols[i].symbol + "\n"; | |
| 1946 } | |
| 1947 } | |
| 1948 } | |
| 1949 // output gap symbols | |
| 1950 i = this.symbols.length - 1; | |
| 1951 if (this.symbols[i].equals.length == 0) { | |
| 1952 out += symbol_as_text(this.symbols[i]) + " =\n"; | |
| 1953 if (typeof this.symbols[i].aliases === "string") { | |
| 1954 for (j = 0; j < this.symbols[i].aliases.length; j++) { | |
| 1955 if (this.symbols[i].aliases.charAt(j) == this.symbols[i].symbol) continue; | |
| 1956 out += this.symbols[i].aliases.charAt(j) + " =\n"; | |
| 1957 } | |
| 1958 } | |
| 1959 } | |
| 1960 return out; | |
| 1961 }; | |
| 1962 // output the alphabet as it appears in minimal MEME format | |
| 1963 Alphabet.prototype.as_meme = function() { | |
| 1964 "use strict"; | |
| 1965 function name_as_text(name) { | |
| 1966 var i, c, out; | |
| 1967 out = "\""; | |
| 1968 for (i = 0; i < name.length; i++) { | |
| 1969 c = name.charAt(i); | |
| 1970 if (c == "\"") { | |
| 1971 out += "\\\""; | |
| 1972 } else if (c == "/") { | |
| 1973 out += "\\/"; | |
| 1974 } else if (c == "\\") { | |
| 1975 out += "\\\\"; | |
| 1976 } else { | |
| 1977 out += c; | |
| 1978 } | |
| 1979 } | |
| 1980 out += "\""; | |
| 1981 return out; | |
| 1982 } | |
| 1983 if (this.equals(AlphStd.DNA)) { | |
| 1984 return "ALPHABET= ACGT\n"; | |
| 1985 } else if (this.equals(AlphStd.PROTEIN)) { | |
| 1986 return "ALPHABET= ACDEFGHIKLMNPQRSTVWY\n"; | |
| 1987 } else { | |
| 1988 return "ALPHABET" + | |
| 1989 (this.name != null ? " " + name_as_text(this.name) : "") + | |
| 1990 (this.like != null ? " " + this.like + "-LIKE" : "") + "\n" + | |
| 1991 this.as_text() + "END ALPHABET\n"; | |
| 1992 } | |
| 1993 }; | |
| 1994 | |
| 1995 // Returns a table showing all the letters in the alphabet | |
| 1996 Alphabet.prototype.as_table = function() { | |
| 1997 "use strict"; | |
| 1998 var i, j, row, th, td, aliases, equals, sym; | |
| 1999 var table = document.createElement("table"); | |
| 2000 // create the core symbol header | |
| 2001 row = table.insertRow(table.rows.length); | |
| 2002 th = document.createElement("th"); | |
| 2003 th.appendChild(document.createTextNode("Symbol(s)")); | |
| 2004 row.appendChild(th); | |
| 2005 th = document.createElement("th"); | |
| 2006 th.appendChild(document.createTextNode("Name")); | |
| 2007 row.appendChild(th); | |
| 2008 th = document.createElement("th"); | |
| 2009 if (this.has_complement()) { | |
| 2010 th.appendChild(document.createTextNode("Complement")); | |
| 2011 } | |
| 2012 row.appendChild(th); | |
| 2013 // list the core symbols | |
| 2014 for (i = 0; i < this.ncore; i++) { | |
| 2015 row = table.insertRow(table.rows.length); | |
| 2016 td = document.createElement("td"); | |
| 2017 if (this.symbols[i].colour != null) { | |
| 2018 td.style.color = '#' + this.symbols[i].colour; | |
| 2019 } | |
| 2020 td.appendChild(document.createTextNode(this.symbols[i].symbol)); | |
| 2021 aliases = this.get_aliases(i); | |
| 2022 if (aliases.length > 0) { | |
| 2023 td.appendChild(document.createTextNode(' ' + aliases.split('').join(' '))); | |
| 2024 } | |
| 2025 row.appendChild(td); | |
| 2026 td = document.createElement("td"); | |
| 2027 if (this.symbols[i].name != null) { | |
| 2028 td.appendChild(document.createTextNode(this.symbols[i].name)); | |
| 2029 } | |
| 2030 row.appendChild(td); | |
| 2031 td = document.createElement("td"); | |
| 2032 if (this.symbols[i].complement != null) { | |
| 2033 td.style.color = this.get_colour(this.get_index(this.symbols[i].complement)); | |
| 2034 td.appendChild(document.createTextNode(this.symbols[i].complement)); | |
| 2035 } | |
| 2036 row.appendChild(td); | |
| 2037 } | |
| 2038 // create the ambiguous symbol header | |
| 2039 row = table.insertRow(table.rows.length); | |
| 2040 th = document.createElement("th"); | |
| 2041 th.appendChild(document.createTextNode("Symbol(s)")); | |
| 2042 row.appendChild(th); | |
| 2043 th = document.createElement("th"); | |
| 2044 th.appendChild(document.createTextNode("Name")); | |
| 2045 row.appendChild(th); | |
| 2046 th = document.createElement("th"); | |
| 2047 th.appendChild(document.createTextNode("Matches")); | |
| 2048 row.appendChild(th); | |
| 2049 // list the ambiguous symbols | |
| 2050 for (i = this.ncore; i < this.symbols.length; i++) { | |
| 2051 row = table.insertRow(table.rows.length); | |
| 2052 td = document.createElement("td"); | |
| 2053 if (this.symbols[i].colour != null) { | |
| 2054 td.style.color = '#' + this.symbols[i].colour; | |
| 2055 } | |
| 2056 td.appendChild(document.createTextNode(this.symbols[i].symbol)); | |
| 2057 aliases = this.get_aliases(i); | |
| 2058 if (aliases.length > 0) { | |
| 2059 td.appendChild(document.createTextNode(' ' + aliases.split('').join(' '))); | |
| 2060 } | |
| 2061 row.appendChild(td); | |
| 2062 td = document.createElement("td"); | |
| 2063 if (this.symbols[i].name != null) { | |
| 2064 td.appendChild(document.createTextNode(this.symbols[i].name)); | |
| 2065 } | |
| 2066 row.appendChild(td); | |
| 2067 td = document.createElement("td"); | |
| 2068 equals = this.symbols[i].equals.split(''); | |
| 2069 for (j = 0; j < equals.length; j++) { | |
| 2070 if (j != 0) td.appendChild(document.createTextNode(' ')); | |
| 2071 sym = document.createElement("span"); | |
| 2072 sym.style.color = this.get_colour(this.get_index(equals[j])); | |
| 2073 sym.appendChild(document.createTextNode(equals[j])); | |
| 2074 td.appendChild(sym); | |
| 2075 } | |
| 2076 row.appendChild(td); | |
| 2077 } | |
| 2078 return table; | |
| 2079 }; | |
| 2080 | |
| 2081 // returns a dictionary of the colours for EPS | |
| 2082 Alphabet.prototype._as_eps_dict = function() { | |
| 2083 "use strict"; | |
| 2084 var i, sym, rgb; | |
| 2085 var out = "/fullColourDict <<\n"; | |
| 2086 for (i = 0; i < this.ncore; i++) { | |
| 2087 sym = this.get_symbol(i); | |
| 2088 sym = sym.replace(/\\/g, "\\\\"); | |
| 2089 sym = sym.replace(/\(/g, "\\("); | |
| 2090 sym = sym.replace(/\)/g, "\\)"); | |
| 2091 rgb = this.get_rgb(i); | |
| 2092 out += " (" + sym + ") [" + rgb.red.toFixed(4) + " " + rgb.green.toFixed(4) + " " + rgb.blue.toFixed(4) + "]\n"; | |
| 2093 } | |
| 2094 out += ">> def\n"; | |
| 2095 out += "/mutedColourDict <<\n"; | |
| 2096 for (i = 0; i < this.ncore; i++) { | |
| 2097 sym = this.get_symbol(i); | |
| 2098 sym = sym.replace(/\\/g, "\\\\"); | |
| 2099 sym = sym.replace(/\(/g, "\\("); | |
| 2100 sym = sym.replace(/\)/g, "\\)"); | |
| 2101 rgb = Alphabet.lighten_colour(this.get_rgb(i)); | |
| 2102 out += " (" + sym + ") [" + rgb.red.toFixed(4) + " " + rgb.green.toFixed(4) + " " + rgb.blue.toFixed(4) + "]\n"; | |
| 2103 } | |
| 2104 out += ">> def\n"; | |
| 2105 return out; | |
| 2106 }; | |
| 2107 | |
| 2108 // return the alphabet name or a list of primary symbols | |
| 2109 Alphabet.prototype.toString = function() { | |
| 2110 "use strict"; | |
| 2111 if (this.name != null) { | |
| 2112 return this.name; | |
| 2113 } else { | |
| 2114 return this.get_symbols(); | |
| 2115 } | |
| 2116 }; | |
| 2117 | |
| 2118 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| 2119 // Helper functions | |
| 2120 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| 2121 | |
| 2122 // Convert a colour specified in RGB colourspace values into LAB colourspace | |
| 2123 Alphabet.rgb2lab = function(rgb) { | |
| 2124 "use strict"; | |
| 2125 var xyzHelper, labHelper; | |
| 2126 // XYZ helper | |
| 2127 xyzHelper = function(value) { | |
| 2128 if (value > 0.0445) { | |
| 2129 value = (value + 0.055) / 1.055; | |
| 2130 value = Math.pow(value, 2.4); | |
| 2131 } else { | |
| 2132 value /= 12.92; | |
| 2133 } | |
| 2134 value *= 100; | |
| 2135 return value; | |
| 2136 }; | |
| 2137 // lab helper | |
| 2138 labHelper = function(value) { | |
| 2139 if (value > 0.008856) { | |
| 2140 value = Math.pow(value, 1.0 / 3.0); | |
| 2141 } else { | |
| 2142 value = (7.787 * value) + (16.0 / 116.0); | |
| 2143 } | |
| 2144 return value; | |
| 2145 }; | |
| 2146 // convert into XYZ colourspace | |
| 2147 var c1, c2, c3; | |
| 2148 if (typeof rgb == "number") { | |
| 2149 c1 = xyzHelper(((rgb >> 16) & 0xFF) / 255.0); | |
| 2150 c2 = xyzHelper(((rgb >> 8) & 0xFF) / 255.0); | |
| 2151 c3 = xyzHelper((rgb & 0xFF) / 255.0); | |
| 2152 } else { | |
| 2153 c1 = xyzHelper(rgb.red); | |
| 2154 c2 = xyzHelper(rgb.green); | |
| 2155 c3 = xyzHelper(rgb.blue); | |
| 2156 } | |
| 2157 var x = (c1 * 0.4124) + (c2 * 0.3576) + (c3 * 0.1805); | |
| 2158 var y = (c1 * 0.2126) + (c2 * 0.7152) + (c3 * 0.0722); | |
| 2159 var z = (c1 * 0.0193) + (c2 * 0.1192) + (c3 * 0.9505); | |
| 2160 // convert into Lab colourspace | |
| 2161 c1 = labHelper(x / 95.047); | |
| 2162 c2 = labHelper(y / 100.0); | |
| 2163 c3 = labHelper(z / 108.883); | |
| 2164 var l = (116.0 * c2) - 16; | |
| 2165 var a = 500.0 * (c1 - c2); | |
| 2166 var b = 200.0 * (c2 - c3); | |
| 2167 return {"l": l, "a": a, "b": b}; | |
| 2168 }; | |
| 2169 | |
| 2170 // Convert a colour specified in HSV colourspace into RGB colourspace | |
| 2171 Alphabet.hsv2rgb = function(hue, sat, value, output_object) { | |
| 2172 // achromatic (grey) | |
| 2173 var r = value; | |
| 2174 var g = value; | |
| 2175 var b = value; | |
| 2176 if (sat != 0) { | |
| 2177 var h = hue / 60.0; | |
| 2178 var i = Math.floor(h); | |
| 2179 var f = h - i; | |
| 2180 var p = value * (1.0 - sat); | |
| 2181 var q = value * (1.0 - (sat * f)); | |
| 2182 var t = value * (1.0 - (sat * (1.0 - f))); | |
| 2183 if (i == 0) { | |
| 2184 r = value; | |
| 2185 g = t; | |
| 2186 b = p; | |
| 2187 } else if (i == 1) { | |
| 2188 r = q; | |
| 2189 g = value; | |
| 2190 b = p; | |
| 2191 } else if (i == 2) { | |
| 2192 r = p; | |
| 2193 g = value; | |
| 2194 b = t; | |
| 2195 } else if (i == 3) { | |
| 2196 r = p; | |
| 2197 g = q; | |
| 2198 b = value; | |
| 2199 } else if (i == 4) { | |
| 2200 r = t; | |
| 2201 g = p; | |
| 2202 b = value; | |
| 2203 } else { | |
| 2204 r = value; | |
| 2205 g = p; | |
| 2206 b = q; | |
| 2207 } | |
| 2208 } | |
| 2209 if (output_object) { | |
| 2210 return {"red": r, "green": g, "blue": b}; | |
| 2211 } else { | |
| 2212 return (Math.floor(r * 255) << 15) | (Math.floor(g * 255) << 8) | (Math.floor(b * 255)); | |
| 2213 } | |
| 2214 }; | |
| 2215 | |
| 2216 // Calculate a distance score between two colours in LAB colourspace | |
| 2217 Alphabet.lab_dist = function(lab1, lab2) { | |
| 2218 var c1 = Math.sqrt((lab1.l * lab1.l) + (lab1.a * lab1.a)); | |
| 2219 var c2 = Math.sqrt((lab2.l * lab2.l) + (lab2.a * lab2.a)); | |
| 2220 var dc = c1 - c2; | |
| 2221 var dl = lab1.l - lab2.l; | |
| 2222 var da = lab1.a - lab2.a; | |
| 2223 var db = lab1.b - lab2.b; | |
| 2224 // we don't want NaN due to rounding errors so fudge things a bit... | |
| 2225 var dh = 0; | |
| 2226 var dh_squared = (da * da) + (db * db) - (dc * dc); | |
| 2227 if (dh_squared > 0) { | |
| 2228 dh = Math.sqrt(dh_squared); | |
| 2229 } | |
| 2230 var first = dl; | |
| 2231 var second = dc / (1.0 + (0.045 * c1)); | |
| 2232 var third = dh / (1.0 + (0.015 * c1)); | |
| 2233 return Math.sqrt((first * first) + (second * second) + (third * third)); | |
| 2234 }; | |
| 2235 | |
| 2236 // convert an RGB value into a HSL value | |
| 2237 Alphabet.rgb2hsl = function(rgb) { | |
| 2238 "use strict"; | |
| 2239 var min, max, delta, h, s, l, r, g, b; | |
| 2240 if (typeof rgb == "number") { | |
| 2241 r = ((rgb >> 16) & 0xFF) / 255.0; | |
| 2242 g = ((rgb >> 8) & 0xFF) / 255.0; | |
| 2243 b = (rgb & 0xFF) / 255.0; | |
| 2244 } else { | |
| 2245 r = rgb.red; | |
| 2246 g = rgb.green; | |
| 2247 b = rgb.blue; | |
| 2248 } | |
| 2249 min = Math.min(r, g, b); | |
| 2250 max = Math.max(r, g, b); | |
| 2251 delta = max - min; | |
| 2252 l = min + (delta / 2); | |
| 2253 if (max == min) { | |
| 2254 h = 0; // achromatic (grayscale) | |
| 2255 s = 0; | |
| 2256 } else { | |
| 2257 if (l > 0.5) { | |
| 2258 s = delta / (2 - max - min); | |
| 2259 } else { | |
| 2260 s = delta / (max + min); | |
| 2261 } | |
| 2262 if (max == r) { | |
| 2263 h = (g - b) / delta; | |
| 2264 if (g < b) h += 6; | |
| 2265 } else if (max == g) { | |
| 2266 h = ((b - r) / delta) + 2; | |
| 2267 } else { | |
| 2268 h = ((r - g) / delta) + 4; | |
| 2269 } | |
| 2270 h /= 6; | |
| 2271 } | |
| 2272 return {"h": h, "s": s, "l": l}; | |
| 2273 }; | |
| 2274 | |
| 2275 // convert a HSL value into an RGB value | |
| 2276 Alphabet.hsl2rgb = function(hsl, output_object) { | |
| 2277 "use strict"; | |
| 2278 function _hue(p, q, t) { | |
| 2279 "use strict"; | |
| 2280 if (t < 0) t += 1; | |
| 2281 else if (t > 1) t -= 1; | |
| 2282 if (t < (1.0 / 6.0)) { | |
| 2283 return p + ((q - p) * 6.0 * t); | |
| 2284 } else if (t < 0.5) { | |
| 2285 return q; | |
| 2286 } else if (t < (2.0 / 3.0)) { | |
| 2287 return p + ((q - p) * ((2.0 / 3.0) - t) * 6.0); | |
| 2288 } else { | |
| 2289 return p; | |
| 2290 } | |
| 2291 } | |
| 2292 var r, g, b, p, q; | |
| 2293 if (hsl.s == 0) { | |
| 2294 // achromatic (grayscale) | |
| 2295 r = hsl.l; | |
| 2296 g = hsl.l; | |
| 2297 b = hsl.l; | |
| 2298 } else { | |
| 2299 if (hsl.l < 0.5) { | |
| 2300 q = hsl.l * (1 + hsl.s); | |
| 2301 } else { | |
| 2302 q = hsl.l + hsl.s - (hsl.l * hsl.s); | |
| 2303 } | |
| 2304 p = (2 * hsl.l) - q; | |
| 2305 r = _hue(p, q, hsl.h + (1.0 / 3.0)); | |
| 2306 g = _hue(p, q, hsl.h); | |
| 2307 b = _hue(p, q, hsl.h - (1.0 / 3.0)); | |
| 2308 } | |
| 2309 if (output_object) { | |
| 2310 return {"red": r, "green": g, "blue": b}; | |
| 2311 } else { | |
| 2312 return (Math.floor(r * 255) << 15) | (Math.floor(g * 255) << 8) | (Math.floor(b * 255)); | |
| 2313 } | |
| 2314 }; | |
| 2315 | |
| 2316 Alphabet.lighten_colour = function(rgb) { | |
| 2317 "use strict"; | |
| 2318 var hsl = Alphabet.rgb2hsl(rgb); | |
| 2319 hsl.l += (1.0 - hsl.l) * 2 / 3; | |
| 2320 return Alphabet.hsl2rgb(hsl, typeof rgb != "number"); | |
| 2321 }; | |
| 2322 | |
| 2323 //====================================================================== | |
| 2324 // end Alphabet object | |
| 2325 //====================================================================== | |
| 2326 | |
| 2327 //====================================================================== | |
| 2328 // start StandardAlphabet object | |
| 2329 //====================================================================== | |
| 2330 | |
| 2331 // an extension of the alphabet object to support some additional fields | |
| 2332 // only present in standard alphabets. | |
| 2333 var StandardAlphabet = function(enum_code, enum_name, alphabet_data) { | |
| 2334 Alphabet.apply(this, [alphabet_data]); | |
| 2335 this.enum_code = enum_code; | |
| 2336 this.enum_name = enum_name; | |
| 2337 }; | |
| 2338 StandardAlphabet.prototype = Alphabet.prototype; | |
| 2339 StandardAlphabet.prototype.constructor = StandardAlphabet; | |
| 2340 | |
| 2341 // A unique code for this standard alphabet. | |
| 2342 // This code will be a power of 2 to enable creation of bitsets for | |
| 2343 // a selection of standard alphabets. | |
| 2344 StandardAlphabet.prototype.get_code = function() { | |
| 2345 return this.enum_code; | |
| 2346 }; | |
| 2347 | |
| 2348 // A unique name for this standard alphabet. | |
| 2349 // this name will be all upper case and the same as the property that | |
| 2350 // refers to this alphabet in the AlphStd collection. | |
| 2351 StandardAlphabet.prototype.get_enum = function() { | |
| 2352 return this.enum_name; | |
| 2353 }; | |
| 2354 | |
| 2355 //====================================================================== | |
| 2356 // end StandardAlphabet object | |
| 2357 //====================================================================== | |
| 2358 | |
| 2359 // A collection of standard alphabets. | |
| 2360 var AlphStd = { | |
| 2361 RNA: new StandardAlphabet(1, "RNA", { | |
| 2362 "name": "RNA", | |
| 2363 "like": "RNA", | |
| 2364 "ncore": 4, | |
| 2365 "symbols": [ | |
| 2366 {"symbol": "A", "name": "Adenine", "colour": "CC0000"}, | |
| 2367 {"symbol": "C", "name": "Cytosine", "colour": "0000CC"}, | |
| 2368 {"symbol": "G", "name": "Guanine", "colour": "FFB300"}, | |
| 2369 {"symbol": "U", "name": "Uracil", "colour": "008000", | |
| 2370 "aliases": "T"}, | |
| 2371 {"symbol": "N", "name": "Any base", "equals": "ACGU", "aliases": "X."}, | |
| 2372 {"symbol": "V", "name": "Not U", "equals": "ACG"}, | |
| 2373 {"symbol": "H", "name": "Not G", "equals": "ACU"}, | |
| 2374 {"symbol": "D", "name": "Not C", "equals": "AGU"}, | |
| 2375 {"symbol": "B", "name": "Not A", "equals": "CGU"}, | |
| 2376 {"symbol": "M", "name": "Amino", "equals": "AC"}, | |
| 2377 {"symbol": "R", "name": "Purine", "equals": "AG"}, | |
| 2378 {"symbol": "W", "name": "Weak", "equals": "AU"}, | |
| 2379 {"symbol": "S", "name": "Strong", "equals": "CG"}, | |
| 2380 {"symbol": "Y", "name": "Pyrimidine", "equals": "CU"}, | |
| 2381 {"symbol": "K", "name": "Keto", "equals": "GU"} | |
| 2382 ] | |
| 2383 }), | |
| 2384 DNA: new StandardAlphabet(2, "DNA", { | |
| 2385 "name": "DNA", | |
| 2386 "like": "DNA", | |
| 2387 "ncore": 4, | |
| 2388 "symbols": [ | |
| 2389 {"symbol": "A", "name": "Adenine", "colour": "CC0000", "complement": "T"}, | |
| 2390 {"symbol": "C", "name": "Cytosine", "colour": "0000CC", "complement": "G"}, | |
| 2391 {"symbol": "G", "name": "Guanine", "colour": "FFB300", "complement": "C"}, | |
| 2392 {"symbol": "T", "name": "Thymine", "colour": "008000", "complement": "A", | |
| 2393 "aliases": "U"}, | |
| 2394 {"symbol": "N", "name": "Any base", "equals": "ACGT", "aliases": "X."}, | |
| 2395 {"symbol": "V", "name": "Not T", "equals": "ACG"}, | |
| 2396 {"symbol": "H", "name": "Not G", "equals": "ACT"}, | |
| 2397 {"symbol": "D", "name": "Not C", "equals": "AGT"}, | |
| 2398 {"symbol": "B", "name": "Not A", "equals": "CGT"}, | |
| 2399 {"symbol": "M", "name": "Amino", "equals": "AC"}, | |
| 2400 {"symbol": "R", "name": "Purine", "equals": "AG"}, | |
| 2401 {"symbol": "W", "name": "Weak", "equals": "AT"}, | |
| 2402 {"symbol": "S", "name": "Strong", "equals": "CG"}, | |
| 2403 {"symbol": "Y", "name": "Pyrimidine", "equals": "CT"}, | |
| 2404 {"symbol": "K", "name": "Keto", "equals": "GT"} | |
| 2405 ] | |
| 2406 }), | |
| 2407 PROTEIN: new StandardAlphabet(4, "PROTEIN", { | |
| 2408 "name": "Protein", | |
| 2409 "like": "PROTEIN", | |
| 2410 "ncore": 20, | |
| 2411 "symbols": [ | |
| 2412 {"symbol": "A", "name": "Alanine", "colour": "0000CC"}, | |
| 2413 {"symbol": "C", "name": "Cysteine", "colour": "0000CC"}, | |
| 2414 {"symbol": "D", "name": "Aspartic acid", "colour": "FF00FF"}, | |
| 2415 {"symbol": "E", "name": "Glutamic acid", "colour": "FF00FF"}, | |
| 2416 {"symbol": "F", "name": "Phenylalanine", "colour": "0000CC"}, | |
| 2417 {"symbol": "G", "name": "Glycine", "colour": "FFB300"}, | |
| 2418 {"symbol": "H", "name": "Histidine", "colour": "FFCCCC"}, | |
| 2419 {"symbol": "I", "name": "Isoleucine", "colour": "0000CC"}, | |
| 2420 {"symbol": "K", "name": "Lysine", "colour": "CC0000"}, | |
| 2421 {"symbol": "L", "name": "Leucine", "colour": "0000CC"}, | |
| 2422 {"symbol": "M", "name": "Methionine", "colour": "0000CC"}, | |
| 2423 {"symbol": "N", "name": "Asparagine", "colour": "008000"}, | |
| 2424 {"symbol": "P", "name": "Proline", "colour": "FFFF00"}, | |
| 2425 {"symbol": "Q", "name": "Glutamine", "colour": "008000"}, | |
| 2426 {"symbol": "R", "name": "Arginine", "colour": "CC0000"}, | |
| 2427 {"symbol": "S", "name": "Serine", "colour": "008000"}, | |
| 2428 {"symbol": "T", "name": "Threonine", "colour": "008000"}, | |
| 2429 {"symbol": "V", "name": "Valine", "colour": "0000CC"}, | |
| 2430 {"symbol": "W", "name": "Tryptophan", "colour": "0000CC"}, | |
| 2431 {"symbol": "Y", "name": "Tyrosine", "colour": "33E6CC"}, | |
| 2432 {"symbol": "X", "name": "Any amino acid", "equals": "ACDEFGHIKLMNPQRSTVWY", "aliases": "*."}, | |
| 2433 {"symbol": "B", "name": "Asparagine or Aspartic acid", "equals": "DN"}, | |
| 2434 {"symbol": "Z", "name": "Glutamine or Glutamic acid", "equals": "EQ"}, | |
| 2435 {"symbol": "J", "name": "Leucine or Isoleucine", "equals": "IL"} | |
| 2436 ] | |
| 2437 }) | |
| 2438 }; | |
| 2439 | |
| 2440 //====================================================================== | |
| 2441 // start Symbol object | |
| 2442 //====================================================================== | |
| 2443 var Symbol = function(alph_index, scale, alphabet) { | |
| 2444 "use strict"; | |
| 2445 //variable prototype | |
| 2446 this.symbol = alphabet.get_symbol(alph_index); | |
| 2447 this.scale = scale; | |
| 2448 this.colour = alphabet.get_colour(alph_index); | |
| 2449 }; | |
| 2450 | |
| 2451 Symbol.prototype.get_symbol = function() { | |
| 2452 "use strict"; | |
| 2453 return this.symbol; | |
| 2454 }; | |
| 2455 | |
| 2456 Symbol.prototype.get_scale = function() { | |
| 2457 "use strict"; | |
| 2458 return this.scale; | |
| 2459 }; | |
| 2460 | |
| 2461 Symbol.prototype.get_colour = function() { | |
| 2462 "use strict"; | |
| 2463 return this.colour; | |
| 2464 }; | |
| 2465 | |
| 2466 Symbol.prototype.toString = function() { | |
| 2467 "use strict"; | |
| 2468 return this.symbol + " " + (Math.round(this.scale*1000)/10) + "%"; | |
| 2469 }; | |
| 2470 | |
| 2471 function compare_symbol(sym1, sym2) { | |
| 2472 "use strict"; | |
| 2473 if (sym1.get_scale() < sym2.get_scale()) { | |
| 2474 return -1; | |
| 2475 } else if (sym1.get_scale() > sym2.get_scale()) { | |
| 2476 return 1; | |
| 2477 } else { | |
| 2478 return 0; | |
| 2479 } | |
| 2480 } | |
| 2481 //====================================================================== | |
| 2482 // end Symbol object | |
| 2483 //====================================================================== | |
| 2484 | |
| 2485 //====================================================================== | |
| 2486 // start Pspm object | |
| 2487 //====================================================================== | |
| 2488 var Pspm = function(matrix, name, ltrim, rtrim, nsites, evalue, pssm, alt, pgm) { | |
| 2489 "use strict"; | |
| 2490 var row, col, data, row_sum, delta, evalue_re; | |
| 2491 if (typeof name !== "string") { | |
| 2492 name = ""; | |
| 2493 } | |
| 2494 this.name = name; | |
| 2495 //construct | |
| 2496 if (matrix instanceof Pspm) { | |
| 2497 // copy constructor | |
| 2498 this.alph_length = matrix.alph_length; | |
| 2499 this.motif_length = matrix.motif_length; | |
| 2500 this.name = matrix.name; | |
| 2501 this.alt = matrix.alt; | |
| 2502 this.nsites = matrix.nsites; | |
| 2503 this.evalue = matrix.evalue; | |
| 2504 this.ltrim = matrix.ltrim; | |
| 2505 this.rtrim = matrix.rtrim; | |
| 2506 this.pspm = []; | |
| 2507 this.pgm = matrix.pgm; | |
| 2508 for (row = 0; row < matrix.motif_length; row++) { | |
| 2509 this.pspm[row] = []; | |
| 2510 for (col = 0; col < matrix.alph_length; col++) { | |
| 2511 this.pspm[row][col] = matrix.pspm[row][col]; | |
| 2512 } | |
| 2513 } | |
| 2514 if (matrix.pssm != null) { | |
| 2515 this.pssm = []; | |
| 2516 for (row = 0; row < matrix.motif_length; row++) { | |
| 2517 this.pspm[row] = []; | |
| 2518 for (col = 0; col < matrix.alph_length; col++) { | |
| 2519 this.pssm[row][col] = matrix.pssm[row][col]; | |
| 2520 } | |
| 2521 } | |
| 2522 } | |
| 2523 } else { | |
| 2524 // check parameters | |
| 2525 if (ltrim == null) { | |
| 2526 ltrim = 0; | |
| 2527 } else if (typeof ltrim !== "number" || ltrim % 1 !== 0 || ltrim < 0) { | |
| 2528 throw new Error("ltrim must be a non-negative integer, got: " + ltrim); | |
| 2529 } | |
| 2530 if (rtrim == null) { | |
| 2531 rtrim = 0; | |
| 2532 } else if (typeof rtrim !== "number" || rtrim % 1 !== 0 || rtrim < 0) { | |
| 2533 throw new Error("rtrim must be a non-negative integer, got: " + rtrim); | |
| 2534 } | |
| 2535 if (nsites != null) { | |
| 2536 if (typeof nsites !== "number" || nsites < 0) { | |
| 2537 throw new Error("nsites must be a positive number, got: " + nsites); | |
| 2538 } else if (nsites == 0) { | |
| 2539 nsites = null; | |
| 2540 } | |
| 2541 } | |
| 2542 if (evalue != null) { | |
| 2543 if (typeof evalue === "number") { | |
| 2544 if (evalue < 0) { | |
| 2545 throw new Error("evalue must be a non-negative number, got: " + evalue); | |
| 2546 } | |
| 2547 } else if (typeof evalue === "string") { | |
| 2548 evalue_re = /^((?:[+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)|inf)$/; | |
| 2549 if (!evalue_re.test(evalue)) { | |
| 2550 throw new Error("evalue must be a non-negative number, got: " + evalue); | |
| 2551 } | |
| 2552 } else { | |
| 2553 throw new Error("evalue must be a non-negative number, got: " + evalue); | |
| 2554 } | |
| 2555 } | |
| 2556 // set properties | |
| 2557 this.name = name; | |
| 2558 this.alt = alt; | |
| 2559 this.nsites = nsites; | |
| 2560 this.evalue = evalue; | |
| 2561 this.ltrim = ltrim; | |
| 2562 this.rtrim = rtrim; | |
| 2563 this.pgm = pgm; | |
| 2564 if (typeof matrix === "string") { | |
| 2565 // string constructor | |
| 2566 data = parse_pspm_string(matrix); | |
| 2567 this.alph_length = data["alph_length"]; | |
| 2568 this.motif_length = data["motif_length"]; | |
| 2569 this.pspm = data["pspm"]; | |
| 2570 if (this.evalue == null) { | |
| 2571 if (data["evalue"] != null) { | |
| 2572 this.evalue = data["evalue"]; | |
| 2573 } else { | |
| 2574 this.evalue = 0; | |
| 2575 } | |
| 2576 } | |
| 2577 if (this.nsites == null) { | |
| 2578 if (typeof data["nsites"] === "number") { | |
| 2579 this.nsites = data["nsites"]; | |
| 2580 } else { | |
| 2581 this.nsites = 20; | |
| 2582 } | |
| 2583 } | |
| 2584 } else { | |
| 2585 // assume pspm is a nested array | |
| 2586 this.motif_length = matrix.length; | |
| 2587 this.alph_length = (matrix.length > 0 ? matrix[0].length : 0); | |
| 2588 if (this.nsites == null) { | |
| 2589 this.nsites = 20; | |
| 2590 } | |
| 2591 if (this.evalue == null) { | |
| 2592 this.evalue = 0; | |
| 2593 } | |
| 2594 this.pspm = []; | |
| 2595 // copy pspm and check | |
| 2596 for (row = 0; row < this.motif_length; row++) { | |
| 2597 if (this.alph_length != matrix[row].length) { | |
| 2598 throw new Error("COLUMN_MISMATCH"); | |
| 2599 } | |
| 2600 this.pspm[row] = []; | |
| 2601 row_sum = 0; | |
| 2602 for (col = 0; col < this.alph_length; col++) { | |
| 2603 this.pspm[row][col] = matrix[row][col]; | |
| 2604 row_sum += this.pspm[row][col]; | |
| 2605 } | |
| 2606 delta = 0.1; | |
| 2607 if (isNaN(row_sum) || (row_sum > 1 && (row_sum - 1) > delta) || | |
| 2608 (row_sum < 1 && (1 - row_sum) > delta)) { | |
| 2609 throw new Error("INVALID_SUM"); | |
| 2610 } | |
| 2611 } | |
| 2612 // copy pssm | |
| 2613 if (pssm != null) { | |
| 2614 this.pssm = []; | |
| 2615 for (row = 0; row < this.motif_length; row++) { | |
| 2616 this.pssm[row] = []; | |
| 2617 for (col = 0; col < this.alph_length; col++) { | |
| 2618 this.pssm[row][col] = pssm[row][col]; | |
| 2619 } | |
| 2620 } | |
| 2621 } | |
| 2622 } | |
| 2623 } | |
| 2624 }; | |
| 2625 | |
| 2626 Pspm.prototype.copy = function() { | |
| 2627 "use strict"; | |
| 2628 return new Pspm(this); | |
| 2629 }; | |
| 2630 | |
| 2631 Pspm.prototype.reverse = function() { | |
| 2632 "use strict"; | |
| 2633 var x, y, temp, temp_trim; | |
| 2634 //reverse | |
| 2635 x = 0; | |
| 2636 y = this.motif_length-1; | |
| 2637 while (x < y) { | |
| 2638 temp = this.pspm[x]; | |
| 2639 this.pspm[x] = this.pspm[y]; | |
| 2640 this.pspm[y] = temp; | |
| 2641 x++; | |
| 2642 y--; | |
| 2643 } | |
| 2644 // reverse pssm (if defined) | |
| 2645 if (typeof this.pssm !== "undefined") { | |
| 2646 //reverse | |
| 2647 x = 0; | |
| 2648 y = this.motif_length-1; | |
| 2649 while (x < y) { | |
| 2650 temp = this.pssm[x]; | |
| 2651 this.pspm[x] = this.pssm[y]; | |
| 2652 this.pssm[y] = temp; | |
| 2653 x++; | |
| 2654 y--; | |
| 2655 } | |
| 2656 } | |
| 2657 //swap triming | |
| 2658 temp_trim = this.ltrim; | |
| 2659 this.ltrim = this.rtrim; | |
| 2660 this.rtrim = temp_trim; | |
| 2661 return this; //allow function chaining... | |
| 2662 }; | |
| 2663 | |
| 2664 Pspm.prototype.reverse_complement = function(alphabet) { | |
| 2665 "use strict"; | |
| 2666 var x, y, temp, i, row, c, temp_trim; | |
| 2667 if (this.alph_length != alphabet.get_size_core()) { | |
| 2668 throw new Error("The alphabet size does not match the size of the pspm."); | |
| 2669 } | |
| 2670 if (!alphabet.has_complement()) { | |
| 2671 throw new Error("The specified alphabet can not be complemented."); | |
| 2672 } | |
| 2673 // reverse motif | |
| 2674 this.reverse(); | |
| 2675 //complement | |
| 2676 for (x = 0; x < this.motif_length; x++) { | |
| 2677 row = this.pspm[x]; | |
| 2678 for (i = 0; i < row.length; i++) { | |
| 2679 c = alphabet.get_complement(i); | |
| 2680 if (c < i) continue; | |
| 2681 temp = row[i]; | |
| 2682 row[i] = row[c]; | |
| 2683 row[c] = temp; | |
| 2684 } | |
| 2685 } | |
| 2686 // complement pssm (if defined) | |
| 2687 if (typeof this.pssm !== "undefined") { | |
| 2688 //complement | |
| 2689 for (x = 0; x < this.motif_length; x++) { | |
| 2690 row = this.pssm[x]; | |
| 2691 for (i = 0; i < row.length; i++) { | |
| 2692 c = alphabet.get_complement(i); | |
| 2693 if (c < i) continue; | |
| 2694 temp = row[i]; | |
| 2695 row[i] = row[c]; | |
| 2696 row[c] = temp; | |
| 2697 } | |
| 2698 } | |
| 2699 } | |
| 2700 return this; //allow function chaining... | |
| 2701 }; | |
| 2702 | |
| 2703 Pspm.prototype.get_stack = function(position, alphabet, ssc) { | |
| 2704 "use strict"; | |
| 2705 var row, stack_ic, alphabet_ic, stack, i, sym; | |
| 2706 if (this.alph_length != alphabet.get_size_core()) { | |
| 2707 throw new Error("The alphabet size does not match the size of the pspm."); | |
| 2708 } | |
| 2709 row = this.pspm[position]; | |
| 2710 stack_ic = this.get_stack_ic(position, alphabet); | |
| 2711 if (ssc) stack_ic -= this.get_error(alphabet); | |
| 2712 alphabet_ic = alphabet.get_ic(); | |
| 2713 stack = []; | |
| 2714 for (i = 0; i < this.alph_length; i++) { | |
| 2715 sym = new Symbol(i, row[i]*stack_ic/alphabet_ic, alphabet); | |
| 2716 if (sym.get_scale() <= 0) { | |
| 2717 continue; | |
| 2718 } | |
| 2719 stack.push(sym); | |
| 2720 } | |
| 2721 stack.sort(compare_symbol); | |
| 2722 return stack; | |
| 2723 }; | |
| 2724 | |
| 2725 Pspm.prototype.get_stack_ic = function(position, alphabet) { | |
| 2726 "use strict"; | |
| 2727 var row, H, i; | |
| 2728 if (this.alph_length != alphabet.get_size_core()) { | |
| 2729 throw new Error("The alphabet size does not match the size fo the pspm."); | |
| 2730 } | |
| 2731 row = this.pspm[position]; | |
| 2732 H = 0; | |
| 2733 for (i = 0; i < this.alph_length; i++) { | |
| 2734 if (row[i] === 0) { | |
| 2735 continue; | |
| 2736 } | |
| 2737 H -= (row[i] * (Math.log(row[i]) / Math.LN2)); | |
| 2738 } | |
| 2739 return alphabet.get_ic() - H; | |
| 2740 }; | |
| 2741 | |
| 2742 Pspm.prototype.get_error = function(alphabet) { | |
| 2743 "use strict"; | |
| 2744 if (this.nsites === 0) { | |
| 2745 return 0; | |
| 2746 } | |
| 2747 return (alphabet.get_size_core()-1) / (2 * Math.LN2 * this.nsites); | |
| 2748 }; | |
| 2749 | |
| 2750 Pspm.prototype.get_motif_length = function() { | |
| 2751 "use strict"; | |
| 2752 return this.motif_length; | |
| 2753 }; | |
| 2754 | |
| 2755 Pspm.prototype.get_alph_length = function() { | |
| 2756 "use strict"; | |
| 2757 return this.alph_length; | |
| 2758 }; | |
| 2759 | |
| 2760 Pspm.prototype.get_left_trim = function() { | |
| 2761 "use strict"; | |
| 2762 return this.ltrim; | |
| 2763 }; | |
| 2764 | |
| 2765 Pspm.prototype.get_right_trim = function() { | |
| 2766 "use strict"; | |
| 2767 return this.rtrim; | |
| 2768 }; | |
| 2769 | |
| 2770 Pspm.prototype.as_best_match = function(alphabet) { | |
| 2771 "use strict"; | |
| 2772 var match, odds, best_odds, best_index; | |
| 2773 var i, j; | |
| 2774 match = ""; | |
| 2775 for (i = 0; i < this.motif_length; i++) { | |
| 2776 best_index = 0; | |
| 2777 best_odds = this.pspm[i][0] / alphabet.get_bg_freq(0); | |
| 2778 for (j = 1; j < this.alph_length; j++) { | |
| 2779 odds = this.pspm[i][j] / alphabet.get_bg_freq(j); | |
| 2780 if (odds > best_odds) { | |
| 2781 best_odds = odds; | |
| 2782 best_index = j; | |
| 2783 } | |
| 2784 } | |
| 2785 match += alphabet.get_symbol(best_index); | |
| 2786 } | |
| 2787 return match; | |
| 2788 }; | |
| 2789 | |
| 2790 Pspm.prototype.as_count_matrix = function() { | |
| 2791 "use strict"; | |
| 2792 var count, count_text, text; | |
| 2793 var i, j; | |
| 2794 text = ""; | |
| 2795 for (i = 0; i < this.motif_length; i++) { | |
| 2796 if (i !== 0) { | |
| 2797 text += "\n"; | |
| 2798 } | |
| 2799 for (j = 0; j < this.alph_length; j++) { | |
| 2800 if (j !== 0) { | |
| 2801 text += " "; | |
| 2802 } | |
| 2803 count = Math.round(this.nsites * this.pspm[i][j]); | |
| 2804 count_text = "" + count; | |
| 2805 // pad up to length of 4 | |
| 2806 if (count_text.length < 4) { | |
| 2807 text += (new Array(5 - count_text.length)).join(" ") + count_text; | |
| 2808 } else { | |
| 2809 text += count_text; | |
| 2810 } | |
| 2811 } | |
| 2812 } | |
| 2813 return text; | |
| 2814 }; | |
| 2815 | |
| 2816 Pspm.prototype.as_probability_matrix = function() { | |
| 2817 "use strict"; | |
| 2818 var text; | |
| 2819 var i, j; | |
| 2820 text = ""; | |
| 2821 for (i = 0; i < this.motif_length; i++) { | |
| 2822 if (i !== 0) { | |
| 2823 text += "\n"; | |
| 2824 } | |
| 2825 for (j = 0; j < this.alph_length; j++) { | |
| 2826 if (j !== 0) { | |
| 2827 text += " "; | |
| 2828 } | |
| 2829 text += this.pspm[i][j].toFixed(6); | |
| 2830 } | |
| 2831 } | |
| 2832 return text; | |
| 2833 }; | |
| 2834 | |
| 2835 Pspm.prototype.as_score_matrix = function(alphabet, pseudo) { | |
| 2836 "use strict"; | |
| 2837 var me, score, out, row, col, score_text; | |
| 2838 me = this; | |
| 2839 if (typeof this.pssm === "undefined") { | |
| 2840 if (!(typeof alphabet === "object" && alphabet != null && alphabet instanceof Alphabet)) { | |
| 2841 throw new Error("The alphabet is required to generate the pssm."); | |
| 2842 } | |
| 2843 if (typeof pseudo === "undefined") { | |
| 2844 pseudo = 0.01; | |
| 2845 } else if (typeof pseudo !== "number" || pseudo < 0) { | |
| 2846 throw new Error("Expected positive number for pseudocount"); | |
| 2847 } | |
| 2848 score = function(row, col) { | |
| 2849 "use strict"; | |
| 2850 var p, bg, p2; | |
| 2851 p = me.pspm[row][col]; | |
| 2852 bg = alphabet.get_bg_freq(col); | |
| 2853 p2 = (p * me.nsites + bg * pseudo) / (me.nsites + pseudo); | |
| 2854 return (p2 > 0 ? Math.round((Math.log(p2 / bg) / Math.LN2) * 100) : -10000); | |
| 2855 }; | |
| 2856 } else { | |
| 2857 score = function(row, col) { | |
| 2858 "use strict"; | |
| 2859 return me.pssm[row][col]; | |
| 2860 }; | |
| 2861 } | |
| 2862 out = ""; | |
| 2863 for (row = 0; row < this.motif_length; row++) { | |
| 2864 for (col = 0; col < this.alph_length; col++) { | |
| 2865 if (col !== 0) { | |
| 2866 out += " "; | |
| 2867 } | |
| 2868 score_text = "" + score(row, col); | |
| 2869 // pad out to 6 characters | |
| 2870 if (score_text.length < 6) { | |
| 2871 out += (new Array(7 - score_text.length)).join(" ") + score_text; | |
| 2872 } else { | |
| 2873 out += score_text; | |
| 2874 } | |
| 2875 } | |
| 2876 out += "\n"; | |
| 2877 } | |
| 2878 return out; | |
| 2879 } | |
| 2880 | |
| 2881 Pspm.prototype.as_pspm = function() { | |
| 2882 "use strict"; | |
| 2883 return "letter-probability matrix: alength= " + this.alph_length + | |
| 2884 " w= " + this.motif_length + " nsites= " + this.nsites + | |
| 2885 (this.pgm === "STREME" ? " P= " : " E= ") + | |
| 2886 (typeof this.evalue === "number" ? | |
| 2887 this.evalue.toExponential() : this.evalue) + "\n" + | |
| 2888 this.as_probability_matrix(); | |
| 2889 }; | |
| 2890 | |
| 2891 Pspm.prototype.as_pssm = function(alphabet, pseudo) { | |
| 2892 "use strict"; | |
| 2893 return "log-odds matrix: alength= " + this.alph_length + | |
| 2894 " w= " + this.motif_length + | |
| 2895 " E= " + (typeof this.evalue == "number" ? | |
| 2896 this.evalue.toExponential() : this.evalue) + "\n" + | |
| 2897 this.as_score_matrix(alphabet, pseudo); | |
| 2898 }; | |
| 2899 | |
| 2900 Pspm.prototype.as_meme = function(options) { | |
| 2901 var with_header, with_pspm, with_pssm, version, alphabet, bg_source, pseudocount, strands; | |
| 2902 var out, alen, i; | |
| 2903 // get the options | |
| 2904 if (typeof options !== "object" || options === null) { | |
| 2905 options = {}; | |
| 2906 } | |
| 2907 with_header = (typeof options["with_header"] === "boolean" ? options["with_header"] : false); | |
| 2908 with_pspm = (typeof options["with_pspm"] === "boolean" ? options["with_pspm"] : false); | |
| 2909 with_pssm = (typeof options["with_pssm"] === "boolean" ? options["with_pssm"] : false); | |
| 2910 if (!with_pspm && !with_pssm) with_pspm = true; | |
| 2911 if (with_header) { | |
| 2912 if (typeof options["version"] === "string" && /^\d+(?:\.\d+){0,2}$/.test(options["version"])) { | |
| 2913 version = options["version"]; | |
| 2914 } else if (typeof options["version"] === "number") { | |
| 2915 version = options["version"].toFixed(0); | |
| 2916 } else { | |
| 2917 version = "4"; | |
| 2918 } | |
| 2919 if (typeof options["strands"] === "number" && options["strands"] === 1) { | |
| 2920 strands = 1; | |
| 2921 } else { | |
| 2922 strands = 2; | |
| 2923 } | |
| 2924 if (typeof options["bg_source"] === "string") { | |
| 2925 bg_source = options["bg_source"]; | |
| 2926 } else { | |
| 2927 bg_source = "unknown source"; | |
| 2928 } | |
| 2929 if (typeof options["alphabet"] === "object" && options["alphabet"] != null | |
| 2930 && options["alphabet"] instanceof Alphabet) { | |
| 2931 alphabet = options["alphabet"]; | |
| 2932 } else { | |
| 2933 throw new Error("The alphabet is required to generate the header."); | |
| 2934 } | |
| 2935 } | |
| 2936 // now create the output | |
| 2937 out = ""; | |
| 2938 if (with_header) { | |
| 2939 out = "MEME version " + version + "\n\n"; | |
| 2940 out += alphabet.as_meme() + "\n"; | |
| 2941 if (alphabet.has_complement()) { // assume DNA has both strands unless otherwise specified | |
| 2942 out += "strands: " + (strands === 1 ? "+" : "+ -") + "\n\n"; | |
| 2943 } | |
| 2944 out += "Background letter frequencies (from " + bg_source + "):\n"; | |
| 2945 alen = alphabet.get_size_core(); | |
| 2946 for (i = 0; i < alen; i++) { | |
| 2947 if (i !== 0) { | |
| 2948 if (i % 9 === 0) { // maximum of nine entries per line | |
| 2949 out += "\n"; | |
| 2950 } else { | |
| 2951 out += " "; | |
| 2952 } | |
| 2953 } | |
| 2954 out += alphabet.get_symbol(i) + " " + alphabet.get_bg_freq(i).toFixed(3); | |
| 2955 } | |
| 2956 } | |
| 2957 out += "\n\n"; | |
| 2958 out += "MOTIF " + this.name + (this.alt == null ? "" : " " + this.alt); | |
| 2959 if (with_pssm) { | |
| 2960 out += "\n\n"; | |
| 2961 out += this.as_pssm(options["alphabet"], options["pseudocount"]); | |
| 2962 } | |
| 2963 if (with_pspm) { | |
| 2964 out += "\n\n"; | |
| 2965 out += this.as_pspm(); | |
| 2966 } | |
| 2967 return out; | |
| 2968 } | |
| 2969 | |
| 2970 Pspm.prototype.toString = function() { | |
| 2971 "use strict"; | |
| 2972 var str, i, row; | |
| 2973 str = ""; | |
| 2974 for (i = 0; i < this.pspm.length; i++) { | |
| 2975 row = this.pspm[i]; | |
| 2976 str += row.join("\t") + "\n"; | |
| 2977 } | |
| 2978 return str; | |
| 2979 }; | |
| 2980 | |
| 2981 function parse_pspm_properties(str) { | |
| 2982 "use strict"; | |
| 2983 var parts, i, eqpos, before, after, properties, prop, num, num_re; | |
| 2984 num_re = /^((?:[+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)|inf)$/; | |
| 2985 parts = trim(str).split(/\s+/); | |
| 2986 // split up words containing = | |
| 2987 for (i = 0; i < parts.length;) { | |
| 2988 eqpos = parts[i].indexOf("="); | |
| 2989 if (eqpos != -1) { | |
| 2990 before = parts[i].substr(0, eqpos); | |
| 2991 after = parts[i].substr(eqpos+1); | |
| 2992 if (before.length > 0 && after.length > 0) { | |
| 2993 parts.splice(i, 1, before, "=", after); | |
| 2994 i += 3; | |
| 2995 } else if (before.length > 0) { | |
| 2996 parts.splice(i, 1, before, "="); | |
| 2997 i += 2; | |
| 2998 } else if (after.length > 0) { | |
| 2999 parts.splice(i, 1, "=", after); | |
| 3000 i += 2; | |
| 3001 } else { | |
| 3002 parts.splice(i, 1, "="); | |
| 3003 i++; | |
| 3004 } | |
| 3005 } else { | |
| 3006 i++; | |
| 3007 } | |
| 3008 } | |
| 3009 properties = {}; | |
| 3010 for (i = 0; i < parts.length; i += 3) { | |
| 3011 if (parts.length - i < 3) { | |
| 3012 throw new Error("Expected PSPM property was incomplete. "+ | |
| 3013 "Remaing parts are: " + parts.slice(i).join(" ")); | |
| 3014 } | |
| 3015 if (parts[i+1] !== "=") { | |
| 3016 throw new Error("Expected '=' in PSPM property between key and " + | |
| 3017 "value but got " + parts[i+1]); | |
| 3018 } | |
| 3019 prop = parts[i].toLowerCase(); | |
| 3020 num = parts[i+2]; | |
| 3021 if (!num_re.test(num)) { | |
| 3022 throw new Error("Expected numeric value for PSPM property '" + | |
| 3023 prop + "' but got '" + num + "'"); | |
| 3024 } | |
| 3025 properties[prop] = num; | |
| 3026 } | |
| 3027 return properties; | |
| 3028 } | |
| 3029 | |
| 3030 function parse_pspm_string(pspm_string) { | |
| 3031 "use strict"; | |
| 3032 var header_re, lines, first_line, line_num, col_num, alph_length, | |
| 3033 motif_length, nsites, evalue, pspm, i, line, match, props, parts, | |
| 3034 j, prob; | |
| 3035 header_re = /^letter-probability\s+matrix:(.*)$/i; | |
| 3036 lines = pspm_string.split(/\n/); | |
| 3037 first_line = true; | |
| 3038 line_num = 0; | |
| 3039 col_num = 0; | |
| 3040 alph_length; | |
| 3041 motif_length; | |
| 3042 nsites; | |
| 3043 evalue; | |
| 3044 pspm = []; | |
| 3045 for (i = 0; i < lines.length; i++) { | |
| 3046 line = trim(lines[i]); | |
| 3047 if (line.length === 0) { | |
| 3048 continue; | |
| 3049 } | |
| 3050 // check the first line for a header though allow matrices without it | |
| 3051 if (first_line) { | |
| 3052 first_line = false; | |
| 3053 match = header_re.exec(line); | |
| 3054 if (match !== null) { | |
| 3055 props = parse_pspm_properties(match[1]); | |
| 3056 if (props.hasOwnProperty("alength")) { | |
| 3057 alph_length = parseFloat(props["alength"]); | |
| 3058 if (alph_length != 4 && alph_length != 20) { | |
| 3059 throw new Error("PSPM property alength should be 4 or 20" + | |
| 3060 " but got " + alph_length); | |
| 3061 } | |
| 3062 } | |
| 3063 if (props.hasOwnProperty("w")) { | |
| 3064 motif_length = parseFloat(props["w"]); | |
| 3065 if (motif_length % 1 !== 0 || motif_length < 1) { | |
| 3066 throw new Error("PSPM property w should be an integer larger " + | |
| 3067 "than zero but got " + motif_length); | |
| 3068 } | |
| 3069 } | |
| 3070 if (props.hasOwnProperty("nsites")) { | |
| 3071 nsites = parseFloat(props["nsites"]); | |
| 3072 if (nsites <= 0) { | |
| 3073 throw new Error("PSPM property nsites should be larger than " + | |
| 3074 "zero but got " + nsites); | |
| 3075 } | |
| 3076 } | |
| 3077 if (props.hasOwnProperty("e")) { | |
| 3078 evalue = props["e"]; | |
| 3079 if (evalue < 0) { | |
| 3080 throw new Error("PSPM property evalue should be " + | |
| 3081 "non-negative but got " + evalue); | |
| 3082 } | |
| 3083 } | |
| 3084 continue; | |
| 3085 } | |
| 3086 } | |
| 3087 pspm[line_num] = []; | |
| 3088 col_num = 0; | |
| 3089 parts = line.split(/\s+/); | |
| 3090 for (j = 0; j < parts.length; j++) { | |
| 3091 prob = parseFloat(parts[j]); | |
| 3092 if (prob != parts[j] || prob < 0 || prob > 1) { | |
| 3093 throw new Error("Expected probability but got '" + parts[j] + "'"); | |
| 3094 } | |
| 3095 pspm[line_num][col_num] = prob; | |
| 3096 col_num++; | |
| 3097 } | |
| 3098 line_num++; | |
| 3099 } | |
| 3100 if (typeof motif_length === "number") { | |
| 3101 if (pspm.length != motif_length) { | |
| 3102 throw new Error("Expected PSPM to have a motif length of " + | |
| 3103 motif_length + " but it was actually " + pspm.length); | |
| 3104 } | |
| 3105 } else { | |
| 3106 motif_length = pspm.length; | |
| 3107 } | |
| 3108 if (typeof alph_length !== "number") { | |
| 3109 alph_length = pspm[0].length; | |
| 3110 if (alph_length != 4 && alph_length != 20) { | |
| 3111 throw new Error("Expected length of first row in the PSPM to be " + | |
| 3112 "either 4 or 20 but got " + alph_length); | |
| 3113 } | |
| 3114 } | |
| 3115 for (i = 0; i < pspm.length; i++) { | |
| 3116 if (pspm[i].length != alph_length) { | |
| 3117 throw new Error("Expected PSPM row " + i + " to have a length of " + | |
| 3118 alph_length + " but the length was " + pspm[i].length); | |
| 3119 } | |
| 3120 } | |
| 3121 return {"pspm": pspm, "motif_length": motif_length, | |
| 3122 "alph_length": alph_length, "nsites": nsites, "evalue": evalue}; | |
| 3123 } | |
| 3124 //====================================================================== | |
| 3125 // end Pspm object | |
| 3126 //====================================================================== | |
| 3127 | |
| 3128 //====================================================================== | |
| 3129 // start Logo object | |
| 3130 //====================================================================== | |
| 3131 | |
| 3132 var Logo = function(alphabet, options) { | |
| 3133 "use strict"; | |
| 3134 this.alphabet = alphabet; | |
| 3135 this.fine_text = ""; | |
| 3136 this.x_axis = 1; | |
| 3137 this.y_axis = true; | |
| 3138 this.xlate_nsyms = 1; | |
| 3139 this.xlate_start = null; | |
| 3140 this.xlate_end = null; | |
| 3141 this.pspm_list = []; | |
| 3142 this.pspm_column = []; | |
| 3143 this.rows = 0; | |
| 3144 this.columns = 0; | |
| 3145 if (typeof options === "string") { | |
| 3146 // the old method signature had fine_text here so we support that | |
| 3147 this.fine_text = options; | |
| 3148 } else if (typeof options === "object" && options != null) { | |
| 3149 this.fine_text = (typeof options.fine_text === "string" ? options.fine_text : ""); | |
| 3150 this.x_axis = (typeof options.x_axis === "boolean" ? (options.x_axis ? 1 : 0) : 1); | |
| 3151 if (options.x_axis_hidden != null && options.x_axis_hidden) this.x_axis = -1; | |
| 3152 this.y_axis = (typeof options.y_axis === "boolean" ? options.y_axis : true); | |
| 3153 this.xlate_nsyms = (typeof options.xlate_nsyms === "number" ? options.xlate_nsyms : this.xlate_nsyms); | |
| 3154 this.xlate_start = (typeof options.xlate_start === "number" ? options.xlate_start : this.xlate_start); | |
| 3155 this.xlate_end = (typeof options.xlate_end === "number" ? options.xlate_end : this.xlate_end); | |
| 3156 } | |
| 3157 }; | |
| 3158 | |
| 3159 Logo.prototype.add_pspm = function(pspm, column) { | |
| 3160 "use strict"; | |
| 3161 var col; | |
| 3162 if (typeof column === "undefined") { | |
| 3163 column = 0; | |
| 3164 } else if (column < 0) { | |
| 3165 throw new Error("Column index out of bounds."); | |
| 3166 } | |
| 3167 this.pspm_list[this.rows] = pspm; | |
| 3168 this.pspm_column[this.rows] = column; | |
| 3169 this.rows++; | |
| 3170 col = column + pspm.get_motif_length(); | |
| 3171 if (col > this.columns) { | |
| 3172 this.columns = col; | |
| 3173 } | |
| 3174 }; | |
| 3175 | |
| 3176 Logo.prototype.get_columns = function() { | |
| 3177 "use strict"; | |
| 3178 return this.columns; | |
| 3179 }; | |
| 3180 | |
| 3181 Logo.prototype.get_xlate_nsyms = function() { | |
| 3182 "use strict"; | |
| 3183 return this.xlate_nsyms; | |
| 3184 }; | |
| 3185 | |
| 3186 Logo.prototype.get_xlate_start = function() { | |
| 3187 "use strict"; | |
| 3188 return (this.xlate_start != null ? this.xlate_start : 0); | |
| 3189 }; | |
| 3190 | |
| 3191 Logo.prototype.get_xlate_end = function() { | |
| 3192 "use strict"; | |
| 3193 return (this.xlate_end != null ? this.xlate_end : this.columns * this.xlate_nsyms); | |
| 3194 }; | |
| 3195 | |
| 3196 Logo.prototype.get_xlate_columns = function() { | |
| 3197 "use strict"; | |
| 3198 return this.get_xlate_end() - this.get_xlate_start(); | |
| 3199 }; | |
| 3200 | |
| 3201 Logo.prototype.get_rows = function() { | |
| 3202 "use strict"; | |
| 3203 return this.rows; | |
| 3204 }; | |
| 3205 | |
| 3206 Logo.prototype.get_pspm = function(row_index) { | |
| 3207 "use strict"; | |
| 3208 if (row_index < 0 || row_index >= this.rows) { | |
| 3209 throw new Error("INDEX_OUT_OF_BOUNDS"); | |
| 3210 } | |
| 3211 return this.pspm_list[row_index]; | |
| 3212 }; | |
| 3213 | |
| 3214 Logo.prototype.get_offset = function(row_index) { | |
| 3215 "use strict"; | |
| 3216 if (row_index < 0 || row_index >= this.rows) { | |
| 3217 throw new Error("INDEX_OUT_OF_BOUNDS"); | |
| 3218 } | |
| 3219 return this.pspm_column[row_index]; | |
| 3220 }; | |
| 3221 | |
| 3222 Logo.prototype._as_eps_data = function(ssc, errbars) { | |
| 3223 var i, j, pos, stack_pos, pspm, stack, sym, out; | |
| 3224 out = ""; | |
| 3225 for (i = 0; i < this.rows; i++) { | |
| 3226 out += "\nStartLine\n"; | |
| 3227 // Indent | |
| 3228 for (j = 0; j < this.pspm_column[i]; j++) { | |
| 3229 out += "() startstack\nendstack\n\n"; | |
| 3230 } | |
| 3231 pspm = this.pspm_list[i]; | |
| 3232 if (pspm.get_left_trim() > 0) { | |
| 3233 out += "MuteColour\nDrawTrimEdge\n" + pspm.get_left_trim() + " DrawTrimBg\n"; | |
| 3234 } | |
| 3235 for (pos = 0; pos < pspm.get_motif_length(); pos++) { | |
| 3236 if (pos != 0 && pos == pspm.get_left_trim()) { // enable full colour | |
| 3237 out += "DrawTrimEdge\nRestoreColour\n"; | |
| 3238 } else if (pos == (pspm.get_motif_length() - pspm.get_right_trim())) { | |
| 3239 out += "MuteColour\n" + pspm.get_right_trim() + " DrawTrimBg\n"; | |
| 3240 } | |
| 3241 out += "(" + (pos + 1) + ") startstack\n"; | |
| 3242 stack = pspm.get_stack(pos, this.alphabet, ssc); | |
| 3243 for (stack_pos = 0; stack_pos < stack.length; stack_pos++) { | |
| 3244 sym = stack[stack_pos]; | |
| 3245 out += " " + (sym.get_scale() * this.alphabet.get_ic()) + " (" + sym.get_symbol() + ") numchar\n"; | |
| 3246 } | |
| 3247 if (errbars) { | |
| 3248 out += " " + pspm.get_error(this.alphabet) + " Ibeam\n"; | |
| 3249 } | |
| 3250 out += "endstack\n\n"; | |
| 3251 } | |
| 3252 if (pspm.get_right_trim() > 0 || pspm.get_left_trim() == pspm.get_motif_length()) { | |
| 3253 out += "RestoreColour\n"; | |
| 3254 } | |
| 3255 out += "EndLine\n"; | |
| 3256 } | |
| 3257 return out; | |
| 3258 }; | |
| 3259 | |
| 3260 Logo.prototype.as_eps = function(options) { | |
| 3261 "use strict"; | |
| 3262 if (this.xlate_nsyms != 1) throw new Error("Unsupported setting xlate_nsyms for EPS"); | |
| 3263 if (this.xlate_start != null) throw new Error("Unsupported setting xlate_start for EPS"); | |
| 3264 if (this.xlate_end != null) throw new Error("Unsupported setting xlate_end for EPS"); | |
| 3265 | |
| 3266 var LOGOHEIGHT = 7.5; // default height of line in cm | |
| 3267 var cm2pts, height, width, now, ssc, errbars; | |
| 3268 if (typeof options === "undefined") { | |
| 3269 options = {}; | |
| 3270 } | |
| 3271 cm2pts = 72 / 2.54; | |
| 3272 if (typeof options.logo_height == "number") { | |
| 3273 height = options.logo_height; | |
| 3274 } else { | |
| 3275 height = LOGOHEIGHT * this.rows; | |
| 3276 } | |
| 3277 if (typeof options.logo_width == "number") { | |
| 3278 width = options.logo_width; | |
| 3279 } else { | |
| 3280 width = this.columns + 2; | |
| 3281 } | |
| 3282 now = new Date(); | |
| 3283 ssc = (typeof options.ssc == "boolean" ? options.ssc : false); | |
| 3284 errbars = (typeof options.show_error_bar == "boolean" ? options.show_error_bar : ssc); | |
| 3285 var values = { | |
| 3286 "LOGOHEIGHT": height, | |
| 3287 "LOGOWIDTH": width, | |
| 3288 "BOUNDINGHEIGHT": Math.round(height * cm2pts), | |
| 3289 "BOUNDINGWIDTH": Math.round(width * cm2pts), | |
| 3290 "LOGOLINEHEIGHT": (height / this.rows), | |
| 3291 "CHARSPERLINE": this.columns, | |
| 3292 "BARBITS": this.alphabet.get_ic(), | |
| 3293 "LOGOTYPE": (this.alphabet.has_complement() ? "NA" : "AA"), | |
| 3294 "CREATIONDATE": now.getDate() + "." + (now.getMonth() + 1) + "." + now.getFullYear() + " " + now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds(), | |
| 3295 "ERRORBARFRACTION": (typeof options.error_bar_fraction == "number" ? options.error_bar_fraction : 1.0), | |
| 3296 "TICBITS": (typeof options.ticbits == "number" ? options.ticbits : 1.0), | |
| 3297 "TITLE": (typeof options.title == "string" ? options.title : ""), | |
| 3298 "FINEPRINT": (typeof options.fineprint == "string" ? options.fineprint : this.fine_text), | |
| 3299 "XAXISLABEL": (typeof options.xaxislabel == "string" ? options.xaxislabel : ""), | |
| 3300 "YAXISLABEL": (typeof options.yaxislabel == "string" ? options.yaxislabel : "bits"), | |
| 3301 "SSC": ssc, | |
| 3302 "YAXIS": (typeof options.show_y_axis == "boolean" ? options.show_y_axis : this.y_axis), | |
| 3303 "SHOWENDS": (typeof options.show_ends == "boolean" ? options.show_ends : false), | |
| 3304 "ERRBAR": errbars, | |
| 3305 "OUTLINE": (typeof options.show_outline == "boolean" ? options.show_outline : false), | |
| 3306 "NUMBERING": (typeof options.show_numbering == "boolean" ? options.show_numbering : this.x_axis != 0), | |
| 3307 "SHOWINGBOX": (typeof options.show_box == "boolean" ? options.show_box : false), | |
| 3308 "CREATOR": (typeof options.creator == "string" ? options.creator : "motif_logo.js"), | |
| 3309 "FONTSIZE": (typeof options.label_font_size == "number" ? options.label_font_size : 12), | |
| 3310 "TITLEFONTSIZE": (typeof options.title_font_size == "number" ? options.title_font_size : 12), | |
| 3311 "SMALLFONTSIZE": (typeof options.small_font_size == "number" ? options.small_font_size : 6), | |
| 3312 "TOPMARGIN" : (typeof options.top_margin == "number" ? options.top_margin : 0.9), | |
| 3313 "BOTTOMMARGIN": (typeof options.bottom_margin == "number" ? options.bottom_margin : 0.9), | |
| 3314 "COLORDICT": this.alphabet._as_eps_dict(), | |
| 3315 "DATA": this._as_eps_data(ssc, errbars) | |
| 3316 }; | |
| 3317 // now this requires that the script containing the template has been imported! | |
| 3318 return motif_logo_template(values); | |
| 3319 }; | |
| 3320 | |
| 3321 //====================================================================== | |
| 3322 // end Logo object | |
| 3323 //====================================================================== | |
| 3324 | |
| 3325 // calculate the exact size (in pixels) of an object drawn on the | |
| 3326 // canvas assuming that the background of the canvas is transparent. | |
| 3327 function canvas_bounds(ctx, cwidth, cheight) { | |
| 3328 "use strict"; | |
| 3329 var data, r, c, top_line, bottom_line, left_line, right_line, | |
| 3330 txt_width, txt_height; | |
| 3331 | |
| 3332 // extract the image data | |
| 3333 data = ctx.getImageData(0, 0, cwidth, cheight).data; | |
| 3334 | |
| 3335 // set initial values | |
| 3336 top_line = -1; bottom_line = -1; left_line = -1; right_line = -1; | |
| 3337 txt_width = 0; txt_height = 0; | |
| 3338 | |
| 3339 // Find the top-most line with a non-transparent pixel | |
| 3340 for (r = 0; r < cheight; r++) { | |
| 3341 for (c = 0; c < cwidth; c++) { | |
| 3342 if (data[r * cwidth * 4 + c * 4 + 3]) { | |
| 3343 top_line = r; | |
| 3344 break; | |
| 3345 } | |
| 3346 } | |
| 3347 if (top_line != -1) { | |
| 3348 break; | |
| 3349 } | |
| 3350 } | |
| 3351 | |
| 3352 // Only bother looking if we found at least one set pixel... | |
| 3353 if (top_line != -1) { | |
| 3354 | |
| 3355 //find the last line with a non-transparent pixel | |
| 3356 for (r = cheight-1; r >= top_line; r--) { | |
| 3357 for(c = 0; c < cwidth; c++) { | |
| 3358 if(data[r * cwidth * 4 + c * 4 + 3]) { | |
| 3359 bottom_line = r; | |
| 3360 break; | |
| 3361 } | |
| 3362 } | |
| 3363 if (bottom_line != -1) { | |
| 3364 break; | |
| 3365 } | |
| 3366 } | |
| 3367 // calculate height | |
| 3368 txt_height = bottom_line - top_line + 1; | |
| 3369 | |
| 3370 // Find the left-most line with a non-transparent pixel | |
| 3371 for (c = 0; c < cwidth; c++) { | |
| 3372 for (r = top_line; r <= bottom_line; r++) { | |
| 3373 if (data[r * cwidth * 4 + c * 4 + 3]) { | |
| 3374 left_line = c; | |
| 3375 break; | |
| 3376 } | |
| 3377 } | |
| 3378 if (left_line != -1) { | |
| 3379 break; | |
| 3380 } | |
| 3381 } | |
| 3382 | |
| 3383 //find the right most line with a non-transparent pixel | |
| 3384 for (c = cwidth-1; c >= left_line; c--) { | |
| 3385 for(r = top_line; r <= bottom_line; r++) { | |
| 3386 if(data[r * cwidth * 4 + c * 4 + 3]) { | |
| 3387 right_line = c; | |
| 3388 break; | |
| 3389 } | |
| 3390 } | |
| 3391 if (right_line != -1) { | |
| 3392 break; | |
| 3393 } | |
| 3394 } | |
| 3395 txt_width = right_line - left_line + 1; | |
| 3396 } | |
| 3397 | |
| 3398 //return the bounds | |
| 3399 return {bound_top: top_line, bound_bottom: bottom_line, | |
| 3400 bound_left: left_line, bound_right: right_line, width: txt_width, | |
| 3401 height: txt_height}; | |
| 3402 } | |
| 3403 | |
| 3404 //====================================================================== | |
| 3405 // start RasterizedAlphabet | |
| 3406 //====================================================================== | |
| 3407 | |
| 3408 // Rasterize Alphabet | |
| 3409 // 1) Measure width of text at default font for all symbols in alphabet | |
| 3410 // 2) sort in width ascending | |
| 3411 // 3) Drop the top and bottom 10% (designed to ignore outliers like 'W' and 'I') | |
| 3412 // 4) Calculate the average as the maximum scaling factor (designed to stop I becoming a rectangular blob). | |
| 3413 // 5) Assume scale of zero would result in width of zero, interpolate scale required to make perfect width font | |
| 3414 // 6) Draw text onto temp canvas at calculated scale | |
| 3415 // 7) Find bounds of drawn text | |
| 3416 // 8) Paint on to another canvas at the desired height (but only scaling width to fit if larger). | |
| 3417 var RasterizedAlphabet = function(alphabet, logo_scale, font, width) { | |
| 3418 "use strict"; | |
| 3419 var default_size, safety_pad, canvas, ctx, middle, baseline, widths, sizes, | |
| 3420 i, sym, size, tenpercent, avg_width, scale, | |
| 3421 target_width, target_height; | |
| 3422 //variable prototypes | |
| 3423 this.alphabet = alphabet; | |
| 3424 this.scale = logo_scale; | |
| 3425 this.sym_cache = {}; | |
| 3426 this.stack_num_cache = []; | |
| 3427 this.scale_num_cache = []; | |
| 3428 // size of canvas | |
| 3429 default_size = 60; // size of measuring canvas | |
| 3430 safety_pad = 20; // pixels to pad around so we don't miss the edges | |
| 3431 // create a canvas to do our measuring | |
| 3432 canvas = document.createElement("canvas"); | |
| 3433 if (!canvas.getContext) throw new Error("No canvas support"); | |
| 3434 canvas.width = default_size + 2 * safety_pad; | |
| 3435 canvas.height = default_size + 2 * safety_pad; | |
| 3436 middle = Math.round(canvas.width / 2); | |
| 3437 baseline = Math.round(canvas.height - safety_pad); | |
| 3438 ctx = canvas.getContext('2d'); | |
| 3439 if (!supports_text(ctx)) throw new Error("Canvas does not support text"); | |
| 3440 ctx.font = font; | |
| 3441 ctx.textAlign = "center"; | |
| 3442 ctx.translate(middle, baseline); | |
| 3443 // list of widths | |
| 3444 widths = []; | |
| 3445 sizes = []; | |
| 3446 //now measure each letter in the alphabet | |
| 3447 for (i = 0; i < alphabet.get_size_core(); ++i) { | |
| 3448 // reset the canvas | |
| 3449 ctx.clearRect(0, 0, canvas.width, canvas.height); | |
| 3450 ctx.fillStyle = alphabet.get_colour(i); | |
| 3451 // draw the test text | |
| 3452 ctx.fillText(alphabet.get_symbol(i), 0, 0); | |
| 3453 //measure | |
| 3454 size = canvas_bounds(ctx, canvas.width, canvas.height); | |
| 3455 if (size.width === 0) throw new Error("Invisible symbol!"); | |
| 3456 widths.push(size.width); | |
| 3457 sizes[i] = size; | |
| 3458 } | |
| 3459 //sort the widths | |
| 3460 widths.sort(function(a,b) {return a - b;}); | |
| 3461 //drop 10% of the items off each end | |
| 3462 tenpercent = Math.floor(widths.length / 10); | |
| 3463 for (i = 0; i < tenpercent; ++i) { | |
| 3464 widths.pop(); | |
| 3465 widths.shift(); | |
| 3466 } | |
| 3467 //calculate average width | |
| 3468 avg_width = 0; | |
| 3469 for (i = 0; i < widths.length; ++i) { | |
| 3470 avg_width += widths[i]; | |
| 3471 } | |
| 3472 avg_width /= widths.length; | |
| 3473 // calculate the target width | |
| 3474 target_width = width * this.scale * 2; | |
| 3475 // calculate scales | |
| 3476 for (i = 0; i < alphabet.get_size_core(); ++i) { | |
| 3477 sym = alphabet.get_symbol(i); | |
| 3478 size = sizes[i]; | |
| 3479 // calculate scale | |
| 3480 scale = target_width / Math.max(avg_width, size.width); | |
| 3481 // estimate scaled height | |
| 3482 target_height = size.height * scale; | |
| 3483 // create an appropriately sized canvas | |
| 3484 canvas = document.createElement("canvas"); | |
| 3485 canvas.width = target_width; | |
| 3486 canvas.height = target_height + safety_pad * 2; | |
| 3487 // calculate the middle | |
| 3488 middle = Math.round(canvas.width / 2); | |
| 3489 // calculate the baseline | |
| 3490 baseline = Math.round(canvas.height - safety_pad); | |
| 3491 // get the context and prepare to draw the rasterized text | |
| 3492 ctx = canvas.getContext('2d'); | |
| 3493 ctx.font = font; | |
| 3494 ctx.fillStyle = alphabet.get_colour(i); | |
| 3495 ctx.textAlign = "center"; | |
| 3496 ctx.translate(middle, baseline); | |
| 3497 ctx.save(); | |
| 3498 ctx.scale(scale, scale); | |
| 3499 // draw the text | |
| 3500 ctx.fillText(sym, 0, 0); | |
| 3501 ctx.restore(); | |
| 3502 this.sym_cache[sym] = {"image": canvas, "size": canvas_bounds(ctx, canvas.width, canvas.height)}; | |
| 3503 } | |
| 3504 }; | |
| 3505 | |
| 3506 RasterizedAlphabet.prototype.get_alphabet = function() { | |
| 3507 return this.alphabet; | |
| 3508 }; | |
| 3509 | |
| 3510 RasterizedAlphabet.prototype.get_scale = function() { | |
| 3511 return this.scale; | |
| 3512 }; | |
| 3513 | |
| 3514 RasterizedAlphabet.prototype.draw_stack_sym = function(ctx, letter, dx, dy, dWidth, dHeight) { | |
| 3515 "use strict"; | |
| 3516 var entry, image, size; | |
| 3517 entry = this.sym_cache[letter]; | |
| 3518 image = entry.image; | |
| 3519 size = entry.size; | |
| 3520 ctx.drawImage(image, 0, size.bound_top -1, image.width, size.height+1, dx, dy, dWidth, dHeight); | |
| 3521 }; | |
| 3522 | |
| 3523 RasterizedAlphabet.prototype.draw_stack_num = function(ctx, font, stack_width, index) { | |
| 3524 var image, image_ctx, text_length; | |
| 3525 if (index >= this.stack_num_cache.length) { | |
| 3526 image = document.createElement("canvas"); | |
| 3527 // measure the text | |
| 3528 image_ctx = image.getContext('2d'); | |
| 3529 image_ctx.save(); | |
| 3530 image_ctx.font = font; | |
| 3531 text_length = image_ctx.measureText("" + (index + 1)).width; | |
| 3532 image_ctx.restore(); | |
| 3533 // resize the canvas to fit | |
| 3534 image.width = Math.ceil(stack_width); | |
| 3535 image.height = Math.ceil(text_length); | |
| 3536 // draw the text | |
| 3537 image_ctx = image.getContext('2d'); | |
| 3538 image_ctx.translate(Math.round(stack_width / 2), 0); | |
| 3539 image_ctx.font = font; | |
| 3540 image_ctx.textBaseline = "middle"; | |
| 3541 image_ctx.textAlign = "right"; | |
| 3542 image_ctx.rotate(-(Math.PI / 2)); | |
| 3543 image_ctx.fillText("" + (index + 1), 0, 0); | |
| 3544 this.stack_num_cache[index] = image; | |
| 3545 } else { | |
| 3546 image = this.stack_num_cache[index]; | |
| 3547 } | |
| 3548 ctx.drawImage(image, 0, 0); | |
| 3549 } | |
| 3550 | |
| 3551 RasterizedAlphabet.prototype.draw_scale_num = function(ctx, font, num) { | |
| 3552 var image, image_ctx, text_size, m_length; | |
| 3553 if (num >= this.scale_num_cache.length) { | |
| 3554 image = document.createElement("canvas"); | |
| 3555 // measure the text | |
| 3556 image_ctx = image.getContext('2d'); | |
| 3557 image_ctx.font = font; | |
| 3558 text_size = image_ctx.measureText("" + num); | |
| 3559 if (text_size.actualBoundingBoxAscent && text_size.actualBoundingBoxDesent) { | |
| 3560 // resize the canvas to fit | |
| 3561 image.width = Math.ceil(text_size.width); | |
| 3562 image.height = Math.ceil(text_size.actualBoundingBoxAscent + text_size.actualBoundingBoxDesent); | |
| 3563 // draw the text | |
| 3564 image_ctx = image.getContext('2d'); | |
| 3565 image_ctx.font = font; | |
| 3566 image_ctx.textAlign = "right"; | |
| 3567 image_ctx.fillText("" + num, image.width, text_size.actualBoundingBoxAscent); | |
| 3568 } else { | |
| 3569 // measure width of 'm' to approximate height, we double it later anyway | |
| 3570 m_length = image_ctx.measureText("m").width; | |
| 3571 // resize the canvas to fit | |
| 3572 image.width = Math.ceil(text_size.width); | |
| 3573 image.height = Math.ceil(2 * m_length); | |
| 3574 // draw the text | |
| 3575 image_ctx = image.getContext('2d'); | |
| 3576 image_ctx.font = font; | |
| 3577 image_ctx.textAlign = "right"; | |
| 3578 image_ctx.textBaseline = "middle"; | |
| 3579 image_ctx.fillText("" + num, image.width, m_length); | |
| 3580 } | |
| 3581 this.scale_num_cache[num] = image; | |
| 3582 } else { | |
| 3583 image = this.scale_num_cache[num]; | |
| 3584 } | |
| 3585 ctx.drawImage(image, -image.width, -Math.round(image.height / 2)) | |
| 3586 } | |
| 3587 | |
| 3588 //====================================================================== | |
| 3589 // end RasterizedAlphabet | |
| 3590 //====================================================================== | |
| 3591 | |
| 3592 //====================================================================== | |
| 3593 // start LogoMetrics object | |
| 3594 //====================================================================== | |
| 3595 | |
| 3596 var LogoMetrics = function(ctx, logo_columns, logo_rows, has_names, has_finetext, x_axis, y_axis) { | |
| 3597 "use strict"; | |
| 3598 var i, row_height; | |
| 3599 //variable prototypes | |
| 3600 this.pad_top = (has_names ? 5 : 0); | |
| 3601 this.pad_left = (y_axis ? 10 : 0); | |
| 3602 this.pad_right = (has_finetext ? 15 : 0); | |
| 3603 this.pad_bottom = 0; | |
| 3604 this.pad_middle = 20; | |
| 3605 this.name_height = 14; | |
| 3606 this.name_font = "bold " + this.name_height + "px Times, sans-serif"; | |
| 3607 this.name_spacer = 0; | |
| 3608 this.y_axis = y_axis; | |
| 3609 this.y_label = "bits"; | |
| 3610 this.y_label_height = 12; | |
| 3611 this.y_label_font = "bold " + this.y_label_height + "px Helvetica, sans-serif"; | |
| 3612 this.y_label_spacer = 3; | |
| 3613 this.y_num_height = 12; | |
| 3614 this.y_num_width = 0; | |
| 3615 this.y_num_font = "bold " + this.y_num_height + "px Helvetica, sans-serif"; | |
| 3616 this.y_tic_width = 5; | |
| 3617 this.stack_pad_left = 0; | |
| 3618 this.stack_font = "bold 25px Helvetica, sans-serif"; | |
| 3619 this.stack_height = 90; | |
| 3620 this.stack_width = 26; | |
| 3621 this.stacks_pad_right = 5; | |
| 3622 this.x_axis = x_axis; | |
| 3623 this.x_num_above = 2; | |
| 3624 this.x_num_height = 12; | |
| 3625 this.x_num_width = 0; | |
| 3626 this.x_num_font = "bold " + this.x_num_height + "px Helvetica, sans-serif"; | |
| 3627 this.fine_txt_height = 6; | |
| 3628 this.fine_txt_above = 2; | |
| 3629 this.fine_txt_font = "normal " + this.fine_txt_height + "px Helvetica, sans-serif"; | |
| 3630 this.letter_metrics = new Array(); | |
| 3631 this.summed_width = 0; | |
| 3632 this.summed_height = 0; | |
| 3633 //calculate the width of the y axis numbers | |
| 3634 ctx.font = this.y_num_font; | |
| 3635 for (i = 0; i <= 2; i++) { | |
| 3636 this.y_num_width = Math.max(this.y_num_width, ctx.measureText("" + i).width); | |
| 3637 } | |
| 3638 //calculate the width of the x axis numbers (but they are rotated so it becomes height) | |
| 3639 if (x_axis == 1) { | |
| 3640 ctx.font = this.x_num_font; | |
| 3641 for (i = 1; i <= logo_columns; i++) { | |
| 3642 this.x_num_width = Math.max(this.x_num_width, ctx.measureText("" + i).width); | |
| 3643 } | |
| 3644 } else if (x_axis == 0) { | |
| 3645 this.x_num_height = 4; | |
| 3646 this.x_num_width = 4; | |
| 3647 } else { | |
| 3648 this.x_num_height = 0; | |
| 3649 this.x_num_width = 0; | |
| 3650 } | |
| 3651 | |
| 3652 //calculate how much vertical space we want to draw this | |
| 3653 //first we add the padding at the top and bottom since that's always there | |
| 3654 this.summed_height += this.pad_top + this.pad_bottom; | |
| 3655 //all except the last row have the same amount of space allocated to them | |
| 3656 if (logo_rows > 1) { | |
| 3657 row_height = this.stack_height + this.pad_middle; | |
| 3658 if (has_names) { | |
| 3659 row_height += this.name_height; | |
| 3660 //the label is allowed to overlap into the spacer | |
| 3661 row_height += Math.max(this.y_num_height/2, this.name_spacer); | |
| 3662 //the label is allowed to overlap the space used by the other label | |
| 3663 row_height += Math.max(this.y_num_height/2, this.x_num_height + this.x_num_above); | |
| 3664 } else { | |
| 3665 row_height += this.y_num_height/2; | |
| 3666 //the label is allowed to overlap the space used by the other label | |
| 3667 row_height += Math.max(this.y_num_height/2, this.x_num_height + this.x_num_above); | |
| 3668 } | |
| 3669 this.summed_height += row_height * (logo_rows - 1); | |
| 3670 } | |
| 3671 //the last row has the name and fine text below it but no padding | |
| 3672 this.summed_height += this.stack_height + (this.y_axis ? this.y_num_height/2 : 0); | |
| 3673 | |
| 3674 var fine_txt_total = (has_finetext ? this.fine_txt_height + this.fine_txt_above : 0); | |
| 3675 if (has_names) { | |
| 3676 this.summed_height += fine_txt_total + this.name_height; | |
| 3677 this.summed_height += Math.max((this.y_axis ? this.y_num_height/2 : 0), | |
| 3678 this.x_num_height + this.x_num_above + this.name_spacer); | |
| 3679 } else { | |
| 3680 this.summed_height += Math.max((this.y_axis ? this.y_num_height/2 : 0), | |
| 3681 this.x_num_height + this.x_num_above + fine_txt_total); | |
| 3682 } | |
| 3683 | |
| 3684 //calculate how much horizontal space we want to draw this | |
| 3685 //first add the padding at the left and right since that's always there | |
| 3686 this.summed_width += this.pad_left + this.pad_right; | |
| 3687 if (this.y_axis) { | |
| 3688 //add on the space for the y-axis label | |
| 3689 this.summed_width += this.y_label_height + this.y_label_spacer; | |
| 3690 //add on the space for the y-axis | |
| 3691 this.summed_width += this.y_num_width + this.y_tic_width; | |
| 3692 } | |
| 3693 //add on the space for the stacks | |
| 3694 this.summed_width += (this.stack_pad_left + this.stack_width) * logo_columns; | |
| 3695 //add on the padding after the stacks (an offset from the fine text) | |
| 3696 this.summed_width += this.stacks_pad_right; | |
| 3697 | |
| 3698 }; | |
| 3699 | |
| 3700 //====================================================================== | |
| 3701 // end LogoMetrics object | |
| 3702 //====================================================================== | |
| 3703 | |
| 3704 //found this trick at http://talideon.com/weblog/2005/02/detecting-broken-images-js.cfm | |
| 3705 function image_ok(img) { | |
| 3706 "use strict"; | |
| 3707 // During the onload event, IE correctly identifies any images that | |
| 3708 // weren't downloaded as not complete. Others should too. Gecko-based | |
| 3709 // browsers act like NS4 in that they report this incorrectly. | |
| 3710 if (!img.complete) { | |
| 3711 return false; | |
| 3712 } | |
| 3713 // However, they do have two very useful properties: naturalWidth and | |
| 3714 // naturalHeight. These give the true size of the image. If it failed | |
| 3715 // to load, either of these should be zero. | |
| 3716 if (typeof img.naturalWidth !== "undefined" && img.naturalWidth === 0) { | |
| 3717 return false; | |
| 3718 } | |
| 3719 // No other way of checking: assume it's ok. | |
| 3720 return true; | |
| 3721 } | |
| 3722 | |
| 3723 function supports_text(ctx) { | |
| 3724 "use strict"; | |
| 3725 if (!ctx.fillText) { | |
| 3726 return false; | |
| 3727 } | |
| 3728 if (!ctx.measureText) { | |
| 3729 return false; | |
| 3730 } | |
| 3731 return true; | |
| 3732 } | |
| 3733 | |
| 3734 //draws the scale, returns the width | |
| 3735 function draw_scale(ctx, metrics, alphabet_ic, raster) { | |
| 3736 "use strict"; | |
| 3737 var tic_height, i; | |
| 3738 tic_height = metrics.stack_height / alphabet_ic; | |
| 3739 ctx.save(); | |
| 3740 ctx.translate(metrics.y_label_height, metrics.y_num_height/2); | |
| 3741 //draw the axis label | |
| 3742 ctx.save(); | |
| 3743 ctx.font = metrics.y_label_font; | |
| 3744 ctx.translate(0, metrics.stack_height/2); | |
| 3745 ctx.rotate(-(Math.PI / 2)); | |
| 3746 ctx.textAlign = "center"; | |
| 3747 ctx.fillText("bits", 0, 0); | |
| 3748 ctx.restore(); | |
| 3749 | |
| 3750 ctx.translate(metrics.y_label_spacer + metrics.y_num_width, 0); | |
| 3751 | |
| 3752 //draw the axis tics | |
| 3753 ctx.save(); | |
| 3754 ctx.translate(0, metrics.stack_height); | |
| 3755 for (i = 0; i <= alphabet_ic; i++) { | |
| 3756 //draw the number | |
| 3757 ctx.save(); | |
| 3758 ctx.translate(-1, 0); | |
| 3759 raster.draw_scale_num(ctx, metrics.y_num_font, i); | |
| 3760 ctx.restore(); | |
| 3761 //draw the tic | |
| 3762 ctx.fillRect(0, -1, metrics.y_tic_width, 2); | |
| 3763 //prepare for next tic | |
| 3764 ctx.translate(0, -tic_height); | |
| 3765 } | |
| 3766 ctx.restore(); | |
| 3767 | |
| 3768 ctx.fillRect(metrics.y_tic_width - 2, 0, 2, metrics.stack_height) | |
| 3769 | |
| 3770 ctx.restore(); | |
| 3771 } | |
| 3772 | |
| 3773 function draw_stack_num(ctx, metrics, row_index, raster) { | |
| 3774 "use strict"; | |
| 3775 ctx.save(); | |
| 3776 ctx.translate(0, Math.round(metrics.stack_height + metrics.x_num_above)); | |
| 3777 if (metrics.x_axis == 1) { | |
| 3778 raster.draw_stack_num(ctx, metrics.x_num_font, metrics.stack_width, row_index); | |
| 3779 } else if (metrics.x_axis == 0) { | |
| 3780 // draw dots instead of the numbers (good for small logos) | |
| 3781 ctx.beginPath(); | |
| 3782 var radius = Math.round(metrics.x_num_height / 2); | |
| 3783 ctx.arc(Math.round(metrics.stack_width / 2), radius, radius, 0, 2 * Math.PI, false); | |
| 3784 ctx.fill(); | |
| 3785 } | |
| 3786 ctx.restore(); | |
| 3787 } | |
| 3788 | |
| 3789 function draw_stack(ctx, metrics, symbols, raster) { | |
| 3790 "use strict"; | |
| 3791 var preferred_pad, sym_min, i, sym, sym_height, pad; | |
| 3792 preferred_pad = 0; | |
| 3793 sym_min = 5; | |
| 3794 | |
| 3795 ctx.save();//1 | |
| 3796 ctx.translate(0, metrics.stack_height); | |
| 3797 for (i = 0; i < symbols.length; i++) { | |
| 3798 sym = symbols[i]; | |
| 3799 sym_height = metrics.stack_height * sym.get_scale(); | |
| 3800 | |
| 3801 pad = preferred_pad; | |
| 3802 if (sym_height - pad < sym_min) { | |
| 3803 pad = Math.min(pad, Math.max(0, sym_height - sym_min)); | |
| 3804 } | |
| 3805 sym_height -= pad; | |
| 3806 | |
| 3807 //translate to the correct position | |
| 3808 ctx.translate(0, -(pad/2 + sym_height)); | |
| 3809 | |
| 3810 //draw | |
| 3811 raster.draw_stack_sym(ctx, sym.get_symbol(), 0, 0, metrics.stack_width, sym_height); | |
| 3812 //translate past the padding | |
| 3813 ctx.translate(0, -(pad/2)); | |
| 3814 } | |
| 3815 ctx.restore();//1 | |
| 3816 } | |
| 3817 | |
| 3818 function draw_dashed_line(ctx, pattern, start, x1, y1, x2, y2) { | |
| 3819 "use strict"; | |
| 3820 var x, y, len, i, dx, dy, tlen, theta, mulx, muly, lx, ly; | |
| 3821 dx = x2 - x1; | |
| 3822 dy = y2 - y1; | |
| 3823 tlen = Math.pow(dx*dx + dy*dy, 0.5); | |
| 3824 theta = Math.atan2(dy,dx); | |
| 3825 mulx = Math.cos(theta); | |
| 3826 muly = Math.sin(theta); | |
| 3827 lx = []; | |
| 3828 ly = []; | |
| 3829 for (i = 0; i < pattern; ++i) { | |
| 3830 lx.push(pattern[i] * mulx); | |
| 3831 ly.push(pattern[i] * muly); | |
| 3832 } | |
| 3833 i = start; | |
| 3834 x = x1; | |
| 3835 y = y1; | |
| 3836 len = 0; | |
| 3837 ctx.beginPath(); | |
| 3838 while (len + pattern[i] < tlen) { | |
| 3839 ctx.moveTo(x, y); | |
| 3840 x += lx[i]; | |
| 3841 y += ly[i]; | |
| 3842 ctx.lineTo(x, y); | |
| 3843 len += pattern[i]; | |
| 3844 i = (i + 1) % pattern.length; | |
| 3845 x += lx[i]; | |
| 3846 y += ly[i]; | |
| 3847 len += pattern[i]; | |
| 3848 i = (i + 1) % pattern.length; | |
| 3849 } | |
| 3850 if (len < tlen) { | |
| 3851 ctx.moveTo(x, y); | |
| 3852 x += mulx * (tlen - len); | |
| 3853 y += muly * (tlen - len); | |
| 3854 ctx.lineTo(x, y); | |
| 3855 } | |
| 3856 ctx.stroke(); | |
| 3857 } | |
| 3858 | |
| 3859 function draw_trim_background(ctx, metrics, left_start, left_end, left_divider, right_start, right_end, right_divider) { | |
| 3860 "use strict"; | |
| 3861 var left_size = left_end - left_start; | |
| 3862 var right_size = right_end - right_start; | |
| 3863 var line_x; | |
| 3864 | |
| 3865 ctx.save();//s8 | |
| 3866 ctx.fillStyle = "rgb(240, 240, 240)"; | |
| 3867 if (left_size > 0) { | |
| 3868 ctx.fillRect(left_start * metrics.stack_width, 0, left_size * metrics.stack_width, metrics.stack_height); | |
| 3869 } | |
| 3870 if (right_size > 0) { | |
| 3871 ctx.fillRect(right_start * metrics.stack_width, 0, right_size * metrics.stack_width, metrics.stack_height); | |
| 3872 } | |
| 3873 ctx.fillStyle = "rgb(51, 51, 51)"; | |
| 3874 if (left_size > 0 && left_divider) { | |
| 3875 line_x = (left_end * metrics.stack_width) - 0.5; | |
| 3876 draw_dashed_line(ctx, [3], 0, line_x, 0, line_x, metrics.stack_height); | |
| 3877 } | |
| 3878 if (right_size > 0 && right_divider) { | |
| 3879 line_x = (right_start * metrics.stack_width) + 0.5; | |
| 3880 draw_dashed_line(ctx, [3], 0, line_x, 0, line_x, metrics.stack_height); | |
| 3881 } | |
| 3882 ctx.restore();//s8 | |
| 3883 } | |
| 3884 | |
| 3885 function size_logo_on_canvas(logo, canvas, show_names, scale) { | |
| 3886 "use strict"; | |
| 3887 var draw_name, draw_finetext, metrics; | |
| 3888 draw_name = (typeof show_names === "boolean" ? show_names : (logo.get_rows() > 1)); | |
| 3889 draw_finetext = (logo.fine_text.length > 0); | |
| 3890 if (canvas.width !== 0 && canvas.height !== 0) { | |
| 3891 return; | |
| 3892 } | |
| 3893 metrics = new LogoMetrics(canvas.getContext('2d'), | |
| 3894 logo.get_xlate_columns(), logo.get_rows(), draw_name, draw_finetext, logo.x_axis, logo.y_axis); | |
| 3895 if (typeof scale == "number") { | |
| 3896 //resize the canvas to fit the scaled logo | |
| 3897 canvas.width = metrics.summed_width * scale; | |
| 3898 canvas.height = metrics.summed_height * scale; | |
| 3899 } else { | |
| 3900 if (canvas.width === 0 && canvas.height === 0) { | |
| 3901 canvas.width = metrics.summed_width; | |
| 3902 canvas.height = metrics.summed_height; | |
| 3903 } else if (canvas.width === 0) { | |
| 3904 canvas.width = metrics.summed_width * (canvas.height / metrics.summed_height); | |
| 3905 } else if (canvas.height === 0) { | |
| 3906 canvas.height = metrics.summed_height * (canvas.width / metrics.summed_width); | |
| 3907 } | |
| 3908 } | |
| 3909 } | |
| 3910 | |
| 3911 function draw_logo_on_canvas(logo, canvas, show_names, scale) { | |
| 3912 "use strict"; | |
| 3913 var i, draw_name, draw_finetext, ctx, metrics, raster, pspm_i, pspm, | |
| 3914 offset, col_index, motif_position, ssc; | |
| 3915 ssc = false; | |
| 3916 draw_name = (typeof show_names === "boolean" ? show_names : (logo.get_rows() > 1)); | |
| 3917 draw_finetext = (logo.fine_text.length > 0); | |
| 3918 ctx = canvas.getContext('2d'); | |
| 3919 //assume that the user wants the canvas scaled equally so calculate what the best width for this image should be | |
| 3920 metrics = new LogoMetrics(ctx, logo.get_xlate_columns(), logo.get_rows(), draw_name, draw_finetext, logo.x_axis, logo.y_axis); | |
| 3921 if (typeof scale == "number") { | |
| 3922 //resize the canvas to fit the scaled logo | |
| 3923 canvas.width = metrics.summed_width * scale; | |
| 3924 canvas.height = metrics.summed_height * scale; | |
| 3925 } else { | |
| 3926 if (canvas.width === 0 && canvas.height === 0) { | |
| 3927 scale = 1; | |
| 3928 canvas.width = metrics.summed_width; | |
| 3929 canvas.height = metrics.summed_height; | |
| 3930 } else if (canvas.width === 0) { | |
| 3931 scale = canvas.height / metrics.summed_height; | |
| 3932 canvas.width = metrics.summed_width * scale; | |
| 3933 } else if (canvas.height === 0) { | |
| 3934 scale = canvas.width / metrics.summed_width; | |
| 3935 canvas.height = metrics.summed_height * scale; | |
| 3936 } else { | |
| 3937 scale = Math.min(canvas.width / metrics.summed_width, canvas.height / metrics.summed_height); | |
| 3938 } | |
| 3939 } | |
| 3940 // cache the raster based on the assumption that we will be drawing a lot | |
| 3941 // of logos the same size and alphabet | |
| 3942 if (typeof draw_logo_on_canvas.raster_cache === "undefined") { | |
| 3943 draw_logo_on_canvas.raster_cache = []; | |
| 3944 } | |
| 3945 for (i = 0; i < draw_logo_on_canvas.raster_cache.length; i++) { | |
| 3946 raster = draw_logo_on_canvas.raster_cache[i]; | |
| 3947 if (raster.get_alphabet().equals(logo.alphabet) && | |
| 3948 Math.abs(raster.get_scale() - scale) < 0.1) break; | |
| 3949 raster = null; | |
| 3950 } | |
| 3951 if (raster == null) { | |
| 3952 raster = new RasterizedAlphabet(logo.alphabet, scale, metrics.stack_font, metrics.stack_width); | |
| 3953 draw_logo_on_canvas.raster_cache.push(raster); | |
| 3954 } | |
| 3955 ctx = canvas.getContext('2d'); | |
| 3956 ctx.save();//s1 | |
| 3957 ctx.scale(scale, scale); | |
| 3958 ctx.save();//s2 | |
| 3959 ctx.save();//s7 | |
| 3960 //create margin | |
| 3961 ctx.translate(Math.round(metrics.pad_left), Math.round(metrics.pad_top)); | |
| 3962 for (pspm_i = 0; pspm_i < logo.get_rows(); ++pspm_i) { | |
| 3963 pspm = logo.get_pspm(pspm_i); | |
| 3964 offset = logo.get_offset(pspm_i); | |
| 3965 //optionally draw name if this isn't the last row or is the only row | |
| 3966 if (draw_name && (logo.get_rows() == 1 || pspm_i != (logo.get_rows()-1))) { | |
| 3967 ctx.save();//s4 | |
| 3968 ctx.translate(Math.round(metrics.summed_width/2), Math.round(metrics.name_height)); | |
| 3969 ctx.font = metrics.name_font; | |
| 3970 ctx.textAlign = "center"; | |
| 3971 ctx.fillText(pspm.name, 0, 0); | |
| 3972 ctx.restore();//s4 | |
| 3973 ctx.translate(0, Math.round(metrics.name_height + | |
| 3974 Math.min(0, metrics.name_spacer - metrics.y_num_height/2))); | |
| 3975 } | |
| 3976 //draw scale | |
| 3977 if (logo.y_axis) draw_scale(ctx, metrics, logo.alphabet.get_ic(), raster); | |
| 3978 ctx.save();//s5 | |
| 3979 //translate across past the scale | |
| 3980 if (logo.y_axis) { | |
| 3981 ctx.translate(Math.round(metrics.y_label_height + metrics.y_label_spacer + | |
| 3982 metrics.y_num_width + metrics.y_tic_width), Math.round(metrics.y_num_height / 2)); | |
| 3983 } | |
| 3984 //draw the trimming background | |
| 3985 if (pspm.get_left_trim() > 0 || pspm.get_right_trim() > 0) { | |
| 3986 var left_start = offset * logo.get_xlate_nsyms(); | |
| 3987 var left_end = (offset + pspm.get_left_trim()) * logo.get_xlate_nsyms(); | |
| 3988 var left_divider = true; | |
| 3989 if (left_end < logo.get_xlate_start() || left_start > logo.get_xlate_end()) { | |
| 3990 // no overlap | |
| 3991 left_start = 0; | |
| 3992 left_end = 0; | |
| 3993 left_divider = false; | |
| 3994 } else { | |
| 3995 if (left_start < logo.get_xlate_start()) { | |
| 3996 left_start = logo.get_xlate_start(); | |
| 3997 } | |
| 3998 if (left_end > logo.get_xlate_end()) { | |
| 3999 left_end = logo.get_xlate_end(); | |
| 4000 left_divider = false; | |
| 4001 } | |
| 4002 left_start -= logo.get_xlate_start(); | |
| 4003 left_end -= logo.get_xlate_start(); | |
| 4004 if (left_end < left_start) { | |
| 4005 left_start = 0; | |
| 4006 left_end = 0; | |
| 4007 left_divider = false; | |
| 4008 } | |
| 4009 } | |
| 4010 var right_end = (offset + pspm.get_motif_length()) * logo.get_xlate_nsyms(); | |
| 4011 //var right_start = right_end - (pspm.get_left_trim() * logo.get_xlate_nsyms()); | |
| 4012 var right_start = right_end - (pspm.get_right_trim() * logo.get_xlate_nsyms()); | |
| 4013 var right_divider = true; | |
| 4014 if (right_end < logo.get_xlate_start() || right_start > logo.get_xlate_end()) { | |
| 4015 // no overlap | |
| 4016 right_start = 0; | |
| 4017 right_end = 0; | |
| 4018 right_divider = false; | |
| 4019 } else { | |
| 4020 if (right_start < logo.get_xlate_start()) { | |
| 4021 right_start = logo.get_xlate_start(); | |
| 4022 right_divider = false; | |
| 4023 } | |
| 4024 if (right_end > logo.get_xlate_end()) { | |
| 4025 right_end = logo.get_xlate_end(); | |
| 4026 } | |
| 4027 right_start -= logo.get_xlate_start(); | |
| 4028 right_end -= logo.get_xlate_start(); | |
| 4029 if (right_end < right_start) { | |
| 4030 right_start = 0; | |
| 4031 right_end = 0; | |
| 4032 right_divider = false; | |
| 4033 } | |
| 4034 } | |
| 4035 draw_trim_background(ctx, metrics, left_start, left_end, left_divider, right_start, right_end, right_divider); | |
| 4036 } | |
| 4037 //draw letters | |
| 4038 var xlate_col; | |
| 4039 for (xlate_col = logo.get_xlate_start(); xlate_col < logo.get_xlate_end(); xlate_col++) { | |
| 4040 ctx.translate(metrics.stack_pad_left,0); | |
| 4041 col_index = Math.floor(xlate_col / logo.get_xlate_nsyms()); | |
| 4042 if (xlate_col % logo.get_xlate_nsyms() == 0) { | |
| 4043 if (col_index >= offset && col_index < (offset + pspm.get_motif_length())) { | |
| 4044 motif_position = col_index - offset; | |
| 4045 draw_stack_num(ctx, metrics, motif_position, raster); | |
| 4046 draw_stack(ctx, metrics, pspm.get_stack(motif_position, logo.alphabet, ssc), raster); | |
| 4047 } | |
| 4048 } else { | |
| 4049 if (col_index >= offset && col_index < (offset + pspm.get_motif_length())) { | |
| 4050 ctx.save();// s5.1 | |
| 4051 ctx.translate(0, Math.round(metrics.stack_height)); | |
| 4052 // TODO draw a dot or dash or something to indicate continuity of the motif | |
| 4053 ctx.restore(); //s5.1 | |
| 4054 } | |
| 4055 } | |
| 4056 ctx.translate(Math.round(metrics.stack_width), 0); | |
| 4057 } | |
| 4058 ctx.restore();//s5 | |
| 4059 ////optionally draw name if this is the last row but isn't the only row | |
| 4060 if (draw_name && (logo.get_rows() != 1 && pspm_i == (logo.get_rows()-1))) { | |
| 4061 //translate vertically past the stack and axis's | |
| 4062 ctx.translate(0, metrics.y_num_height/2 + metrics.stack_height + | |
| 4063 Math.max(metrics.y_num_height/2, metrics.x_num_above + metrics.x_num_width + metrics.name_spacer)); | |
| 4064 | |
| 4065 ctx.save();//s6 | |
| 4066 ctx.translate(metrics.summed_width/2, metrics.name_height); | |
| 4067 ctx.font = metrics.name_font; | |
| 4068 ctx.textAlign = "center"; | |
| 4069 ctx.fillText(pspm.name, 0, 0); | |
| 4070 ctx.restore();//s6 | |
| 4071 ctx.translate(0, metrics.name_height); | |
| 4072 } else { | |
| 4073 //translate vertically past the stack and axis's | |
| 4074 ctx.translate(0, metrics.y_num_height/2 + metrics.stack_height + | |
| 4075 Math.max(metrics.y_num_height/2, metrics.x_num_above + metrics.x_num_width)); | |
| 4076 } | |
| 4077 //if not the last row then add middle padding | |
| 4078 if (pspm_i != (logo.get_rows() -1)) { | |
| 4079 ctx.translate(0, metrics.pad_middle); | |
| 4080 } | |
| 4081 } | |
| 4082 ctx.restore();//s7 | |
| 4083 if (logo.fine_text.length > 0) { | |
| 4084 ctx.translate(metrics.summed_width - metrics.pad_right, metrics.summed_height - metrics.pad_bottom); | |
| 4085 ctx.font = metrics.fine_txt_font; | |
| 4086 ctx.textAlign = "right"; | |
| 4087 ctx.fillText(logo.fine_text, 0,0); | |
| 4088 } | |
| 4089 ctx.restore();//s2 | |
| 4090 ctx.restore();//s1 | |
| 4091 } | |
| 4092 | |
| 4093 function create_canvas(c_width, c_height, c_id, c_title, c_display) { | |
| 4094 "use strict"; | |
| 4095 var canvas = document.createElement("canvas"); | |
| 4096 //check for canvas support before attempting anything | |
| 4097 if (!canvas.getContext) { | |
| 4098 return null; | |
| 4099 } | |
| 4100 var ctx = canvas.getContext('2d'); | |
| 4101 //check for html5 text drawing support | |
| 4102 if (!supports_text(ctx)) { | |
| 4103 return null; | |
| 4104 } | |
| 4105 //size the canvas | |
| 4106 canvas.width = c_width; | |
| 4107 canvas.height = c_height; | |
| 4108 canvas.id = c_id; | |
| 4109 canvas.title = c_title; | |
| 4110 canvas.style.display = c_display; | |
| 4111 return canvas; | |
| 4112 } | |
| 4113 | |
| 4114 function logo_1(alphabet, fine_text, pspm) { | |
| 4115 "use strict"; | |
| 4116 var logo = new Logo(alphabet, fine_text); | |
| 4117 logo.add_pspm(pspm); | |
| 4118 return logo; | |
| 4119 } | |
| 4120 | |
| 4121 function logo_2(alphabet, fine_text, target, query, query_offset) { | |
| 4122 "use strict"; | |
| 4123 var logo = new Logo(alphabet, fine_text); | |
| 4124 if (query_offset < 0) { | |
| 4125 logo.add_pspm(target, -query_offset); | |
| 4126 logo.add_pspm(query); | |
| 4127 } else { | |
| 4128 logo.add_pspm(target); | |
| 4129 logo.add_pspm(query, query_offset); | |
| 4130 } | |
| 4131 return logo; | |
| 4132 } | |
| 4133 | |
| 4134 /* | |
| 4135 * Specifies an alternate source for an image. | |
| 4136 * If the image with the image_id specified has | |
| 4137 * not loaded then a generated logo will be used | |
| 4138 * to replace it. | |
| 4139 * | |
| 4140 * Note that the image must either have dimensions | |
| 4141 * or a scale must be set. | |
| 4142 */ | |
| 4143 function alternate_logo(logo, image_id, scale) { | |
| 4144 "use strict"; | |
| 4145 var image = document.getElementById(image_id); | |
| 4146 if (!image) { | |
| 4147 alert("Can't find specified image id (" + image_id + ")"); | |
| 4148 return; | |
| 4149 } | |
| 4150 //if the image has loaded then there is no reason to use the canvas | |
| 4151 if (image_ok(image)) { | |
| 4152 return; | |
| 4153 } | |
| 4154 //the image has failed to load so replace it with a canvas if we can. | |
| 4155 var canvas = create_canvas(image.width, image.height, image_id, image.title, image.style.display); | |
| 4156 if (canvas === null) { | |
| 4157 return; | |
| 4158 } | |
| 4159 //draw the logo on the canvas | |
| 4160 draw_logo_on_canvas(logo, canvas, null, scale); | |
| 4161 //replace the image with the canvas | |
| 4162 image.parentNode.replaceChild(canvas, image); | |
| 4163 } | |
| 4164 | |
| 4165 /* | |
| 4166 * Specifies that the element with the specified id | |
| 4167 * should be replaced with a generated logo. | |
| 4168 */ | |
| 4169 function replace_logo(logo, replace_id, scale, title_txt, display_style) { | |
| 4170 "use strict"; | |
| 4171 var element = document.getElementById(replace_id); | |
| 4172 if (!replace_id) { | |
| 4173 alert("Can't find specified id (" + replace_id + ")"); | |
| 4174 return; | |
| 4175 } | |
| 4176 //found the element! | |
| 4177 var canvas = create_canvas(50, 120, replace_id, title_txt, display_style); | |
| 4178 if (canvas === null) { | |
| 4179 return; | |
| 4180 } | |
| 4181 //draw the logo on the canvas | |
| 4182 draw_logo_on_canvas(logo, canvas, null, scale); | |
| 4183 //replace the element with the canvas | |
| 4184 element.parentNode.replaceChild(canvas, element); | |
| 4185 } | |
| 4186 | |
| 4187 /* | |
| 4188 * Fast string trimming implementation found at | |
| 4189 * http://blog.stevenlevithan.com/archives/faster-trim-javascript | |
| 4190 * | |
| 4191 * Note that regex is good at removing leading space but | |
| 4192 * bad at removing trailing space as it has to first go through | |
| 4193 * the whole string. | |
| 4194 */ | |
| 4195 function trim (str) { | |
| 4196 "use strict"; | |
| 4197 var ws, i; | |
| 4198 str = str.replace(/^\s\s*/, ''); | |
| 4199 ws = /\s/; i = str.length; | |
| 4200 while (ws.test(str.charAt(--i))); | |
| 4201 return str.slice(0, i + 1); | |
| 4202 } | |
| 4203 | |
| 4204 // | |
| 4205 // Delay drawing a logo | |
| 4206 // | |
| 4207 var DelayLogoTask = function(logo, canvas) { | |
| 4208 "use strict"; | |
| 4209 canvas.width = canvas.width; // clear canvas | |
| 4210 this.logo = logo; | |
| 4211 this.canvas = canvas; | |
| 4212 }; | |
| 4213 | |
| 4214 DelayLogoTask.prototype.run = function () { | |
| 4215 "use strict"; | |
| 4216 this.canvas.width = this.canvas.width; // clear canvas | |
| 4217 draw_logo_on_canvas(this.logo, this.canvas, false); | |
| 4218 }; | |
| 4219 | |
| 4220 /* | |
| 4221 * Make a canvas with the motif logo drawn on it. | |
| 4222 */ | |
| 4223 function make_logo(alphabet, pspm, height, rc, offset, className) { | |
| 4224 if (rc) pspm = pspm.copy().reverse_complement(alphabet); | |
| 4225 var logo = new Logo(alphabet); | |
| 4226 logo.add_pspm(pspm, offset); | |
| 4227 var canvas = document.createElement('canvas'); | |
| 4228 var sizeit = (height < 0); | |
| 4229 canvas.height = (sizeit ? -height : height); | |
| 4230 canvas.width = 0; | |
| 4231 canvas.className = className; | |
| 4232 if (sizeit) size_logo_on_canvas(logo, canvas, false); | |
| 4233 add_draw_task(canvas, new DelayLogoTask(logo, canvas)); | |
| 4234 return canvas; | |
| 4235 } | |
| 4236 </script> | |
| 4237 <script> | |
| 4238 | |
| 4239 // PRIVATE GLOBAL (uhoh) | |
| 4240 var _block_colour_lookup = {}; | |
| 4241 | |
| 4242 function block_colour(index) { | |
| 4243 function hsl2rgb(hue, saturation, lightness) { | |
| 4244 "use strict"; | |
| 4245 function _hue(p, q, t) { | |
| 4246 "use strict"; | |
| 4247 if (t < 0) t += 1; | |
| 4248 else if (t > 1) t -= 1; | |
| 4249 if (t < (1.0 / 6.0)) { | |
| 4250 return p + ((q - p) * 6.0 * t); | |
| 4251 } else if (t < 0.5) { | |
| 4252 return q; | |
| 4253 } else if (t < (2.0 / 3.0)) { | |
| 4254 return p + ((q - p) * ((2.0 / 3.0) - t) * 6.0); | |
| 4255 } else { | |
| 4256 return p; | |
| 4257 } | |
| 4258 } | |
| 4259 function _pad_hex(value) { | |
| 4260 var hex = Math.round(value * 255).toString(16); | |
| 4261 if (hex.length < 2) hex = "0" + hex; | |
| 4262 return hex; | |
| 4263 } | |
| 4264 var r, g, b, p, q; | |
| 4265 if (saturation == 0) { | |
| 4266 // achromatic (grayscale) | |
| 4267 r = lightness; | |
| 4268 g = lightness; | |
| 4269 b = lightness; | |
| 4270 } else { | |
| 4271 if (lightness < 0.5) { | |
| 4272 q = lightness * (1 + saturation); | |
| 4273 } else { | |
| 4274 q = lightness + saturation - (lightness * saturation); | |
| 4275 } | |
| 4276 p = (2 * lightness) - q; | |
| 4277 r = _hue(p, q, hue + (1.0 / 3.0)); | |
| 4278 g = _hue(p, q, hue); | |
| 4279 b = _hue(p, q, hue - (1.0 / 3.0)); | |
| 4280 } | |
| 4281 return "#" + _pad_hex(r) + _pad_hex(g) + _pad_hex(b); | |
| 4282 } | |
| 4283 if (typeof index !== "number" || index % 1 !== 0 || index < 0) return "#000000"; | |
| 4284 // check for override | |
| 4285 if (_block_colour_lookup[index] == null) { | |
| 4286 var start = 0; //red | |
| 4287 var sat = 100; | |
| 4288 var light = 50; | |
| 4289 var divisions = 1 << Math.ceil(Math.log(index + 1) / Math.LN2); | |
| 4290 hue = start + (360 / divisions) * ((index - (divisions >> 1)) * 2 + 1); | |
| 4291 // colour input fields only support values in the form #RRGGBB | |
| 4292 _block_colour_lookup[index] = hsl2rgb(hue / 360, sat / 100, light / 100); | |
| 4293 } | |
| 4294 return _block_colour_lookup[index]; | |
| 4295 } | |
| 4296 | |
| 4297 function set_block_colour(index, new_colour) { | |
| 4298 _block_colour_lookup[index] = new_colour; | |
| 4299 var blocks = document.querySelectorAll("div.block_motif[data-colour-index=\"" + index + "\"]"); | |
| 4300 var i; | |
| 4301 for (i = 0; i < blocks.length; i++) { | |
| 4302 blocks[i].style.backgroundColor = new_colour; | |
| 4303 } | |
| 4304 var swatches = document.querySelectorAll("div.legend_swatch[data-colour-index=\"" + index + "\"]"); | |
| 4305 var picker; | |
| 4306 for (i = 0; i < swatches.length; i++) { | |
| 4307 swatches[i].style.backgroundColor = new_colour; | |
| 4308 picker = swatches[i].querySelector("input[type=\"color\"]"); | |
| 4309 if (picker != null) picker.value = new_colour; | |
| 4310 } | |
| 4311 } | |
| 4312 | |
| 4313 function make_block_legend_entry(motif_name, motif_colour_index) { | |
| 4314 if (typeof make_block_legend_entry.has_colour_picker !== "boolean") { | |
| 4315 // test if colour picker is supported, based off Modernizer | |
| 4316 // see http://stackoverflow.com/a/7787648/66387 | |
| 4317 make_block_legend_entry.has_colour_picker = (function() { | |
| 4318 var doc_ele = document.documentElement; | |
| 4319 // We first check to see if the type we give it sticks.. | |
| 4320 var input_ele = document.createElement('input'); | |
| 4321 input_ele.setAttribute('type', 'color'); | |
| 4322 var value_ok = input_ele.type !== 'text'; | |
| 4323 if (value_ok) { | |
| 4324 // If the type does, we feed it a textual value, which shouldn't be valid. | |
| 4325 // If the value doesn't stick, we know there's input sanitization which infers a custom UI | |
| 4326 var smile = ':)'; | |
| 4327 input_ele.value = smile; | |
| 4328 input_ele.style.cssText = 'position:absolute;visibility:hidden;'; | |
| 4329 // chuck into DOM and force reflow for Opera bug in 11.00 | |
| 4330 // github.com/Modernizr/Modernizr/issues#issue/159 | |
| 4331 doc_ele.appendChild(input_ele); | |
| 4332 doc_ele.offsetWidth; | |
| 4333 value_ok = input_ele.value != smile; | |
| 4334 doc_ele.removeChild(input_ele); | |
| 4335 } | |
| 4336 return value_ok; | |
| 4337 })(); | |
| 4338 } | |
| 4339 var entry = document.createElement("div"); | |
| 4340 entry.className = "legend_entry"; | |
| 4341 var swatch; | |
| 4342 swatch = document.createElement("div"); | |
| 4343 swatch.className = "legend_swatch"; | |
| 4344 swatch.setAttribute("data-colour-index", motif_colour_index); | |
| 4345 swatch.style.backgroundColor = block_colour(motif_colour_index); | |
| 4346 if (make_block_legend_entry.has_colour_picker) { | |
| 4347 var picker = document.createElement("input"); | |
| 4348 picker.type = "color"; | |
| 4349 picker.value = block_colour(motif_colour_index); | |
| 4350 picker.addEventListener("change", function(e) { | |
| 4351 set_block_colour(motif_colour_index, picker.value); | |
| 4352 }, false); | |
| 4353 swatch.addEventListener("click", function(e) { | |
| 4354 picker.click(); | |
| 4355 }, false); | |
| 4356 swatch.appendChild(picker); | |
| 4357 } | |
| 4358 entry.appendChild(swatch); | |
| 4359 var name = document.createElement("div"); | |
| 4360 name.className = "legend_text"; | |
| 4361 name.appendChild(document.createTextNode(motif_name)); | |
| 4362 entry.appendChild(name); | |
| 4363 return entry; | |
| 4364 } | |
| 4365 | |
| 4366 function make_block_ruler(max_len) { | |
| 4367 var container = document.createElement("div"); | |
| 4368 container.className = "block_container"; | |
| 4369 var step; | |
| 4370 if (max_len < 50) { | |
| 4371 step = 1; | |
| 4372 } else if (max_len < 100) { | |
| 4373 step = 2; | |
| 4374 } else if (max_len < 200) { | |
| 4375 step = 4; | |
| 4376 } else if (max_len < 500) { | |
| 4377 step = 10; | |
| 4378 } else if (max_len < 1000) { | |
| 4379 step = 20; | |
| 4380 } else if (max_len < 2000) { | |
| 4381 step = 40; | |
| 4382 } else if (max_len < 5000) { | |
| 4383 step = 100; | |
| 4384 } else if (max_len < 10000) { | |
| 4385 step = 200; | |
| 4386 } else if (max_len < 20000) { | |
| 4387 step = 400; | |
| 4388 } else { | |
| 4389 step = Math.floor(max_len / 20000) * 400; | |
| 4390 } | |
| 4391 var peroid; | |
| 4392 if (max_len < 10) { | |
| 4393 peroid = 1; | |
| 4394 } else if (max_len < 20) { | |
| 4395 peroid = 2; | |
| 4396 } else { | |
| 4397 peroid = 5; | |
| 4398 } | |
| 4399 var i, cycle, offset, tic, label; | |
| 4400 for (i = 0, cycle = 0; i < max_len; i += step, cycle = (cycle + 1) % peroid) { | |
| 4401 offset = "" + ((i / max_len) * 100) + "%"; | |
| 4402 tic = document.createElement("div"); | |
| 4403 tic.style.left = offset; | |
| 4404 tic.className = (cycle == 0 ? "tic_major" : "tic_minor"); | |
| 4405 container.appendChild(tic); | |
| 4406 if (cycle == 0) { | |
| 4407 label = document.createElement("div"); | |
| 4408 label.className = "tic_label"; | |
| 4409 label.style.left = offset; | |
| 4410 label.appendChild(document.createTextNode(i)); | |
| 4411 container.appendChild(label); | |
| 4412 } | |
| 4413 } | |
| 4414 return container; | |
| 4415 } | |
| 4416 | |
| 4417 function _calculate_block_needle_drag_pos(e, data) { | |
| 4418 var mouse; | |
| 4419 e = e || window.event; | |
| 4420 if (e.pageX || ev.pageY) { | |
| 4421 mouse = {"x": e.pageX, "y": e.pageY}; | |
| 4422 } else { | |
| 4423 mouse = { | |
| 4424 x:e.clientX + document.body.scrollLeft - document.body.clientLeft, | |
| 4425 y:e.clientY + document.body.scrollTop - document.body.clientTop | |
| 4426 }; | |
| 4427 } | |
| 4428 var cont = data.container; | |
| 4429 var dragable_length = cont.clientWidth - | |
| 4430 (cont.style.paddingLeft ? cont.style.paddingLeft : 0) - | |
| 4431 (cont.style.paddingRight ? cont.style.paddingRight : 0); | |
| 4432 //I believe that the offset parent is the body | |
| 4433 //otherwise I would need to make this recursive | |
| 4434 //maybe clientLeft would work, but the explanation of | |
| 4435 //it is hard to understand and it apparently doesn't work | |
| 4436 //in firefox 2. | |
| 4437 var diff = mouse.x - cont.offsetLeft; | |
| 4438 if (diff < 0) diff = 0; | |
| 4439 if (diff > dragable_length) diff = dragable_length; | |
| 4440 var pos = Math.round(diff / dragable_length * data.max); | |
| 4441 if (pos > data.len) pos = data.len; | |
| 4442 return pos; | |
| 4443 } | |
| 4444 | |
| 4445 function _update_block_needle_drag(e, data, done) { | |
| 4446 "use strict"; | |
| 4447 var pos = _calculate_block_needle_drag_pos(e, data); | |
| 4448 // read the needle positions | |
| 4449 var left = parseInt(data.llabel.textContent, 10) - data.off - 1; | |
| 4450 var right = parseInt(data.rlabel.textContent, 10) - data.off; | |
| 4451 // validate needle positions | |
| 4452 if (left >= data.len) left = data.len - 1; | |
| 4453 if (left < 0) left = 0; | |
| 4454 if (right > data.len) right = data.len; | |
| 4455 if (right <= left) right = left + 1; | |
| 4456 // calculate the new needle positions | |
| 4457 if (data.moveboth) { | |
| 4458 var size = right - left; | |
| 4459 if (data.isleft) { | |
| 4460 if ((pos + size) > data.len) pos = data.len - size; | |
| 4461 left = pos; | |
| 4462 right = pos + size; | |
| 4463 } else { | |
| 4464 if ((pos - size) < 0) pos = size; | |
| 4465 left = pos - size; | |
| 4466 right = pos; | |
| 4467 } | |
| 4468 } else { | |
| 4469 if (data.isleft) { | |
| 4470 if (pos >= right) pos = right - 1; | |
| 4471 left = pos; | |
| 4472 } else { | |
| 4473 if (pos <= left) pos = left + 1; | |
| 4474 right = pos; | |
| 4475 } | |
| 4476 } | |
| 4477 // update the needle positions | |
| 4478 data.lneedle.style.left = "" + (left / data.max * 100) + "%"; | |
| 4479 data.llabel.textContent = "" + (left + data.off + 1); | |
| 4480 data.rneedle.style.left = "" + (right / data.max * 100) + "%"; | |
| 4481 data.rlabel.textContent = "" + (right + data.off); | |
| 4482 data.handler(left, right, done); | |
| 4483 } | |
| 4484 | |
| 4485 function _make_block_needle_drag_start_handler(isleft, data) { | |
| 4486 return function (e) { | |
| 4487 data.isleft = isleft; | |
| 4488 data.moveboth = !(e.shiftKey); | |
| 4489 document.addEventListener("mousemove", data.drag_during, false); | |
| 4490 document.addEventListener("mouseup", data.drag_end, false); | |
| 4491 }; | |
| 4492 } | |
| 4493 | |
| 4494 function _make_block_needle_drag_end_handler(data) { | |
| 4495 return function (e) { | |
| 4496 document.removeEventListener("mousemove", data.drag_during, false); | |
| 4497 document.removeEventListener("mouseup", data.drag_end, false); | |
| 4498 _update_block_needle_drag(e, data, true); | |
| 4499 }; | |
| 4500 } | |
| 4501 | |
| 4502 function _make_block_needle_drag_during_handler(data) { | |
| 4503 return function (e) { | |
| 4504 _update_block_needle_drag(e, data, false); | |
| 4505 }; | |
| 4506 } | |
| 4507 | |
| 4508 // private function used by make_block_container | |
| 4509 function _make_block_needle(isleft, value, data) { | |
| 4510 var vbar = document.createElement('div'); | |
| 4511 vbar.className = "block_needle " + (isleft ? "left" : "right"); | |
| 4512 vbar.style.left = "" + (value / data.max * 100)+ "%"; | |
| 4513 var label = document.createElement('div'); | |
| 4514 label.className = "block_handle " + (isleft ? "left" : "right"); | |
| 4515 // The needles sit between the sequence positions, so the left one sits at the | |
| 4516 // start and the right at the end. This is why 1 is added to the displayed | |
| 4517 // value for a left handle as the user doesn't need to know about this detail | |
| 4518 label.textContent = "" + (isleft ? value + data.off + 1 : value + data.off); | |
| 4519 label.unselectable = "on"; // so IE and Opera don't select the text, others are done in css | |
| 4520 label.title = "Drag to move the displayed range. Hold shift and drag to change " + (isleft ? "lower" : "upper") + " bound of the range."; | |
| 4521 vbar.appendChild(label); | |
| 4522 if (isleft) { | |
| 4523 data.lneedle = vbar; | |
| 4524 data.llabel = label; | |
| 4525 } else { | |
| 4526 data.rneedle = vbar; | |
| 4527 data.rlabel = label; | |
| 4528 } | |
| 4529 label.addEventListener("mousedown", _make_block_needle_drag_start_handler(isleft, data), false); | |
| 4530 return vbar; | |
| 4531 } | |
| 4532 | |
| 4533 function make_block_container(is_stranded, has_both_strands, max_len, show_len, offset, range_handler) { | |
| 4534 offset = (offset != null ? offset : 0); | |
| 4535 // make the container for the block diagram | |
| 4536 var container = document.createElement("div"); | |
| 4537 container.className = "block_container"; | |
| 4538 container.setAttribute("data-max", max_len); | |
| 4539 container.setAttribute("data-off", offset); | |
| 4540 if (is_stranded) { | |
| 4541 var plus = document.createElement("div"); | |
| 4542 plus.appendChild(document.createTextNode("+")); | |
| 4543 plus.className = "block_plus_sym"; | |
| 4544 container.appendChild(plus); | |
| 4545 if (has_both_strands) { | |
| 4546 var minus = document.createElement("div"); | |
| 4547 minus.appendChild(document.createTextNode("-")); | |
| 4548 minus.className = "block_minus_sym"; | |
| 4549 container.appendChild(minus); | |
| 4550 } | |
| 4551 } | |
| 4552 var rule = document.createElement("div"); | |
| 4553 rule.className = "block_rule"; | |
| 4554 rule.style.width = ((show_len / max_len) * 100) + "%"; | |
| 4555 container.appendChild(rule); | |
| 4556 if (range_handler != null) { | |
| 4557 var range_data = { | |
| 4558 "max": max_len, | |
| 4559 "len": show_len, | |
| 4560 "off": offset, | |
| 4561 "handler": range_handler, | |
| 4562 "container": container, | |
| 4563 "lneedle": null, "llabel": null, | |
| 4564 "rneedle": null, "rlabel": null, | |
| 4565 "isleft": false, "moveboth" : false | |
| 4566 }; | |
| 4567 range_data.drag_during = _make_block_needle_drag_during_handler(range_data); | |
| 4568 range_data.drag_end = _make_block_needle_drag_end_handler(range_data); | |
| 4569 container.appendChild(_make_block_needle(false, 1, range_data)); // add right first so z-index works | |
| 4570 container.appendChild(_make_block_needle(true, 0, range_data)); | |
| 4571 } | |
| 4572 return container; | |
| 4573 } | |
| 4574 | |
| 4575 function make_block_label(container, max_len, pos, length, message) { | |
| 4576 "use strict"; | |
| 4577 var label = document.createElement("div"); | |
| 4578 label.className = "block_label"; | |
| 4579 label.style.left = (((pos + (length / 2)) / max_len) * 100) + "%"; | |
| 4580 label.appendChild(document.createTextNode(message)); | |
| 4581 container.appendChild(label); | |
| 4582 } | |
| 4583 | |
| 4584 function make_block(container, max_len, | |
| 4585 site_pos, site_len, site_pvalue, site_rc, site_colour_index, site_secondary) { | |
| 4586 "use strict"; | |
| 4587 var block_height, block, block_region1, block_region2; | |
| 4588 var max_block_height = 12; | |
| 4589 var max_pvalue = 1e-10; | |
| 4590 // calculate the height of the block | |
| 4591 block_height = (site_pvalue < max_pvalue ? max_block_height : | |
| 4592 (Math.log(site_pvalue) / Math.log(max_pvalue)) * max_block_height); | |
| 4593 if (block_height < 1) block_height = 1; | |
| 4594 // create a block to represent the motif | |
| 4595 block = document.createElement("div"); | |
| 4596 block.className = "block_motif" + (site_secondary ? " scanned_site" : "") + (site_rc ? " bottom" : " top"); | |
| 4597 block.style.left = ((site_pos / max_len) * 100) + "%"; | |
| 4598 block.style.top = (!site_rc ? max_block_height - block_height : | |
| 4599 max_block_height + 1) + "px"; | |
| 4600 block.style.width = ((site_len / max_len) * 100) + "%"; | |
| 4601 block.style.height = block_height + "px"; | |
| 4602 block.style.backgroundColor = block_colour(site_colour_index); | |
| 4603 block.setAttribute("data-colour-index", site_colour_index); | |
| 4604 // add to container | |
| 4605 container.appendChild(block); | |
| 4606 var activator = function (e) { | |
| 4607 toggle_class(block, "active", true); | |
| 4608 var new_e = new e.constructor(e.type, e); | |
| 4609 block.dispatchEvent(new_e); | |
| 4610 }; | |
| 4611 var deactivator = function (e) { | |
| 4612 toggle_class(block, "active", false); | |
| 4613 var new_e = new e.constructor(e.type, e); | |
| 4614 block.dispatchEvent(new_e); | |
| 4615 } | |
| 4616 // create a larger region to detect mouseover for the block | |
| 4617 block_region1 = document.createElement("div"); | |
| 4618 block_region1.className = "block_region top" + | |
| 4619 (site_secondary ? " scanned_site" : "") + (site_rc ? "" : " main"); | |
| 4620 block_region1.style.left = block.style.left; | |
| 4621 block_region1.style.width = block.style.width; | |
| 4622 block_region1.addEventListener('mouseover', activator, false); | |
| 4623 block_region1.addEventListener('mouseout', deactivator, false); | |
| 4624 container.appendChild(block_region1); | |
| 4625 block_region2 = document.createElement("div"); | |
| 4626 block_region2.className = "block_region bottom" + | |
| 4627 (site_secondary ? " scanned_site" : "") + (site_rc ? " main" : ""); | |
| 4628 block_region2.style.left = block.style.left; | |
| 4629 block_region2.style.width = block.style.width; | |
| 4630 block_region2.addEventListener('mouseover', activator, false); | |
| 4631 block_region2.addEventListener('mouseout', deactivator, false); | |
| 4632 container.appendChild(block_region2); | |
| 4633 return block; | |
| 4634 } | |
| 4635 | |
| 4636 function set_block_needle_positions(containingNode, start, end) { | |
| 4637 var container, lneedle, llabel, rneedle, rlabel, max, off, left, right; | |
| 4638 container = (/\bblock_container\b/.test(containingNode.className) ? containingNode : containingNode.querySelector(".block_container")); | |
| 4639 max = parseInt(container.getAttribute("data-max"), 10); | |
| 4640 off = parseInt(container.getAttribute("data-off"), 10); | |
| 4641 left = start - off; | |
| 4642 right = end - off; | |
| 4643 lneedle = containingNode.querySelector(".block_needle.left"); | |
| 4644 llabel = lneedle.querySelector(".block_handle.left"); | |
| 4645 rneedle = containingNode.querySelector(".block_needle.right"); | |
| 4646 rlabel = rneedle.querySelector(".block_handle.right"); | |
| 4647 // update the needle positions | |
| 4648 lneedle.style.left = "" + (left / max * 100) + "%"; | |
| 4649 llabel.textContent = "" + (left + off + 1); | |
| 4650 rneedle.style.left = "" + (right / max * 100) + "%"; | |
| 4651 rlabel.textContent = "" + (right + off); | |
| 4652 } | |
| 4653 | |
| 4654 function get_block_needle_positions(containingNode) { | |
| 4655 var container, llabel, rlabel, max, off, left, right; | |
| 4656 container = (/\bblock_container\b/.test(containingNode.className) ? containingNode : containingNode.querySelector(".block_container")); | |
| 4657 max = parseInt(container.getAttribute("data-max"), 10); | |
| 4658 off = parseInt(container.getAttribute("data-off"), 10); | |
| 4659 llabel = containingNode.querySelector(".block_needle.left > .block_handle.left"); | |
| 4660 rlabel = containingNode.querySelector(".block_needle.right > .block_handle.right"); | |
| 4661 left = parseInt(llabel.textContent, 10) - off - 1; | |
| 4662 right = parseInt(rlabel.textContent, 10) - off; | |
| 4663 return {"start": left + off, "end": right + off}; | |
| 4664 } | |
| 4665 </script> | |
| 4666 <script> | |
| 4667 function make_alpha_bg_table(alph, freqs) { | |
| 4668 function colour_symbol(index) { | |
| 4669 var span = document.createElement("span"); | |
| 4670 span.appendChild(document.createTextNode(alph.get_symbol(index))); | |
| 4671 span.style.color = alph.get_colour(index); | |
| 4672 span.className = "alpha_symbol"; | |
| 4673 return span; | |
| 4674 } | |
| 4675 var table, thead, tbody, row, th, span, i; | |
| 4676 // create table | |
| 4677 table = document.createElement("table"); | |
| 4678 table.className = "alpha_bg_table"; | |
| 4679 // create header | |
| 4680 thead = document.createElement("thead"); | |
| 4681 table.appendChild(thead); | |
| 4682 row = thead.insertRow(thead.rows.length); | |
| 4683 if (alph.has_complement()) { | |
| 4684 add_text_header_cell(row, "Name", "pop_alph_name"); | |
| 4685 if (freqs != null) add_text_header_cell(row, "Freq.", "pop_alph_freq"); | |
| 4686 if (alph.has_bg()) add_text_header_cell(row, "Bg.", "pop_alph_bg"); | |
| 4687 add_text_header_cell(row, ""); | |
| 4688 add_text_header_cell(row, ""); | |
| 4689 add_text_header_cell(row, ""); | |
| 4690 if (alph.has_bg()) add_text_header_cell(row, "Bg.", "pop_alph_bg"); | |
| 4691 if (freqs != null) add_text_header_cell(row, "Freq.", "pop_alph_freq"); | |
| 4692 add_text_header_cell(row, "Name", "pop_alph_name"); | |
| 4693 } else { | |
| 4694 add_text_header_cell(row, ""); | |
| 4695 add_text_header_cell(row, "Name", "pop_alph_name"); | |
| 4696 if (freqs != null) add_text_header_cell(row, "Freq.", "pop_alph_freq"); | |
| 4697 if (alph.has_bg()) add_text_header_cell(row, "Bg.", "pop_alph_bg"); | |
| 4698 } | |
| 4699 // add alphabet entries | |
| 4700 tbody = document.createElement("tbody"); | |
| 4701 table.appendChild(tbody); | |
| 4702 if (alph.has_complement()) { | |
| 4703 for (i = 0; i < alph.get_size_core(); i++) { | |
| 4704 var c = alph.get_complement(i); | |
| 4705 if (i > c) continue; | |
| 4706 row = tbody.insertRow(tbody.rows.length); | |
| 4707 add_text_cell(row, alph.get_name(i)); | |
| 4708 if (freqs != null) add_text_cell(row, "" + freqs[i]); | |
| 4709 if (alph.has_bg()) add_text_cell(row, "" + alph.get_bg_freq(i)); | |
| 4710 add_cell(row, colour_symbol(i)); | |
| 4711 add_text_cell(row, "~"); | |
| 4712 add_cell(row, colour_symbol(c)); | |
| 4713 if (alph.has_bg()) add_text_cell(row, "" + alph.get_bg_freq(c)); | |
| 4714 if (freqs != null) add_text_cell(row, "" + freqs[c]); | |
| 4715 add_text_cell(row, alph.get_name(c)); | |
| 4716 } | |
| 4717 } else { | |
| 4718 for (i = 0; i < alph.get_size_core(); i++) { | |
| 4719 row = tbody.insertRow(tbody.rows.length); | |
| 4720 add_cell(row, colour_symbol(i)); | |
| 4721 add_text_cell(row, alph.get_name(i)); | |
| 4722 if (freqs != null) add_text_cell(row, "" + freqs[i]); | |
| 4723 if (alph.has_bg()) add_text_cell(row, "" + alph.get_bg_freq(i)); | |
| 4724 } | |
| 4725 } | |
| 4726 return table; | |
| 4727 } | |
| 4728 | |
| 4729 </script> | |
| 4730 <script> | |
| 4731 // | |
| 4732 // simple-shared-doc.js | |
| 4733 // | |
| 4734 | |
| 4735 // | |
| 4736 // Function to redirect to appropriate doc file. | |
| 4737 // | |
| 4738 function get_doc_text(pgm, doc_type, extra, extra2) { | |
| 4739 switch (pgm) { | |
| 4740 case 'shared': | |
| 4741 return(get_shared_doc_text(doc_type, extra, extra2)); | |
| 4742 case 'ame': | |
| 4743 return(get_ame_doc_text(doc_type, extra, extra2)); | |
| 4744 case 'centrimo': | |
| 4745 return(get_centrimo_doc_text(doc_type, extra, extra2)); | |
| 4746 case 'discovery': | |
| 4747 return(get_discovery_doc_text(doc_type, extra, extra2)); | |
| 4748 case 'fimo': | |
| 4749 return(get_fimo_doc_text(doc_type, extra, extra2)); | |
| 4750 case 'gomo': | |
| 4751 return(get_gomo_doc_text(doc_type, extra, extra2)); | |
| 4752 case 'mcast': | |
| 4753 return(get_mcast_doc_text(doc_type, extra, extra2)); | |
| 4754 case 'meme-chip': | |
| 4755 return(get_meme_chip_doc_text(doc_type, extra, extra2)); | |
| 4756 case 'momo': | |
| 4757 return(get_momo_doc_text(doc_type, extra, extra2)); | |
| 4758 case 'sea': | |
| 4759 return(get_sea_doc_text(doc_type, extra, extra2)); | |
| 4760 case 'spamo': | |
| 4761 return(get_spamo_doc_text(doc_type, extra, extra2)); | |
| 4762 case 'streme': | |
| 4763 return(get_streme_doc_text(doc_type, extra, extra2)); | |
| 4764 case 'tgene': | |
| 4765 return(get_tgene_doc_text(doc_type, extra, extra2)); | |
| 4766 case 'tomtom': | |
| 4767 return(get_tomtom_doc_text(doc_type, extra, extra2)); | |
| 4768 case 'xstreme': | |
| 4769 return(get_xstreme_doc_text(doc_type, extra, extra2)); | |
| 4770 default: | |
| 4771 return("<b>Unknown program type: <font color=red>" + pgm + "</font></b>"); | |
| 4772 } | |
| 4773 } // get_doc_text | |
| 4774 | |
| 4775 // | |
| 4776 // Function to replace the innerHTML of element "id" with the HTML indicated by "doc_type". | |
| 4777 // Easier to read and update than the more flexible approach in shared-doc.js. | |
| 4778 // | |
| 4779 function print_doc(id, pgm, doc_type, extra) { | |
| 4780 document.getElementById(id).insertAdjacentHTML('beforeend', get_doc_text(pgm, doc_type, extra)); | |
| 4781 } // print_doc | |
| 4782 | |
| 4783 // | |
| 4784 // Function to replace the innerHTML of element "id" with an HTML paragraph | |
| 4785 // containing the text for 'pgm' and 'doc_type'. | |
| 4786 // This function can be used in help pop-ups. | |
| 4787 // | |
| 4788 function print_doc_para(id, pgm, doc_type, extra, extra2) { | |
| 4789 html = "<p>" + get_doc_text(pgm, doc_type, extra, extra2) + "</p>"; | |
| 4790 document.getElementById(id).insertAdjacentHTML('beforeend', html); | |
| 4791 } // print_doc_para | |
| 4792 | |
| 4793 // | |
| 4794 // Function to return the Shared HTML text of a given type. | |
| 4795 // This function can be used directly to document the output format (xx-output-format.html) | |
| 4796 // and indirectly via print_doc_para for help pop-ups in the actual output HTML, | |
| 4797 // to prevent duplication of documentation. | |
| 4798 // | |
| 4799 function get_shared_doc_text(doc_type, extra, extra2) { | |
| 4800 if (extra == undefined) {extra = ""}; | |
| 4801 if (extra2 == undefined) {extra2 = ""}; | |
| 4802 switch (doc_type) { | |
| 4803 case 'fasta-coordinates-name': | |
| 4804 return(` | |
| 4805 The sequence IDs in the FASTA header lines are used as the source of sequence names. | |
| 4806 The sequence ID is the string following the initial '>' up to the first white space character. | |
| 4807 If the sequence ID is in an accepted <a href="` + site_url + `/doc/fasta-coordinates-format.html">FASTA Coordinates Format</a> | |
| 4808 (e.g., UCSC or Galaxy format), and you did NOT specify the <code>` + extra + `-no-pgc</code> option, | |
| 4809 then the coordinates are removed from the sequence ID to create the sequence name. | |
| 4810 `); | |
| 4811 case 'fasta-coordinates-brief': | |
| 4812 return(` | |
| 4813 The sequence IDs in the FASTA header lines are used as the source of sequence names. | |
| 4814 The sequence ID is the string following the initial '>' up to the first white space character. | |
| 4815 If the sequence ID is in an accepted <a href="` + site_url + `/doc/fasta-coordinates-format.html">FASTA Coordinates Format</a> | |
| 4816 (e.g., UCSC or Galaxy format), and you did NOT specify the <code>` + extra + `-no-pgc</code> option, | |
| 4817 then the coordinates are removed from the sequence ID to create the sequence name, | |
| 4818 and the starting coordinate given in the sequence ID will be used as the | |
| 4819 coordinate of the first position of the sequence. | |
| 4820 Otherwise, the coordinate of the first position of the sequence is taken as 1 and the entire | |
| 4821 sequence ID is used as the sequence name. | |
| 4822 `); | |
| 4823 case 'fasta-coordinates': | |
| 4824 return(` | |
| 4825 For sequence coordinates embedded in FASTA sequence headers to be parsed | |
| 4826 correctly by MEME Suite programs, the sequence ID in the FASTA header should have one of the two | |
| 4827 following formats: | |
| 4828 | |
| 4829 <h4>UCSC Format</h4> | |
| 4830 <div style='margin: 5px 0'> | |
| 4831 ><span class='pdat'>sequence name</span>:<span class='pdat' | |
| 4832 >starting position</span>-<span class='pdat'>ending position</span> | |
| 4833 </div> | |
| 4834 where | |
| 4835 <ul style='margin-top: 0; margin-bottom: 5px'> | |
| 4836 <li><span class='pdat'>sequence name</span> is the name of the genomic sequence,</li> | |
| 4837 <li><span class='pdat'>starting position</span> is the genomic position of the first base and</li> | |
| 4838 <li><span class='pdat'>ending position</span> is the genomic position of the final base.</li> | |
| 4839 </ul> | |
| 4840 <b>Example:</b> <span class="pdata">chr1:156887119-156887619</span> | |
| 4841 | |
| 4842 <h4>Galaxy Format</h4> | |
| 4843 <div style='margin: 5px 0'> | |
| 4844 ><span class='pdat'>assembly name</span>_<span class='pdat' | |
| 4845 >sequence name</span>_<span class='pdat' | |
| 4846 >starting position</span>_<span class='pdat' | |
| 4847 >ending position</span>_<span class='pdat' | |
| 4848 >strand</span> | |
| 4849 </div> | |
| 4850 where | |
| 4851 <ul style='margin-top: 0; margin-bottom: 5px'> | |
| 4852 <li><span class='pdat'>assembly name</span> is the name of the genomic assembly,</li> | |
| 4853 <li><span class='pdat'>sequence name</span> is the name of the genomic sequence,</li> | |
| 4854 <li><span class='pdat'>starting position</span> is the genomic position of the first base and</li> | |
| 4855 <li><span class='pdat'>ending position</span> is the genomic position of the final base.</li> | |
| 4856 </ul> | |
| 4857 <b>Example:</b> <span class="pdata">>mm9_chr18_75759530_7575972_-</span> | |
| 4858 | |
| 4859 <p> | |
| 4860 For both formats, the following rules also apply: | |
| 4861 <ul> | |
| 4862 <li>The the coordinates are 1-start, fully-closed. This means | |
| 4863 that the first base of the chromosome is position "1", and the bases at the | |
| 4864 first and last positions of the given range are included.</li> | |
| 4865 <li>The <span class='pdat'>sequence name</span> may not contain any white space.</li> | |
| 4866 <li>The <span class='pdat'>assembly name</span> is not optional.</li> | |
| 4867 <li>The <span class='pdat'>strand</span> is ignored.</li> | |
| 4868 <li>When no valid genomic coordinates are found in a FASTA sequence header, the starting | |
| 4869 position of the sequence is taken to be position 1.</li> | |
| 4870 </ul> | |
| 4871 </p> | |
| 4872 `); | |
| 4873 case 'motif-db': | |
| 4874 return(` | |
| 4875 The name of ` + extra2 + ` a file of motifs ("motif database file") that contains ` + extra + ` | |
| 4876 `); | |
| 4877 case 'motif-id': | |
| 4878 return(` | |
| 4879 The name of the ` + extra + ` motif, which is unique ` + extra2 + ` in the motif database file. | |
| 4880 `); | |
| 4881 case 'motif-alt-id': | |
| 4882 return(` | |
| 4883 An alternate name for the ` + extra + ` motif that may be provided ` + extra2 + ` in the motif database file. | |
| 4884 `); | |
| 4885 case 'motif-width': | |
| 4886 return(` | |
| 4887 The width of the motif. No gaps are allowed in motifs supplied to ` + extra + ` | |
| 4888 as it only works for motifs of a fixed width. | |
| 4889 `); | |
| 4890 case 'motif-cons': | |
| 4891 return(` | |
| 4892 A consensus sequence computed from the ` + extra + ` motif (as described <a href="#consensus_doc">below</a>). | |
| 4893 `); | |
| 4894 case 'motif-match-score': | |
| 4895 return(` | |
| 4896 ` + extra2 + ` The motif match score of a position in a sequence is | |
| 4897 computed by summing the appropriate entry from each column of the | |
| 4898 position-dependent scoring matrix that represents the motif. ` + extra + ` | |
| 4899 `); | |
| 4900 case 'motif-match-p-value': | |
| 4901 return(` | |
| 4902 The <i>p</i>-value of a motif match is the probability of a single random | |
| 4903 subsequence of the length of the motif <a href="javascript:help_refine('pop_motif_match_score')">scoring</a> | |
| 4904 at least as well as the observed match. | |
| 4905 `); | |
| 4906 case 'bh-q-value': | |
| 4907 if (extra2 == "") extra2 = "match"; | |
| 4908 return(` | |
| 4909 The q-value is the minimum False Discovery Rate (FDR) required to consider this | |
| 4910 ` + extra2 + ` significant.</br>` + | |
| 4911 get_shared_doc_text('bh-q-value-method', extra, extra2) + ` | |
| 4912 `); | |
| 4913 case 'bh-q-value-method': | |
| 4914 return(` | |
| 4915 <br>` + extra + ` estimates q-values from all the ` + extra2 + ` <i>p</i>-values | |
| 4916 using the method proposed by Benjamini & Hochberg (<i>Journal of the Royal Statistical Society B</i>, 57:289-300, 1995). | |
| 4917 See also Storey JD, Tibshirani R. Statistical significance for | |
| 4918 genome-wide studies, <i>Proc. Natl. Acad. Sci. USA</i> (2003) <b>100</b>:9440–9445. | |
| 4919 `); | |
| 4920 case 'sdb-name': | |
| 4921 return(` | |
| 4922 The name of the (FASTA) sequence database file. | |
| 4923 `); | |
| 4924 case 'sdb-psp': | |
| 4925 return(` | |
| 4926 The name of the position specific priors (PSP) file. | |
| 4927 `); | |
| 4928 case 'sdb-dist': | |
| 4929 return(` | |
| 4930 The name of the binned distribution of priors file. | |
| 4931 `); | |
| 4932 case 'sdb-count': | |
| 4933 return(` | |
| 4934 The number of sequences in the database. | |
| 4935 `); | |
| 4936 case 'sdb-letters': | |
| 4937 return(` | |
| 4938 The number of letters in the sequence database. | |
| 4939 `); | |
| 4940 case 'lastmod': | |
| 4941 return(` | |
| 4942 The date of the last modification to the ` + extra + ` database. | |
| 4943 `); | |
| 4944 case 'sequence-id': | |
| 4945 return(` | |
| 4946 The identifier of the sequence (from the FASTA sequence header line)` + extra + ` | |
| 4947 `); | |
| 4948 case 'sequence-desc': | |
| 4949 return(` | |
| 4950 The description appearing after the identifier of the sequence in the FASTA header line. | |
| 4951 `); | |
| 4952 case 'alph-name': | |
| 4953 return(` | |
| 4954 The name of the alphabet symbol. | |
| 4955 `); | |
| 4956 case 'alph-bg': | |
| 4957 return(` | |
| 4958 The frequency of the alphabet symbol as defined by the background model. | |
| 4959 `); | |
| 4960 case 'match-start': | |
| 4961 return(` | |
| 4962 The start position of the ` + extra + `. | |
| 4963 `); | |
| 4964 case 'match-stop': | |
| 4965 return(` | |
| 4966 The end position of the ` + extra + `. | |
| 4967 `); | |
| 4968 case 'match-start-seq': | |
| 4969 return(` | |
| 4970 The start position of the ` + extra + `; 1-based sequence coordinates. | |
| 4971 `); | |
| 4972 case 'match-stop-seq': | |
| 4973 return(` | |
| 4974 The end position of the ` + extra + `; 1-based sequence coordinates. | |
| 4975 `); | |
| 4976 case 'match-start-genomic': | |
| 4977 return(` | |
| 4978 The start position of the ` + extra + `; genomic coordinates. | |
| 4979 `); | |
| 4980 case 'match-stop-genomic': | |
| 4981 return(` | |
| 4982 The end position of the ` + extra + `; genomic coordinates. | |
| 4983 `); | |
| 4984 case 'motif-consensus': | |
| 4985 return(` | |
| 4986 <p id="consensus_doc"> | |
| 4987 A <b>consensus sequence</b> is constructed from each column in a | |
| 4988 motif's frequency matrix using the <b>"50% rule"</b> | |
| 4989 as follows: | |
| 4990 </p> | |
| 4991 <ol> | |
| 4992 <li>The letter frequencies in the column are sorted in decreasing order.</li> | |
| 4993 <li>Letters with frequency less 50% of the maximum are discarded.</li> | |
| 4994 <li>The letter used in this position in the consensus sequence is determined | |
| 4995 by the first rule below that applies:</li> | |
| 4996 <ul> | |
| 4997 <li>If there is only one letter left, or if the remaining letters exactly match | |
| 4998 an ambiguous symbol in the alphabet, the <b>letter</b> or <b>ambiguous symbol</b>, | |
| 4999 respectively, is used.</li> | |
| 5000 <li>Otherwise, if the remaining set contains at least 50% of the core | |
| 5001 symbols in the alphabet, the alphabet's <b>wildcard</b> | |
| 5002 (e.g., "N" for DNA or RNA, and "X" for protein) is used.</li> | |
| 5003 <li>Otherwise, the letter with the <b>maximum frequency</b> is used.</li> | |
| 5004 </ul> | |
| 5005 </ol> | |
| 5006 `); | |
| 5007 default: | |
| 5008 return("Error--Unrecognized shared doc_type: " + doc_type); | |
| 5009 } | |
| 5010 } // get_shared_doc_text | |
| 5011 </script> | |
| 5012 <script> | |
| 5013 // | |
| 5014 // discovery_doc.js | |
| 5015 // Documentation common to motif discovery tools. | |
| 5016 // | |
| 5017 | |
| 5018 // | |
| 5019 // Function to return the HTML text of a given type. | |
| 5020 // This function can be used directly to document the output format (xx-output-format.html) | |
| 5021 // and indirectly via print_doc_para for help pop-ups in the actual output HTML, | |
| 5022 // to prevent duplication of documentation. | |
| 5023 // | |
| 5024 function get_discovery_doc_text(doc_type, extra, extra2) { | |
| 5025 if (extra == undefined) {extra = ""}; | |
| 5026 if (extra2 == undefined) {extra2 = ""}; | |
| 5027 | |
| 5028 switch (doc_type) { | |
| 5029 case 'motif_logo': | |
| 5030 return(` | |
| 5031 The sequence logo of the motif. | |
| 5032 The rules for construction logos are given in | |
| 5033 the <i>Description</i> section of the documentation for the MEME Suite utility | |
| 5034 <a href="` + extra + `/doc/ceqlogo.html#description">ceqlogo</a>. | |
| 5035 `); | |
| 5036 case 'motif_rc_logo': | |
| 5037 return(` | |
| 5038 The sequence logo of the reverse complement motif. | |
| 5039 The rules for construction logos are given in | |
| 5040 the <i>Description</i> section of the documentation for the MEME Suite utility | |
| 5041 <a href="` + extra + `/doc/ceqlogo.html#description">ceqlogo</a>. | |
| 5042 `); | |
| 5043 case 'more': | |
| 5044 return(` | |
| 5045 Click on the blue symbol below to reveal detailed information about the motif. | |
| 5046 `); | |
| 5047 case 'submit_dl': | |
| 5048 return(` | |
| 5049 Click on the blue symbol below to reveal options allowing you | |
| 5050 to submit this motif to another MEME Suite motif analysis program, to download this | |
| 5051 motif in various text formats, or to download a sequence "logo" of | |
| 5052 this motif PNG or EPS format.</p> | |
| 5053 <h5>Supported Programs</h5> | |
| 5054 <dl> | |
| 5055 <dt>Tomtom</dt> | |
| 5056 <dd>Tomtom is a tool for searching for similar known motifs. | |
| 5057 [<a href="` + extra + `/doc/tomtom.html?man_type=web">manual</a>]</dd> | |
| 5058 <dt>MAST</dt> | |
| 5059 <dd>MAST is a tool for searching biological sequence databases for | |
| 5060 sequences that contain one or more of a group of known motifs. | |
| 5061 [<a href="` + extra + `/doc/mast.html?man_type=web">manual</a>]</dd> | |
| 5062 <dt>FIMO</dt> | |
| 5063 <dd>FIMO is a tool for searching biological sequence databases for | |
| 5064 sequences that contain one or more known motifs. | |
| 5065 [<a href="` + extra + `/doc/fimo.html?man_type=web">manual</a>]</dd> | |
| 5066 <dt>GOMo</dt> | |
| 5067 <dd>GOMo is a tool for identifying possible roles (Gene Ontology | |
| 5068 terms) for DNA binding motifs. | |
| 5069 [<a href="` + extra + `/doc/gomo.html?man_type=web">manual</a>]</dd> | |
| 5070 <dt>SpaMo</dt> | |
| 5071 <dd>SpaMo is a tool for inferring possible transcription factor | |
| 5072 complexes by finding motifs with enriched spacings. | |
| 5073 [<a href="` + extra + `/doc/spamo.html?man_type=web">manual</a>]</dd> | |
| 5074 </dl> | |
| 5075 `); | |
| 5076 case 'site_distr': | |
| 5077 return(` | |
| 5078 This plot shows the positional distribution of the best match to the motif in the ` + | |
| 5079 extra + ` sequences. | |
| 5080 Only matches with scores at least the ` + | |
| 5081 extra2 + ` score threshold are considered. | |
| 5082 The plot is smoothed with a triangular function whose width is 5% of the maximum ` + | |
| 5083 extra + ` sequence length. | |
| 5084 The position of the dotted vertical line indicates whether the sequences were | |
| 5085 aligned on their left ends, centers, or right ends, respectively. | |
| 5086 `); | |
| 5087 case 'site_hist': | |
| 5088 return(` | |
| 5089 This histogram shows the distribution of the <b>number</b> of matches to the motif in the ` + | |
| 5090 extra + ` sequences with at least one match. | |
| 5091 Only matches with scores at least the ` + | |
| 5092 extra2 + ` score threshold are considered. | |
| 5093 `); | |
| 5094 default: | |
| 5095 return("Error--Unrecognized discovery doc_type: " + doc_type); | |
| 5096 } | |
| 5097 } // get_discovery_doc_text | |
| 5098 </script> | |
| 5099 <script> | |
| 5100 // | |
| 5101 // submit_or_download_motif.js | |
| 5102 // | |
| 5103 | |
| 5104 function make_submit_or_download_motif_form(id, site_url, program) { | |
| 5105 var html = ` | |
| 5106 <div class="popup_wrapper"> | |
| 5107 <div class="popup" style="display:none; top: -150px;" id="download"> | |
| 5108 <div> | |
| 5109 <div style="float:right; "> | |
| 5110 <div id="outpop_close" class="close" tabindex="0">x</div> | |
| 5111 </div> | |
| 5112 <h2 class="mainh" style="margin:0; padding:0;">Submit or Download</h2> | |
| 5113 <div style="clear:both"></div> | |
| 5114 </div> | |
| 5115 <div style="height:100px"> | |
| 5116 <div style="float:right; width: 30px;"> | |
| 5117 <div id="outpop_prev" class="navarrow" tabindex="0"> | |
| 5118 <span class="inactive">⇧</span><span class="active">⬆</span> | |
| 5119 </div> | |
| 5120 <div id="outpop_num" class="navnum"></div> | |
| 5121 <div id="outpop_next" class="navarrow" tabindex="0"> | |
| 5122 <span class="inactive">⇩</span><span class="active">⬇</span> | |
| 5123 </div> | |
| 5124 </div> | |
| 5125 <div id="logo_box" style="height: 100px; margin-right: 40px;"> | |
| 5126 <canvas id="outpop_logo" height="100" width="250"></canvas> | |
| 5127 <canvas id="outpop_logo_rc" height="100" width="250"></canvas> | |
| 5128 </div> | |
| 5129 </div> | |
| 5130 <!-- tabs start --> | |
| 5131 <div class="tabArea top"> | |
| 5132 <span id="outpop_tab_1" class="tab">Submit Motif</span><span | |
| 5133 id="outpop_tab_2" class="tab middle">Download Motif</span><span | |
| 5134 id="outpop_tab_3" class="tab middle">Download Logo</span> | |
| 5135 </div> | |
| 5136 <div class="tabMain top"> | |
| 5137 <!-- Submit to another program --> | |
| 5138 <div id="outpop_pnl_1"> | |
| 5139 <h4 class="compact">Submit to program</h4> | |
| 5140 <table id="programs" class="programs"> | |
| 5141 <tr> | |
| 5142 <td><input type="radio" name="program" value="tomtom" id="submit_tomtom"></td> | |
| 5143 <td><label for="submit_tomtom">Tomtom</label></td> | |
| 5144 <td><label for="submit_tomtom">Find similar motifs in | |
| 5145 published libraries or a library you supply.</label></td> | |
| 5146 </tr> | |
| 5147 <tr> | |
| 5148 <td><input type="radio" name="program" value="fimo" id="submit_fimo"></td> | |
| 5149 <td><label for="submit_fimo">FIMO</label></td> | |
| 5150 <td><label for="submit_fimo">Find motif occurrences in | |
| 5151 sequence data.</label></td> | |
| 5152 </tr> | |
| 5153 <tr> | |
| 5154 <td><input type="radio" name="program" value="mast" id="submit_mast"></td> | |
| 5155 <td><label for="submit_mast">MAST</label></td> | |
| 5156 <td><label for="submit_mast">Rank sequences by affinity to | |
| 5157 groups of motifs.</label></td> | |
| 5158 </tr> | |
| 5159 <tr class="dna_only"> | |
| 5160 <td><input type="radio" name="program" value="gomo" id="submit_gomo"></td> | |
| 5161 <td><label for="submit_gomo">GOMo</label></td> | |
| 5162 <td><label for="submit_gomo">Identify possible roles (Gene | |
| 5163 Ontology terms) for motifs.</label></td> | |
| 5164 </tr> | |
| 5165 <tr class="dna_only"> | |
| 5166 <td><input type="radio" name="program" value="spamo" id="submit_spamo"></td> | |
| 5167 <td><label for="submit_spamo">SpaMo</label></td> | |
| 5168 <td><label for="submit_spamo">Find other motifs that are | |
| 5169 enriched at specific close spacings which might imply the existence of a complex.</label></td> | |
| 5170 </tr> | |
| 5171 </table> | |
| 5172 </div> | |
| 5173 <!-- download text format --> | |
| 5174 <div id="outpop_pnl_2"> | |
| 5175 <div> | |
| 5176 <label for="text_format">Format:</label> | |
| 5177 <select id="text_format"> | |
| 5178 <option value="0">Count Matrix</option> | |
| 5179 <option value="1">Probability Matrix</option> | |
| 5180 <option value="2">Minimal MEME</option> ` + | |
| 5181 (program == "MEME" ? ` | |
| 5182 <option value="3">FASTA</option> | |
| 5183 <option value="4">Raw</option> ` : ``) + ` | |
| 5184 </select> | |
| 5185 </div> | |
| 5186 <textarea id="outpop_text" name="content" | |
| 5187 style="width:99%; white-space: pre; word-wrap: normal; overflow-x: scroll;" | |
| 5188 rows="8" readonly="readonly" wrap="off"></textarea> | |
| 5189 <a id="outpop_text_dl" download="meme.txt" href=""></a> | |
| 5190 </div> | |
| 5191 <!-- download logo format --> | |
| 5192 <div id="outpop_pnl_3"> | |
| 5193 <form id="logo_form" method="post" action=""> | |
| 5194 <input type="hidden" name="program" value=" ` + program + `"/> | |
| 5195 <input type="hidden" id="logo_motifs" name="motifs" value=""/> | |
| 5196 <input type="hidden" id="logo_id1" name="id1" value=""/> | |
| 5197 <table> | |
| 5198 <tr> | |
| 5199 <td><label for="logo_format">Format:</label></td> | |
| 5200 <td> | |
| 5201 <select id="logo_format" name="png"> | |
| 5202 <option value="1">PNG (for web)</option> | |
| 5203 <option value="0">EPS (for publication)</option> | |
| 5204 </select> | |
| 5205 </td> | |
| 5206 </tr> | |
| 5207 <tr> | |
| 5208 <td><label for="logo_rc">Orientation:</label></td> | |
| 5209 <td> | |
| 5210 <select id="logo_rc" name="rc1"> | |
| 5211 <option value="0">Normal</option> | |
| 5212 <option value="1" id="logo_rc_option">Reverse Complement</option> | |
| 5213 </select> | |
| 5214 </td> | |
| 5215 </tr> | |
| 5216 <tr> | |
| 5217 <td><label for="logo_ssc">Small Sample Correction:</label></td> | |
| 5218 <td> | |
| 5219 <input type="hidden" id="logo_err" name="errbars" value="0"/> | |
| 5220 <select id="logo_ssc" name="ssc"> | |
| 5221 <option value="0">Off</option> | |
| 5222 <option value="1">On</option> | |
| 5223 </select> | |
| 5224 </td> | |
| 5225 </tr> | |
| 5226 <tr> | |
| 5227 <td><label for="logo_width">Width:</label></td> | |
| 5228 <td> | |
| 5229 <input type="text" id="logo_width" size="4" placeholder="default" name="width"/> cm | |
| 5230 </td> | |
| 5231 </tr> | |
| 5232 <tr> | |
| 5233 <td><label for="logo_height">Height:</label></td> | |
| 5234 <td> | |
| 5235 <input type="text" id="logo_height" size="4" placeholder="default" name="height"/> cm | |
| 5236 </td> | |
| 5237 </tr> | |
| 5238 </table> | |
| 5239 </form> | |
| 5240 </div> | |
| 5241 <!-- Buttons --> | |
| 5242 <div> | |
| 5243 <div style="float:left;"> | |
| 5244 <input type="button" id="outpop_do" value="Submit" /> | |
| 5245 </div> | |
| 5246 <div style="float:right;"> | |
| 5247 <input id="outpop_cancel" type="button" value="Cancel" /> | |
| 5248 </div> | |
| 5249 <div style="clear:both;"></div> | |
| 5250 </div> | |
| 5251 </div> | |
| 5252 </div> | |
| 5253 </div> | |
| 5254 `; | |
| 5255 document.getElementById(id).insertAdjacentHTML('beforeend', html); | |
| 5256 $("logo_form").action = site_url + "/utilities/generate_logo"; | |
| 5257 } // make_submit_or_download_motif_form | |
| 5258 | |
| 5259 // | |
| 5260 // Functions to update the submit_or_download_motif form. | |
| 5261 // | |
| 5262 | |
| 5263 // | |
| 5264 // Initialise and display the download popup. | |
| 5265 // | |
| 5266 function action_show_outpop(e, ordinal) { | |
| 5267 "use strict"; | |
| 5268 function init() { | |
| 5269 "use strict"; | |
| 5270 var close_btn, next_btn, prev_btn, cancel_btn, do_btn; | |
| 5271 var tab1, tab2, tab3; | |
| 5272 var pnl1, pnl2, pnl3; | |
| 5273 var format_list; | |
| 5274 var tbl_submit, inputs, i, default_prog; | |
| 5275 close_btn = $("outpop_close"); | |
| 5276 close_btn.addEventListener("click", action_hide_outpop, false); | |
| 5277 close_btn.addEventListener("keydown", action_hide_outpop, false); | |
| 5278 next_btn = $("outpop_next"); | |
| 5279 next_btn.addEventListener("click", action_outpop_next, false); | |
| 5280 next_btn.addEventListener("keydown", action_outpop_next, false); | |
| 5281 prev_btn = $("outpop_prev"); | |
| 5282 prev_btn.addEventListener("click", action_outpop_prev, false); | |
| 5283 prev_btn.addEventListener("keydown", action_outpop_prev, false); | |
| 5284 cancel_btn = $("outpop_cancel"); | |
| 5285 cancel_btn.addEventListener("click", action_hide_outpop, false); | |
| 5286 do_btn = $("outpop_do"); | |
| 5287 do_btn.addEventListener("click", action_outpop_submit, false); | |
| 5288 tab1 = $("outpop_tab_1"); | |
| 5289 tab1.tabIndex = 0; | |
| 5290 tab1.addEventListener("click", action_outpop_tab, false); | |
| 5291 tab1.addEventListener("keydown", action_outpop_tab, false); | |
| 5292 tab2 = $("outpop_tab_2"); | |
| 5293 tab2.tabIndex = 0; | |
| 5294 tab2.addEventListener("click", action_outpop_tab, false); | |
| 5295 tab2.addEventListener("keydown", action_outpop_tab, false); | |
| 5296 tab3 = $("outpop_tab_3"); | |
| 5297 tab3.tabIndex = 0; | |
| 5298 tab3.addEventListener("click", action_outpop_tab, false); | |
| 5299 tab3.addEventListener("keydown", action_outpop_tab, false); | |
| 5300 pnl1 = $("outpop_pnl_1"); | |
| 5301 pnl2 = $("outpop_pnl_2"); | |
| 5302 pnl3 = $("outpop_pnl_3"); | |
| 5303 toggle_class(tab1, "activeTab", true); | |
| 5304 toggle_class(tab2, "activeTab", false); | |
| 5305 toggle_class(tab3, "activeTab", false); | |
| 5306 pnl1.style.display = "block"; | |
| 5307 pnl2.style.display = "none"; | |
| 5308 pnl3.style.display = "none"; | |
| 5309 format_list = $("text_format"); | |
| 5310 format_list.addEventListener("change", action_outpop_format, false); | |
| 5311 // setup program selection | |
| 5312 tbl_submit = $("programs"); | |
| 5313 // when not dna, hide the inputs for programs that require dna motifs | |
| 5314 toggle_class(tbl_submit, "alphabet_dna", current_alphabet.has_complement());//TODO alphabet_dna is a bad name for a field when allowing custom alphabets | |
| 5315 // add a click listener for the radio buttons | |
| 5316 inputs = tbl_submit.querySelectorAll("input[type='radio']"); | |
| 5317 for (i = 0; i < inputs.length; i++) { | |
| 5318 inputs[i].addEventListener("click", action_outpop_program, false); | |
| 5319 } | |
| 5320 // ensure that a default program option is selected for DNA and Protein | |
| 5321 default_prog = document.getElementById(current_alphabet.has_complement() ? "submit_tomtom" : "submit_fimo"); //TODO Tomtom might require a more strict definition of DNA | |
| 5322 default_prog.checked = true; | |
| 5323 action_outpop_program.call(default_prog); | |
| 5324 // disable reverse-complement when not DNA | |
| 5325 $("logo_rc_option").disabled = !current_alphabet.has_complement(); | |
| 5326 // set errorbars on when ssc is on | |
| 5327 $("logo_ssc").addEventListener("change", action_outpop_ssc, false); | |
| 5328 } | |
| 5329 // store the focused element | |
| 5330 action_hide_outpop.last_active = document.activeElement; | |
| 5331 if (!e) e = window.event; | |
| 5332 if (e.type === "keydown") { | |
| 5333 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
| 5334 return; | |
| 5335 } | |
| 5336 // stop a submit or something like that | |
| 5337 e.preventDefault(); | |
| 5338 } | |
| 5339 // hide the help popup | |
| 5340 help_popup(); | |
| 5341 // on first load initilize the popup | |
| 5342 if (!action_show_outpop.ready) { | |
| 5343 init(); | |
| 5344 action_show_outpop.ready = true; | |
| 5345 } | |
| 5346 update_outpop_motif(ordinal - 1); | |
| 5347 // display the download popup | |
| 5348 $("grey_out_page").style.display = "block"; | |
| 5349 $("download").style.display = "block"; | |
| 5350 $("outpop_close").focus(); | |
| 5351 } // action_show_output | |
| 5352 | |
| 5353 // | |
| 5354 // Hide the submit or download popup. | |
| 5355 // | |
| 5356 function action_hide_outpop(e) { | |
| 5357 if (!e) e = window.event; | |
| 5358 if (e.type === "keydown") { | |
| 5359 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
| 5360 return; | |
| 5361 } | |
| 5362 // stop a submit or something like that | |
| 5363 e.preventDefault(); | |
| 5364 } | |
| 5365 $("download").style.display = "none"; | |
| 5366 $("grey_out_page").style.display = "none"; | |
| 5367 if (typeof action_hide_outpop.last_active !== "undefined") { | |
| 5368 action_hide_outpop.last_active.focus(); | |
| 5369 } | |
| 5370 } // action_hide_outpop | |
| 5371 | |
| 5372 /* | |
| 5373 * Show the next motif in the download popup. | |
| 5374 */ | |
| 5375 function action_outpop_next(e) { | |
| 5376 if (!e) e = window.event; | |
| 5377 if (e.type === "keydown") { | |
| 5378 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
| 5379 return; | |
| 5380 } | |
| 5381 // stop a submit or something like that | |
| 5382 e.preventDefault(); | |
| 5383 } | |
| 5384 update_outpop_motif(current_motif + 1); | |
| 5385 } // action_outpop_next | |
| 5386 | |
| 5387 /* | |
| 5388 * Show the previous motif in the download popup. | |
| 5389 */ | |
| 5390 function action_outpop_prev(e) { | |
| 5391 if (!e) e = window.event; | |
| 5392 if (e.type === "keydown") { | |
| 5393 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
| 5394 return; | |
| 5395 } | |
| 5396 // stop a submit or something like that | |
| 5397 e.preventDefault(); | |
| 5398 } | |
| 5399 update_outpop_motif(current_motif - 1); | |
| 5400 } // action_outpop_prev | |
| 5401 | |
| 5402 /* | |
| 5403 * Highlight the selected row in the program list. | |
| 5404 */ | |
| 5405 function action_outpop_program() { | |
| 5406 "use strict"; | |
| 5407 var table, tr, rows, i; | |
| 5408 tr = find_parent_tag(this, "TR"); | |
| 5409 table = find_parent_tag(tr, "TABLE"); | |
| 5410 rows = table.querySelectorAll("tr"); | |
| 5411 for (i = 0; i < rows.length; i++) { | |
| 5412 toggle_class(rows[i], "selected", rows[i] === tr); | |
| 5413 } | |
| 5414 } // action_outpop_program | |
| 5415 | |
| 5416 /* | |
| 5417 * Submit the motif to the selected program. | |
| 5418 */ | |
| 5419 function action_outpop_submit(e) { | |
| 5420 "use strict"; | |
| 5421 var form, input, program, motifs; | |
| 5422 // find out which program is selected | |
| 5423 var radios, i; | |
| 5424 radios = document.getElementsByName("program"); | |
| 5425 program = "fimo"; // default to fimo, since it works with all alphabet types | |
| 5426 for (i = 0; i < radios.length; i++) { | |
| 5427 if (radios[i].checked) program = radios[i].value; | |
| 5428 } | |
| 5429 | |
| 5430 motifs = motif_minimal_meme(data.motifs[current_motif]); | |
| 5431 form = document.createElement("form"); | |
| 5432 form.setAttribute("method", "post"); | |
| 5433 form.setAttribute("action", site_url + "/tools/" + program); | |
| 5434 | |
| 5435 input = document.createElement("input"); | |
| 5436 input.setAttribute("type", "hidden"); | |
| 5437 input.setAttribute("name", "motifs_embed"); | |
| 5438 input.setAttribute("value", motifs); | |
| 5439 form.appendChild(input); | |
| 5440 | |
| 5441 var current_file = location.pathname.substring(location.pathname.lastIndexOf('/')+1); | |
| 5442 input = document.createElement("input"); | |
| 5443 input.setAttribute("type", "hidden"); | |
| 5444 input.setAttribute("name", "motifs_name"); | |
| 5445 input.setAttribute("value", "motif number " + (current_motif+1) + " from " + current_file); | |
| 5446 form.appendChild(input); | |
| 5447 | |
| 5448 document.body.appendChild(form); | |
| 5449 form.submit(); | |
| 5450 document.body.removeChild(form); | |
| 5451 } // action_outpop_submit(e) | |
| 5452 | |
| 5453 /* | |
| 5454 * Enable error bars when small sample correction is enabled. | |
| 5455 */ | |
| 5456 function action_outpop_ssc() { | |
| 5457 "use strict"; | |
| 5458 $("logo_err").value = $("logo_ssc").value; | |
| 5459 } // action_outpop_ssc | |
| 5460 | |
| 5461 // | |
| 5462 // Update the motif logos and format download text in the popup. | |
| 5463 // This is called whenever the current motif changes. | |
| 5464 // | |
| 5465 function update_outpop_motif(index) { | |
| 5466 "use strict"; | |
| 5467 var motifs, motif, pspm, logo, canvas, num; | |
| 5468 motifs = data["motifs"]; | |
| 5469 if (index < 0 || index >= motifs.length) {return;} | |
| 5470 current_motif = index; | |
| 5471 motif = motifs[index]; | |
| 5472 pspm = new Pspm(motif["pwm"]); | |
| 5473 logo = new Logo(current_alphabet, ""); | |
| 5474 logo.add_pspm(pspm, 0); | |
| 5475 canvas = $("outpop_logo"); | |
| 5476 canvas.width = canvas.width; // clear canvas | |
| 5477 draw_logo_on_canvas(logo, canvas, false); | |
| 5478 canvas = $("outpop_logo_rc"); | |
| 5479 canvas.width = canvas.width; // clear rc canvas | |
| 5480 if (data.options.strands === "both" || data.options.revcomp) { | |
| 5481 pspm.reverse_complement(current_alphabet); | |
| 5482 logo = new Logo(current_alphabet, ""); | |
| 5483 logo.add_pspm(pspm, 0); | |
| 5484 draw_logo_on_canvas(logo, canvas, false); | |
| 5485 } | |
| 5486 num = $("outpop_num"); | |
| 5487 num.innerHTML = ""; | |
| 5488 num.appendChild(document.createTextNode("" + (index + 1))); | |
| 5489 update_outpop_format(index); | |
| 5490 } // action_outpop_motif | |
| 5491 | |
| 5492 // | |
| 5493 // Create the download menu. | |
| 5494 // | |
| 5495 function update_outpop_format(index) { | |
| 5496 var motif = data.motifs[index]; | |
| 5497 var fn = [motif_count_matrix, motif_prob_matrix, motif_minimal_meme, motif_fasta, motif_raw]; | |
| 5498 var suffix = ["_counts.txt", "_freqs.txt", ".meme", "_fasta.txt", "_raw.txt"]; | |
| 5499 var format = parseInt($("text_format").value); | |
| 5500 var text = fn[format](motif); | |
| 5501 prepare_download(text, "text/plain", motif.id + suffix[format], $("outpop_text_dl")); | |
| 5502 $("outpop_text").value = text; | |
| 5503 } // update_outpop_format | |
| 5504 | |
| 5505 /* | |
| 5506 * Update the text in the download format popup. | |
| 5507 */ | |
| 5508 function action_outpop_format() { | |
| 5509 update_outpop_format(current_motif); | |
| 5510 } // action_outpop_format | |
| 5511 | |
| 5512 /* | |
| 5513 * Download the format text. | |
| 5514 * Wire the link containing the data URI text to a download button so it looks | |
| 5515 * the same as the server submit stuff. | |
| 5516 */ | |
| 5517 function action_outpop_download_motif(e) { | |
| 5518 $("outpop_text_dl").click(); | |
| 5519 } // action_outpop_download_motif | |
| 5520 | |
| 5521 /* | |
| 5522 * Download the motif logo. | |
| 5523 * The EPS format can be calculated locally in Javascript | |
| 5524 */ | |
| 5525 function action_outpop_download_logo(e) { | |
| 5526 "use strict"; | |
| 5527 var motif = data.motifs[current_motif]; | |
| 5528 if ($("logo_format").value === "0") { // EPS | |
| 5529 var pspm, logo, eps; | |
| 5530 var logo_rc, logo_ssc, logo_width, logo_height; | |
| 5531 logo_rc = ($("logo_rc").value === "1"); | |
| 5532 logo_ssc = ($("logo_ssc").value === "1"); | |
| 5533 logo_width = parseFloat($("logo_width").value); | |
| 5534 if (isNaN(logo_width) || !isFinite(logo_width) || logo_width <= 0) logo_width = null; | |
| 5535 logo_height = parseFloat($("logo_height").value); | |
| 5536 if (isNaN(logo_height) || !isFinite(logo_height) || logo_height <= 0) logo_height = null; | |
| 5537 // create a PSPM from the motif | |
| 5538 pspm = motif_pspm(motif); | |
| 5539 if (logo_rc) pspm.reverse_complement(current_alphabet); | |
| 5540 logo = new Logo(current_alphabet); | |
| 5541 logo.add_pspm(pspm, 0); | |
| 5542 eps = logo.as_eps({"ssc": logo_ssc, "logo_width": logo_width, "logo_height": logo_height}); | |
| 5543 prepare_download(eps, "application/postscript", motif.id + (logo_rc ? "_rc" : "") + ".eps"); | |
| 5544 } else { | |
| 5545 $("logo_motifs").value = motif_minimal_meme(motif); | |
| 5546 $("logo_id1").value = motif.id; | |
| 5547 $("logo_form").submit(); | |
| 5548 } | |
| 5549 } // action_outpop_download_logo | |
| 5550 | |
| 5551 /* | |
| 5552 * Change the selected tab in the download popup. | |
| 5553 */ | |
| 5554 function action_outpop_tab(e) { | |
| 5555 "use strict"; | |
| 5556 var tab1, tab2, tab3, pnl1, pnl2, pnl3, do_btn; | |
| 5557 if (!e) e = window.event; | |
| 5558 if (e.type === "keydown") { | |
| 5559 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
| 5560 return; | |
| 5561 } | |
| 5562 // stop a submit or something like that | |
| 5563 e.preventDefault(); | |
| 5564 } | |
| 5565 tab1 = $("outpop_tab_1"); | |
| 5566 tab2 = $("outpop_tab_2"); | |
| 5567 tab3 = $("outpop_tab_3"); | |
| 5568 pnl1 = $("outpop_pnl_1"); | |
| 5569 pnl2 = $("outpop_pnl_2"); | |
| 5570 pnl3 = $("outpop_pnl_3"); | |
| 5571 do_btn = $("outpop_do"); | |
| 5572 | |
| 5573 toggle_class(tab1, "activeTab", (this === tab1)); | |
| 5574 toggle_class(tab2, "activeTab", (this === tab2)); | |
| 5575 toggle_class(tab3, "activeTab", (this === tab3)); | |
| 5576 pnl1.style.display = ((this === tab1) ? "block" : "none"); | |
| 5577 pnl2.style.display = ((this === tab2) ? "block" : "none"); | |
| 5578 pnl3.style.display = ((this === tab3) ? "block" : "none"); | |
| 5579 do_btn.value = ((this === tab1) ? "Submit" : "Download"); | |
| 5580 do_btn.removeEventListener("click", action_outpop_submit, false); | |
| 5581 do_btn.removeEventListener("click", action_outpop_download_logo, false); | |
| 5582 do_btn.removeEventListener("click", action_outpop_download_motif, false); | |
| 5583 if (this === tab1) { | |
| 5584 do_btn.addEventListener("click", action_outpop_submit, false); | |
| 5585 } else if (this === tab2) { | |
| 5586 do_btn.addEventListener("click", action_outpop_download_motif, false); | |
| 5587 } else { | |
| 5588 do_btn.addEventListener("click", action_outpop_download_logo, false); | |
| 5589 } | |
| 5590 } // action_outpop_tab | |
| 5591 | |
| 5592 function motif_fasta(motif) { | |
| 5593 "use strict"; | |
| 5594 var sites, site, seq, sequences, sequence, i, num, counter, out; | |
| 5595 counter = {}; | |
| 5596 sequences = data["sequence_db"]["sequences"]; | |
| 5597 sites = motif["sites"]; | |
| 5598 out = ""; | |
| 5599 for (i = 0; i < sites.length; i++) { | |
| 5600 site = sites[i]; | |
| 5601 seq = site["seq"]; | |
| 5602 sequence = sequences[seq]; | |
| 5603 counter[seq] = (num = counter[seq]) ? (++num) : (num = 1); // inc counter | |
| 5604 if (i !== 0) {out += "\n";} | |
| 5605 out += ">" + sequence["name"] + "_site_" + num + " offset= " + site["pos"] + | |
| 5606 (site["rc"] ? " RC\n" : "\n"); | |
| 5607 out += site["match"]; | |
| 5608 } | |
| 5609 return out; | |
| 5610 } // motif_fasta | |
| 5611 | |
| 5612 function motif_raw(motif) { | |
| 5613 "use strict"; | |
| 5614 var sites, i, out; | |
| 5615 sites = motif["sites"]; | |
| 5616 out = ""; | |
| 5617 for (i = 0; i < sites.length; i++) { | |
| 5618 if (i !== 0) {out += "\n";} | |
| 5619 out += sites[i]["match"]; | |
| 5620 } | |
| 5621 return out; | |
| 5622 } // motif_raw | |
| 5623 | |
| 5624 /* | |
| 5625 * Create a pspm for the given motif data | |
| 5626 */ | |
| 5627 function motif_pspm(motif) { | |
| 5628 var pwm = motif.pwm; | |
| 5629 var name = motif.id; | |
| 5630 var ltrim = 0; | |
| 5631 var rtrim = 0; | |
| 5632 var nsites = motif.nsites; | |
| 5633 var sig = (current_program === "STREME" ? motif.test_pvalue : motif.evalue); | |
| 5634 return new Pspm(pwm, name, ltrim, rtrim, nsites, sig, null, motif.alt, current_program); | |
| 5635 } // motif_pspm | |
| 5636 | |
| 5637 /* | |
| 5638 * Create a count matrix from the given motif data | |
| 5639 */ | |
| 5640 function motif_count_matrix(motif) { | |
| 5641 return motif_pspm(motif).as_count_matrix(); | |
| 5642 } // motif_count_matrix | |
| 5643 | |
| 5644 /* | |
| 5645 * Create a probablity matrix from the given motif data | |
| 5646 */ | |
| 5647 function motif_prob_matrix(motif) { | |
| 5648 return motif_pspm(motif).as_probability_matrix(); | |
| 5649 } // motif_prob_matrix | |
| 5650 | |
| 5651 function motif_minimal_meme(motif) { | |
| 5652 var strands; | |
| 5653 if (current_program === "STREME") { | |
| 5654 strands = (data.options.strands === "both" ? 2 : 1); | |
| 5655 } else { | |
| 5656 strands = (data.options.revcomp ? 2 : 1); | |
| 5657 } | |
| 5658 return motif_pspm(motif).as_meme({ | |
| 5659 "with_header": true, | |
| 5660 "with_pspm": true, | |
| 5661 "with_pssm": (current_program === "MEME" ? true : false), | |
| 5662 "version": data["version"], | |
| 5663 "alphabet": current_alphabet, | |
| 5664 "strands": strands | |
| 5665 }); | |
| 5666 } | |
| 5667 </script> | |
| 5668 <script> | |
| 5669 var current_program = "MEME"; | |
| 5670 var current_alphabet = new Alphabet(data.alphabet, data.background.freqs); | |
| 5671 var current_motif = 0; | |
| 5672 //var new_icon_src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAQCAMAAAAyEe/dAAAACVBMVEX//wAAAAD////s2cV/AAAAdUlEQVQYlXVRBxLAIAhL+P+jC2HZhXcBZEWEldDsZcLIcAhHWWnK8SDcWQhMFUHdAQ1CqQ5+CWPmlHojl+nCJNRtzu4qRc3IUzmTVpXYK0nox0z0PI1stgchdK7lEv7ekhvalw8WW547Gyzedt/2/gLx8WXjXF/1AYFriNAWAAAAAElFTkSuQmCC"; | |
| 5673 | |
| 5674 var DelayLogoTask = function(logo, canvas) { | |
| 5675 this.logo = logo; | |
| 5676 this.canvas = canvas; | |
| 5677 }; | |
| 5678 | |
| 5679 DelayLogoTask.prototype.run = function () { | |
| 5680 draw_logo_on_canvas(this.logo, this.canvas, false); | |
| 5681 }; | |
| 5682 | |
| 5683 function clone_template(template) { | |
| 5684 "use strict"; | |
| 5685 var node, help_btns, i, button; | |
| 5686 node = $(template).cloneNode(true); | |
| 5687 toggle_class(node, "template", false); | |
| 5688 node.id = ""; | |
| 5689 help_btns = node.querySelectorAll(".help"); | |
| 5690 for (i = 0; i < help_btns.length; i++) { | |
| 5691 button = help_btns[i]; | |
| 5692 if (button.hasAttribute("data-topic")) { | |
| 5693 button.tabIndex = "0"; | |
| 5694 button.addEventListener("click", __toggle_help, false); | |
| 5695 button.addEventListener("keydown", __toggle_help, false); | |
| 5696 } | |
| 5697 } | |
| 5698 return node; | |
| 5699 } | |
| 5700 | |
| 5701 function make_small_logo(alphabet, pspm, options) { | |
| 5702 if (typeof options === "undefined") options = {}; | |
| 5703 if (options.rc) pspm = pspm.copy().reverse_complement(alphabet); | |
| 5704 var logo = new Logo(alphabet, {x_axis: false, y_axis: false}); | |
| 5705 logo.add_pspm(pspm, (typeof options.offset === "number" ? options.offset : 0)); | |
| 5706 var canvas = document.createElement('canvas'); | |
| 5707 if (typeof options.className === "string") canvas.className = options.className; | |
| 5708 if (typeof options.width === "number" && options.width > 0) { | |
| 5709 canvas.height = 0; | |
| 5710 canvas.width = options.width; | |
| 5711 draw_logo_on_canvas(logo, canvas, false); | |
| 5712 } else { | |
| 5713 draw_logo_on_canvas(logo, canvas, false, 1/3); | |
| 5714 } | |
| 5715 return canvas; | |
| 5716 } | |
| 5717 | |
| 5718 function make_large_logo(alphabet, pspm, rc, offset, className) { | |
| 5719 if (rc) pspm = pspm.copy().reverse_complement(alphabet); | |
| 5720 var logo = new Logo(alphabet, ""); | |
| 5721 logo.add_pspm(pspm, offset); | |
| 5722 var canvas = document.createElement('canvas'); | |
| 5723 canvas.height = 200; | |
| 5724 canvas.width = 0; | |
| 5725 canvas.className = className; | |
| 5726 size_logo_on_canvas(logo, canvas, false); | |
| 5727 add_draw_task(canvas, new DelayLogoTask(logo, canvas)); | |
| 5728 return canvas; | |
| 5729 } | |
| 5730 | |
| 5731 function make_sym_btn(symbol, title, action) { | |
| 5732 var box; | |
| 5733 box = document.createElement("div"); | |
| 5734 box.tabIndex = 0; | |
| 5735 box.className = "sym_btn"; | |
| 5736 box.appendChild(document.createTextNode(symbol)); | |
| 5737 box.title = title; | |
| 5738 box.addEventListener('click', action, false); | |
| 5739 box.addEventListener('keydown', action, false); | |
| 5740 return box; | |
| 5741 } | |
| 5742 | |
| 5743 function make_seq(alphabet, seq) { | |
| 5744 var i, j, letter, lbox, sbox; | |
| 5745 sbox = document.createElement("span"); | |
| 5746 for (i = 0; i < seq.length; i = j) { | |
| 5747 letter = seq.charAt(i); | |
| 5748 for (j = i+1; j < seq.length; j++) { | |
| 5749 if (seq.charAt(j) !== letter) { | |
| 5750 break; | |
| 5751 } | |
| 5752 } | |
| 5753 lbox = document.createElement("span"); | |
| 5754 lbox.style.color = alphabet.get_colour(alphabet.get_index(letter)); | |
| 5755 lbox.appendChild(document.createTextNode(seq.substring(i, j))); | |
| 5756 sbox.appendChild(lbox); | |
| 5757 } | |
| 5758 return sbox; | |
| 5759 } | |
| 5760 | |
| 5761 // | |
| 5762 // make_pv_text | |
| 5763 // | |
| 5764 // Returns the string p-value, with the p italicised. | |
| 5765 /// | |
| 5766 function make_pv_text() { | |
| 5767 var pv_text = document.createElement("span"); | |
| 5768 var pv_italic_text = document.createElement("span"); | |
| 5769 pv_italic_text.appendChild(document.createTextNode("p")); | |
| 5770 pv_italic_text.style.fontStyle = "italic"; | |
| 5771 pv_text.appendChild(pv_italic_text); | |
| 5772 pv_text.appendChild(document.createTextNode("-value")); | |
| 5773 return pv_text; | |
| 5774 } | |
| 5775 | |
| 5776 function append_site_entries(tbody, motif, site_index, count) { | |
| 5777 "use strict"; | |
| 5778 var i, end; | |
| 5779 var sites, site, sequences, sequence; | |
| 5780 var rbody; | |
| 5781 if (typeof count !== "number") { | |
| 5782 count = 20; | |
| 5783 } | |
| 5784 sequences = data["sequence_db"]["sequences"]; | |
| 5785 sites = motif["sites"]; | |
| 5786 end = Math.min(site_index + count, sites.length); | |
| 5787 for (i = site_index; i < end; i++) { | |
| 5788 site = sites[i]; | |
| 5789 sequence = sequences[site["seq"]]; | |
| 5790 | |
| 5791 rbody = tbody.insertRow(tbody.rows.length); | |
| 5792 add_text_cell(rbody, "" + (site["seq"] + 1) + ".", "site_num"); | |
| 5793 add_text_cell(rbody, sequence["name"], "site_name"); | |
| 5794 add_text_cell(rbody, site["rc"] ? "-" : "+", "site_strand"); | |
| 5795 add_text_cell(rbody, site["pos"] + 1, "site_start"); | |
| 5796 add_text_cell(rbody, site["pvalue"].toExponential(2), "site_pvalue"); | |
| 5797 add_text_cell(rbody, site["lflank"], "site lflank"); | |
| 5798 add_cell(rbody, make_seq(current_alphabet, site["match"]), "site match"); | |
| 5799 add_text_cell(rbody, site["rflank"], "site rflank"); | |
| 5800 } | |
| 5801 return i; | |
| 5802 } | |
| 5803 | |
| 5804 function make_site_entries() { | |
| 5805 "use strict"; | |
| 5806 var region; | |
| 5807 region = this; | |
| 5808 if (region.data_site_index >= region.data_motif["sites"].length) { | |
| 5809 // all sites created | |
| 5810 region.removeEventListener('scroll', make_site_entries, false); | |
| 5811 return; | |
| 5812 } | |
| 5813 // if there's still 100 pixels to scroll than don't do anything yet | |
| 5814 if (region.scrollHeight - (region.scrollTop + region.offsetHeight) > 100) { | |
| 5815 return; | |
| 5816 } | |
| 5817 | |
| 5818 region.data_site_index = append_site_entries( | |
| 5819 find_child(region, "sites_tbl").tBodies[0], | |
| 5820 region.data_motif, region.data_site_index, 20 | |
| 5821 ); | |
| 5822 } | |
| 5823 | |
| 5824 function make_sites(motif) { | |
| 5825 "use strict"; | |
| 5826 function add_site_header(row, title, nopad, help_topic, tag_class) { | |
| 5827 var div, divcp, th; | |
| 5828 th = document.createElement("th"); | |
| 5829 div = document.createElement("div"); | |
| 5830 div.className = "sites_th_inner"; | |
| 5831 if (typeof title !== "object") { | |
| 5832 title = document.createTextNode("" + title); | |
| 5833 } | |
| 5834 div.appendChild(title); | |
| 5835 if (help_topic) { | |
| 5836 div.appendChild(document.createTextNode("\xA0")); | |
| 5837 div.appendChild(help_button(help_topic)); | |
| 5838 } | |
| 5839 divcp = div.cloneNode(true); | |
| 5840 divcp.className = "sites_th_hidden"; | |
| 5841 th.appendChild(div); | |
| 5842 th.appendChild(divcp); | |
| 5843 if (nopad) { | |
| 5844 th.className = "nopad"; | |
| 5845 } | |
| 5846 if (tag_class) { | |
| 5847 th.className += " " + tag_class; | |
| 5848 } | |
| 5849 row.appendChild(th); | |
| 5850 } | |
| 5851 var outer_tbl, inner_tbl, tbl, thead, tbody, rhead; | |
| 5852 | |
| 5853 outer_tbl = document.createElement("div"); | |
| 5854 outer_tbl.className = "sites_outer"; | |
| 5855 | |
| 5856 inner_tbl = document.createElement("div"); | |
| 5857 inner_tbl.className = "sites_inner"; | |
| 5858 outer_tbl.appendChild(inner_tbl); | |
| 5859 | |
| 5860 tbl = document.createElement("table"); | |
| 5861 tbl.className = "sites_tbl"; | |
| 5862 inner_tbl.appendChild(tbl); | |
| 5863 | |
| 5864 thead = document.createElement("thead"); | |
| 5865 tbl.appendChild(thead); | |
| 5866 tbody = document.createElement("tbody"); | |
| 5867 tbl.appendChild(tbody); | |
| 5868 | |
| 5869 rhead = thead.insertRow(thead.rows.length); | |
| 5870 add_site_header(rhead, "", true); | |
| 5871 add_site_header(rhead, "Name", false, "pop_seq_name"); | |
| 5872 add_site_header(rhead, "Strand", false, "pop_site_strand", "site_strand_title"); | |
| 5873 add_site_header(rhead, "Start", false, "pop_site_start"); | |
| 5874 add_site_header(rhead, make_pv_text(), false, "pop_site_pvalue"); | |
| 5875 add_site_header(rhead, "", false); | |
| 5876 add_site_header(rhead, "Sites", true, "pop_site_match"); | |
| 5877 add_site_header(rhead, "", false); | |
| 5878 | |
| 5879 inner_tbl.data_motif = motif; | |
| 5880 inner_tbl.data_site_index = append_site_entries(tbody, motif, 0, 20); | |
| 5881 if (inner_tbl.data_site_index < motif["sites"].length) { | |
| 5882 inner_tbl.addEventListener('scroll', make_site_entries, false); | |
| 5883 } | |
| 5884 return outer_tbl; | |
| 5885 } | |
| 5886 | |
| 5887 function make_motif_table_entry(row, alphabet, ordinal, motif, colw) { | |
| 5888 "use strict"; | |
| 5889 function ev_sig(evalue_str) { | |
| 5890 "use strict"; | |
| 5891 var ev_re, match, sig, exp, num; | |
| 5892 ev_re = /^(.*)e(.*)$/; | |
| 5893 if (match = ev_re.exec(evalue_str)) { | |
| 5894 sig = parseFloat(match[1]); | |
| 5895 exp = parseInt(match[2]); | |
| 5896 if (exp >= 0) { | |
| 5897 return false; | |
| 5898 } else if (exp <= -3) { | |
| 5899 return true; | |
| 5900 } else { | |
| 5901 return sig * Math.pow(10, exp) <= 0.05; | |
| 5902 } | |
| 5903 } | |
| 5904 return true; | |
| 5905 } | |
| 5906 function make_preview(alphabet, motif) { | |
| 5907 "use strict"; | |
| 5908 var pspm, preview, preview_rc; | |
| 5909 var box, btn_box, logo_box, btn_plus, btn_minus; | |
| 5910 if (motif["preview_logo"]) { | |
| 5911 preview = motif["preview_logo"]; | |
| 5912 preview_rc = motif["preview_logo_rc"]; | |
| 5913 } else { | |
| 5914 pspm = new Pspm(motif["pwm"]); | |
| 5915 preview = make_logo(alphabet, pspm, 50, false, 0); | |
| 5916 motif["preview_logo"] = preview; | |
| 5917 if (alphabet.has_complement()) { | |
| 5918 preview_rc = make_logo(alphabet, pspm, 50, true, 0, "logo_rc"); | |
| 5919 motif["preview_logo_rc"] = preview_rc; | |
| 5920 } | |
| 5921 } | |
| 5922 if (preview_rc) { | |
| 5923 btn_plus = document.createElement("div"); | |
| 5924 btn_plus.appendChild(document.createTextNode("+")); | |
| 5925 btn_plus.className = "preview_btn plus"; | |
| 5926 btn_plus.tabIndex = "0"; | |
| 5927 btn_plus.addEventListener("click", action_btn_rc, false); | |
| 5928 btn_plus.addEventListener("keydown", action_btn_rc, false); | |
| 5929 btn_minus = document.createElement("div"); | |
| 5930 btn_minus.appendChild(document.createTextNode("-")); | |
| 5931 btn_minus.className = "preview_btn minus"; | |
| 5932 btn_minus.tabIndex = "0"; | |
| 5933 btn_minus.addEventListener("click", action_btn_rc, false); | |
| 5934 btn_minus.addEventListener("keydown", action_btn_rc, false); | |
| 5935 btn_box = document.createElement("div"); | |
| 5936 btn_box.className = "preview_btn_box"; | |
| 5937 btn_box.appendChild(btn_plus); | |
| 5938 btn_box.appendChild(btn_minus); | |
| 5939 } | |
| 5940 logo_box = document.createElement("div"); | |
| 5941 logo_box.className = "preview_logo_box"; | |
| 5942 logo_box.appendChild(preview); | |
| 5943 if (preview_rc) logo_box.appendChild(preview_rc); | |
| 5944 box = document.createElement("div"); | |
| 5945 box.className = "preview_box"; | |
| 5946 if (preview_rc) box.appendChild(btn_box); | |
| 5947 box.appendChild(logo_box); | |
| 5948 if (preview_rc) { | |
| 5949 if (motif["rc"]) { | |
| 5950 btn_minus.className += " active"; | |
| 5951 logo_box.className += " show_rc_logo"; | |
| 5952 } else { | |
| 5953 btn_plus.className += " active"; | |
| 5954 } | |
| 5955 } | |
| 5956 return box; | |
| 5957 } | |
| 5958 var pspm, preview, preview_rc, c; | |
| 5959 row.data_motif = motif; | |
| 5960 row.id = motif["alt"]; | |
| 5961 row.data_ordinal = ordinal; | |
| 5962 if (!ev_sig(motif["evalue"])) { | |
| 5963 row.style.opacity = 0.4; | |
| 5964 } | |
| 5965 add_text_cell(row, "" + ordinal + ".", "motif_ordinal"); | |
| 5966 add_cell(row, make_preview(alphabet, motif), "motif_logo"); | |
| 5967 add_text_cell(row, motif["evalue"], "motif_evalue"); | |
| 5968 add_text_cell(row, motif["nsites"], "motif_nsites"); | |
| 5969 add_text_cell(row, motif["len"], "motif_width"); | |
| 5970 add_cell(row, make_sym_btn("\u21A7", "Show more information.", | |
| 5971 action_show_more), "motif_more"); | |
| 5972 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"); | |
| 5973 if (colw) { | |
| 5974 for (c = 0; c < row.cells.length; c++) { | |
| 5975 row.cells[c].style.minWidth = colw[c] + "px"; | |
| 5976 } | |
| 5977 } | |
| 5978 } | |
| 5979 | |
| 5980 function make_motifs_table(alphabet, start_ordinal, motifs, colw, stop_reason) { | |
| 5981 var i, j; | |
| 5982 var tbl, thead, tbody, tfoot, row, preview; | |
| 5983 var motif, pspm; | |
| 5984 | |
| 5985 tbl = document.createElement("table"); | |
| 5986 | |
| 5987 thead = document.createElement("thead"); | |
| 5988 tbl.appendChild(thead); | |
| 5989 tbody = document.createElement("tbody"); | |
| 5990 tbl.appendChild(tbody); | |
| 5991 tfoot = document.createElement("tfoot"); | |
| 5992 tbl.appendChild(tfoot); | |
| 5993 | |
| 5994 row = thead.insertRow(thead.rows.length); | |
| 5995 add_text_header_cell(row, "", "", "motif_ordinal"); | |
| 5996 add_text_header_cell(row, "Logo", "pop_logo", "motif_logo"); | |
| 5997 add_text_header_cell(row, "E-value", "pop_ev", "motif_evalue"); | |
| 5998 add_text_header_cell(row, "Sites", "pop_sites", "motif_nsites"); | |
| 5999 add_text_header_cell(row, "Width", "pop_width", "motif_width"); | |
| 6000 add_text_header_cell(row, "More", "pop_more", "motif_more"); | |
| 6001 add_text_header_cell(row, "Submit/Download", "pop_submit_dl", "motif_submit"); | |
| 6002 | |
| 6003 for (i = 0; i < motifs.length; i++) { | |
| 6004 row = tbody.insertRow(tbody.rows.length); | |
| 6005 make_motif_table_entry(row, alphabet, start_ordinal + i, motifs[i], colw); | |
| 6006 } | |
| 6007 | |
| 6008 row = tfoot.insertRow(tfoot.rows.length); | |
| 6009 add_text_header_cell(row, stop_reason, "", "stop_reason", "", 6); | |
| 6010 | |
| 6011 return tbl; | |
| 6012 } | |
| 6013 | |
| 6014 function make_expanded_motif(alphabet, ordinal, motif, less_x, submit_x) { | |
| 6015 "use strict"; | |
| 6016 var box, pspm, logo_box, large_logo, large_logo_rc, tab_logo, tab_logo_rc; | |
| 6017 var btn, offset, norc; | |
| 6018 | |
| 6019 box = clone_template("tmpl_motif_expanded"); | |
| 6020 box.data_motif = motif; | |
| 6021 box.data_ordinal = ordinal; | |
| 6022 | |
| 6023 pspm = new Pspm(motif["pwm"]); | |
| 6024 if (typeof motif["rc"] !== "boolean") { | |
| 6025 motif["rc"] = false; | |
| 6026 } | |
| 6027 if (motif["large_logo"]) { | |
| 6028 large_logo = motif["large_logo"]; | |
| 6029 large_logo_rc = motif["large_logo_rc"]; | |
| 6030 } else { | |
| 6031 large_logo = make_large_logo(alphabet, pspm, false, 0); | |
| 6032 motif["large_logo"] = large_logo; | |
| 6033 if (alphabet.has_complement()) { | |
| 6034 large_logo_rc = make_large_logo(alphabet, pspm, true, 0, "logo_rc"); | |
| 6035 motif["large_logo_rc"] = large_logo_rc; | |
| 6036 } | |
| 6037 } | |
| 6038 norc = (large_logo_rc == null); | |
| 6039 toggle_class(box, "norc", norc); | |
| 6040 | |
| 6041 logo_box = find_child(box, "tvar_logo"); | |
| 6042 logo_box.appendChild(large_logo); | |
| 6043 if (large_logo_rc) logo_box.appendChild(large_logo_rc); | |
| 6044 toggle_class(logo_box, "show_rc_logo", motif["rc"]); | |
| 6045 | |
| 6046 tab_logo = find_child(box, "tvar_tab"); | |
| 6047 tab_logo_rc = find_child(box, "tvar_tab_rc"); | |
| 6048 | |
| 6049 toggle_class(tab_logo, "activeTab", !motif["rc"]); | |
| 6050 toggle_class(tab_logo_rc, "activeTab", motif["rc"]); | |
| 6051 | |
| 6052 tab_logo.addEventListener('click', action_rc_tab, false); | |
| 6053 tab_logo.addEventListener('keydown', action_rc_tab, false); | |
| 6054 tab_logo_rc.addEventListener('click', action_rc_tab, false); | |
| 6055 tab_logo_rc.addEventListener('keydown', action_rc_tab, false); | |
| 6056 | |
| 6057 set_tvar(box, "tvar_ordinal", ordinal); | |
| 6058 set_tvar(box, "tvar_evalue", motif["evalue"]); | |
| 6059 set_tvar(box, "tvar_width", motif["len"]); | |
| 6060 set_tvar(box, "tvar_site_count", motif["nsites"]); | |
| 6061 set_tvar(box, "tvar_llr", motif["llr"]); | |
| 6062 set_tvar(box, "tvar_ic", motif["ic"]); | |
| 6063 set_tvar(box, "tvar_re", motif["re"]); | |
| 6064 set_tvar(box, "tvar_bt", motif["bt"]); | |
| 6065 if (data.sequence_db.primary_count > data.options.brief) { | |
| 6066 if (data.options.brief == 1000) { | |
| 6067 set_tvar(box, "tvar_sites", "Output of sites suppressed because there were more than 1000 (primary) sequences."); | |
| 6068 } else { | |
| 6069 set_tvar(box, "tvar_sites", "Output of sites suppressed by -brief option."); | |
| 6070 } | |
| 6071 } else { | |
| 6072 set_tvar(box, "tvar_sites", make_sites(motif)); | |
| 6073 } | |
| 6074 | |
| 6075 offset = 32; // 1* 5px padding + 2 * 10px padding + 2 * 2px border + 3px ?? | |
| 6076 | |
| 6077 btn = find_child(box, "tvar_less"); | |
| 6078 btn.style.left = (less_x - offset) + "px"; | |
| 6079 btn.addEventListener('click', action_show_less, false); | |
| 6080 btn.addEventListener('keydown', action_show_less, false); | |
| 6081 btn = find_child(box, "tvar_submit"); | |
| 6082 btn.style.left = (submit_x - offset) + "px"; | |
| 6083 btn.addEventListener('click', action_show_outpop, false); | |
| 6084 btn.addEventListener('keydown', action_show_outpop, false); | |
| 6085 return box; | |
| 6086 } | |
| 6087 | |
| 6088 // | |
| 6089 // | |
| 6090 // | |
| 6091 function make_motifs() { | |
| 6092 "use strict"; | |
| 6093 function pixel_value(str_in) { | |
| 6094 "use strict"; | |
| 6095 var px_re, match; | |
| 6096 px_re = /^(\d+)px$/; | |
| 6097 if (match = px_re.exec(str_in)) { | |
| 6098 return parseInt(match[1], 10); | |
| 6099 } | |
| 6100 return 0; | |
| 6101 } | |
| 6102 var container, tbl; | |
| 6103 var colw, r, row, c, cell, cell_style, pad_left, pad_right; | |
| 6104 | |
| 6105 // make the motifs table | |
| 6106 container = $("motifs"); | |
| 6107 container.innerHTML = ""; // clear content | |
| 6108 | |
| 6109 tbl = make_motifs_table(current_alphabet, 1, data["motifs"], colw, data["stop_reason"]); | |
| 6110 container.appendChild(tbl); | |
| 6111 | |
| 6112 // measure table column widths | |
| 6113 colw = []; | |
| 6114 row = tbl.tBodies[0].rows[0]; | |
| 6115 for (c = 0; c < row.cells.length; c++) { | |
| 6116 var padLeft, padRight; | |
| 6117 cell = row.cells[c]; | |
| 6118 cell_style = window.getComputedStyle(cell, null); | |
| 6119 pad_left = pixel_value(cell_style.getPropertyValue("padding-left")); | |
| 6120 pad_right = pixel_value(cell_style.getPropertyValue("padding-right")); | |
| 6121 colw[c] = cell.clientWidth - pad_left - pad_right; | |
| 6122 if (typeof colw[c] !== "number" || colw[c] < 0) { | |
| 6123 colw[c] = 1; | |
| 6124 } | |
| 6125 } | |
| 6126 | |
| 6127 // set minimum table column widths on each row so later when we remove rows it still aligns | |
| 6128 for (r = 0; r < tbl.tBodies[0].rows.length; r++) { | |
| 6129 row = tbl.tBodies[0].rows[r]; | |
| 6130 for (c = 0; c < row.cells.length; c++) { | |
| 6131 row.cells[c].style.minWidth = colw[c] + "px"; | |
| 6132 } | |
| 6133 } | |
| 6134 | |
| 6135 // store the table column widths so we can create rows latter with the same minimums | |
| 6136 container.data_colw = colw; | |
| 6137 | |
| 6138 // calculate the x offset for the buttons | |
| 6139 row = tbl.tBodies[0].rows[0]; | |
| 6140 container.data_more_x = coords(find_child(find_child(row, "motif_more"), "sym_btn"))[0]; | |
| 6141 container.data_submit_x = coords(find_child(find_child(row, "motif_submit"), "sym_btn"))[0]; | |
| 6142 | |
| 6143 draw_on_screen(); | |
| 6144 } | |
| 6145 | |
| 6146 function make_meme_block(container, max_seq_len, is_scan, site) { | |
| 6147 "use strict"; | |
| 6148 var motif = data.motifs[site.motif]; | |
| 6149 var block = make_block(container, max_seq_len, site.pos, motif.len, | |
| 6150 site.pvalue, site.rc, site.motif, is_scan); | |
| 6151 var handler = (is_scan ? | |
| 6152 make_scan_popup(site, motif, block) : | |
| 6153 make_block_popup(site, motif, block)); | |
| 6154 block.addEventListener("mouseover", handler, false); | |
| 6155 block.addEventListener("mouseout", handler, false); | |
| 6156 } | |
| 6157 | |
| 6158 function append_blocks_entries(tbody, seq_index, count) { | |
| 6159 "use strict"; | |
| 6160 var i, end, j; | |
| 6161 var max_pvalue, max_block_height, max_seq_len, sequences; | |
| 6162 var sequence, sites, scans, scan; | |
| 6163 var container, plus, minus, rule, row; | |
| 6164 // define some constants | |
| 6165 max_seq_len = data.sequence_db.max_length; | |
| 6166 // determine how many to load | |
| 6167 end = Math.min(seq_index + count, data.sequence_db.sequences.length); | |
| 6168 for (i = seq_index; i < end; i++) { | |
| 6169 // get the sequence | |
| 6170 sequence = data.sequence_db.sequences[i]; | |
| 6171 // make the containers for the block diagram | |
| 6172 container = make_block_container(current_alphabet.has_complement(), | |
| 6173 data.options.revcomp, max_seq_len, sequence.length); | |
| 6174 // create blocks for the motif sites | |
| 6175 sites = sequence["sites"]; | |
| 6176 for (j = 0; j < sites.length; j++) | |
| 6177 make_meme_block(container, max_seq_len, false, sites[j]); | |
| 6178 // create blocks for the scanned sites | |
| 6179 scan = data.scan[i]; | |
| 6180 for (j = 0; j < scan.sites.length; j++) | |
| 6181 make_meme_block(container, max_seq_len, true, scan.sites[j]); | |
| 6182 // create a row for the sequence | |
| 6183 row = tbody.insertRow(tbody.rows.length); | |
| 6184 toggle_class(row, "empty_seq", sites.length == 0 && scan.sites.length == 0); | |
| 6185 toggle_class(row, "only_scan", sites.length == 0 && scan.sites.length > 0); | |
| 6186 add_text_cell(row, (i + 1) + ".", "blockdiag_num"); | |
| 6187 add_text_cell(row, sequence["name"], "blockdiag_name"); | |
| 6188 add_text_cell(row, scan["pvalue"].toExponential(2), "blockdiag_pvalue"); | |
| 6189 add_cell(row, container, "block_td"); | |
| 6190 } | |
| 6191 return end; | |
| 6192 } | |
| 6193 | |
| 6194 function make_blocks_entries() { | |
| 6195 "use strict"; | |
| 6196 var region; | |
| 6197 region = this; | |
| 6198 if (region.data_blocks_index >= data["sequence_db"]["sequences"].length) { | |
| 6199 // all sites created | |
| 6200 region.removeEventListener('scroll', make_blocks_entries, false); | |
| 6201 return; | |
| 6202 } | |
| 6203 // if there's still 100 pixels to scroll then don't do anything yet | |
| 6204 if (region.scrollHeight - (region.scrollTop + region.offsetHeight) > 100) { | |
| 6205 return; | |
| 6206 } | |
| 6207 | |
| 6208 region.data_blocks_index = append_blocks_entries( | |
| 6209 find_child(region, "blocks_tbl").tBodies[0], | |
| 6210 region.data_blocks_index, 20 | |
| 6211 ); | |
| 6212 } | |
| 6213 | |
| 6214 // Apply opacity alpha to color rgb with backrgound bkg. | |
| 6215 function RGBAtoRGB(rgb, bkg, opacity) { | |
| 6216 var i; | |
| 6217 var rgb_new = []; | |
| 6218 for (i=0; i<3; i++) { | |
| 6219 rgb_new[i] = Math.round(((1-opacity) * bkg[i]) + (opacity * rgb[i])); | |
| 6220 } | |
| 6221 return rgb_new; | |
| 6222 } | |
| 6223 | |
| 6224 // Function to measure the size of text on a canvas. | |
| 6225 var MeasureText = function(font, text) { | |
| 6226 var image = document.createElement("canvas"); | |
| 6227 var image_ctx = image.getContext('2d'); | |
| 6228 image_ctx.save(); | |
| 6229 image_ctx.font = font; | |
| 6230 var text_length = image_ctx.measureText(text).width; | |
| 6231 image.remove(); | |
| 6232 return text_length; | |
| 6233 } // MeasureText | |
| 6234 | |
| 6235 // Functions to download the motif block diagram as a PDF or SVG file. | |
| 6236 function download_PDF_block_diagram() { | |
| 6237 downloadBlockDiagram(true, 'bitbucket'); | |
| 6238 } | |
| 6239 function download_SVG_block_diagram() { | |
| 6240 downloadBlockDiagram(false, 'bitbucket'); | |
| 6241 } | |
| 6242 | |
| 6243 // Helper function to create a script element for downloading javascript. | |
| 6244 function createScriptElement(url, file, integrity) | |
| 6245 { | |
| 6246 console.log('Downloading ' + file + ' from ' + url + '.'); | |
| 6247 const script = document.createElement('script'); | |
| 6248 script.setAttribute('src', url + '/' + file); | |
| 6249 if (integrity) script.setAttribute('integrity', integrity); | |
| 6250 script.setAttribute('crossorigin', 'anonymous'); | |
| 6251 script.setAttribute('async', ''); | |
| 6252 document.head.appendChild(script); | |
| 6253 } | |
| 6254 | |
| 6255 // Helper function to download the motif block diagram as a PDF or SVG file. | |
| 6256 var downloadBlockDiagram = function( | |
| 6257 make_pdf, | |
| 6258 next_source | |
| 6259 ) { | |
| 6260 var script_file, integrity_key; | |
| 6261 // Check if necessary javascript has been loaded. | |
| 6262 if ( (make_pdf && typeof jsPDF === 'undefined') || | |
| 6263 (!make_pdf && typeof d3 === 'undefined') ) { | |
| 6264 if (next_source === 'bitbucket') { | |
| 6265 if (make_pdf) { | |
| 6266 script_file = 'jspdf.min.js'; | |
| 6267 } else { | |
| 6268 script_file = 'd3.v5.min.js'; | |
| 6269 } | |
| 6270 createScriptElement('https://memesuite.bitbucket.io/javascript', script_file); | |
| 6271 // Wait 3 seconds and then try to create the diagram. | |
| 6272 setTimeout(downloadBlockDiagram, 3000, make_pdf, 'cloudflare'); | |
| 6273 } else if (next_source === 'cloudflare') { | |
| 6274 if (make_pdf) { | |
| 6275 script_file = 'jspdf/1.5.3/jspdf.min.js'; | |
| 6276 integrity_key = 'sha512-ToRWKKOvhBSS8EtqSflysM/S7v9bB9V0X3B1+E7xo7XZBEZCPL3VX5SFIp8zxY19r7Sz0svqQVbAOx+QcLQSAQ==' | |
| 6277 } else { | |
| 6278 script_file = 'd3/5.16.0/d3.min.js'; | |
| 6279 integrity_key = 'sha512-FHsFVKQ/T1KWJDGSbrUhTJyS1ph3eRrxI228ND0EGaEp6v4a/vGwPWd3Dtd/+9cI7ccofZvl/wulICEurHN1pg=='; | |
| 6280 } | |
| 6281 createScriptElement('https://cdnjs.cloudflare.com/ajax/libs', script_file, integrity_key); | |
| 6282 // Wait 3 seconds and then try to create the diagram. | |
| 6283 setTimeout(downloadBlockDiagram, 3000, make_pdf, 'none'); | |
| 6284 } else { | |
| 6285 // No more sources for javascript. Call so error message will be shown. | |
| 6286 downloadBlockDiagramMain(make_pdf); | |
| 6287 } | |
| 6288 } else { | |
| 6289 // Create the diagram. | |
| 6290 downloadBlockDiagramMain(make_pdf); | |
| 6291 } | |
| 6292 } | |
| 6293 | |
| 6294 // Main function to download the motif block diagram as a PDF or SVG file. | |
| 6295 var downloadBlockDiagramMain= function( | |
| 6296 make_pdf | |
| 6297 ) { | |
| 6298 // Check that necessary javascript was downloaded. | |
| 6299 if ( (make_pdf && typeof jsPDF === 'undefined') || | |
| 6300 (!make_pdf && typeof d3 === 'undefined') ) { | |
| 6301 var id = make_pdf ? $("pdfButton") : $("svgButton"); | |
| 6302 help_popup(id, "pop_offline"); | |
| 6303 return; | |
| 6304 } | |
| 6305 | |
| 6306 // Determine which lines are visible in the HTML inner scroll window. | |
| 6307 var inner_tbl = $("blocks_scroll"); | |
| 6308 var pix_per_sequence = 27; // (vertical) pixels per sequence diagram line | |
| 6309 var first = Math.round(inner_tbl.scrollTop / pix_per_sequence) + 1; | |
| 6310 var last = first + Math.round(inner_tbl.offsetHeight / pix_per_sequence) - 1; | |
| 6311 | |
| 6312 // Get the contents of the HTML inner scroll window while saving the sequences to be printed. | |
| 6313 var numbers = document.getElementsByClassName("blockdiag_num"); | |
| 6314 var bars = {}; | |
| 6315 var visible_motifs = {}; | |
| 6316 var seq_index = 0; | |
| 6317 var rgb; | |
| 6318 for (var i=0; i<numbers.length && seq_index < last; i++) { | |
| 6319 var row_node = numbers[i].parentNode; | |
| 6320 // Check if the sequence is displayed in the outer scrolling window. | |
| 6321 var seq_name = numbers[i].nextSibling.innerHTML; | |
| 6322 if ( | |
| 6323 ($("rdo_sites_only").checked && row_node.getAttribute("class").includes("only_scan")) | |
| 6324 || | |
| 6325 (! $("rdo_all_seqs").checked && row_node.getAttribute("class").includes("empty_seq")) | |
| 6326 ) { continue; } | |
| 6327 seq_index++; | |
| 6328 | |
| 6329 if (seq_index < first) { continue; } // sequence not in HTML inner scrolling window | |
| 6330 | |
| 6331 var pvalue = numbers[i].nextSibling.nextSibling.innerHTML; | |
| 6332 var far = numbers[i].nextSibling.nextSibling.nextSibling.children[0].children; | |
| 6333 var seq_length = data.sequence_db.sequences[i].length; | |
| 6334 | |
| 6335 var seqObj = []; | |
| 6336 seqObj["length"] = seq_length; | |
| 6337 seqObj["pvalue"] = pvalue; | |
| 6338 seqObj["pn"] = []; | |
| 6339 seqObj["width"] = []; | |
| 6340 seqObj["left"] = []; | |
| 6341 seqObj["height"] = []; | |
| 6342 seqObj["color"] = []; | |
| 6343 seqObj["opacity"] = []; | |
| 6344 for (var x = 0; x < far.length; x++) { | |
| 6345 if ((far[x].getAttribute("style") != null) | |
| 6346 && ( | |
| 6347 ( $("rdo_sites_only").checked && ! far[x].getAttribute("class").includes("scanned")) | |
| 6348 || ( $("rdo_sites_and_scan").checked || $("rdo_all_seqs").checked ) | |
| 6349 ) | |
| 6350 ) { | |
| 6351 if (far[x].getAttribute("style").includes("rgb")) { | |
| 6352 var compStyles = far[x].style; | |
| 6353 // Make scanned sites get displayed first so they will not "cover" regular sites. | |
| 6354 var site_pn = far[x].getAttribute("class").includes("top") ? "+" : "-"; | |
| 6355 var site_width = parseFloat(compStyles.width.slice(0, -1)); | |
| 6356 var site_left = parseFloat(compStyles.left.slice(0, -1)); | |
| 6357 var site_height = parseFloat(compStyles.height.slice(0, -2)); | |
| 6358 var site_color = compStyles.backgroundColor.slice(4, -1).replace(/ /g, ""); | |
| 6359 if (far[x].getAttribute("class").includes("scanned")) { | |
| 6360 seqObj["pn"].unshift(site_pn); | |
| 6361 seqObj["width"].unshift(site_width); | |
| 6362 seqObj["left"].unshift(site_left); | |
| 6363 seqObj["height"].unshift(site_height); | |
| 6364 seqObj["color"].unshift(site_color); | |
| 6365 seqObj["opacity"].unshift(0.3); | |
| 6366 } else { | |
| 6367 seqObj["pn"].push(site_pn); | |
| 6368 seqObj["width"].push(site_width); | |
| 6369 seqObj["left"].push(site_left); | |
| 6370 seqObj["height"].push(site_height); | |
| 6371 seqObj["color"].push(site_color); | |
| 6372 seqObj["opacity"].push(1); | |
| 6373 } | |
| 6374 visible_motifs[far[x].getAttribute("data-colour-index")] = site_color; | |
| 6375 } | |
| 6376 } | |
| 6377 } | |
| 6378 // Save the sequence data if it has motifs (or rdo_all_seqs is checked) | |
| 6379 if ($("rdo_all_seqs").checked || seqObj["width"].length > 0) { | |
| 6380 bars[seq_name] = seqObj; | |
| 6381 } | |
| 6382 } | |
| 6383 | |
| 6384 // jsPDF coordinates are always in points. | |
| 6385 var font_size = 13; | |
| 6386 var nbars = Object.keys(bars).length; | |
| 6387 var legend_font_size = 0.8 * font_size; | |
| 6388 | |
| 6389 // Initialize field widths in points by measuring header text. | |
| 6390 var font = "bold " + font_size + "pt Helvetica, sans-serif"; | |
| 6391 var max_name_width = MeasureText(font, "Name"); | |
| 6392 var max_pvalue_width = MeasureText(font, "p-value"); | |
| 6393 | |
| 6394 var max_seq_length = 0; // in characters | |
| 6395 var has_complement = current_alphabet.has_complement(); | |
| 6396 var revcomp = data.options["revcomp"]; | |
| 6397 | |
| 6398 // Measure text of numbers, names and p-values, convert to points and save the max. | |
| 6399 font = font_size + "pt Helvetica, sans-serif"; | |
| 6400 var seq_name; | |
| 6401 for (seq_name in bars) { | |
| 6402 var seq_name_width = MeasureText(font, seq_name); | |
| 6403 var pvalue_width = MeasureText(font, pvalue); | |
| 6404 var seq_length = bars[seq_name]["length"]; | |
| 6405 if (seq_length > max_seq_length) { max_seq_length = seq_length; } | |
| 6406 if (seq_name_width > max_name_width) { max_name_width = seq_name_width; } | |
| 6407 if (pvalue_width > max_pvalue_width) { max_pvalue_width = pvalue_width; } | |
| 6408 } | |
| 6409 | |
| 6410 // Get the length in characters of the longest visible motif. | |
| 6411 var max_motif_length = 0; | |
| 6412 var motif_index, motif_length; | |
| 6413 for (motif_index in visible_motifs) { | |
| 6414 motif_length = data.motifs[motif_index].len; | |
| 6415 if (motif_length > max_motif_length) { max_motif_length = motif_length; } | |
| 6416 } | |
| 6417 | |
| 6418 // Sort the motif indices. | |
| 6419 var motif_indices = []; | |
| 6420 var sorted_motif_indices = []; | |
| 6421 for (motif_index in visible_motifs) { | |
| 6422 motif_indices.push(Number(motif_index)); | |
| 6423 } | |
| 6424 sorted_motif_indices = motif_indices.sort(function(a, b){return a-b;}); | |
| 6425 | |
| 6426 // Set up values for main section. | |
| 6427 var height = (nbars+1) * (2.6*font_size); | |
| 6428 var nmotifs = Object.keys(visible_motifs).length; | |
| 6429 var name_field_width = max_name_width + font_size; | |
| 6430 var pvalue_field_width = max_pvalue_width + font_size; | |
| 6431 var plus_minus_field_width = has_complement ? 2*font_size : font_size; | |
| 6432 var non_diagram_width = name_field_width + pvalue_field_width + plus_minus_field_width; | |
| 6433 var diagram_width = 47 * font_size; | |
| 6434 var pix_per_char = diagram_width/max_seq_length; | |
| 6435 var x_scale_factor = data.sequence_db.max_length/100; // Scale factor comes from function make_block(). | |
| 6436 var diagram_line_height = (height-2*font_size)/nbars; | |
| 6437 var doc_width = diagram_width + non_diagram_width + 2*font_size; | |
| 6438 var doc_height = height + 0.5*font_size; | |
| 6439 | |
| 6440 // Set up values for the legend. | |
| 6441 var tmp_font = legend_font_size + "pt Courier, normal"; | |
| 6442 var courier_width = MeasureText(tmp_font, "A"); | |
| 6443 | |
| 6444 var legend_line_height = 1.2 * legend_font_size; | |
| 6445 var index_field_width = 3 * legend_font_size; | |
| 6446 var symbol_field_width = 5 * legend_font_size; | |
| 6447 var legend_non_consensus_width = index_field_width + symbol_field_width + 3*legend_font_size; | |
| 6448 var legend_hdr_font = legend_font_size + "pt Helvetica, sans-serif"; | |
| 6449 var consensus_hdr_width = MeasureText(legend_hdr_font, "Motif Consensus"); | |
| 6450 var consensus_field_width = doc_width - legend_non_consensus_width - legend_font_size; | |
| 6451 // Get number of characters that will fit in legend consensus field. | |
| 6452 var legend_split_length = Math.floor(consensus_field_width/courier_width); | |
| 6453 // Get number of lines in legend. | |
| 6454 var n_legend_lines = 0; | |
| 6455 for (motif_index in visible_motifs) { | |
| 6456 motif_length = data.motifs[motif_index].len; | |
| 6457 n_legend_lines += Math.ceil(motif_length/legend_split_length); | |
| 6458 } | |
| 6459 if (n_legend_lines > 0) { n_legend_lines += 3; } // header line + 2*space | |
| 6460 var legend_width = legend_non_consensus_width + Math.min(legend_split_length, max_motif_length)*courier_width; | |
| 6461 var legend_height = n_legend_lines * legend_line_height; | |
| 6462 doc_height += legend_height + 1*font_size; | |
| 6463 | |
| 6464 if (make_pdf) { | |
| 6465 // Now create the PDF document. | |
| 6466 // This next line is necessary because jsPDF silently swaps width and height. | |
| 6467 var orient = doc_width > doc_height ? 'landscape' : 'portrait'; | |
| 6468 doc = new jsPDF( | |
| 6469 { | |
| 6470 orientation: orient, | |
| 6471 unit: 'pt', | |
| 6472 format: [doc_width, doc_height] | |
| 6473 } | |
| 6474 ); | |
| 6475 | |
| 6476 // Set the font size for the PDF. | |
| 6477 doc.setFontSize(1.33*font_size); | |
| 6478 | |
| 6479 // Create the header. | |
| 6480 var offset = font_size; | |
| 6481 var liney = 1.5*font_size; | |
| 6482 // .. Name hdr .. | |
| 6483 doc.setFont("Helvetica", "bold"); | |
| 6484 doc.text("Name", offset, liney); | |
| 6485 offset += name_field_width; | |
| 6486 | |
| 6487 // p-value hdr | |
| 6488 doc.setFont("Helvetica", "bolditalic"); | |
| 6489 doc.text("p", offset + font_size, liney); | |
| 6490 doc.setFont("Helvetica", "bold"); | |
| 6491 doc.text("-value", offset + 2*font_size, liney); | |
| 6492 offset += pvalue_field_width + plus_minus_field_width; | |
| 6493 | |
| 6494 // Motif Location hdr | |
| 6495 doc.text("Motif Locations", offset, liney); | |
| 6496 | |
| 6497 // Generate the data object for the PDF. | |
| 6498 liney -= 0.5*font_size; | |
| 6499 var dy = font_size/3.5; | |
| 6500 for (var seq_name in bars) { | |
| 6501 liney += diagram_line_height; | |
| 6502 offset = font_size; | |
| 6503 | |
| 6504 // | |
| 6505 // Generate the text fields. | |
| 6506 // | |
| 6507 doc.setFont("Helvetica", "normal"); | |
| 6508 | |
| 6509 // Sequence name text | |
| 6510 doc.text(seq_name, offset, liney + dy); | |
| 6511 offset += name_field_width; | |
| 6512 | |
| 6513 // p-value text | |
| 6514 doc.text(bars[seq_name]["pvalue"], offset + pvalue_field_width, liney + dy, {align: "right"}); | |
| 6515 offset += pvalue_field_width; | |
| 6516 | |
| 6517 // +/- text (optional) | |
| 6518 if (has_complement) { | |
| 6519 doc.text("+", offset+font_size, liney + dy - font_size/2); | |
| 6520 if (revcomp) { | |
| 6521 doc.text("-", offset+1.15*font_size, liney + dy + font_size/2); | |
| 6522 } | |
| 6523 } | |
| 6524 offset += plus_minus_field_width; | |
| 6525 | |
| 6526 // Generate the base line. | |
| 6527 doc.setLineWidth(0.35); | |
| 6528 doc.line(offset, liney, offset + (bars[seq_name]["length"] * pix_per_char), liney); | |
| 6529 | |
| 6530 // Generate the blocks. | |
| 6531 for (var i = 0; i < bars[seq_name]["width"].length; i++) { | |
| 6532 if (bars[seq_name]["pn"][i] == undefined) { continue; } | |
| 6533 rgb = bars[seq_name]["color"][i].split(",").map(Number); | |
| 6534 var opacity = bars[seq_name]["opacity"][i]; | |
| 6535 if (opacity != 1) { rgb = RGBAtoRGB(rgb, [255,255,255], opacity); } | |
| 6536 var bar_x = offset + (bars[seq_name]["left"][i] * x_scale_factor * pix_per_char); | |
| 6537 var bar_y = (bars[seq_name]["pn"][i] == "+") ? (liney - 0.1*font_size*bars[seq_name]["height"][i]) : liney; | |
| 6538 doc.setFillColor(rgb[0], rgb[1], rgb[2]); | |
| 6539 doc.rect(bar_x, bar_y, bars[seq_name]["width"][i] * x_scale_factor * pix_per_char, 0.1*font_size*bars[seq_name]["height"][i], 'FD'); | |
| 6540 } | |
| 6541 } | |
| 6542 | |
| 6543 // | |
| 6544 // Generate the legend. | |
| 6545 // | |
| 6546 if (n_legend_lines > 0) { | |
| 6547 doc.setFontSize(1.33*legend_font_size); | |
| 6548 dy = 0.8 * legend_font_size; | |
| 6549 | |
| 6550 // The legend header. | |
| 6551 var legend_top = liney + 2*legend_font_size; | |
| 6552 liney += 4.5*legend_font_size; | |
| 6553 offset = legend_font_size; | |
| 6554 doc.setFont("Helvetica", "bold"); | |
| 6555 doc.text("Motif", offset, liney); | |
| 6556 offset += index_field_width + legend_font_size; | |
| 6557 doc.text("Symbol", offset, liney); | |
| 6558 offset += symbol_field_width + legend_font_size; | |
| 6559 doc.text("Motif Consensus", offset, liney); | |
| 6560 liney -= 0.5*legend_font_size; | |
| 6561 liney += legend_line_height; | |
| 6562 | |
| 6563 for (var i=0; i<motif_indices.length; i++) { | |
| 6564 motif_index = sorted_motif_indices[i]; | |
| 6565 offset = legend_font_size; | |
| 6566 | |
| 6567 // Motif Name | |
| 6568 doc.setFont("Helvetica", "normal"); | |
| 6569 var motif_index_string = (motif_index+1).toString(); | |
| 6570 motif_index_string = motif_index_string + "."; | |
| 6571 var dx = 3 * legend_font_size; | |
| 6572 doc.text(motif_index_string, offset+dx, liney+dy, {align: "right"}); | |
| 6573 offset += index_field_width + legend_font_size; | |
| 6574 | |
| 6575 // Motif Symbol | |
| 6576 motif_length = data.motifs[motif_index].len; | |
| 6577 rgb = visible_motifs[motif_index].split(",").map(Number); | |
| 6578 var bar_x = offset; | |
| 6579 var bar_y = liney; | |
| 6580 doc.setFillColor(rgb[0], rgb[1], rgb[2]); | |
| 6581 doc.rect(bar_x, bar_y, symbol_field_width*(motif_length/max_motif_length), legend_font_size, 'FD'); | |
| 6582 offset += symbol_field_width + legend_font_size; | |
| 6583 | |
| 6584 // Motif Consensus Sequence | |
| 6585 doc.setFont("Courier", "normal"); | |
| 6586 var motif_consensus = data.motifs[motif_index].id; | |
| 6587 doc.text(motif_consensus, offset, liney+dy, {maxWidth: legend_split_length*courier_width}); | |
| 6588 liney += Math.ceil(motif_length/legend_split_length) * legend_line_height; | |
| 6589 } | |
| 6590 | |
| 6591 // Draw box around legend. | |
| 6592 doc.rect( | |
| 6593 0.5*legend_font_size, | |
| 6594 legend_top + 0.5*legend_font_size, | |
| 6595 Math.max(legend_width, legend_non_consensus_width + consensus_hdr_width + courier_width), | |
| 6596 legend_height | |
| 6597 ); | |
| 6598 } // legend | |
| 6599 | |
| 6600 doc.save('motif_locations.pdf'); | |
| 6601 | |
| 6602 } else { | |
| 6603 // Download an SVG document. | |
| 6604 var body = d3.select("#blocks").append("svg") | |
| 6605 .attr("width", (diagram_width + non_diagram_width) + "pt") | |
| 6606 .attr("height", (doc_height+legend_font_size).toString()) | |
| 6607 .attr("background-color", "lightgrey") | |
| 6608 .attr("id", "memeSVG") | |
| 6609 .attr("xmlns", "http://www.w3.org/2000/svg"); | |
| 6610 | |
| 6611 // Create the header. | |
| 6612 var x = 0; | |
| 6613 var offset = font_size; | |
| 6614 var liney = 1.5*font_size; | |
| 6615 | |
| 6616 // .. Name hdr .. | |
| 6617 body.append("text") | |
| 6618 .attr("x", offset) | |
| 6619 .attr("y", liney) | |
| 6620 .attr("font-family", "Helvetica, sans-serif") | |
| 6621 .attr("font-size", font_size+"pt") | |
| 6622 .attr("font-weight", "bold") | |
| 6623 .text("Name"); | |
| 6624 offset += name_field_width; | |
| 6625 | |
| 6626 // p-value hdr | |
| 6627 body.append("text") | |
| 6628 .attr("x", offset + 2*font_size) | |
| 6629 .attr("y", liney) | |
| 6630 .attr("font-family", "Helvetica, sans-serif") | |
| 6631 .attr("font-size", font_size+"pt") | |
| 6632 .attr("font-weight", "bold") | |
| 6633 .attr("font-style", "italic") | |
| 6634 .text("p"); | |
| 6635 body.append("text") | |
| 6636 .attr("x", offset + 3*font_size) | |
| 6637 .attr("y", liney) | |
| 6638 .attr("font-family", "Helvetica, sans-serif") | |
| 6639 .attr("font-size", font_size+"pt") | |
| 6640 .attr("font-weight", "bold") | |
| 6641 .text("-value"); | |
| 6642 offset += pvalue_field_width + plus_minus_field_width + font_size; | |
| 6643 | |
| 6644 // Motif Location hdr | |
| 6645 body.append("text") | |
| 6646 .attr("x", offset) | |
| 6647 .attr("y", liney) | |
| 6648 .attr("font-family", "Helvetica, sans-serif") | |
| 6649 .attr("font-size", font_size+"pt") | |
| 6650 .attr("font-weight", "bold") | |
| 6651 .text("Motif Locations"); | |
| 6652 | |
| 6653 // Generate the data for the SVG. | |
| 6654 liney -= 0.5*font_size; | |
| 6655 var dy = font_size/3.5; | |
| 6656 for (var seq_name in bars) { | |
| 6657 liney += diagram_line_height; | |
| 6658 offset = font_size; | |
| 6659 | |
| 6660 // | |
| 6661 // Generate the text fields. | |
| 6662 // | |
| 6663 | |
| 6664 // Sequence name text | |
| 6665 body.append("text") | |
| 6666 .attr("x", offset) | |
| 6667 .attr("y", liney + dy) | |
| 6668 .attr("font-family", "Helvetica, sans-serif") | |
| 6669 .attr("font-size", font_size+"pt") | |
| 6670 .text(seq_name); | |
| 6671 offset += name_field_width; | |
| 6672 | |
| 6673 // p-value text | |
| 6674 body.append("text") | |
| 6675 .attr("x", offset + font_size+ pvalue_field_width) | |
| 6676 .attr("y", liney + dy) | |
| 6677 .attr("font-family", "Helvetica, sans-serif") | |
| 6678 .attr("font-size", font_size+"pt") | |
| 6679 .attr("text-anchor", "end") | |
| 6680 .text(bars[seq_name]["pvalue"]); | |
| 6681 offset += pvalue_field_width + font_size; | |
| 6682 | |
| 6683 // +/- text (optional) | |
| 6684 if (has_complement) { | |
| 6685 body.append("text") | |
| 6686 .attr("x", offset+font_size) | |
| 6687 .attr("y", liney + dy - font_size/2) | |
| 6688 .attr("font-family", "Helvetica, sans-serif") | |
| 6689 .attr("font-size", font_size+"pt") | |
| 6690 .text("+"); | |
| 6691 if (revcomp) { | |
| 6692 body.append("text") | |
| 6693 .attr("x", offset+1.15*font_size) | |
| 6694 .attr("y", liney + dy + font_size/2) | |
| 6695 .attr("font-family", "Helvetica, sans-serif") | |
| 6696 .attr("font-size", font_size+"pt") | |
| 6697 .text("-"); | |
| 6698 } | |
| 6699 } | |
| 6700 offset += plus_minus_field_width; | |
| 6701 | |
| 6702 // Generate the base line. | |
| 6703 body.append("line") | |
| 6704 .attr("x1", offset) | |
| 6705 .attr("x2", offset + (bars[seq_name]["length"] * pix_per_char)) | |
| 6706 .attr("y1", liney) | |
| 6707 .attr("y2", liney) | |
| 6708 .attr("stroke-width", 0.5) | |
| 6709 .attr("stroke","black"); | |
| 6710 | |
| 6711 // Generate the blocks. | |
| 6712 for (var i = 0; i < bars[seq_name]["width"].length; i++) { | |
| 6713 if (bars[seq_name]["pn"][i] == undefined) { continue; } | |
| 6714 body.append("rect") | |
| 6715 .attr("x", offset + (bars[seq_name]["left"][i] * x_scale_factor * pix_per_char) ) | |
| 6716 .attr("y", (bars[seq_name]["pn"][i] == "+") ? (liney - 0.1*font_size*bars[seq_name]["height"][i]) : liney) | |
| 6717 .attr("width", bars[seq_name]["width"][i] * x_scale_factor * pix_per_char) | |
| 6718 .attr("height", 0.1*font_size*bars[seq_name]["height"][i]) | |
| 6719 .attr("fill", "rgb("+bars[seq_name]["color"][i] + ")") | |
| 6720 .attr("fill-opacity", bars[seq_name]["opacity"][i]) | |
| 6721 .attr("stroke-width", 0.5) | |
| 6722 .attr("stroke","black"); | |
| 6723 } | |
| 6724 } | |
| 6725 | |
| 6726 // | |
| 6727 // Generate the legend. | |
| 6728 // | |
| 6729 if (n_legend_lines > 0) { | |
| 6730 dy = 0.8 * legend_font_size; | |
| 6731 | |
| 6732 // The legend header. | |
| 6733 var legend_top = liney + 2*legend_font_size; | |
| 6734 liney += 4.5*legend_font_size; | |
| 6735 offset = legend_font_size; | |
| 6736 body.append("text") | |
| 6737 .attr("x", offset) | |
| 6738 .attr("y", liney) | |
| 6739 .attr("font-family", "Helvetica, sans-serif") | |
| 6740 .attr("font-size", legend_font_size+"pt") | |
| 6741 .attr("font-weight", "bold") | |
| 6742 .text("Motif"); | |
| 6743 offset += index_field_width + legend_font_size; | |
| 6744 body.append("text") | |
| 6745 .attr("x", offset) | |
| 6746 .attr("y", liney) | |
| 6747 .attr("font-family", "Helvetica, sans-serif") | |
| 6748 .attr("font-size", legend_font_size+"pt") | |
| 6749 .attr("font-weight", "bold") | |
| 6750 .text("Symbol"); | |
| 6751 offset += symbol_field_width + legend_font_size; | |
| 6752 body.append("text") | |
| 6753 .attr("x", offset) | |
| 6754 .attr("y", liney) | |
| 6755 .attr("font-family", "Helvetica, sans-serif") | |
| 6756 .attr("font-size", legend_font_size+"pt") | |
| 6757 .attr("font-weight", "bold") | |
| 6758 .text("Motif Consensus"); | |
| 6759 liney -= 0.5*legend_font_size; | |
| 6760 liney += legend_line_height; | |
| 6761 | |
| 6762 for (var i=0; i<motif_indices.length; i++) { | |
| 6763 motif_index = sorted_motif_indices[i]; | |
| 6764 offset = legend_font_size; | |
| 6765 | |
| 6766 // Motif Name | |
| 6767 var motif_index_string = (motif_index+1).toString(); | |
| 6768 motif_index_string = motif_index_string + "."; | |
| 6769 var dx = 3.3 * legend_font_size; | |
| 6770 body.append("text") | |
| 6771 .attr("x", offset+dx) | |
| 6772 .attr("y", liney+dy) | |
| 6773 .attr("font-family", "Helvetica, sans-serif") | |
| 6774 .attr("font-size", legend_font_size+"pt") | |
| 6775 .attr("text-anchor", "end") | |
| 6776 .text(motif_index_string); | |
| 6777 offset += index_field_width + legend_font_size; | |
| 6778 | |
| 6779 // Motif Symbol | |
| 6780 motif_length = data.motifs[motif_index].len; | |
| 6781 var bar_x = offset; | |
| 6782 var bar_y = liney; | |
| 6783 body.append("rect") | |
| 6784 .attr("x", bar_x ) | |
| 6785 .attr("y", liney) | |
| 6786 .attr("width", symbol_field_width*(motif_length/max_motif_length)) | |
| 6787 .attr("height", legend_font_size) | |
| 6788 .attr("fill", "rgb("+ visible_motifs[motif_index] + ")") | |
| 6789 .attr("stroke-width", 0.5) | |
| 6790 .attr("stroke","black"); | |
| 6791 offset += symbol_field_width + legend_font_size; | |
| 6792 | |
| 6793 // Motif Consensus Sequence | |
| 6794 var motif_consensus = data.motifs[motif_index].id; | |
| 6795 var cons_length = motif_consensus.length; | |
| 6796 var start_index = 0; | |
| 6797 while (start_index < cons_length) { | |
| 6798 body.append("text") | |
| 6799 .attr("x", offset) | |
| 6800 .attr("y", liney+dy) | |
| 6801 .attr("font-family", "Courier") | |
| 6802 .attr("font-size", legend_font_size+"pt") | |
| 6803 .text(motif_consensus.slice(start_index, Math.min(cons_length, start_index+legend_split_length))) | |
| 6804 liney += legend_line_height; | |
| 6805 start_index += legend_split_length; | |
| 6806 } | |
| 6807 } | |
| 6808 | |
| 6809 // Draw box around legend. | |
| 6810 body.append("rect") | |
| 6811 .attr("x", 0.5*legend_font_size) | |
| 6812 .attr("y", legend_top + 0.5*legend_font_size) | |
| 6813 .attr("width", Math.max(legend_width, legend_non_consensus_width + consensus_hdr_width + courier_width)) | |
| 6814 .attr("height", legend_height) | |
| 6815 .attr("fill", "none") | |
| 6816 .attr("stroke-width", 1) | |
| 6817 .attr("stroke", "black"); | |
| 6818 | |
| 6819 } // legend | |
| 6820 | |
| 6821 var svg = document.getElementsByTagName("svg")[0].outerHTML; | |
| 6822 var svgBlob = new Blob([svg], {type:"image/svg+xml;charset=utf-8"}); | |
| 6823 var svgUrl = URL.createObjectURL(svgBlob); | |
| 6824 var downloadLink = document.createElement("a"); | |
| 6825 downloadLink.href = svgUrl; | |
| 6826 downloadLink.download = "meme-motif-locations.svg"; | |
| 6827 document.getElementById("sites_sec").appendChild(downloadLink); | |
| 6828 downloadLink.click(); | |
| 6829 downloadLink.remove(); | |
| 6830 document.getElementById("memeSVG").remove(); | |
| 6831 | |
| 6832 } // SVG | |
| 6833 | |
| 6834 }; | |
| 6835 | |
| 6836 function make_blocks() { | |
| 6837 "use strict"; | |
| 6838 function add_seqs_filter(container, id, checked, label_text, help_topic) { | |
| 6839 "use strict"; | |
| 6840 var label, radio; | |
| 6841 radio = document.createElement("input"); | |
| 6842 radio.type = "radio"; | |
| 6843 radio.name = "seqs_display"; | |
| 6844 radio.id = id; | |
| 6845 radio.checked = checked; | |
| 6846 radio.addEventListener('click', action_seqs_filter, false); | |
| 6847 label = document.createElement("label"); | |
| 6848 label.appendChild(document.createTextNode(label_text)); | |
| 6849 label.htmlFor = id; | |
| 6850 container.appendChild(radio); | |
| 6851 container.appendChild(label); | |
| 6852 if (help_topic) { | |
| 6853 container.appendChild(document.createTextNode("\xA0")); | |
| 6854 container.appendChild(help_button(help_topic)); | |
| 6855 } | |
| 6856 } | |
| 6857 function add_block_diagram_button(container, id, buttonText, help_topic) { | |
| 6858 var button, label; | |
| 6859 button = document.createElement("button"); | |
| 6860 button.id = id; | |
| 6861 label = document.createTextNode(buttonText); | |
| 6862 button.appendChild(label); | |
| 6863 button.onclick = (id === "pdfButton") ? download_PDF_block_diagram : download_SVG_block_diagram; | |
| 6864 container.appendChild(document.createTextNode(" ")); | |
| 6865 container.appendChild(button); | |
| 6866 if (help_topic) { | |
| 6867 container.appendChild(document.createTextNode("\xA0")); | |
| 6868 container.appendChild(help_button(help_topic)); | |
| 6869 } | |
| 6870 //var new_icon = document.createElement("img"); | |
| 6871 //new_icon.src = new_icon_src; | |
| 6872 //new_icon.alt = "NEW"; | |
| 6873 //container.appendChild(document.createTextNode(" ")); | |
| 6874 //container.appendChild(new_icon); | |
| 6875 } | |
| 6876 function add_blocks_header(row, title, nopad, help_topic) { | |
| 6877 "use strict"; | |
| 6878 var div, divcp, th; | |
| 6879 th = document.createElement("th"); | |
| 6880 div = document.createElement("div"); | |
| 6881 div.className = "blocks_th_inner"; | |
| 6882 if (typeof title !== "object") { | |
| 6883 title = document.createTextNode("" + title); | |
| 6884 } | |
| 6885 div.appendChild(title); | |
| 6886 if (help_topic) { | |
| 6887 div.appendChild(document.createTextNode("\xA0")); | |
| 6888 div.appendChild(help_button(help_topic)); | |
| 6889 } | |
| 6890 divcp = div.cloneNode(true); | |
| 6891 divcp.className = "blocks_th_hidden"; | |
| 6892 th.appendChild(div); | |
| 6893 th.appendChild(divcp); | |
| 6894 if (nopad) { | |
| 6895 th.className = "nopad"; | |
| 6896 } | |
| 6897 row.appendChild(th); | |
| 6898 } | |
| 6899 var container; | |
| 6900 var page, view_height, outer_tbl, inner_tbl, tbl, thead, tbody, rhead; | |
| 6901 var in_view, i, seq_count; | |
| 6902 | |
| 6903 page = (document.compatMode === "CSS1Compat") ? document.documentElement : document.body; | |
| 6904 view_height = Math.max(page.clientHeight - 300, 300); | |
| 6905 | |
| 6906 container = $("blocks"); | |
| 6907 toggle_class(container, "hide_empty_seqs", true); | |
| 6908 toggle_class(container, "hide_only_scan", true); | |
| 6909 container.innerHTML = ""; | |
| 6910 add_seqs_filter(container, "rdo_sites_only", true, "Only Motif Sites", "pop_motif_sites"); | |
| 6911 add_seqs_filter(container, "rdo_sites_and_scan", false, "Motif Sites+Scanned Sites", "pop_scanned_sites"); | |
| 6912 add_seqs_filter(container, "rdo_all_seqs", false, "All Sequences", "pop_all_sequences"); | |
| 6913 add_block_diagram_button(container, "pdfButton", "Download PDF", "pop_download_pdf_motif_locations"); | |
| 6914 add_block_diagram_button(container, "svgButton", "Download SVG", "pop_download_svg_motif_locations"); | |
| 6915 | |
| 6916 outer_tbl = document.createElement("div"); | |
| 6917 outer_tbl.className = "blocks_outer"; | |
| 6918 | |
| 6919 inner_tbl = document.createElement("div"); | |
| 6920 inner_tbl.id = "blocks_scroll"; | |
| 6921 inner_tbl.className = "blocks_inner"; | |
| 6922 inner_tbl.style.maxHeight = view_height + "px"; | |
| 6923 outer_tbl.appendChild(inner_tbl); | |
| 6924 | |
| 6925 tbl = document.createElement("table"); | |
| 6926 tbl.className = "blocks_tbl"; | |
| 6927 inner_tbl.appendChild(tbl); | |
| 6928 | |
| 6929 thead = document.createElement("thead"); | |
| 6930 tbl.appendChild(thead); | |
| 6931 tbody = document.createElement("tbody"); | |
| 6932 tbl.appendChild(tbody); | |
| 6933 | |
| 6934 rhead = thead.insertRow(thead.rows.length); | |
| 6935 add_blocks_header(rhead, "", true); | |
| 6936 add_blocks_header(rhead, "Name", false, "pop_seq_name"); | |
| 6937 add_blocks_header(rhead, make_pv_text(), false, "pop_seq_pvalue"); | |
| 6938 add_blocks_header(rhead, "Motif Locations", false, "pop_motif_location"); | |
| 6939 | |
| 6940 container.appendChild(outer_tbl); | |
| 6941 | |
| 6942 seq_count = data["sequence_db"]["sequences"].length; | |
| 6943 in_view = Math.max(Math.ceil(view_height / 25), 1); | |
| 6944 i = append_blocks_entries(tbody, 0, in_view); | |
| 6945 | |
| 6946 while (i < seq_count && inner_tbl.scrollHeight - (inner_tbl.scrollTop + inner_tbl.offsetHeight) < 400) { | |
| 6947 i = append_blocks_entries(tbody, i, 20); | |
| 6948 } | |
| 6949 inner_tbl.data_blocks_index = i; | |
| 6950 if (i < seq_count) { | |
| 6951 inner_tbl.addEventListener('scroll', make_blocks_entries, false); | |
| 6952 } | |
| 6953 } | |
| 6954 | |
| 6955 function make_scan_popup(site, motif) { | |
| 6956 return function (e) { | |
| 6957 "use strict"; | |
| 6958 var pop, xy, padding, edge_padding, pop_left, pop_top, page_width; | |
| 6959 var lflank, match, rflank, pspm; | |
| 6960 if (!e) var e = window.event; | |
| 6961 pop = make_scan_popup.pop; | |
| 6962 if (e.type === "mouseover") { | |
| 6963 if (pop) return; | |
| 6964 pop = clone_template("tmpl_scan_info"); | |
| 6965 pspm = new Pspm(motif.pwm); | |
| 6966 if (site.rc) pspm.reverse_complement(current_alphabet); | |
| 6967 set_tvar(pop, "tvar_logo", make_small_logo(current_alphabet, pspm, {"className": "scan_logo"})); | |
| 6968 set_tvar(pop, "tvar_motif", motif.id); | |
| 6969 set_tvar(pop, "tvar_pvalue", site.pvalue.toExponential(2)); | |
| 6970 set_tvar(pop, "tvar_start", site.pos + 1); | |
| 6971 set_tvar(pop, "tvar_end", site.pos + motif.len); | |
| 6972 | |
| 6973 document.body.appendChild(pop); | |
| 6974 position_popup(this, pop); | |
| 6975 make_scan_popup.pop = pop; | |
| 6976 } else if (e.type === "mouseout") { | |
| 6977 if (pop) { | |
| 6978 pop.parentNode.removeChild(pop); | |
| 6979 make_scan_popup.pop = null; | |
| 6980 } | |
| 6981 } | |
| 6982 }; | |
| 6983 } | |
| 6984 | |
| 6985 function make_block_popup(site, motif, block) { | |
| 6986 return function (e) { | |
| 6987 "use strict"; | |
| 6988 var pop; | |
| 6989 var lflank, match, rflank, pspm, ruler, match_seq, match_width; | |
| 6990 if (!e) var e = window.event; | |
| 6991 pop = make_block_popup.pop; | |
| 6992 if (e.type === "mouseover") { | |
| 6993 if (pop) return; | |
| 6994 pop = clone_template("tmpl_block_info"); | |
| 6995 pspm = new Pspm(motif.pwm); | |
| 6996 if (site.rc) { // must be dna | |
| 6997 pspm.reverse_complement(current_alphabet); | |
| 6998 lflank = current_alphabet.invcomp_seq(site.rflank); | |
| 6999 match = current_alphabet.invcomp_seq(site.match); | |
| 7000 rflank = current_alphabet.invcomp_seq(site.lflank); | |
| 7001 } else { | |
| 7002 lflank = site.lflank; | |
| 7003 match = site.match; | |
| 7004 rflank = site.rflank; | |
| 7005 } | |
| 7006 ruler = document.getElementById("measure_match"); | |
| 7007 match_seq = make_seq(current_alphabet, match); | |
| 7008 ruler.innerHTML = ""; | |
| 7009 ruler.appendChild(match_seq); | |
| 7010 match_width = ruler.clientWidth; | |
| 7011 ruler.removeChild(match_seq); | |
| 7012 set_tvar(pop, "tvar_lflank", lflank); | |
| 7013 set_tvar(pop, "tvar_match", match_seq); | |
| 7014 set_tvar(pop, "tvar_rflank", rflank); | |
| 7015 set_tvar(pop, "tvar_logo_pad", lflank); | |
| 7016 set_tvar(pop, "tvar_logo", make_small_logo(current_alphabet, pspm, {"width": match_width})); | |
| 7017 set_tvar(pop, "tvar_motif", motif.id); | |
| 7018 set_tvar(pop, "tvar_pvalue", site.pvalue.toExponential(2)); | |
| 7019 set_tvar(pop, "tvar_start", site.pos + 1); | |
| 7020 set_tvar(pop, "tvar_end", site.pos + motif.len); | |
| 7021 | |
| 7022 document.body.appendChild(pop); | |
| 7023 position_popup(block, pop); | |
| 7024 make_block_popup.pop = pop; | |
| 7025 } else if (e.type === "mouseout") { | |
| 7026 if (pop) { | |
| 7027 pop.parentNode.removeChild(pop); | |
| 7028 make_block_popup.pop = null; | |
| 7029 } | |
| 7030 } | |
| 7031 }; | |
| 7032 } | |
| 7033 | |
| 7034 // | |
| 7035 // action_show_more | |
| 7036 // | |
| 7037 // Show more information on the motif. | |
| 7038 /// | |
| 7039 function action_show_more(e) { | |
| 7040 var node, tr, tbody, table, container, motif, ordinal; | |
| 7041 var expanded_motif; | |
| 7042 if (!e) e = window.event; | |
| 7043 if (e.type === "keydown") { | |
| 7044 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
| 7045 return; | |
| 7046 } | |
| 7047 // stop a submit or something like that | |
| 7048 e.preventDefault(); | |
| 7049 } | |
| 7050 // find the row that contains the cell | |
| 7051 node = this; | |
| 7052 do { | |
| 7053 if (node.tagName === "TR") break; | |
| 7054 } while (node = node.parentNode); | |
| 7055 if (!node) throw new Error("Expected to find row!?"); | |
| 7056 tr = node; | |
| 7057 // get info | |
| 7058 motif = tr.data_motif; | |
| 7059 ordinal = tr.data_ordinal; | |
| 7060 // find tbody | |
| 7061 do { | |
| 7062 if (node.tagName === "TBODY") break; | |
| 7063 } while (node = node.parentNode); | |
| 7064 if (!node) throw new Error("Expected to find tbody!?"); | |
| 7065 tbody = node; | |
| 7066 // find table | |
| 7067 do { | |
| 7068 if (node.tagName === "TABLE") break; | |
| 7069 } while (node = node.parentNode); | |
| 7070 if (!node) throw new Error("Expected to find table!?"); | |
| 7071 table = node; | |
| 7072 // find container | |
| 7073 container = node.parentNode; | |
| 7074 // make a expanded motif | |
| 7075 motif["expanded"] = true; | |
| 7076 expanded_motif = make_expanded_motif(current_alphabet, ordinal, motif, | |
| 7077 container.data_more_x, container.data_submit_x); | |
| 7078 // now determine how to place it | |
| 7079 if (tbody.rows.length === 1) { | |
| 7080 // only us in the table so the table can be replaced | |
| 7081 container.replaceChild(expanded_motif, table); | |
| 7082 } else if (tbody.rows[0] === tr) { | |
| 7083 // first row, so remove and insert an expanded motif before | |
| 7084 table.deleteRow(tr.rowIndex); | |
| 7085 container.insertBefore(expanded_motif, table); | |
| 7086 } else if (tbody.rows[tbody.rows.length - 1] === tr) { | |
| 7087 // last row, so remove and insert an expanded motif after | |
| 7088 table.deleteRow(tr.rowIndex); | |
| 7089 container.insertBefore(expanded_motif, table.nextSibling); | |
| 7090 } else { | |
| 7091 var table2, tbody2; | |
| 7092 table2 = table.cloneNode(false); | |
| 7093 table2.appendChild(table.tHead.cloneNode(true)); | |
| 7094 tbody2 = table.tBodies[0].cloneNode(false); | |
| 7095 table2.appendChild(tbody2); | |
| 7096 container.insertBefore(table2, table.nextSibling); | |
| 7097 for (i = tbody.rows.length - 1; i >= 0; i--) { | |
| 7098 row = tbody.rows[i]; | |
| 7099 row.parentNode.removeChild(row); | |
| 7100 if (row === tr) { | |
| 7101 break; | |
| 7102 } | |
| 7103 tbody2.insertBefore(row, tbody2.rows[0]); | |
| 7104 } | |
| 7105 container.insertBefore(expanded_motif, table2); | |
| 7106 } | |
| 7107 find_child(expanded_motif, "tvar_less").focus(); | |
| 7108 } | |
| 7109 | |
| 7110 // | |
| 7111 // action_show_less | |
| 7112 // | |
| 7113 // Show less information on the motif. | |
| 7114 /// | |
| 7115 function action_show_less(e) { | |
| 7116 var btn; | |
| 7117 var expanded_motif, container, motif, ordinal, colw, focus_target; | |
| 7118 var table, tbody, tbody2, row, table_before, table_after; | |
| 7119 if (!e) e = window.event; | |
| 7120 if (e.type === "keydown") { | |
| 7121 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
| 7122 return; | |
| 7123 } | |
| 7124 // stop a submit or something like that | |
| 7125 e.preventDefault(); | |
| 7126 } | |
| 7127 btn = this; | |
| 7128 // find expanded motif | |
| 7129 expanded_motif = find_parent(btn, "expanded_motif"); | |
| 7130 if (!expanded_motif) throw new Error("Expected expanded motif."); | |
| 7131 // find the container | |
| 7132 container = expanded_motif.parentNode; | |
| 7133 // get data | |
| 7134 motif = expanded_motif.data_motif; | |
| 7135 ordinal = expanded_motif.data_ordinal; | |
| 7136 colw = container.data_colw; | |
| 7137 // get the table before | |
| 7138 table_before = expanded_motif.previousSibling; | |
| 7139 if (table_before && table_before.tagName !== "TABLE") { | |
| 7140 table_before = null; | |
| 7141 } | |
| 7142 // get the table after | |
| 7143 table_after = expanded_motif.nextSibling; | |
| 7144 if (table_after && table_after.tagName !== "TABLE") { | |
| 7145 table_after = null; | |
| 7146 } | |
| 7147 // see if there is a table below or above that we can put this in. | |
| 7148 // if there is a table both below and above then add this motif and | |
| 7149 // all ones below to the above table | |
| 7150 motif["expanded"] = false; | |
| 7151 if (table_before && table_after) { | |
| 7152 tbody = table_before.tBodies[0]; | |
| 7153 row = tbody.insertRow(tbody.rows.length); | |
| 7154 make_motif_table_entry(row, current_alphabet, ordinal, motif, colw); | |
| 7155 focus_target = find_child(row.cells[5], "sym_btn"); | |
| 7156 container.removeChild(expanded_motif); | |
| 7157 tbody2 = table_after.tBodies[0]; | |
| 7158 while (tbody2.rows.length > 0) { | |
| 7159 row = tbody2.rows[0]; | |
| 7160 row.parentNode.removeChild(row); | |
| 7161 tbody.appendChild(row); | |
| 7162 } | |
| 7163 container.removeChild(table_after); | |
| 7164 } else if (table_before) { | |
| 7165 tbody = table_before.tBodies[0]; | |
| 7166 row = tbody.insertRow(tbody.rows.length); | |
| 7167 make_motif_table_entry(row, current_alphabet, ordinal, motif, colw); | |
| 7168 focus_target = find_child(row.cells[5], "sym_btn"); | |
| 7169 container.removeChild(expanded_motif); | |
| 7170 } else if (table_after) { | |
| 7171 tbody = table_after.tBodies[0]; | |
| 7172 row = tbody.insertRow(0); | |
| 7173 make_motif_table_entry(row, current_alphabet, ordinal, motif, colw); | |
| 7174 focus_target = find_child(row.cells[5], "sym_btn"); | |
| 7175 container.removeChild(expanded_motif); | |
| 7176 } else { | |
| 7177 //no table above or below! | |
| 7178 // make a new table | |
| 7179 table = make_motifs_table(current_alphabet, ordinal, [motif], colw, data["stop_reason"]); | |
| 7180 focus_target = find_child(table.tBodies[0].rows[0].cells[5], "sym_btn"); | |
| 7181 container.replaceChild(table, expanded_motif); | |
| 7182 } | |
| 7183 focus_target.focus(); | |
| 7184 } | |
| 7185 | |
| 7186 //TODO -- can we delete this junk? | |
| 7187 //function action_show_outpop(e) { | |
| 7188 function fred_action_show_outpop(e) { | |
| 7189 "use strict"; | |
| 7190 function init() { | |
| 7191 "use strict"; | |
| 7192 var close_btn, next_btn, prev_btn, cancel_btn, do_btn; | |
| 7193 var tab1, tab2, tab3; | |
| 7194 var pnl1, pnl2, pnl3; | |
| 7195 var format_list; | |
| 7196 var tbl_submit, inputs, i, default_prog; | |
| 7197 close_btn = $("outpop_close"); | |
| 7198 close_btn.addEventListener("click", action_hide_outpop, false); | |
| 7199 close_btn.addEventListener("keydown", action_hide_outpop, false); | |
| 7200 next_btn = $("outpop_next"); | |
| 7201 next_btn.addEventListener("click", action_outpop_next, false); | |
| 7202 next_btn.addEventListener("keydown", action_outpop_next, false); | |
| 7203 prev_btn = $("outpop_prev"); | |
| 7204 prev_btn.addEventListener("click", action_outpop_prev, false); | |
| 7205 prev_btn.addEventListener("keydown", action_outpop_prev, false); | |
| 7206 cancel_btn = $("outpop_cancel"); | |
| 7207 cancel_btn.addEventListener("click", action_hide_outpop, false); | |
| 7208 do_btn = $("outpop_do"); | |
| 7209 do_btn.addEventListener("click", action_outpop_submit, false); | |
| 7210 tab1 = $("outpop_tab_1"); | |
| 7211 tab1.tabIndex = 0; | |
| 7212 tab1.addEventListener("click", action_outpop_tab, false); | |
| 7213 tab1.addEventListener("keydown", action_outpop_tab, false); | |
| 7214 tab2 = $("outpop_tab_2"); | |
| 7215 tab2.tabIndex = 0; | |
| 7216 tab2.addEventListener("click", action_outpop_tab, false); | |
| 7217 tab2.addEventListener("keydown", action_outpop_tab, false); | |
| 7218 tab3 = $("outpop_tab_3"); | |
| 7219 tab3.tabIndex = 0; | |
| 7220 tab3.addEventListener("click", action_outpop_tab, false); | |
| 7221 tab3.addEventListener("keydown", action_outpop_tab, false); | |
| 7222 pnl1 = $("outpop_pnl_1"); | |
| 7223 pnl2 = $("outpop_pnl_2"); | |
| 7224 pnl3 = $("outpop_pnl_3"); | |
| 7225 toggle_class(tab1, "activeTab", true); | |
| 7226 toggle_class(tab2, "activeTab", false); | |
| 7227 toggle_class(tab3, "activeTab", false); | |
| 7228 pnl1.style.display = "block"; | |
| 7229 pnl2.style.display = "none"; | |
| 7230 pnl3.style.display = "none"; | |
| 7231 format_list = $("text_format"); | |
| 7232 format_list.addEventListener("change", action_outpop_format, false); | |
| 7233 // setup program selection | |
| 7234 tbl_submit = $("programs"); | |
| 7235 // when not dna, hide the inputs for programs that require dna motifs | |
| 7236 toggle_class(tbl_submit, "alphabet_dna", current_alphabet.has_complement());//TODO alphabet_dna is a bad name for a field when allowing custom alphabets | |
| 7237 // add a click listener for the radio buttons | |
| 7238 inputs = tbl_submit.querySelectorAll("input[type='radio']"); | |
| 7239 for (i = 0; i < inputs.length; i++) { | |
| 7240 inputs[i].addEventListener("click", action_outpop_program, false); | |
| 7241 } | |
| 7242 // ensure that a default program option is selected for DNA and Protein | |
| 7243 default_prog = document.getElementById(current_alphabet.has_complement() ? "submit_tomtom" : "submit_fimo"); //TODO Tomtom might require a more strict definition of DNA | |
| 7244 default_prog.checked = true; | |
| 7245 action_outpop_program.call(default_prog); | |
| 7246 // disable reverse-complement when not DNA | |
| 7247 $("logo_rc_option").disabled = !current_alphabet.has_complement(); | |
| 7248 // set errorbars on when ssc is on | |
| 7249 $("logo_ssc").addEventListener("change", action_outpop_ssc, false); | |
| 7250 } | |
| 7251 var node; | |
| 7252 // store the focused element | |
| 7253 action_hide_outpop.last_active = document.activeElement; | |
| 7254 if (!e) e = window.event; | |
| 7255 if (e.type === "keydown") { | |
| 7256 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
| 7257 return; | |
| 7258 } | |
| 7259 // stop a submit or something like that | |
| 7260 e.preventDefault(); | |
| 7261 } | |
| 7262 // hide the help popup | |
| 7263 help_popup(); | |
| 7264 // on first load initilize the popup | |
| 7265 if (!action_show_outpop.ready) { | |
| 7266 init(); | |
| 7267 action_show_outpop.ready = true; | |
| 7268 } | |
| 7269 // load the motif logo | |
| 7270 node = this; | |
| 7271 do { | |
| 7272 if (/\bexpanded_motif\b/.test(node.className) || node.tagName === "TR") break; | |
| 7273 } while (node = node.parentNode); | |
| 7274 if (node === null) throw new Error("Expected node!"); | |
| 7275 update_outpop_motif(node.data_ordinal - 1); | |
| 7276 // display the download popup | |
| 7277 $("grey_out_page").style.display = "block"; | |
| 7278 $("download").style.display = "block"; | |
| 7279 $("outpop_close").focus(); | |
| 7280 } // fred_action_show_outpop | |
| 7281 | |
| 7282 function action_btn_rc(e) { | |
| 7283 "use strict"; | |
| 7284 var node, tr, motif, box, logo_box, tab_st, tab_rc, rc; | |
| 7285 if (!e) e = window.event; | |
| 7286 if (e.type === "keydown") { | |
| 7287 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
| 7288 return; | |
| 7289 } | |
| 7290 // stop a submit or something like that | |
| 7291 e.preventDefault(); | |
| 7292 } | |
| 7293 node = this; | |
| 7294 do { | |
| 7295 if (node.tagName === "TR") break; | |
| 7296 } while (node = node.parentNode); | |
| 7297 if (!node) throw new Error("Expected to find row!?"); | |
| 7298 tr = node; | |
| 7299 // get info | |
| 7300 motif = tr.data_motif; | |
| 7301 box = find_parent(this, "preview_box"); | |
| 7302 logo_box = find_child(box, "preview_logo_box"); | |
| 7303 tab_st = find_child(box, "plus"); | |
| 7304 tab_rc = find_child(box, "minus"); | |
| 7305 rc = (this === tab_rc); | |
| 7306 motif["rc"] = rc; | |
| 7307 toggle_class(logo_box, "show_rc_logo", rc); | |
| 7308 toggle_class(tab_st, "active", !rc); | |
| 7309 toggle_class(tab_rc, "active", rc); | |
| 7310 } | |
| 7311 | |
| 7312 function action_rc_tab(e) { | |
| 7313 "use strict"; | |
| 7314 var box, logo_box, tab_st, tab_rc, rc; | |
| 7315 if (!e) e = window.event; | |
| 7316 if (e.type === "keydown") { | |
| 7317 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
| 7318 return; | |
| 7319 } | |
| 7320 // stop a submit or something like that | |
| 7321 e.preventDefault(); | |
| 7322 } | |
| 7323 box = find_parent(this, "expanded_motif"); | |
| 7324 logo_box = find_child(box, "tvar_logo"); | |
| 7325 tab_st = find_child(box, "tvar_tab"); | |
| 7326 tab_rc = find_child(box, "tvar_tab_rc"); | |
| 7327 rc = (this === tab_rc); | |
| 7328 box.data_motif["rc"] = rc; | |
| 7329 toggle_class(logo_box, "show_rc_logo", rc); | |
| 7330 toggle_class(tab_st, "activeTab", !rc); | |
| 7331 toggle_class(tab_rc, "activeTab", rc); | |
| 7332 } | |
| 7333 | |
| 7334 function action_seqs_filter() { | |
| 7335 "use strict"; | |
| 7336 var block_container; | |
| 7337 block_container = $("blocks"); | |
| 7338 if ($("rdo_all_seqs").checked) { | |
| 7339 toggle_class(block_container, "hide_empty_seqs", false); | |
| 7340 toggle_class(block_container, "hide_only_scan", false); | |
| 7341 } else if ($("rdo_sites_and_scan").checked) { | |
| 7342 toggle_class(block_container, "hide_empty_seqs", true); | |
| 7343 toggle_class(block_container, "hide_only_scan", false); | |
| 7344 } else if ($("rdo_sites_only").checked) { | |
| 7345 toggle_class(block_container, "hide_empty_seqs", true); | |
| 7346 toggle_class(block_container, "hide_only_scan", true); | |
| 7347 } | |
| 7348 } | |
| 7349 | |
| 7350 // | |
| 7351 // page_loaded | |
| 7352 // | |
| 7353 // Called when the page has loaded for the first time. | |
| 7354 /// | |
| 7355 function page_loaded() { | |
| 7356 post_load_setup(); | |
| 7357 } | |
| 7358 | |
| 7359 // | |
| 7360 // page_loaded | |
| 7361 // | |
| 7362 // Called when a cached page is reshown. | |
| 7363 /// | |
| 7364 function page_shown(e) { | |
| 7365 if (e.persisted) post_load_setup(); | |
| 7366 } | |
| 7367 | |
| 7368 // | |
| 7369 // page_loaded | |
| 7370 // | |
| 7371 // Called when the page is resized | |
| 7372 /// | |
| 7373 function page_resized() { | |
| 7374 var page, blocks_scroll; | |
| 7375 update_scroll_pad(); | |
| 7376 page = (document.compatMode === "CSS1Compat") ? document.documentElement : document.body; | |
| 7377 blocks_scroll = $("blocks_scroll"); | |
| 7378 if (blocks_scroll) { | |
| 7379 blocks_scroll.style.maxHeight = Math.max(page.clientHeight - 300, 300) + "px"; | |
| 7380 } | |
| 7381 } | |
| 7382 | |
| 7383 // | |
| 7384 // pre_load_setup | |
| 7385 // | |
| 7386 // Run before the page is displayed | |
| 7387 /// | |
| 7388 function pre_load_setup() { | |
| 7389 var start, hue, sat, light, divisions; | |
| 7390 var i, j, motifs, motif, sites, site, sequences, sequence; | |
| 7391 var max_seq_len; | |
| 7392 motifs = data["motifs"]; | |
| 7393 sequences = data["sequence_db"]["sequences"]; | |
| 7394 max_seq_len = 1; | |
| 7395 if (sequences) { // no sequences if -brief | |
| 7396 for (i = 0; i < sequences.length; i++) { | |
| 7397 sequence = sequences[i]; | |
| 7398 sequence["sites"] = []; | |
| 7399 if (sequence["length"] > max_seq_len) { | |
| 7400 max_seq_len = sequence["length"]; | |
| 7401 } | |
| 7402 } | |
| 7403 } | |
| 7404 data["sequence_db"]["max_length"] = max_seq_len; | |
| 7405 // use hsl colours | |
| 7406 start = 0; //red | |
| 7407 sat = 100; | |
| 7408 light = 50; | |
| 7409 for (i = 0; i < motifs.length; i++) { | |
| 7410 motif = motifs[i]; | |
| 7411 // give the motif a colour | |
| 7412 divisions = 1 << Math.ceil(Math.log(i + 1) / Math.LN2); | |
| 7413 hue = start + (360 / divisions) * ((i - (divisions >> 1)) * 2 + 1); | |
| 7414 motif["colour"] = "hsl(" + hue + ", " + sat + "%, " + light + "%)"; | |
| 7415 // associate sites with sequences as well | |
| 7416 // to make generating the block diagram easier | |
| 7417 sites = motif["sites"]; | |
| 7418 for (j = 0; j < sites.length; j++) { | |
| 7419 site = sites[j]; | |
| 7420 sequence = sequences[site["seq"]]; | |
| 7421 // record the motif index | |
| 7422 site["motif"] = i; | |
| 7423 // add the site to the sequence | |
| 7424 sequence["sites"].push(site); | |
| 7425 } | |
| 7426 } | |
| 7427 } | |
| 7428 | |
| 7429 // | |
| 7430 // post_load_setup | |
| 7431 // | |
| 7432 // Run when the page has loaded, or been reloaded. | |
| 7433 // | |
| 7434 function post_load_setup() { | |
| 7435 update_scroll_pad(); | |
| 7436 if (data["motifs"].length > 0) { | |
| 7437 make_motifs(); | |
| 7438 if (data.sequence_db.primary_count > data.options.brief) { | |
| 7439 if (data.options.brief == 1000) { | |
| 7440 $("blocks").innerHTML = "<p>Output of sites suppressed because there were more than 1000 (primary) sequences.</p>"; | |
| 7441 } else { | |
| 7442 $("blocks").innerHTML = "<p>Output of motif locations suppressed by -brief option.</p>"; | |
| 7443 } | |
| 7444 } else { | |
| 7445 make_blocks(); | |
| 7446 } | |
| 7447 } else { | |
| 7448 $("motifs").innerHTML = "<p>No significant motifs found!</p>"; // clear content | |
| 7449 $("motifs").innerHTML += "<p><b>" + data["stop_reason"] + "</b></p>"; | |
| 7450 $("blocks").innerHTML = "<p>No significant motifs found!</p>"; | |
| 7451 } | |
| 7452 } | |
| 7453 | |
| 7454 pre_load_setup(); | |
| 7455 </script> | |
| 7456 <script> | |
| 7457 // | |
| 7458 // template.js | |
| 7459 // | |
| 7460 | |
| 7461 /* | |
| 7462 * Fill in a template variable | |
| 7463 */ | |
| 7464 function set_tvar(template, tvar, value) { | |
| 7465 var node; | |
| 7466 node = find_child(template, tvar); | |
| 7467 if (node === null) { | |
| 7468 throw new Error("Template does not contain variable " + tvar); | |
| 7469 } | |
| 7470 node.innerHTML = ""; | |
| 7471 if (typeof value !== "object") { | |
| 7472 node.appendChild(document.createTextNode(value)); | |
| 7473 } else { | |
| 7474 node.appendChild(value); | |
| 7475 } | |
| 7476 } // set_tvar | |
| 7477 | |
| 7478 /* | |
| 7479 * Get the text contained within the element. | |
| 7480 */ | |
| 7481 function elem_text(elem, separator) { | |
| 7482 if (separator === undefined) separator = ''; | |
| 7483 return text_nodes(elem).map(node_text).join(separator); | |
| 7484 } | |
| 7485 | |
| 7486 /* | |
| 7487 * Get the text out of a specific text node. | |
| 7488 */ | |
| 7489 function node_text(node) { | |
| 7490 if (node === undefined) { | |
| 7491 return ''; | |
| 7492 } else if (node.textContent) { | |
| 7493 return node.textContent; | |
| 7494 } else if (node.innerText) { | |
| 7495 return node.innerText; | |
| 7496 } else { | |
| 7497 return ''; | |
| 7498 } | |
| 7499 } | |
| 7500 | |
| 7501 /* | |
| 7502 * Find all text nodes in the given container. | |
| 7503 */ | |
| 7504 function text_nodes(container) { | |
| 7505 var textNodes = []; | |
| 7506 var stack = [container]; | |
| 7507 // depth first search to maintain ordering when flattened | |
| 7508 while (stack.length > 0) { | |
| 7509 var node = stack.pop(); | |
| 7510 if (node.nodeType == Node.TEXT_NODE) { | |
| 7511 textNodes.push(node); | |
| 7512 } else { | |
| 7513 for (var i = node.childNodes.length-1; i >= 0; i--) { | |
| 7514 stack.push(node.childNodes[i]); | |
| 7515 } | |
| 7516 } | |
| 7517 } | |
| 7518 return textNodes; | |
| 7519 } | |
| 7520 | |
| 7521 /* | |
| 7522 * Create a button designed to contain a single symbol | |
| 7523 */ | |
| 7524 function make_sym_btn(symbol, title, action) { | |
| 7525 var box, sbox; | |
| 7526 box = document.createElement("div"); | |
| 7527 box.tabIndex = 0; | |
| 7528 box.className = "sym_btn"; | |
| 7529 sbox = document.createElement("span"); | |
| 7530 if (typeof symbol === "string") { | |
| 7531 sbox.appendChild(document.createTextNode(symbol)); | |
| 7532 } else { | |
| 7533 sbox.appendChild(symbol); | |
| 7534 } | |
| 7535 box.appendChild(sbox); | |
| 7536 box.title = title; | |
| 7537 box.addEventListener('click', action, false); | |
| 7538 box.addEventListener('keydown', action, false); | |
| 7539 return box; | |
| 7540 } | |
| 7541 | |
| 7542 /* | |
| 7543 * Create a pair of text spans with different classes. | |
| 7544 * This is useful when using CSS to only display one of them. | |
| 7545 */ | |
| 7546 function text_pair(txt1, cls1, txt2, cls2) { | |
| 7547 var container, part1, part2; | |
| 7548 container = document.createElement("span"); | |
| 7549 part1 = document.createElement("span"); | |
| 7550 part1.appendChild(document.createTextNode(txt1)); | |
| 7551 part1.className = cls1; | |
| 7552 container.appendChild(part1); | |
| 7553 part2 = document.createElement("span"); | |
| 7554 part2.appendChild(document.createTextNode(txt2)); | |
| 7555 part2.className = cls2; | |
| 7556 container.appendChild(part2); | |
| 7557 return container; | |
| 7558 } | |
| 7559 </script> | |
| 7560 <script> | |
| 7561 // | |
| 7562 // citation.js | |
| 7563 // | |
| 7564 function get_citation_text(doc_type, extra) { | |
| 7565 var html; | |
| 7566 | |
| 7567 switch (doc_type) { | |
| 7568 case 'AMA': | |
| 7569 return(get_citation_text("GOMo", extra)); | |
| 7570 case 'AME': | |
| 7571 return(extra + ` | |
| 7572 <span class="citation"> | |
| 7573 Robert C. McLeay and Timothy L. Bailey, | |
| 7574 "Motif Enrichment Analysis: a unified framework and an evaluation on ChIP data", | |
| 7575 <i>BMC Bioinformatics</i>, <b>11</b>:165, 2010. | |
| 7576 <a href="http://www.biomedcentral.com/1471-2105/11/165">[full text]</a> | |
| 7577 </span> | |
| 7578 `); | |
| 7579 case 'CentriMo': | |
| 7580 return(extra + ` | |
| 7581 <span class="citation"> | |
| 7582 Timothy L. Bailey and Philip Machanick, | |
| 7583 "Inferring direct DNA binding from ChIP-seq", | |
| 7584 <i>Nucleic Acids Research</i>, <b>40</b>:e128, 2012. | |
| 7585 <a href="http://nar.oxfordjournals.org/content/40/17/e128">[Full Text]</a> | |
| 7586 </span> | |
| 7587 `); | |
| 7588 case 'DREME': | |
| 7589 return(extra + ` | |
| 7590 <span class="citation"> | |
| 7591 Timothy L. Bailey, | |
| 7592 "DREME: Motif discovery in transcription factor ChIP-seq data", | |
| 7593 <i>Bioinformatics</i>, <b>27</b>(12):1653-1659, 2011. | |
| 7594 <a href="http://bioinformatics.oxfordjournals.org/content/27/12/1653">[full text]</a> | |
| 7595 </span> | |
| 7596 `); | |
| 7597 case 'FIMO': | |
| 7598 return(extra + ` | |
| 7599 <span class="citation"> | |
| 7600 Charles E. Grant, Timothy L. Bailey and William Stafford Noble, | |
| 7601 "FIMO: Scanning for occurrences of a given motif", | |
| 7602 <i>Bioinformatics</i> <b>27</b>(7):1017-1018, 2011. | |
| 7603 <a href="http://bioinformatics.oxfordjournals.org/content/27/7/1017">[full text]</a> | |
| 7604 </span> | |
| 7605 `); | |
| 7606 case 'GLAM2': | |
| 7607 case 'GLAM2SCAN': | |
| 7608 return(extra + ` | |
| 7609 <span class="citation"> | |
| 7610 Martin C. Frith, Neil F. W. Saunders, Bostjan Kobe and Timothy L. Bailey, | |
| 7611 "Discovering sequence motifs with arbitrary insertions and deletions", | |
| 7612 <i>PLoS Computational Biology</i>, <b>4</b>(5):e1000071, 2008. | |
| 7613 <a href="https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1000071">[full text]</a> | |
| 7614 </span> | |
| 7615 `); | |
| 7616 case 'GOMo': | |
| 7617 return(extra + ` | |
| 7618 <span class="citation"> | |
| 7619 Fabian A. Buske, Mikael Bodén, Denis C. Bauer and Timothy L. Bailey, | |
| 7620 "Assigning roles to DNA regulatory motifs using comparative genomics", | |
| 7621 <i>Bioinformatics</i>, <b>26</b>(7), 860-866, 2010. | |
| 7622 <a href="http://bioinformatics.oxfordjournals.org/cgi/content/full/26/7/860">[full text]</a> | |
| 7623 </span> | |
| 7624 `); | |
| 7625 case 'MAST': | |
| 7626 return(extra + ` | |
| 7627 <span class="citation"> | |
| 7628 Timothy L. Bailey and Michael Gribskov, | |
| 7629 "Combining evidence using p-values: application to sequence homology searches", | |
| 7630 <i>Bioinformatics</i>, <b>14</b>(1):48-54, 1998. | |
| 7631 <a href="http://bioinformatics.oxfordjournals.org/content/14/1/48">[full text]</a> | |
| 7632 </span> | |
| 7633 `); | |
| 7634 case 'MCAST': | |
| 7635 return(extra + ` | |
| 7636 <span class="citation"> | |
| 7637 Timothy Bailey and William Stafford Noble, | |
| 7638 "Searching for statistically significant regulatory modules", | |
| 7639 <i>Bioinformatics (Proceedings of the European Conference on Computational Biology)</i>, | |
| 7640 <b>19</b>(Suppl. 2):ii16-ii25, 2003. | |
| 7641 <a href="http://bioinformatics.oxfordjournals.org/cgi/content/abstract/19/suppl_2/ii16">[full text]</a> | |
| 7642 </span> | |
| 7643 `); | |
| 7644 case 'Meta-MEME': | |
| 7645 return(extra + ` | |
| 7646 <span class="citation"> | |
| 7647 William N. Grundy, Timothy L. Bailey, Charles P. Elkan and Michael E. Baker. | |
| 7648 "Meta-MEME: Motif-based Hidden Markov Models of Protein Families" | |
| 7649 <i>Computer Applications in the Biological Sciences (CABIOS)</i>, | |
| 7650 <b>13</b>(4):397-406, 1997. | |
| 7651 <a href="http://bioinformatics.oxfordjournals.org/content/13/4/397">[full text]</a> | |
| 7652 </span> | |
| 7653 `); | |
| 7654 case 'MEME': | |
| 7655 return(extra + ` | |
| 7656 <span class="citation"> | |
| 7657 Timothy L. Bailey and Charles Elkan, | |
| 7658 "Fitting a mixture model by expectation maximization to | |
| 7659 discover motifs in biopolymers", | |
| 7660 <em>Proceedings of the Second International Conference on Intelligent Systems | |
| 7661 for Molecular Biology</em>, pp. 28-36, AAAI Press, Menlo Park, California, 1994. | |
| 7662 <a href="http://www.aaai.org/Papers/ISMB/1994/ISMB94-004.pdf">[full text]</a> | |
| 7663 </span> | |
| 7664 `); | |
| 7665 case 'MEME-ChIP': | |
| 7666 return(extra + ` | |
| 7667 <span class="citation"> | |
| 7668 Philip Machanick and Timothy L. Bailey, | |
| 7669 "MEME-ChIP: motif analysis of large DNA datasets", | |
| 7670 <i>Bioinformatics</i> <b>27</b>(12):1696-1697, 2011. | |
| 7671 <a href="http://bioinformatics.oxfordjournals.org/content/27/12/1696.full">[full text]</a> | |
| 7672 </span> | |
| 7673 `); | |
| 7674 case 'MEME_SUITE': | |
| 7675 return(extra + ` | |
| 7676 <span class="citation"> | |
| 7677 Timothy L. Bailey, James Johnson, Charles E. Grant, William S. Noble, | |
| 7678 "The MEME Suite", | |
| 7679 <i>Nucleic Acids Research</i>, <b>43</b>(W1):W39-W49, 2015. | |
| 7680 <a href="https://academic.oup.com/nar/article/43/W1/W39/2467905">[full text]</a> | |
| 7681 </span> | |
| 7682 `); | |
| 7683 case 'MoMo': | |
| 7684 return(extra + ` | |
| 7685 <span class="citation"> | |
| 7686 Alice Cheng, Charles Grant, Timothy L. Bailey and William Noble, | |
| 7687 "MoMo: Discovery of statistically significant post-translational modification motifs", | |
| 7688 <i>Bioinformatics</i>, <b>35</b>(16):2774-2782, 2018. | |
| 7689 <a href="https://doi.org/10.1093/bioinformatics/bty1058">[full text]</a> | |
| 7690 </span> | |
| 7691 `); | |
| 7692 case 'PSPs': | |
| 7693 return(extra + ` | |
| 7694 <span class="citation"> | |
| 7695 Timothy L. Bailey, Mikael Bodén, Tom Whitington and Philip Machanick, | |
| 7696 "The value of position-specific priors in motif discovery using MEME", | |
| 7697 <i>BMC Bioinformatics</i>, <b>11</b>(1):179, 2010. | |
| 7698 <a href="http://www.biomedcentral.com/1471-2105/11/179">[full text]</a> | |
| 7699 </span> | |
| 7700 `); | |
| 7701 case 'SEA': | |
| 7702 return(extra + ` | |
| 7703 <span class="citation"> | |
| 7704 Timothy L. Bailey and Charles E. Grant, "SEA: Simple Enrichment Analysis of motifs", | |
| 7705 <i>BioRxiv</i>, August 24, 2021. | |
| 7706 <a href="https://www.biorxiv.org/content/10.1101/2021.08.23.457422v1">[full text]</a> | |
| 7707 </span> | |
| 7708 `); | |
| 7709 case 'SpaMo': | |
| 7710 return(extra + ` | |
| 7711 <span class="citation"> | |
| 7712 Tom Whitington, Martin C. Frith, James Johnson and Timothy L. Bailey | |
| 7713 "Inferring transcription factor complexes from ChIP-seq data", | |
| 7714 <i>Nucleic Acids Res.</i> <b>39</b>(15):e98, 2011. | |
| 7715 <a href="http://nar.oxfordjournals.org/content/39/15/e98">[full text]</a> | |
| 7716 </span> | |
| 7717 `); | |
| 7718 case 'STREME': | |
| 7719 return(extra + ` | |
| 7720 <span class="citation"> | |
| 7721 Timothy L. Bailey, | |
| 7722 "STREME: accurate and versatile sequence motif discovery", | |
| 7723 <i>Bioinformatics</i>, Mar. 24, 2021. | |
| 7724 <a href="https://academic.oup.com/bioinformatics/advance-article-abstract/doi/10.1093/bioinformatics/btab203/6184861" >[full text]</a> | |
| 7725 </span> | |
| 7726 `); | |
| 7727 case 'Tomtom': | |
| 7728 return(extra + ` | |
| 7729 <span class="citation"> | |
| 7730 Shobhit Gupta, JA Stamatoyannopolous, Timothy Bailey and William Stafford Noble, | |
| 7731 "Quantifying similarity between motifs", | |
| 7732 <i>Genome Biology</i>, <b>8</b>(2):R24, 2007. | |
| 7733 <a href="http://genomebiology.com/2007/8/2/R24">[full text]</a> | |
| 7734 </span> | |
| 7735 `); | |
| 7736 case 'T-Gene': | |
| 7737 return(extra + ` | |
| 7738 <span class="citation"> | |
| 7739 Timothy O'Connor, Charles E. Grant, Mikael Bodén, Timothy L. Bailey, | |
| 7740 "T-Gene: Improved target gene prediction", | |
| 7741 <i>Bioinformatics</i>, <b>36</b>(12):3902-3904, 2020. | |
| 7742 <a href="https://academic.oup.com/bioinformatics/article/36/12/3902/5815978?guestAccessKey=aa625a49-a2aa-4d7a-858e-8bc82867a534">[Full Text]</a> | |
| 7743 </span> | |
| 7744 `); | |
| 7745 case 'XSTREME': | |
| 7746 return(extra + ` | |
| 7747 <span class="citation"> | |
| 7748 Charles E. Grant and Timothy L. Bailey, "XSTREME: comprehensive motif analysis of biological sequence datasets", | |
| 7749 <i>BioRxiv</i>, September 3, 2021. | |
| 7750 <a href="https://www.biorxiv.org/content/10.1101/2021.09.02.458722v1">[full text]</a> | |
| 7751 </span> | |
| 7752 `); | |
| 7753 default: | |
| 7754 return("Unknown program: " + doc_type); | |
| 7755 } | |
| 7756 } // get_citation_text | |
| 7757 | |
| 7758 // | |
| 7759 // Function to replace the innerHTML of element "id" with an HTML paragraph | |
| 7760 // containing the text for 'program', which is known to function get_citation_text. | |
| 7761 // If "id" is either "citation" or "reference" some extra text is printed. | |
| 7762 // | |
| 7763 function print_citation(id, program) { | |
| 7764 var extra; | |
| 7765 switch (id) { | |
| 7766 case 'citation': | |
| 7767 extra = "If you use " + program + " in your research, please cite the following paper:<br>"; | |
| 7768 break; | |
| 7769 case 'reference': | |
| 7770 extra = "<h5>Reference</h5>"; | |
| 7771 break; | |
| 7772 default: | |
| 7773 extra = ""; | |
| 7774 break; | |
| 7775 }; | |
| 7776 var html = get_citation_text(program, extra); | |
| 7777 document.getElementById(id).insertAdjacentHTML('beforeend', html); | |
| 7778 } // print_citation | |
| 7779 | |
| 7780 // | |
| 7781 // Function to convert a citation for a program to a C #define statement. | |
| 7782 // | |
| 7783 function print_citation_define(lang, pgm) { | |
| 7784 var citation = get_citation_text(pgm, ''); | |
| 7785 citation = citation.replace(/<[^>]*>/g, ''); | |
| 7786 citation = citation.replace(/\[.*\]/g, ''); | |
| 7787 citation = citation.replace(/\n\s*/g, '\\n'); | |
| 7788 citation = citation.replace(/"/g, '\\"'); | |
| 7789 citation = citation.replace(/é/g, 'e'); | |
| 7790 citation = citation.replace(/^\\n/, ''); | |
| 7791 pgm = pgm.replace(/-/, ''); | |
| 7792 citation = "If you use this program in your research, please cite:\\n\\n" + citation; | |
| 7793 if (lang == "C") { | |
| 7794 citation = "#define " + pgm + '_CITE "' + citation + '"'; | |
| 7795 } else if (lang == "perl") { | |
| 7796 citation = '"' + pgm + '" => "' + citation + '",'; | |
| 7797 } | |
| 7798 return(citation); | |
| 7799 } // print_citation_define | |
| 7800 | |
| 7801 // | |
| 7802 // Main program (for use with nodejs "node" javascript engine) | |
| 7803 // to create citation.js.h and citation.pm from citation.js. | |
| 7804 // The command line: | |
| 7805 // node citation.js C > citation.js.h | |
| 7806 // will output the C #define statements for each of the | |
| 7807 // programs listed below, defining macros <program>_CITE. | |
| 7808 // The command line: | |
| 7809 // node citation.js perl > citation.js.pm | |
| 7810 // will output perl hash <program> => text | |
| 7811 // | |
| 7812 // | |
| 7813 if (typeof process !== 'undefined') { | |
| 7814 var lang = process.argv[2]; | |
| 7815 var programs = ['AMA', 'AME', 'CentriMo', 'DREME', 'FIMO', 'GLAM2', | |
| 7816 'GLAM2SCAN', 'GOMo', 'MAST', 'MCAST', 'Meta-MEME', 'MEME', | |
| 7817 'MEME-ChIP', 'MEME_SUITE', 'MoMo', 'PSPs', 'SEA', 'SpaMo', | |
| 7818 'STREME', 'Tomtom', 'T-Gene', 'XSTREME']; | |
| 7819 | |
| 7820 if (lang == "C") { | |
| 7821 console.log("// Do not edit this file. It is created from etc/citation.js."); | |
| 7822 console.log("#ifndef citation_js_h\n#define citation_js_h\n"); | |
| 7823 for (var i=0; i<programs.length; i++) { | |
| 7824 console.log(print_citation_define(lang, programs[i])); | |
| 7825 } | |
| 7826 console.log("\n#endif"); | |
| 7827 } else if (lang == "perl") { | |
| 7828 console.log("# Do not edit this file. It is created from etc/citation.js."); | |
| 7829 console.log("package Citation;"); | |
| 7830 console.log("sub cite {\n my ($pgm) = @_;\n return $citation{$pgm};\n}"); | |
| 7831 console.log("%citation = ("); | |
| 7832 for (var i=0; i<programs.length; i++) { | |
| 7833 console.log(print_citation_define(lang, programs[i])); | |
| 7834 } | |
| 7835 console.log(");"); | |
| 7836 } | |
| 7837 } | |
| 7838 </script> | |
| 7839 <style> | |
| 7840 /* The following is the content of meme.css */ | |
| 7841 body { background-color:white; font-size: 12px; font-family: Verdana, Arial, Helvetica, sans-serif;} | |
| 7842 | |
| 7843 div.help { | |
| 7844 display: inline-block; | |
| 7845 margin: 0px; | |
| 7846 padding: 0px; | |
| 7847 width: 12px; | |
| 7848 height: 13px; | |
| 7849 cursor: pointer; | |
| 7850 background-image: url(data:image/gif;base64,R0lGODlhDAANAIABANR0AP///yH5BAEAAAEALAAAAAAMAA0AAAIdhI8Xy22MIFgv1DttrrJ7mlGNNo4c+aFg6SQuUAAAOw==); | |
| 7851 } | |
| 7852 | |
| 7853 div.help:hover { | |
| 7854 background-image: url(data:image/gif;base64,R0lGODlhDAANAKEAANR0AP///9R0ANR0ACH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEAAAIALAAAAAAMAA0AAAIdDGynCe3PgoxONntvwqz2/z2K2ImjR0KhmSIZUgAAOw==); | |
| 7855 } | |
| 7856 | |
| 7857 p.spaced { line-height: 1.8em;} | |
| 7858 | |
| 7859 span.citation { font-family: "Book Antiqua", "Palatino Linotype", serif; color: #004a4d;} | |
| 7860 | |
| 7861 p.pad { padding-left: 30px; padding-top: 5px; padding-bottom: 10px;} | |
| 7862 | |
| 7863 td.jump { font-size: 13px; color: #ffffff; background-color: #00666a; | |
| 7864 font-family: Georgia, "Times New Roman", Times, serif;} | |
| 7865 | |
| 7866 a.jump { margin: 15px 0 0; font-style: normal; font-variant: small-caps; | |
| 7867 font-weight: bolder; font-family: Georgia, "Times New Roman", Times, serif;} | |
| 7868 | |
| 7869 h2.mainh {font-size: 1.5em; font-style: normal; margin: 15px 0 0; | |
| 7870 font-variant: small-caps; font-family: Georgia, "Times New Roman", Times, serif;} | |
| 7871 | |
| 7872 h2.line {border-bottom: 1px solid #CCCCCC; font-size: 1.5em; font-style: normal; | |
| 7873 margin: 15px 0 0; padding-bottom: 3px; font-variant: small-caps; | |
| 7874 font-family: Georgia, "Times New Roman", Times, serif;} | |
| 7875 | |
| 7876 h4 {border-bottom: 1px solid #CCCCCC; font-size: 1.2em; font-style: normal; | |
| 7877 margin: 10px 0 0; padding-bottom: 3px; font-family: Georgia, "Times New Roman", Times, serif;} | |
| 7878 | |
| 7879 h5 {margin: 0px} | |
| 7880 | |
| 7881 a.help { font-size: 9px; font-style: normal; text-transform: uppercase; | |
| 7882 font-family: Georgia, "Times New Roman", Times, serif;} | |
| 7883 | |
| 7884 div.pad { padding-left: 30px; padding-top: 5px; padding-bottom: 10px;} | |
| 7885 | |
| 7886 div.pad1 { margin: 10px 5px;} | |
| 7887 | |
| 7888 div.pad2 { margin: 25px 5px 5px;} | |
| 7889 h2.pad2 { padding: 25px 5px 5px;} | |
| 7890 | |
| 7891 div.pad3 { padding: 5px 0px 10px 30px;} | |
| 7892 | |
| 7893 div.box { border: 2px solid #CCCCCC; padding:10px; overflow: hidden;} | |
| 7894 | |
| 7895 div.bar { border-left: 7px solid #00666a; padding:5px; margin-top:25px; } | |
| 7896 | |
| 7897 div.subsection {margin:25px 0px;} | |
| 7898 | |
| 7899 img {border:0px none;} | |
| 7900 | |
| 7901 th.majorth {text-align:left;} | |
| 7902 th.minorth {font-weight:normal; text-align:left; width:8em; padding: 3px 0px;} | |
| 7903 th.actionth {font-weight:normal; text-align:left;} | |
| 7904 | |
| 7905 .explain h5 {font-size:1em; margin-left: 1em;} | |
| 7906 | |
| 7907 div.doc {margin-left: 2em; margin-bottom: 3em;} | |
| 7908 | |
| 7909 th.trainingset { | |
| 7910 border-bottom: thin dashed black; | |
| 7911 font-weight:normal; | |
| 7912 padding:0px 10px; | |
| 7913 } | |
| 7914 div.pop_content { | |
| 7915 position:absolute; | |
| 7916 z-index:50; | |
| 7917 width:300px; | |
| 7918 padding: 5px; | |
| 7919 background: #E4ECEC; | |
| 7920 font-size: 12px; | |
| 7921 font-family: Arial; | |
| 7922 border-style: double; | |
| 7923 border-width: 3px; | |
| 7924 border-color: #AA2244; | |
| 7925 display:none; | |
| 7926 } | |
| 7927 div.pop_content_wide { | |
| 7928 position:absolute; | |
| 7929 z-index:1; | |
| 7930 width:700px; | |
| 7931 padding: 5px; | |
| 7932 background: #E4ECEC; | |
| 7933 font-size: 12px; | |
| 7934 font-family: Arial; | |
| 7935 border-style: double; | |
| 7936 border-width: 3px; | |
| 7937 border-color: #AA2244; | |
| 7938 display:none; | |
| 7939 } | |
| 7940 | |
| 7941 div.pop_content > *:first-child { | |
| 7942 margin-top: 0px; | |
| 7943 } | |
| 7944 | |
| 7945 div.pop_content h1, div.pop_content h2, div.pop_content h3, div.pop_content h4, | |
| 7946 div.pop_content h5, div.pop_content h6, div.pop_content p { | |
| 7947 margin: 0px; | |
| 7948 } | |
| 7949 | |
| 7950 div.pop_content p + h1, div.pop_content p + h2, div.pop_content p + h3, | |
| 7951 div.pop_content p + h4, div.pop_content p + h5, div.pop_content p + h6 { | |
| 7952 margin-top: 5px; | |
| 7953 } | |
| 7954 | |
| 7955 div.pop_content p + p { | |
| 7956 margin-top: 5px; | |
| 7957 } | |
| 7958 | |
| 7959 div.pop_content > *:last-child { | |
| 7960 margin-bottom: 0px; | |
| 7961 } | |
| 7962 | |
| 7963 div.pop_content div.pop_close { | |
| 7964 /* old definition */ | |
| 7965 float:right; | |
| 7966 bottom: 0; | |
| 7967 } | |
| 7968 | |
| 7969 div.pop_content span.pop_close, div.pop_content span.pop_back { | |
| 7970 display: inline-block; | |
| 7971 border: 2px outset #661429; | |
| 7972 background-color: #CCC; | |
| 7973 padding-left: 1px; | |
| 7974 padding-right: 1px; | |
| 7975 padding-top: 0px; | |
| 7976 padding-bottom: 0px; | |
| 7977 cursor: pointer; | |
| 7978 color: #AA2244; /*#661429;*/ | |
| 7979 font-weight: bold; | |
| 7980 } | |
| 7981 | |
| 7982 div.pop_content span.pop_close:active, div.pop_content span.pop_back:active { | |
| 7983 border-style: inset; | |
| 7984 } | |
| 7985 | |
| 7986 div.pop_content span.pop_close { | |
| 7987 float:right; | |
| 7988 /*border: 2px outset #AA002B;*/ | |
| 7989 /*color: #AA2244;*/ | |
| 7990 } | |
| 7991 | |
| 7992 div.pop_content:not(.nested) .nested_only { | |
| 7993 display: none; | |
| 7994 } | |
| 7995 | |
| 7996 div.pop_back_sec { | |
| 7997 margin-bottom: 5px; | |
| 7998 } | |
| 7999 | |
| 8000 div.pop_close_sec { | |
| 8001 margin-top: 5px; | |
| 8002 } | |
| 8003 | |
| 8004 table.hide_advanced tr.advanced { | |
| 8005 display: none; | |
| 8006 } | |
| 8007 span.show_more { | |
| 8008 display: none; | |
| 8009 } | |
| 8010 table.hide_advanced span.show_more { | |
| 8011 display: inline; | |
| 8012 } | |
| 8013 table.hide_advanced span.show_less { | |
| 8014 display: none; | |
| 8015 } | |
| 8016 | |
| 8017 | |
| 8018 /***************************************************************************** | |
| 8019 * Program logo styling | |
| 8020 ****************************************************************************/ | |
| 8021 div.prog_logo { | |
| 8022 border-bottom: 0.25em solid #0f5f60; | |
| 8023 height: 4.5em; | |
| 8024 width: 25em; | |
| 8025 display:inline-block; | |
| 8026 } | |
| 8027 div.prog_logo img { | |
| 8028 float:left; | |
| 8029 width: 4em; | |
| 8030 border-style: none; | |
| 8031 margin-right: 0.2em; | |
| 8032 } | |
| 8033 div.prog_logo h1, div.prog_logo h1:hover, div.prog_logo h1:active, div.prog_logo h1:visited { | |
| 8034 margin:0; | |
| 8035 padding:0; | |
| 8036 font-family: Arial, Helvetica, sans-serif; | |
| 8037 font-size: 3.2em; | |
| 8038 line-height: 1em; | |
| 8039 vertical-align: top; | |
| 8040 display: block; | |
| 8041 color: #026666; | |
| 8042 letter-spacing: -0.06em; | |
| 8043 text-shadow: 0.04em 0.06em 0.05em #666; | |
| 8044 } | |
| 8045 div.prog_logo h2, div.prog_logo h2:hover, div.prog_logo h2:active, div.prog_logo h2:visited { | |
| 8046 display: block; | |
| 8047 margin:0; | |
| 8048 padding:0; | |
| 8049 font-family: Helvetica, sans-serif; | |
| 8050 font-size: 0.9em; | |
| 8051 line-height: 1em; | |
| 8052 letter-spacing: -0.06em; | |
| 8053 color: black; | |
| 8054 } | |
| 8055 div.prog_logo h3, div.prog_logo h3:hover, div.prog_logo h3:active, div.prog_logo h3:visited { | |
| 8056 display: block; | |
| 8057 margin:0; | |
| 8058 padding:0; | |
| 8059 font-family: Helvetica, sans-serif; | |
| 8060 font-size: 0.9em; | |
| 8061 line-height: 1.5em; | |
| 8062 letter-spacing: -0.06em; | |
| 8063 color: black; | |
| 8064 } | |
| 8065 | |
| 8066 div.big.prog_logo { | |
| 8067 font-size: 18px; | |
| 8068 } | |
| 8069 | |
| 8070 /* These are for centered columns in tables */ | |
| 8071 td.ctr { | |
| 8072 text-align: center; | |
| 8073 } | |
| 8074 | |
| 8075 /* These are for the navigation bars at the top of outputs. */ | |
| 8076 table.navigation { | |
| 8077 margin-top: 0px; | |
| 8078 border-collapse:collapse; | |
| 8079 } | |
| 8080 table.navigation * td | |
| 8081 { | |
| 8082 padding-left: 0px; | |
| 8083 padding-right: 10px; | |
| 8084 padding-top: 0px; | |
| 8085 padding-bottom: 0px; | |
| 8086 } | |
| 8087 </style> | |
| 8088 <style> | |
| 8089 .block_td { | |
| 8090 height:25px; | |
| 8091 } | |
| 8092 .block_container { | |
| 8093 position:relative; | |
| 8094 box-sizing: border-box; | |
| 8095 height: 25px; | |
| 8096 padding: 0px; | |
| 8097 margin: 0px; | |
| 8098 margin-left: 1em; | |
| 8099 } | |
| 8100 .block_label { | |
| 8101 position: absolute; | |
| 8102 display: inline-block; | |
| 8103 padding: 3px; | |
| 8104 z-index: 4; | |
| 8105 top: 6px; | |
| 8106 height: 12px; | |
| 8107 line-height: 12px; | |
| 8108 font-size: 12px; | |
| 8109 background-color: white; | |
| 8110 border: 1px solid black; | |
| 8111 -moz-border-radius: 12px; | |
| 8112 -webkit-border-radius: 12px; | |
| 8113 border-radius: 12px; | |
| 8114 transform: translateX(-50%); | |
| 8115 } | |
| 8116 .block_motif { | |
| 8117 position: absolute; | |
| 8118 z-index: 3; | |
| 8119 top: 0px; | |
| 8120 box-sizing: border-box; | |
| 8121 border: 1px solid black; | |
| 8122 height: 12px; | |
| 8123 background-color: cyan; | |
| 8124 } | |
| 8125 .block_motif.top { | |
| 8126 border-bottom-width: 0; | |
| 8127 } | |
| 8128 .block_motif.bottom { | |
| 8129 border-top-width: 0; | |
| 8130 } | |
| 8131 .block_motif.scanned_site { | |
| 8132 opacity: 0.3; | |
| 8133 } | |
| 8134 .block_motif.scanned_site.active { | |
| 8135 opacity: 0.9; | |
| 8136 } | |
| 8137 .block_region { | |
| 8138 position:absolute; | |
| 8139 z-index:6; | |
| 8140 height:25px; | |
| 8141 top:0px; | |
| 8142 } | |
| 8143 .block_region.main { | |
| 8144 z-index:8; | |
| 8145 } | |
| 8146 .block_region.scanned_site { | |
| 8147 z-index:5; | |
| 8148 } | |
| 8149 .block_region.scanned_site.main { | |
| 8150 z-index:7; | |
| 8151 } | |
| 8152 .block_region.top { | |
| 8153 height:13px; | |
| 8154 } | |
| 8155 .block_region.bottom { | |
| 8156 height:13px; | |
| 8157 top:12px; | |
| 8158 } | |
| 8159 .block_rule { | |
| 8160 position:absolute; | |
| 8161 z-index:2; | |
| 8162 width:100%; | |
| 8163 height:1px; | |
| 8164 top:12px; | |
| 8165 left:0px; | |
| 8166 background-color:gray; | |
| 8167 } | |
| 8168 .block_plus_sym { | |
| 8169 position:absolute; | |
| 8170 z-index:4; | |
| 8171 line-height:12px; | |
| 8172 top:0px; | |
| 8173 left:-1em; | |
| 8174 } | |
| 8175 .block_minus_sym { | |
| 8176 position:absolute; | |
| 8177 z-index:4; | |
| 8178 line-height:12px; | |
| 8179 top:13px; | |
| 8180 left:-1em; | |
| 8181 } | |
| 8182 | |
| 8183 .tic_major { | |
| 8184 position:absolute; | |
| 8185 top:0em; | |
| 8186 height:0.5em; | |
| 8187 width: 2px; | |
| 8188 margin-left: -1px; | |
| 8189 background-color: blue; | |
| 8190 } | |
| 8191 .tic_minor { | |
| 8192 position:absolute; | |
| 8193 top:0em; | |
| 8194 height:0.2em; | |
| 8195 width: 1px; | |
| 8196 margin-left: -0.5px; | |
| 8197 background-color: blue; | |
| 8198 } | |
| 8199 .tic_label { | |
| 8200 position:absolute; | |
| 8201 display: inline-block; | |
| 8202 top:0.5em; | |
| 8203 height: 1em; | |
| 8204 color: blue; | |
| 8205 transform: translateX(-50%); | |
| 8206 } | |
| 8207 | |
| 8208 .block_needle { | |
| 8209 position:absolute; | |
| 8210 z-index:4; | |
| 8211 height:30px; | |
| 8212 width:1px; | |
| 8213 top:-2px; | |
| 8214 background-color:gray; | |
| 8215 } | |
| 8216 .block_needle.right { | |
| 8217 height: 60px; | |
| 8218 } | |
| 8219 .block_handle { | |
| 8220 position: absolute; | |
| 8221 display: inline-block; | |
| 8222 z-index: 5; | |
| 8223 top: 27px; | |
| 8224 min-width: 3ex; | |
| 8225 text-align: center; | |
| 8226 font-size: 12px; | |
| 8227 line-height: 12px; | |
| 8228 transform: translateX(-50%); | |
| 8229 background-color: LightGrey; | |
| 8230 border:3px outset grey; | |
| 8231 cursor: pointer; | |
| 8232 -webkit-user-select: none; /* Chrome/Safari */ | |
| 8233 -moz-user-select: none; /* Firefox */ | |
| 8234 -ms-user-select: none; /* IE10+ */ | |
| 8235 /* Rules below not implemented in browsers yet */ | |
| 8236 -o-user-select: none; | |
| 8237 user-select: none; | |
| 8238 } | |
| 8239 .block_handle.right { | |
| 8240 top: 47px; | |
| 8241 } | |
| 8242 | |
| 8243 .legend_container { | |
| 8244 text-align: right; | |
| 8245 } | |
| 8246 .legend_entry { | |
| 8247 display: inline-block; | |
| 8248 padding: 5px; | |
| 8249 } | |
| 8250 div.legend_swatch { | |
| 8251 box-sizing: border-box; | |
| 8252 width: 15px; | |
| 8253 height: 15px; | |
| 8254 border: 1px solid black; | |
| 8255 background-color: cyan; | |
| 8256 float: left; | |
| 8257 } | |
| 8258 div.legend_swatch input { | |
| 8259 display: none; | |
| 8260 } | |
| 8261 .legend_text { | |
| 8262 line-height: 15px; | |
| 8263 margin-left: 20px; | |
| 8264 } | |
| 8265 </style> | |
| 8266 <style> | |
| 8267 /* meme output specific css */ | |
| 8268 | |
| 8269 div.pop_block { | |
| 8270 position:absolute; | |
| 8271 z-index:5; | |
| 8272 padding: 5px; | |
| 8273 border: 1px solid black; | |
| 8274 display: inline-block; | |
| 8275 background-color: white; | |
| 8276 } | |
| 8277 | |
| 8278 #measure_match { | |
| 8279 position: absolute; | |
| 8280 visibility: hidden; | |
| 8281 height: auto; | |
| 8282 width: auto; | |
| 8283 white-space: nowrap; | |
| 8284 } | |
| 8285 | |
| 8286 div.template { | |
| 8287 position: absolute; | |
| 8288 z-index: 1; | |
| 8289 left: 0; | |
| 8290 top: 0; | |
| 8291 visibility: hidden; | |
| 8292 } | |
| 8293 | |
| 8294 table.block_information { | |
| 8295 margin-left: auto; | |
| 8296 margin-right: auto; | |
| 8297 } | |
| 8298 | |
| 8299 table.block_information * th { | |
| 8300 text-align: right; | |
| 8301 } | |
| 8302 | |
| 8303 *.hide_empty_seqs * tr.empty_seq { | |
| 8304 display: none; | |
| 8305 } | |
| 8306 | |
| 8307 *.hide_only_scan * tr.only_scan { | |
| 8308 display: none; | |
| 8309 } | |
| 8310 | |
| 8311 *.hide_only_scan * div.scanned_site { | |
| 8312 display: none; | |
| 8313 } | |
| 8314 | |
| 8315 td.symaction { | |
| 8316 text-align: center; | |
| 8317 text-decoration: underline; | |
| 8318 font-size: 20px; | |
| 8319 cursor: pointer; | |
| 8320 } | |
| 8321 div.sym_btn { | |
| 8322 display:inline-block; | |
| 8323 text-decoration: underline; | |
| 8324 cursor: pointer; | |
| 8325 font-size: 20px; | |
| 8326 line-height:20px; | |
| 8327 text-align: center; | |
| 8328 width: 20px; | |
| 8329 height: 20px; | |
| 8330 color: blue; | |
| 8331 } | |
| 8332 div.sym_btn:hover { | |
| 8333 color: white; | |
| 8334 background-color: blue; | |
| 8335 } | |
| 8336 | |
| 8337 div.sym_btn.positioned { | |
| 8338 position: absolute; | |
| 8339 top: 0px; | |
| 8340 } | |
| 8341 | |
| 8342 div.actionbutton { | |
| 8343 display:inline-block; | |
| 8344 cursor: pointer; | |
| 8345 font-size: 18px; | |
| 8346 line-height:20px; | |
| 8347 padding: 5px; | |
| 8348 margin: 10px 0; | |
| 8349 border: 1px solid black; | |
| 8350 } | |
| 8351 | |
| 8352 div.actionbutton:hover { | |
| 8353 color:#FFF; | |
| 8354 background-color:#000; | |
| 8355 } | |
| 8356 | |
| 8357 div.param_box { | |
| 8358 display: inline-block; | |
| 8359 margin-right: 20px; | |
| 8360 } | |
| 8361 | |
| 8362 span.param { | |
| 8363 font-weight: bold; | |
| 8364 } | |
| 8365 | |
| 8366 div.box + div.box { | |
| 8367 margin-top: 5px; | |
| 8368 } | |
| 8369 | |
| 8370 div.sites_outer { | |
| 8371 position: relative; | |
| 8372 padding-top: 20px; /* height of header */ | |
| 8373 display: inline-block; | |
| 8374 } | |
| 8375 | |
| 8376 div.sites_inner { | |
| 8377 overflow-x: hidden; | |
| 8378 overflow-y: auto; | |
| 8379 max-height: 200px; | |
| 8380 } | |
| 8381 table.sites_tbl { | |
| 8382 border-collapse: collapse; | |
| 8383 } | |
| 8384 | |
| 8385 div.sites_th_inner { | |
| 8386 position: absolute; | |
| 8387 top: 0; | |
| 8388 line-height: 20px; /* height of header */ | |
| 8389 text-align: left; | |
| 8390 padding-left: 5px; | |
| 8391 } | |
| 8392 th.nopad div.sites_th_inner { | |
| 8393 padding-left: 0; | |
| 8394 } | |
| 8395 div.sites_th_hidden { | |
| 8396 visibility: hidden; | |
| 8397 height: 0; | |
| 8398 padding: 0 10px; | |
| 8399 } | |
| 8400 th.nopad div.sites_th_hidden { | |
| 8401 padding: 0; | |
| 8402 } | |
| 8403 div.sites_inner * th { | |
| 8404 height: 0; | |
| 8405 } | |
| 8406 | |
| 8407 table.sites_tbl { | |
| 8408 overflow-x: hidden; | |
| 8409 overflow-y: auto; | |
| 8410 } | |
| 8411 | |
| 8412 .site_num { | |
| 8413 text-align: right; | |
| 8414 } | |
| 8415 .site_name { | |
| 8416 padding:0px 5px; | |
| 8417 text-align:left; | |
| 8418 } | |
| 8419 .site_strand { | |
| 8420 padding:0px 5px; | |
| 8421 text-align:center; | |
| 8422 } | |
| 8423 .norc .site_strand, .norc .site_strand_title { | |
| 8424 display: none; | |
| 8425 } | |
| 8426 .site_start { | |
| 8427 padding:0px 15px; | |
| 8428 text-align: right; | |
| 8429 } | |
| 8430 .site_pvalue { | |
| 8431 text-align:center; | |
| 8432 padding:0px 15px; | |
| 8433 text-align:right; | |
| 8434 white-space: nowrap; | |
| 8435 } | |
| 8436 .lflank, .rflank, .match, .alpha_symbol { | |
| 8437 font-weight:bold; | |
| 8438 font-size:15px; | |
| 8439 font-family: 'Courier New', Courier, monospace; | |
| 8440 color:gray; | |
| 8441 } | |
| 8442 | |
| 8443 .site.lflank { | |
| 8444 text-align:right; | |
| 8445 padding-right:5px; | |
| 8446 color:gray; | |
| 8447 } | |
| 8448 .site.match { | |
| 8449 text-align:center; | |
| 8450 } | |
| 8451 .site.rflank { | |
| 8452 text-align:left; | |
| 8453 padding-left:5px; | |
| 8454 padding-right: 20px; | |
| 8455 } | |
| 8456 | |
| 8457 th.stop_reason { | |
| 8458 text-align: left; | |
| 8459 padding-right: 10px; | |
| 8460 } | |
| 8461 | |
| 8462 th.motif_ordinal { | |
| 8463 | |
| 8464 } | |
| 8465 td.motif_ordinal { | |
| 8466 text-align: right; | |
| 8467 padding-right: 10px; | |
| 8468 } | |
| 8469 th.motif_logo { | |
| 8470 padding-right: 10px; | |
| 8471 } | |
| 8472 td.motif_logo { | |
| 8473 padding-right: 10px; | |
| 8474 } | |
| 8475 th.motif_evalue { | |
| 8476 text-align:right; | |
| 8477 padding-right: 10px; | |
| 8478 } | |
| 8479 td.motif_evalue { | |
| 8480 text-align: right; | |
| 8481 white-space: nowrap; | |
| 8482 padding-right: 20px; | |
| 8483 } | |
| 8484 th.motif_nsites { | |
| 8485 text-align: right; | |
| 8486 padding-right: 10px; | |
| 8487 } | |
| 8488 td.motif_nsites { | |
| 8489 text-align: right; | |
| 8490 padding-right: 20px; | |
| 8491 } | |
| 8492 th.motif_width { | |
| 8493 text-align: right; | |
| 8494 padding-right: 5px; | |
| 8495 } | |
| 8496 td.motif_width { | |
| 8497 text-align: right; | |
| 8498 padding-right: 15px; | |
| 8499 } | |
| 8500 th.motif_more { | |
| 8501 padding: 0 5px; | |
| 8502 } | |
| 8503 td.motif_more { | |
| 8504 text-align: center; | |
| 8505 padding: 0 5px; | |
| 8506 } | |
| 8507 th.motif_submit { | |
| 8508 padding: 0 5px; | |
| 8509 } | |
| 8510 td.motif_submit { | |
| 8511 text-align: center; | |
| 8512 padding: 0 5px; | |
| 8513 } | |
| 8514 th.motif_download { | |
| 8515 padding-left: 5px; | |
| 8516 } | |
| 8517 td.motif_download { | |
| 8518 text-align: center; | |
| 8519 padding-left: 5px; | |
| 8520 } | |
| 8521 | |
| 8522 | |
| 8523 div.tabArea { | |
| 8524 font-size: 80%; | |
| 8525 font-weight: bold; | |
| 8526 } | |
| 8527 | |
| 8528 .norc div.tabArea { | |
| 8529 display: none; | |
| 8530 } | |
| 8531 | |
| 8532 span.tab, span.tab:visited { | |
| 8533 cursor: pointer; | |
| 8534 color: #888; | |
| 8535 background-color: #ddd; | |
| 8536 border: 2px solid #ccc; | |
| 8537 padding: 2px 1em; | |
| 8538 text-decoration: none; | |
| 8539 } | |
| 8540 span.tab.middle { | |
| 8541 border-left-width: 0px; | |
| 8542 } | |
| 8543 div.tabArea.base span.tab { | |
| 8544 border-top-width: 0px; | |
| 8545 } | |
| 8546 div.tabArea.top span.tab { | |
| 8547 border-bottom-width: 0px; | |
| 8548 } | |
| 8549 | |
| 8550 span.tab:hover { | |
| 8551 background-color: #bbb; | |
| 8552 border-color: #bbb; | |
| 8553 color: #666; | |
| 8554 } | |
| 8555 span.tab.activeTab, span.tab.activeTab:hover, span.tab.activeTab:visited { | |
| 8556 background-color: white; | |
| 8557 color: black; | |
| 8558 cursor: default; | |
| 8559 } | |
| 8560 div.tabMain { | |
| 8561 border: 2px solid #ccc; | |
| 8562 background-color: white; | |
| 8563 padding: 10px; | |
| 8564 } | |
| 8565 div.tabMain.base { | |
| 8566 margin-top: 5px; | |
| 8567 display: inline-block; | |
| 8568 max-width: 98%; | |
| 8569 } | |
| 8570 | |
| 8571 div.tabMain.top { | |
| 8572 margin-bottom: 5px; | |
| 8573 } | |
| 8574 | |
| 8575 div.tabCenter { | |
| 8576 max-width: 100%; | |
| 8577 overflow-x: auto; | |
| 8578 height: 200px; | |
| 8579 overflow-y: hidden; | |
| 8580 } | |
| 8581 | |
| 8582 canvas.logo_rc { | |
| 8583 display:none; | |
| 8584 } | |
| 8585 .show_rc_logo > canvas { | |
| 8586 display: none; | |
| 8587 } | |
| 8588 .show_rc_logo > canvas.logo_rc { | |
| 8589 display: block; | |
| 8590 } | |
| 8591 | |
| 8592 canvas.scan_logo { | |
| 8593 margin-left: 10px; | |
| 8594 } | |
| 8595 | |
| 8596 div.blocks_outer { | |
| 8597 position: relative; | |
| 8598 padding-top: 20px; /* height of header */ | |
| 8599 } | |
| 8600 | |
| 8601 div.blocks_inner { | |
| 8602 overflow-x: hidden; | |
| 8603 overflow-y: auto; | |
| 8604 max-height: 200px; | |
| 8605 } | |
| 8606 table.blocks_tbl { | |
| 8607 border-collapse: collapse; | |
| 8608 width: 100%; | |
| 8609 } | |
| 8610 | |
| 8611 table.blocks_tbl .blockdiag_name { | |
| 8612 white-space: nowrap | |
| 8613 } | |
| 8614 | |
| 8615 div.blocks_th_inner { | |
| 8616 position: absolute; | |
| 8617 top: 0; | |
| 8618 line-height: 20px; /* height of header */ | |
| 8619 text-align: left; | |
| 8620 padding-left: 5px; | |
| 8621 } | |
| 8622 th.nopad div.blocks_th_inner { | |
| 8623 padding-left: 0; | |
| 8624 } | |
| 8625 div.blocks_th_hidden { | |
| 8626 visibility: hidden; | |
| 8627 height: 0; | |
| 8628 padding: 0 10px; | |
| 8629 } | |
| 8630 th.nopad div.blocks_th_hidden { | |
| 8631 padding: 0; | |
| 8632 } | |
| 8633 div.blocks_inner * th { | |
| 8634 height: 0; | |
| 8635 } | |
| 8636 | |
| 8637 table.blocks_tbl { | |
| 8638 overflow-x: hidden; | |
| 8639 overflow-y: auto; | |
| 8640 } | |
| 8641 td.block_td { | |
| 8642 width: 99%; | |
| 8643 } | |
| 8644 | |
| 8645 *.blockdiag_num { | |
| 8646 text-align: right; | |
| 8647 } | |
| 8648 | |
| 8649 td.blockdiag_name { | |
| 8650 text-align: left; | |
| 8651 padding:0px 10px; | |
| 8652 } | |
| 8653 | |
| 8654 td.blockdiag_pvalue { | |
| 8655 padding:0px 10px; | |
| 8656 text-align:right; | |
| 8657 white-space: nowrap; | |
| 8658 } | |
| 8659 | |
| 8660 div.preview_btn { | |
| 8661 border: 2px solid white; | |
| 8662 height: 16px; | |
| 8663 width: 16px; | |
| 8664 font-size: 12px; | |
| 8665 line-height: 16px; | |
| 8666 text-align: center; | |
| 8667 cursor: pointer; | |
| 8668 } | |
| 8669 div.preview_btn + div.preview_btn { | |
| 8670 margin-top: 3px; | |
| 8671 } | |
| 8672 | |
| 8673 div.preview_btn.active { | |
| 8674 border: 2px solid black; | |
| 8675 cursor: default; | |
| 8676 } | |
| 8677 | |
| 8678 div.preview_btn:hover { | |
| 8679 background-color: black; | |
| 8680 color: white; | |
| 8681 border-color: black; | |
| 8682 } | |
| 8683 | |
| 8684 div.preview_btn.active:hover { | |
| 8685 background-color: white; | |
| 8686 color: black; | |
| 8687 border-color: black; | |
| 8688 } | |
| 8689 | |
| 8690 | |
| 8691 div.preview_btn_box { | |
| 8692 position: absolute; | |
| 8693 left: 0px; | |
| 8694 top: 0px; | |
| 8695 padding: 3px; | |
| 8696 } | |
| 8697 | |
| 8698 div.preview_logo_box { | |
| 8699 height: 50px; | |
| 8700 overflow-y: hidden; | |
| 8701 } | |
| 8702 | |
| 8703 div.preview_btn_box + div.preview_logo_box { | |
| 8704 margin-left: 25px; | |
| 8705 } | |
| 8706 | |
| 8707 div.preview_box { | |
| 8708 position: relative; | |
| 8709 } | |
| 8710 | |
| 8711 div.grey_background { | |
| 8712 position:fixed; | |
| 8713 z-index: 8; | |
| 8714 background-color: #000; | |
| 8715 -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; | |
| 8716 opacity: 0.5; | |
| 8717 left: 0; | |
| 8718 top: 0; | |
| 8719 width: 100%; | |
| 8720 height: 100%; | |
| 8721 } | |
| 8722 | |
| 8723 div.popup_wrapper { | |
| 8724 position:fixed; | |
| 8725 z-index:9; | |
| 8726 width:100%; | |
| 8727 height:0; | |
| 8728 top:50%; | |
| 8729 left:0; | |
| 8730 } | |
| 8731 | |
| 8732 div.popup { | |
| 8733 width: 600px; | |
| 8734 z-index:9; | |
| 8735 margin-left: auto; | |
| 8736 margin-right: auto; | |
| 8737 padding: 5px; | |
| 8738 background-color: #FFF; | |
| 8739 border-style: double; | |
| 8740 border-width: 5px; | |
| 8741 border-color: #00666a; | |
| 8742 position:relative; | |
| 8743 } | |
| 8744 div.close { | |
| 8745 cursor: pointer; | |
| 8746 border: 1px solid black; | |
| 8747 width:15px; | |
| 8748 height:15px; | |
| 8749 line-height:15px; /* this causes vertical centering */ | |
| 8750 text-align:center; | |
| 8751 background-color:#FFF; | |
| 8752 color:#000; | |
| 8753 font-size:15px; | |
| 8754 font-family:monospace; | |
| 8755 } | |
| 8756 | |
| 8757 div.close:hover { | |
| 8758 color:#FFF; | |
| 8759 background-color:#000; | |
| 8760 } | |
| 8761 | |
| 8762 div.navnum { | |
| 8763 width:100%; | |
| 8764 height:20px; | |
| 8765 line-height:20px; | |
| 8766 text-align:center; | |
| 8767 font-size:medium; | |
| 8768 } | |
| 8769 | |
| 8770 div.navarrow { | |
| 8771 font-size: 30px; | |
| 8772 text-decoration:none; | |
| 8773 cursor: pointer; | |
| 8774 -moz-user-select: none; | |
| 8775 -webkit-user-select: none; | |
| 8776 -ms-user-select: none; | |
| 8777 } | |
| 8778 | |
| 8779 div.navarrow > span.inactive { | |
| 8780 display: inline; | |
| 8781 } | |
| 8782 div.navarrow > span.active { | |
| 8783 display: none; | |
| 8784 } | |
| 8785 | |
| 8786 div.navarrow:hover > span.active { | |
| 8787 display: inline; | |
| 8788 } | |
| 8789 div.navarrow:hover > span.inactive { | |
| 8790 display: none; | |
| 8791 } | |
| 8792 | |
| 8793 table.programs { | |
| 8794 width: 100%; | |
| 8795 } | |
| 8796 | |
| 8797 table.programs tr { | |
| 8798 background-color: #EFE; | |
| 8799 } | |
| 8800 | |
| 8801 table.programs tr.selected { | |
| 8802 background-color: #262; | |
| 8803 color: #FFF; | |
| 8804 } | |
| 8805 | |
| 8806 table.programs tr.dna_only { | |
| 8807 display: none; | |
| 8808 } | |
| 8809 | |
| 8810 table.programs.alphabet_dna tr.dna_only { | |
| 8811 display: table-row; | |
| 8812 } | |
| 8813 | |
| 8814 div.programs_scroll { | |
| 8815 width: 100%; | |
| 8816 height: 90px; | |
| 8817 overflow-y: auto; | |
| 8818 overflow-x: hidden; | |
| 8819 margin: 0 auto; | |
| 8820 } | |
| 8821 table.inputs, table.alpha_bg_table { | |
| 8822 margin-top: 20px; | |
| 8823 border-collapse:collapse; | |
| 8824 } | |
| 8825 table.inputs * td, table.inputs * th, table.alpha_bg_table * td, table.alpha_bg_table * th { | |
| 8826 padding-left: 15px; | |
| 8827 padding-right: 15px; | |
| 8828 padding-top: 1px; | |
| 8829 padding-bottom: 1px; | |
| 8830 } | |
| 8831 | |
| 8832 table.hide_psp td.col_psp, table.hide_psp th.col_psp { | |
| 8833 display: none; | |
| 8834 } | |
| 8835 | |
| 8836 table.hide_control td.col_control, table.hide_control th.col_control { | |
| 8837 display: none; | |
| 8838 } | |
| 8839 | |
| 8840 /* program settings */ | |
| 8841 span.mod_oops, span.mod_zoops, span.mod_anr { | |
| 8842 display: none; | |
| 8843 } | |
| 8844 td.oops span.mod_oops,td.zoops span.mod_zoops, td.anr span.mod_anr { | |
| 8845 display: inline; | |
| 8846 } | |
| 8847 span.strand_none, span.strand_given, span.strand_both { | |
| 8848 display: none; | |
| 8849 } | |
| 8850 td.none span.strand_none, td.given span.strand_given, td.both span.strand_both { | |
| 8851 display: inline; | |
| 8852 } | |
| 8853 span.spmap_uni, span.spmap_pam { | |
| 8854 display: none; | |
| 8855 } | |
| 8856 td.uni span.spmap_uni, td.pam span.spmap_pam { | |
| 8857 display: inline; | |
| 8858 } | |
| 8859 span.prior_dirichlet, span.prior_dmix, span.prior_mega, span.prior_megap, span.prior_addone { | |
| 8860 display: none; | |
| 8861 } | |
| 8862 td.dirichlet span.prior_dirichlet, td.dmix span.prior_dmix, td.mega span.prior_mega, | |
| 8863 td.megap span.prior_megap, td.addone span.prior_addone { | |
| 8864 display: inline; | |
| 8865 } | |
| 8866 span.noendgaps_on, span.noendgaps_off { | |
| 8867 display: none; | |
| 8868 } | |
| 8869 td.on span.noendgaps_on, td.off span.noendgaps_off { | |
| 8870 display: inline; | |
| 8871 } | |
| 8872 span.substring_on, span.substring_off { | |
| 8873 display: none; | |
| 8874 } | |
| 8875 td.on span.substring_on, td.off span.substring_off { | |
| 8876 display: inline; | |
| 8877 } | |
| 8878 </style> | |
| 8879 </head> | |
| 8880 <body onload="page_loaded()" onpageshow="page_shown(event)" onresize="page_resized()"> | |
| 8881 <!-- --> | |
| 8882 <div id="grey_out_page" class="grey_background" style="display:none;"> | |
| 8883 </div> | |
| 8884 <!-- Help popups --> | |
| 8885 <div class="pop_content" id="pop_results_txt"> | |
| 8886 <p>MEME results in plain text format.</p> | |
| 8887 <div style="float:right; bottom:0px;">[ | |
| 8888 <a href="javascript:help_popup()">close</a> ]</div> | |
| 8889 </div> | |
| 8890 <div class="pop_content" id="pop_results_xml"> | |
| 8891 <p>MEME results in XML format.</p> | |
| 8892 <div style="float:right; bottom:0px;">[ | |
| 8893 <a href="javascript:help_popup()">close</a> ]</div> | |
| 8894 </div> | |
| 8895 <div class="pop_content" id="pop_"> | |
| 8896 <p>Help poup.</p> | |
| 8897 <div style="float:right; bottom:0px;">[ | |
| 8898 <a href="javascript:help_popup()">close</a> ]</div> | |
| 8899 </div> | |
| 8900 <div class="pop_content" id="pop_logo"> | |
| 8901 <script>print_doc_para("pop_logo", "discovery", "motif_logo", site_url);</script> | |
| 8902 Click on the "+" or "-" buttons to the left of the motif to see | |
| 8903 the forward or reverse complement of the motif if available. | |
| 8904 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 8905 </div> | |
| 8906 <div class="pop_content" id="pop_ev"> | |
| 8907 <p>The statistical significance of the motif. MEME usually finds the most | |
| 8908 statistically significant (low E-value) motifs first. It is unusual to | |
| 8909 consider a motif with an E-value larger than 0.05 significant so, as an | |
| 8910 additional indicator, MEME displays these greyed out.</p> | |
| 8911 <p>The E-value of a motif is based on its log likelihood ratio, width, | |
| 8912 sites, the background letter frequencies (given in the command line | |
| 8913 summary), and the size of the training set.</p> | |
| 8914 <p>The E-value is an estimate of the expected number of motifs with the | |
| 8915 given log likelihood ratio (or higher), and with the same width and site | |
| 8916 count, that one would find in a similarly sized set of random | |
| 8917 sequences (sequences where each position is independent and letters are | |
| 8918 chosen according to the background letter frequencies).</p> | |
| 8919 <div style="float:right; bottom:0px;">[ | |
| 8920 <a href="javascript:help_popup()">close</a> ]</div> | |
| 8921 </div> | |
| 8922 <div class="pop_content" id="pop_sites"> | |
| 8923 <p>The number of sites contributing to the construction of the motif.</p> | |
| 8924 <div style="float:right; bottom:0px;">[ | |
| 8925 <a href="javascript:help_popup()">close</a> ]</div> | |
| 8926 </div> | |
| 8927 <div class="pop_content" id="pop_width"> | |
| 8928 <p>The width of the motif. Each motif describes a pattern of a fixed | |
| 8929 width, as no gaps are allowed in MEME motifs.</p> | |
| 8930 <div style="float:right; bottom:0px;">[ | |
| 8931 <a href="javascript:help_popup()">close</a> ]</div> | |
| 8932 </div> | |
| 8933 <div class="pop_content" id="pop_more"> | |
| 8934 <script>print_doc_para("pop_more", "discovery", "more");</script> | |
| 8935 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
| 8936 </div> | |
| 8937 <div class="pop_content" id="pop_submit_dl"> | |
| 8938 <script>print_doc_para("pop_submit_dl", "discovery", "submit_dl", "https://meme-suite.org/meme");</script> | |
| 8939 <div style="float:right; bottom:0px;">[ | |
| 8940 <a href="javascript:help_popup()">close</a> ]</div> | |
| 8941 </div> | |
| 8942 <div class="pop_content" id="pop_llr"> | |
| 8943 <p>The log likelihood ratio of the motif. The log likelihood ratio is the | |
| 8944 logarithm of the ratio of the probability of the occurrences of the motif | |
| 8945 given the motif model (likelihood given the motif) versus their | |
| 8946 probability given the background model (likelihood given the null model). | |
| 8947 (Normally the background model is a 0-order Markov model using the | |
| 8948 background letter frequencies, but higher order Markov models may be | |
| 8949 specified via the -bfile option to MEME.).</p> | |
| 8950 <div style="float:right; bottom:0px;">[ | |
| 8951 <a href="javascript:help_popup()">close</a> ]</div> | |
| 8952 </div> | |
| 8953 <div class="pop_content" id="pop_ic"> | |
| 8954 <p>The information content of the motif in bits. It is equal to the sum | |
| 8955 of the uncorrected information content, R(), in the columns of the motif. | |
| 8956 This is equal relative entropy of the motif relative to a uniform | |
| 8957 background frequency model.</p> | |
| 8958 <div style="float:right; bottom:0px;">[ | |
| 8959 <a href="javascript:help_popup()">close</a> ]</div> | |
| 8960 </div> | |
| 8961 <div class="pop_content" id="pop_re"> | |
| 8962 <p>The relative entropy of the motif.</p> | |
| 8963 | |
| 8964 <p style="font-family: monospace;">re = llr / (sites * ln(2))</p> | |
| 8965 <div style="float:right; bottom:0px;">[ | |
| 8966 <a href="javascript:help_popup()">close</a> ]</div> | |
| 8967 </div> | |
| 8968 <div class="pop_content" id="pop_bt"> | |
| 8969 <p>The Bayes Threshold.</p> | |
| 8970 <div style="float:right; bottom:0px;">[ | |
| 8971 <a href="javascript:help_popup()">close</a> ]</div> | |
| 8972 </div> | |
| 8973 <div class="pop_content" id="pop_site_strand"> | |
| 8974 <p>The strand used for the motif site.</p> | |
| 8975 <dl> | |
| 8976 <dt>+</dt> | |
| 8977 <dd>The motif site was found in the sequence as it was supplied.</dd> | |
| 8978 <dt>-</dt> | |
| 8979 <dd>The motif site was found in the reverse complement of the supplied sequence.</dd> | |
| 8980 </dl> | |
| 8981 <div style="float:right; bottom:0px;">[ | |
| 8982 <a href="javascript:help_popup()">close</a> ]</div> | |
| 8983 </div> | |
| 8984 <div class="pop_content" id="pop_site_start"> | |
| 8985 <p>The position in the sequence where the motif site starts. If a motif | |
| 8986 started right at the beginning of a sequence it would be described as | |
| 8987 starting at position 1.</p> | |
| 8988 <div style="float:right; bottom:0px;">[ | |
| 8989 <a href="javascript:help_popup()">close</a> ]</div> | |
| 8990 </div> | |
| 8991 <div class="pop_content" id="pop_site_pvalue"> | |
| 8992 <p>The probability that an equal or better site would be found in a | |
| 8993 random sequence of the same length conforming to the background letter | |
| 8994 frequencies.</p> | |
| 8995 <div style="float:right; bottom:0px;">[ | |
| 8996 <a href="javascript:help_popup()">close</a> ]</div> | |
| 8997 </div> | |
| 8998 <div class="pop_content" id="pop_site_match"> | |
| 8999 <p>A motif site with the 10 flanking letters on either side.</p> | |
| 9000 <p>When the site is not on the given strand then the site | |
| 9001 and both flanks are reverse complemented so they align.</p> | |
| 9002 <div style="float:right; bottom:0px;">[ | |
| 9003 <a href="javascript:help_popup()">close</a> ]</div> | |
| 9004 </div> | |
| 9005 | |
| 9006 <div class="pop_content" id="pop_seq_name"> | |
| 9007 <p>The name of the sequences as given in the FASTA file.</p> | |
| 9008 <p>The number to the left of the sequence name is the position | |
| 9009 of the sequence in the input sequence file.</p> | |
| 9010 <div style="float:right; bottom:0px;">[ | |
| 9011 <a href="javascript:help_popup()">close</a> ]</div> | |
| 9012 </div> | |
| 9013 | |
| 9014 <div class="pop_content" id="pop_motif_sites"> | |
| 9015 <p>These are the motif sites predicted by MEME and used to build the motif.</p> | |
| 9016 <p>These sites are shown in solid color and hovering the cursor | |
| 9017 over a site will reveal details about the site. Only sequences | |
| 9018 that contain a motif site are shown.</p> | |
| 9019 <div style="float:right; bottom:0px;">[ | |
| 9020 <a href="javascript:help_popup()">close</a> ]</div> | |
| 9021 </div> | |
| 9022 | |
| 9023 <div class="pop_content" id="pop_scanned_sites"> | |
| 9024 <p>These are the motif sites predicted by MEME plus | |
| 9025 any additional sites detected using a motif scanning | |
| 9026 algorithm.</p> | |
| 9027 <p>These MEME sites are shown in solid color and | |
| 9028 additional scanned sites are shown greyed out. | |
| 9029 Hovering the cursor over a site will reveal details about the site. | |
| 9030 Only sequences containing a predicted or scanned motif site are shown.</p> | |
| 9031 <p>The scanned sites are predicted using a | |
| 9032 log-odds scoring matrix constructed from the MEME sites. | |
| 9033 Only scanned sites with position <i>p</i>-values less | |
| 9034 than 0.0001 are shown.</p> | |
| 9035 <div style="float:right; bottom:0px;">[ | |
| 9036 <a href="javascript:help_popup()">close</a> ]</div> | |
| 9037 </div> | |
| 9038 | |
| 9039 <div class="pop_content" id="pop_all_sequences"> | |
| 9040 <p>These are the same sites as shown by selecting the | |
| 9041 "Motif Sites + Scanned Sites" button except that all | |
| 9042 sequences, including those with no sites, are included | |
| 9043 in the diagram.</p> | |
| 9044 <div style="float:right; bottom:0px;">[ | |
| 9045 <a href="javascript:help_popup()">close</a> ]</div> | |
| 9046 </div> | |
| 9047 | |
| 9048 <div class="pop_content" id="pop_seq_pvalue"> | |
| 9049 <p>This is the combined match <i>p</i>-value.</p> | |
| 9050 <p>The combined match <i>p</i>-value is defined as the probability that a | |
| 9051 random sequence (with the same length and conforming to the background) | |
| 9052 would have position <i>p</i>-values such that the product is smaller | |
| 9053 or equal to the value calculated for the sequence under test.</p> | |
| 9054 <p>The position <i>p</i>-value is defined as the probability that a | |
| 9055 random sequence (with the same length and conforming to the background) | |
| 9056 would have a match to the motif under test with a score greater or equal | |
| 9057 to the largest found in the sequence under test.</p> | |
| 9058 <p>Hovering your mouse over a motif site in the motif location | |
| 9059 block diagram will show its position <i>p</i>-value and other information | |
| 9060 about the site.</p> | |
| 9061 <div style="float:right; bottom:0px;">[ | |
| 9062 <a href="javascript:help_popup()">close</a> ]</div> | |
| 9063 </div> | |
| 9064 | |
| 9065 <div class="pop_content" id="pop_download_pdf_motif_locations"> | |
| 9066 <p>Use this button to download the "Motif Locations" block diagrams | |
| 9067 as a PDF image suitable for publication. | |
| 9068 </p> | |
| 9069 <p> | |
| 9070 Only the block diagrams currently visible in the inner scrolling | |
| 9071 window (below) will be included in the image, and the numbers to | |
| 9072 the left of each sequence name will not be included in the image. | |
| 9073 You can change the size of the inner scrolling by moving the bottom | |
| 9074 of the main document window up and down. You can display more | |
| 9075 diagrams by making your browser's font size smaller. | |
| 9076 </p> | |
| 9077 <div style="float:right; bottom:0px;">[ | |
| 9078 <a href="javascript:help_popup()">close</a> ]</div> | |
| 9079 </div> | |
| 9080 | |
| 9081 <div class="pop_content" id="pop_download_svg_motif_locations"> | |
| 9082 <p>Use this button to download the "Motif Locations" block diagrams | |
| 9083 as a SVG image use in HTML documents. | |
| 9084 </p> | |
| 9085 <p> | |
| 9086 Only the block diagrams currently visible in the inner scrolling | |
| 9087 window (below) will be included in the image, and the numbers to | |
| 9088 the left of each sequence name will not be included in the image. | |
| 9089 You can change the size of the inner scrolling by moving the bottom | |
| 9090 of the main document window up and down. You can display more | |
| 9091 diagrams by making your browser's font size smaller. | |
| 9092 </p> | |
| 9093 <div style="float:right; bottom:0px;">[ | |
| 9094 <a href="javascript:help_popup()">close</a> ]</div> | |
| 9095 </div> | |
| 9096 | |
| 9097 <div class="pop_content" id="pop_offline"> | |
| 9098 <p> | |
| 9099 This button will only function if your browser was | |
| 9100 connected to the internet when you loaded this page. | |
| 9101 </p> | |
| 9102 <p> | |
| 9103 To use this button, make sure your browser is connected to the internet | |
| 9104 and then reload this page. (You may need to do a "hard refresh" to clear the cache. | |
| 9105 On Mac, hold down the Shift key and click the Reload button. | |
| 9106 On Windows/Linux, hold down Ctrl and press F5.) | |
| 9107 </p> | |
| 9108 <div style="float:right; bottom:0px;">[ | |
| 9109 <a href="javascript:help_popup()">close</a> ]</div> | |
| 9110 </div> | |
| 9111 | |
| 9112 <div class="pop_content" id="pop_motif_location"> | |
| 9113 <p>This diagram shows the location of motif sites.</p> | |
| 9114 <p>Each block shows the position and strength of a motif | |
| 9115 site. The height of a block gives an indication of the | |
| 9116 significance of the site as taller blocks are more significant. | |
| 9117 The height is calculated to be proportional to the negative | |
| 9118 logarithm of the <i>p</i>-value of the site, truncated at | |
| 9119 the height for a <i>p</i>-value of 1e-10.</p> | |
| 9120 <p>For complementable alphabets (like DNA), sites on the | |
| 9121 positive strand are shown above the line, | |
| 9122 sites on the negative strand are shown below.</p> | |
| 9123 <p>Placing the cursor | |
| 9124 over a motif site will reveal more information about the site | |
| 9125 including its position <i>p</i>-value. (See the help | |
| 9126 for the <i>p</i>-value column for an explanation of position | |
| 9127 <i>p</i>-values.)</p> | |
| 9128 <div style="float:right; bottom:0px;">[ | |
| 9129 <a href="javascript:help_popup()">close</a> ]</div> | |
| 9130 </div> | |
| 9131 | |
| 9132 <div class="pop_content" id="pop_seq_source"> | |
| 9133 <p>The name of the file(s) of sequences input to MEME.</p> | |
| 9134 <div style="float:right; bottom:0px;">[ | |
| 9135 <a href="javascript:help_popup()">close</a> ]</div> | |
| 9136 </div> | |
| 9137 <div class="pop_content" id="pop_psp_source"> | |
| 9138 <p>The position specific priors file used by MEME to find the motifs.</p> | |
| 9139 <div style="float:right; bottom:0px;">[ | |
| 9140 <a href="javascript:help_popup()">close</a> ]</div> | |
| 9141 </div> | |
| 9142 <div class="pop_content" id="pop_seq_alph"> | |
| 9143 <p>The alphabet used by the sequences.</p> | |
| 9144 <div style="float:right; bottom:0px;">[ | |
| 9145 <a href="javascript:help_popup()">close</a> ]</div> | |
| 9146 </div> | |
| 9147 <div class="pop_content" id="pop_seq_count"> | |
| 9148 <p>The number of FASTA sequences provided in this input file.</p> | |
| 9149 <div style="float:right; bottom:0px;">[ | |
| 9150 <a href="javascript:help_popup()">close</a> ]</div> | |
| 9151 </div> | |
| 9152 <div class="pop_content" id="pop_num_positions"> | |
| 9153 <p>The number of characters in the sequences provided in this FASTA input file.</p> | |
| 9154 <div style="float:right; bottom:0px;">[ | |
| 9155 <a href="javascript:help_popup()">close</a> ]</div> | |
| 9156 </div> | |
| 9157 | |
| 9158 <div class="pop_content" id="pop_alph_name"> | |
| 9159 <p>The name of the alphabet symbol.</p> | |
| 9160 <div style="float:right; bottom:0px;">[ | |
| 9161 <a href="javascript:help_popup()">close</a> ]</div> | |
| 9162 </div> | |
| 9163 <div class="pop_content" id="pop_alph_freq"> | |
| 9164 <p>The frequency of the alphabet symbol in the dataset.</p> | |
| 9165 <div style="float:right; bottom:0px;">[ | |
| 9166 <a href="javascript:help_popup()">close</a> ]</div> | |
| 9167 </div> | |
| 9168 <div class="pop_content" id="pop_alph_bg"> | |
| 9169 <p>The frequency of the alphabet symbol as defined by the background model.</p> | |
| 9170 <div style="float:right; bottom:0px;">[ | |
| 9171 <a href="javascript:help_popup()">close</a> ]</div> | |
| 9172 </div> | |
| 9173 | |
| 9174 <!-- templates --> | |
| 9175 <div id="measure_match" class="match"></div> | |
| 9176 <div class="template pop_block" id="tmpl_block_info"> | |
| 9177 <div> | |
| 9178 <span class="tvar_logo_pad lflank" style="visibility:hidden;"></span> | |
| 9179 <span class="tvar_logo"></span> | |
| 9180 </div> | |
| 9181 <div class="block_sequence_fragment"> | |
| 9182 <span class="tvar_lflank lflank"></span> | |
| 9183 <span class="tvar_match match"></span> | |
| 9184 <span class="tvar_rflank rflank"></span> | |
| 9185 </div> | |
| 9186 <table class="block_information"> | |
| 9187 <tr><th>Motif</th><td class="tvar_motif">1</td></tr> | |
| 9188 <tr><th><i>p</i>-value</th><td class="tvar_pvalue">8.23e-7</td></tr> | |
| 9189 <tr><th>Start</th><td class="tvar_start">23</td></tr> | |
| 9190 <tr><th>End</th><td class="tvar_end">33</td></tr> | |
| 9191 </table> | |
| 9192 </div> | |
| 9193 | |
| 9194 <div class="template pop_block" id="tmpl_scan_info"> | |
| 9195 <h5>Scanned Site</h5> | |
| 9196 <div class="tvar_logo"></div> | |
| 9197 <table class="block_information"> | |
| 9198 <tr><th>Motif</th><td class="tvar_motif">1</td></tr> | |
| 9199 <tr><th><i>p</i>-value</th><td class="tvar_pvalue">8.23e-7</td></tr> | |
| 9200 <tr><th>Start</th><td class="tvar_start">23</td></tr> | |
| 9201 <tr><th>End</th><td class="tvar_end">33</td></tr> | |
| 9202 </table> | |
| 9203 </div> | |
| 9204 | |
| 9205 <div class="template box expanded_motif" id="tmpl_motif_expanded"> | |
| 9206 <div style="position: relative; min-height: 20px"> | |
| 9207 <div class="param_box"> | |
| 9208 <span class="param"><span class="tvar_ordinal"></span>.</span> | |
| 9209 </div> | |
| 9210 <div class="sym_btn positioned tvar_less" tabindex="0" | |
| 9211 title="Show less information.">↥</div> | |
| 9212 <div class="sym_btn positioned tvar_submit" tabindex="0" | |
| 9213 title="Submit the motif to another MEME Suite program or download it.">⇢</div> | |
| 9214 </div> | |
| 9215 <div> | |
| 9216 <div class="param_box"> | |
| 9217 <span class="param"><i>E</i>-value:</span> | |
| 9218 <span class="tvar_evalue"></span> | |
| 9219 <div class="help" data-topic="pop_ev"></div> | |
| 9220 </div> | |
| 9221 <div class="param_box"> | |
| 9222 <span class="param">Site Count:</span> | |
| 9223 <span class="tvar_site_count"></span> | |
| 9224 <div class="help" data-topic="pop_sites"></div> | |
| 9225 </div> | |
| 9226 <div class="param_box"> | |
| 9227 <span class="param">Width:</span> | |
| 9228 <span class="tvar_width"></span> | |
| 9229 <div class="help" data-topic="pop_width"></div> | |
| 9230 </div> | |
| 9231 </div> | |
| 9232 <div class="tabMain base"> | |
| 9233 <div class="tabCenter tvar_logo"></div> | |
| 9234 </div> | |
| 9235 <div class="tabArea base"> | |
| 9236 <span class="tvar_tab tab" tabindex="0">Standard</span><span | |
| 9237 class="tvar_tab_rc tab middle" tabindex="0">Reverse | |
| 9238 Complement</span> | |
| 9239 </div> | |
| 9240 <div style="padding: 10px 0"> | |
| 9241 <div class="param_box"> | |
| 9242 <span class="param">Log Likelihood Ratio:</span> | |
| 9243 <span class="tvar_llr"></span> | |
| 9244 <div class="help" data-topic="pop_llr"></div> | |
| 9245 </div> | |
| 9246 <div class="param_box"> | |
| 9247 <span class="param">Information Content:</span> | |
| 9248 <span class="tvar_ic"></span> | |
| 9249 <div class="help" data-topic="pop_ic"></div> | |
| 9250 </div> | |
| 9251 <div class="param_box"> | |
| 9252 <span class="param">Relative Entropy:</span> | |
| 9253 <span class="tvar_re"></span> | |
| 9254 <div class="help" data-topic="pop_re"></div> | |
| 9255 </div> | |
| 9256 <div class="param_box"> | |
| 9257 <span class="param">Bayes Threshold:</span> | |
| 9258 <span class="tvar_bt"></span> | |
| 9259 <div class="help" data-topic="pop_bt"></div> | |
| 9260 </div> | |
| 9261 </div> | |
| 9262 <div class="tvar_sites"></div> | |
| 9263 </div> | |
| 9264 | |
| 9265 <div id="tab_submit_or_download_motif"></div> | |
| 9266 <script> make_submit_or_download_motif_form("tab_submit_or_download_motif", site_url, "MEME"); </script> | |
| 9267 | |
| 9268 <!-- Page starts here --> | |
| 9269 <div id="top" class="pad1"> | |
| 9270 <div class="prog_logo big"> | |
| 9271 <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAD4AAABGCAYAAACUsCfoAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9wLHAInL7an9JAAAA8eSURBVHja7ZpZbxv3ucZ/s3AZ7qQkypZELZS3WHKiRJbTFnHqOjZcGzCQBf0A7cUB8glOb89dz30vChRp7xK0aZo4TYq6dtKqSew6iuJWpmPJlrVFFCmKI+7LLJw5F81MFZyec5rTILJaP8AAnAty+Pzf933ebeAhHuIhHuIhHmJPo6TpaeGLfimTydhLS0sUCgWazSbVahXDMOh0Ong8HiKRCAMDAxw+fJhHH31UeFDJ/91/bHp62r558ya6rmOaJuvr6+TzeYrFIoZhAOD1epFlmWAwSDqdZnR0lCNHjjA+Ps7+/fuFPUU8k8nYV69epVKpEA6HuXPnDrOzs7RaLbxeL93d3fj9foLBIB6Ph0ajQaVSAUBRFNLpNI888ggHDhzg3Llzwp4g/vbbb9szMzPE43FyuRwffPAB2WyWnp4ehoaGiMVi2LZNvV6n3W7j8/mIRqN4vV4ajQa1Wo2trS0OHTqEaZo89dRTfO973xMeaOKvvfaaffv2bcLhMCsrK/z+979HVVWOHz/O0NAQxWIRVVVRVRW/348oihiGgSiKxGIxurq6CAQCBINB3n//fcbGxiiXyxw7dowXX3yx1Nvbm3igiBcKhY9u3rw5OTMzQzgc5vbt23zwwQfIsszExAS2bZPP55EkiVAoRCQSIRwOI8sy5XKZ7e1tWq0WnU6HTqfD4OAggUAAVVXpdDo0m01GRkb44Q9/KDxQxH/729/as7Oz+Hw+Pv30U37zm9/g9Xrp6emht7eXYDCI3+/H6/UiCALtdhvLsuh0OkiSRCAQQJZl6vU65XKZ1dVV+vv7SSQS5PN5vF4vhUKBgwcP8qMf/WjXyIs7b+7cuWMvLi4iCAL1ep133nkHXdcZHh4mlUoRDofp7+8nHo9jGAaCIBCPx+nr6yOdTtPX14fH40HTNGRZpqenh76+PrLZLMVikYGBARRFwTRN5ubm+PGPf2zvFnF5583c3BzlchlZlrl16xa1Wo2xsTGi0SiDg4OutUzTJBKJIIoiXq+XQCBAIpEgFAphmiaqqlIoFKjVaoyOjhKJRFhfXycajRKNRunp6WFhYYG33nqLQqHwUTKZPP5VE5ecD5cvX7bv37+Poijkcjl++ctfMjo6ytDQEMlkEo/HQ7lcpt1u4/F4kGUZQRDw+Xz09fVx4MABRkZGSKVSdHV10el0KJVK1Ot14vE4kiRRKpVQFIV4PI5t23z66ad0Op2+K1eu/MeuWVxVVUzTRNd17t27RzKZJJFIEI1GEUWRdruNaZqIoogkSYiiiCiK+P1+AoEA0WiUQ4cOCQCbm5vb5XI5Xi6XWVtbw7ZtZFnG7/e7D1YUhWAwSLVaJZvN2v39/cJXHuOLi4t2sVhEEAS2t7eZn59n//79JBIJwuEwlmXRbDbRdR1BEBAEAcuysCwL2/5LmIriX+Wit7c3EY/HSSQSeDweDMPA4/EQCoWwbRvbtgmFQoTDYUqlErlcbnfE7f79+64gbWxs0G63icViRCIRBEHANE0Mw0CSJLxer0vSUfVKpUKxWGRtbc12UmKr1cI0TQD3t4PBoHtwgUAAv99Ps9kkn8/vjrjlcjlkWabdbrO8vEx3dzfhcJhoNEq9XkfTNERRxOfz4fV6sW0bSZJcBS+VSqytrdFqtbh+/bq9sLBALpdje3sbXddd4jutLwh/8WzLslBVdXeI12o1RFFkc3PTLTFjsRixWAxVVbEsC1mWkSTJdVXnMgyDarWKrusUi0X8fj+WZVGtVimXyxiGgW3bblh4PB78fj/tdhvbthFF0a3tv3Liuq4jiiKFQgFZlolEInR3d6MoConEXytLp1Bx0Ol0sG0bXdfRdZ1arYYgCNi2jaZptFot1zOcQ3LSnxMGzsHvCnHH/RqNBtFoFEmSCIfDAAwNDWFZFu12m0ajgaZpWJbl/oDH4/ncoTii5xBz1N+2bTqdDrIs4/P53MxgWRaapu2exQOBgHsAnU6HdrvN5uYmgUCASCRCJBIhkUhgmibNZpNarUaj0XAt71jfET1RFN1YliTJfUa73SaZTLK1tUWtVkPTNKampnaHuFNGyrKM1+t141bTNGq1GrVazS1EIpEI8XjcLWJqtdrnYthNF58pv23beL1eJEly1VzXder1Oq1Wi2AwiK7ru0PcSSs73bBYLBIMBpEkCdM0qVQq6LpOq9UiHA673Vmn08E0TfdyRM85CNM08Xq9bkoMhULugRqGgd/v3z1Xl2X5c24aDodRVZV4PO7W406/raoqtVqNYDCIz+dDURQ3vp3LuXeEz4nlTqdDOBxG13U3TBRF2b0mJRAI0Gg0EAQBTdPw+/2Uy2VXoJzRkkPEifNWq0UgEHCLEsedLcty1X1nzIdCIRRFoVQqUSqVsCyLYDBIPB7fncotlUq5JaWu6xiGgWmabG1tuanINE0EQXAPwSlk6vU6zWaTdruNpmnud3eWsk6fnkwmMU2Tzc1NVFVFURSi0SjpdHp3iB89ehRRFOnt7XWHCNFolFwu58ar08AYhuEWNH6/H4/H8zlL71R4J5XJskwgECAWi5HP51lZWaHVajE8PEw8Hufw4cO7Q3zfvn0C4E5MNU2jq6uLarVKPp//nPA5hYgzcYlEIoRCIfcQHLd3SDvEPR4PHo+HfD5PPp9HEARGRkYIhUIMDQ0Ju0Ic4NixY3g8Hnp7e8lms8RiMcLhMNlsFk3TXHW3bZtgMOjmfScEvF6vW8s7qczxgGazSSAQ4M9//jOqqlKpVHj88cfx+XyMjY3t7ujp1KlTQrlcdicupVKJkZER1tbWuHfvHqqqEolEiMVitNtt6vW6q+iNRoNms4kkSfh8PtfC0WgURVHo7+9nc3OTjY0N3n33XQYGBkilUiSTSU6ePCnsKnGA559/nkqlwtGjR8nn8/j9fh5//HEsy+LWrVvMz8+j6zqhUAifz4dhGG7z0mq1UFUVXdeJRCIEg0F32loqlbh37x53796lq6sLRVGIRCJMTEzs2pT1c8Qfe+wxYWpqyv1j2WyWVCpFIpGgUqkwNzfHnTt33JTnDCUlSaK7u5v+/n66u7vxeDxun16r1SgUCly/fp3bt29z+vRpBgcHqdVqRKPRB2uh8LOf/cyemZlhYWGBYDBIJBIBoFgs0mw2CYfDxONxfD4fg4ODros7vXWhUKDT6bg5/v3332dlZYWhoSHS6TSnTp1i3759VKtVzpw5w1c9dvpfNymvvPKKff36dbLZLIqiuBVWs9mk0WhgWZbbn4fDYURRRNM0AoEAvb29+Hw+Go0Gly9fZmNjg4mJCcbGxti/f787mJyfn6darTI5OcmJEyeEB4J4sVj8wfT09L/Pzs6SzWbdpWEikXBJGoZBs9kkGo0iyzKmabrLhtXVVTKZDJubm5w9e5YjR44wMDDAd7/7XWF2dtZeXFzkyJEj7pZm//79pFIpenp6vhIP+D8f8NZbb9nr6+ssLy+ztbXl1t5er9fdhxeLRSRJoquri0qlQiaTYXt7m0gkwpkzZ5BlmbGxMV544QX3eR9//LE9Pz/PsWPHuHHjBouLi0iSxMDAAMePH2dqakrYVeIAKysr9u3bt1leXnZn65qmoeu6W9AYhkGj0aBYLGKaJocOHeLRRx8lkUgwOTnpjp4d5HI5++OPP6ZcLjM+Ps61a9dYWFig2Wzi9Xp5/vnnOX36tLCrxHfi1q1b9tLSkuv+9XrdbTEVRSEWi5FMJhkdHWV4eJiDBw/+j8/Y2tr6+WuvvfYdwzBIpVJIkoSqqszPz1Ov17lw4QJf+9rXziYSiau7TvxvoVQqpUul0n2n2/oibz+899579k9/+lPC4TCnTp3i61//Orlcjp/85CdomsbTTz/NiRMn/pvH/CMotrQfyF/GD8Xj8aX/7yEePnx4dnx8fPJ3v/sdiqIwODjI5OSkcOPGDfvNN9/kypUr5PN5MpmMPT4+/qWQ71Z835fZZSSTyeMrKyt2vV7n7t27vPrqq1y7ds1+8sknhT/+8Y/2pUuXuHnzJs1mk6WlJTudTn8p5EUeAAwPDwvPPfccBw4cYHV1lc+KJ3t0dPTVixcvcvDgQebn53n55ZeZmZn5h1fLalv/twfqTaRMJmP/4he/YH19nSNHjnDhwgVisRj37t3jnXfeYWFhgQMHDnD+/HmeeuopYc9b3MH4+LjwrW99C0EQuHXrFtPT09RqNfr7+zlx4gTpdJqNjQ3efPNN3njjDbtYLP5gV1X9y8bly5ft119/nUqlwsmTJzl9+jQej4ePPvqITz75hLW1NYLBIJOTk5w7d46+vr4vzEN+EImfO3dOuHr1qv3666/z3nvvoWkaZ8+e5YknnnBXU5qmcfPmTQzDYG1tzR4cHBT2vMUdTE9P25cuXaJQKDA5OcmFCxfQdZ3Z2VkWFhbY2NhAURRSqRTPPvssY2Njwp62uINvfvObwp/+9Cf7V7/6FTdu3KDT6XD+/HmmpqbcoaaqqszOzmIYxhfK9Q80cYCJiQlhbm7OFgSBubk5vF4vzzzzDBMTE+7CMRgMsrCwwEsvvcS1a9fsb3zjG8KedvWd+OSTT+y3336bubk5JicnOXnyJJZl8eGHH7K2tkahUKDRaLBv3z4uXrzI2bNnhT1tcQdHjx4VMpmM3el0WFxcJBQKMTU1xbFjx6hWqwC0221WVlb49a9/zY0bN+wnn3xS2BN5/O/J88899xzDw8NkMhnm5uaIx+M8/fTT9PT0YFkWg4ODrK6u8sorrzA/P2/veVffidXVVfvdd99lenqa8+fPMzo6iqZp/OEPf2BhYYFWq4Usy4yMjHDx4kX+luXlvUh8aGhImJmZsS3L4vr165imSSqVYt++fbTbbWq1GtVqlWq1yt27d7l//749Ojoq7HniAFNTU8LS0pJdLpe5e/cu6+vrRCIRhoeHqVarrK6uous66+vrZLNZSqVS+rP2eW8TB0in00I2m3XVHqCvr49YLEYikXBfRiqXy+Ryufs7Q1tkj6O/v1/49re/zTPPPIMgCMzPz7sraJ/Ph9/vd/d3pVIp/U9hcQdOnX7p0iU7k8kgiqK76lIUxX19dKerC/wTYXt7+8zm5uaV5eVlNjY2iMfj+P1+hoeH6enpmd35evg/FXEHm1uF7ZXltXgg6EcSPSR7u/+zO9H1ff6VsFWq/pyHeIiHeIh/FfwXjVDdIW9O2PAAAAAASUVORK5CYII=" alt="MEME Logo"> | |
| 9272 <h1>MEME</h1> | |
| 9273 <h2>Multiple Em for Motif Elicitation</h2> | |
| 9274 </div> | |
| 9275 <p> | |
| 9276 For further information on how to interpret these results please access | |
| 9277 <a href="https://meme-suite.org/meme/doc/meme.html">https://meme-suite.org/meme/doc/meme.html</a>. <br /> | |
| 9278 To get a copy of the MEME software please access | |
| 9279 <a href="https://meme-suite.org">https://meme-suite.org</a>. | |
| 9280 </p> | |
| 9281 <p id="citation"> <script>print_citation("citation", "MEME");</script></p> | |
| 9282 </div> | |
| 9283 <!-- navigation --> | |
| 9284 <div class="pad2"> | |
| 9285 <a class="jump" href="#motifs_sec">Discovered Motifs</a> | |
| 9286 | | |
| 9287 <a class="jump" href="#sites_sec">Motif Locations</a> | |
| 9288 | | |
| 9289 <a class="jump" href="#inputs_sec">Inputs & Settings</a> | |
| 9290 | | |
| 9291 <a class="jump" href="#info_sec">Program Information</a> | |
| 9292 | | |
| 9293 <a class="jump" href="meme.txt">Results in Text Format</a> <span id="results_txt_help"></span> | |
| 9294 | | |
| 9295 <a class="jump" href="meme.xml">Results in XML Format</a> <span id="results_xml_help"></span> | |
| 9296 <script> | |
| 9297 make_help_button($("results_txt_help"), "pop_results_txt"); | |
| 9298 make_help_button($("results_xml_help"), "pop_results_xml"); | |
| 9299 </script> | |
| 9300 </div> | |
| 9301 <!-- alert the user when their browser is not up to the task --> | |
| 9302 <noscript><h1 style="color:red">Javascript is required to view these results!</h1></noscript> | |
| 9303 <h1 id="html5_warning" style="color:red; display:none;">Your browser does not support canvas!</h1> | |
| 9304 <script> | |
| 9305 if (!window.HTMLCanvasElement) $("html5_warning").style.display = "block"; | |
| 9306 </script> | |
| 9307 <h2 class="mainh pad2" id="motifs_sec">Discovered Motifs</h2> | |
| 9308 <div id="motifs" class="box"> | |
| 9309 <p>Please wait... Loading...</p> | |
| 9310 <p>If the page has fully loaded and this message does not disappear then an error may have occurred.</p> | |
| 9311 </div> | |
| 9312 <h2 class="mainh pad2" id="sites_sec">Motif Locations</h2> | |
| 9313 <div id="blocks" class="box"> | |
| 9314 <p>Please wait... Loading...</p> | |
| 9315 <p>If the page has fully loaded and this message does not disappear then an error may have occurred.</p> | |
| 9316 </div> | |
| 9317 <h2 class="mainh pad2" id="inputs_sec">Inputs & Settings</h2> | |
| 9318 <div class="box"> | |
| 9319 <h4>Sequences</h4> | |
| 9320 <table id="seq_info" class="inputs"> | |
| 9321 <tr> | |
| 9322 <th>Role <div class="help" data-topic="pop_seq_role"></div></th> | |
| 9323 <th>Source <div class="help" data-topic="pop_seq_source"></div></th> | |
| 9324 <th class="col_psp">PSP Source <div class="help" data-topic="pop_psp_source"></div></th> | |
| 9325 <th>Alphabet <div class="help" data-topic="pop_seq_alph"></div></th> | |
| 9326 <th>Sequence Count <div class="help" data-topic="pop_seq_count"></div></th> | |
| 9327 <th>Total Size <div class="help" data-topic="pop_num_positions"></div></th> | |
| 9328 </tr> | |
| 9329 <tr> | |
| 9330 <td>Primary Sequences</td> | |
| 9331 <td id="ins_seq_source"></td> | |
| 9332 <td id="ins_seq_psp" class="col_psp"></td> | |
| 9333 <td id="ins_seq_alphabet"></td> | |
| 9334 <td id="ins_seq_count"></td> | |
| 9335 <td id="ins_num_positions"></td> | |
| 9336 </tr> | |
| 9337 <tr class="col_control"> | |
| 9338 <td class="col_control">Control Sequences</td> | |
| 9339 <td id="ins_control_source" class="col_control"></td> | |
| 9340 <td id="ins_control_psp" class="col_control col_psp"></td> | |
| 9341 <td id="ins_control_alphabet" class="col_control"></td> | |
| 9342 <td id="ins_control_count" class="col_control"></td> | |
| 9343 <td id="ins_control_positions" class="col_control"></td> | |
| 9344 </tr> | |
| 9345 </table> | |
| 9346 <script> | |
| 9347 { | |
| 9348 var db = data.sequence_db; | |
| 9349 $("ins_seq_source").innerHTML = db.primary_source; | |
| 9350 $("ins_seq_alphabet").innerHTML = current_alphabet.get_alphabet_name(); | |
| 9351 $("ins_seq_count").innerHTML = db.primary_count; | |
| 9352 $("ins_num_positions").innerHTML = db.primary_positions; | |
| 9353 $("ins_control_source").innerHTML = db.control_source; | |
| 9354 $("ins_control_alphabet").innerHTML = current_alphabet.get_alphabet_name(); | |
| 9355 $("ins_control_count").innerHTML = db.control_count; | |
| 9356 $("ins_control_positions").innerHTML = db.control_positions; | |
| 9357 if (db.psp_source) { | |
| 9358 $("ins_seq_psp").innerHTML = db.psp_source; | |
| 9359 } | |
| 9360 toggle_class($("seq_info"), "hide_psp", !(db.psp_source)); | |
| 9361 toggle_class($("seq_info"), "hide_control", (db.control_source == "--none--")); | |
| 9362 } | |
| 9363 </script> | |
| 9364 <h4>Background Model</h4> | |
| 9365 <span id="bg_source"></span> | |
| 9366 <span id="bg_order"></span> | |
| 9367 <span id="alpha_bg"></span> | |
| 9368 <script> | |
| 9369 { | |
| 9370 $("bg_source").appendChild(make_background_source("Source", data.background.source, false)); | |
| 9371 $("bg_order").innerHTML = " <b>Order:</b> " + data.background.order + | |
| 9372 (data.background.order>0 ? " (only order-0 shown)" : ""); | |
| 9373 $("alpha_bg").appendChild(make_alpha_bg_table(current_alphabet, data.sequence_db.freqs)); | |
| 9374 } | |
| 9375 </script> | |
| 9376 <h4>Other Settings</h4> | |
| 9377 <table id="tbl_settings" class="inputs hide_advanced"> | |
| 9378 <tr> | |
| 9379 <th>Motif Site Distribution</th> | |
| 9380 <td id="opt_mod"> | |
| 9381 <span class="mod_zoops">ZOOPS: Zero or one site per sequence</span> | |
| 9382 <span class="mod_oops">OOPS: Exactly one site per sequence</span> | |
| 9383 <span class="mod_anr">ANR: Any number of sites per sequence</span> | |
| 9384 </td> | |
| 9385 </tr> | |
| 9386 <tr> | |
| 9387 <th>Objective Function</th> | |
| 9388 <td id=opt_objfun></td> | |
| 9389 </tr> | |
| 9390 <tr> | |
| 9391 <th>Starting Point Function</th> | |
| 9392 <td id=opt_spfun></td> | |
| 9393 </tr> | |
| 9394 <tr> | |
| 9395 <th>Site Strand Handling</th> | |
| 9396 <td id="opt_strand"> | |
| 9397 <span class="strand_none">This alphabet only has one strand</span> | |
| 9398 <span class="strand_given">Sites must be on the given strand</span> | |
| 9399 <span class="strand_both">Sites may be on either strand</span> | |
| 9400 </td> | |
| 9401 </tr> | |
| 9402 <tr> | |
| 9403 <th>Maximum Number of Motifs</th> | |
| 9404 <td id="opt_nmotifs"></td> | |
| 9405 </tr> | |
| 9406 <tr> | |
| 9407 <th>Motif E-value Threshold</th> | |
| 9408 <td id="opt_evt"></td> | |
| 9409 </tr> | |
| 9410 <tr> | |
| 9411 <th>Minimum Motif Width</th> | |
| 9412 <td id="opt_minw"></td> | |
| 9413 </tr> | |
| 9414 <tr> | |
| 9415 <th>Maximum Motif Width</th> | |
| 9416 <td id="opt_maxw"></td> | |
| 9417 </tr> | |
| 9418 <tr> | |
| 9419 <th>Minimum Sites per Motif</th> | |
| 9420 <td id="opt_minsites"></td> | |
| 9421 </tr> | |
| 9422 <tr> | |
| 9423 <th>Maximum Sites per Motif</th> | |
| 9424 <td id="opt_maxsites"></td> | |
| 9425 </tr> | |
| 9426 <tr class="advanced"> | |
| 9427 <th>Bias on Number of Sites</th> | |
| 9428 <td id="opt_wnsites"></td> | |
| 9429 </tr> | |
| 9430 <tr class="advanced"> | |
| 9431 <th>Sequence Prior</th> | |
| 9432 <td id="opt_prior"> | |
| 9433 <span class="prior_dirichlet">Simple Dirichlet</span> | |
| 9434 <span class="prior_dmix">Dirichlet Mixture</span> | |
| 9435 <span class="prior_mega">Mega-weight Dirichlet Mixture</span> | |
| 9436 <span class="prior_megap">Mega-weight Dirichlet Mixture Plus</span> | |
| 9437 <span class="prior_addone">Add One</span> | |
| 9438 </td> | |
| 9439 </tr> | |
| 9440 <tr class="advanced"> | |
| 9441 <th>Sequence Prior Source</th> | |
| 9442 <td id="opt_prior_source"></td> | |
| 9443 </tr> | |
| 9444 <tr class="advanced"> | |
| 9445 <th>Sequence Prior Strength</th> | |
| 9446 <td id="opt_b"></td> | |
| 9447 </tr> | |
| 9448 <tr class="advanced"> | |
| 9449 <th>EM Starting Point Source</th> | |
| 9450 <td id="opt_substring"> | |
| 9451 <span class="substring_on">From substrings in input sequences</span> | |
| 9452 <span class="substring_off">From strings on command line (-cons)</span> | |
| 9453 </td> | |
| 9454 </tr> | |
| 9455 <tr class="advanced"> | |
| 9456 <th>EM Starting Point Map Type</th> | |
| 9457 <td id="opt_spmap"> | |
| 9458 <span class="spmap_uni">Uniform</span> | |
| 9459 <span class="spmap_pam">Point Accepted Mutation</span> | |
| 9460 </td> | |
| 9461 </tr> | |
| 9462 <tr class="advanced"> | |
| 9463 <th>EM Starting Point Fuzz</th> | |
| 9464 <td id="opt_spfuzz"></td> | |
| 9465 </tr> | |
| 9466 <tr class="advanced"> | |
| 9467 <th>EM Maximum Iterations</th> | |
| 9468 <td id="opt_maxiter"></td> | |
| 9469 </tr> | |
| 9470 <tr class="advanced"> | |
| 9471 <th>EM Improvement Threshold</th> | |
| 9472 <td id="opt_distance"></td> | |
| 9473 </tr> | |
| 9474 <tr class="advanced"> | |
| 9475 <th>Maximum Search Size</th> | |
| 9476 <td id="opt_searchsize"></td> | |
| 9477 </tr> | |
| 9478 <tr class="advanced"> | |
| 9479 <th>Maximum Number of Sites for E-values</th> | |
| 9480 <td id="opt_csites"></td> | |
| 9481 </tr> | |
| 9482 <tr class="advanced"> | |
| 9483 <th>Trim Gap Open Cost</th> | |
| 9484 <td id="opt_wg"></td> | |
| 9485 </tr> | |
| 9486 <tr class="advanced"> | |
| 9487 <th>Trim Gap Extend Cost</th> | |
| 9488 <td id="opt_ws"></td> | |
| 9489 </tr> | |
| 9490 <tr class="advanced"> | |
| 9491 <th>End Gap Treatment</th> | |
| 9492 <td id="opt_noendgaps"> | |
| 9493 <span class="noendgaps_on">No cost</span> | |
| 9494 <span class="noendgaps_off">Same cost as other gaps</span> | |
| 9495 </td> | |
| 9496 </tr> | |
| 9497 <tr> | |
| 9498 <td colspan="2" style="text-align: center"> | |
| 9499 <a href="javascript:toggle_class(document.getElementById('tbl_settings'), 'hide_advanced')"> | |
| 9500 <span class="show_more">Show Advanced Settings</span> | |
| 9501 <span class="show_less">Hide Advanced Settings</span> | |
| 9502 </a> | |
| 9503 </td> | |
| 9504 </tr> | |
| 9505 </table> | |
| 1 <script> | 9506 <script> |
| 2 { | 9507 { |
| 3 $("opt_mod").className = data.options.mod; | 9508 $("opt_mod").className = data.options.mod; |
| 4 $("opt_objfun").textContent = data.options.objfun; | 9509 $("opt_objfun").textContent = data.options.objfun; |
| 5 $("opt_spfun").textContent = data.options.spfun; | 9510 $("opt_spfun").textContent = data.options.spfun; |
