comparison test-data/dreme_output_test1.html @ 13:4eb02864e5df draft

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