comparison test-data/dreme1.html @ 12:5585f04eb317 draft

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