comparison test-data/dreme2.html @ 0:b386032d758d draft

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