comparison test-data/dreme_output_test2.html @ 1:cb8a2242bf03 draft

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