comparison test-data/meme_output_test2.html @ 8:8436beac5eed draft default tip

planemo upload for repository https://github.com/galaxyproject/tools-iuc/tree/master/tools/meme commit 89ee0af6e955ff964b7984d77ad536e0a9154278
author iuc
date Mon, 14 Jul 2025 21:32:53 +0000
parents 5f95d385a33c
children
comparison
equal deleted inserted replaced
7:47e7ae719765 8:8436beac5eed
1 * See http://stackoverflow.com/a/5450113/66387
2 * Does string multiplication like the perl x operator.
3 */
4 function string_mult(pattern, count) {
5 if (count < 1) return '';
6 var result = '';
7 while (count > 1) {
8 if (count & 1) result += pattern;
9 count >>= 1, pattern += pattern;
10 }
11 return result + pattern;
12 }
13
14 /*
15 * See http://stackoverflow.com/questions/814613/how-to-read-get-data-from-a-url-using-javascript
16 * Slightly modified with information from
17 * https://developer.mozilla.org/en/DOM/window.location
18 */
19 function parse_params() {
20 "use strict";
21 var search, queryStart, queryEnd, query, params, nvPairs, i, nv, n, v;
22 search = window.location.search;
23 queryStart = search.indexOf("?") + 1;
24 queryEnd = search.indexOf("#") + 1 || search.length + 1;
25 query = search.slice(queryStart, queryEnd - 1);
26
27 if (query === search || query === "") return {};
28
29 params = {};
30 nvPairs = query.replace(/\+/g, " ").split("&");
31
32 for (i = 0; i < nvPairs.length; i++) {
33 nv = nvPairs[i].split("=");
34 n = decodeURIComponent(nv[0]);
35 v = decodeURIComponent(nv[1]);
36 // allow a name to be used multiple times
37 // storing each value in the array
38 if (!(n in params)) {
39 params[n] = [];
40 }
41 params[n].push(nv.length === 2 ? v : null);
42 }
43 return params;
44 }
45
46 /*
47 * coords
48 *
49 * Calculates the x and y offset of an element.
50 * From http://www.quirksmode.org/js/findpos.html
51 * with alterations to take into account scrolling regions
52 */
53 function coords(elem) {
54 var myX = myY = 0;
55 if (elem.getBoundingClientRect) {
56 var rect;
57 rect = elem.getBoundingClientRect();
58 myX = rect.left + ((typeof window.pageXOffset !== "undefined") ?
59 window.pageXOffset : document.body.scrollLeft);
60 myY = rect.top + ((typeof window.pageYOffset !== "undefined") ?
61 window.pageYOffset : document.body.scrollTop);
62 } else {
63 // this fall back doesn't properly handle absolutely positioned elements
64 // inside a scrollable box
65 var node;
66 if (elem.offsetParent) {
67 // subtract all scrolling
68 node = elem;
69 do {
70 myX -= node.scrollLeft ? node.scrollLeft : 0;
71 myY -= node.scrollTop ? node.scrollTop : 0;
72 } while (node = node.parentNode);
73 // this will include the page scrolling (which is unwanted) so add it back on
74 myX += (typeof window.pageXOffset !== "undefined") ? window.pageXOffset : document.body.scrollLeft;
75 myY += (typeof window.pageYOffset !== "undefined") ? window.pageYOffset : document.body.scrollTop;
76 // sum up offsets
77 node = elem;
78 do {
79 myX += node.offsetLeft;
80 myY += node.offsetTop;
81 } while (node = node.offsetParent);
82 }
83 }
84 return [myX, myY];
85 }
86
87 /*
88 * position_popup
89 *
90 * Positions a popup relative to an anchor element.
91 *
92 * The available positions are:
93 * 0 - Centered below the anchor.
94 */
95 function position_popup(anchor, popup, position) {
96 "use strict";
97 var a_x, a_y, a_w, a_h, p_x, p_y, p_w, p_h;
98 var a_xy, spacer, margin, scrollbar, page_w;
99 // define constants
100 spacer = 5;
101 margin = 15;
102 scrollbar = 15;
103 // define the positions and widths
104 a_xy = coords(anchor);
105 a_x = a_xy[0];
106 a_y = a_xy[1];
107 a_w = anchor.offsetWidth;
108 a_h = anchor.offsetHeight;
109 p_w = popup.offsetWidth;
110 p_h = popup.offsetHeight;
111 page_w = null;
112 if (window.innerWidth) {
113 page_w = window.innerWidth;
114 } else if (document.body) {
115 page_w = document.body.clientWidth;
116 }
117 // check the position type is defined
118 if (typeof position !== "number") {
119 position = 0;
120 }
121 // calculate the popup position
122 switch (position) {
123 case 1:
124 p_x = a_x + a_w + spacer;
125 p_y = a_y + (a_h / 2) - (p_h / 2);
126 break;
127 case 0:
128 default:
129 p_x = a_x + (a_w / 2) - (p_w / 2);
130 p_y = a_y + a_h + spacer;
131 break;
132 }
133 // constrain the popup position
134 if (p_x < margin) {
135 p_x = margin;
136 } else if (page_w != null && (p_x + p_w) > (page_w - margin - scrollbar)) {
137 p_x = page_w - margin - scrollbar - p_w;
138 }
139 if (p_y < margin) {
140 p_y = margin;
141 }
142 // position the popup
143 popup.style.left = p_x + "px";
144 popup.style.top = p_y + "px";
145 }
146
147 function lookup_help_popup(popup_id) {
148 var _body, pop, info;
149 pop = document.getElementById(popup_id);
150 if (pop == null) {
151 _body = document.getElementsByTagName("body")[0];
152 pop = document.createElement("div");
153 pop.className = "pop_content";
154 pop.id = popup_id;
155 pop.style.backgroundColor = "#FFC";
156 pop.style.borderColor = "black";
157 info = document.createElement("p");
158 info.style.fontWeight = "bold";
159 info.appendChild(document.createTextNode("Error: No popup for topic \"" + popup_id + "\"."));
160 pop.appendChild(info);
161 // this might cause problems with the menu, but as this only happens
162 // when something is already wrong I don't think that's too much of a problem
163 _body.insertBefore(pop, _body.firstChild);
164 }
165 if (document.getElementsByTagName('body')[0].hasAttribute("data-autobtns")) {
166 if (!/\bauto_buttons\b/.test(pop.className)) {
167 pop.className += " auto_buttons";
168 var back_btn_sec = document.createElement("div");
169 back_btn_sec.className = "nested_only pop_back_sec";
170 var back_btn = document.createElement("span");
171 back_btn.className = "pop_back";
172 back_btn.appendChild(document.createTextNode("<< back"));
173 back_btn.addEventListener("click", function(e) {
174 help_return();
175 }, false);
176 back_btn_sec.appendChild(back_btn);
177 pop.insertBefore(back_btn_sec, pop.firstChild);
178 var close_btn_sec = document.createElement("div");
179 close_btn_sec.className = "pop_close_sec";
180 var close_btn = document.createElement("span");
181 close_btn.className = "pop_close";
182 close_btn.appendChild(document.createTextNode("close"));
183 close_btn.addEventListener("click", function(e) {
184 help_popup();
185 }, false);
186 close_btn_sec.appendChild(close_btn);
187 pop.appendChild(close_btn_sec);
188 }
189 }
190 return pop;
191 }
192
193 /*
194 * help_popup
195 *
196 * Moves around help pop-ups so they appear
197 * below an activator.
198 */
199 function help_popup(activator, popup_id) {
200 "use strict";
201 var pop;
202 // set default values
203 if (typeof help_popup.popup === "undefined") {
204 help_popup.popup = [];
205 }
206 if (typeof help_popup.activator === "undefined") {
207 help_popup.activator = null;
208 }
209 var last_pop = (help_popup.popup.length > 0 ? help_popup.popup[help_popup.popup.length - 1] : null);
210 if (typeof(activator) == "undefined") { // no activator so hide
211 if (last_pop != null) {
212 last_pop.style.display = 'none';
213 help_popup.popup = [];
214 }
215 return;
216 }
217 pop = lookup_help_popup(popup_id);
218 if (pop == last_pop) {
219 if (activator == help_popup.activator) {
220 //hide popup (as we've already shown it for the current help button)
221 last_pop.style.display = 'none';
222 help_popup.popup = [];
223 return; // toggling complete!
224 }
225 } else if (last_pop != null) {
226 //activating different popup so hide current one
227 last_pop.style.display = 'none';
228 }
229 help_popup.popup = [pop];
230 help_popup.activator = activator;
231 toggle_class(pop, "nested", false);
232 //must make the popup visible to measure it or it has zero width
233 pop.style.display = 'block';
234 position_popup(activator, pop);
235 }
236
237 /*
238 * help_refine
239 *
240 * Intended for links within a help popup. Stores a stack of state so
241 * you can go back.
242 */
243 function help_refine(popup_id) {
244 if (help_popup.popup == null || help_popup.popup.length == 0 || help_popup.activator == null) {
245 //throw new Error("Cannot refine a help popup when one is not shown!");
246 var pop = lookup_help_popup(popup_id);
247 var act_id = popup_id + '_act';
248 var activator = document.getElementById(act_id);
249 help_popup(activator, popup_id);
250 }
251 var pop = lookup_help_popup(popup_id);
252 var last_pop = help_popup.popup[help_popup.popup.length - 1];
253 if (pop == last_pop) return; // slightly odd, but no real cause for alarm
254 help_popup.popup.push(pop);
255 toggle_class(pop, "nested", true);
256 last_pop.style.display = "none";
257 //must make the popup visible to measure it or it has zero width
258 pop.style.display = "block";
259 position_popup(help_popup.activator, pop);
260 }
261
262 /*
263 * help_return
264 *
265 * Intended for links within a help popup. Stores a stack of state so
266 * you can go back.
267 */
268 function help_return() {
269 if (help_popup.popup == null || help_popup.popup.length == 0 || help_popup.activator == null) {
270 throw new Error("Can not return to a earlier help popup when one is not shown!");
271 }
272 var last_pop = help_popup.popup.pop();
273 last_pop.style.display = "none";
274 var pop = (help_popup.popup.length > 0 ? help_popup.popup[help_popup.popup.length - 1] : null);
275 if (pop != null) {
276 toggle_class(pop, "nested", help_popup.popup.length > 1);
277 pop.style.display = "block";
278 position_popup(help_popup.activator, pop);
279 } else {
280 help_popup.activator = null;
281 }
282 }
283
284 /*
285 * update_scroll_pad
286 *
287 * Creates padding at the bottom of the page to allow
288 * scrolling of anything into view.
289 */
290 function update_scroll_pad() {
291 var page, pad;
292 page = (document.compatMode === "CSS1Compat") ? document.documentElement : document.body;
293 pad = $("scrollpad");
294 if (pad === null) {
295 pad = document.createElement("div");
296 pad.id = 'scrollpad';
297 document.getElementsByTagName('body')[0].appendChild(pad);
298 }
299 pad.style.height = Math.abs(page.clientHeight - 100) + "px";
300 }
301
302 function substitute_classes(node, remove, add) {
303 "use strict";
304 var list, all, i, cls, classes;
305 list = node.className.split(/\s+/);
306 all = {};
307 for (i = 0; i < list.length; i++) {
308 if (list[i].length > 0) all[list[i]] = true;
309 }
310 for (i = 0; i < remove.length; i++) {
311 if (all.hasOwnProperty(remove[i])) {
312 delete all[remove[i]];
313 }
314 }
315 for (i = 0; i < add.length; i++) {
316 all[add[i]] = true;
317 }
318 classes = "";
319 for (cls in all) {
320 classes += cls + " ";
321 }
322 node.className = classes;
323 }
324
325 /*
326 * toggle_class
327 *
328 * Adds or removes a class from the node. If the parameter 'enabled' is not
329 * passed then the existence of the class will be toggled, otherwise it will be
330 * included if enabled is true.
331 */
332 function toggle_class(node, cls, enabled) {
333 var classes = node.className;
334 var list = classes.replace(/^\s+/, '').replace(/\s+$/, '').split(/\s+/);
335 var found = false;
336 for (var i = 0; i < list.length; i++) {
337 if (list[i] == cls) {
338 list.splice(i, 1);
339 i--;
340 found = true;
341 }
342 }
343 if (typeof enabled == "undefined") {
344 if (!found) list.push(cls);
345 } else {
346 if (enabled) list.push(cls);
347 }
348 node.className = list.join(" ");
349 }
350
351 /*
352 * find_child
353 *
354 * Searches child nodes in depth first order and returns the first it finds
355 * with the className specified.
356 * TODO replace with querySelector
357 */
358 function find_child(node, className) {
359 var pattern;
360 if (node == null || typeof node !== "object") {
361 return null;
362 }
363 if (typeof className === "string") {
364 pattern = new RegExp("\\b" + className + "\\b");
365 } else {
366 pattern = className;
367 }
368 if (node.nodeType == Node.ELEMENT_NODE &&
369 pattern.test(node.className)) {
370 return node;
371 } else {
372 var result = null;
373 for (var i = 0; i < node.childNodes.length; i++) {
374 result = find_child(node.childNodes[i], pattern);
375 if (result != null) break;
376 }
377 return result;
378 }
379 }
380
381 /*
382 * find_parent
383 *
384 * Searches parent nodes outwards from the node and returns the first it finds
385 * with the className specified.
386 */
387 function find_parent(node, className) {
388 var pattern;
389 pattern = new RegExp("\\b" + className + "\\b");
390 do {
391 if (node.nodeType == Node.ELEMENT_NODE &&
392 pattern.test(node.className)) {
393 return node;
394 }
395 } while (node = node.parentNode);
396 return null;
397 }
398
399 /*
400 * find_parent_tag
401 *
402 * Searches parent nodes outwards from the node and returns the first it finds
403 * with the tag name specified. HTML tags should be specified in upper case.
404 */
405 function find_parent_tag(node, tag_name) {
406 do {
407 if (node.nodeType == Node.ELEMENT_NODE && node.tagName == tag_name) {
408 return node;
409 }
410 } while (node = node.parentNode);
411 return null;
412 }
413
414 /*
415 * __toggle_help
416 *
417 * Uses the 'topic' property of the this object to
418 * toggle display of a help topic.
419 *
420 * This function is not intended to be called directly.
421 */
422 function __toggle_help(e) {
423 if (!e) e = window.event;
424 if (e.type === "keydown") {
425 if (e.keyCode !== 13 && e.keyCode !== 32) {
426 return;
427 }
428 // stop a submit or something like that
429 e.preventDefault();
430 }
431
432 help_popup(this, this.getAttribute("data-topic"));
433 }
434
435 function setup_help_button(button) {
436 "use strict";
437 var topic;
438 if (button.hasAttribute("data-topic")) {
439 topic = button.getAttribute("data-topic");
440 if (document.getElementById(topic) != null) {
441 button.tabIndex = "0"; // make keyboard selectable
442 button.addEventListener("click", function() {
443 help_popup(button, topic);
444 }, false);
445 button.addEventListener("keydown", function(e) {
446 // toggle only on Enter or Spacebar, let other keys do their thing
447 if (e.keyCode !== 13 && e.keyCode !== 32) return;
448 // stop a submit or something like that
449 e.preventDefault();
450 help_popup(button, topic);
451 }, false);
452 } else {
453 button.style.visibility = "hidden";
454 }
455 }
456 button.className += " active";
457 }
458
459 /*
460 * help_button
461 *
462 * Makes a help button for the passed topic.
463 */
464 function help_button(topic) {
465 var btn = document.createElement("div");
466 btn.className = "help";
467 btn.setAttribute("data-topic", topic);
468 setup_help_button(btn);
469 return btn;
470 }
471
472 /*
473 * prepare_download
474 *
475 * Sets the attributes of a link to setup a file download using the given content.
476 * If no link is provided then create one and click it.
477 */
478 function prepare_download(content, mimetype, filename, link) {
479 "use strict";
480 // if no link is provided then create one and click it
481 var click_link = false;
482 if (!link) {
483 link = document.createElement("a");
484 click_link = true;
485 }
486 try {
487 // Use a BLOB to convert the text into a data URL.
488 // We could do this manually with a base 64 conversion.
489 // This will only be supported on modern browsers,
490 // hence the try block.
491 var blob = new Blob([content], {type: mimetype});
492 var reader = new FileReader();
493 reader.onloadend = function() {
494 // If we're lucky the browser will also support the download
495 // attribute which will let us suggest a file name to save the link.
496 // Otherwise it is likely that the filename will be unintelligible.
497 link.setAttribute("download", filename);
498 link.href = reader.result;
499 if (click_link) {
500 // must add the link to click it
501 document.body.appendChild(link);
502 link.click();
503 document.body.removeChild(link);
504 }
505 }
506 reader.readAsDataURL(blob);
507 } catch (error) {
508 if (console && console.log) console.log(error);
509 // probably an old browser
510 link.href = "";
511 link.visible = false;
512 }
513 }
514
515 /*
516 * add_cell
517 *
518 * Add a cell to the table row.
519 */
520 function add_cell(row, node, cls, click_action) {
521 var cell = row.insertCell(row.cells.length);
522 if (node) cell.appendChild(node);
523 if (cls && cls !== "") cell.className = cls;
524 if (click_action) cell.addEventListener("click", click_action, false);
525 }
526
527 /*
528 * add_header_cell
529 *
530 * Add a header cell to the table row.
531 */
532 function add_header_cell(row, node, help_topic, cls, colspan, is_new) {
533 var th = document.createElement("th");
534 if (node) th.appendChild(node);
535 if (help_topic && help_topic !== "") th.appendChild(help_button(help_topic));
536 if (is_new && is_new !== "") {
537 var br = document.createElement("span");
538 br.innerHTML = "<br>";
539 th.appendChild(br);
540 var new_icon = document.createElement("img");
541 new_icon.src = new_icon_src;
542 new_icon.alt = "NEW";
543 th.appendChild(new_icon);
544 }
545 if (cls && cls !== "") th.className = cls;
546 if (typeof colspan == "number" && colspan > 1) th.colSpan = colspan;
547 row.appendChild(th);
548 }
549
550 /*
551 * add_text_cell
552 *
553 * Add a text cell to the table row.
554 */
555 function add_text_cell(row, text, cls, action) {
556 var node = null;
557 if (typeof(text) != 'undefined') node = document.createTextNode(text);
558 add_cell(row, node, cls, action);
559 }
560
561 /*
562 * add_text_header_cell
563 *
564 * Add a text header cell to the table row.
565 */
566 function add_text_header_cell(row, text, help_topic, cls, action, colspan, is_new) {
567 var node = null;
568 if (typeof(text) != 'undefined') {
569 var nbsp = (help_topic ? "\u00A0" : "");
570 var str = "" + text;
571 var parts = str.split(/\n/);
572 if (parts.length === 1) {
573 if (action) {
574 node = document.createElement("span");
575 node.appendChild(document.createTextNode(str + nbsp));
576 } else {
577 node = document.createTextNode(str + nbsp);
578 }
579 } else {
580 node = document.createElement("span");
581 for (var i = 0; i < parts.length; i++) {
582 if (i !== 0) {
583 node.appendChild(document.createElement("br"));
584 }
585 node.appendChild(document.createTextNode(parts[i]));
586 }
587 }
588 if (action) {
589 node.addEventListener("click", action, false);
590 node.style.cursor = "pointer";
591 }
592 }
593 add_header_cell(row, node, help_topic, cls, colspan, is_new);
594 }
595
596 function setup_help() {
597 "use strict";
598 var help_buttons, i;
599 help_buttons = document.querySelectorAll(".help:not(.active)");
600 for (i = 0; i < help_buttons.length; i++) {
601 setup_help_button(help_buttons[i]);
602 }
603 }
604
605 function setup_scrollpad() {
606 "use strict";
607 if (document.getElementsByTagName('body')[0].hasAttribute("data-scrollpad") && document.getElementById("scrollpad") == null) {
608 window.addEventListener("resize", update_scroll_pad, false);
609 update_scroll_pad();
610 }
611 }
612
613 // anon function to avoid polluting global scope
614 (function() {
615 "use strict";
616 window.addEventListener("load", function load(evt) {
617 window.removeEventListener("load", load, false);
618 setup_help();
619 setup_scrollpad();
620 }, false);
621 })();
622
623 /*
624 * make_link
625 *
626 * Creates a text node and if a URL is specified it surrounds it with a link.
627 * If the URL doesn't begin with "http://" it automatically adds it, as
628 * relative links don't make much sense in this context.
629 */
630 function make_link(text, url) {
631 var textNode = null;
632 var link = null;
633 if (typeof text !== "undefined" && text !== null) textNode = document.createTextNode(text);
634 if (typeof url === "string") {
635 if (url.indexOf("//") == -1) {
636 url = "http://" + url;
637 }
638 link = document.createElement('a');
639 link.href = url;
640 if (textNode) link.appendChild(textNode);
641 return link;
642 }
643 return textNode;
644 }
645
646 //
647 // Function to create an HTML paragraph describing the
648 // MEME Suite background model source.
649 //
650 function make_background_source(title, source, text) {
651 var paraNode = document.createElement("P");
652 var titleNode = document.createElement("B");
653 var textNode1 = document.createTextNode("\u00A0\u00A0\u00A0\u00A0" + title + ": ");
654 titleNode.appendChild(textNode1);
655 var source_text = ((source == "--motif--") ? "the (first) motif file" : (source == "--nrdb--") ? "an old version of the NCBI non-redundant database" : (source == "--uniform--") ? "the uniform model" : (source == "--query--") ? "the query file" : (source == "--sequences--") ? "built from the (primary) sequences" : (source == "--control--") ? "built from the control (negative) sequences" : ((source == "--negatives--") ? "built from the negative (control) sequences" : "the file '" + source + "'"));
656 if (text) { return source_text; }
657 var textNode2 = document.createTextNode(source_text);
658 paraNode.appendChild(titleNode);
659 paraNode.appendChild(textNode2);
660 return paraNode;
661 }
662
663 // Function to create a help button
664 function make_help_button(container, help_topic) {
665 container.appendChild(help_button(help_topic));
666 }
667
668 // Function to toggle display.
669 function change_display(id) {
670 var element=document.getElementById(id);
671 element.style.display=(element.style.display=='none') ? element.style.display='inline' : element.style.display='none';
672 }
673 </script>
674 <script>
675 //
676 // return true if any part of the passed element is visible in the viewport
677 //
678 function element_in_viewport(elem) {
679 var rect;
680 try {
681 rect = elem.getBoundingClientRect();
682 } catch (e) {
683 return false;
684 }
685 return (
686 rect.top < (window.innerHeight || document.body.clientHeight) &&
687 rect.bottom > 0 &&
688 rect.left < (window.innerWidth || document.body.clientWidth) &&
689 rect.right > 0
690 );
691 }
692
693 //
694 // Functions to delay a drawing task until it is required or it would not lag the display to do so
695 //
696
697 // a list of items still to be drawn
698 var drawable_list = [];
699 // the delay between drawing objects that are not currently visible
700 var draw_delay = 1;
701 // the delay after a user interaction
702 var user_delay = 300;
703 // the delay after a user has stopped scrolling and is viewing the stuff drawn on the current page
704 var stop_delay = 300;
705 // the timer handle; allows resetting of the timer after user interactions
706 var draw_timer = null;
707
708 //
709 // Drawable
710 //
711 // elem - a page element which defines the position on the page that drawing is to be done
712 // task - an object with the method run which takes care of painting the object
713 //
714 var Drawable = function(elem, task) {
715 this.elem = elem;
716 this.task = task;
717 }
718
719 //
720 // Drawable.is_visible
721 //
722 // Determines if the element is visible in the viewport
723 //
724 Drawable.prototype.is_visible = function() {
725 return element_in_viewport(this.elem);
726 }
727
728 //
729 // Drawable.run
730 //
731 // Run the task held by the drawable
732 Drawable.prototype.run = function() {
733 if (this.task) this.task.run();
734 this.task = null;
735 }
736
737 //
738 // Drawable.run
739 //
740 // Run the task iff visible
741 // returns true if the task ran or has already run
742 Drawable.prototype.run_visible = function() {
743 if (this.task) {
744 if (element_in_viewport(this.elem)) {
745 this.task.run();
746 this.task = null;
747 return true;
748 }
749 return false;
750 } else {
751 return true;
752 }
753 }
754
755 //
756 // draw_on_screen
757 //
758 // Checks each drawable object and draws those on screen.
759 //
760 function draw_on_screen() {
761 var found = false;
762 for (var i = 0; i < drawable_list.length; i++) {
763 if (drawable_list[i].run_visible()) {
764 drawable_list.splice(i--, 1);
765 found = true;
766 }
767 }
768 return found;
769 }
770
771 //
772 // process_draw_tasks
773 //
774 // Called on a delay to process the next available
775 // draw task.
776 //
777 function process_draw_tasks() {
778 var delay = draw_delay;
779 draw_timer = null;
780 if (drawable_list.length == 0) return; //no more tasks
781 if (draw_on_screen()) {
782 delay = stop_delay; //give the user a chance to scroll
783 } else {
784 //get next task
785 var drawable = drawable_list.shift();
786 drawable.task.run();
787 }
788 //allow UI updates between tasks
789 draw_timer = window.setTimeout("process_draw_tasks()", delay);
790 }
791
792 //
793 // delayed_process_draw_tasks
794 //
795 // Call process_draw_tasks after a short delay.
796 // The delay serves to group multiple redundant events.
797 // Should be set as event handler for onscroll and onresize.
798 //
799 function delayed_process_draw_tasks() {
800 //reset the timer
801 if (drawable_list.length > 0) {
802 if (draw_timer != null) clearTimeout(draw_timer);
803 draw_timer = window.setTimeout("process_draw_tasks()", user_delay);
804 }
805 }
806
807 //
808 // add_draw_task
809 //
810 // Add a drawing task to be called immediately if it is
811 // visible, or to be called on a delay to reduce stuttering
812 // effect on the web browser.
813 function add_draw_task(elem, task) {
814 drawable = new Drawable(elem, task);
815 if (drawable.is_visible()) {
816 task.run();
817 } else {
818 drawable_list.push(drawable);
819 //reset timer
820 if (draw_timer != null) clearTimeout(draw_timer);
821 draw_timer = window.setTimeout("process_draw_tasks()", user_delay);
822 }
823 }
824
825 </script>
826 <script>
827 function motif_logo_template(inputs) {
828 function _input(name) {
829 if (typeof inputs[name] === "undefined") {
830 throw new Error("Missing template variable: " + name);
831 }
832 return inputs[name];
833 }
834 return (
835 "%!PS-Adobe-3.0 EPSF-3.0\n" +
836 "%%Title: Sequence Logo : " + _input("TITLE") + "\n" +
837 "%%Creator: " + _input("CREATOR") + "\n" +
838 "%%CreationDate: " + _input("CREATIONDATE") + "\n" +
839 "%%BoundingBox: 0 0 " + _input("BOUNDINGWIDTH") + " " + _input("BOUNDINGHEIGHT") + " \n" +
840 "%%Pages: 0\n" +
841 "%%DocumentFonts: \n" +
842 "%%EndComments\n" +
843 "\n" +
844 "% ---- CONSTANTS ----\n" +
845 "\/cmfactor 72 2.54 div def % defines points -> cm conversion\n" +
846 "\/cm {cmfactor mul} bind def % defines centimeters\n" +
847 "\n" +
848 "% ---- VARIABLES ----\n" +
849 "\n" +
850 "% NA = Nucleic Acid, AA = Amino Acid\n" +
851 "\/logoType (" + _input("LOGOTYPE") + ") def \n" +
852 "\n" +
853 "\/logoTitle (" + _input("TITLE") + ") def\n" +
854 "\n" +
855 "% Dimensions in cm\n" +
856 "\/logoWidth " + _input("LOGOWIDTH") + " cm def\n" +
857 "\/logoHeight " + _input("LOGOLINEHEIGHT") + " cm def\n" +
858 "\/totalHeight " + _input("LOGOHEIGHT") + " cm def\n" +
859 "\n" +
860 "\/yaxis " + _input("YAXIS") + " def\n" +
861 "\/yaxisLabel (" + _input("YAXISLABEL") + ") def\n" +
862 "\/yaxisBits " + _input("BARBITS") + " def % bits\n" +
863 "\/yaxisTicBits " + _input("TICBITS") + " def\n" +
864 "\n" +
865 "\/xaxis " + _input("NUMBERING") + " def\n" +
866 "\/xaxisLabel (" + _input("XAXISLABEL") + ") def\n" +
867 "\/showEnds (" + _input("SHOWENDS") + ") def \n" +
868 "\n" +
869 "\/showFineprint true def\n" +
870 "\/fineprint (" + _input("FINEPRINT") + ") def\n" +
871 "\n" +
872 "\/charsPerLine " + _input("CHARSPERLINE") + " def\n" +
873 "\n" +
874 "\/showingBox " + _input("SHOWINGBOX") + " def \n" +
875 "\/shrinking false def % true falses\n" +
876 "\/shrink 1.0 def\n" +
877 "\/outline " + _input("OUTLINE") + " def\n" +
878 "\n" +
879 "\/IbeamFraction " + _input("ERRORBARFRACTION") + " def\n" +
880 "\/IbeamGray 0.50 def\n" +
881 "\/IbeamLineWidth 0.5 def\n" +
882 "\n" +
883 "\/fontsize " + _input("FONTSIZE") + " def\n" +
884 "\/titleFontsize " + _input("TITLEFONTSIZE") + " def\n" +
885 "\/smallFontsize " + _input("SMALLFONTSIZE") + " def\n" +
886 "\n" +
887 "\/topMargin " + _input("TOPMARGIN") + " cm def\n" +
888 "\/bottomMargin " + _input("BOTTOMMARGIN") + " cm def\n" +
889 "\n" +
890 "\/defaultColor [0 0 0] def \n" +
891 "\n" +
892 _input("COLORDICT") + "\n" +
893 "\n" +
894 "\/colorDict fullColourDict def\n" +
895 "\n" +
896 "% ---- DERIVED PARAMETERS ----\n" +
897 "\n" +
898 "\/leftMargin\n" +
899 " fontsize 3.5 mul\n" +
900 "\n" +
901 "def \n" +
902 "\n" +
903 "\/rightMargin \n" +
904 " %Add extra room if showing ends\n" +
905 " showEnds (false) eq { fontsize}{fontsize 1.5 mul} ifelse\n" +
906 "def\n" +
907 "\n" +
908 "\/yaxisHeight \n" +
909 " logoHeight \n" +
910 " bottomMargin sub \n" +
911 " topMargin sub\n" +
912 "def\n" +
913 "\n" +
914 "\/ticWidth fontsize 2 div def\n" +
915 "\n" +
916 "\/pointsPerBit yaxisHeight yaxisBits div def\n" +
917 "\n" +
918 "\/stackMargin 1 def\n" +
919 "\n" +
920 "% Do not add space aroung characters if characters are boxed\n" +
921 "\/charRightMargin \n" +
922 " showingBox { 0.0 } {stackMargin} ifelse\n" +
923 "def\n" +
924 "\n" +
925 "\/charTopMargin \n" +
926 " showingBox { 0.0 } {stackMargin} ifelse\n" +
927 "def\n" +
928 "\n" +
929 "\/charWidth\n" +
930 " logoWidth\n" +
931 " leftMargin sub\n" +
932 " rightMargin sub\n" +
933 " charsPerLine div\n" +
934 " charRightMargin sub\n" +
935 "def\n" +
936 "\n" +
937 "\/charWidth4 charWidth 4 div def\n" +
938 "\/charWidth2 charWidth 2 div def\n" +
939 "\n" +
940 "\/stackWidth \n" +
941 " charWidth charRightMargin add\n" +
942 "def\n" +
943 " \n" +
944 "\/numberFontsize \n" +
945 " fontsize charWidth lt {fontsize}{charWidth} ifelse\n" +
946 "def\n" +
947 "\n" +
948 "% movements to place 5'\/N and 3'\/C symbols\n" +
949 "\/leftEndDeltaX fontsize neg def\n" +
950 "\/leftEndDeltaY fontsize 1.5 mul neg def\n" +
951 "\/rightEndDeltaX fontsize 0.25 mul def\n" +
952 "\/rightEndDeltaY leftEndDeltaY def\n" +
953 "\n" +
954 "% Outline width is proporional to charWidth, \n" +
955 "% but no less that 1 point\n" +
956 "\/outlinewidth \n" +
957 " charWidth 32 div dup 1 gt {}{pop 1} ifelse\n" +
958 "def\n" +
959 "\n" +
960 "\n" +
961 "% ---- PROCEDURES ----\n" +
962 "\n" +
963 "\/StartLogo { \n" +
964 " % Save state\n" +
965 " save \n" +
966 " gsave \n" +
967 "\n" +
968 " % Print Logo Title, top center \n" +
969 " gsave \n" +
970 " SetStringFont\n" +
971 "\n" +
972 " logoWidth 2 div\n" +
973 " logoTitle\n" +
974 " stringwidth pop 2 div sub\n" +
975 " totalHeight\n" +
976 " titleFontsize sub\n" +
977 " moveto\n" +
978 "\n" +
979 " logoTitle\n" +
980 " show\n" +
981 " grestore\n" +
982 "\n" +
983 " % Print X-axis label, bottom center\n" +
984 " gsave\n" +
985 " SetStringFont\n" +
986 "\n" +
987 " logoWidth 2 div\n" +
988 " xaxisLabel\n" +
989 " stringwidth pop 2 div sub\n" +
990 " 0\n" +
991 " titleFontsize 3 div\n" +
992 " add\n" +
993 " moveto\n" +
994 "\n" +
995 " xaxisLabel\n" +
996 " show\n" +
997 " grestore\n" +
998 "\n" +
999 " % Show Fine Print\n" +
1000 " showFineprint {\n" +
1001 " gsave\n" +
1002 " SetSmallFont\n" +
1003 " logoWidth\n" +
1004 " fineprint stringwidth pop sub\n" +
1005 " smallFontsize sub\n" +
1006 " smallFontsize 3 div\n" +
1007 " moveto\n" +
1008 " \n" +
1009 " fineprint show\n" +
1010 " grestore\n" +
1011 " } if\n" +
1012 "\n" +
1013 " % Move to lower left corner of last line, first stack\n" +
1014 " leftMargin bottomMargin translate\n" +
1015 "\n" +
1016 " % Move above first line ready for StartLine \n" +
1017 " 0 totalHeight translate\n" +
1018 "\n" +
1019 " SetLogoFont\n" +
1020 "} bind def\n" +
1021 "\n" +
1022 "\/EndLogo { \n" +
1023 " grestore \n" +
1024 " showpage \n" +
1025 " restore \n" +
1026 "} bind def\n" +
1027 "\n" +
1028 "\n" +
1029 "\/StartLine { \n" +
1030 " % move down to the bottom of the line:\n" +
1031 " 0 logoHeight neg translate\n" +
1032 " \n" +
1033 " gsave \n" +
1034 " yaxis { MakeYaxis } if\n" +
1035 " xaxis { showEnds (true) eq {ShowLeftEnd} if } if\n" +
1036 "} bind def\n" +
1037 "\n" +
1038 "\/EndLine{ \n" +
1039 " xaxis { showEnds (true) eq {ShowRightEnd} if } if\n" +
1040 " grestore \n" +
1041 "} bind def\n" +
1042 "\n" +
1043 "\n" +
1044 "\/MakeYaxis {\n" +
1045 " gsave \n" +
1046 " stackMargin neg 0 translate\n" +
1047 " ShowYaxisBar\n" +
1048 " ShowYaxisLabel\n" +
1049 " grestore\n" +
1050 "} bind def\n" +
1051 "\n" +
1052 "\n" +
1053 "\/ShowYaxisBar { \n" +
1054 " gsave \n" +
1055 " SetStringFont\n" +
1056 "\n" +
1057 " \/str 10 string def % string to hold number \n" +
1058 " \/smallgap stackMargin 2 div def\n" +
1059 "\n" +
1060 " % Draw first tic and bar\n" +
1061 " gsave \n" +
1062 " ticWidth neg 0 moveto \n" +
1063 " ticWidth 0 rlineto \n" +
1064 " 0 yaxisHeight rlineto\n" +
1065 " stroke\n" +
1066 " grestore\n" +
1067 "\n" +
1068 " \n" +
1069 " % Draw the tics\n" +
1070 " % initial increment limit proc for\n" +
1071 " 0 yaxisTicBits yaxisBits abs %cvi\n" +
1072 " {\/loopnumber exch def\n" +
1073 "\n" +
1074 " % convert the number coming from the loop to a string\n" +
1075 " % and find its width\n" +
1076 " loopnumber 10 str cvrs\n" +
1077 " \/stringnumber exch def % string representing the number\n" +
1078 "\n" +
1079 " stringnumber stringwidth pop\n" +
1080 " \/numberwidth exch def % width of number to show\n" +
1081 "\n" +
1082 " \/halfnumberheight\n" +
1083 " stringnumber CharBoxHeight 2 div\n" +
1084 " def\n" +
1085 "\n" +
1086 " numberwidth % move back width of number\n" +
1087 " neg loopnumber pointsPerBit mul % shift on y axis\n" +
1088 " halfnumberheight sub % down half the digit\n" +
1089 "\n" +
1090 " moveto % move back the width of the string\n" +
1091 "\n" +
1092 " ticWidth neg smallgap sub % Move back a bit more \n" +
1093 " 0 rmoveto % move back the width of the tic \n" +
1094 "\n" +
1095 " stringnumber show\n" +
1096 " smallgap 0 rmoveto % Make a small gap \n" +
1097 "\n" +
1098 " % now show the tic mark\n" +
1099 " 0 halfnumberheight rmoveto % shift up again\n" +
1100 " ticWidth 0 rlineto\n" +
1101 " stroke\n" +
1102 " } for\n" +
1103 " grestore\n" +
1104 "} bind def\n" +
1105 "\n" +
1106 "\/ShowYaxisLabel {\n" +
1107 " gsave\n" +
1108 " SetStringFont\n" +
1109 "\n" +
1110 " % How far we move left depends on the size of\n" +
1111 " % the tic labels.\n" +
1112 " \/str 10 string def % string to hold number \n" +
1113 " yaxisBits yaxisTicBits div cvi yaxisTicBits mul \n" +
1114 " str cvs stringwidth pop\n" +
1115 " ticWidth 1.5 mul add neg \n" +
1116 "\n" +
1117 "\n" +
1118 " yaxisHeight\n" +
1119 " yaxisLabel stringwidth pop\n" +
1120 " sub 2 div\n" +
1121 "\n" +
1122 " translate\n" +
1123 " 90 rotate\n" +
1124 " 0 0 moveto\n" +
1125 " yaxisLabel show\n" +
1126 " grestore\n" +
1127 "} bind def\n" +
1128 "\n" +
1129 "\n" +
1130 "\/StartStack { % <stackNumber> startstack\n" +
1131 " xaxis {MakeNumber}{pop} ifelse\n" +
1132 " gsave\n" +
1133 "} bind def\n" +
1134 "\n" +
1135 "\/EndStack {\n" +
1136 " grestore\n" +
1137 " stackWidth 0 translate\n" +
1138 "} bind def\n" +
1139 "\n" +
1140 "\n" +
1141 "% Draw a character whose height is proportional to symbol bits\n" +
1142 "\/MakeSymbol{ % charbits character MakeSymbol\n" +
1143 " gsave\n" +
1144 " \/char exch def\n" +
1145 " \/bits exch def\n" +
1146 "\n" +
1147 " \/bitsHeight \n" +
1148 " bits pointsPerBit mul \n" +
1149 " def\n" +
1150 "\n" +
1151 " \/charHeight \n" +
1152 " bitsHeight charTopMargin sub\n" +
1153 " dup \n" +
1154 " 0.0 gt {}{pop 0.0} ifelse % if neg replace with zero \n" +
1155 " def \n" +
1156 " \n" +
1157 " charHeight 0.0 gt {\n" +
1158 " char SetColor\n" +
1159 " charWidth charHeight char ShowChar\n" +
1160 "\n" +
1161 " showingBox { % Unfilled box\n" +
1162 " 0 0 charWidth charHeight false ShowBox\n" +
1163 " } if\n" +
1164 "\n" +
1165 "\n" +
1166 " } if\n" +
1167 "\n" +
1168 " grestore\n" +
1169 "\n" +
1170 " 0 bitsHeight translate \n" +
1171 "} bind def\n" +
1172 "\n" +
1173 "\n" +
1174 "\/ShowChar { % <width> <height> <char> ShowChar\n" +
1175 " gsave\n" +
1176 " \/tc exch def % The character\n" +
1177 " \/ysize exch def % the y size of the character\n" +
1178 " \/xsize exch def % the x size of the character\n" +
1179 "\n" +
1180 " \/xmulfactor 1 def \n" +
1181 " \/ymulfactor 1 def\n" +
1182 " \/limmulfactor 0.01 def\n" +
1183 " \/drawable true def\n" +
1184 "\n" +
1185 " \n" +
1186 " % if ysize is negative, make everything upside down!\n" +
1187 " ysize 0 lt {\n" +
1188 " % put ysize normal in this orientation\n" +
1189 " \/ysize ysize abs def\n" +
1190 " xsize ysize translate\n" +
1191 " 180 rotate\n" +
1192 " } if\n" +
1193 "\n" +
1194 " shrinking {\n" +
1195 " xsize 1 shrink sub 2 div mul\n" +
1196 " ysize 1 shrink sub 2 div mul translate \n" +
1197 "\n" +
1198 " shrink shrink scale\n" +
1199 " } if\n" +
1200 "\n" +
1201 " % Calculate the font scaling factors\n" +
1202 " % Loop twice to catch small correction due to first scaling\n" +
1203 " 2 {\n" +
1204 " gsave\n" +
1205 " xmulfactor ymulfactor scale\n" +
1206 " \n" +
1207 " ysize % desired size of character in points\n" +
1208 " tc CharBoxHeight \n" +
1209 " dup 0.0 ne {\n" +
1210 " div % factor by which to scale up the character\n" +
1211 " \/ymulfactor exch def\n" +
1212 " } % end if\n" +
1213 " {pop pop}\n" +
1214 " ifelse\n" +
1215 "\n" +
1216 " xsize % desired size of character in points\n" +
1217 " tc CharBoxWidth \n" +
1218 " dup 0.0 ne {\n" +
1219 " div % factor by which to scale up the character\n" +
1220 " \/xmulfactor exch def\n" +
1221 " } % end if\n" +
1222 " {pop pop}\n" +
1223 " ifelse\n" +
1224 " grestore\n" +
1225 " % if the multiplication factors get too small we need to avoid a crash\n" +
1226 " xmulfactor limmulfactor lt {\n" +
1227 " \/xmulfactor 1 def\n" +
1228 " \/drawable false def\n" +
1229 " } if\n" +
1230 " ymulfactor limmulfactor lt {\n" +
1231 " \/ymulfactor 1 def\n" +
1232 " \/drawable false def\n" +
1233 " } if\n" +
1234 " } repeat\n" +
1235 "\n" +
1236 " % Adjust horizontal position if the symbol is an I\n" +
1237 " tc (I) eq {\n" +
1238 " charWidth 2 div % half of requested character width\n" +
1239 " tc CharBoxWidth 2 div % half of the actual character\n" +
1240 " sub 0 translate\n" +
1241 " % Avoid x scaling for I \n" +
1242 " \/xmulfactor 1 def \n" +
1243 " } if\n" +
1244 "\n" +
1245 "\n" +
1246 " % ---- Finally, draw the character\n" +
1247 " drawable { \n" +
1248 " newpath\n" +
1249 " xmulfactor ymulfactor scale\n" +
1250 "\n" +
1251 " % Move lower left corner of character to start point\n" +
1252 " tc CharBox pop pop % llx lly : Lower left corner\n" +
1253 " exch neg exch neg\n" +
1254 " moveto\n" +
1255 "\n" +
1256 " outline { % outline characters:\n" +
1257 " outlinewidth setlinewidth\n" +
1258 " tc true charpath\n" +
1259 " gsave 1 setgray fill grestore\n" +
1260 " clip stroke\n" +
1261 " } { % regular characters\n" +
1262 " tc show\n" +
1263 " } ifelse\n" +
1264 " } if\n" +
1265 "\n" +
1266 " grestore\n" +
1267 "} bind def\n" +
1268 "\n" +
1269 "\n" +
1270 "\/ShowBox { % x1 y1 x2 y2 filled ShowBox\n" +
1271 " gsave\n" +
1272 " \/filled exch def \n" +
1273 " \/y2 exch def\n" +
1274 " \/x2 exch def\n" +
1275 " \/y1 exch def\n" +
1276 " \/x1 exch def\n" +
1277 " newpath\n" +
1278 " x1 y1 moveto\n" +
1279 " x2 y1 lineto\n" +
1280 " x2 y2 lineto\n" +
1281 " x1 y2 lineto\n" +
1282 " closepath\n" +
1283 "\n" +
1284 " clip\n" +
1285 " \n" +
1286 " filled {\n" +
1287 " fill\n" +
1288 " }{ \n" +
1289 " 0 setgray stroke \n" +
1290 " } ifelse\n" +
1291 "\n" +
1292 " grestore\n" +
1293 "} bind def\n" +
1294 "\n" +
1295 "\n" +
1296 "\/MakeNumber { % number MakeNumber\n" +
1297 " gsave\n" +
1298 " SetNumberFont\n" +
1299 " stackWidth 0 translate\n" +
1300 " 90 rotate % rotate so the number fits\n" +
1301 " dup stringwidth pop % find the length of the number\n" +
1302 " neg % prepare for move\n" +
1303 " stackMargin sub % Move back a bit\n" +
1304 " charWidth (0) CharBoxHeight % height of numbers\n" +
1305 " sub 2 div %\n" +
1306 " moveto % move back to provide space\n" +
1307 " show\n" +
1308 " grestore\n" +
1309 "} bind def\n" +
1310 "\n" +
1311 "\n" +
1312 "\/Ibeam{ % heightInBits Ibeam\n" +
1313 " gsave\n" +
1314 " % Make an Ibeam of twice the given height in bits\n" +
1315 " \/height exch pointsPerBit mul def \n" +
1316 " \/heightDRAW height IbeamFraction mul def\n" +
1317 "\n" +
1318 " IbeamLineWidth setlinewidth\n" +
1319 " IbeamGray setgray \n" +
1320 "\n" +
1321 " charWidth2 height neg translate\n" +
1322 " ShowIbar\n" +
1323 " newpath\n" +
1324 " 0 0 moveto\n" +
1325 " 0 heightDRAW rlineto\n" +
1326 " stroke\n" +
1327 " newpath\n" +
1328 " 0 height moveto\n" +
1329 " 0 height rmoveto\n" +
1330 " currentpoint translate\n" +
1331 " ShowIbar\n" +
1332 " newpath\n" +
1333 " 0 0 moveto\n" +
1334 " 0 heightDRAW neg rlineto\n" +
1335 " currentpoint translate\n" +
1336 " stroke\n" +
1337 " grestore\n" +
1338 "} bind def\n" +
1339 "\n" +
1340 "\n" +
1341 "\/ShowIbar { % make a horizontal bar\n" +
1342 " gsave\n" +
1343 " newpath\n" +
1344 " charWidth4 neg 0 moveto\n" +
1345 " charWidth4 0 lineto\n" +
1346 " stroke\n" +
1347 " grestore\n" +
1348 "} bind def\n" +
1349 "\n" +
1350 "\n" +
1351 "\/ShowLeftEnd {\n" +
1352 " gsave\n" +
1353 " SetStringFont\n" +
1354 " leftEndDeltaX leftEndDeltaY moveto\n" +
1355 " logoType (NA) eq {(5) show ShowPrime} if\n" +
1356 " logoType (AA) eq {(N) show} if\n" +
1357 " grestore\n" +
1358 "} bind def\n" +
1359 "\n" +
1360 "\n" +
1361 "\/ShowRightEnd { \n" +
1362 " gsave\n" +
1363 " SetStringFont\n" +
1364 " rightEndDeltaX rightEndDeltaY moveto\n" +
1365 " logoType (NA) eq {(3) show ShowPrime} if\n" +
1366 " logoType (AA) eq {(C) show} if\n" +
1367 " grestore\n" +
1368 "} bind def\n" +
1369 "\n" +
1370 "\n" +
1371 "\/ShowPrime {\n" +
1372 " gsave\n" +
1373 " SetPrimeFont\n" +
1374 " (\\242) show \n" +
1375 " grestore\n" +
1376 "} bind def\n" +
1377 "\n" +
1378 " \n" +
1379 "\/SetColor{ % <char> SetColor\n" +
1380 " dup colorDict exch known {\n" +
1381 " colorDict exch get aload pop setrgbcolor\n" +
1382 " } {\n" +
1383 " pop\n" +
1384 " defaultColor aload pop setrgbcolor\n" +
1385 " } ifelse \n" +
1386 "} bind def\n" +
1387 "\n" +
1388 "% define fonts\n" +
1389 "\/SetTitleFont {\/Times-Bold findfont titleFontsize scalefont setfont} bind def\n" +
1390 "\/SetLogoFont {\/Helvetica-Bold findfont charWidth scalefont setfont} bind def\n" +
1391 "\/SetStringFont{\/Helvetica-Bold findfont fontsize scalefont setfont} bind def\n" +
1392 "\/SetPrimeFont {\/Symbol findfont fontsize scalefont setfont} bind def\n" +
1393 "\/SetSmallFont {\/Helvetica findfont smallFontsize scalefont setfont} bind def\n" +
1394 "\n" +
1395 "\/SetNumberFont {\n" +
1396 " \/Helvetica-Bold findfont \n" +
1397 " numberFontsize\n" +
1398 " scalefont\n" +
1399 " setfont\n" +
1400 "} bind def\n" +
1401 "\n" +
1402 "%Take a single character and return the bounding box\n" +
1403 "\/CharBox { % <char> CharBox <lx> <ly> <ux> <uy>\n" +
1404 " gsave\n" +
1405 " newpath\n" +
1406 " 0 0 moveto\n" +
1407 " % take the character off the stack and use it here:\n" +
1408 " true charpath \n" +
1409 " flattenpath \n" +
1410 " pathbbox % compute bounding box of 1 pt. char => lx ly ux uy\n" +
1411 " % the path is here, but toss it away ...\n" +
1412 " grestore\n" +
1413 "} bind def\n" +
1414 "\n" +
1415 "\n" +
1416 "% The height of a characters bounding box\n" +
1417 "\/CharBoxHeight { % <char> CharBoxHeight <num>\n" +
1418 " CharBox\n" +
1419 " exch pop sub neg exch pop\n" +
1420 "} bind def\n" +
1421 "\n" +
1422 "\n" +
1423 "% The width of a characters bounding box\n" +
1424 "\/CharBoxWidth { % <char> CharBoxHeight <num>\n" +
1425 " CharBox\n" +
1426 " pop exch pop sub neg \n" +
1427 "} bind def\n" +
1428 "\n" +
1429 "% Set the colour scheme to be faded to indicate trimming\n" +
1430 "\/MuteColour {\n" +
1431 " \/colorDict mutedColourDict def\n" +
1432 "} def\n" +
1433 "\n" +
1434 "% Restore the colour scheme to the normal colours\n" +
1435 "\/RestoreColour {\n" +
1436 " \/colorDict fullColourDict def\n" +
1437 "} def\n" +
1438 "\n" +
1439 "% Draw the background for a trimmed section\n" +
1440 "% takes the number of columns as a parameter\n" +
1441 "\/DrawTrimBg { % <num> DrawTrimBox\n" +
1442 " \/col exch def\n" +
1443 " \n" +
1444 " \/boxwidth \n" +
1445 " col stackWidth mul \n" +
1446 " def\n" +
1447 " \n" +
1448 " gsave\n" +
1449 " 0.97 setgray\n" +
1450 "\n" +
1451 " newpath\n" +
1452 " 0 0 moveto\n" +
1453 " boxwidth 0 rlineto\n" +
1454 " 0 yaxisHeight rlineto\n" +
1455 " 0 yaxisHeight lineto\n" +
1456 " closepath\n" +
1457 " \n" +
1458 " fill\n" +
1459 " grestore\n" +
1460 "} def\n" +
1461 "\n" +
1462 "\/DrawTrimEdge {\n" +
1463 " gsave\n" +
1464 " 0.2 setgray\n" +
1465 " [2] 0 setdash\n" +
1466 "\n" +
1467 " newpath\n" +
1468 " 0 0 moveto\n" +
1469 " 0 yaxisHeight lineto\n" +
1470 " \n" +
1471 " stroke\n" +
1472 "\n" +
1473 "} def\n" +
1474 "\n" +
1475 "\n" +
1476 "% Deprecated names\n" +
1477 "\/startstack {StartStack} bind def\n" +
1478 "\/endstack {EndStack} bind def\n" +
1479 "\/makenumber {MakeNumber} bind def\n" +
1480 "\/numchar { MakeSymbol } bind def\n" +
1481 "\n" +
1482 "%%EndProlog\n" +
1483 "\n" +
1484 "%%Page: 1 1\n" +
1485 "StartLogo\n" +
1486 "\n" +
1487 _input("DATA") + "\n" +
1488 "\n" +
1489 "EndLogo\n" +
1490 "\n" +
1491 "%%EOF\n"
1492 );
1493 }</script>
1494 <script>
1495 //======================================================================
1496 // start Alphabet object
1497 //======================================================================
1498 var Alphabet = function(alphabet, background) {
1499 "use strict";
1500 var i, j, sym, aliases, complement, comp_e_sym, ambigs, generate_background;
1501 generate_background = (background == null);
1502 if (generate_background) {
1503 background = [];
1504 for (i = 0; i < alphabet.ncore; i++) background[i] = 1.0 / alphabet.ncore;
1505 } else if (alphabet.ncore != background.length) {
1506 throw new Error("The background length does not match the alphabet length.");
1507 }
1508 this.name = alphabet.name;
1509 this.like = (alphabet.like != null ? alphabet.like.toUpperCase() : null);
1510 this.ncore = alphabet.ncore;
1511 this.symbols = alphabet.symbols;
1512 this.background = background;
1513 this.genbg = generate_background;
1514 this.encode = {};
1515 this.encode2core = {};
1516 this.complement = {};
1517 // check if all symbols are same case
1518 var seen_uc = false;
1519 var seen_lc = false;
1520 var check_case = function (syms) {
1521 var s, sym;
1522 if (typeof syms === "string") {
1523 for (s = 0; s < syms.length; s++) {
1524 sym = syms.charAt(s);
1525 if (sym >= 'a' && sym <= 'z') seen_lc = true;
1526 else if (sym >= 'A' && sym <= 'Z') seen_uc = true;
1527 }
1528 }
1529 };
1530 for (i = 0; i < this.symbols.length; i++) {
1531 check_case(this.symbols[i].symbol);
1532 check_case(this.symbols[i].aliases);
1533 }
1534 // now map symbols to indexes
1535 var update_array = function(array, syms, index) {
1536 var s, sym;
1537 if (typeof syms === "string") {
1538 for (s = 0; s < syms.length; s++) {
1539 sym = syms.charAt(s);
1540 array[sym] = index;
1541 // when only a single case is used, then encode as case insensitive
1542 if (seen_uc != seen_lc) {
1543 if (sym >= 'a' && sym <= 'z') {
1544 array[sym.toUpperCase()] = index;
1545 } else if (sym >= 'A' && sym <= 'Z') {
1546 array[sym.toLowerCase()] = index;
1547 }
1548 }
1549 }
1550 }
1551 }
1552 // map core symbols to index
1553 for (i = 0; i < this.ncore; i++) {
1554 update_array(this.encode2core, this.symbols[i].symbol, i);
1555 update_array(this.encode, this.symbols[i].symbol, i);
1556 update_array(this.encode2core, this.symbols[i].aliases, i);
1557 update_array(this.encode, this.symbols[i].aliases, i);
1558 }
1559 // map ambiguous symbols to index
1560 ambigs = {};
1561 for (i = this.ncore; i < this.symbols.length; i++) {
1562 update_array(this.encode, this.symbols[i].symbol, i);
1563 update_array(this.encode, this.symbols[i].aliases, i);
1564 ambigs[this.symbols[i].equals] = i;
1565 }
1566 // determine complements
1567 for (i = 0; i < this.ncore; i++) {
1568 complement = this.symbols[i].complement;
1569 if (typeof complement === "string") {
1570 this.complement[i] = this.encode2core[complement];
1571 }
1572 }
1573 next_symbol:
1574 for (i = this.ncore; i < this.symbols.length; i++) {
1575 complement = "";
1576 for (j = 0; j < this.symbols[i].equals.length; j++) {
1577 comp_e_sym = this.complement[this.encode2core[this.symbols[i].equals.charAt(j)]];
1578 if (typeof comp_e_sym !== "number") continue next_symbol;
1579 complement += this.symbols[comp_e_sym].symbol;
1580 }
1581 complement = complement.split("").sort().join("");
1582 if (typeof ambigs[complement] === "number") {
1583 this.complement[i] = ambigs[complement];
1584 }
1585 }
1586 // determine case insensitivity
1587 this.case_insensitive = true;
1588 if (seen_uc == seen_lc) {
1589 // when there is a mixture of cases it probably won't
1590 // be case insensitive but we still need to check
1591 loop:
1592 for (i = 0; i < this.symbols.length; i++) {
1593 sym = this.symbols[i].symbol;
1594 if (sym >= 'A' && sym <= 'Z') {
1595 if (this.encode[sym.toLowerCase()] != i) {
1596 this.case_insensitive = false;
1597 break loop;
1598 }
1599 } else if (sym >= 'a' && sym <= 'z') {
1600 if (this.encode[sym.toUpperCase()] != i) {
1601 this.case_insensitive = false;
1602 break loop;
1603 }
1604 }
1605 aliases = this.symbols[i].aliases;
1606 if (aliases != null) {
1607 for (j = 0; j < aliases.length; j++) {
1608 sym = aliases.charAt(j);
1609 if (sym >= 'A' && sym <= 'Z') {
1610 if (this.encode[sym.toLowerCase()] != i) {
1611 this.case_insensitive = false;
1612 break loop;
1613 }
1614 } else if (sym >= 'a' && sym <= 'z') {
1615 if (this.encode[sym.toUpperCase()] != i) {
1616 this.case_insensitive = false;
1617 break loop;
1618 }
1619 }
1620 }
1621 }
1622 }
1623 }
1624 // normalise aliases to remove the prime symbol and eliminate
1625 // the alternate cases when the alphabet is case insensitive
1626 var seen, out;
1627 for (i = 0; i < this.symbols.length; i++) {
1628 sym = this.symbols[i].symbol;
1629 aliases = this.symbols[i].aliases;
1630 if (typeof aliases != "string") aliases = "";
1631 seen = {};
1632 out = [];
1633 if (this.case_insensitive) {
1634 sym = sym.toUpperCase();
1635 aliases = aliases.toUpperCase();
1636 }
1637 seen[sym] = true;
1638 for (j = 0; j < aliases.length; j++) {
1639 if (!seen[aliases.charAt(j)]) {
1640 seen[aliases.charAt(j)] = true;
1641 out.push(aliases.charAt(j));
1642 }
1643 }
1644 this.symbols[i].aliases = out.sort().join("");
1645 }
1646 };
1647 // return the name of the alphabet
1648 Alphabet.prototype.get_alphabet_name = function() {
1649 return this.name;
1650 };
1651 // return if the alphabet can be complemented
1652 Alphabet.prototype.has_complement = function() {
1653 return (typeof this.symbols[0].complement === "string");
1654 };
1655 // return true if an uppercase letter has the same meaning as the lowercase form
1656 Alphabet.prototype.is_case_insensitive = function() {
1657 return this.case_insensitive;
1658 };
1659 // return the information content of an alphabet letter
1660 Alphabet.prototype.get_ic = function() {
1661 return Math.log(this.ncore) / Math.LN2;
1662 };
1663 // return the count of the core alphabet symbols
1664 Alphabet.prototype.get_size_core = function() {
1665 return this.ncore;
1666 };
1667 // return the count of all alphabet symbols
1668 Alphabet.prototype.get_size_full = function() {
1669 return this.symbols.length;
1670 };
1671 // return the symbol for the given alphabet index
1672 Alphabet.prototype.get_symbol = function(alph_index) {
1673 "use strict";
1674 if (alph_index < 0 || alph_index >= this.symbols.length) {
1675 throw new Error("Alphabet index out of bounds");
1676 }
1677 return this.symbols[alph_index].symbol;
1678 };
1679 // return the aliases for the given alphabet index
1680 Alphabet.prototype.get_aliases = function(alph_index) {
1681 "use strict";
1682 if (alph_index < 0 || alph_index >= this.symbols.length) {
1683 throw new Error("Alphabet index out of bounds");
1684 }
1685 var sym_obj = this.symbols[alph_index];
1686 return (sym_obj.aliases != null ? sym_obj.aliases : "");
1687 };
1688 // return the name for the given alphabet index
1689 Alphabet.prototype.get_name = function(alph_index) {
1690 "use strict";
1691 var sym;
1692 if (alph_index < 0 || alph_index >= this.symbols.length) {
1693 throw new Error("Alphabet index out of bounds");
1694 }
1695 sym = this.symbols[alph_index];
1696 return (typeof sym.name === "string" ? sym.name : sym.symbol);
1697 };
1698 // return the alphabet it is like or null
1699 Alphabet.prototype.get_like = function() {
1700 "use strict";
1701 return this.like;
1702 };
1703 // return the index of the complement for the given alphabet index
1704 Alphabet.prototype.get_complement = function(alph_index) {
1705 var comp_e_sym = this.complement[alph_index];
1706 if (typeof comp_e_sym === "number") {
1707 return comp_e_sym;
1708 } else {
1709 return -1;
1710 }
1711 };
1712 // return a string containing the core symbols
1713 Alphabet.prototype.get_symbols = function() {
1714 "use strict";
1715 var i, core_symbols;
1716 core_symbols = "";
1717 for (i = 0; i < this.ncore; i++) {
1718 core_symbols += this.symbols[i].symbol;
1719 }
1720 return core_symbols;
1721 };
1722 // return if the background was not a uniform generated background
1723 Alphabet.prototype.has_bg = function() {
1724 "use strict";
1725 return !this.genbg;
1726 };
1727 // get the background frequency for the index
1728 Alphabet.prototype.get_bg_freq = function(alph_index) {
1729 "use strict";
1730 var freq, i, symbols;
1731 if (alph_index >= 0) {
1732 if (alph_index < this.ncore) {
1733 return this.background[alph_index];
1734 } else if (alph_index < this.symbols.length) {
1735 freq = 0;
1736 symbols = this.symbols[alph_index].equals;
1737 for (i = 0; i < symbols.length; i++) {
1738 freq += this.background[this.encode2core[symbols.charAt(i)]];
1739 }
1740 return freq;
1741 }
1742 }
1743 throw new Error("The alphabet index is out of range.");
1744 };
1745 // get the colour of the index
1746 Alphabet.prototype.get_colour = function(alph_index) {
1747 "use strict";
1748 if (alph_index < 0 || alph_index >= this.symbols.length) {
1749 throw new Error("BAD_ALPHABET_INDEX");
1750 }
1751 if (typeof this.symbols[alph_index].colour != "string") {
1752 return "black";
1753 }
1754 return "#" + this.symbols[alph_index].colour;
1755 };
1756 // get the rgb components of the colour at the index
1757 Alphabet.prototype.get_rgb = function(alph_index) {
1758 "use strict";
1759 if (alph_index < 0 || alph_index >= this.symbols.length) {
1760 throw new Error("BAD_ALPHABET_INDEX");
1761 }
1762 if (typeof this.symbols[alph_index].colour != "string") {
1763 return {"red": 0, "green": 0, "blue": 0};
1764 }
1765 var colour = this.symbols[alph_index].colour;
1766 var red = parseInt(colour.substr(0, 2), 16) / 255;
1767 var green = parseInt(colour.substr(2, 2), 16) / 255;
1768 var blue = parseInt(colour.substr(4, 2), 16) / 255;
1769 return {"red": red, "green": green, "blue": blue};
1770 };
1771 // convert a symbol into the index
1772 Alphabet.prototype.get_index = function(letter) {
1773 "use strict";
1774 var alph_index;
1775 alph_index = this.encode[letter];
1776 if (typeof alph_index === "undefined") {
1777 return -1;
1778 }
1779 return alph_index;
1780 };
1781 // convert a symbol into the list of core indexes that it equals
1782 Alphabet.prototype.get_indexes = function(letter) {
1783 "use strict";
1784 var alph_index, comprise_str, i, comprise_list;
1785 alph_index = this.encode[letter];
1786 if (typeof alph_index === "undefined") {
1787 throw new Error("Unknown letter");
1788 }
1789 comprise_str = this.symbols[alph_index].equals;
1790 comprise_list = [];
1791 if (typeof comprise_str == "string") {
1792 for (i = 0; i < comprise_str.length; i++) {
1793 comprise_list.push(this.encode2core[comprise_str.charAt(i)]);
1794 }
1795 } else {
1796 comprise_list.push(alph_index);
1797 }
1798 return comprise_list;
1799 };
1800 // check if a symbol is the primary way of representing the symbol in the alphabet
1801 Alphabet.prototype.is_prime_symbol = function(letter) {
1802 var alph_index;
1803 alph_index = this.encode[letter];
1804 if (alph_index == null) return false;
1805 if (this.is_case_insensitive()) {
1806 return (this.symbols[alph_index].symbol.toUpperCase() == letter.toUpperCase());
1807 } else {
1808 return (this.symbols[alph_index].symbol == letter);
1809 }
1810 };
1811 // compare 2 alphabets
1812 Alphabet.prototype.equals = function(other) {
1813 "use strict";
1814 var i, sym1, sym2;
1815 // first check that it's actually an alphabet object
1816 if (!(typeof other === "object" && other != null && other instanceof Alphabet)) {
1817 return false;
1818 }
1819 // second shortcircuit if it's the same object
1820 if (this === other) return true;
1821 // compare
1822 if (this.name !== other.name) return false;
1823 if (this.ncore !== other.ncore) return false;
1824 if (this.symbols.length !== other.symbols.length) return false;
1825 for (i = 0; i < this.symbols.length; i++) {
1826 sym1 = this.symbols[i];
1827 sym2 = other.symbols[i];
1828 if (sym1.symbol !== sym2.symbol) return false;
1829 if (sym1.aliases !== sym2.aliases) return false;
1830 if (sym1.name !== sym2.name) return false;
1831 if (typeof sym1.colour !== typeof sym2.colour ||
1832 (typeof sym1.colour === "string" && typeof sym2.colour === "string" &&
1833 parseInt(sym1.colour, 16) != parseInt(sym2.colour, 16))) {
1834 return false;
1835 }
1836 if (sym1.complement !== sym2.complement) return false;
1837 if (sym1.equals !== sym2.equals) return false;
1838 }
1839 return true;
1840 };
1841 Alphabet.prototype.check_core_subset = function(super_alph) {
1842 var complement_same = true;
1843 var seen_set = {};
1844 var sub_i, sub_symbol, super_i, super_symbol;
1845 for (sub_i = 0; sub_i < this.ncore; sub_i++) {
1846 sub_symbol = this.symbols[sub_i];
1847 super_i = super_alph.encode[sub_symbol.symbol];
1848 if (super_i == null) return 0;
1849 super_symbol = super_alph.symbols[super_i];
1850 if (seen_set[super_i]) return 0;
1851 seen_set[super_i] = true;
1852 // check complement
1853 if (sub_symbol.complement != null && super_symbol.complement != null) {
1854 if (super_alph.encode[sub_symbol.complement] != super_alph.encode[super_symbol.complement]) {
1855 complement_same = false;
1856 }
1857 } else if (sub_symbol.complement != null || super_symbol.complement != null) {
1858 complement_same = false;
1859 }
1860 }
1861 return (complement_same ? 1 : -1);
1862 };
1863 // convert a sequence to its reverse complement
1864 Alphabet.prototype.invcomp_seq = function(seq) {
1865 "use strict";
1866 var syms, i, e_sym, comp_e_sym;
1867 if (!this.has_complement()) throw new Error("Alphabet must be complementable");
1868 syms = seq.split("");
1869 for (i = 0; i < syms.length; i++) {
1870 e_sym = this.encode[syms[i]];
1871 if (typeof e_sym === "undefined") {
1872 e_sym = this.ncore; // wildcard
1873 }
1874 comp_e_sym = this.complement[e_sym];
1875 if (typeof comp_e_sym === "undefined") {
1876 comp_e_sym = e_sym; // not complementable
1877 }
1878 syms[i] = this.symbols[comp_e_sym].symbol;
1879 }
1880 return syms.reverse().join("");
1881 };
1882 // convert the alphabet to the text version
1883 Alphabet.prototype.as_text = function() {
1884 "use strict";
1885 function name_as_text(name) {
1886 var i, c, out;
1887 out = "\"";
1888 for (i = 0; i < name.length; i++) {
1889 c = name.charAt(i);
1890 if (c == "\"") {
1891 out += "\\\"";
1892 } else if (c == "/") {
1893 out += "\\/";
1894 } else if (c == "\\") {
1895 out += "\\\\";
1896 } else {
1897 out += c;
1898 }
1899 }
1900 out += "\"";
1901 return out;
1902 }
1903 function symbol_as_text(sym) {
1904 var out;
1905 out = sym.symbol;
1906 if (typeof sym.name === "string" && sym.name != sym.symbol) {
1907 out += " " + name_as_text(sym.name);
1908 }
1909 if (typeof sym.colour === "string") {
1910 out += " " + sym.colour;
1911 }
1912 return out;
1913 }
1914 var out, i, j, c, sym;
1915 out = "";
1916 // output core symbols with 2 way complements
1917 for (i = 0; i < this.ncore; i++) {
1918 c = this.complement[i];
1919 if (typeof c === "number" && i < c && this.complement[c] === i) {
1920 out += symbol_as_text(this.symbols[i]) + " ~ " + symbol_as_text(this.symbols[c]) + "\n";
1921 }
1922 }
1923 // output core symbols with no complement
1924 for (i = 0; i < this.ncore; i++) {
1925 if (typeof this.complement[i] === "undefined") {
1926 out += symbol_as_text(this.symbols[i]) + "\n";
1927 }
1928 }
1929 // output ambiguous symbols that have comprising characters
1930 for (i = this.ncore; i < this.symbols.length; i++) {
1931 if (this.symbols[i].equals.length == 0) break;
1932 out += symbol_as_text(this.symbols[i]) + " = " + this.symbols[i].equals + "\n";
1933 if (typeof this.symbols[i].aliases === "string") {
1934 for (j = 0; j < this.symbols[i].aliases.length; j++) {
1935 if (this.symbols[i].aliases.charAt(j) == this.symbols[i].symbol) continue;
1936 out += this.symbols[i].aliases.charAt(j) + " = " + this.symbols[i].equals + "\n";
1937 }
1938 }
1939 }
1940 // output aliases of core symbols
1941 for (i = 0; i < this.ncore; i++) {
1942 if (typeof this.symbols[i].aliases === "string") {
1943 for (j = 0; j < this.symbols[i].aliases.length; j++) {
1944 if (this.symbols[i].aliases.charAt(j) == this.symbols[i].symbol) continue;
1945 out += this.symbols[i].aliases.charAt(j) + " = " + this.symbols[i].symbol + "\n";
1946 }
1947 }
1948 }
1949 // output gap symbols
1950 i = this.symbols.length - 1;
1951 if (this.symbols[i].equals.length == 0) {
1952 out += symbol_as_text(this.symbols[i]) + " =\n";
1953 if (typeof this.symbols[i].aliases === "string") {
1954 for (j = 0; j < this.symbols[i].aliases.length; j++) {
1955 if (this.symbols[i].aliases.charAt(j) == this.symbols[i].symbol) continue;
1956 out += this.symbols[i].aliases.charAt(j) + " =\n";
1957 }
1958 }
1959 }
1960 return out;
1961 };
1962 // output the alphabet as it appears in minimal MEME format
1963 Alphabet.prototype.as_meme = function() {
1964 "use strict";
1965 function name_as_text(name) {
1966 var i, c, out;
1967 out = "\"";
1968 for (i = 0; i < name.length; i++) {
1969 c = name.charAt(i);
1970 if (c == "\"") {
1971 out += "\\\"";
1972 } else if (c == "/") {
1973 out += "\\/";
1974 } else if (c == "\\") {
1975 out += "\\\\";
1976 } else {
1977 out += c;
1978 }
1979 }
1980 out += "\"";
1981 return out;
1982 }
1983 if (this.equals(AlphStd.DNA)) {
1984 return "ALPHABET= ACGT\n";
1985 } else if (this.equals(AlphStd.PROTEIN)) {
1986 return "ALPHABET= ACDEFGHIKLMNPQRSTVWY\n";
1987 } else {
1988 return "ALPHABET" +
1989 (this.name != null ? " " + name_as_text(this.name) : "") +
1990 (this.like != null ? " " + this.like + "-LIKE" : "") + "\n" +
1991 this.as_text() + "END ALPHABET\n";
1992 }
1993 };
1994
1995 // Returns a table showing all the letters in the alphabet
1996 Alphabet.prototype.as_table = function() {
1997 "use strict";
1998 var i, j, row, th, td, aliases, equals, sym;
1999 var table = document.createElement("table");
2000 // create the core symbol header
2001 row = table.insertRow(table.rows.length);
2002 th = document.createElement("th");
2003 th.appendChild(document.createTextNode("Symbol(s)"));
2004 row.appendChild(th);
2005 th = document.createElement("th");
2006 th.appendChild(document.createTextNode("Name"));
2007 row.appendChild(th);
2008 th = document.createElement("th");
2009 if (this.has_complement()) {
2010 th.appendChild(document.createTextNode("Complement"));
2011 }
2012 row.appendChild(th);
2013 // list the core symbols
2014 for (i = 0; i < this.ncore; i++) {
2015 row = table.insertRow(table.rows.length);
2016 td = document.createElement("td");
2017 if (this.symbols[i].colour != null) {
2018 td.style.color = '#' + this.symbols[i].colour;
2019 }
2020 td.appendChild(document.createTextNode(this.symbols[i].symbol));
2021 aliases = this.get_aliases(i);
2022 if (aliases.length > 0) {
2023 td.appendChild(document.createTextNode(' ' + aliases.split('').join(' ')));
2024 }
2025 row.appendChild(td);
2026 td = document.createElement("td");
2027 if (this.symbols[i].name != null) {
2028 td.appendChild(document.createTextNode(this.symbols[i].name));
2029 }
2030 row.appendChild(td);
2031 td = document.createElement("td");
2032 if (this.symbols[i].complement != null) {
2033 td.style.color = this.get_colour(this.get_index(this.symbols[i].complement));
2034 td.appendChild(document.createTextNode(this.symbols[i].complement));
2035 }
2036 row.appendChild(td);
2037 }
2038 // create the ambiguous symbol header
2039 row = table.insertRow(table.rows.length);
2040 th = document.createElement("th");
2041 th.appendChild(document.createTextNode("Symbol(s)"));
2042 row.appendChild(th);
2043 th = document.createElement("th");
2044 th.appendChild(document.createTextNode("Name"));
2045 row.appendChild(th);
2046 th = document.createElement("th");
2047 th.appendChild(document.createTextNode("Matches"));
2048 row.appendChild(th);
2049 // list the ambiguous symbols
2050 for (i = this.ncore; i < this.symbols.length; i++) {
2051 row = table.insertRow(table.rows.length);
2052 td = document.createElement("td");
2053 if (this.symbols[i].colour != null) {
2054 td.style.color = '#' + this.symbols[i].colour;
2055 }
2056 td.appendChild(document.createTextNode(this.symbols[i].symbol));
2057 aliases = this.get_aliases(i);
2058 if (aliases.length > 0) {
2059 td.appendChild(document.createTextNode(' ' + aliases.split('').join(' ')));
2060 }
2061 row.appendChild(td);
2062 td = document.createElement("td");
2063 if (this.symbols[i].name != null) {
2064 td.appendChild(document.createTextNode(this.symbols[i].name));
2065 }
2066 row.appendChild(td);
2067 td = document.createElement("td");
2068 equals = this.symbols[i].equals.split('');
2069 for (j = 0; j < equals.length; j++) {
2070 if (j != 0) td.appendChild(document.createTextNode(' '));
2071 sym = document.createElement("span");
2072 sym.style.color = this.get_colour(this.get_index(equals[j]));
2073 sym.appendChild(document.createTextNode(equals[j]));
2074 td.appendChild(sym);
2075 }
2076 row.appendChild(td);
2077 }
2078 return table;
2079 };
2080
2081 // returns a dictionary of the colours for EPS
2082 Alphabet.prototype._as_eps_dict = function() {
2083 "use strict";
2084 var i, sym, rgb;
2085 var out = "/fullColourDict <<\n";
2086 for (i = 0; i < this.ncore; i++) {
2087 sym = this.get_symbol(i);
2088 sym = sym.replace(/\\/g, "\\\\");
2089 sym = sym.replace(/\(/g, "\\(");
2090 sym = sym.replace(/\)/g, "\\)");
2091 rgb = this.get_rgb(i);
2092 out += " (" + sym + ") [" + rgb.red.toFixed(4) + " " + rgb.green.toFixed(4) + " " + rgb.blue.toFixed(4) + "]\n";
2093 }
2094 out += ">> def\n";
2095 out += "/mutedColourDict <<\n";
2096 for (i = 0; i < this.ncore; i++) {
2097 sym = this.get_symbol(i);
2098 sym = sym.replace(/\\/g, "\\\\");
2099 sym = sym.replace(/\(/g, "\\(");
2100 sym = sym.replace(/\)/g, "\\)");
2101 rgb = Alphabet.lighten_colour(this.get_rgb(i));
2102 out += " (" + sym + ") [" + rgb.red.toFixed(4) + " " + rgb.green.toFixed(4) + " " + rgb.blue.toFixed(4) + "]\n";
2103 }
2104 out += ">> def\n";
2105 return out;
2106 };
2107
2108 // return the alphabet name or a list of primary symbols
2109 Alphabet.prototype.toString = function() {
2110 "use strict";
2111 if (this.name != null) {
2112 return this.name;
2113 } else {
2114 return this.get_symbols();
2115 }
2116 };
2117
2118 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2119 // Helper functions
2120 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2121
2122 // Convert a colour specified in RGB colourspace values into LAB colourspace
2123 Alphabet.rgb2lab = function(rgb) {
2124 "use strict";
2125 var xyzHelper, labHelper;
2126 // XYZ helper
2127 xyzHelper = function(value) {
2128 if (value > 0.0445) {
2129 value = (value + 0.055) / 1.055;
2130 value = Math.pow(value, 2.4);
2131 } else {
2132 value /= 12.92;
2133 }
2134 value *= 100;
2135 return value;
2136 };
2137 // lab helper
2138 labHelper = function(value) {
2139 if (value > 0.008856) {
2140 value = Math.pow(value, 1.0 / 3.0);
2141 } else {
2142 value = (7.787 * value) + (16.0 / 116.0);
2143 }
2144 return value;
2145 };
2146 // convert into XYZ colourspace
2147 var c1, c2, c3;
2148 if (typeof rgb == "number") {
2149 c1 = xyzHelper(((rgb >> 16) & 0xFF) / 255.0);
2150 c2 = xyzHelper(((rgb >> 8) & 0xFF) / 255.0);
2151 c3 = xyzHelper((rgb & 0xFF) / 255.0);
2152 } else {
2153 c1 = xyzHelper(rgb.red);
2154 c2 = xyzHelper(rgb.green);
2155 c3 = xyzHelper(rgb.blue);
2156 }
2157 var x = (c1 * 0.4124) + (c2 * 0.3576) + (c3 * 0.1805);
2158 var y = (c1 * 0.2126) + (c2 * 0.7152) + (c3 * 0.0722);
2159 var z = (c1 * 0.0193) + (c2 * 0.1192) + (c3 * 0.9505);
2160 // convert into Lab colourspace
2161 c1 = labHelper(x / 95.047);
2162 c2 = labHelper(y / 100.0);
2163 c3 = labHelper(z / 108.883);
2164 var l = (116.0 * c2) - 16;
2165 var a = 500.0 * (c1 - c2);
2166 var b = 200.0 * (c2 - c3);
2167 return {"l": l, "a": a, "b": b};
2168 };
2169
2170 // Convert a colour specified in HSV colourspace into RGB colourspace
2171 Alphabet.hsv2rgb = function(hue, sat, value, output_object) {
2172 // achromatic (grey)
2173 var r = value;
2174 var g = value;
2175 var b = value;
2176 if (sat != 0) {
2177 var h = hue / 60.0;
2178 var i = Math.floor(h);
2179 var f = h - i;
2180 var p = value * (1.0 - sat);
2181 var q = value * (1.0 - (sat * f));
2182 var t = value * (1.0 - (sat * (1.0 - f)));
2183 if (i == 0) {
2184 r = value;
2185 g = t;
2186 b = p;
2187 } else if (i == 1) {
2188 r = q;
2189 g = value;
2190 b = p;
2191 } else if (i == 2) {
2192 r = p;
2193 g = value;
2194 b = t;
2195 } else if (i == 3) {
2196 r = p;
2197 g = q;
2198 b = value;
2199 } else if (i == 4) {
2200 r = t;
2201 g = p;
2202 b = value;
2203 } else {
2204 r = value;
2205 g = p;
2206 b = q;
2207 }
2208 }
2209 if (output_object) {
2210 return {"red": r, "green": g, "blue": b};
2211 } else {
2212 return (Math.floor(r * 255) << 15) | (Math.floor(g * 255) << 8) | (Math.floor(b * 255));
2213 }
2214 };
2215
2216 // Calculate a distance score between two colours in LAB colourspace
2217 Alphabet.lab_dist = function(lab1, lab2) {
2218 var c1 = Math.sqrt((lab1.l * lab1.l) + (lab1.a * lab1.a));
2219 var c2 = Math.sqrt((lab2.l * lab2.l) + (lab2.a * lab2.a));
2220 var dc = c1 - c2;
2221 var dl = lab1.l - lab2.l;
2222 var da = lab1.a - lab2.a;
2223 var db = lab1.b - lab2.b;
2224 // we don't want NaN due to rounding errors so fudge things a bit...
2225 var dh = 0;
2226 var dh_squared = (da * da) + (db * db) - (dc * dc);
2227 if (dh_squared > 0) {
2228 dh = Math.sqrt(dh_squared);
2229 }
2230 var first = dl;
2231 var second = dc / (1.0 + (0.045 * c1));
2232 var third = dh / (1.0 + (0.015 * c1));
2233 return Math.sqrt((first * first) + (second * second) + (third * third));
2234 };
2235
2236 // convert an RGB value into a HSL value
2237 Alphabet.rgb2hsl = function(rgb) {
2238 "use strict";
2239 var min, max, delta, h, s, l, r, g, b;
2240 if (typeof rgb == "number") {
2241 r = ((rgb >> 16) & 0xFF) / 255.0;
2242 g = ((rgb >> 8) & 0xFF) / 255.0;
2243 b = (rgb & 0xFF) / 255.0;
2244 } else {
2245 r = rgb.red;
2246 g = rgb.green;
2247 b = rgb.blue;
2248 }
2249 min = Math.min(r, g, b);
2250 max = Math.max(r, g, b);
2251 delta = max - min;
2252 l = min + (delta / 2);
2253 if (max == min) {
2254 h = 0; // achromatic (grayscale)
2255 s = 0;
2256 } else {
2257 if (l > 0.5) {
2258 s = delta / (2 - max - min);
2259 } else {
2260 s = delta / (max + min);
2261 }
2262 if (max == r) {
2263 h = (g - b) / delta;
2264 if (g < b) h += 6;
2265 } else if (max == g) {
2266 h = ((b - r) / delta) + 2;
2267 } else {
2268 h = ((r - g) / delta) + 4;
2269 }
2270 h /= 6;
2271 }
2272 return {"h": h, "s": s, "l": l};
2273 };
2274
2275 // convert a HSL value into an RGB value
2276 Alphabet.hsl2rgb = function(hsl, output_object) {
2277 "use strict";
2278 function _hue(p, q, t) {
2279 "use strict";
2280 if (t < 0) t += 1;
2281 else if (t > 1) t -= 1;
2282 if (t < (1.0 / 6.0)) {
2283 return p + ((q - p) * 6.0 * t);
2284 } else if (t < 0.5) {
2285 return q;
2286 } else if (t < (2.0 / 3.0)) {
2287 return p + ((q - p) * ((2.0 / 3.0) - t) * 6.0);
2288 } else {
2289 return p;
2290 }
2291 }
2292 var r, g, b, p, q;
2293 if (hsl.s == 0) {
2294 // achromatic (grayscale)
2295 r = hsl.l;
2296 g = hsl.l;
2297 b = hsl.l;
2298 } else {
2299 if (hsl.l < 0.5) {
2300 q = hsl.l * (1 + hsl.s);
2301 } else {
2302 q = hsl.l + hsl.s - (hsl.l * hsl.s);
2303 }
2304 p = (2 * hsl.l) - q;
2305 r = _hue(p, q, hsl.h + (1.0 / 3.0));
2306 g = _hue(p, q, hsl.h);
2307 b = _hue(p, q, hsl.h - (1.0 / 3.0));
2308 }
2309 if (output_object) {
2310 return {"red": r, "green": g, "blue": b};
2311 } else {
2312 return (Math.floor(r * 255) << 15) | (Math.floor(g * 255) << 8) | (Math.floor(b * 255));
2313 }
2314 };
2315
2316 Alphabet.lighten_colour = function(rgb) {
2317 "use strict";
2318 var hsl = Alphabet.rgb2hsl(rgb);
2319 hsl.l += (1.0 - hsl.l) * 2 / 3;
2320 return Alphabet.hsl2rgb(hsl, typeof rgb != "number");
2321 };
2322
2323 //======================================================================
2324 // end Alphabet object
2325 //======================================================================
2326
2327 //======================================================================
2328 // start StandardAlphabet object
2329 //======================================================================
2330
2331 // an extension of the alphabet object to support some additional fields
2332 // only present in standard alphabets.
2333 var StandardAlphabet = function(enum_code, enum_name, alphabet_data) {
2334 Alphabet.apply(this, [alphabet_data]);
2335 this.enum_code = enum_code;
2336 this.enum_name = enum_name;
2337 };
2338 StandardAlphabet.prototype = Alphabet.prototype;
2339 StandardAlphabet.prototype.constructor = StandardAlphabet;
2340
2341 // A unique code for this standard alphabet.
2342 // This code will be a power of 2 to enable creation of bitsets for
2343 // a selection of standard alphabets.
2344 StandardAlphabet.prototype.get_code = function() {
2345 return this.enum_code;
2346 };
2347
2348 // A unique name for this standard alphabet.
2349 // this name will be all upper case and the same as the property that
2350 // refers to this alphabet in the AlphStd collection.
2351 StandardAlphabet.prototype.get_enum = function() {
2352 return this.enum_name;
2353 };
2354
2355 //======================================================================
2356 // end StandardAlphabet object
2357 //======================================================================
2358
2359 // A collection of standard alphabets.
2360 var AlphStd = {
2361 RNA: new StandardAlphabet(1, "RNA", {
2362 "name": "RNA",
2363 "like": "RNA",
2364 "ncore": 4,
2365 "symbols": [
2366 {"symbol": "A", "name": "Adenine", "colour": "CC0000"},
2367 {"symbol": "C", "name": "Cytosine", "colour": "0000CC"},
2368 {"symbol": "G", "name": "Guanine", "colour": "FFB300"},
2369 {"symbol": "U", "name": "Uracil", "colour": "008000",
2370 "aliases": "T"},
2371 {"symbol": "N", "name": "Any base", "equals": "ACGU", "aliases": "X."},
2372 {"symbol": "V", "name": "Not U", "equals": "ACG"},
2373 {"symbol": "H", "name": "Not G", "equals": "ACU"},
2374 {"symbol": "D", "name": "Not C", "equals": "AGU"},
2375 {"symbol": "B", "name": "Not A", "equals": "CGU"},
2376 {"symbol": "M", "name": "Amino", "equals": "AC"},
2377 {"symbol": "R", "name": "Purine", "equals": "AG"},
2378 {"symbol": "W", "name": "Weak", "equals": "AU"},
2379 {"symbol": "S", "name": "Strong", "equals": "CG"},
2380 {"symbol": "Y", "name": "Pyrimidine", "equals": "CU"},
2381 {"symbol": "K", "name": "Keto", "equals": "GU"}
2382 ]
2383 }),
2384 DNA: new StandardAlphabet(2, "DNA", {
2385 "name": "DNA",
2386 "like": "DNA",
2387 "ncore": 4,
2388 "symbols": [
2389 {"symbol": "A", "name": "Adenine", "colour": "CC0000", "complement": "T"},
2390 {"symbol": "C", "name": "Cytosine", "colour": "0000CC", "complement": "G"},
2391 {"symbol": "G", "name": "Guanine", "colour": "FFB300", "complement": "C"},
2392 {"symbol": "T", "name": "Thymine", "colour": "008000", "complement": "A",
2393 "aliases": "U"},
2394 {"symbol": "N", "name": "Any base", "equals": "ACGT", "aliases": "X."},
2395 {"symbol": "V", "name": "Not T", "equals": "ACG"},
2396 {"symbol": "H", "name": "Not G", "equals": "ACT"},
2397 {"symbol": "D", "name": "Not C", "equals": "AGT"},
2398 {"symbol": "B", "name": "Not A", "equals": "CGT"},
2399 {"symbol": "M", "name": "Amino", "equals": "AC"},
2400 {"symbol": "R", "name": "Purine", "equals": "AG"},
2401 {"symbol": "W", "name": "Weak", "equals": "AT"},
2402 {"symbol": "S", "name": "Strong", "equals": "CG"},
2403 {"symbol": "Y", "name": "Pyrimidine", "equals": "CT"},
2404 {"symbol": "K", "name": "Keto", "equals": "GT"}
2405 ]
2406 }),
2407 PROTEIN: new StandardAlphabet(4, "PROTEIN", {
2408 "name": "Protein",
2409 "like": "PROTEIN",
2410 "ncore": 20,
2411 "symbols": [
2412 {"symbol": "A", "name": "Alanine", "colour": "0000CC"},
2413 {"symbol": "C", "name": "Cysteine", "colour": "0000CC"},
2414 {"symbol": "D", "name": "Aspartic acid", "colour": "FF00FF"},
2415 {"symbol": "E", "name": "Glutamic acid", "colour": "FF00FF"},
2416 {"symbol": "F", "name": "Phenylalanine", "colour": "0000CC"},
2417 {"symbol": "G", "name": "Glycine", "colour": "FFB300"},
2418 {"symbol": "H", "name": "Histidine", "colour": "FFCCCC"},
2419 {"symbol": "I", "name": "Isoleucine", "colour": "0000CC"},
2420 {"symbol": "K", "name": "Lysine", "colour": "CC0000"},
2421 {"symbol": "L", "name": "Leucine", "colour": "0000CC"},
2422 {"symbol": "M", "name": "Methionine", "colour": "0000CC"},
2423 {"symbol": "N", "name": "Asparagine", "colour": "008000"},
2424 {"symbol": "P", "name": "Proline", "colour": "FFFF00"},
2425 {"symbol": "Q", "name": "Glutamine", "colour": "008000"},
2426 {"symbol": "R", "name": "Arginine", "colour": "CC0000"},
2427 {"symbol": "S", "name": "Serine", "colour": "008000"},
2428 {"symbol": "T", "name": "Threonine", "colour": "008000"},
2429 {"symbol": "V", "name": "Valine", "colour": "0000CC"},
2430 {"symbol": "W", "name": "Tryptophan", "colour": "0000CC"},
2431 {"symbol": "Y", "name": "Tyrosine", "colour": "33E6CC"},
2432 {"symbol": "X", "name": "Any amino acid", "equals": "ACDEFGHIKLMNPQRSTVWY", "aliases": "*."},
2433 {"symbol": "B", "name": "Asparagine or Aspartic acid", "equals": "DN"},
2434 {"symbol": "Z", "name": "Glutamine or Glutamic acid", "equals": "EQ"},
2435 {"symbol": "J", "name": "Leucine or Isoleucine", "equals": "IL"}
2436 ]
2437 })
2438 };
2439
2440 //======================================================================
2441 // start Symbol object
2442 //======================================================================
2443 var Symbol = function(alph_index, scale, alphabet) {
2444 "use strict";
2445 //variable prototype
2446 this.symbol = alphabet.get_symbol(alph_index);
2447 this.scale = scale;
2448 this.colour = alphabet.get_colour(alph_index);
2449 };
2450
2451 Symbol.prototype.get_symbol = function() {
2452 "use strict";
2453 return this.symbol;
2454 };
2455
2456 Symbol.prototype.get_scale = function() {
2457 "use strict";
2458 return this.scale;
2459 };
2460
2461 Symbol.prototype.get_colour = function() {
2462 "use strict";
2463 return this.colour;
2464 };
2465
2466 Symbol.prototype.toString = function() {
2467 "use strict";
2468 return this.symbol + " " + (Math.round(this.scale*1000)/10) + "%";
2469 };
2470
2471 function compare_symbol(sym1, sym2) {
2472 "use strict";
2473 if (sym1.get_scale() < sym2.get_scale()) {
2474 return -1;
2475 } else if (sym1.get_scale() > sym2.get_scale()) {
2476 return 1;
2477 } else {
2478 return 0;
2479 }
2480 }
2481 //======================================================================
2482 // end Symbol object
2483 //======================================================================
2484
2485 //======================================================================
2486 // start Pspm object
2487 //======================================================================
2488 var Pspm = function(matrix, name, ltrim, rtrim, nsites, evalue, pssm, alt, pgm) {
2489 "use strict";
2490 var row, col, data, row_sum, delta, evalue_re;
2491 if (typeof name !== "string") {
2492 name = "";
2493 }
2494 this.name = name;
2495 //construct
2496 if (matrix instanceof Pspm) {
2497 // copy constructor
2498 this.alph_length = matrix.alph_length;
2499 this.motif_length = matrix.motif_length;
2500 this.name = matrix.name;
2501 this.alt = matrix.alt;
2502 this.nsites = matrix.nsites;
2503 this.evalue = matrix.evalue;
2504 this.ltrim = matrix.ltrim;
2505 this.rtrim = matrix.rtrim;
2506 this.pspm = [];
2507 this.pgm = matrix.pgm;
2508 for (row = 0; row < matrix.motif_length; row++) {
2509 this.pspm[row] = [];
2510 for (col = 0; col < matrix.alph_length; col++) {
2511 this.pspm[row][col] = matrix.pspm[row][col];
2512 }
2513 }
2514 if (matrix.pssm != null) {
2515 this.pssm = [];
2516 for (row = 0; row < matrix.motif_length; row++) {
2517 this.pspm[row] = [];
2518 for (col = 0; col < matrix.alph_length; col++) {
2519 this.pssm[row][col] = matrix.pssm[row][col];
2520 }
2521 }
2522 }
2523 } else {
2524 // check parameters
2525 if (ltrim == null) {
2526 ltrim = 0;
2527 } else if (typeof ltrim !== "number" || ltrim % 1 !== 0 || ltrim < 0) {
2528 throw new Error("ltrim must be a non-negative integer, got: " + ltrim);
2529 }
2530 if (rtrim == null) {
2531 rtrim = 0;
2532 } else if (typeof rtrim !== "number" || rtrim % 1 !== 0 || rtrim < 0) {
2533 throw new Error("rtrim must be a non-negative integer, got: " + rtrim);
2534 }
2535 if (nsites != null) {
2536 if (typeof nsites !== "number" || nsites < 0) {
2537 throw new Error("nsites must be a positive number, got: " + nsites);
2538 } else if (nsites == 0) {
2539 nsites = null;
2540 }
2541 }
2542 if (evalue != null) {
2543 if (typeof evalue === "number") {
2544 if (evalue < 0) {
2545 throw new Error("evalue must be a non-negative number, got: " + evalue);
2546 }
2547 } else if (typeof evalue === "string") {
2548 evalue_re = /^((?:[+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)|inf)$/;
2549 if (!evalue_re.test(evalue)) {
2550 throw new Error("evalue must be a non-negative number, got: " + evalue);
2551 }
2552 } else {
2553 throw new Error("evalue must be a non-negative number, got: " + evalue);
2554 }
2555 }
2556 // set properties
2557 this.name = name;
2558 this.alt = alt;
2559 this.nsites = nsites;
2560 this.evalue = evalue;
2561 this.ltrim = ltrim;
2562 this.rtrim = rtrim;
2563 this.pgm = pgm;
2564 if (typeof matrix === "string") {
2565 // string constructor
2566 data = parse_pspm_string(matrix);
2567 this.alph_length = data["alph_length"];
2568 this.motif_length = data["motif_length"];
2569 this.pspm = data["pspm"];
2570 if (this.evalue == null) {
2571 if (data["evalue"] != null) {
2572 this.evalue = data["evalue"];
2573 } else {
2574 this.evalue = 0;
2575 }
2576 }
2577 if (this.nsites == null) {
2578 if (typeof data["nsites"] === "number") {
2579 this.nsites = data["nsites"];
2580 } else {
2581 this.nsites = 20;
2582 }
2583 }
2584 } else {
2585 // assume pspm is a nested array
2586 this.motif_length = matrix.length;
2587 this.alph_length = (matrix.length > 0 ? matrix[0].length : 0);
2588 if (this.nsites == null) {
2589 this.nsites = 20;
2590 }
2591 if (this.evalue == null) {
2592 this.evalue = 0;
2593 }
2594 this.pspm = [];
2595 // copy pspm and check
2596 for (row = 0; row < this.motif_length; row++) {
2597 if (this.alph_length != matrix[row].length) {
2598 throw new Error("COLUMN_MISMATCH");
2599 }
2600 this.pspm[row] = [];
2601 row_sum = 0;
2602 for (col = 0; col < this.alph_length; col++) {
2603 this.pspm[row][col] = matrix[row][col];
2604 row_sum += this.pspm[row][col];
2605 }
2606 delta = 0.1;
2607 if (isNaN(row_sum) || (row_sum > 1 && (row_sum - 1) > delta) ||
2608 (row_sum < 1 && (1 - row_sum) > delta)) {
2609 throw new Error("INVALID_SUM");
2610 }
2611 }
2612 // copy pssm
2613 if (pssm != null) {
2614 this.pssm = [];
2615 for (row = 0; row < this.motif_length; row++) {
2616 this.pssm[row] = [];
2617 for (col = 0; col < this.alph_length; col++) {
2618 this.pssm[row][col] = pssm[row][col];
2619 }
2620 }
2621 }
2622 }
2623 }
2624 };
2625
2626 Pspm.prototype.copy = function() {
2627 "use strict";
2628 return new Pspm(this);
2629 };
2630
2631 Pspm.prototype.reverse = function() {
2632 "use strict";
2633 var x, y, temp, temp_trim;
2634 //reverse
2635 x = 0;
2636 y = this.motif_length-1;
2637 while (x < y) {
2638 temp = this.pspm[x];
2639 this.pspm[x] = this.pspm[y];
2640 this.pspm[y] = temp;
2641 x++;
2642 y--;
2643 }
2644 // reverse pssm (if defined)
2645 if (typeof this.pssm !== "undefined") {
2646 //reverse
2647 x = 0;
2648 y = this.motif_length-1;
2649 while (x < y) {
2650 temp = this.pssm[x];
2651 this.pspm[x] = this.pssm[y];
2652 this.pssm[y] = temp;
2653 x++;
2654 y--;
2655 }
2656 }
2657 //swap triming
2658 temp_trim = this.ltrim;
2659 this.ltrim = this.rtrim;
2660 this.rtrim = temp_trim;
2661 return this; //allow function chaining...
2662 };
2663
2664 Pspm.prototype.reverse_complement = function(alphabet) {
2665 "use strict";
2666 var x, y, temp, i, row, c, temp_trim;
2667 if (this.alph_length != alphabet.get_size_core()) {
2668 throw new Error("The alphabet size does not match the size of the pspm.");
2669 }
2670 if (!alphabet.has_complement()) {
2671 throw new Error("The specified alphabet can not be complemented.");
2672 }
2673 // reverse motif
2674 this.reverse();
2675 //complement
2676 for (x = 0; x < this.motif_length; x++) {
2677 row = this.pspm[x];
2678 for (i = 0; i < row.length; i++) {
2679 c = alphabet.get_complement(i);
2680 if (c < i) continue;
2681 temp = row[i];
2682 row[i] = row[c];
2683 row[c] = temp;
2684 }
2685 }
2686 // complement pssm (if defined)
2687 if (typeof this.pssm !== "undefined") {
2688 //complement
2689 for (x = 0; x < this.motif_length; x++) {
2690 row = this.pssm[x];
2691 for (i = 0; i < row.length; i++) {
2692 c = alphabet.get_complement(i);
2693 if (c < i) continue;
2694 temp = row[i];
2695 row[i] = row[c];
2696 row[c] = temp;
2697 }
2698 }
2699 }
2700 return this; //allow function chaining...
2701 };
2702
2703 Pspm.prototype.get_stack = function(position, alphabet, ssc) {
2704 "use strict";
2705 var row, stack_ic, alphabet_ic, stack, i, sym;
2706 if (this.alph_length != alphabet.get_size_core()) {
2707 throw new Error("The alphabet size does not match the size of the pspm.");
2708 }
2709 row = this.pspm[position];
2710 stack_ic = this.get_stack_ic(position, alphabet);
2711 if (ssc) stack_ic -= this.get_error(alphabet);
2712 alphabet_ic = alphabet.get_ic();
2713 stack = [];
2714 for (i = 0; i < this.alph_length; i++) {
2715 sym = new Symbol(i, row[i]*stack_ic/alphabet_ic, alphabet);
2716 if (sym.get_scale() <= 0) {
2717 continue;
2718 }
2719 stack.push(sym);
2720 }
2721 stack.sort(compare_symbol);
2722 return stack;
2723 };
2724
2725 Pspm.prototype.get_stack_ic = function(position, alphabet) {
2726 "use strict";
2727 var row, H, i;
2728 if (this.alph_length != alphabet.get_size_core()) {
2729 throw new Error("The alphabet size does not match the size fo the pspm.");
2730 }
2731 row = this.pspm[position];
2732 H = 0;
2733 for (i = 0; i < this.alph_length; i++) {
2734 if (row[i] === 0) {
2735 continue;
2736 }
2737 H -= (row[i] * (Math.log(row[i]) / Math.LN2));
2738 }
2739 return alphabet.get_ic() - H;
2740 };
2741
2742 Pspm.prototype.get_error = function(alphabet) {
2743 "use strict";
2744 if (this.nsites === 0) {
2745 return 0;
2746 }
2747 return (alphabet.get_size_core()-1) / (2 * Math.LN2 * this.nsites);
2748 };
2749
2750 Pspm.prototype.get_motif_length = function() {
2751 "use strict";
2752 return this.motif_length;
2753 };
2754
2755 Pspm.prototype.get_alph_length = function() {
2756 "use strict";
2757 return this.alph_length;
2758 };
2759
2760 Pspm.prototype.get_left_trim = function() {
2761 "use strict";
2762 return this.ltrim;
2763 };
2764
2765 Pspm.prototype.get_right_trim = function() {
2766 "use strict";
2767 return this.rtrim;
2768 };
2769
2770 Pspm.prototype.as_best_match = function(alphabet) {
2771 "use strict";
2772 var match, odds, best_odds, best_index;
2773 var i, j;
2774 match = "";
2775 for (i = 0; i < this.motif_length; i++) {
2776 best_index = 0;
2777 best_odds = this.pspm[i][0] / alphabet.get_bg_freq(0);
2778 for (j = 1; j < this.alph_length; j++) {
2779 odds = this.pspm[i][j] / alphabet.get_bg_freq(j);
2780 if (odds > best_odds) {
2781 best_odds = odds;
2782 best_index = j;
2783 }
2784 }
2785 match += alphabet.get_symbol(best_index);
2786 }
2787 return match;
2788 };
2789
2790 Pspm.prototype.as_count_matrix = function() {
2791 "use strict";
2792 var count, count_text, text;
2793 var i, j;
2794 text = "";
2795 for (i = 0; i < this.motif_length; i++) {
2796 if (i !== 0) {
2797 text += "\n";
2798 }
2799 for (j = 0; j < this.alph_length; j++) {
2800 if (j !== 0) {
2801 text += " ";
2802 }
2803 count = Math.round(this.nsites * this.pspm[i][j]);
2804 count_text = "" + count;
2805 // pad up to length of 4
2806 if (count_text.length < 4) {
2807 text += (new Array(5 - count_text.length)).join(" ") + count_text;
2808 } else {
2809 text += count_text;
2810 }
2811 }
2812 }
2813 return text;
2814 };
2815
2816 Pspm.prototype.as_probability_matrix = function() {
2817 "use strict";
2818 var text;
2819 var i, j;
2820 text = "";
2821 for (i = 0; i < this.motif_length; i++) {
2822 if (i !== 0) {
2823 text += "\n";
2824 }
2825 for (j = 0; j < this.alph_length; j++) {
2826 if (j !== 0) {
2827 text += " ";
2828 }
2829 text += this.pspm[i][j].toFixed(6);
2830 }
2831 }
2832 return text;
2833 };
2834
2835 Pspm.prototype.as_score_matrix = function(alphabet, pseudo) {
2836 "use strict";
2837 var me, score, out, row, col, score_text;
2838 me = this;
2839 if (typeof this.pssm === "undefined") {
2840 if (!(typeof alphabet === "object" && alphabet != null && alphabet instanceof Alphabet)) {
2841 throw new Error("The alphabet is required to generate the pssm.");
2842 }
2843 if (typeof pseudo === "undefined") {
2844 pseudo = 0.01;
2845 } else if (typeof pseudo !== "number" || pseudo < 0) {
2846 throw new Error("Expected positive number for pseudocount");
2847 }
2848 score = function(row, col) {
2849 "use strict";
2850 var p, bg, p2;
2851 p = me.pspm[row][col];
2852 bg = alphabet.get_bg_freq(col);
2853 p2 = (p * me.nsites + bg * pseudo) / (me.nsites + pseudo);
2854 return (p2 > 0 ? Math.round((Math.log(p2 / bg) / Math.LN2) * 100) : -10000);
2855 };
2856 } else {
2857 score = function(row, col) {
2858 "use strict";
2859 return me.pssm[row][col];
2860 };
2861 }
2862 out = "";
2863 for (row = 0; row < this.motif_length; row++) {
2864 for (col = 0; col < this.alph_length; col++) {
2865 if (col !== 0) {
2866 out += " ";
2867 }
2868 score_text = "" + score(row, col);
2869 // pad out to 6 characters
2870 if (score_text.length < 6) {
2871 out += (new Array(7 - score_text.length)).join(" ") + score_text;
2872 } else {
2873 out += score_text;
2874 }
2875 }
2876 out += "\n";
2877 }
2878 return out;
2879 }
2880
2881 Pspm.prototype.as_pspm = function() {
2882 "use strict";
2883 return "letter-probability matrix: alength= " + this.alph_length +
2884 " w= " + this.motif_length + " nsites= " + this.nsites +
2885 (this.pgm === "STREME" ? " P= " : " E= ") +
2886 (typeof this.evalue === "number" ?
2887 this.evalue.toExponential() : this.evalue) + "\n" +
2888 this.as_probability_matrix();
2889 };
2890
2891 Pspm.prototype.as_pssm = function(alphabet, pseudo) {
2892 "use strict";
2893 return "log-odds matrix: alength= " + this.alph_length +
2894 " w= " + this.motif_length +
2895 " E= " + (typeof this.evalue == "number" ?
2896 this.evalue.toExponential() : this.evalue) + "\n" +
2897 this.as_score_matrix(alphabet, pseudo);
2898 };
2899
2900 Pspm.prototype.as_meme = function(options) {
2901 var with_header, with_pspm, with_pssm, version, alphabet, bg_source, pseudocount, strands;
2902 var out, alen, i;
2903 // get the options
2904 if (typeof options !== "object" || options === null) {
2905 options = {};
2906 }
2907 with_header = (typeof options["with_header"] === "boolean" ? options["with_header"] : false);
2908 with_pspm = (typeof options["with_pspm"] === "boolean" ? options["with_pspm"] : false);
2909 with_pssm = (typeof options["with_pssm"] === "boolean" ? options["with_pssm"] : false);
2910 if (!with_pspm && !with_pssm) with_pspm = true;
2911 if (with_header) {
2912 if (typeof options["version"] === "string" && /^\d+(?:\.\d+){0,2}$/.test(options["version"])) {
2913 version = options["version"];
2914 } else if (typeof options["version"] === "number") {
2915 version = options["version"].toFixed(0);
2916 } else {
2917 version = "4";
2918 }
2919 if (typeof options["strands"] === "number" && options["strands"] === 1) {
2920 strands = 1;
2921 } else {
2922 strands = 2;
2923 }
2924 if (typeof options["bg_source"] === "string") {
2925 bg_source = options["bg_source"];
2926 } else {
2927 bg_source = "unknown source";
2928 }
2929 if (typeof options["alphabet"] === "object" && options["alphabet"] != null
2930 && options["alphabet"] instanceof Alphabet) {
2931 alphabet = options["alphabet"];
2932 } else {
2933 throw new Error("The alphabet is required to generate the header.");
2934 }
2935 }
2936 // now create the output
2937 out = "";
2938 if (with_header) {
2939 out = "MEME version " + version + "\n\n";
2940 out += alphabet.as_meme() + "\n";
2941 if (alphabet.has_complement()) { // assume DNA has both strands unless otherwise specified
2942 out += "strands: " + (strands === 1 ? "+" : "+ -") + "\n\n";
2943 }
2944 out += "Background letter frequencies (from " + bg_source + "):\n";
2945 alen = alphabet.get_size_core();
2946 for (i = 0; i < alen; i++) {
2947 if (i !== 0) {
2948 if (i % 9 === 0) { // maximum of nine entries per line
2949 out += "\n";
2950 } else {
2951 out += " ";
2952 }
2953 }
2954 out += alphabet.get_symbol(i) + " " + alphabet.get_bg_freq(i).toFixed(3);
2955 }
2956 }
2957 out += "\n\n";
2958 out += "MOTIF " + this.name + (this.alt == null ? "" : " " + this.alt);
2959 if (with_pssm) {
2960 out += "\n\n";
2961 out += this.as_pssm(options["alphabet"], options["pseudocount"]);
2962 }
2963 if (with_pspm) {
2964 out += "\n\n";
2965 out += this.as_pspm();
2966 }
2967 return out;
2968 }
2969
2970 Pspm.prototype.toString = function() {
2971 "use strict";
2972 var str, i, row;
2973 str = "";
2974 for (i = 0; i < this.pspm.length; i++) {
2975 row = this.pspm[i];
2976 str += row.join("\t") + "\n";
2977 }
2978 return str;
2979 };
2980
2981 function parse_pspm_properties(str) {
2982 "use strict";
2983 var parts, i, eqpos, before, after, properties, prop, num, num_re;
2984 num_re = /^((?:[+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)|inf)$/;
2985 parts = trim(str).split(/\s+/);
2986 // split up words containing =
2987 for (i = 0; i < parts.length;) {
2988 eqpos = parts[i].indexOf("=");
2989 if (eqpos != -1) {
2990 before = parts[i].substr(0, eqpos);
2991 after = parts[i].substr(eqpos+1);
2992 if (before.length > 0 && after.length > 0) {
2993 parts.splice(i, 1, before, "=", after);
2994 i += 3;
2995 } else if (before.length > 0) {
2996 parts.splice(i, 1, before, "=");
2997 i += 2;
2998 } else if (after.length > 0) {
2999 parts.splice(i, 1, "=", after);
3000 i += 2;
3001 } else {
3002 parts.splice(i, 1, "=");
3003 i++;
3004 }
3005 } else {
3006 i++;
3007 }
3008 }
3009 properties = {};
3010 for (i = 0; i < parts.length; i += 3) {
3011 if (parts.length - i < 3) {
3012 throw new Error("Expected PSPM property was incomplete. "+
3013 "Remaing parts are: " + parts.slice(i).join(" "));
3014 }
3015 if (parts[i+1] !== "=") {
3016 throw new Error("Expected '=' in PSPM property between key and " +
3017 "value but got " + parts[i+1]);
3018 }
3019 prop = parts[i].toLowerCase();
3020 num = parts[i+2];
3021 if (!num_re.test(num)) {
3022 throw new Error("Expected numeric value for PSPM property '" +
3023 prop + "' but got '" + num + "'");
3024 }
3025 properties[prop] = num;
3026 }
3027 return properties;
3028 }
3029
3030 function parse_pspm_string(pspm_string) {
3031 "use strict";
3032 var header_re, lines, first_line, line_num, col_num, alph_length,
3033 motif_length, nsites, evalue, pspm, i, line, match, props, parts,
3034 j, prob;
3035 header_re = /^letter-probability\s+matrix:(.*)$/i;
3036 lines = pspm_string.split(/\n/);
3037 first_line = true;
3038 line_num = 0;
3039 col_num = 0;
3040 alph_length;
3041 motif_length;
3042 nsites;
3043 evalue;
3044 pspm = [];
3045 for (i = 0; i < lines.length; i++) {
3046 line = trim(lines[i]);
3047 if (line.length === 0) {
3048 continue;
3049 }
3050 // check the first line for a header though allow matrices without it
3051 if (first_line) {
3052 first_line = false;
3053 match = header_re.exec(line);
3054 if (match !== null) {
3055 props = parse_pspm_properties(match[1]);
3056 if (props.hasOwnProperty("alength")) {
3057 alph_length = parseFloat(props["alength"]);
3058 if (alph_length != 4 && alph_length != 20) {
3059 throw new Error("PSPM property alength should be 4 or 20" +
3060 " but got " + alph_length);
3061 }
3062 }
3063 if (props.hasOwnProperty("w")) {
3064 motif_length = parseFloat(props["w"]);
3065 if (motif_length % 1 !== 0 || motif_length < 1) {
3066 throw new Error("PSPM property w should be an integer larger " +
3067 "than zero but got " + motif_length);
3068 }
3069 }
3070 if (props.hasOwnProperty("nsites")) {
3071 nsites = parseFloat(props["nsites"]);
3072 if (nsites <= 0) {
3073 throw new Error("PSPM property nsites should be larger than " +
3074 "zero but got " + nsites);
3075 }
3076 }
3077 if (props.hasOwnProperty("e")) {
3078 evalue = props["e"];
3079 if (evalue < 0) {
3080 throw new Error("PSPM property evalue should be " +
3081 "non-negative but got " + evalue);
3082 }
3083 }
3084 continue;
3085 }
3086 }
3087 pspm[line_num] = [];
3088 col_num = 0;
3089 parts = line.split(/\s+/);
3090 for (j = 0; j < parts.length; j++) {
3091 prob = parseFloat(parts[j]);
3092 if (prob != parts[j] || prob < 0 || prob > 1) {
3093 throw new Error("Expected probability but got '" + parts[j] + "'");
3094 }
3095 pspm[line_num][col_num] = prob;
3096 col_num++;
3097 }
3098 line_num++;
3099 }
3100 if (typeof motif_length === "number") {
3101 if (pspm.length != motif_length) {
3102 throw new Error("Expected PSPM to have a motif length of " +
3103 motif_length + " but it was actually " + pspm.length);
3104 }
3105 } else {
3106 motif_length = pspm.length;
3107 }
3108 if (typeof alph_length !== "number") {
3109 alph_length = pspm[0].length;
3110 if (alph_length != 4 && alph_length != 20) {
3111 throw new Error("Expected length of first row in the PSPM to be " +
3112 "either 4 or 20 but got " + alph_length);
3113 }
3114 }
3115 for (i = 0; i < pspm.length; i++) {
3116 if (pspm[i].length != alph_length) {
3117 throw new Error("Expected PSPM row " + i + " to have a length of " +
3118 alph_length + " but the length was " + pspm[i].length);
3119 }
3120 }
3121 return {"pspm": pspm, "motif_length": motif_length,
3122 "alph_length": alph_length, "nsites": nsites, "evalue": evalue};
3123 }
3124 //======================================================================
3125 // end Pspm object
3126 //======================================================================
3127
3128 //======================================================================
3129 // start Logo object
3130 //======================================================================
3131
3132 var Logo = function(alphabet, options) {
3133 "use strict";
3134 this.alphabet = alphabet;
3135 this.fine_text = "";
3136 this.x_axis = 1;
3137 this.y_axis = true;
3138 this.xlate_nsyms = 1;
3139 this.xlate_start = null;
3140 this.xlate_end = null;
3141 this.pspm_list = [];
3142 this.pspm_column = [];
3143 this.rows = 0;
3144 this.columns = 0;
3145 if (typeof options === "string") {
3146 // the old method signature had fine_text here so we support that
3147 this.fine_text = options;
3148 } else if (typeof options === "object" && options != null) {
3149 this.fine_text = (typeof options.fine_text === "string" ? options.fine_text : "");
3150 this.x_axis = (typeof options.x_axis === "boolean" ? (options.x_axis ? 1 : 0) : 1);
3151 if (options.x_axis_hidden != null && options.x_axis_hidden) this.x_axis = -1;
3152 this.y_axis = (typeof options.y_axis === "boolean" ? options.y_axis : true);
3153 this.xlate_nsyms = (typeof options.xlate_nsyms === "number" ? options.xlate_nsyms : this.xlate_nsyms);
3154 this.xlate_start = (typeof options.xlate_start === "number" ? options.xlate_start : this.xlate_start);
3155 this.xlate_end = (typeof options.xlate_end === "number" ? options.xlate_end : this.xlate_end);
3156 }
3157 };
3158
3159 Logo.prototype.add_pspm = function(pspm, column) {
3160 "use strict";
3161 var col;
3162 if (typeof column === "undefined") {
3163 column = 0;
3164 } else if (column < 0) {
3165 throw new Error("Column index out of bounds.");
3166 }
3167 this.pspm_list[this.rows] = pspm;
3168 this.pspm_column[this.rows] = column;
3169 this.rows++;
3170 col = column + pspm.get_motif_length();
3171 if (col > this.columns) {
3172 this.columns = col;
3173 }
3174 };
3175
3176 Logo.prototype.get_columns = function() {
3177 "use strict";
3178 return this.columns;
3179 };
3180
3181 Logo.prototype.get_xlate_nsyms = function() {
3182 "use strict";
3183 return this.xlate_nsyms;
3184 };
3185
3186 Logo.prototype.get_xlate_start = function() {
3187 "use strict";
3188 return (this.xlate_start != null ? this.xlate_start : 0);
3189 };
3190
3191 Logo.prototype.get_xlate_end = function() {
3192 "use strict";
3193 return (this.xlate_end != null ? this.xlate_end : this.columns * this.xlate_nsyms);
3194 };
3195
3196 Logo.prototype.get_xlate_columns = function() {
3197 "use strict";
3198 return this.get_xlate_end() - this.get_xlate_start();
3199 };
3200
3201 Logo.prototype.get_rows = function() {
3202 "use strict";
3203 return this.rows;
3204 };
3205
3206 Logo.prototype.get_pspm = function(row_index) {
3207 "use strict";
3208 if (row_index < 0 || row_index >= this.rows) {
3209 throw new Error("INDEX_OUT_OF_BOUNDS");
3210 }
3211 return this.pspm_list[row_index];
3212 };
3213
3214 Logo.prototype.get_offset = function(row_index) {
3215 "use strict";
3216 if (row_index < 0 || row_index >= this.rows) {
3217 throw new Error("INDEX_OUT_OF_BOUNDS");
3218 }
3219 return this.pspm_column[row_index];
3220 };
3221
3222 Logo.prototype._as_eps_data = function(ssc, errbars) {
3223 var i, j, pos, stack_pos, pspm, stack, sym, out;
3224 out = "";
3225 for (i = 0; i < this.rows; i++) {
3226 out += "\nStartLine\n";
3227 // Indent
3228 for (j = 0; j < this.pspm_column[i]; j++) {
3229 out += "() startstack\nendstack\n\n";
3230 }
3231 pspm = this.pspm_list[i];
3232 if (pspm.get_left_trim() > 0) {
3233 out += "MuteColour\nDrawTrimEdge\n" + pspm.get_left_trim() + " DrawTrimBg\n";
3234 }
3235 for (pos = 0; pos < pspm.get_motif_length(); pos++) {
3236 if (pos != 0 && pos == pspm.get_left_trim()) { // enable full colour
3237 out += "DrawTrimEdge\nRestoreColour\n";
3238 } else if (pos == (pspm.get_motif_length() - pspm.get_right_trim())) {
3239 out += "MuteColour\n" + pspm.get_right_trim() + " DrawTrimBg\n";
3240 }
3241 out += "(" + (pos + 1) + ") startstack\n";
3242 stack = pspm.get_stack(pos, this.alphabet, ssc);
3243 for (stack_pos = 0; stack_pos < stack.length; stack_pos++) {
3244 sym = stack[stack_pos];
3245 out += " " + (sym.get_scale() * this.alphabet.get_ic()) + " (" + sym.get_symbol() + ") numchar\n";
3246 }
3247 if (errbars) {
3248 out += " " + pspm.get_error(this.alphabet) + " Ibeam\n";
3249 }
3250 out += "endstack\n\n";
3251 }
3252 if (pspm.get_right_trim() > 0 || pspm.get_left_trim() == pspm.get_motif_length()) {
3253 out += "RestoreColour\n";
3254 }
3255 out += "EndLine\n";
3256 }
3257 return out;
3258 };
3259
3260 Logo.prototype.as_eps = function(options) {
3261 "use strict";
3262 if (this.xlate_nsyms != 1) throw new Error("Unsupported setting xlate_nsyms for EPS");
3263 if (this.xlate_start != null) throw new Error("Unsupported setting xlate_start for EPS");
3264 if (this.xlate_end != null) throw new Error("Unsupported setting xlate_end for EPS");
3265
3266 var LOGOHEIGHT = 7.5; // default height of line in cm
3267 var cm2pts, height, width, now, ssc, errbars;
3268 if (typeof options === "undefined") {
3269 options = {};
3270 }
3271 cm2pts = 72 / 2.54;
3272 if (typeof options.logo_height == "number") {
3273 height = options.logo_height;
3274 } else {
3275 height = LOGOHEIGHT * this.rows;
3276 }
3277 if (typeof options.logo_width == "number") {
3278 width = options.logo_width;
3279 } else {
3280 width = this.columns + 2;
3281 }
3282 now = new Date();
3283 ssc = (typeof options.ssc == "boolean" ? options.ssc : false);
3284 errbars = (typeof options.show_error_bar == "boolean" ? options.show_error_bar : ssc);
3285 var values = {
3286 "LOGOHEIGHT": height,
3287 "LOGOWIDTH": width,
3288 "BOUNDINGHEIGHT": Math.round(height * cm2pts),
3289 "BOUNDINGWIDTH": Math.round(width * cm2pts),
3290 "LOGOLINEHEIGHT": (height / this.rows),
3291 "CHARSPERLINE": this.columns,
3292 "BARBITS": this.alphabet.get_ic(),
3293 "LOGOTYPE": (this.alphabet.has_complement() ? "NA" : "AA"),
3294 "CREATIONDATE": now.getDate() + "." + (now.getMonth() + 1) + "." + now.getFullYear() + " " + now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds(),
3295 "ERRORBARFRACTION": (typeof options.error_bar_fraction == "number" ? options.error_bar_fraction : 1.0),
3296 "TICBITS": (typeof options.ticbits == "number" ? options.ticbits : 1.0),
3297 "TITLE": (typeof options.title == "string" ? options.title : ""),
3298 "FINEPRINT": (typeof options.fineprint == "string" ? options.fineprint : this.fine_text),
3299 "XAXISLABEL": (typeof options.xaxislabel == "string" ? options.xaxislabel : ""),
3300 "YAXISLABEL": (typeof options.yaxislabel == "string" ? options.yaxislabel : "bits"),
3301 "SSC": ssc,
3302 "YAXIS": (typeof options.show_y_axis == "boolean" ? options.show_y_axis : this.y_axis),
3303 "SHOWENDS": (typeof options.show_ends == "boolean" ? options.show_ends : false),
3304 "ERRBAR": errbars,
3305 "OUTLINE": (typeof options.show_outline == "boolean" ? options.show_outline : false),
3306 "NUMBERING": (typeof options.show_numbering == "boolean" ? options.show_numbering : this.x_axis != 0),
3307 "SHOWINGBOX": (typeof options.show_box == "boolean" ? options.show_box : false),
3308 "CREATOR": (typeof options.creator == "string" ? options.creator : "motif_logo.js"),
3309 "FONTSIZE": (typeof options.label_font_size == "number" ? options.label_font_size : 12),
3310 "TITLEFONTSIZE": (typeof options.title_font_size == "number" ? options.title_font_size : 12),
3311 "SMALLFONTSIZE": (typeof options.small_font_size == "number" ? options.small_font_size : 6),
3312 "TOPMARGIN" : (typeof options.top_margin == "number" ? options.top_margin : 0.9),
3313 "BOTTOMMARGIN": (typeof options.bottom_margin == "number" ? options.bottom_margin : 0.9),
3314 "COLORDICT": this.alphabet._as_eps_dict(),
3315 "DATA": this._as_eps_data(ssc, errbars)
3316 };
3317 // now this requires that the script containing the template has been imported!
3318 return motif_logo_template(values);
3319 };
3320
3321 //======================================================================
3322 // end Logo object
3323 //======================================================================
3324
3325 // calculate the exact size (in pixels) of an object drawn on the
3326 // canvas assuming that the background of the canvas is transparent.
3327 function canvas_bounds(ctx, cwidth, cheight) {
3328 "use strict";
3329 var data, r, c, top_line, bottom_line, left_line, right_line,
3330 txt_width, txt_height;
3331
3332 // extract the image data
3333 data = ctx.getImageData(0, 0, cwidth, cheight).data;
3334
3335 // set initial values
3336 top_line = -1; bottom_line = -1; left_line = -1; right_line = -1;
3337 txt_width = 0; txt_height = 0;
3338
3339 // Find the top-most line with a non-transparent pixel
3340 for (r = 0; r < cheight; r++) {
3341 for (c = 0; c < cwidth; c++) {
3342 if (data[r * cwidth * 4 + c * 4 + 3]) {
3343 top_line = r;
3344 break;
3345 }
3346 }
3347 if (top_line != -1) {
3348 break;
3349 }
3350 }
3351
3352 // Only bother looking if we found at least one set pixel...
3353 if (top_line != -1) {
3354
3355 //find the last line with a non-transparent pixel
3356 for (r = cheight-1; r >= top_line; r--) {
3357 for(c = 0; c < cwidth; c++) {
3358 if(data[r * cwidth * 4 + c * 4 + 3]) {
3359 bottom_line = r;
3360 break;
3361 }
3362 }
3363 if (bottom_line != -1) {
3364 break;
3365 }
3366 }
3367 // calculate height
3368 txt_height = bottom_line - top_line + 1;
3369
3370 // Find the left-most line with a non-transparent pixel
3371 for (c = 0; c < cwidth; c++) {
3372 for (r = top_line; r <= bottom_line; r++) {
3373 if (data[r * cwidth * 4 + c * 4 + 3]) {
3374 left_line = c;
3375 break;
3376 }
3377 }
3378 if (left_line != -1) {
3379 break;
3380 }
3381 }
3382
3383 //find the right most line with a non-transparent pixel
3384 for (c = cwidth-1; c >= left_line; c--) {
3385 for(r = top_line; r <= bottom_line; r++) {
3386 if(data[r * cwidth * 4 + c * 4 + 3]) {
3387 right_line = c;
3388 break;
3389 }
3390 }
3391 if (right_line != -1) {
3392 break;
3393 }
3394 }
3395 txt_width = right_line - left_line + 1;
3396 }
3397
3398 //return the bounds
3399 return {bound_top: top_line, bound_bottom: bottom_line,
3400 bound_left: left_line, bound_right: right_line, width: txt_width,
3401 height: txt_height};
3402 }
3403
3404 //======================================================================
3405 // start RasterizedAlphabet
3406 //======================================================================
3407
3408 // Rasterize Alphabet
3409 // 1) Measure width of text at default font for all symbols in alphabet
3410 // 2) sort in width ascending
3411 // 3) Drop the top and bottom 10% (designed to ignore outliers like 'W' and 'I')
3412 // 4) Calculate the average as the maximum scaling factor (designed to stop I becoming a rectangular blob).
3413 // 5) Assume scale of zero would result in width of zero, interpolate scale required to make perfect width font
3414 // 6) Draw text onto temp canvas at calculated scale
3415 // 7) Find bounds of drawn text
3416 // 8) Paint on to another canvas at the desired height (but only scaling width to fit if larger).
3417 var RasterizedAlphabet = function(alphabet, logo_scale, font, width) {
3418 "use strict";
3419 var default_size, safety_pad, canvas, ctx, middle, baseline, widths, sizes,
3420 i, sym, size, tenpercent, avg_width, scale,
3421 target_width, target_height;
3422 //variable prototypes
3423 this.alphabet = alphabet;
3424 this.scale = logo_scale;
3425 this.sym_cache = {};
3426 this.stack_num_cache = [];
3427 this.scale_num_cache = [];
3428 // size of canvas
3429 default_size = 60; // size of measuring canvas
3430 safety_pad = 20; // pixels to pad around so we don't miss the edges
3431 // create a canvas to do our measuring
3432 canvas = document.createElement("canvas");
3433 if (!canvas.getContext) throw new Error("No canvas support");
3434 canvas.width = default_size + 2 * safety_pad;
3435 canvas.height = default_size + 2 * safety_pad;
3436 middle = Math.round(canvas.width / 2);
3437 baseline = Math.round(canvas.height - safety_pad);
3438 ctx = canvas.getContext('2d');
3439 if (!supports_text(ctx)) throw new Error("Canvas does not support text");
3440 ctx.font = font;
3441 ctx.textAlign = "center";
3442 ctx.translate(middle, baseline);
3443 // list of widths
3444 widths = [];
3445 sizes = [];
3446 //now measure each letter in the alphabet
3447 for (i = 0; i < alphabet.get_size_core(); ++i) {
3448 // reset the canvas
3449 ctx.clearRect(0, 0, canvas.width, canvas.height);
3450 ctx.fillStyle = alphabet.get_colour(i);
3451 // draw the test text
3452 ctx.fillText(alphabet.get_symbol(i), 0, 0);
3453 //measure
3454 size = canvas_bounds(ctx, canvas.width, canvas.height);
3455 if (size.width === 0) throw new Error("Invisible symbol!");
3456 widths.push(size.width);
3457 sizes[i] = size;
3458 }
3459 //sort the widths
3460 widths.sort(function(a,b) {return a - b;});
3461 //drop 10% of the items off each end
3462 tenpercent = Math.floor(widths.length / 10);
3463 for (i = 0; i < tenpercent; ++i) {
3464 widths.pop();
3465 widths.shift();
3466 }
3467 //calculate average width
3468 avg_width = 0;
3469 for (i = 0; i < widths.length; ++i) {
3470 avg_width += widths[i];
3471 }
3472 avg_width /= widths.length;
3473 // calculate the target width
3474 target_width = width * this.scale * 2;
3475 // calculate scales
3476 for (i = 0; i < alphabet.get_size_core(); ++i) {
3477 sym = alphabet.get_symbol(i);
3478 size = sizes[i];
3479 // calculate scale
3480 scale = target_width / Math.max(avg_width, size.width);
3481 // estimate scaled height
3482 target_height = size.height * scale;
3483 // create an appropriately sized canvas
3484 canvas = document.createElement("canvas");
3485 canvas.width = target_width;
3486 canvas.height = target_height + safety_pad * 2;
3487 // calculate the middle
3488 middle = Math.round(canvas.width / 2);
3489 // calculate the baseline
3490 baseline = Math.round(canvas.height - safety_pad);
3491 // get the context and prepare to draw the rasterized text
3492 ctx = canvas.getContext('2d');
3493 ctx.font = font;
3494 ctx.fillStyle = alphabet.get_colour(i);
3495 ctx.textAlign = "center";
3496 ctx.translate(middle, baseline);
3497 ctx.save();
3498 ctx.scale(scale, scale);
3499 // draw the text
3500 ctx.fillText(sym, 0, 0);
3501 ctx.restore();
3502 this.sym_cache[sym] = {"image": canvas, "size": canvas_bounds(ctx, canvas.width, canvas.height)};
3503 }
3504 };
3505
3506 RasterizedAlphabet.prototype.get_alphabet = function() {
3507 return this.alphabet;
3508 };
3509
3510 RasterizedAlphabet.prototype.get_scale = function() {
3511 return this.scale;
3512 };
3513
3514 RasterizedAlphabet.prototype.draw_stack_sym = function(ctx, letter, dx, dy, dWidth, dHeight) {
3515 "use strict";
3516 var entry, image, size;
3517 entry = this.sym_cache[letter];
3518 image = entry.image;
3519 size = entry.size;
3520 ctx.drawImage(image, 0, size.bound_top -1, image.width, size.height+1, dx, dy, dWidth, dHeight);
3521 };
3522
3523 RasterizedAlphabet.prototype.draw_stack_num = function(ctx, font, stack_width, index) {
3524 var image, image_ctx, text_length;
3525 if (index >= this.stack_num_cache.length) {
3526 image = document.createElement("canvas");
3527 // measure the text
3528 image_ctx = image.getContext('2d');
3529 image_ctx.save();
3530 image_ctx.font = font;
3531 text_length = image_ctx.measureText("" + (index + 1)).width;
3532 image_ctx.restore();
3533 // resize the canvas to fit
3534 image.width = Math.ceil(stack_width);
3535 image.height = Math.ceil(text_length);
3536 // draw the text
3537 image_ctx = image.getContext('2d');
3538 image_ctx.translate(Math.round(stack_width / 2), 0);
3539 image_ctx.font = font;
3540 image_ctx.textBaseline = "middle";
3541 image_ctx.textAlign = "right";
3542 image_ctx.rotate(-(Math.PI / 2));
3543 image_ctx.fillText("" + (index + 1), 0, 0);
3544 this.stack_num_cache[index] = image;
3545 } else {
3546 image = this.stack_num_cache[index];
3547 }
3548 ctx.drawImage(image, 0, 0);
3549 }
3550
3551 RasterizedAlphabet.prototype.draw_scale_num = function(ctx, font, num) {
3552 var image, image_ctx, text_size, m_length;
3553 if (num >= this.scale_num_cache.length) {
3554 image = document.createElement("canvas");
3555 // measure the text
3556 image_ctx = image.getContext('2d');
3557 image_ctx.font = font;
3558 text_size = image_ctx.measureText("" + num);
3559 if (text_size.actualBoundingBoxAscent && text_size.actualBoundingBoxDesent) {
3560 // resize the canvas to fit
3561 image.width = Math.ceil(text_size.width);
3562 image.height = Math.ceil(text_size.actualBoundingBoxAscent + text_size.actualBoundingBoxDesent);
3563 // draw the text
3564 image_ctx = image.getContext('2d');
3565 image_ctx.font = font;
3566 image_ctx.textAlign = "right";
3567 image_ctx.fillText("" + num, image.width, text_size.actualBoundingBoxAscent);
3568 } else {
3569 // measure width of 'm' to approximate height, we double it later anyway
3570 m_length = image_ctx.measureText("m").width;
3571 // resize the canvas to fit
3572 image.width = Math.ceil(text_size.width);
3573 image.height = Math.ceil(2 * m_length);
3574 // draw the text
3575 image_ctx = image.getContext('2d');
3576 image_ctx.font = font;
3577 image_ctx.textAlign = "right";
3578 image_ctx.textBaseline = "middle";
3579 image_ctx.fillText("" + num, image.width, m_length);
3580 }
3581 this.scale_num_cache[num] = image;
3582 } else {
3583 image = this.scale_num_cache[num];
3584 }
3585 ctx.drawImage(image, -image.width, -Math.round(image.height / 2))
3586 }
3587
3588 //======================================================================
3589 // end RasterizedAlphabet
3590 //======================================================================
3591
3592 //======================================================================
3593 // start LogoMetrics object
3594 //======================================================================
3595
3596 var LogoMetrics = function(ctx, logo_columns, logo_rows, has_names, has_finetext, x_axis, y_axis) {
3597 "use strict";
3598 var i, row_height;
3599 //variable prototypes
3600 this.pad_top = (has_names ? 5 : 0);
3601 this.pad_left = (y_axis ? 10 : 0);
3602 this.pad_right = (has_finetext ? 15 : 0);
3603 this.pad_bottom = 0;
3604 this.pad_middle = 20;
3605 this.name_height = 14;
3606 this.name_font = "bold " + this.name_height + "px Times, sans-serif";
3607 this.name_spacer = 0;
3608 this.y_axis = y_axis;
3609 this.y_label = "bits";
3610 this.y_label_height = 12;
3611 this.y_label_font = "bold " + this.y_label_height + "px Helvetica, sans-serif";
3612 this.y_label_spacer = 3;
3613 this.y_num_height = 12;
3614 this.y_num_width = 0;
3615 this.y_num_font = "bold " + this.y_num_height + "px Helvetica, sans-serif";
3616 this.y_tic_width = 5;
3617 this.stack_pad_left = 0;
3618 this.stack_font = "bold 25px Helvetica, sans-serif";
3619 this.stack_height = 90;
3620 this.stack_width = 26;
3621 this.stacks_pad_right = 5;
3622 this.x_axis = x_axis;
3623 this.x_num_above = 2;
3624 this.x_num_height = 12;
3625 this.x_num_width = 0;
3626 this.x_num_font = "bold " + this.x_num_height + "px Helvetica, sans-serif";
3627 this.fine_txt_height = 6;
3628 this.fine_txt_above = 2;
3629 this.fine_txt_font = "normal " + this.fine_txt_height + "px Helvetica, sans-serif";
3630 this.letter_metrics = new Array();
3631 this.summed_width = 0;
3632 this.summed_height = 0;
3633 //calculate the width of the y axis numbers
3634 ctx.font = this.y_num_font;
3635 for (i = 0; i <= 2; i++) {
3636 this.y_num_width = Math.max(this.y_num_width, ctx.measureText("" + i).width);
3637 }
3638 //calculate the width of the x axis numbers (but they are rotated so it becomes height)
3639 if (x_axis == 1) {
3640 ctx.font = this.x_num_font;
3641 for (i = 1; i <= logo_columns; i++) {
3642 this.x_num_width = Math.max(this.x_num_width, ctx.measureText("" + i).width);
3643 }
3644 } else if (x_axis == 0) {
3645 this.x_num_height = 4;
3646 this.x_num_width = 4;
3647 } else {
3648 this.x_num_height = 0;
3649 this.x_num_width = 0;
3650 }
3651
3652 //calculate how much vertical space we want to draw this
3653 //first we add the padding at the top and bottom since that's always there
3654 this.summed_height += this.pad_top + this.pad_bottom;
3655 //all except the last row have the same amount of space allocated to them
3656 if (logo_rows > 1) {
3657 row_height = this.stack_height + this.pad_middle;
3658 if (has_names) {
3659 row_height += this.name_height;
3660 //the label is allowed to overlap into the spacer
3661 row_height += Math.max(this.y_num_height/2, this.name_spacer);
3662 //the label is allowed to overlap the space used by the other label
3663 row_height += Math.max(this.y_num_height/2, this.x_num_height + this.x_num_above);
3664 } else {
3665 row_height += this.y_num_height/2;
3666 //the label is allowed to overlap the space used by the other label
3667 row_height += Math.max(this.y_num_height/2, this.x_num_height + this.x_num_above);
3668 }
3669 this.summed_height += row_height * (logo_rows - 1);
3670 }
3671 //the last row has the name and fine text below it but no padding
3672 this.summed_height += this.stack_height + (this.y_axis ? this.y_num_height/2 : 0);
3673
3674 var fine_txt_total = (has_finetext ? this.fine_txt_height + this.fine_txt_above : 0);
3675 if (has_names) {
3676 this.summed_height += fine_txt_total + this.name_height;
3677 this.summed_height += Math.max((this.y_axis ? this.y_num_height/2 : 0),
3678 this.x_num_height + this.x_num_above + this.name_spacer);
3679 } else {
3680 this.summed_height += Math.max((this.y_axis ? this.y_num_height/2 : 0),
3681 this.x_num_height + this.x_num_above + fine_txt_total);
3682 }
3683
3684 //calculate how much horizontal space we want to draw this
3685 //first add the padding at the left and right since that's always there
3686 this.summed_width += this.pad_left + this.pad_right;
3687 if (this.y_axis) {
3688 //add on the space for the y-axis label
3689 this.summed_width += this.y_label_height + this.y_label_spacer;
3690 //add on the space for the y-axis
3691 this.summed_width += this.y_num_width + this.y_tic_width;
3692 }
3693 //add on the space for the stacks
3694 this.summed_width += (this.stack_pad_left + this.stack_width) * logo_columns;
3695 //add on the padding after the stacks (an offset from the fine text)
3696 this.summed_width += this.stacks_pad_right;
3697
3698 };
3699
3700 //======================================================================
3701 // end LogoMetrics object
3702 //======================================================================
3703
3704 //found this trick at http://talideon.com/weblog/2005/02/detecting-broken-images-js.cfm
3705 function image_ok(img) {
3706 "use strict";
3707 // During the onload event, IE correctly identifies any images that
3708 // weren't downloaded as not complete. Others should too. Gecko-based
3709 // browsers act like NS4 in that they report this incorrectly.
3710 if (!img.complete) {
3711 return false;
3712 }
3713 // However, they do have two very useful properties: naturalWidth and
3714 // naturalHeight. These give the true size of the image. If it failed
3715 // to load, either of these should be zero.
3716 if (typeof img.naturalWidth !== "undefined" && img.naturalWidth === 0) {
3717 return false;
3718 }
3719 // No other way of checking: assume it's ok.
3720 return true;
3721 }
3722
3723 function supports_text(ctx) {
3724 "use strict";
3725 if (!ctx.fillText) {
3726 return false;
3727 }
3728 if (!ctx.measureText) {
3729 return false;
3730 }
3731 return true;
3732 }
3733
3734 //draws the scale, returns the width
3735 function draw_scale(ctx, metrics, alphabet_ic, raster) {
3736 "use strict";
3737 var tic_height, i;
3738 tic_height = metrics.stack_height / alphabet_ic;
3739 ctx.save();
3740 ctx.translate(metrics.y_label_height, metrics.y_num_height/2);
3741 //draw the axis label
3742 ctx.save();
3743 ctx.font = metrics.y_label_font;
3744 ctx.translate(0, metrics.stack_height/2);
3745 ctx.rotate(-(Math.PI / 2));
3746 ctx.textAlign = "center";
3747 ctx.fillText("bits", 0, 0);
3748 ctx.restore();
3749
3750 ctx.translate(metrics.y_label_spacer + metrics.y_num_width, 0);
3751
3752 //draw the axis tics
3753 ctx.save();
3754 ctx.translate(0, metrics.stack_height);
3755 for (i = 0; i <= alphabet_ic; i++) {
3756 //draw the number
3757 ctx.save();
3758 ctx.translate(-1, 0);
3759 raster.draw_scale_num(ctx, metrics.y_num_font, i);
3760 ctx.restore();
3761 //draw the tic
3762 ctx.fillRect(0, -1, metrics.y_tic_width, 2);
3763 //prepare for next tic
3764 ctx.translate(0, -tic_height);
3765 }
3766 ctx.restore();
3767
3768 ctx.fillRect(metrics.y_tic_width - 2, 0, 2, metrics.stack_height)
3769
3770 ctx.restore();
3771 }
3772
3773 function draw_stack_num(ctx, metrics, row_index, raster) {
3774 "use strict";
3775 ctx.save();
3776 ctx.translate(0, Math.round(metrics.stack_height + metrics.x_num_above));
3777 if (metrics.x_axis == 1) {
3778 raster.draw_stack_num(ctx, metrics.x_num_font, metrics.stack_width, row_index);
3779 } else if (metrics.x_axis == 0) {
3780 // draw dots instead of the numbers (good for small logos)
3781 ctx.beginPath();
3782 var radius = Math.round(metrics.x_num_height / 2);
3783 ctx.arc(Math.round(metrics.stack_width / 2), radius, radius, 0, 2 * Math.PI, false);
3784 ctx.fill();
3785 }
3786 ctx.restore();
3787 }
3788
3789 function draw_stack(ctx, metrics, symbols, raster) {
3790 "use strict";
3791 var preferred_pad, sym_min, i, sym, sym_height, pad;
3792 preferred_pad = 0;
3793 sym_min = 5;
3794
3795 ctx.save();//1
3796 ctx.translate(0, metrics.stack_height);
3797 for (i = 0; i < symbols.length; i++) {
3798 sym = symbols[i];
3799 sym_height = metrics.stack_height * sym.get_scale();
3800
3801 pad = preferred_pad;
3802 if (sym_height - pad < sym_min) {
3803 pad = Math.min(pad, Math.max(0, sym_height - sym_min));
3804 }
3805 sym_height -= pad;
3806
3807 //translate to the correct position
3808 ctx.translate(0, -(pad/2 + sym_height));
3809
3810 //draw
3811 raster.draw_stack_sym(ctx, sym.get_symbol(), 0, 0, metrics.stack_width, sym_height);
3812 //translate past the padding
3813 ctx.translate(0, -(pad/2));
3814 }
3815 ctx.restore();//1
3816 }
3817
3818 function draw_dashed_line(ctx, pattern, start, x1, y1, x2, y2) {
3819 "use strict";
3820 var x, y, len, i, dx, dy, tlen, theta, mulx, muly, lx, ly;
3821 dx = x2 - x1;
3822 dy = y2 - y1;
3823 tlen = Math.pow(dx*dx + dy*dy, 0.5);
3824 theta = Math.atan2(dy,dx);
3825 mulx = Math.cos(theta);
3826 muly = Math.sin(theta);
3827 lx = [];
3828 ly = [];
3829 for (i = 0; i < pattern; ++i) {
3830 lx.push(pattern[i] * mulx);
3831 ly.push(pattern[i] * muly);
3832 }
3833 i = start;
3834 x = x1;
3835 y = y1;
3836 len = 0;
3837 ctx.beginPath();
3838 while (len + pattern[i] < tlen) {
3839 ctx.moveTo(x, y);
3840 x += lx[i];
3841 y += ly[i];
3842 ctx.lineTo(x, y);
3843 len += pattern[i];
3844 i = (i + 1) % pattern.length;
3845 x += lx[i];
3846 y += ly[i];
3847 len += pattern[i];
3848 i = (i + 1) % pattern.length;
3849 }
3850 if (len < tlen) {
3851 ctx.moveTo(x, y);
3852 x += mulx * (tlen - len);
3853 y += muly * (tlen - len);
3854 ctx.lineTo(x, y);
3855 }
3856 ctx.stroke();
3857 }
3858
3859 function draw_trim_background(ctx, metrics, left_start, left_end, left_divider, right_start, right_end, right_divider) {
3860 "use strict";
3861 var left_size = left_end - left_start;
3862 var right_size = right_end - right_start;
3863 var line_x;
3864
3865 ctx.save();//s8
3866 ctx.fillStyle = "rgb(240, 240, 240)";
3867 if (left_size > 0) {
3868 ctx.fillRect(left_start * metrics.stack_width, 0, left_size * metrics.stack_width, metrics.stack_height);
3869 }
3870 if (right_size > 0) {
3871 ctx.fillRect(right_start * metrics.stack_width, 0, right_size * metrics.stack_width, metrics.stack_height);
3872 }
3873 ctx.fillStyle = "rgb(51, 51, 51)";
3874 if (left_size > 0 && left_divider) {
3875 line_x = (left_end * metrics.stack_width) - 0.5;
3876 draw_dashed_line(ctx, [3], 0, line_x, 0, line_x, metrics.stack_height);
3877 }
3878 if (right_size > 0 && right_divider) {
3879 line_x = (right_start * metrics.stack_width) + 0.5;
3880 draw_dashed_line(ctx, [3], 0, line_x, 0, line_x, metrics.stack_height);
3881 }
3882 ctx.restore();//s8
3883 }
3884
3885 function size_logo_on_canvas(logo, canvas, show_names, scale) {
3886 "use strict";
3887 var draw_name, draw_finetext, metrics;
3888 draw_name = (typeof show_names === "boolean" ? show_names : (logo.get_rows() > 1));
3889 draw_finetext = (logo.fine_text.length > 0);
3890 if (canvas.width !== 0 && canvas.height !== 0) {
3891 return;
3892 }
3893 metrics = new LogoMetrics(canvas.getContext('2d'),
3894 logo.get_xlate_columns(), logo.get_rows(), draw_name, draw_finetext, logo.x_axis, logo.y_axis);
3895 if (typeof scale == "number") {
3896 //resize the canvas to fit the scaled logo
3897 canvas.width = metrics.summed_width * scale;
3898 canvas.height = metrics.summed_height * scale;
3899 } else {
3900 if (canvas.width === 0 && canvas.height === 0) {
3901 canvas.width = metrics.summed_width;
3902 canvas.height = metrics.summed_height;
3903 } else if (canvas.width === 0) {
3904 canvas.width = metrics.summed_width * (canvas.height / metrics.summed_height);
3905 } else if (canvas.height === 0) {
3906 canvas.height = metrics.summed_height * (canvas.width / metrics.summed_width);
3907 }
3908 }
3909 }
3910
3911 function draw_logo_on_canvas(logo, canvas, show_names, scale) {
3912 "use strict";
3913 var i, draw_name, draw_finetext, ctx, metrics, raster, pspm_i, pspm,
3914 offset, col_index, motif_position, ssc;
3915 ssc = false;
3916 draw_name = (typeof show_names === "boolean" ? show_names : (logo.get_rows() > 1));
3917 draw_finetext = (logo.fine_text.length > 0);
3918 ctx = canvas.getContext('2d');
3919 //assume that the user wants the canvas scaled equally so calculate what the best width for this image should be
3920 metrics = new LogoMetrics(ctx, logo.get_xlate_columns(), logo.get_rows(), draw_name, draw_finetext, logo.x_axis, logo.y_axis);
3921 if (typeof scale == "number") {
3922 //resize the canvas to fit the scaled logo
3923 canvas.width = metrics.summed_width * scale;
3924 canvas.height = metrics.summed_height * scale;
3925 } else {
3926 if (canvas.width === 0 && canvas.height === 0) {
3927 scale = 1;
3928 canvas.width = metrics.summed_width;
3929 canvas.height = metrics.summed_height;
3930 } else if (canvas.width === 0) {
3931 scale = canvas.height / metrics.summed_height;
3932 canvas.width = metrics.summed_width * scale;
3933 } else if (canvas.height === 0) {
3934 scale = canvas.width / metrics.summed_width;
3935 canvas.height = metrics.summed_height * scale;
3936 } else {
3937 scale = Math.min(canvas.width / metrics.summed_width, canvas.height / metrics.summed_height);
3938 }
3939 }
3940 // cache the raster based on the assumption that we will be drawing a lot
3941 // of logos the same size and alphabet
3942 if (typeof draw_logo_on_canvas.raster_cache === "undefined") {
3943 draw_logo_on_canvas.raster_cache = [];
3944 }
3945 for (i = 0; i < draw_logo_on_canvas.raster_cache.length; i++) {
3946 raster = draw_logo_on_canvas.raster_cache[i];
3947 if (raster.get_alphabet().equals(logo.alphabet) &&
3948 Math.abs(raster.get_scale() - scale) < 0.1) break;
3949 raster = null;
3950 }
3951 if (raster == null) {
3952 raster = new RasterizedAlphabet(logo.alphabet, scale, metrics.stack_font, metrics.stack_width);
3953 draw_logo_on_canvas.raster_cache.push(raster);
3954 }
3955 ctx = canvas.getContext('2d');
3956 ctx.save();//s1
3957 ctx.scale(scale, scale);
3958 ctx.save();//s2
3959 ctx.save();//s7
3960 //create margin
3961 ctx.translate(Math.round(metrics.pad_left), Math.round(metrics.pad_top));
3962 for (pspm_i = 0; pspm_i < logo.get_rows(); ++pspm_i) {
3963 pspm = logo.get_pspm(pspm_i);
3964 offset = logo.get_offset(pspm_i);
3965 //optionally draw name if this isn't the last row or is the only row
3966 if (draw_name && (logo.get_rows() == 1 || pspm_i != (logo.get_rows()-1))) {
3967 ctx.save();//s4
3968 ctx.translate(Math.round(metrics.summed_width/2), Math.round(metrics.name_height));
3969 ctx.font = metrics.name_font;
3970 ctx.textAlign = "center";
3971 ctx.fillText(pspm.name, 0, 0);
3972 ctx.restore();//s4
3973 ctx.translate(0, Math.round(metrics.name_height +
3974 Math.min(0, metrics.name_spacer - metrics.y_num_height/2)));
3975 }
3976 //draw scale
3977 if (logo.y_axis) draw_scale(ctx, metrics, logo.alphabet.get_ic(), raster);
3978 ctx.save();//s5
3979 //translate across past the scale
3980 if (logo.y_axis) {
3981 ctx.translate(Math.round(metrics.y_label_height + metrics.y_label_spacer +
3982 metrics.y_num_width + metrics.y_tic_width), Math.round(metrics.y_num_height / 2));
3983 }
3984 //draw the trimming background
3985 if (pspm.get_left_trim() > 0 || pspm.get_right_trim() > 0) {
3986 var left_start = offset * logo.get_xlate_nsyms();
3987 var left_end = (offset + pspm.get_left_trim()) * logo.get_xlate_nsyms();
3988 var left_divider = true;
3989 if (left_end < logo.get_xlate_start() || left_start > logo.get_xlate_end()) {
3990 // no overlap
3991 left_start = 0;
3992 left_end = 0;
3993 left_divider = false;
3994 } else {
3995 if (left_start < logo.get_xlate_start()) {
3996 left_start = logo.get_xlate_start();
3997 }
3998 if (left_end > logo.get_xlate_end()) {
3999 left_end = logo.get_xlate_end();
4000 left_divider = false;
4001 }
4002 left_start -= logo.get_xlate_start();
4003 left_end -= logo.get_xlate_start();
4004 if (left_end < left_start) {
4005 left_start = 0;
4006 left_end = 0;
4007 left_divider = false;
4008 }
4009 }
4010 var right_end = (offset + pspm.get_motif_length()) * logo.get_xlate_nsyms();
4011 //var right_start = right_end - (pspm.get_left_trim() * logo.get_xlate_nsyms());
4012 var right_start = right_end - (pspm.get_right_trim() * logo.get_xlate_nsyms());
4013 var right_divider = true;
4014 if (right_end < logo.get_xlate_start() || right_start > logo.get_xlate_end()) {
4015 // no overlap
4016 right_start = 0;
4017 right_end = 0;
4018 right_divider = false;
4019 } else {
4020 if (right_start < logo.get_xlate_start()) {
4021 right_start = logo.get_xlate_start();
4022 right_divider = false;
4023 }
4024 if (right_end > logo.get_xlate_end()) {
4025 right_end = logo.get_xlate_end();
4026 }
4027 right_start -= logo.get_xlate_start();
4028 right_end -= logo.get_xlate_start();
4029 if (right_end < right_start) {
4030 right_start = 0;
4031 right_end = 0;
4032 right_divider = false;
4033 }
4034 }
4035 draw_trim_background(ctx, metrics, left_start, left_end, left_divider, right_start, right_end, right_divider);
4036 }
4037 //draw letters
4038 var xlate_col;
4039 for (xlate_col = logo.get_xlate_start(); xlate_col < logo.get_xlate_end(); xlate_col++) {
4040 ctx.translate(metrics.stack_pad_left,0);
4041 col_index = Math.floor(xlate_col / logo.get_xlate_nsyms());
4042 if (xlate_col % logo.get_xlate_nsyms() == 0) {
4043 if (col_index >= offset && col_index < (offset + pspm.get_motif_length())) {
4044 motif_position = col_index - offset;
4045 draw_stack_num(ctx, metrics, motif_position, raster);
4046 draw_stack(ctx, metrics, pspm.get_stack(motif_position, logo.alphabet, ssc), raster);
4047 }
4048 } else {
4049 if (col_index >= offset && col_index < (offset + pspm.get_motif_length())) {
4050 ctx.save();// s5.1
4051 ctx.translate(0, Math.round(metrics.stack_height));
4052 // TODO draw a dot or dash or something to indicate continuity of the motif
4053 ctx.restore(); //s5.1
4054 }
4055 }
4056 ctx.translate(Math.round(metrics.stack_width), 0);
4057 }
4058 ctx.restore();//s5
4059 ////optionally draw name if this is the last row but isn't the only row
4060 if (draw_name && (logo.get_rows() != 1 && pspm_i == (logo.get_rows()-1))) {
4061 //translate vertically past the stack and axis's
4062 ctx.translate(0, metrics.y_num_height/2 + metrics.stack_height +
4063 Math.max(metrics.y_num_height/2, metrics.x_num_above + metrics.x_num_width + metrics.name_spacer));
4064
4065 ctx.save();//s6
4066 ctx.translate(metrics.summed_width/2, metrics.name_height);
4067 ctx.font = metrics.name_font;
4068 ctx.textAlign = "center";
4069 ctx.fillText(pspm.name, 0, 0);
4070 ctx.restore();//s6
4071 ctx.translate(0, metrics.name_height);
4072 } else {
4073 //translate vertically past the stack and axis's
4074 ctx.translate(0, metrics.y_num_height/2 + metrics.stack_height +
4075 Math.max(metrics.y_num_height/2, metrics.x_num_above + metrics.x_num_width));
4076 }
4077 //if not the last row then add middle padding
4078 if (pspm_i != (logo.get_rows() -1)) {
4079 ctx.translate(0, metrics.pad_middle);
4080 }
4081 }
4082 ctx.restore();//s7
4083 if (logo.fine_text.length > 0) {
4084 ctx.translate(metrics.summed_width - metrics.pad_right, metrics.summed_height - metrics.pad_bottom);
4085 ctx.font = metrics.fine_txt_font;
4086 ctx.textAlign = "right";
4087 ctx.fillText(logo.fine_text, 0,0);
4088 }
4089 ctx.restore();//s2
4090 ctx.restore();//s1
4091 }
4092
4093 function create_canvas(c_width, c_height, c_id, c_title, c_display) {
4094 "use strict";
4095 var canvas = document.createElement("canvas");
4096 //check for canvas support before attempting anything
4097 if (!canvas.getContext) {
4098 return null;
4099 }
4100 var ctx = canvas.getContext('2d');
4101 //check for html5 text drawing support
4102 if (!supports_text(ctx)) {
4103 return null;
4104 }
4105 //size the canvas
4106 canvas.width = c_width;
4107 canvas.height = c_height;
4108 canvas.id = c_id;
4109 canvas.title = c_title;
4110 canvas.style.display = c_display;
4111 return canvas;
4112 }
4113
4114 function logo_1(alphabet, fine_text, pspm) {
4115 "use strict";
4116 var logo = new Logo(alphabet, fine_text);
4117 logo.add_pspm(pspm);
4118 return logo;
4119 }
4120
4121 function logo_2(alphabet, fine_text, target, query, query_offset) {
4122 "use strict";
4123 var logo = new Logo(alphabet, fine_text);
4124 if (query_offset < 0) {
4125 logo.add_pspm(target, -query_offset);
4126 logo.add_pspm(query);
4127 } else {
4128 logo.add_pspm(target);
4129 logo.add_pspm(query, query_offset);
4130 }
4131 return logo;
4132 }
4133
4134 /*
4135 * Specifies an alternate source for an image.
4136 * If the image with the image_id specified has
4137 * not loaded then a generated logo will be used
4138 * to replace it.
4139 *
4140 * Note that the image must either have dimensions
4141 * or a scale must be set.
4142 */
4143 function alternate_logo(logo, image_id, scale) {
4144 "use strict";
4145 var image = document.getElementById(image_id);
4146 if (!image) {
4147 alert("Can't find specified image id (" + image_id + ")");
4148 return;
4149 }
4150 //if the image has loaded then there is no reason to use the canvas
4151 if (image_ok(image)) {
4152 return;
4153 }
4154 //the image has failed to load so replace it with a canvas if we can.
4155 var canvas = create_canvas(image.width, image.height, image_id, image.title, image.style.display);
4156 if (canvas === null) {
4157 return;
4158 }
4159 //draw the logo on the canvas
4160 draw_logo_on_canvas(logo, canvas, null, scale);
4161 //replace the image with the canvas
4162 image.parentNode.replaceChild(canvas, image);
4163 }
4164
4165 /*
4166 * Specifies that the element with the specified id
4167 * should be replaced with a generated logo.
4168 */
4169 function replace_logo(logo, replace_id, scale, title_txt, display_style) {
4170 "use strict";
4171 var element = document.getElementById(replace_id);
4172 if (!replace_id) {
4173 alert("Can't find specified id (" + replace_id + ")");
4174 return;
4175 }
4176 //found the element!
4177 var canvas = create_canvas(50, 120, replace_id, title_txt, display_style);
4178 if (canvas === null) {
4179 return;
4180 }
4181 //draw the logo on the canvas
4182 draw_logo_on_canvas(logo, canvas, null, scale);
4183 //replace the element with the canvas
4184 element.parentNode.replaceChild(canvas, element);
4185 }
4186
4187 /*
4188 * Fast string trimming implementation found at
4189 * http://blog.stevenlevithan.com/archives/faster-trim-javascript
4190 *
4191 * Note that regex is good at removing leading space but
4192 * bad at removing trailing space as it has to first go through
4193 * the whole string.
4194 */
4195 function trim (str) {
4196 "use strict";
4197 var ws, i;
4198 str = str.replace(/^\s\s*/, '');
4199 ws = /\s/; i = str.length;
4200 while (ws.test(str.charAt(--i)));
4201 return str.slice(0, i + 1);
4202 }
4203
4204 //
4205 // Delay drawing a logo
4206 //
4207 var DelayLogoTask = function(logo, canvas) {
4208 "use strict";
4209 canvas.width = canvas.width; // clear canvas
4210 this.logo = logo;
4211 this.canvas = canvas;
4212 };
4213
4214 DelayLogoTask.prototype.run = function () {
4215 "use strict";
4216 this.canvas.width = this.canvas.width; // clear canvas
4217 draw_logo_on_canvas(this.logo, this.canvas, false);
4218 };
4219
4220 /*
4221 * Make a canvas with the motif logo drawn on it.
4222 */
4223 function make_logo(alphabet, pspm, height, rc, offset, className) {
4224 if (rc) pspm = pspm.copy().reverse_complement(alphabet);
4225 var logo = new Logo(alphabet);
4226 logo.add_pspm(pspm, offset);
4227 var canvas = document.createElement('canvas');
4228 var sizeit = (height < 0);
4229 canvas.height = (sizeit ? -height : height);
4230 canvas.width = 0;
4231 canvas.className = className;
4232 if (sizeit) size_logo_on_canvas(logo, canvas, false);
4233 add_draw_task(canvas, new DelayLogoTask(logo, canvas));
4234 return canvas;
4235 }
4236 </script>
4237 <script>
4238
4239 // PRIVATE GLOBAL (uhoh)
4240 var _block_colour_lookup = {};
4241
4242 function block_colour(index) {
4243 function hsl2rgb(hue, saturation, lightness) {
4244 "use strict";
4245 function _hue(p, q, t) {
4246 "use strict";
4247 if (t < 0) t += 1;
4248 else if (t > 1) t -= 1;
4249 if (t < (1.0 / 6.0)) {
4250 return p + ((q - p) * 6.0 * t);
4251 } else if (t < 0.5) {
4252 return q;
4253 } else if (t < (2.0 / 3.0)) {
4254 return p + ((q - p) * ((2.0 / 3.0) - t) * 6.0);
4255 } else {
4256 return p;
4257 }
4258 }
4259 function _pad_hex(value) {
4260 var hex = Math.round(value * 255).toString(16);
4261 if (hex.length < 2) hex = "0" + hex;
4262 return hex;
4263 }
4264 var r, g, b, p, q;
4265 if (saturation == 0) {
4266 // achromatic (grayscale)
4267 r = lightness;
4268 g = lightness;
4269 b = lightness;
4270 } else {
4271 if (lightness < 0.5) {
4272 q = lightness * (1 + saturation);
4273 } else {
4274 q = lightness + saturation - (lightness * saturation);
4275 }
4276 p = (2 * lightness) - q;
4277 r = _hue(p, q, hue + (1.0 / 3.0));
4278 g = _hue(p, q, hue);
4279 b = _hue(p, q, hue - (1.0 / 3.0));
4280 }
4281 return "#" + _pad_hex(r) + _pad_hex(g) + _pad_hex(b);
4282 }
4283 if (typeof index !== "number" || index % 1 !== 0 || index < 0) return "#000000";
4284 // check for override
4285 if (_block_colour_lookup[index] == null) {
4286 var start = 0; //red
4287 var sat = 100;
4288 var light = 50;
4289 var divisions = 1 << Math.ceil(Math.log(index + 1) / Math.LN2);
4290 hue = start + (360 / divisions) * ((index - (divisions >> 1)) * 2 + 1);
4291 // colour input fields only support values in the form #RRGGBB
4292 _block_colour_lookup[index] = hsl2rgb(hue / 360, sat / 100, light / 100);
4293 }
4294 return _block_colour_lookup[index];
4295 }
4296
4297 function set_block_colour(index, new_colour) {
4298 _block_colour_lookup[index] = new_colour;
4299 var blocks = document.querySelectorAll("div.block_motif[data-colour-index=\"" + index + "\"]");
4300 var i;
4301 for (i = 0; i < blocks.length; i++) {
4302 blocks[i].style.backgroundColor = new_colour;
4303 }
4304 var swatches = document.querySelectorAll("div.legend_swatch[data-colour-index=\"" + index + "\"]");
4305 var picker;
4306 for (i = 0; i < swatches.length; i++) {
4307 swatches[i].style.backgroundColor = new_colour;
4308 picker = swatches[i].querySelector("input[type=\"color\"]");
4309 if (picker != null) picker.value = new_colour;
4310 }
4311 }
4312
4313 function make_block_legend_entry(motif_name, motif_colour_index) {
4314 if (typeof make_block_legend_entry.has_colour_picker !== "boolean") {
4315 // test if colour picker is supported, based off Modernizer
4316 // see http://stackoverflow.com/a/7787648/66387
4317 make_block_legend_entry.has_colour_picker = (function() {
4318 var doc_ele = document.documentElement;
4319 // We first check to see if the type we give it sticks..
4320 var input_ele = document.createElement('input');
4321 input_ele.setAttribute('type', 'color');
4322 var value_ok = input_ele.type !== 'text';
4323 if (value_ok) {
4324 // If the type does, we feed it a textual value, which shouldn't be valid.
4325 // If the value doesn't stick, we know there's input sanitization which infers a custom UI
4326 var smile = ':)';
4327 input_ele.value = smile;
4328 input_ele.style.cssText = 'position:absolute;visibility:hidden;';
4329 // chuck into DOM and force reflow for Opera bug in 11.00
4330 // github.com/Modernizr/Modernizr/issues#issue/159
4331 doc_ele.appendChild(input_ele);
4332 doc_ele.offsetWidth;
4333 value_ok = input_ele.value != smile;
4334 doc_ele.removeChild(input_ele);
4335 }
4336 return value_ok;
4337 })();
4338 }
4339 var entry = document.createElement("div");
4340 entry.className = "legend_entry";
4341 var swatch;
4342 swatch = document.createElement("div");
4343 swatch.className = "legend_swatch";
4344 swatch.setAttribute("data-colour-index", motif_colour_index);
4345 swatch.style.backgroundColor = block_colour(motif_colour_index);
4346 if (make_block_legend_entry.has_colour_picker) {
4347 var picker = document.createElement("input");
4348 picker.type = "color";
4349 picker.value = block_colour(motif_colour_index);
4350 picker.addEventListener("change", function(e) {
4351 set_block_colour(motif_colour_index, picker.value);
4352 }, false);
4353 swatch.addEventListener("click", function(e) {
4354 picker.click();
4355 }, false);
4356 swatch.appendChild(picker);
4357 }
4358 entry.appendChild(swatch);
4359 var name = document.createElement("div");
4360 name.className = "legend_text";
4361 name.appendChild(document.createTextNode(motif_name));
4362 entry.appendChild(name);
4363 return entry;
4364 }
4365
4366 function make_block_ruler(max_len) {
4367 var container = document.createElement("div");
4368 container.className = "block_container";
4369 var step;
4370 if (max_len < 50) {
4371 step = 1;
4372 } else if (max_len < 100) {
4373 step = 2;
4374 } else if (max_len < 200) {
4375 step = 4;
4376 } else if (max_len < 500) {
4377 step = 10;
4378 } else if (max_len < 1000) {
4379 step = 20;
4380 } else if (max_len < 2000) {
4381 step = 40;
4382 } else if (max_len < 5000) {
4383 step = 100;
4384 } else if (max_len < 10000) {
4385 step = 200;
4386 } else if (max_len < 20000) {
4387 step = 400;
4388 } else {
4389 step = Math.floor(max_len / 20000) * 400;
4390 }
4391 var peroid;
4392 if (max_len < 10) {
4393 peroid = 1;
4394 } else if (max_len < 20) {
4395 peroid = 2;
4396 } else {
4397 peroid = 5;
4398 }
4399 var i, cycle, offset, tic, label;
4400 for (i = 0, cycle = 0; i < max_len; i += step, cycle = (cycle + 1) % peroid) {
4401 offset = "" + ((i / max_len) * 100) + "%";
4402 tic = document.createElement("div");
4403 tic.style.left = offset;
4404 tic.className = (cycle == 0 ? "tic_major" : "tic_minor");
4405 container.appendChild(tic);
4406 if (cycle == 0) {
4407 label = document.createElement("div");
4408 label.className = "tic_label";
4409 label.style.left = offset;
4410 label.appendChild(document.createTextNode(i));
4411 container.appendChild(label);
4412 }
4413 }
4414 return container;
4415 }
4416
4417 function _calculate_block_needle_drag_pos(e, data) {
4418 var mouse;
4419 e = e || window.event;
4420 if (e.pageX || ev.pageY) {
4421 mouse = {"x": e.pageX, "y": e.pageY};
4422 } else {
4423 mouse = {
4424 x:e.clientX + document.body.scrollLeft - document.body.clientLeft,
4425 y:e.clientY + document.body.scrollTop - document.body.clientTop
4426 };
4427 }
4428 var cont = data.container;
4429 var dragable_length = cont.clientWidth -
4430 (cont.style.paddingLeft ? cont.style.paddingLeft : 0) -
4431 (cont.style.paddingRight ? cont.style.paddingRight : 0);
4432 //I believe that the offset parent is the body
4433 //otherwise I would need to make this recursive
4434 //maybe clientLeft would work, but the explanation of
4435 //it is hard to understand and it apparently doesn't work
4436 //in firefox 2.
4437 var diff = mouse.x - cont.offsetLeft;
4438 if (diff < 0) diff = 0;
4439 if (diff > dragable_length) diff = dragable_length;
4440 var pos = Math.round(diff / dragable_length * data.max);
4441 if (pos > data.len) pos = data.len;
4442 return pos;
4443 }
4444
4445 function _update_block_needle_drag(e, data, done) {
4446 "use strict";
4447 var pos = _calculate_block_needle_drag_pos(e, data);
4448 // read the needle positions
4449 var left = parseInt(data.llabel.textContent, 10) - data.off - 1;
4450 var right = parseInt(data.rlabel.textContent, 10) - data.off;
4451 // validate needle positions
4452 if (left >= data.len) left = data.len - 1;
4453 if (left < 0) left = 0;
4454 if (right > data.len) right = data.len;
4455 if (right <= left) right = left + 1;
4456 // calculate the new needle positions
4457 if (data.moveboth) {
4458 var size = right - left;
4459 if (data.isleft) {
4460 if ((pos + size) > data.len) pos = data.len - size;
4461 left = pos;
4462 right = pos + size;
4463 } else {
4464 if ((pos - size) < 0) pos = size;
4465 left = pos - size;
4466 right = pos;
4467 }
4468 } else {
4469 if (data.isleft) {
4470 if (pos >= right) pos = right - 1;
4471 left = pos;
4472 } else {
4473 if (pos <= left) pos = left + 1;
4474 right = pos;
4475 }
4476 }
4477 // update the needle positions
4478 data.lneedle.style.left = "" + (left / data.max * 100) + "%";
4479 data.llabel.textContent = "" + (left + data.off + 1);
4480 data.rneedle.style.left = "" + (right / data.max * 100) + "%";
4481 data.rlabel.textContent = "" + (right + data.off);
4482 data.handler(left, right, done);
4483 }
4484
4485 function _make_block_needle_drag_start_handler(isleft, data) {
4486 return function (e) {
4487 data.isleft = isleft;
4488 data.moveboth = !(e.shiftKey);
4489 document.addEventListener("mousemove", data.drag_during, false);
4490 document.addEventListener("mouseup", data.drag_end, false);
4491 };
4492 }
4493
4494 function _make_block_needle_drag_end_handler(data) {
4495 return function (e) {
4496 document.removeEventListener("mousemove", data.drag_during, false);
4497 document.removeEventListener("mouseup", data.drag_end, false);
4498 _update_block_needle_drag(e, data, true);
4499 };
4500 }
4501
4502 function _make_block_needle_drag_during_handler(data) {
4503 return function (e) {
4504 _update_block_needle_drag(e, data, false);
4505 };
4506 }
4507
4508 // private function used by make_block_container
4509 function _make_block_needle(isleft, value, data) {
4510 var vbar = document.createElement('div');
4511 vbar.className = "block_needle " + (isleft ? "left" : "right");
4512 vbar.style.left = "" + (value / data.max * 100)+ "%";
4513 var label = document.createElement('div');
4514 label.className = "block_handle " + (isleft ? "left" : "right");
4515 // The needles sit between the sequence positions, so the left one sits at the
4516 // start and the right at the end. This is why 1 is added to the displayed
4517 // value for a left handle as the user doesn't need to know about this detail
4518 label.textContent = "" + (isleft ? value + data.off + 1 : value + data.off);
4519 label.unselectable = "on"; // so IE and Opera don't select the text, others are done in css
4520 label.title = "Drag to move the displayed range. Hold shift and drag to change " + (isleft ? "lower" : "upper") + " bound of the range.";
4521 vbar.appendChild(label);
4522 if (isleft) {
4523 data.lneedle = vbar;
4524 data.llabel = label;
4525 } else {
4526 data.rneedle = vbar;
4527 data.rlabel = label;
4528 }
4529 label.addEventListener("mousedown", _make_block_needle_drag_start_handler(isleft, data), false);
4530 return vbar;
4531 }
4532
4533 function make_block_container(is_stranded, has_both_strands, max_len, show_len, offset, range_handler) {
4534 offset = (offset != null ? offset : 0);
4535 // make the container for the block diagram
4536 var container = document.createElement("div");
4537 container.className = "block_container";
4538 container.setAttribute("data-max", max_len);
4539 container.setAttribute("data-off", offset);
4540 if (is_stranded) {
4541 var plus = document.createElement("div");
4542 plus.appendChild(document.createTextNode("+"));
4543 plus.className = "block_plus_sym";
4544 container.appendChild(plus);
4545 if (has_both_strands) {
4546 var minus = document.createElement("div");
4547 minus.appendChild(document.createTextNode("-"));
4548 minus.className = "block_minus_sym";
4549 container.appendChild(minus);
4550 }
4551 }
4552 var rule = document.createElement("div");
4553 rule.className = "block_rule";
4554 rule.style.width = ((show_len / max_len) * 100) + "%";
4555 container.appendChild(rule);
4556 if (range_handler != null) {
4557 var range_data = {
4558 "max": max_len,
4559 "len": show_len,
4560 "off": offset,
4561 "handler": range_handler,
4562 "container": container,
4563 "lneedle": null, "llabel": null,
4564 "rneedle": null, "rlabel": null,
4565 "isleft": false, "moveboth" : false
4566 };
4567 range_data.drag_during = _make_block_needle_drag_during_handler(range_data);
4568 range_data.drag_end = _make_block_needle_drag_end_handler(range_data);
4569 container.appendChild(_make_block_needle(false, 1, range_data)); // add right first so z-index works
4570 container.appendChild(_make_block_needle(true, 0, range_data));
4571 }
4572 return container;
4573 }
4574
4575 function make_block_label(container, max_len, pos, length, message) {
4576 "use strict";
4577 var label = document.createElement("div");
4578 label.className = "block_label";
4579 label.style.left = (((pos + (length / 2)) / max_len) * 100) + "%";
4580 label.appendChild(document.createTextNode(message));
4581 container.appendChild(label);
4582 }
4583
4584 function make_block(container, max_len,
4585 site_pos, site_len, site_pvalue, site_rc, site_colour_index, site_secondary) {
4586 "use strict";
4587 var block_height, block, block_region1, block_region2;
4588 var max_block_height = 12;
4589 var max_pvalue = 1e-10;
4590 // calculate the height of the block
4591 block_height = (site_pvalue < max_pvalue ? max_block_height :
4592 (Math.log(site_pvalue) / Math.log(max_pvalue)) * max_block_height);
4593 if (block_height < 1) block_height = 1;
4594 // create a block to represent the motif
4595 block = document.createElement("div");
4596 block.className = "block_motif" + (site_secondary ? " scanned_site" : "") + (site_rc ? " bottom" : " top");
4597 block.style.left = ((site_pos / max_len) * 100) + "%";
4598 block.style.top = (!site_rc ? max_block_height - block_height :
4599 max_block_height + 1) + "px";
4600 block.style.width = ((site_len / max_len) * 100) + "%";
4601 block.style.height = block_height + "px";
4602 block.style.backgroundColor = block_colour(site_colour_index);
4603 block.setAttribute("data-colour-index", site_colour_index);
4604 // add to container
4605 container.appendChild(block);
4606 var activator = function (e) {
4607 toggle_class(block, "active", true);
4608 var new_e = new e.constructor(e.type, e);
4609 block.dispatchEvent(new_e);
4610 };
4611 var deactivator = function (e) {
4612 toggle_class(block, "active", false);
4613 var new_e = new e.constructor(e.type, e);
4614 block.dispatchEvent(new_e);
4615 }
4616 // create a larger region to detect mouseover for the block
4617 block_region1 = document.createElement("div");
4618 block_region1.className = "block_region top" +
4619 (site_secondary ? " scanned_site" : "") + (site_rc ? "" : " main");
4620 block_region1.style.left = block.style.left;
4621 block_region1.style.width = block.style.width;
4622 block_region1.addEventListener('mouseover', activator, false);
4623 block_region1.addEventListener('mouseout', deactivator, false);
4624 container.appendChild(block_region1);
4625 block_region2 = document.createElement("div");
4626 block_region2.className = "block_region bottom" +
4627 (site_secondary ? " scanned_site" : "") + (site_rc ? " main" : "");
4628 block_region2.style.left = block.style.left;
4629 block_region2.style.width = block.style.width;
4630 block_region2.addEventListener('mouseover', activator, false);
4631 block_region2.addEventListener('mouseout', deactivator, false);
4632 container.appendChild(block_region2);
4633 return block;
4634 }
4635
4636 function set_block_needle_positions(containingNode, start, end) {
4637 var container, lneedle, llabel, rneedle, rlabel, max, off, left, right;
4638 container = (/\bblock_container\b/.test(containingNode.className) ? containingNode : containingNode.querySelector(".block_container"));
4639 max = parseInt(container.getAttribute("data-max"), 10);
4640 off = parseInt(container.getAttribute("data-off"), 10);
4641 left = start - off;
4642 right = end - off;
4643 lneedle = containingNode.querySelector(".block_needle.left");
4644 llabel = lneedle.querySelector(".block_handle.left");
4645 rneedle = containingNode.querySelector(".block_needle.right");
4646 rlabel = rneedle.querySelector(".block_handle.right");
4647 // update the needle positions
4648 lneedle.style.left = "" + (left / max * 100) + "%";
4649 llabel.textContent = "" + (left + off + 1);
4650 rneedle.style.left = "" + (right / max * 100) + "%";
4651 rlabel.textContent = "" + (right + off);
4652 }
4653
4654 function get_block_needle_positions(containingNode) {
4655 var container, llabel, rlabel, max, off, left, right;
4656 container = (/\bblock_container\b/.test(containingNode.className) ? containingNode : containingNode.querySelector(".block_container"));
4657 max = parseInt(container.getAttribute("data-max"), 10);
4658 off = parseInt(container.getAttribute("data-off"), 10);
4659 llabel = containingNode.querySelector(".block_needle.left > .block_handle.left");
4660 rlabel = containingNode.querySelector(".block_needle.right > .block_handle.right");
4661 left = parseInt(llabel.textContent, 10) - off - 1;
4662 right = parseInt(rlabel.textContent, 10) - off;
4663 return {"start": left + off, "end": right + off};
4664 }
4665 </script>
4666 <script>
4667 function make_alpha_bg_table(alph, freqs) {
4668 function colour_symbol(index) {
4669 var span = document.createElement("span");
4670 span.appendChild(document.createTextNode(alph.get_symbol(index)));
4671 span.style.color = alph.get_colour(index);
4672 span.className = "alpha_symbol";
4673 return span;
4674 }
4675 var table, thead, tbody, row, th, span, i;
4676 // create table
4677 table = document.createElement("table");
4678 table.className = "alpha_bg_table";
4679 // create header
4680 thead = document.createElement("thead");
4681 table.appendChild(thead);
4682 row = thead.insertRow(thead.rows.length);
4683 if (alph.has_complement()) {
4684 add_text_header_cell(row, "Name", "pop_alph_name");
4685 if (freqs != null) add_text_header_cell(row, "Freq.", "pop_alph_freq");
4686 if (alph.has_bg()) add_text_header_cell(row, "Bg.", "pop_alph_bg");
4687 add_text_header_cell(row, "");
4688 add_text_header_cell(row, "");
4689 add_text_header_cell(row, "");
4690 if (alph.has_bg()) add_text_header_cell(row, "Bg.", "pop_alph_bg");
4691 if (freqs != null) add_text_header_cell(row, "Freq.", "pop_alph_freq");
4692 add_text_header_cell(row, "Name", "pop_alph_name");
4693 } else {
4694 add_text_header_cell(row, "");
4695 add_text_header_cell(row, "Name", "pop_alph_name");
4696 if (freqs != null) add_text_header_cell(row, "Freq.", "pop_alph_freq");
4697 if (alph.has_bg()) add_text_header_cell(row, "Bg.", "pop_alph_bg");
4698 }
4699 // add alphabet entries
4700 tbody = document.createElement("tbody");
4701 table.appendChild(tbody);
4702 if (alph.has_complement()) {
4703 for (i = 0; i < alph.get_size_core(); i++) {
4704 var c = alph.get_complement(i);
4705 if (i > c) continue;
4706 row = tbody.insertRow(tbody.rows.length);
4707 add_text_cell(row, alph.get_name(i));
4708 if (freqs != null) add_text_cell(row, "" + freqs[i]);
4709 if (alph.has_bg()) add_text_cell(row, "" + alph.get_bg_freq(i));
4710 add_cell(row, colour_symbol(i));
4711 add_text_cell(row, "~");
4712 add_cell(row, colour_symbol(c));
4713 if (alph.has_bg()) add_text_cell(row, "" + alph.get_bg_freq(c));
4714 if (freqs != null) add_text_cell(row, "" + freqs[c]);
4715 add_text_cell(row, alph.get_name(c));
4716 }
4717 } else {
4718 for (i = 0; i < alph.get_size_core(); i++) {
4719 row = tbody.insertRow(tbody.rows.length);
4720 add_cell(row, colour_symbol(i));
4721 add_text_cell(row, alph.get_name(i));
4722 if (freqs != null) add_text_cell(row, "" + freqs[i]);
4723 if (alph.has_bg()) add_text_cell(row, "" + alph.get_bg_freq(i));
4724 }
4725 }
4726 return table;
4727 }
4728
4729 </script>
4730 <script>
4731 //
4732 // simple-shared-doc.js
4733 //
4734
4735 //
4736 // Function to redirect to appropriate doc file.
4737 //
4738 function get_doc_text(pgm, doc_type, extra, extra2) {
4739 switch (pgm) {
4740 case 'shared':
4741 return(get_shared_doc_text(doc_type, extra, extra2));
4742 case 'ame':
4743 return(get_ame_doc_text(doc_type, extra, extra2));
4744 case 'centrimo':
4745 return(get_centrimo_doc_text(doc_type, extra, extra2));
4746 case 'discovery':
4747 return(get_discovery_doc_text(doc_type, extra, extra2));
4748 case 'fimo':
4749 return(get_fimo_doc_text(doc_type, extra, extra2));
4750 case 'gomo':
4751 return(get_gomo_doc_text(doc_type, extra, extra2));
4752 case 'mcast':
4753 return(get_mcast_doc_text(doc_type, extra, extra2));
4754 case 'meme-chip':
4755 return(get_meme_chip_doc_text(doc_type, extra, extra2));
4756 case 'momo':
4757 return(get_momo_doc_text(doc_type, extra, extra2));
4758 case 'sea':
4759 return(get_sea_doc_text(doc_type, extra, extra2));
4760 case 'spamo':
4761 return(get_spamo_doc_text(doc_type, extra, extra2));
4762 case 'streme':
4763 return(get_streme_doc_text(doc_type, extra, extra2));
4764 case 'tgene':
4765 return(get_tgene_doc_text(doc_type, extra, extra2));
4766 case 'tomtom':
4767 return(get_tomtom_doc_text(doc_type, extra, extra2));
4768 case 'xstreme':
4769 return(get_xstreme_doc_text(doc_type, extra, extra2));
4770 default:
4771 return("<b>Unknown program type: <font color=red>" + pgm + "</font></b>");
4772 }
4773 } // get_doc_text
4774
4775 //
4776 // Function to replace the innerHTML of element "id" with the HTML indicated by "doc_type".
4777 // Easier to read and update than the more flexible approach in shared-doc.js.
4778 //
4779 function print_doc(id, pgm, doc_type, extra) {
4780 document.getElementById(id).insertAdjacentHTML('beforeend', get_doc_text(pgm, doc_type, extra));
4781 } // print_doc
4782
4783 //
4784 // Function to replace the innerHTML of element "id" with an HTML paragraph
4785 // containing the text for 'pgm' and 'doc_type'.
4786 // This function can be used in help pop-ups.
4787 //
4788 function print_doc_para(id, pgm, doc_type, extra, extra2) {
4789 html = "<p>" + get_doc_text(pgm, doc_type, extra, extra2) + "</p>";
4790 document.getElementById(id).insertAdjacentHTML('beforeend', html);
4791 } // print_doc_para
4792
4793 //
4794 // Function to return the Shared HTML text of a given type.
4795 // This function can be used directly to document the output format (xx-output-format.html)
4796 // and indirectly via print_doc_para for help pop-ups in the actual output HTML,
4797 // to prevent duplication of documentation.
4798 //
4799 function get_shared_doc_text(doc_type, extra, extra2) {
4800 if (extra == undefined) {extra = ""};
4801 if (extra2 == undefined) {extra2 = ""};
4802 switch (doc_type) {
4803 case 'fasta-coordinates-name':
4804 return(`
4805 The sequence IDs in the FASTA header lines are used as the source of sequence names.
4806 The sequence ID is the string following the initial '>' up to the first white space character.
4807 If the sequence ID is in an accepted <a href="` + site_url + `/doc/fasta-coordinates-format.html">FASTA Coordinates Format</a>
4808 (e.g., UCSC or Galaxy format), and you did NOT specify the <code>` + extra + `-no-pgc</code> option,
4809 then the coordinates are removed from the sequence ID to create the sequence name.
4810 `);
4811 case 'fasta-coordinates-brief':
4812 return(`
4813 The sequence IDs in the FASTA header lines are used as the source of sequence names.
4814 The sequence ID is the string following the initial '>' up to the first white space character.
4815 If the sequence ID is in an accepted <a href="` + site_url + `/doc/fasta-coordinates-format.html">FASTA Coordinates Format</a>
4816 (e.g., UCSC or Galaxy format), and you did NOT specify the <code>` + extra + `-no-pgc</code> option,
4817 then the coordinates are removed from the sequence ID to create the sequence name,
4818 and the starting coordinate given in the sequence ID will be used as the
4819 coordinate of the first position of the sequence.
4820 Otherwise, the coordinate of the first position of the sequence is taken as 1 and the entire
4821 sequence ID is used as the sequence name.
4822 `);
4823 case 'fasta-coordinates':
4824 return(`
4825 For sequence coordinates embedded in FASTA sequence headers to be parsed
4826 correctly by MEME Suite programs, the sequence ID in the FASTA header should have one of the two
4827 following formats:
4828
4829 <h4>UCSC Format</h4>
4830 <div style='margin: 5px 0'>
4831 &gt;<span class='pdat'>sequence name</span>:<span class='pdat'
4832 >starting position</span>-<span class='pdat'>ending position</span>
4833 </div>
4834 where
4835 <ul style='margin-top: 0; margin-bottom: 5px'>
4836 <li><span class='pdat'>sequence name</span> is the name of the genomic sequence,</li>
4837 <li><span class='pdat'>starting position</span> is the genomic position of the first base and</li>
4838 <li><span class='pdat'>ending position</span> is the genomic position of the final base.</li>
4839 </ul>
4840 <b>Example:</b> <span class="pdata">chr1:156887119-156887619</span>
4841
4842 <h4>Galaxy Format</h4>
4843 <div style='margin: 5px 0'>
4844 &gt;<span class='pdat'>assembly name</span>_<span class='pdat'
4845 >sequence name</span>_<span class='pdat'
4846 >starting position</span>_<span class='pdat'
4847 >ending position</span>_<span class='pdat'
4848 >strand</span>
4849 </div>
4850 where
4851 <ul style='margin-top: 0; margin-bottom: 5px'>
4852 <li><span class='pdat'>assembly name</span> is the name of the genomic assembly,</li>
4853 <li><span class='pdat'>sequence name</span> is the name of the genomic sequence,</li>
4854 <li><span class='pdat'>starting position</span> is the genomic position of the first base and</li>
4855 <li><span class='pdat'>ending position</span> is the genomic position of the final base.</li>
4856 </ul>
4857 <b>Example:</b> <span class="pdata">>mm9_chr18_75759530_7575972_-</span>
4858
4859 <p>
4860 For both formats, the following rules also apply:
4861 <ul>
4862 <li>The the coordinates are 1-start, fully-closed. This means
4863 that the first base of the chromosome is position "1", and the bases at the
4864 first and last positions of the given range are included.</li>
4865 <li>The <span class='pdat'>sequence name</span> may not contain any white space.</li>
4866 <li>The <span class='pdat'>assembly name</span> is not optional.</li>
4867 <li>The <span class='pdat'>strand</span> is ignored.</li>
4868 <li>When no valid genomic coordinates are found in a FASTA sequence header, the starting
4869 position of the sequence is taken to be position 1.</li>
4870 </ul>
4871 </p>
4872 `);
4873 case 'motif-db':
4874 return(`
4875 The name of ` + extra2 + ` a file of motifs ("motif database file") that contains ` + extra + `
4876 `);
4877 case 'motif-id':
4878 return(`
4879 The name of the ` + extra + ` motif, which is unique ` + extra2 + ` in the motif database file.
4880 `);
4881 case 'motif-alt-id':
4882 return(`
4883 An alternate name for the ` + extra + ` motif that may be provided ` + extra2 + ` in the motif database file.
4884 `);
4885 case 'motif-width':
4886 return(`
4887 The width of the motif. No gaps are allowed in motifs supplied to ` + extra + `
4888 as it only works for motifs of a fixed width.
4889 `);
4890 case 'motif-cons':
4891 return(`
4892 A consensus sequence computed from the ` + extra + ` motif (as described <a href="#consensus_doc">below</a>).
4893 `);
4894 case 'motif-match-score':
4895 return(`
4896 ` + extra2 + ` The motif match score of a position in a sequence is
4897 computed by summing the appropriate entry from each column of the
4898 position-dependent scoring matrix that represents the motif. ` + extra + `
4899 `);
4900 case 'motif-match-p-value':
4901 return(`
4902 The <i>p</i>-value of a motif match is the probability of a single random
4903 subsequence of the length of the motif <a href="javascript:help_refine('pop_motif_match_score')">scoring</a>
4904 at least as well as the observed match.
4905 `);
4906 case 'bh-q-value':
4907 if (extra2 == "") extra2 = "match";
4908 return(`
4909 The q-value is the minimum False Discovery Rate (FDR) required to consider this
4910 ` + extra2 + ` significant.</br>` +
4911 get_shared_doc_text('bh-q-value-method', extra, extra2) + `
4912 `);
4913 case 'bh-q-value-method':
4914 return(`
4915 <br>` + extra + ` estimates q-values from all the ` + extra2 + ` <i>p</i>-values
4916 using the method proposed by Benjamini & Hochberg (<i>Journal of the Royal Statistical Society B</i>, 57:289-300, 1995).
4917 See also Storey JD, Tibshirani R. Statistical significance for
4918 genome-wide studies, <i>Proc. Natl. Acad. Sci. USA</i> (2003) <b>100</b>:9440&ndash;9445.
4919 `);
4920 case 'sdb-name':
4921 return(`
4922 The name of the (FASTA) sequence database file.
4923 `);
4924 case 'sdb-psp':
4925 return(`
4926 The name of the position specific priors (PSP) file.
4927 `);
4928 case 'sdb-dist':
4929 return(`
4930 The name of the binned distribution of priors file.
4931 `);
4932 case 'sdb-count':
4933 return(`
4934 The number of sequences in the database.
4935 `);
4936 case 'sdb-letters':
4937 return(`
4938 The number of letters in the sequence database.
4939 `);
4940 case 'lastmod':
4941 return(`
4942 The date of the last modification to the ` + extra + ` database.
4943 `);
4944 case 'sequence-id':
4945 return(`
4946 The identifier of the sequence (from the FASTA sequence header line)` + extra + `
4947 `);
4948 case 'sequence-desc':
4949 return(`
4950 The description appearing after the identifier of the sequence in the FASTA header line.
4951 `);
4952 case 'alph-name':
4953 return(`
4954 The name of the alphabet symbol.
4955 `);
4956 case 'alph-bg':
4957 return(`
4958 The frequency of the alphabet symbol as defined by the background model.
4959 `);
4960 case 'match-start':
4961 return(`
4962 The start position of the ` + extra + `.
4963 `);
4964 case 'match-stop':
4965 return(`
4966 The end position of the ` + extra + `.
4967 `);
4968 case 'match-start-seq':
4969 return(`
4970 The start position of the ` + extra + `; 1-based sequence coordinates.
4971 `);
4972 case 'match-stop-seq':
4973 return(`
4974 The end position of the ` + extra + `; 1-based sequence coordinates.
4975 `);
4976 case 'match-start-genomic':
4977 return(`
4978 The start position of the ` + extra + `; genomic coordinates.
4979 `);
4980 case 'match-stop-genomic':
4981 return(`
4982 The end position of the ` + extra + `; genomic coordinates.
4983 `);
4984 case 'motif-consensus':
4985 return(`
4986 <p id="consensus_doc">
4987 A <b>consensus sequence</b> is constructed from each column in a
4988 motif's frequency matrix using the <b>"50% rule"</b>
4989 as follows:
4990 </p>
4991 <ol>
4992 <li>The letter frequencies in the column are sorted in decreasing order.</li>
4993 <li>Letters with frequency less 50% of the maximum are discarded.</li>
4994 <li>The letter used in this position in the consensus sequence is determined
4995 by the first rule below that applies:</li>
4996 <ul>
4997 <li>If there is only one letter left, or if the remaining letters exactly match
4998 an ambiguous symbol in the alphabet, the <b>letter</b> or <b>ambiguous symbol</b>,
4999 respectively, is used.</li>
5000 <li>Otherwise, if the remaining set contains at least 50% of the core
5001 symbols in the alphabet, the alphabet's <b>wildcard</b>
5002 (e.g., "N" for DNA or RNA, and "X" for protein) is used.</li>
5003 <li>Otherwise, the letter with the <b>maximum frequency</b> is used.</li>
5004 </ul>
5005 </ol>
5006 `);
5007 default:
5008 return("Error--Unrecognized shared doc_type: " + doc_type);
5009 }
5010 } // get_shared_doc_text
5011 </script>
5012 <script>
5013 //
5014 // discovery_doc.js
5015 // Documentation common to motif discovery tools.
5016 //
5017
5018 //
5019 // Function to return the HTML text of a given type.
5020 // This function can be used directly to document the output format (xx-output-format.html)
5021 // and indirectly via print_doc_para for help pop-ups in the actual output HTML,
5022 // to prevent duplication of documentation.
5023 //
5024 function get_discovery_doc_text(doc_type, extra, extra2) {
5025 if (extra == undefined) {extra = ""};
5026 if (extra2 == undefined) {extra2 = ""};
5027
5028 switch (doc_type) {
5029 case 'motif_logo':
5030 return(`
5031 The sequence logo of the motif.
5032 The rules for construction logos are given in
5033 the <i>Description</i> section of the documentation for the MEME Suite utility
5034 <a href="` + extra + `/doc/ceqlogo.html#description">ceqlogo</a>.
5035 `);
5036 case 'motif_rc_logo':
5037 return(`
5038 The sequence logo of the reverse complement motif.
5039 The rules for construction logos are given in
5040 the <i>Description</i> section of the documentation for the MEME Suite utility
5041 <a href="` + extra + `/doc/ceqlogo.html#description">ceqlogo</a>.
5042 `);
5043 case 'more':
5044 return(`
5045 Click on the blue symbol below to reveal detailed information about the motif.
5046 `);
5047 case 'submit_dl':
5048 return(`
5049 Click on the blue symbol below to reveal options allowing you
5050 to submit this motif to another MEME Suite motif analysis program, to download this
5051 motif in various text formats, or to download a sequence "logo" of
5052 this motif PNG or EPS format.</p>
5053 <h5>Supported Programs</h5>
5054 <dl>
5055 <dt>Tomtom</dt>
5056 <dd>Tomtom is a tool for searching for similar known motifs.
5057 [<a href="` + extra + `/doc/tomtom.html?man_type=web">manual</a>]</dd>
5058 <dt>MAST</dt>
5059 <dd>MAST is a tool for searching biological sequence databases for
5060 sequences that contain one or more of a group of known motifs.
5061 [<a href="` + extra + `/doc/mast.html?man_type=web">manual</a>]</dd>
5062 <dt>FIMO</dt>
5063 <dd>FIMO is a tool for searching biological sequence databases for
5064 sequences that contain one or more known motifs.
5065 [<a href="` + extra + `/doc/fimo.html?man_type=web">manual</a>]</dd>
5066 <dt>GOMo</dt>
5067 <dd>GOMo is a tool for identifying possible roles (Gene Ontology
5068 terms) for DNA binding motifs.
5069 [<a href="` + extra + `/doc/gomo.html?man_type=web">manual</a>]</dd>
5070 <dt>SpaMo</dt>
5071 <dd>SpaMo is a tool for inferring possible transcription factor
5072 complexes by finding motifs with enriched spacings.
5073 [<a href="` + extra + `/doc/spamo.html?man_type=web">manual</a>]</dd>
5074 </dl>
5075 `);
5076 case 'site_distr':
5077 return(`
5078 This plot shows the positional distribution of the best match to the motif in the ` +
5079 extra + ` sequences.
5080 Only matches with scores at least the ` +
5081 extra2 + ` score threshold are considered.
5082 The plot is smoothed with a triangular function whose width is 5% of the maximum ` +
5083 extra + ` sequence length.
5084 The position of the dotted vertical line indicates whether the sequences were
5085 aligned on their left ends, centers, or right ends, respectively.
5086 `);
5087 case 'site_hist':
5088 return(`
5089 This histogram shows the distribution of the <b>number</b> of matches to the motif in the ` +
5090 extra + ` sequences with at least one match.
5091 Only matches with scores at least the ` +
5092 extra2 + ` score threshold are considered.
5093 `);
5094 default:
5095 return("Error--Unrecognized discovery doc_type: " + doc_type);
5096 }
5097 } // get_discovery_doc_text
5098 </script>
5099 <script>
5100 //
5101 // submit_or_download_motif.js
5102 //
5103
5104 function make_submit_or_download_motif_form(id, site_url, program) {
5105 var html = `
5106 <div class="popup_wrapper">
5107 <div class="popup" style="display:none; top: -150px;" id="download">
5108 <div>
5109 <div style="float:right; ">
5110 <div id="outpop_close" class="close" tabindex="0">x</div>
5111 </div>
5112 <h2 class="mainh" style="margin:0; padding:0;">Submit or Download</h2>
5113 <div style="clear:both"></div>
5114 </div>
5115 <div style="height:100px">
5116 <div style="float:right; width: 30px;">
5117 <div id="outpop_prev" class="navarrow" tabindex="0">
5118 <span class="inactive">&#8679;</span><span class="active">&#11014;</span>
5119 </div>
5120 <div id="outpop_num" class="navnum"></div>
5121 <div id="outpop_next" class="navarrow" tabindex="0">
5122 <span class="inactive">&#8681;</span><span class="active">&#11015;</span>
5123 </div>
5124 </div>
5125 <div id="logo_box" style="height: 100px; margin-right: 40px;">
5126 <canvas id="outpop_logo" height="100" width="250"></canvas>
5127 <canvas id="outpop_logo_rc" height="100" width="250"></canvas>
5128 </div>
5129 </div>
5130 <!-- tabs start -->
5131 <div class="tabArea top">
5132 <span id="outpop_tab_1" class="tab">Submit Motif</span><span
5133 id="outpop_tab_2" class="tab middle">Download Motif</span><span
5134 id="outpop_tab_3" class="tab middle">Download Logo</span>
5135 </div>
5136 <div class="tabMain top">
5137 <!-- Submit to another program -->
5138 <div id="outpop_pnl_1">
5139 <h4 class="compact">Submit to program</h4>
5140 <table id="programs" class="programs">
5141 <tr>
5142 <td><input type="radio" name="program" value="tomtom" id="submit_tomtom"></td>
5143 <td><label for="submit_tomtom">Tomtom</label></td>
5144 <td><label for="submit_tomtom">Find similar motifs in
5145 published libraries or a library you supply.</label></td>
5146 </tr>
5147 <tr>
5148 <td><input type="radio" name="program" value="fimo" id="submit_fimo"></td>
5149 <td><label for="submit_fimo">FIMO</label></td>
5150 <td><label for="submit_fimo">Find motif occurrences in
5151 sequence data.</label></td>
5152 </tr>
5153 <tr>
5154 <td><input type="radio" name="program" value="mast" id="submit_mast"></td>
5155 <td><label for="submit_mast">MAST</label></td>
5156 <td><label for="submit_mast">Rank sequences by affinity to
5157 groups of motifs.</label></td>
5158 </tr>
5159 <tr class="dna_only">
5160 <td><input type="radio" name="program" value="gomo" id="submit_gomo"></td>
5161 <td><label for="submit_gomo">GOMo</label></td>
5162 <td><label for="submit_gomo">Identify possible roles (Gene
5163 Ontology terms) for motifs.</label></td>
5164 </tr>
5165 <tr class="dna_only">
5166 <td><input type="radio" name="program" value="spamo" id="submit_spamo"></td>
5167 <td><label for="submit_spamo">SpaMo</label></td>
5168 <td><label for="submit_spamo">Find other motifs that are
5169 enriched at specific close spacings which might imply the existence of a complex.</label></td>
5170 </tr>
5171 </table>
5172 </div>
5173 <!-- download text format -->
5174 <div id="outpop_pnl_2">
5175 <div>
5176 <label for="text_format">Format:</label>
5177 <select id="text_format">
5178 <option value="0">Count Matrix</option>
5179 <option value="1">Probability Matrix</option>
5180 <option value="2">Minimal MEME</option> ` +
5181 (program == "MEME" ? `
5182 <option value="3">FASTA</option>
5183 <option value="4">Raw</option> ` : ``) + `
5184 </select>
5185 </div>
5186 <textarea id="outpop_text" name="content"
5187 style="width:99%; white-space: pre; word-wrap: normal; overflow-x: scroll;"
5188 rows="8" readonly="readonly" wrap="off"></textarea>
5189 <a id="outpop_text_dl" download="meme.txt" href=""></a>
5190 </div>
5191 <!-- download logo format -->
5192 <div id="outpop_pnl_3">
5193 <form id="logo_form" method="post" action="">
5194 <input type="hidden" name="program" value=" ` + program + `"/>
5195 <input type="hidden" id="logo_motifs" name="motifs" value=""/>
5196 <input type="hidden" id="logo_id1" name="id1" value=""/>
5197 <table>
5198 <tr>
5199 <td><label for="logo_format">Format:</label></td>
5200 <td>
5201 <select id="logo_format" name="png">
5202 <option value="1">PNG (for web)</option>
5203 <option value="0">EPS (for publication)</option>
5204 </select>
5205 </td>
5206 </tr>
5207 <tr>
5208 <td><label for="logo_rc">Orientation:</label></td>
5209 <td>
5210 <select id="logo_rc" name="rc1">
5211 <option value="0">Normal</option>
5212 <option value="1" id="logo_rc_option">Reverse Complement</option>
5213 </select>
5214 </td>
5215 </tr>
5216 <tr>
5217 <td><label for="logo_ssc">Small Sample Correction:</label></td>
5218 <td>
5219 <input type="hidden" id="logo_err" name="errbars" value="0"/>
5220 <select id="logo_ssc" name="ssc">
5221 <option value="0">Off</option>
5222 <option value="1">On</option>
5223 </select>
5224 </td>
5225 </tr>
5226 <tr>
5227 <td><label for="logo_width">Width:</label></td>
5228 <td>
5229 <input type="text" id="logo_width" size="4" placeholder="default" name="width"/>&nbsp;cm
5230 </td>
5231 </tr>
5232 <tr>
5233 <td><label for="logo_height">Height:</label></td>
5234 <td>
5235 <input type="text" id="logo_height" size="4" placeholder="default" name="height"/>&nbsp;cm
5236 </td>
5237 </tr>
5238 </table>
5239 </form>
5240 </div>
5241 <!-- Buttons -->
5242 <div>
5243 <div style="float:left;">
5244 <input type="button" id="outpop_do" value="Submit" />
5245 </div>
5246 <div style="float:right;">
5247 <input id="outpop_cancel" type="button" value="Cancel" />
5248 </div>
5249 <div style="clear:both;"></div>
5250 </div>
5251 </div>
5252 </div>
5253 </div>
5254 `;
5255 document.getElementById(id).insertAdjacentHTML('beforeend', html);
5256 $("logo_form").action = site_url + "/utilities/generate_logo";
5257 } // make_submit_or_download_motif_form
5258
5259 //
5260 // Functions to update the submit_or_download_motif form.
5261 //
5262
5263 //
5264 // Initialise and display the download popup.
5265 //
5266 function action_show_outpop(e, ordinal) {
5267 "use strict";
5268 function init() {
5269 "use strict";
5270 var close_btn, next_btn, prev_btn, cancel_btn, do_btn;
5271 var tab1, tab2, tab3;
5272 var pnl1, pnl2, pnl3;
5273 var format_list;
5274 var tbl_submit, inputs, i, default_prog;
5275 close_btn = $("outpop_close");
5276 close_btn.addEventListener("click", action_hide_outpop, false);
5277 close_btn.addEventListener("keydown", action_hide_outpop, false);
5278 next_btn = $("outpop_next");
5279 next_btn.addEventListener("click", action_outpop_next, false);
5280 next_btn.addEventListener("keydown", action_outpop_next, false);
5281 prev_btn = $("outpop_prev");
5282 prev_btn.addEventListener("click", action_outpop_prev, false);
5283 prev_btn.addEventListener("keydown", action_outpop_prev, false);
5284 cancel_btn = $("outpop_cancel");
5285 cancel_btn.addEventListener("click", action_hide_outpop, false);
5286 do_btn = $("outpop_do");
5287 do_btn.addEventListener("click", action_outpop_submit, false);
5288 tab1 = $("outpop_tab_1");
5289 tab1.tabIndex = 0;
5290 tab1.addEventListener("click", action_outpop_tab, false);
5291 tab1.addEventListener("keydown", action_outpop_tab, false);
5292 tab2 = $("outpop_tab_2");
5293 tab2.tabIndex = 0;
5294 tab2.addEventListener("click", action_outpop_tab, false);
5295 tab2.addEventListener("keydown", action_outpop_tab, false);
5296 tab3 = $("outpop_tab_3");
5297 tab3.tabIndex = 0;
5298 tab3.addEventListener("click", action_outpop_tab, false);
5299 tab3.addEventListener("keydown", action_outpop_tab, false);
5300 pnl1 = $("outpop_pnl_1");
5301 pnl2 = $("outpop_pnl_2");
5302 pnl3 = $("outpop_pnl_3");
5303 toggle_class(tab1, "activeTab", true);
5304 toggle_class(tab2, "activeTab", false);
5305 toggle_class(tab3, "activeTab", false);
5306 pnl1.style.display = "block";
5307 pnl2.style.display = "none";
5308 pnl3.style.display = "none";
5309 format_list = $("text_format");
5310 format_list.addEventListener("change", action_outpop_format, false);
5311 // setup program selection
5312 tbl_submit = $("programs");
5313 // when not dna, hide the inputs for programs that require dna motifs
5314 toggle_class(tbl_submit, "alphabet_dna", current_alphabet.has_complement());//TODO alphabet_dna is a bad name for a field when allowing custom alphabets
5315 // add a click listener for the radio buttons
5316 inputs = tbl_submit.querySelectorAll("input[type='radio']");
5317 for (i = 0; i < inputs.length; i++) {
5318 inputs[i].addEventListener("click", action_outpop_program, false);
5319 }
5320 // ensure that a default program option is selected for DNA and Protein
5321 default_prog = document.getElementById(current_alphabet.has_complement() ? "submit_tomtom" : "submit_fimo"); //TODO Tomtom might require a more strict definition of DNA
5322 default_prog.checked = true;
5323 action_outpop_program.call(default_prog);
5324 // disable reverse-complement when not DNA
5325 $("logo_rc_option").disabled = !current_alphabet.has_complement();
5326 // set errorbars on when ssc is on
5327 $("logo_ssc").addEventListener("change", action_outpop_ssc, false);
5328 }
5329 // store the focused element
5330 action_hide_outpop.last_active = document.activeElement;
5331 if (!e) e = window.event;
5332 if (e.type === "keydown") {
5333 if (e.keyCode !== 13 && e.keyCode !== 32) {
5334 return;
5335 }
5336 // stop a submit or something like that
5337 e.preventDefault();
5338 }
5339 // hide the help popup
5340 help_popup();
5341 // on first load initilize the popup
5342 if (!action_show_outpop.ready) {
5343 init();
5344 action_show_outpop.ready = true;
5345 }
5346 update_outpop_motif(ordinal - 1);
5347 // display the download popup
5348 $("grey_out_page").style.display = "block";
5349 $("download").style.display = "block";
5350 $("outpop_close").focus();
5351 } // action_show_output
5352
5353 //
5354 // Hide the submit or download popup.
5355 //
5356 function action_hide_outpop(e) {
5357 if (!e) e = window.event;
5358 if (e.type === "keydown") {
5359 if (e.keyCode !== 13 && e.keyCode !== 32) {
5360 return;
5361 }
5362 // stop a submit or something like that
5363 e.preventDefault();
5364 }
5365 $("download").style.display = "none";
5366 $("grey_out_page").style.display = "none";
5367 if (typeof action_hide_outpop.last_active !== "undefined") {
5368 action_hide_outpop.last_active.focus();
5369 }
5370 } // action_hide_outpop
5371
5372 /*
5373 * Show the next motif in the download popup.
5374 */
5375 function action_outpop_next(e) {
5376 if (!e) e = window.event;
5377 if (e.type === "keydown") {
5378 if (e.keyCode !== 13 && e.keyCode !== 32) {
5379 return;
5380 }
5381 // stop a submit or something like that
5382 e.preventDefault();
5383 }
5384 update_outpop_motif(current_motif + 1);
5385 } // action_outpop_next
5386
5387 /*
5388 * Show the previous motif in the download popup.
5389 */
5390 function action_outpop_prev(e) {
5391 if (!e) e = window.event;
5392 if (e.type === "keydown") {
5393 if (e.keyCode !== 13 && e.keyCode !== 32) {
5394 return;
5395 }
5396 // stop a submit or something like that
5397 e.preventDefault();
5398 }
5399 update_outpop_motif(current_motif - 1);
5400 } // action_outpop_prev
5401
5402 /*
5403 * Highlight the selected row in the program list.
5404 */
5405 function action_outpop_program() {
5406 "use strict";
5407 var table, tr, rows, i;
5408 tr = find_parent_tag(this, "TR");
5409 table = find_parent_tag(tr, "TABLE");
5410 rows = table.querySelectorAll("tr");
5411 for (i = 0; i < rows.length; i++) {
5412 toggle_class(rows[i], "selected", rows[i] === tr);
5413 }
5414 } // action_outpop_program
5415
5416 /*
5417 * Submit the motif to the selected program.
5418 */
5419 function action_outpop_submit(e) {
5420 "use strict";
5421 var form, input, program, motifs;
5422 // find out which program is selected
5423 var radios, i;
5424 radios = document.getElementsByName("program");
5425 program = "fimo"; // default to fimo, since it works with all alphabet types
5426 for (i = 0; i < radios.length; i++) {
5427 if (radios[i].checked) program = radios[i].value;
5428 }
5429
5430 motifs = motif_minimal_meme(data.motifs[current_motif]);
5431 form = document.createElement("form");
5432 form.setAttribute("method", "post");
5433 form.setAttribute("action", site_url + "/tools/" + program);
5434
5435 input = document.createElement("input");
5436 input.setAttribute("type", "hidden");
5437 input.setAttribute("name", "motifs_embed");
5438 input.setAttribute("value", motifs);
5439 form.appendChild(input);
5440
5441 var current_file = location.pathname.substring(location.pathname.lastIndexOf('/')+1);
5442 input = document.createElement("input");
5443 input.setAttribute("type", "hidden");
5444 input.setAttribute("name", "motifs_name");
5445 input.setAttribute("value", "motif number " + (current_motif+1) + " from " + current_file);
5446 form.appendChild(input);
5447
5448 document.body.appendChild(form);
5449 form.submit();
5450 document.body.removeChild(form);
5451 } // action_outpop_submit(e)
5452
5453 /*
5454 * Enable error bars when small sample correction is enabled.
5455 */
5456 function action_outpop_ssc() {
5457 "use strict";
5458 $("logo_err").value = $("logo_ssc").value;
5459 } // action_outpop_ssc
5460
5461 //
5462 // Update the motif logos and format download text in the popup.
5463 // This is called whenever the current motif changes.
5464 //
5465 function update_outpop_motif(index) {
5466 "use strict";
5467 var motifs, motif, pspm, logo, canvas, num;
5468 motifs = data["motifs"];
5469 if (index < 0 || index >= motifs.length) {return;}
5470 current_motif = index;
5471 motif = motifs[index];
5472 pspm = new Pspm(motif["pwm"]);
5473 logo = new Logo(current_alphabet, "");
5474 logo.add_pspm(pspm, 0);
5475 canvas = $("outpop_logo");
5476 canvas.width = canvas.width; // clear canvas
5477 draw_logo_on_canvas(logo, canvas, false);
5478 canvas = $("outpop_logo_rc");
5479 canvas.width = canvas.width; // clear rc canvas
5480 if (data.options.strands === "both" || data.options.revcomp) {
5481 pspm.reverse_complement(current_alphabet);
5482 logo = new Logo(current_alphabet, "");
5483 logo.add_pspm(pspm, 0);
5484 draw_logo_on_canvas(logo, canvas, false);
5485 }
5486 num = $("outpop_num");
5487 num.innerHTML = "";
5488 num.appendChild(document.createTextNode("" + (index + 1)));
5489 update_outpop_format(index);
5490 } // action_outpop_motif
5491
5492 //
5493 // Create the download menu.
5494 //
5495 function update_outpop_format(index) {
5496 var motif = data.motifs[index];
5497 var fn = [motif_count_matrix, motif_prob_matrix, motif_minimal_meme, motif_fasta, motif_raw];
5498 var suffix = ["_counts.txt", "_freqs.txt", ".meme", "_fasta.txt", "_raw.txt"];
5499 var format = parseInt($("text_format").value);
5500 var text = fn[format](motif);
5501 prepare_download(text, "text/plain", motif.id + suffix[format], $("outpop_text_dl"));
5502 $("outpop_text").value = text;
5503 } // update_outpop_format
5504
5505 /*
5506 * Update the text in the download format popup.
5507 */
5508 function action_outpop_format() {
5509 update_outpop_format(current_motif);
5510 } // action_outpop_format
5511
5512 /*
5513 * Download the format text.
5514 * Wire the link containing the data URI text to a download button so it looks
5515 * the same as the server submit stuff.
5516 */
5517 function action_outpop_download_motif(e) {
5518 $("outpop_text_dl").click();
5519 } // action_outpop_download_motif
5520
5521 /*
5522 * Download the motif logo.
5523 * The EPS format can be calculated locally in Javascript
5524 */
5525 function action_outpop_download_logo(e) {
5526 "use strict";
5527 var motif = data.motifs[current_motif];
5528 if ($("logo_format").value === "0") { // EPS
5529 var pspm, logo, eps;
5530 var logo_rc, logo_ssc, logo_width, logo_height;
5531 logo_rc = ($("logo_rc").value === "1");
5532 logo_ssc = ($("logo_ssc").value === "1");
5533 logo_width = parseFloat($("logo_width").value);
5534 if (isNaN(logo_width) || !isFinite(logo_width) || logo_width <= 0) logo_width = null;
5535 logo_height = parseFloat($("logo_height").value);
5536 if (isNaN(logo_height) || !isFinite(logo_height) || logo_height <= 0) logo_height = null;
5537 // create a PSPM from the motif
5538 pspm = motif_pspm(motif);
5539 if (logo_rc) pspm.reverse_complement(current_alphabet);
5540 logo = new Logo(current_alphabet);
5541 logo.add_pspm(pspm, 0);
5542 eps = logo.as_eps({"ssc": logo_ssc, "logo_width": logo_width, "logo_height": logo_height});
5543 prepare_download(eps, "application/postscript", motif.id + (logo_rc ? "_rc" : "") + ".eps");
5544 } else {
5545 $("logo_motifs").value = motif_minimal_meme(motif);
5546 $("logo_id1").value = motif.id;
5547 $("logo_form").submit();
5548 }
5549 } // action_outpop_download_logo
5550
5551 /*
5552 * Change the selected tab in the download popup.
5553 */
5554 function action_outpop_tab(e) {
5555 "use strict";
5556 var tab1, tab2, tab3, pnl1, pnl2, pnl3, do_btn;
5557 if (!e) e = window.event;
5558 if (e.type === "keydown") {
5559 if (e.keyCode !== 13 && e.keyCode !== 32) {
5560 return;
5561 }
5562 // stop a submit or something like that
5563 e.preventDefault();
5564 }
5565 tab1 = $("outpop_tab_1");
5566 tab2 = $("outpop_tab_2");
5567 tab3 = $("outpop_tab_3");
5568 pnl1 = $("outpop_pnl_1");
5569 pnl2 = $("outpop_pnl_2");
5570 pnl3 = $("outpop_pnl_3");
5571 do_btn = $("outpop_do");
5572
5573 toggle_class(tab1, "activeTab", (this === tab1));
5574 toggle_class(tab2, "activeTab", (this === tab2));
5575 toggle_class(tab3, "activeTab", (this === tab3));
5576 pnl1.style.display = ((this === tab1) ? "block" : "none");
5577 pnl2.style.display = ((this === tab2) ? "block" : "none");
5578 pnl3.style.display = ((this === tab3) ? "block" : "none");
5579 do_btn.value = ((this === tab1) ? "Submit" : "Download");
5580 do_btn.removeEventListener("click", action_outpop_submit, false);
5581 do_btn.removeEventListener("click", action_outpop_download_logo, false);
5582 do_btn.removeEventListener("click", action_outpop_download_motif, false);
5583 if (this === tab1) {
5584 do_btn.addEventListener("click", action_outpop_submit, false);
5585 } else if (this === tab2) {
5586 do_btn.addEventListener("click", action_outpop_download_motif, false);
5587 } else {
5588 do_btn.addEventListener("click", action_outpop_download_logo, false);
5589 }
5590 } // action_outpop_tab
5591
5592 function motif_fasta(motif) {
5593 "use strict";
5594 var sites, site, seq, sequences, sequence, i, num, counter, out;
5595 counter = {};
5596 sequences = data["sequence_db"]["sequences"];
5597 sites = motif["sites"];
5598 out = "";
5599 for (i = 0; i < sites.length; i++) {
5600 site = sites[i];
5601 seq = site["seq"];
5602 sequence = sequences[seq];
5603 counter[seq] = (num = counter[seq]) ? (++num) : (num = 1); // inc counter
5604 if (i !== 0) {out += "\n";}
5605 out += ">" + sequence["name"] + "_site_" + num + " offset= " + site["pos"] +
5606 (site["rc"] ? " RC\n" : "\n");
5607 out += site["match"];
5608 }
5609 return out;
5610 } // motif_fasta
5611
5612 function motif_raw(motif) {
5613 "use strict";
5614 var sites, i, out;
5615 sites = motif["sites"];
5616 out = "";
5617 for (i = 0; i < sites.length; i++) {
5618 if (i !== 0) {out += "\n";}
5619 out += sites[i]["match"];
5620 }
5621 return out;
5622 } // motif_raw
5623
5624 /*
5625 * Create a pspm for the given motif data
5626 */
5627 function motif_pspm(motif) {
5628 var pwm = motif.pwm;
5629 var name = motif.id;
5630 var ltrim = 0;
5631 var rtrim = 0;
5632 var nsites = motif.nsites;
5633 var sig = (current_program === "STREME" ? motif.test_pvalue : motif.evalue);
5634 return new Pspm(pwm, name, ltrim, rtrim, nsites, sig, null, motif.alt, current_program);
5635 } // motif_pspm
5636
5637 /*
5638 * Create a count matrix from the given motif data
5639 */
5640 function motif_count_matrix(motif) {
5641 return motif_pspm(motif).as_count_matrix();
5642 } // motif_count_matrix
5643
5644 /*
5645 * Create a probablity matrix from the given motif data
5646 */
5647 function motif_prob_matrix(motif) {
5648 return motif_pspm(motif).as_probability_matrix();
5649 } // motif_prob_matrix
5650
5651 function motif_minimal_meme(motif) {
5652 var strands;
5653 if (current_program === "STREME") {
5654 strands = (data.options.strands === "both" ? 2 : 1);
5655 } else {
5656 strands = (data.options.revcomp ? 2 : 1);
5657 }
5658 return motif_pspm(motif).as_meme({
5659 "with_header": true,
5660 "with_pspm": true,
5661 "with_pssm": (current_program === "MEME" ? true : false),
5662 "version": data["version"],
5663 "alphabet": current_alphabet,
5664 "strands": strands
5665 });
5666 }
5667 </script>
5668 <script>
5669 var current_program = "MEME";
5670 var current_alphabet = new Alphabet(data.alphabet, data.background.freqs);
5671 var current_motif = 0;
5672 //var new_icon_src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAQCAMAAAAyEe/dAAAACVBMVEX//wAAAAD////s2cV/AAAAdUlEQVQYlXVRBxLAIAhL+P+jC2HZhXcBZEWEldDsZcLIcAhHWWnK8SDcWQhMFUHdAQ1CqQ5+CWPmlHojl+nCJNRtzu4qRc3IUzmTVpXYK0nox0z0PI1stgchdK7lEv7ekhvalw8WW547Gyzedt/2/gLx8WXjXF/1AYFriNAWAAAAAElFTkSuQmCC";
5673
5674 var DelayLogoTask = function(logo, canvas) {
5675 this.logo = logo;
5676 this.canvas = canvas;
5677 };
5678
5679 DelayLogoTask.prototype.run = function () {
5680 draw_logo_on_canvas(this.logo, this.canvas, false);
5681 };
5682
5683 function clone_template(template) {
5684 "use strict";
5685 var node, help_btns, i, button;
5686 node = $(template).cloneNode(true);
5687 toggle_class(node, "template", false);
5688 node.id = "";
5689 help_btns = node.querySelectorAll(".help");
5690 for (i = 0; i < help_btns.length; i++) {
5691 button = help_btns[i];
5692 if (button.hasAttribute("data-topic")) {
5693 button.tabIndex = "0";
5694 button.addEventListener("click", __toggle_help, false);
5695 button.addEventListener("keydown", __toggle_help, false);
5696 }
5697 }
5698 return node;
5699 }
5700
5701 function make_small_logo(alphabet, pspm, options) {
5702 if (typeof options === "undefined") options = {};
5703 if (options.rc) pspm = pspm.copy().reverse_complement(alphabet);
5704 var logo = new Logo(alphabet, {x_axis: false, y_axis: false});
5705 logo.add_pspm(pspm, (typeof options.offset === "number" ? options.offset : 0));
5706 var canvas = document.createElement('canvas');
5707 if (typeof options.className === "string") canvas.className = options.className;
5708 if (typeof options.width === "number" && options.width > 0) {
5709 canvas.height = 0;
5710 canvas.width = options.width;
5711 draw_logo_on_canvas(logo, canvas, false);
5712 } else {
5713 draw_logo_on_canvas(logo, canvas, false, 1/3);
5714 }
5715 return canvas;
5716 }
5717
5718 function make_large_logo(alphabet, pspm, rc, offset, className) {
5719 if (rc) pspm = pspm.copy().reverse_complement(alphabet);
5720 var logo = new Logo(alphabet, "");
5721 logo.add_pspm(pspm, offset);
5722 var canvas = document.createElement('canvas');
5723 canvas.height = 200;
5724 canvas.width = 0;
5725 canvas.className = className;
5726 size_logo_on_canvas(logo, canvas, false);
5727 add_draw_task(canvas, new DelayLogoTask(logo, canvas));
5728 return canvas;
5729 }
5730
5731 function make_sym_btn(symbol, title, action) {
5732 var box;
5733 box = document.createElement("div");
5734 box.tabIndex = 0;
5735 box.className = "sym_btn";
5736 box.appendChild(document.createTextNode(symbol));
5737 box.title = title;
5738 box.addEventListener('click', action, false);
5739 box.addEventListener('keydown', action, false);
5740 return box;
5741 }
5742
5743 function make_seq(alphabet, seq) {
5744 var i, j, letter, lbox, sbox;
5745 sbox = document.createElement("span");
5746 for (i = 0; i < seq.length; i = j) {
5747 letter = seq.charAt(i);
5748 for (j = i+1; j < seq.length; j++) {
5749 if (seq.charAt(j) !== letter) {
5750 break;
5751 }
5752 }
5753 lbox = document.createElement("span");
5754 lbox.style.color = alphabet.get_colour(alphabet.get_index(letter));
5755 lbox.appendChild(document.createTextNode(seq.substring(i, j)));
5756 sbox.appendChild(lbox);
5757 }
5758 return sbox;
5759 }
5760
5761 //
5762 // make_pv_text
5763 //
5764 // Returns the string p-value, with the p italicised.
5765 ///
5766 function make_pv_text() {
5767 var pv_text = document.createElement("span");
5768 var pv_italic_text = document.createElement("span");
5769 pv_italic_text.appendChild(document.createTextNode("p"));
5770 pv_italic_text.style.fontStyle = "italic";
5771 pv_text.appendChild(pv_italic_text);
5772 pv_text.appendChild(document.createTextNode("-value"));
5773 return pv_text;
5774 }
5775
5776 function append_site_entries(tbody, motif, site_index, count) {
5777 "use strict";
5778 var i, end;
5779 var sites, site, sequences, sequence;
5780 var rbody;
5781 if (typeof count !== "number") {
5782 count = 20;
5783 }
5784 sequences = data["sequence_db"]["sequences"];
5785 sites = motif["sites"];
5786 end = Math.min(site_index + count, sites.length);
5787 for (i = site_index; i < end; i++) {
5788 site = sites[i];
5789 sequence = sequences[site["seq"]];
5790
5791 rbody = tbody.insertRow(tbody.rows.length);
5792 add_text_cell(rbody, "" + (site["seq"] + 1) + ".", "site_num");
5793 add_text_cell(rbody, sequence["name"], "site_name");
5794 add_text_cell(rbody, site["rc"] ? "-" : "+", "site_strand");
5795 add_text_cell(rbody, site["pos"] + 1, "site_start");
5796 add_text_cell(rbody, site["pvalue"].toExponential(2), "site_pvalue");
5797 add_text_cell(rbody, site["lflank"], "site lflank");
5798 add_cell(rbody, make_seq(current_alphabet, site["match"]), "site match");
5799 add_text_cell(rbody, site["rflank"], "site rflank");
5800 }
5801 return i;
5802 }
5803
5804 function make_site_entries() {
5805 "use strict";
5806 var region;
5807 region = this;
5808 if (region.data_site_index >= region.data_motif["sites"].length) {
5809 // all sites created
5810 region.removeEventListener('scroll', make_site_entries, false);
5811 return;
5812 }
5813 // if there's still 100 pixels to scroll than don't do anything yet
5814 if (region.scrollHeight - (region.scrollTop + region.offsetHeight) > 100) {
5815 return;
5816 }
5817
5818 region.data_site_index = append_site_entries(
5819 find_child(region, "sites_tbl").tBodies[0],
5820 region.data_motif, region.data_site_index, 20
5821 );
5822 }
5823
5824 function make_sites(motif) {
5825 "use strict";
5826 function add_site_header(row, title, nopad, help_topic, tag_class) {
5827 var div, divcp, th;
5828 th = document.createElement("th");
5829 div = document.createElement("div");
5830 div.className = "sites_th_inner";
5831 if (typeof title !== "object") {
5832 title = document.createTextNode("" + title);
5833 }
5834 div.appendChild(title);
5835 if (help_topic) {
5836 div.appendChild(document.createTextNode("\xA0"));
5837 div.appendChild(help_button(help_topic));
5838 }
5839 divcp = div.cloneNode(true);
5840 divcp.className = "sites_th_hidden";
5841 th.appendChild(div);
5842 th.appendChild(divcp);
5843 if (nopad) {
5844 th.className = "nopad";
5845 }
5846 if (tag_class) {
5847 th.className += " " + tag_class;
5848 }
5849 row.appendChild(th);
5850 }
5851 var outer_tbl, inner_tbl, tbl, thead, tbody, rhead;
5852
5853 outer_tbl = document.createElement("div");
5854 outer_tbl.className = "sites_outer";
5855
5856 inner_tbl = document.createElement("div");
5857 inner_tbl.className = "sites_inner";
5858 outer_tbl.appendChild(inner_tbl);
5859
5860 tbl = document.createElement("table");
5861 tbl.className = "sites_tbl";
5862 inner_tbl.appendChild(tbl);
5863
5864 thead = document.createElement("thead");
5865 tbl.appendChild(thead);
5866 tbody = document.createElement("tbody");
5867 tbl.appendChild(tbody);
5868
5869 rhead = thead.insertRow(thead.rows.length);
5870 add_site_header(rhead, "", true);
5871 add_site_header(rhead, "Name", false, "pop_seq_name");
5872 add_site_header(rhead, "Strand", false, "pop_site_strand", "site_strand_title");
5873 add_site_header(rhead, "Start", false, "pop_site_start");
5874 add_site_header(rhead, make_pv_text(), false, "pop_site_pvalue");
5875 add_site_header(rhead, "", false);
5876 add_site_header(rhead, "Sites", true, "pop_site_match");
5877 add_site_header(rhead, "", false);
5878
5879 inner_tbl.data_motif = motif;
5880 inner_tbl.data_site_index = append_site_entries(tbody, motif, 0, 20);
5881 if (inner_tbl.data_site_index < motif["sites"].length) {
5882 inner_tbl.addEventListener('scroll', make_site_entries, false);
5883 }
5884 return outer_tbl;
5885 }
5886
5887 function make_motif_table_entry(row, alphabet, ordinal, motif, colw) {
5888 "use strict";
5889 function ev_sig(evalue_str) {
5890 "use strict";
5891 var ev_re, match, sig, exp, num;
5892 ev_re = /^(.*)e(.*)$/;
5893 if (match = ev_re.exec(evalue_str)) {
5894 sig = parseFloat(match[1]);
5895 exp = parseInt(match[2]);
5896 if (exp >= 0) {
5897 return false;
5898 } else if (exp <= -3) {
5899 return true;
5900 } else {
5901 return sig * Math.pow(10, exp) <= 0.05;
5902 }
5903 }
5904 return true;
5905 }
5906 function make_preview(alphabet, motif) {
5907 "use strict";
5908 var pspm, preview, preview_rc;
5909 var box, btn_box, logo_box, btn_plus, btn_minus;
5910 if (motif["preview_logo"]) {
5911 preview = motif["preview_logo"];
5912 preview_rc = motif["preview_logo_rc"];
5913 } else {
5914 pspm = new Pspm(motif["pwm"]);
5915 preview = make_logo(alphabet, pspm, 50, false, 0);
5916 motif["preview_logo"] = preview;
5917 if (alphabet.has_complement()) {
5918 preview_rc = make_logo(alphabet, pspm, 50, true, 0, "logo_rc");
5919 motif["preview_logo_rc"] = preview_rc;
5920 }
5921 }
5922 if (preview_rc) {
5923 btn_plus = document.createElement("div");
5924 btn_plus.appendChild(document.createTextNode("+"));
5925 btn_plus.className = "preview_btn plus";
5926 btn_plus.tabIndex = "0";
5927 btn_plus.addEventListener("click", action_btn_rc, false);
5928 btn_plus.addEventListener("keydown", action_btn_rc, false);
5929 btn_minus = document.createElement("div");
5930 btn_minus.appendChild(document.createTextNode("-"));
5931 btn_minus.className = "preview_btn minus";
5932 btn_minus.tabIndex = "0";
5933 btn_minus.addEventListener("click", action_btn_rc, false);
5934 btn_minus.addEventListener("keydown", action_btn_rc, false);
5935 btn_box = document.createElement("div");
5936 btn_box.className = "preview_btn_box";
5937 btn_box.appendChild(btn_plus);
5938 btn_box.appendChild(btn_minus);
5939 }
5940 logo_box = document.createElement("div");
5941 logo_box.className = "preview_logo_box";
5942 logo_box.appendChild(preview);
5943 if (preview_rc) logo_box.appendChild(preview_rc);
5944 box = document.createElement("div");
5945 box.className = "preview_box";
5946 if (preview_rc) box.appendChild(btn_box);
5947 box.appendChild(logo_box);
5948 if (preview_rc) {
5949 if (motif["rc"]) {
5950 btn_minus.className += " active";
5951 logo_box.className += " show_rc_logo";
5952 } else {
5953 btn_plus.className += " active";
5954 }
5955 }
5956 return box;
5957 }
5958 var pspm, preview, preview_rc, c;
5959 row.data_motif = motif;
5960 row.id = motif["alt"];
5961 row.data_ordinal = ordinal;
5962 if (!ev_sig(motif["evalue"])) {
5963 row.style.opacity = 0.4;
5964 }
5965 add_text_cell(row, "" + ordinal + ".", "motif_ordinal");
5966 add_cell(row, make_preview(alphabet, motif), "motif_logo");
5967 add_text_cell(row, motif["evalue"], "motif_evalue");
5968 add_text_cell(row, motif["nsites"], "motif_nsites");
5969 add_text_cell(row, motif["len"], "motif_width");
5970 add_cell(row, make_sym_btn("\u21A7", "Show more information.",
5971 action_show_more), "motif_more");
5972 add_cell(row, make_sym_btn("\u21E2", "Submit the motif to another MEME Suite program or download it.", function(e) { action_show_outpop(e, ordinal); }), "motif_submit");
5973 if (colw) {
5974 for (c = 0; c < row.cells.length; c++) {
5975 row.cells[c].style.minWidth = colw[c] + "px";
5976 }
5977 }
5978 }
5979
5980 function make_motifs_table(alphabet, start_ordinal, motifs, colw, stop_reason) {
5981 var i, j;
5982 var tbl, thead, tbody, tfoot, row, preview;
5983 var motif, pspm;
5984
5985 tbl = document.createElement("table");
5986
5987 thead = document.createElement("thead");
5988 tbl.appendChild(thead);
5989 tbody = document.createElement("tbody");
5990 tbl.appendChild(tbody);
5991 tfoot = document.createElement("tfoot");
5992 tbl.appendChild(tfoot);
5993
5994 row = thead.insertRow(thead.rows.length);
5995 add_text_header_cell(row, "", "", "motif_ordinal");
5996 add_text_header_cell(row, "Logo", "pop_logo", "motif_logo");
5997 add_text_header_cell(row, "E-value", "pop_ev", "motif_evalue");
5998 add_text_header_cell(row, "Sites", "pop_sites", "motif_nsites");
5999 add_text_header_cell(row, "Width", "pop_width", "motif_width");
6000 add_text_header_cell(row, "More", "pop_more", "motif_more");
6001 add_text_header_cell(row, "Submit/Download", "pop_submit_dl", "motif_submit");
6002
6003 for (i = 0; i < motifs.length; i++) {
6004 row = tbody.insertRow(tbody.rows.length);
6005 make_motif_table_entry(row, alphabet, start_ordinal + i, motifs[i], colw);
6006 }
6007
6008 row = tfoot.insertRow(tfoot.rows.length);
6009 add_text_header_cell(row, stop_reason, "", "stop_reason", "", 6);
6010
6011 return tbl;
6012 }
6013
6014 function make_expanded_motif(alphabet, ordinal, motif, less_x, submit_x) {
6015 "use strict";
6016 var box, pspm, logo_box, large_logo, large_logo_rc, tab_logo, tab_logo_rc;
6017 var btn, offset, norc;
6018
6019 box = clone_template("tmpl_motif_expanded");
6020 box.data_motif = motif;
6021 box.data_ordinal = ordinal;
6022
6023 pspm = new Pspm(motif["pwm"]);
6024 if (typeof motif["rc"] !== "boolean") {
6025 motif["rc"] = false;
6026 }
6027 if (motif["large_logo"]) {
6028 large_logo = motif["large_logo"];
6029 large_logo_rc = motif["large_logo_rc"];
6030 } else {
6031 large_logo = make_large_logo(alphabet, pspm, false, 0);
6032 motif["large_logo"] = large_logo;
6033 if (alphabet.has_complement()) {
6034 large_logo_rc = make_large_logo(alphabet, pspm, true, 0, "logo_rc");
6035 motif["large_logo_rc"] = large_logo_rc;
6036 }
6037 }
6038 norc = (large_logo_rc == null);
6039 toggle_class(box, "norc", norc);
6040
6041 logo_box = find_child(box, "tvar_logo");
6042 logo_box.appendChild(large_logo);
6043 if (large_logo_rc) logo_box.appendChild(large_logo_rc);
6044 toggle_class(logo_box, "show_rc_logo", motif["rc"]);
6045
6046 tab_logo = find_child(box, "tvar_tab");
6047 tab_logo_rc = find_child(box, "tvar_tab_rc");
6048
6049 toggle_class(tab_logo, "activeTab", !motif["rc"]);
6050 toggle_class(tab_logo_rc, "activeTab", motif["rc"]);
6051
6052 tab_logo.addEventListener('click', action_rc_tab, false);
6053 tab_logo.addEventListener('keydown', action_rc_tab, false);
6054 tab_logo_rc.addEventListener('click', action_rc_tab, false);
6055 tab_logo_rc.addEventListener('keydown', action_rc_tab, false);
6056
6057 set_tvar(box, "tvar_ordinal", ordinal);
6058 set_tvar(box, "tvar_evalue", motif["evalue"]);
6059 set_tvar(box, "tvar_width", motif["len"]);
6060 set_tvar(box, "tvar_site_count", motif["nsites"]);
6061 set_tvar(box, "tvar_llr", motif["llr"]);
6062 set_tvar(box, "tvar_ic", motif["ic"]);
6063 set_tvar(box, "tvar_re", motif["re"]);
6064 set_tvar(box, "tvar_bt", motif["bt"]);
6065 if (data.sequence_db.primary_count > data.options.brief) {
6066 if (data.options.brief == 1000) {
6067 set_tvar(box, "tvar_sites", "Output of sites suppressed because there were more than 1000 (primary) sequences.");
6068 } else {
6069 set_tvar(box, "tvar_sites", "Output of sites suppressed by -brief option.");
6070 }
6071 } else {
6072 set_tvar(box, "tvar_sites", make_sites(motif));
6073 }
6074
6075 offset = 32; // 1* 5px padding + 2 * 10px padding + 2 * 2px border + 3px ??
6076
6077 btn = find_child(box, "tvar_less");
6078 btn.style.left = (less_x - offset) + "px";
6079 btn.addEventListener('click', action_show_less, false);
6080 btn.addEventListener('keydown', action_show_less, false);
6081 btn = find_child(box, "tvar_submit");
6082 btn.style.left = (submit_x - offset) + "px";
6083 btn.addEventListener('click', action_show_outpop, false);
6084 btn.addEventListener('keydown', action_show_outpop, false);
6085 return box;
6086 }
6087
6088 //
6089 //
6090 //
6091 function make_motifs() {
6092 "use strict";
6093 function pixel_value(str_in) {
6094 "use strict";
6095 var px_re, match;
6096 px_re = /^(\d+)px$/;
6097 if (match = px_re.exec(str_in)) {
6098 return parseInt(match[1], 10);
6099 }
6100 return 0;
6101 }
6102 var container, tbl;
6103 var colw, r, row, c, cell, cell_style, pad_left, pad_right;
6104
6105 // make the motifs table
6106 container = $("motifs");
6107 container.innerHTML = ""; // clear content
6108
6109 tbl = make_motifs_table(current_alphabet, 1, data["motifs"], colw, data["stop_reason"]);
6110 container.appendChild(tbl);
6111
6112 // measure table column widths
6113 colw = [];
6114 row = tbl.tBodies[0].rows[0];
6115 for (c = 0; c < row.cells.length; c++) {
6116 var padLeft, padRight;
6117 cell = row.cells[c];
6118 cell_style = window.getComputedStyle(cell, null);
6119 pad_left = pixel_value(cell_style.getPropertyValue("padding-left"));
6120 pad_right = pixel_value(cell_style.getPropertyValue("padding-right"));
6121 colw[c] = cell.clientWidth - pad_left - pad_right;
6122 if (typeof colw[c] !== "number" || colw[c] < 0) {
6123 colw[c] = 1;
6124 }
6125 }
6126
6127 // set minimum table column widths on each row so later when we remove rows it still aligns
6128 for (r = 0; r < tbl.tBodies[0].rows.length; r++) {
6129 row = tbl.tBodies[0].rows[r];
6130 for (c = 0; c < row.cells.length; c++) {
6131 row.cells[c].style.minWidth = colw[c] + "px";
6132 }
6133 }
6134
6135 // store the table column widths so we can create rows latter with the same minimums
6136 container.data_colw = colw;
6137
6138 // calculate the x offset for the buttons
6139 row = tbl.tBodies[0].rows[0];
6140 container.data_more_x = coords(find_child(find_child(row, "motif_more"), "sym_btn"))[0];
6141 container.data_submit_x = coords(find_child(find_child(row, "motif_submit"), "sym_btn"))[0];
6142
6143 draw_on_screen();
6144 }
6145
6146 function make_meme_block(container, max_seq_len, is_scan, site) {
6147 "use strict";
6148 var motif = data.motifs[site.motif];
6149 var block = make_block(container, max_seq_len, site.pos, motif.len,
6150 site.pvalue, site.rc, site.motif, is_scan);
6151 var handler = (is_scan ?
6152 make_scan_popup(site, motif, block) :
6153 make_block_popup(site, motif, block));
6154 block.addEventListener("mouseover", handler, false);
6155 block.addEventListener("mouseout", handler, false);
6156 }
6157
6158 function append_blocks_entries(tbody, seq_index, count) {
6159 "use strict";
6160 var i, end, j;
6161 var max_pvalue, max_block_height, max_seq_len, sequences;
6162 var sequence, sites, scans, scan;
6163 var container, plus, minus, rule, row;
6164 // define some constants
6165 max_seq_len = data.sequence_db.max_length;
6166 // determine how many to load
6167 end = Math.min(seq_index + count, data.sequence_db.sequences.length);
6168 for (i = seq_index; i < end; i++) {
6169 // get the sequence
6170 sequence = data.sequence_db.sequences[i];
6171 // make the containers for the block diagram
6172 container = make_block_container(current_alphabet.has_complement(),
6173 data.options.revcomp, max_seq_len, sequence.length);
6174 // create blocks for the motif sites
6175 sites = sequence["sites"];
6176 for (j = 0; j < sites.length; j++)
6177 make_meme_block(container, max_seq_len, false, sites[j]);
6178 // create blocks for the scanned sites
6179 scan = data.scan[i];
6180 for (j = 0; j < scan.sites.length; j++)
6181 make_meme_block(container, max_seq_len, true, scan.sites[j]);
6182 // create a row for the sequence
6183 row = tbody.insertRow(tbody.rows.length);
6184 toggle_class(row, "empty_seq", sites.length == 0 && scan.sites.length == 0);
6185 toggle_class(row, "only_scan", sites.length == 0 && scan.sites.length > 0);
6186 add_text_cell(row, (i + 1) + ".", "blockdiag_num");
6187 add_text_cell(row, sequence["name"], "blockdiag_name");
6188 add_text_cell(row, scan["pvalue"].toExponential(2), "blockdiag_pvalue");
6189 add_cell(row, container, "block_td");
6190 }
6191 return end;
6192 }
6193
6194 function make_blocks_entries() {
6195 "use strict";
6196 var region;
6197 region = this;
6198 if (region.data_blocks_index >= data["sequence_db"]["sequences"].length) {
6199 // all sites created
6200 region.removeEventListener('scroll', make_blocks_entries, false);
6201 return;
6202 }
6203 // if there's still 100 pixels to scroll then don't do anything yet
6204 if (region.scrollHeight - (region.scrollTop + region.offsetHeight) > 100) {
6205 return;
6206 }
6207
6208 region.data_blocks_index = append_blocks_entries(
6209 find_child(region, "blocks_tbl").tBodies[0],
6210 region.data_blocks_index, 20
6211 );
6212 }
6213
6214 // Apply opacity alpha to color rgb with backrgound bkg.
6215 function RGBAtoRGB(rgb, bkg, opacity) {
6216 var i;
6217 var rgb_new = [];
6218 for (i=0; i<3; i++) {
6219 rgb_new[i] = Math.round(((1-opacity) * bkg[i]) + (opacity * rgb[i]));
6220 }
6221 return rgb_new;
6222 }
6223
6224 // Function to measure the size of text on a canvas.
6225 var MeasureText = function(font, text) {
6226 var image = document.createElement("canvas");
6227 var image_ctx = image.getContext('2d');
6228 image_ctx.save();
6229 image_ctx.font = font;
6230 var text_length = image_ctx.measureText(text).width;
6231 image.remove();
6232 return text_length;
6233 } // MeasureText
6234
6235 // Functions to download the motif block diagram as a PDF or SVG file.
6236 function download_PDF_block_diagram() {
6237 downloadBlockDiagram(true, 'bitbucket');
6238 }
6239 function download_SVG_block_diagram() {
6240 downloadBlockDiagram(false, 'bitbucket');
6241 }
6242
6243 // Helper function to create a script element for downloading javascript.
6244 function createScriptElement(url, file, integrity)
6245 {
6246 console.log('Downloading ' + file + ' from ' + url + '.');
6247 const script = document.createElement('script');
6248 script.setAttribute('src', url + '/' + file);
6249 if (integrity) script.setAttribute('integrity', integrity);
6250 script.setAttribute('crossorigin', 'anonymous');
6251 script.setAttribute('async', '');
6252 document.head.appendChild(script);
6253 }
6254
6255 // Helper function to download the motif block diagram as a PDF or SVG file.
6256 var downloadBlockDiagram = function(
6257 make_pdf,
6258 next_source
6259 ) {
6260 var script_file, integrity_key;
6261 // Check if necessary javascript has been loaded.
6262 if ( (make_pdf && typeof jsPDF === 'undefined') ||
6263 (!make_pdf && typeof d3 === 'undefined') ) {
6264 if (next_source === 'bitbucket') {
6265 if (make_pdf) {
6266 script_file = 'jspdf.min.js';
6267 } else {
6268 script_file = 'd3.v5.min.js';
6269 }
6270 createScriptElement('https://memesuite.bitbucket.io/javascript', script_file);
6271 // Wait 3 seconds and then try to create the diagram.
6272 setTimeout(downloadBlockDiagram, 3000, make_pdf, 'cloudflare');
6273 } else if (next_source === 'cloudflare') {
6274 if (make_pdf) {
6275 script_file = 'jspdf/1.5.3/jspdf.min.js';
6276 integrity_key = 'sha512-ToRWKKOvhBSS8EtqSflysM/S7v9bB9V0X3B1+E7xo7XZBEZCPL3VX5SFIp8zxY19r7Sz0svqQVbAOx+QcLQSAQ=='
6277 } else {
6278 script_file = 'd3/5.16.0/d3.min.js';
6279 integrity_key = 'sha512-FHsFVKQ/T1KWJDGSbrUhTJyS1ph3eRrxI228ND0EGaEp6v4a/vGwPWd3Dtd/+9cI7ccofZvl/wulICEurHN1pg==';
6280 }
6281 createScriptElement('https://cdnjs.cloudflare.com/ajax/libs', script_file, integrity_key);
6282 // Wait 3 seconds and then try to create the diagram.
6283 setTimeout(downloadBlockDiagram, 3000, make_pdf, 'none');
6284 } else {
6285 // No more sources for javascript. Call so error message will be shown.
6286 downloadBlockDiagramMain(make_pdf);
6287 }
6288 } else {
6289 // Create the diagram.
6290 downloadBlockDiagramMain(make_pdf);
6291 }
6292 }
6293
6294 // Main function to download the motif block diagram as a PDF or SVG file.
6295 var downloadBlockDiagramMain= function(
6296 make_pdf
6297 ) {
6298 // Check that necessary javascript was downloaded.
6299 if ( (make_pdf && typeof jsPDF === 'undefined') ||
6300 (!make_pdf && typeof d3 === 'undefined') ) {
6301 var id = make_pdf ? $("pdfButton") : $("svgButton");
6302 help_popup(id, "pop_offline");
6303 return;
6304 }
6305
6306 // Determine which lines are visible in the HTML inner scroll window.
6307 var inner_tbl = $("blocks_scroll");
6308 var pix_per_sequence = 27; // (vertical) pixels per sequence diagram line
6309 var first = Math.round(inner_tbl.scrollTop / pix_per_sequence) + 1;
6310 var last = first + Math.round(inner_tbl.offsetHeight / pix_per_sequence) - 1;
6311
6312 // Get the contents of the HTML inner scroll window while saving the sequences to be printed.
6313 var numbers = document.getElementsByClassName("blockdiag_num");
6314 var bars = {};
6315 var visible_motifs = {};
6316 var seq_index = 0;
6317 var rgb;
6318 for (var i=0; i<numbers.length && seq_index < last; i++) {
6319 var row_node = numbers[i].parentNode;
6320 // Check if the sequence is displayed in the outer scrolling window.
6321 var seq_name = numbers[i].nextSibling.innerHTML;
6322 if (
6323 ($("rdo_sites_only").checked && row_node.getAttribute("class").includes("only_scan"))
6324 ||
6325 (! $("rdo_all_seqs").checked && row_node.getAttribute("class").includes("empty_seq"))
6326 ) { continue; }
6327 seq_index++;
6328
6329 if (seq_index < first) { continue; } // sequence not in HTML inner scrolling window
6330
6331 var pvalue = numbers[i].nextSibling.nextSibling.innerHTML;
6332 var far = numbers[i].nextSibling.nextSibling.nextSibling.children[0].children;
6333 var seq_length = data.sequence_db.sequences[i].length;
6334
6335 var seqObj = [];
6336 seqObj["length"] = seq_length;
6337 seqObj["pvalue"] = pvalue;
6338 seqObj["pn"] = [];
6339 seqObj["width"] = [];
6340 seqObj["left"] = [];
6341 seqObj["height"] = [];
6342 seqObj["color"] = [];
6343 seqObj["opacity"] = [];
6344 for (var x = 0; x < far.length; x++) {
6345 if ((far[x].getAttribute("style") != null)
6346 && (
6347 ( $("rdo_sites_only").checked && ! far[x].getAttribute("class").includes("scanned"))
6348 || ( $("rdo_sites_and_scan").checked || $("rdo_all_seqs").checked )
6349 )
6350 ) {
6351 if (far[x].getAttribute("style").includes("rgb")) {
6352 var compStyles = far[x].style;
6353 // Make scanned sites get displayed first so they will not "cover" regular sites.
6354 var site_pn = far[x].getAttribute("class").includes("top") ? "+" : "-";
6355 var site_width = parseFloat(compStyles.width.slice(0, -1));
6356 var site_left = parseFloat(compStyles.left.slice(0, -1));
6357 var site_height = parseFloat(compStyles.height.slice(0, -2));
6358 var site_color = compStyles.backgroundColor.slice(4, -1).replace(/ /g, "");
6359 if (far[x].getAttribute("class").includes("scanned")) {
6360 seqObj["pn"].unshift(site_pn);
6361 seqObj["width"].unshift(site_width);
6362 seqObj["left"].unshift(site_left);
6363 seqObj["height"].unshift(site_height);
6364 seqObj["color"].unshift(site_color);
6365 seqObj["opacity"].unshift(0.3);
6366 } else {
6367 seqObj["pn"].push(site_pn);
6368 seqObj["width"].push(site_width);
6369 seqObj["left"].push(site_left);
6370 seqObj["height"].push(site_height);
6371 seqObj["color"].push(site_color);
6372 seqObj["opacity"].push(1);
6373 }
6374 visible_motifs[far[x].getAttribute("data-colour-index")] = site_color;
6375 }
6376 }
6377 }
6378 // Save the sequence data if it has motifs (or rdo_all_seqs is checked)
6379 if ($("rdo_all_seqs").checked || seqObj["width"].length > 0) {
6380 bars[seq_name] = seqObj;
6381 }
6382 }
6383
6384 // jsPDF coordinates are always in points.
6385 var font_size = 13;
6386 var nbars = Object.keys(bars).length;
6387 var legend_font_size = 0.8 * font_size;
6388
6389 // Initialize field widths in points by measuring header text.
6390 var font = "bold " + font_size + "pt Helvetica, sans-serif";
6391 var max_name_width = MeasureText(font, "Name");
6392 var max_pvalue_width = MeasureText(font, "p-value");
6393
6394 var max_seq_length = 0; // in characters
6395 var has_complement = current_alphabet.has_complement();
6396 var revcomp = data.options["revcomp"];
6397
6398 // Measure text of numbers, names and p-values, convert to points and save the max.
6399 font = font_size + "pt Helvetica, sans-serif";
6400 var seq_name;
6401 for (seq_name in bars) {
6402 var seq_name_width = MeasureText(font, seq_name);
6403 var pvalue_width = MeasureText(font, pvalue);
6404 var seq_length = bars[seq_name]["length"];
6405 if (seq_length > max_seq_length) { max_seq_length = seq_length; }
6406 if (seq_name_width > max_name_width) { max_name_width = seq_name_width; }
6407 if (pvalue_width > max_pvalue_width) { max_pvalue_width = pvalue_width; }
6408 }
6409
6410 // Get the length in characters of the longest visible motif.
6411 var max_motif_length = 0;
6412 var motif_index, motif_length;
6413 for (motif_index in visible_motifs) {
6414 motif_length = data.motifs[motif_index].len;
6415 if (motif_length > max_motif_length) { max_motif_length = motif_length; }
6416 }
6417
6418 // Sort the motif indices.
6419 var motif_indices = [];
6420 var sorted_motif_indices = [];
6421 for (motif_index in visible_motifs) {
6422 motif_indices.push(Number(motif_index));
6423 }
6424 sorted_motif_indices = motif_indices.sort(function(a, b){return a-b;});
6425
6426 // Set up values for main section.
6427 var height = (nbars+1) * (2.6*font_size);
6428 var nmotifs = Object.keys(visible_motifs).length;
6429 var name_field_width = max_name_width + font_size;
6430 var pvalue_field_width = max_pvalue_width + font_size;
6431 var plus_minus_field_width = has_complement ? 2*font_size : font_size;
6432 var non_diagram_width = name_field_width + pvalue_field_width + plus_minus_field_width;
6433 var diagram_width = 47 * font_size;
6434 var pix_per_char = diagram_width/max_seq_length;
6435 var x_scale_factor = data.sequence_db.max_length/100; // Scale factor comes from function make_block().
6436 var diagram_line_height = (height-2*font_size)/nbars;
6437 var doc_width = diagram_width + non_diagram_width + 2*font_size;
6438 var doc_height = height + 0.5*font_size;
6439
6440 // Set up values for the legend.
6441 var tmp_font = legend_font_size + "pt Courier, normal";
6442 var courier_width = MeasureText(tmp_font, "A");
6443
6444 var legend_line_height = 1.2 * legend_font_size;
6445 var index_field_width = 3 * legend_font_size;
6446 var symbol_field_width = 5 * legend_font_size;
6447 var legend_non_consensus_width = index_field_width + symbol_field_width + 3*legend_font_size;
6448 var legend_hdr_font = legend_font_size + "pt Helvetica, sans-serif";
6449 var consensus_hdr_width = MeasureText(legend_hdr_font, "Motif Consensus");
6450 var consensus_field_width = doc_width - legend_non_consensus_width - legend_font_size;
6451 // Get number of characters that will fit in legend consensus field.
6452 var legend_split_length = Math.floor(consensus_field_width/courier_width);
6453 // Get number of lines in legend.
6454 var n_legend_lines = 0;
6455 for (motif_index in visible_motifs) {
6456 motif_length = data.motifs[motif_index].len;
6457 n_legend_lines += Math.ceil(motif_length/legend_split_length);
6458 }
6459 if (n_legend_lines > 0) { n_legend_lines += 3; } // header line + 2*space
6460 var legend_width = legend_non_consensus_width + Math.min(legend_split_length, max_motif_length)*courier_width;
6461 var legend_height = n_legend_lines * legend_line_height;
6462 doc_height += legend_height + 1*font_size;
6463
6464 if (make_pdf) {
6465 // Now create the PDF document.
6466 // This next line is necessary because jsPDF silently swaps width and height.
6467 var orient = doc_width > doc_height ? 'landscape' : 'portrait';
6468 doc = new jsPDF(
6469 {
6470 orientation: orient,
6471 unit: 'pt',
6472 format: [doc_width, doc_height]
6473 }
6474 );
6475
6476 // Set the font size for the PDF.
6477 doc.setFontSize(1.33*font_size);
6478
6479 // Create the header.
6480 var offset = font_size;
6481 var liney = 1.5*font_size;
6482 // .. Name hdr ..
6483 doc.setFont("Helvetica", "bold");
6484 doc.text("Name", offset, liney);
6485 offset += name_field_width;
6486
6487 // p-value hdr
6488 doc.setFont("Helvetica", "bolditalic");
6489 doc.text("p", offset + font_size, liney);
6490 doc.setFont("Helvetica", "bold");
6491 doc.text("-value", offset + 2*font_size, liney);
6492 offset += pvalue_field_width + plus_minus_field_width;
6493
6494 // Motif Location hdr
6495 doc.text("Motif Locations", offset, liney);
6496
6497 // Generate the data object for the PDF.
6498 liney -= 0.5*font_size;
6499 var dy = font_size/3.5;
6500 for (var seq_name in bars) {
6501 liney += diagram_line_height;
6502 offset = font_size;
6503
6504 //
6505 // Generate the text fields.
6506 //
6507 doc.setFont("Helvetica", "normal");
6508
6509 // Sequence name text
6510 doc.text(seq_name, offset, liney + dy);
6511 offset += name_field_width;
6512
6513 // p-value text
6514 doc.text(bars[seq_name]["pvalue"], offset + pvalue_field_width, liney + dy, {align: "right"});
6515 offset += pvalue_field_width;
6516
6517 // +/- text (optional)
6518 if (has_complement) {
6519 doc.text("+", offset+font_size, liney + dy - font_size/2);
6520 if (revcomp) {
6521 doc.text("-", offset+1.15*font_size, liney + dy + font_size/2);
6522 }
6523 }
6524 offset += plus_minus_field_width;
6525
6526 // Generate the base line.
6527 doc.setLineWidth(0.35);
6528 doc.line(offset, liney, offset + (bars[seq_name]["length"] * pix_per_char), liney);
6529
6530 // Generate the blocks.
6531 for (var i = 0; i < bars[seq_name]["width"].length; i++) {
6532 if (bars[seq_name]["pn"][i] == undefined) { continue; }
6533 rgb = bars[seq_name]["color"][i].split(",").map(Number);
6534 var opacity = bars[seq_name]["opacity"][i];
6535 if (opacity != 1) { rgb = RGBAtoRGB(rgb, [255,255,255], opacity); }
6536 var bar_x = offset + (bars[seq_name]["left"][i] * x_scale_factor * pix_per_char);
6537 var bar_y = (bars[seq_name]["pn"][i] == "+") ? (liney - 0.1*font_size*bars[seq_name]["height"][i]) : liney;
6538 doc.setFillColor(rgb[0], rgb[1], rgb[2]);
6539 doc.rect(bar_x, bar_y, bars[seq_name]["width"][i] * x_scale_factor * pix_per_char, 0.1*font_size*bars[seq_name]["height"][i], 'FD');
6540 }
6541 }
6542
6543 //
6544 // Generate the legend.
6545 //
6546 if (n_legend_lines > 0) {
6547 doc.setFontSize(1.33*legend_font_size);
6548 dy = 0.8 * legend_font_size;
6549
6550 // The legend header.
6551 var legend_top = liney + 2*legend_font_size;
6552 liney += 4.5*legend_font_size;
6553 offset = legend_font_size;
6554 doc.setFont("Helvetica", "bold");
6555 doc.text("Motif", offset, liney);
6556 offset += index_field_width + legend_font_size;
6557 doc.text("Symbol", offset, liney);
6558 offset += symbol_field_width + legend_font_size;
6559 doc.text("Motif Consensus", offset, liney);
6560 liney -= 0.5*legend_font_size;
6561 liney += legend_line_height;
6562
6563 for (var i=0; i<motif_indices.length; i++) {
6564 motif_index = sorted_motif_indices[i];
6565 offset = legend_font_size;
6566
6567 // Motif Name
6568 doc.setFont("Helvetica", "normal");
6569 var motif_index_string = (motif_index+1).toString();
6570 motif_index_string = motif_index_string + ".";
6571 var dx = 3 * legend_font_size;
6572 doc.text(motif_index_string, offset+dx, liney+dy, {align: "right"});
6573 offset += index_field_width + legend_font_size;
6574
6575 // Motif Symbol
6576 motif_length = data.motifs[motif_index].len;
6577 rgb = visible_motifs[motif_index].split(",").map(Number);
6578 var bar_x = offset;
6579 var bar_y = liney;
6580 doc.setFillColor(rgb[0], rgb[1], rgb[2]);
6581 doc.rect(bar_x, bar_y, symbol_field_width*(motif_length/max_motif_length), legend_font_size, 'FD');
6582 offset += symbol_field_width + legend_font_size;
6583
6584 // Motif Consensus Sequence
6585 doc.setFont("Courier", "normal");
6586 var motif_consensus = data.motifs[motif_index].id;
6587 doc.text(motif_consensus, offset, liney+dy, {maxWidth: legend_split_length*courier_width});
6588 liney += Math.ceil(motif_length/legend_split_length) * legend_line_height;
6589 }
6590
6591 // Draw box around legend.
6592 doc.rect(
6593 0.5*legend_font_size,
6594 legend_top + 0.5*legend_font_size,
6595 Math.max(legend_width, legend_non_consensus_width + consensus_hdr_width + courier_width),
6596 legend_height
6597 );
6598 } // legend
6599
6600 doc.save('motif_locations.pdf');
6601
6602 } else {
6603 // Download an SVG document.
6604 var body = d3.select("#blocks").append("svg")
6605 .attr("width", (diagram_width + non_diagram_width) + "pt")
6606 .attr("height", (doc_height+legend_font_size).toString())
6607 .attr("background-color", "lightgrey")
6608 .attr("id", "memeSVG")
6609 .attr("xmlns", "http://www.w3.org/2000/svg");
6610
6611 // Create the header.
6612 var x = 0;
6613 var offset = font_size;
6614 var liney = 1.5*font_size;
6615
6616 // .. Name hdr ..
6617 body.append("text")
6618 .attr("x", offset)
6619 .attr("y", liney)
6620 .attr("font-family", "Helvetica, sans-serif")
6621 .attr("font-size", font_size+"pt")
6622 .attr("font-weight", "bold")
6623 .text("Name");
6624 offset += name_field_width;
6625
6626 // p-value hdr
6627 body.append("text")
6628 .attr("x", offset + 2*font_size)
6629 .attr("y", liney)
6630 .attr("font-family", "Helvetica, sans-serif")
6631 .attr("font-size", font_size+"pt")
6632 .attr("font-weight", "bold")
6633 .attr("font-style", "italic")
6634 .text("p");
6635 body.append("text")
6636 .attr("x", offset + 3*font_size)
6637 .attr("y", liney)
6638 .attr("font-family", "Helvetica, sans-serif")
6639 .attr("font-size", font_size+"pt")
6640 .attr("font-weight", "bold")
6641 .text("-value");
6642 offset += pvalue_field_width + plus_minus_field_width + font_size;
6643
6644 // Motif Location hdr
6645 body.append("text")
6646 .attr("x", offset)
6647 .attr("y", liney)
6648 .attr("font-family", "Helvetica, sans-serif")
6649 .attr("font-size", font_size+"pt")
6650 .attr("font-weight", "bold")
6651 .text("Motif Locations");
6652
6653 // Generate the data for the SVG.
6654 liney -= 0.5*font_size;
6655 var dy = font_size/3.5;
6656 for (var seq_name in bars) {
6657 liney += diagram_line_height;
6658 offset = font_size;
6659
6660 //
6661 // Generate the text fields.
6662 //
6663
6664 // Sequence name text
6665 body.append("text")
6666 .attr("x", offset)
6667 .attr("y", liney + dy)
6668 .attr("font-family", "Helvetica, sans-serif")
6669 .attr("font-size", font_size+"pt")
6670 .text(seq_name);
6671 offset += name_field_width;
6672
6673 // p-value text
6674 body.append("text")
6675 .attr("x", offset + font_size+ pvalue_field_width)
6676 .attr("y", liney + dy)
6677 .attr("font-family", "Helvetica, sans-serif")
6678 .attr("font-size", font_size+"pt")
6679 .attr("text-anchor", "end")
6680 .text(bars[seq_name]["pvalue"]);
6681 offset += pvalue_field_width + font_size;
6682
6683 // +/- text (optional)
6684 if (has_complement) {
6685 body.append("text")
6686 .attr("x", offset+font_size)
6687 .attr("y", liney + dy - font_size/2)
6688 .attr("font-family", "Helvetica, sans-serif")
6689 .attr("font-size", font_size+"pt")
6690 .text("+");
6691 if (revcomp) {
6692 body.append("text")
6693 .attr("x", offset+1.15*font_size)
6694 .attr("y", liney + dy + font_size/2)
6695 .attr("font-family", "Helvetica, sans-serif")
6696 .attr("font-size", font_size+"pt")
6697 .text("-");
6698 }
6699 }
6700 offset += plus_minus_field_width;
6701
6702 // Generate the base line.
6703 body.append("line")
6704 .attr("x1", offset)
6705 .attr("x2", offset + (bars[seq_name]["length"] * pix_per_char))
6706 .attr("y1", liney)
6707 .attr("y2", liney)
6708 .attr("stroke-width", 0.5)
6709 .attr("stroke","black");
6710
6711 // Generate the blocks.
6712 for (var i = 0; i < bars[seq_name]["width"].length; i++) {
6713 if (bars[seq_name]["pn"][i] == undefined) { continue; }
6714 body.append("rect")
6715 .attr("x", offset + (bars[seq_name]["left"][i] * x_scale_factor * pix_per_char) )
6716 .attr("y", (bars[seq_name]["pn"][i] == "+") ? (liney - 0.1*font_size*bars[seq_name]["height"][i]) : liney)
6717 .attr("width", bars[seq_name]["width"][i] * x_scale_factor * pix_per_char)
6718 .attr("height", 0.1*font_size*bars[seq_name]["height"][i])
6719 .attr("fill", "rgb("+bars[seq_name]["color"][i] + ")")
6720 .attr("fill-opacity", bars[seq_name]["opacity"][i])
6721 .attr("stroke-width", 0.5)
6722 .attr("stroke","black");
6723 }
6724 }
6725
6726 //
6727 // Generate the legend.
6728 //
6729 if (n_legend_lines > 0) {
6730 dy = 0.8 * legend_font_size;
6731
6732 // The legend header.
6733 var legend_top = liney + 2*legend_font_size;
6734 liney += 4.5*legend_font_size;
6735 offset = legend_font_size;
6736 body.append("text")
6737 .attr("x", offset)
6738 .attr("y", liney)
6739 .attr("font-family", "Helvetica, sans-serif")
6740 .attr("font-size", legend_font_size+"pt")
6741 .attr("font-weight", "bold")
6742 .text("Motif");
6743 offset += index_field_width + legend_font_size;
6744 body.append("text")
6745 .attr("x", offset)
6746 .attr("y", liney)
6747 .attr("font-family", "Helvetica, sans-serif")
6748 .attr("font-size", legend_font_size+"pt")
6749 .attr("font-weight", "bold")
6750 .text("Symbol");
6751 offset += symbol_field_width + legend_font_size;
6752 body.append("text")
6753 .attr("x", offset)
6754 .attr("y", liney)
6755 .attr("font-family", "Helvetica, sans-serif")
6756 .attr("font-size", legend_font_size+"pt")
6757 .attr("font-weight", "bold")
6758 .text("Motif Consensus");
6759 liney -= 0.5*legend_font_size;
6760 liney += legend_line_height;
6761
6762 for (var i=0; i<motif_indices.length; i++) {
6763 motif_index = sorted_motif_indices[i];
6764 offset = legend_font_size;
6765
6766 // Motif Name
6767 var motif_index_string = (motif_index+1).toString();
6768 motif_index_string = motif_index_string + ".";
6769 var dx = 3.3 * legend_font_size;
6770 body.append("text")
6771 .attr("x", offset+dx)
6772 .attr("y", liney+dy)
6773 .attr("font-family", "Helvetica, sans-serif")
6774 .attr("font-size", legend_font_size+"pt")
6775 .attr("text-anchor", "end")
6776 .text(motif_index_string);
6777 offset += index_field_width + legend_font_size;
6778
6779 // Motif Symbol
6780 motif_length = data.motifs[motif_index].len;
6781 var bar_x = offset;
6782 var bar_y = liney;
6783 body.append("rect")
6784 .attr("x", bar_x )
6785 .attr("y", liney)
6786 .attr("width", symbol_field_width*(motif_length/max_motif_length))
6787 .attr("height", legend_font_size)
6788 .attr("fill", "rgb("+ visible_motifs[motif_index] + ")")
6789 .attr("stroke-width", 0.5)
6790 .attr("stroke","black");
6791 offset += symbol_field_width + legend_font_size;
6792
6793 // Motif Consensus Sequence
6794 var motif_consensus = data.motifs[motif_index].id;
6795 var cons_length = motif_consensus.length;
6796 var start_index = 0;
6797 while (start_index < cons_length) {
6798 body.append("text")
6799 .attr("x", offset)
6800 .attr("y", liney+dy)
6801 .attr("font-family", "Courier")
6802 .attr("font-size", legend_font_size+"pt")
6803 .text(motif_consensus.slice(start_index, Math.min(cons_length, start_index+legend_split_length)))
6804 liney += legend_line_height;
6805 start_index += legend_split_length;
6806 }
6807 }
6808
6809 // Draw box around legend.
6810 body.append("rect")
6811 .attr("x", 0.5*legend_font_size)
6812 .attr("y", legend_top + 0.5*legend_font_size)
6813 .attr("width", Math.max(legend_width, legend_non_consensus_width + consensus_hdr_width + courier_width))
6814 .attr("height", legend_height)
6815 .attr("fill", "none")
6816 .attr("stroke-width", 1)
6817 .attr("stroke", "black");
6818
6819 } // legend
6820
6821 var svg = document.getElementsByTagName("svg")[0].outerHTML;
6822 var svgBlob = new Blob([svg], {type:"image/svg+xml;charset=utf-8"});
6823 var svgUrl = URL.createObjectURL(svgBlob);
6824 var downloadLink = document.createElement("a");
6825 downloadLink.href = svgUrl;
6826 downloadLink.download = "meme-motif-locations.svg";
6827 document.getElementById("sites_sec").appendChild(downloadLink);
6828 downloadLink.click();
6829 downloadLink.remove();
6830 document.getElementById("memeSVG").remove();
6831
6832 } // SVG
6833
6834 };
6835
6836 function make_blocks() {
6837 "use strict";
6838 function add_seqs_filter(container, id, checked, label_text, help_topic) {
6839 "use strict";
6840 var label, radio;
6841 radio = document.createElement("input");
6842 radio.type = "radio";
6843 radio.name = "seqs_display";
6844 radio.id = id;
6845 radio.checked = checked;
6846 radio.addEventListener('click', action_seqs_filter, false);
6847 label = document.createElement("label");
6848 label.appendChild(document.createTextNode(label_text));
6849 label.htmlFor = id;
6850 container.appendChild(radio);
6851 container.appendChild(label);
6852 if (help_topic) {
6853 container.appendChild(document.createTextNode("\xA0"));
6854 container.appendChild(help_button(help_topic));
6855 }
6856 }
6857 function add_block_diagram_button(container, id, buttonText, help_topic) {
6858 var button, label;
6859 button = document.createElement("button");
6860 button.id = id;
6861 label = document.createTextNode(buttonText);
6862 button.appendChild(label);
6863 button.onclick = (id === "pdfButton") ? download_PDF_block_diagram : download_SVG_block_diagram;
6864 container.appendChild(document.createTextNode(" "));
6865 container.appendChild(button);
6866 if (help_topic) {
6867 container.appendChild(document.createTextNode("\xA0"));
6868 container.appendChild(help_button(help_topic));
6869 }
6870 //var new_icon = document.createElement("img");
6871 //new_icon.src = new_icon_src;
6872 //new_icon.alt = "NEW";
6873 //container.appendChild(document.createTextNode(" "));
6874 //container.appendChild(new_icon);
6875 }
6876 function add_blocks_header(row, title, nopad, help_topic) {
6877 "use strict";
6878 var div, divcp, th;
6879 th = document.createElement("th");
6880 div = document.createElement("div");
6881 div.className = "blocks_th_inner";
6882 if (typeof title !== "object") {
6883 title = document.createTextNode("" + title);
6884 }
6885 div.appendChild(title);
6886 if (help_topic) {
6887 div.appendChild(document.createTextNode("\xA0"));
6888 div.appendChild(help_button(help_topic));
6889 }
6890 divcp = div.cloneNode(true);
6891 divcp.className = "blocks_th_hidden";
6892 th.appendChild(div);
6893 th.appendChild(divcp);
6894 if (nopad) {
6895 th.className = "nopad";
6896 }
6897 row.appendChild(th);
6898 }
6899 var container;
6900 var page, view_height, outer_tbl, inner_tbl, tbl, thead, tbody, rhead;
6901 var in_view, i, seq_count;
6902
6903 page = (document.compatMode === "CSS1Compat") ? document.documentElement : document.body;
6904 view_height = Math.max(page.clientHeight - 300, 300);
6905
6906 container = $("blocks");
6907 toggle_class(container, "hide_empty_seqs", true);
6908 toggle_class(container, "hide_only_scan", true);
6909 container.innerHTML = "";
6910 add_seqs_filter(container, "rdo_sites_only", true, "Only Motif Sites", "pop_motif_sites");
6911 add_seqs_filter(container, "rdo_sites_and_scan", false, "Motif Sites+Scanned Sites", "pop_scanned_sites");
6912 add_seqs_filter(container, "rdo_all_seqs", false, "All Sequences", "pop_all_sequences");
6913 add_block_diagram_button(container, "pdfButton", "Download PDF", "pop_download_pdf_motif_locations");
6914 add_block_diagram_button(container, "svgButton", "Download SVG", "pop_download_svg_motif_locations");
6915
6916 outer_tbl = document.createElement("div");
6917 outer_tbl.className = "blocks_outer";
6918
6919 inner_tbl = document.createElement("div");
6920 inner_tbl.id = "blocks_scroll";
6921 inner_tbl.className = "blocks_inner";
6922 inner_tbl.style.maxHeight = view_height + "px";
6923 outer_tbl.appendChild(inner_tbl);
6924
6925 tbl = document.createElement("table");
6926 tbl.className = "blocks_tbl";
6927 inner_tbl.appendChild(tbl);
6928
6929 thead = document.createElement("thead");
6930 tbl.appendChild(thead);
6931 tbody = document.createElement("tbody");
6932 tbl.appendChild(tbody);
6933
6934 rhead = thead.insertRow(thead.rows.length);
6935 add_blocks_header(rhead, "", true);
6936 add_blocks_header(rhead, "Name", false, "pop_seq_name");
6937 add_blocks_header(rhead, make_pv_text(), false, "pop_seq_pvalue");
6938 add_blocks_header(rhead, "Motif Locations", false, "pop_motif_location");
6939
6940 container.appendChild(outer_tbl);
6941
6942 seq_count = data["sequence_db"]["sequences"].length;
6943 in_view = Math.max(Math.ceil(view_height / 25), 1);
6944 i = append_blocks_entries(tbody, 0, in_view);
6945
6946 while (i < seq_count && inner_tbl.scrollHeight - (inner_tbl.scrollTop + inner_tbl.offsetHeight) < 400) {
6947 i = append_blocks_entries(tbody, i, 20);
6948 }
6949 inner_tbl.data_blocks_index = i;
6950 if (i < seq_count) {
6951 inner_tbl.addEventListener('scroll', make_blocks_entries, false);
6952 }
6953 }
6954
6955 function make_scan_popup(site, motif) {
6956 return function (e) {
6957 "use strict";
6958 var pop, xy, padding, edge_padding, pop_left, pop_top, page_width;
6959 var lflank, match, rflank, pspm;
6960 if (!e) var e = window.event;
6961 pop = make_scan_popup.pop;
6962 if (e.type === "mouseover") {
6963 if (pop) return;
6964 pop = clone_template("tmpl_scan_info");
6965 pspm = new Pspm(motif.pwm);
6966 if (site.rc) pspm.reverse_complement(current_alphabet);
6967 set_tvar(pop, "tvar_logo", make_small_logo(current_alphabet, pspm, {"className": "scan_logo"}));
6968 set_tvar(pop, "tvar_motif", motif.id);
6969 set_tvar(pop, "tvar_pvalue", site.pvalue.toExponential(2));
6970 set_tvar(pop, "tvar_start", site.pos + 1);
6971 set_tvar(pop, "tvar_end", site.pos + motif.len);
6972
6973 document.body.appendChild(pop);
6974 position_popup(this, pop);
6975 make_scan_popup.pop = pop;
6976 } else if (e.type === "mouseout") {
6977 if (pop) {
6978 pop.parentNode.removeChild(pop);
6979 make_scan_popup.pop = null;
6980 }
6981 }
6982 };
6983 }
6984
6985 function make_block_popup(site, motif, block) {
6986 return function (e) {
6987 "use strict";
6988 var pop;
6989 var lflank, match, rflank, pspm, ruler, match_seq, match_width;
6990 if (!e) var e = window.event;
6991 pop = make_block_popup.pop;
6992 if (e.type === "mouseover") {
6993 if (pop) return;
6994 pop = clone_template("tmpl_block_info");
6995 pspm = new Pspm(motif.pwm);
6996 if (site.rc) { // must be dna
6997 pspm.reverse_complement(current_alphabet);
6998 lflank = current_alphabet.invcomp_seq(site.rflank);
6999 match = current_alphabet.invcomp_seq(site.match);
7000 rflank = current_alphabet.invcomp_seq(site.lflank);
7001 } else {
7002 lflank = site.lflank;
7003 match = site.match;
7004 rflank = site.rflank;
7005 }
7006 ruler = document.getElementById("measure_match");
7007 match_seq = make_seq(current_alphabet, match);
7008 ruler.innerHTML = "";
7009 ruler.appendChild(match_seq);
7010 match_width = ruler.clientWidth;
7011 ruler.removeChild(match_seq);
7012 set_tvar(pop, "tvar_lflank", lflank);
7013 set_tvar(pop, "tvar_match", match_seq);
7014 set_tvar(pop, "tvar_rflank", rflank);
7015 set_tvar(pop, "tvar_logo_pad", lflank);
7016 set_tvar(pop, "tvar_logo", make_small_logo(current_alphabet, pspm, {"width": match_width}));
7017 set_tvar(pop, "tvar_motif", motif.id);
7018 set_tvar(pop, "tvar_pvalue", site.pvalue.toExponential(2));
7019 set_tvar(pop, "tvar_start", site.pos + 1);
7020 set_tvar(pop, "tvar_end", site.pos + motif.len);
7021
7022 document.body.appendChild(pop);
7023 position_popup(block, pop);
7024 make_block_popup.pop = pop;
7025 } else if (e.type === "mouseout") {
7026 if (pop) {
7027 pop.parentNode.removeChild(pop);
7028 make_block_popup.pop = null;
7029 }
7030 }
7031 };
7032 }
7033
7034 //
7035 // action_show_more
7036 //
7037 // Show more information on the motif.
7038 ///
7039 function action_show_more(e) {
7040 var node, tr, tbody, table, container, motif, ordinal;
7041 var expanded_motif;
7042 if (!e) e = window.event;
7043 if (e.type === "keydown") {
7044 if (e.keyCode !== 13 && e.keyCode !== 32) {
7045 return;
7046 }
7047 // stop a submit or something like that
7048 e.preventDefault();
7049 }
7050 // find the row that contains the cell
7051 node = this;
7052 do {
7053 if (node.tagName === "TR") break;
7054 } while (node = node.parentNode);
7055 if (!node) throw new Error("Expected to find row!?");
7056 tr = node;
7057 // get info
7058 motif = tr.data_motif;
7059 ordinal = tr.data_ordinal;
7060 // find tbody
7061 do {
7062 if (node.tagName === "TBODY") break;
7063 } while (node = node.parentNode);
7064 if (!node) throw new Error("Expected to find tbody!?");
7065 tbody = node;
7066 // find table
7067 do {
7068 if (node.tagName === "TABLE") break;
7069 } while (node = node.parentNode);
7070 if (!node) throw new Error("Expected to find table!?");
7071 table = node;
7072 // find container
7073 container = node.parentNode;
7074 // make a expanded motif
7075 motif["expanded"] = true;
7076 expanded_motif = make_expanded_motif(current_alphabet, ordinal, motif,
7077 container.data_more_x, container.data_submit_x);
7078 // now determine how to place it
7079 if (tbody.rows.length === 1) {
7080 // only us in the table so the table can be replaced
7081 container.replaceChild(expanded_motif, table);
7082 } else if (tbody.rows[0] === tr) {
7083 // first row, so remove and insert an expanded motif before
7084 table.deleteRow(tr.rowIndex);
7085 container.insertBefore(expanded_motif, table);
7086 } else if (tbody.rows[tbody.rows.length - 1] === tr) {
7087 // last row, so remove and insert an expanded motif after
7088 table.deleteRow(tr.rowIndex);
7089 container.insertBefore(expanded_motif, table.nextSibling);
7090 } else {
7091 var table2, tbody2;
7092 table2 = table.cloneNode(false);
7093 table2.appendChild(table.tHead.cloneNode(true));
7094 tbody2 = table.tBodies[0].cloneNode(false);
7095 table2.appendChild(tbody2);
7096 container.insertBefore(table2, table.nextSibling);
7097 for (i = tbody.rows.length - 1; i >= 0; i--) {
7098 row = tbody.rows[i];
7099 row.parentNode.removeChild(row);
7100 if (row === tr) {
7101 break;
7102 }
7103 tbody2.insertBefore(row, tbody2.rows[0]);
7104 }
7105 container.insertBefore(expanded_motif, table2);
7106 }
7107 find_child(expanded_motif, "tvar_less").focus();
7108 }
7109
7110 //
7111 // action_show_less
7112 //
7113 // Show less information on the motif.
7114 ///
7115 function action_show_less(e) {
7116 var btn;
7117 var expanded_motif, container, motif, ordinal, colw, focus_target;
7118 var table, tbody, tbody2, row, table_before, table_after;
7119 if (!e) e = window.event;
7120 if (e.type === "keydown") {
7121 if (e.keyCode !== 13 && e.keyCode !== 32) {
7122 return;
7123 }
7124 // stop a submit or something like that
7125 e.preventDefault();
7126 }
7127 btn = this;
7128 // find expanded motif
7129 expanded_motif = find_parent(btn, "expanded_motif");
7130 if (!expanded_motif) throw new Error("Expected expanded motif.");
7131 // find the container
7132 container = expanded_motif.parentNode;
7133 // get data
7134 motif = expanded_motif.data_motif;
7135 ordinal = expanded_motif.data_ordinal;
7136 colw = container.data_colw;
7137 // get the table before
7138 table_before = expanded_motif.previousSibling;
7139 if (table_before && table_before.tagName !== "TABLE") {
7140 table_before = null;
7141 }
7142 // get the table after
7143 table_after = expanded_motif.nextSibling;
7144 if (table_after && table_after.tagName !== "TABLE") {
7145 table_after = null;
7146 }
7147 // see if there is a table below or above that we can put this in.
7148 // if there is a table both below and above then add this motif and
7149 // all ones below to the above table
7150 motif["expanded"] = false;
7151 if (table_before && table_after) {
7152 tbody = table_before.tBodies[0];
7153 row = tbody.insertRow(tbody.rows.length);
7154 make_motif_table_entry(row, current_alphabet, ordinal, motif, colw);
7155 focus_target = find_child(row.cells[5], "sym_btn");
7156 container.removeChild(expanded_motif);
7157 tbody2 = table_after.tBodies[0];
7158 while (tbody2.rows.length > 0) {
7159 row = tbody2.rows[0];
7160 row.parentNode.removeChild(row);
7161 tbody.appendChild(row);
7162 }
7163 container.removeChild(table_after);
7164 } else if (table_before) {
7165 tbody = table_before.tBodies[0];
7166 row = tbody.insertRow(tbody.rows.length);
7167 make_motif_table_entry(row, current_alphabet, ordinal, motif, colw);
7168 focus_target = find_child(row.cells[5], "sym_btn");
7169 container.removeChild(expanded_motif);
7170 } else if (table_after) {
7171 tbody = table_after.tBodies[0];
7172 row = tbody.insertRow(0);
7173 make_motif_table_entry(row, current_alphabet, ordinal, motif, colw);
7174 focus_target = find_child(row.cells[5], "sym_btn");
7175 container.removeChild(expanded_motif);
7176 } else {
7177 //no table above or below!
7178 // make a new table
7179 table = make_motifs_table(current_alphabet, ordinal, [motif], colw, data["stop_reason"]);
7180 focus_target = find_child(table.tBodies[0].rows[0].cells[5], "sym_btn");
7181 container.replaceChild(table, expanded_motif);
7182 }
7183 focus_target.focus();
7184 }
7185
7186 //TODO -- can we delete this junk?
7187 //function action_show_outpop(e) {
7188 function fred_action_show_outpop(e) {
7189 "use strict";
7190 function init() {
7191 "use strict";
7192 var close_btn, next_btn, prev_btn, cancel_btn, do_btn;
7193 var tab1, tab2, tab3;
7194 var pnl1, pnl2, pnl3;
7195 var format_list;
7196 var tbl_submit, inputs, i, default_prog;
7197 close_btn = $("outpop_close");
7198 close_btn.addEventListener("click", action_hide_outpop, false);
7199 close_btn.addEventListener("keydown", action_hide_outpop, false);
7200 next_btn = $("outpop_next");
7201 next_btn.addEventListener("click", action_outpop_next, false);
7202 next_btn.addEventListener("keydown", action_outpop_next, false);
7203 prev_btn = $("outpop_prev");
7204 prev_btn.addEventListener("click", action_outpop_prev, false);
7205 prev_btn.addEventListener("keydown", action_outpop_prev, false);
7206 cancel_btn = $("outpop_cancel");
7207 cancel_btn.addEventListener("click", action_hide_outpop, false);
7208 do_btn = $("outpop_do");
7209 do_btn.addEventListener("click", action_outpop_submit, false);
7210 tab1 = $("outpop_tab_1");
7211 tab1.tabIndex = 0;
7212 tab1.addEventListener("click", action_outpop_tab, false);
7213 tab1.addEventListener("keydown", action_outpop_tab, false);
7214 tab2 = $("outpop_tab_2");
7215 tab2.tabIndex = 0;
7216 tab2.addEventListener("click", action_outpop_tab, false);
7217 tab2.addEventListener("keydown", action_outpop_tab, false);
7218 tab3 = $("outpop_tab_3");
7219 tab3.tabIndex = 0;
7220 tab3.addEventListener("click", action_outpop_tab, false);
7221 tab3.addEventListener("keydown", action_outpop_tab, false);
7222 pnl1 = $("outpop_pnl_1");
7223 pnl2 = $("outpop_pnl_2");
7224 pnl3 = $("outpop_pnl_3");
7225 toggle_class(tab1, "activeTab", true);
7226 toggle_class(tab2, "activeTab", false);
7227 toggle_class(tab3, "activeTab", false);
7228 pnl1.style.display = "block";
7229 pnl2.style.display = "none";
7230 pnl3.style.display = "none";
7231 format_list = $("text_format");
7232 format_list.addEventListener("change", action_outpop_format, false);
7233 // setup program selection
7234 tbl_submit = $("programs");
7235 // when not dna, hide the inputs for programs that require dna motifs
7236 toggle_class(tbl_submit, "alphabet_dna", current_alphabet.has_complement());//TODO alphabet_dna is a bad name for a field when allowing custom alphabets
7237 // add a click listener for the radio buttons
7238 inputs = tbl_submit.querySelectorAll("input[type='radio']");
7239 for (i = 0; i < inputs.length; i++) {
7240 inputs[i].addEventListener("click", action_outpop_program, false);
7241 }
7242 // ensure that a default program option is selected for DNA and Protein
7243 default_prog = document.getElementById(current_alphabet.has_complement() ? "submit_tomtom" : "submit_fimo"); //TODO Tomtom might require a more strict definition of DNA
7244 default_prog.checked = true;
7245 action_outpop_program.call(default_prog);
7246 // disable reverse-complement when not DNA
7247 $("logo_rc_option").disabled = !current_alphabet.has_complement();
7248 // set errorbars on when ssc is on
7249 $("logo_ssc").addEventListener("change", action_outpop_ssc, false);
7250 }
7251 var node;
7252 // store the focused element
7253 action_hide_outpop.last_active = document.activeElement;
7254 if (!e) e = window.event;
7255 if (e.type === "keydown") {
7256 if (e.keyCode !== 13 && e.keyCode !== 32) {
7257 return;
7258 }
7259 // stop a submit or something like that
7260 e.preventDefault();
7261 }
7262 // hide the help popup
7263 help_popup();
7264 // on first load initilize the popup
7265 if (!action_show_outpop.ready) {
7266 init();
7267 action_show_outpop.ready = true;
7268 }
7269 // load the motif logo
7270 node = this;
7271 do {
7272 if (/\bexpanded_motif\b/.test(node.className) || node.tagName === "TR") break;
7273 } while (node = node.parentNode);
7274 if (node === null) throw new Error("Expected node!");
7275 update_outpop_motif(node.data_ordinal - 1);
7276 // display the download popup
7277 $("grey_out_page").style.display = "block";
7278 $("download").style.display = "block";
7279 $("outpop_close").focus();
7280 } // fred_action_show_outpop
7281
7282 function action_btn_rc(e) {
7283 "use strict";
7284 var node, tr, motif, box, logo_box, tab_st, tab_rc, rc;
7285 if (!e) e = window.event;
7286 if (e.type === "keydown") {
7287 if (e.keyCode !== 13 && e.keyCode !== 32) {
7288 return;
7289 }
7290 // stop a submit or something like that
7291 e.preventDefault();
7292 }
7293 node = this;
7294 do {
7295 if (node.tagName === "TR") break;
7296 } while (node = node.parentNode);
7297 if (!node) throw new Error("Expected to find row!?");
7298 tr = node;
7299 // get info
7300 motif = tr.data_motif;
7301 box = find_parent(this, "preview_box");
7302 logo_box = find_child(box, "preview_logo_box");
7303 tab_st = find_child(box, "plus");
7304 tab_rc = find_child(box, "minus");
7305 rc = (this === tab_rc);
7306 motif["rc"] = rc;
7307 toggle_class(logo_box, "show_rc_logo", rc);
7308 toggle_class(tab_st, "active", !rc);
7309 toggle_class(tab_rc, "active", rc);
7310 }
7311
7312 function action_rc_tab(e) {
7313 "use strict";
7314 var box, logo_box, tab_st, tab_rc, rc;
7315 if (!e) e = window.event;
7316 if (e.type === "keydown") {
7317 if (e.keyCode !== 13 && e.keyCode !== 32) {
7318 return;
7319 }
7320 // stop a submit or something like that
7321 e.preventDefault();
7322 }
7323 box = find_parent(this, "expanded_motif");
7324 logo_box = find_child(box, "tvar_logo");
7325 tab_st = find_child(box, "tvar_tab");
7326 tab_rc = find_child(box, "tvar_tab_rc");
7327 rc = (this === tab_rc);
7328 box.data_motif["rc"] = rc;
7329 toggle_class(logo_box, "show_rc_logo", rc);
7330 toggle_class(tab_st, "activeTab", !rc);
7331 toggle_class(tab_rc, "activeTab", rc);
7332 }
7333
7334 function action_seqs_filter() {
7335 "use strict";
7336 var block_container;
7337 block_container = $("blocks");
7338 if ($("rdo_all_seqs").checked) {
7339 toggle_class(block_container, "hide_empty_seqs", false);
7340 toggle_class(block_container, "hide_only_scan", false);
7341 } else if ($("rdo_sites_and_scan").checked) {
7342 toggle_class(block_container, "hide_empty_seqs", true);
7343 toggle_class(block_container, "hide_only_scan", false);
7344 } else if ($("rdo_sites_only").checked) {
7345 toggle_class(block_container, "hide_empty_seqs", true);
7346 toggle_class(block_container, "hide_only_scan", true);
7347 }
7348 }
7349
7350 //
7351 // page_loaded
7352 //
7353 // Called when the page has loaded for the first time.
7354 ///
7355 function page_loaded() {
7356 post_load_setup();
7357 }
7358
7359 //
7360 // page_loaded
7361 //
7362 // Called when a cached page is reshown.
7363 ///
7364 function page_shown(e) {
7365 if (e.persisted) post_load_setup();
7366 }
7367
7368 //
7369 // page_loaded
7370 //
7371 // Called when the page is resized
7372 ///
7373 function page_resized() {
7374 var page, blocks_scroll;
7375 update_scroll_pad();
7376 page = (document.compatMode === "CSS1Compat") ? document.documentElement : document.body;
7377 blocks_scroll = $("blocks_scroll");
7378 if (blocks_scroll) {
7379 blocks_scroll.style.maxHeight = Math.max(page.clientHeight - 300, 300) + "px";
7380 }
7381 }
7382
7383 //
7384 // pre_load_setup
7385 //
7386 // Run before the page is displayed
7387 ///
7388 function pre_load_setup() {
7389 var start, hue, sat, light, divisions;
7390 var i, j, motifs, motif, sites, site, sequences, sequence;
7391 var max_seq_len;
7392 motifs = data["motifs"];
7393 sequences = data["sequence_db"]["sequences"];
7394 max_seq_len = 1;
7395 if (sequences) { // no sequences if -brief
7396 for (i = 0; i < sequences.length; i++) {
7397 sequence = sequences[i];
7398 sequence["sites"] = [];
7399 if (sequence["length"] > max_seq_len) {
7400 max_seq_len = sequence["length"];
7401 }
7402 }
7403 }
7404 data["sequence_db"]["max_length"] = max_seq_len;
7405 // use hsl colours
7406 start = 0; //red
7407 sat = 100;
7408 light = 50;
7409 for (i = 0; i < motifs.length; i++) {
7410 motif = motifs[i];
7411 // give the motif a colour
7412 divisions = 1 << Math.ceil(Math.log(i + 1) / Math.LN2);
7413 hue = start + (360 / divisions) * ((i - (divisions >> 1)) * 2 + 1);
7414 motif["colour"] = "hsl(" + hue + ", " + sat + "%, " + light + "%)";
7415 // associate sites with sequences as well
7416 // to make generating the block diagram easier
7417 sites = motif["sites"];
7418 for (j = 0; j < sites.length; j++) {
7419 site = sites[j];
7420 sequence = sequences[site["seq"]];
7421 // record the motif index
7422 site["motif"] = i;
7423 // add the site to the sequence
7424 sequence["sites"].push(site);
7425 }
7426 }
7427 }
7428
7429 //
7430 // post_load_setup
7431 //
7432 // Run when the page has loaded, or been reloaded.
7433 //
7434 function post_load_setup() {
7435 update_scroll_pad();
7436 if (data["motifs"].length > 0) {
7437 make_motifs();
7438 if (data.sequence_db.primary_count > data.options.brief) {
7439 if (data.options.brief == 1000) {
7440 $("blocks").innerHTML = "<p>Output of sites suppressed because there were more than 1000 (primary) sequences.</p>";
7441 } else {
7442 $("blocks").innerHTML = "<p>Output of motif locations suppressed by -brief option.</p>";
7443 }
7444 } else {
7445 make_blocks();
7446 }
7447 } else {
7448 $("motifs").innerHTML = "<p>No significant motifs found!</p>"; // clear content
7449 $("motifs").innerHTML += "<p><b>" + data["stop_reason"] + "</b></p>";
7450 $("blocks").innerHTML = "<p>No significant motifs found!</p>";
7451 }
7452 }
7453
7454 pre_load_setup();
7455 </script>
7456 <script>
7457 //
7458 // template.js
7459 //
7460
7461 /*
7462 * Fill in a template variable
7463 */
7464 function set_tvar(template, tvar, value) {
7465 var node;
7466 node = find_child(template, tvar);
7467 if (node === null) {
7468 throw new Error("Template does not contain variable " + tvar);
7469 }
7470 node.innerHTML = "";
7471 if (typeof value !== "object") {
7472 node.appendChild(document.createTextNode(value));
7473 } else {
7474 node.appendChild(value);
7475 }
7476 } // set_tvar
7477
7478 /*
7479 * Get the text contained within the element.
7480 */
7481 function elem_text(elem, separator) {
7482 if (separator === undefined) separator = '';
7483 return text_nodes(elem).map(node_text).join(separator);
7484 }
7485
7486 /*
7487 * Get the text out of a specific text node.
7488 */
7489 function node_text(node) {
7490 if (node === undefined) {
7491 return '';
7492 } else if (node.textContent) {
7493 return node.textContent;
7494 } else if (node.innerText) {
7495 return node.innerText;
7496 } else {
7497 return '';
7498 }
7499 }
7500
7501 /*
7502 * Find all text nodes in the given container.
7503 */
7504 function text_nodes(container) {
7505 var textNodes = [];
7506 var stack = [container];
7507 // depth first search to maintain ordering when flattened
7508 while (stack.length > 0) {
7509 var node = stack.pop();
7510 if (node.nodeType == Node.TEXT_NODE) {
7511 textNodes.push(node);
7512 } else {
7513 for (var i = node.childNodes.length-1; i >= 0; i--) {
7514 stack.push(node.childNodes[i]);
7515 }
7516 }
7517 }
7518 return textNodes;
7519 }
7520
7521 /*
7522 * Create a button designed to contain a single symbol
7523 */
7524 function make_sym_btn(symbol, title, action) {
7525 var box, sbox;
7526 box = document.createElement("div");
7527 box.tabIndex = 0;
7528 box.className = "sym_btn";
7529 sbox = document.createElement("span");
7530 if (typeof symbol === "string") {
7531 sbox.appendChild(document.createTextNode(symbol));
7532 } else {
7533 sbox.appendChild(symbol);
7534 }
7535 box.appendChild(sbox);
7536 box.title = title;
7537 box.addEventListener('click', action, false);
7538 box.addEventListener('keydown', action, false);
7539 return box;
7540 }
7541
7542 /*
7543 * Create a pair of text spans with different classes.
7544 * This is useful when using CSS to only display one of them.
7545 */
7546 function text_pair(txt1, cls1, txt2, cls2) {
7547 var container, part1, part2;
7548 container = document.createElement("span");
7549 part1 = document.createElement("span");
7550 part1.appendChild(document.createTextNode(txt1));
7551 part1.className = cls1;
7552 container.appendChild(part1);
7553 part2 = document.createElement("span");
7554 part2.appendChild(document.createTextNode(txt2));
7555 part2.className = cls2;
7556 container.appendChild(part2);
7557 return container;
7558 }
7559 </script>
7560 <script>
7561 //
7562 // citation.js
7563 //
7564 function get_citation_text(doc_type, extra) {
7565 var html;
7566
7567 switch (doc_type) {
7568 case 'AMA':
7569 return(get_citation_text("GOMo", extra));
7570 case 'AME':
7571 return(extra + `
7572 <span class="citation">
7573 Robert C. McLeay and Timothy L. Bailey,
7574 &quot;Motif Enrichment Analysis: a unified framework and an evaluation on ChIP data&quot;,
7575 <i>BMC Bioinformatics</i>, <b>11</b>:165, 2010.
7576 <a href="http://www.biomedcentral.com/1471-2105/11/165">[full text]</a>
7577 </span>
7578 `);
7579 case 'CentriMo':
7580 return(extra + `
7581 <span class="citation">
7582 Timothy L. Bailey and Philip Machanick,
7583 &quot;Inferring direct DNA binding from ChIP-seq&quot;,
7584 <i>Nucleic Acids Research</i>, <b>40</b>:e128, 2012.
7585 <a href="http://nar.oxfordjournals.org/content/40/17/e128">[Full Text]</a>
7586 </span>
7587 `);
7588 case 'DREME':
7589 return(extra + `
7590 <span class="citation">
7591 Timothy L. Bailey,
7592 &quot;DREME: Motif discovery in transcription factor ChIP-seq data&quot;,
7593 <i>Bioinformatics</i>, <b>27</b>(12):1653-1659, 2011.
7594 <a href="http://bioinformatics.oxfordjournals.org/content/27/12/1653">[full text]</a>
7595 </span>
7596 `);
7597 case 'FIMO':
7598 return(extra + `
7599 <span class="citation">
7600 Charles E. Grant, Timothy L. Bailey and William Stafford Noble,
7601 &quot;FIMO: Scanning for occurrences of a given motif&quot;,
7602 <i>Bioinformatics</i> <b>27</b>(7):1017-1018, 2011.
7603 <a href="http://bioinformatics.oxfordjournals.org/content/27/7/1017">[full text]</a>
7604 </span>
7605 `);
7606 case 'GLAM2':
7607 case 'GLAM2SCAN':
7608 return(extra + `
7609 <span class="citation">
7610 Martin C. Frith, Neil F. W. Saunders, Bostjan Kobe and Timothy L. Bailey,
7611 &quot;Discovering sequence motifs with arbitrary insertions and deletions&quot;,
7612 <i>PLoS Computational Biology</i>, <b>4</b>(5):e1000071, 2008.
7613 <a href="https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1000071">[full text]</a>
7614 </span>
7615 `);
7616 case 'GOMo':
7617 return(extra + `
7618 <span class="citation">
7619 Fabian A. Buske, Mikael Bod&eacute;n, Denis C. Bauer and Timothy L. Bailey,
7620 &quot;Assigning roles to DNA regulatory motifs using comparative genomics&quot;,
7621 <i>Bioinformatics</i>, <b>26</b>(7), 860-866, 2010.
7622 <a href="http://bioinformatics.oxfordjournals.org/cgi/content/full/26/7/860">[full text]</a>
7623 </span>
7624 `);
7625 case 'MAST':
7626 return(extra + `
7627 <span class="citation">
7628 Timothy L. Bailey and Michael Gribskov,
7629 &quot;Combining evidence using p-values: application to sequence homology searches&quot;,
7630 <i>Bioinformatics</i>, <b>14</b>(1):48-54, 1998.
7631 <a href="http://bioinformatics.oxfordjournals.org/content/14/1/48">[full text]</a>
7632 </span>
7633 `);
7634 case 'MCAST':
7635 return(extra + `
7636 <span class="citation">
7637 Timothy Bailey and William Stafford Noble,
7638 &quot;Searching for statistically significant regulatory modules&quot;,
7639 <i>Bioinformatics (Proceedings of the European Conference on Computational Biology)</i>,
7640 <b>19</b>(Suppl. 2):ii16-ii25, 2003.
7641 <a href="http://bioinformatics.oxfordjournals.org/cgi/content/abstract/19/suppl_2/ii16">[full text]</a>
7642 </span>
7643 `);
7644 case 'Meta-MEME':
7645 return(extra + `
7646 <span class="citation">
7647 William N. Grundy, Timothy L. Bailey, Charles P. Elkan and Michael E. Baker.
7648 &quot;Meta-MEME: Motif-based Hidden Markov Models of Protein Families&quot;
7649 <i>Computer Applications in the Biological Sciences (CABIOS)</i>,
7650 <b>13</b>(4):397-406, 1997.
7651 <a href="http://bioinformatics.oxfordjournals.org/content/13/4/397">[full text]</a>
7652 </span>
7653 `);
7654 case 'MEME':
7655 return(extra + `
7656 <span class="citation">
7657 Timothy L. Bailey and Charles Elkan,
7658 &quot;Fitting a mixture model by expectation maximization to
7659 discover motifs in biopolymers&quot;,
7660 <em>Proceedings of the Second International Conference on Intelligent Systems
7661 for Molecular Biology</em>, pp. 28-36, AAAI Press, Menlo Park, California, 1994.
7662 <a href="http://www.aaai.org/Papers/ISMB/1994/ISMB94-004.pdf">[full text]</a>
7663 </span>
7664 `);
7665 case 'MEME-ChIP':
7666 return(extra + `
7667 <span class="citation">
7668 Philip Machanick and Timothy L. Bailey,
7669 &quot;MEME-ChIP: motif analysis of large DNA datasets&quot;,
7670 <i>Bioinformatics</i> <b>27</b>(12):1696-1697, 2011.
7671 <a href="http://bioinformatics.oxfordjournals.org/content/27/12/1696.full">[full text]</a>
7672 </span>
7673 `);
7674 case 'MEME_SUITE':
7675 return(extra + `
7676 <span class="citation">
7677 Timothy L. Bailey, James Johnson, Charles E. Grant, William S. Noble,
7678 &quot;The MEME Suite&quot;,
7679 <i>Nucleic Acids Research</i>, <b>43</b>(W1):W39-W49, 2015.
7680 <a href="https://academic.oup.com/nar/article/43/W1/W39/2467905">[full text]</a>
7681 </span>
7682 `);
7683 case 'MoMo':
7684 return(extra + `
7685 <span class="citation">
7686 Alice Cheng, Charles Grant, Timothy L. Bailey and William Noble,
7687 &quot;MoMo: Discovery of statistically significant post-translational modification motifs&quot;,
7688 <i>Bioinformatics</i>, <b>35</b>(16):2774-2782, 2018.
7689 <a href="https://doi.org/10.1093/bioinformatics/bty1058">[full text]</a>
7690 </span>
7691 `);
7692 case 'PSPs':
7693 return(extra + `
7694 <span class="citation">
7695 Timothy L. Bailey, Mikael Bod&eacute;n, Tom Whitington and Philip Machanick,
7696 &quot;The value of position-specific priors in motif discovery using MEME&quot;,
7697 <i>BMC Bioinformatics</i>, <b>11</b>(1):179, 2010.
7698 <a href="http://www.biomedcentral.com/1471-2105/11/179">[full text]</a>
7699 </span>
7700 `);
7701 case 'SEA':
7702 return(extra + `
7703 <span class="citation">
7704 Timothy L. Bailey and Charles E. Grant, &quot;SEA: Simple Enrichment Analysis of motifs&quot;,
7705 <i>BioRxiv</i>, August 24, 2021.
7706 <a href="https://www.biorxiv.org/content/10.1101/2021.08.23.457422v1">[full text]</a>
7707 </span>
7708 `);
7709 case 'SpaMo':
7710 return(extra + `
7711 <span class="citation">
7712 Tom Whitington, Martin C. Frith, James Johnson and Timothy L. Bailey
7713 &quot;Inferring transcription factor complexes from ChIP-seq data&quot;,
7714 <i>Nucleic Acids Res.</i> <b>39</b>(15):e98, 2011.
7715 <a href="http://nar.oxfordjournals.org/content/39/15/e98">[full text]</a>
7716 </span>
7717 `);
7718 case 'STREME':
7719 return(extra + `
7720 <span class="citation">
7721 Timothy L. Bailey,
7722 &quot;STREME: accurate and versatile sequence motif discovery&quot;,
7723 <i>Bioinformatics</i>, Mar. 24, 2021.
7724 <a href="https://academic.oup.com/bioinformatics/advance-article-abstract/doi/10.1093/bioinformatics/btab203/6184861" >[full text]</a>
7725 </span>
7726 `);
7727 case 'Tomtom':
7728 return(extra + `
7729 <span class="citation">
7730 Shobhit Gupta, JA Stamatoyannopolous, Timothy Bailey and William Stafford Noble,
7731 &quot;Quantifying similarity between motifs&quot;,
7732 <i>Genome Biology</i>, <b>8</b>(2):R24, 2007.
7733 <a href="http://genomebiology.com/2007/8/2/R24">[full text]</a>
7734 </span>
7735 `);
7736 case 'T-Gene':
7737 return(extra + `
7738 <span class="citation">
7739 Timothy O'Connor, Charles E. Grant, Mikael Bod&eacute;n, Timothy L. Bailey,
7740 &quot;T-Gene: Improved target gene prediction&quot;,
7741 <i>Bioinformatics</i>, <b>36</b>(12):3902-3904, 2020.
7742 <a href="https://academic.oup.com/bioinformatics/article/36/12/3902/5815978?guestAccessKey=aa625a49-a2aa-4d7a-858e-8bc82867a534">[Full Text]</a>
7743 </span>
7744 `);
7745 case 'XSTREME':
7746 return(extra + `
7747 <span class="citation">
7748 Charles E. Grant and Timothy L. Bailey, &quot;XSTREME: comprehensive motif analysis of biological sequence datasets&quot;,
7749 <i>BioRxiv</i>, September 3, 2021.
7750 <a href="https://www.biorxiv.org/content/10.1101/2021.09.02.458722v1">[full text]</a>
7751 </span>
7752 `);
7753 default:
7754 return("Unknown program: " + doc_type);
7755 }
7756 } // get_citation_text
7757
7758 //
7759 // Function to replace the innerHTML of element "id" with an HTML paragraph
7760 // containing the text for 'program', which is known to function get_citation_text.
7761 // If "id" is either "citation" or "reference" some extra text is printed.
7762 //
7763 function print_citation(id, program) {
7764 var extra;
7765 switch (id) {
7766 case 'citation':
7767 extra = "If you use " + program + " in your research, please cite the following paper:<br>";
7768 break;
7769 case 'reference':
7770 extra = "<h5>Reference</h5>";
7771 break;
7772 default:
7773 extra = "";
7774 break;
7775 };
7776 var html = get_citation_text(program, extra);
7777 document.getElementById(id).insertAdjacentHTML('beforeend', html);
7778 } // print_citation
7779
7780 //
7781 // Function to convert a citation for a program to a C #define statement.
7782 //
7783 function print_citation_define(lang, pgm) {
7784 var citation = get_citation_text(pgm, '');
7785 citation = citation.replace(/<[^>]*>/g, '');
7786 citation = citation.replace(/\[.*\]/g, '');
7787 citation = citation.replace(/\n\s*/g, '\\n');
7788 citation = citation.replace(/&quot;/g, '\\"');
7789 citation = citation.replace(/&eacute;/g, 'e');
7790 citation = citation.replace(/^\\n/, '');
7791 pgm = pgm.replace(/-/, '');
7792 citation = "If you use this program in your research, please cite:\\n\\n" + citation;
7793 if (lang == "C") {
7794 citation = "#define " + pgm + '_CITE "' + citation + '"';
7795 } else if (lang == "perl") {
7796 citation = '"' + pgm + '" => "' + citation + '",';
7797 }
7798 return(citation);
7799 } // print_citation_define
7800
7801 //
7802 // Main program (for use with nodejs "node" javascript engine)
7803 // to create citation.js.h and citation.pm from citation.js.
7804 // The command line:
7805 // node citation.js C > citation.js.h
7806 // will output the C #define statements for each of the
7807 // programs listed below, defining macros <program>_CITE.
7808 // The command line:
7809 // node citation.js perl > citation.js.pm
7810 // will output perl hash <program> => text
7811 //
7812 //
7813 if (typeof process !== 'undefined') {
7814 var lang = process.argv[2];
7815 var programs = ['AMA', 'AME', 'CentriMo', 'DREME', 'FIMO', 'GLAM2',
7816 'GLAM2SCAN', 'GOMo', 'MAST', 'MCAST', 'Meta-MEME', 'MEME',
7817 'MEME-ChIP', 'MEME_SUITE', 'MoMo', 'PSPs', 'SEA', 'SpaMo',
7818 'STREME', 'Tomtom', 'T-Gene', 'XSTREME'];
7819
7820 if (lang == "C") {
7821 console.log("// Do not edit this file. It is created from etc/citation.js.");
7822 console.log("#ifndef citation_js_h\n#define citation_js_h\n");
7823 for (var i=0; i<programs.length; i++) {
7824 console.log(print_citation_define(lang, programs[i]));
7825 }
7826 console.log("\n#endif");
7827 } else if (lang == "perl") {
7828 console.log("# Do not edit this file. It is created from etc/citation.js.");
7829 console.log("package Citation;");
7830 console.log("sub cite {\n my ($pgm) = @_;\n return $citation{$pgm};\n}");
7831 console.log("%citation = (");
7832 for (var i=0; i<programs.length; i++) {
7833 console.log(print_citation_define(lang, programs[i]));
7834 }
7835 console.log(");");
7836 }
7837 }
7838 </script>
7839 <style>
7840 /* The following is the content of meme.css */
7841 body { background-color:white; font-size: 12px; font-family: Verdana, Arial, Helvetica, sans-serif;}
7842
7843 div.help {
7844 display: inline-block;
7845 margin: 0px;
7846 padding: 0px;
7847 width: 12px;
7848 height: 13px;
7849 cursor: pointer;
7850 background-image: url(data:image/gif;base64,R0lGODlhDAANAIABANR0AP///yH5BAEAAAEALAAAAAAMAA0AAAIdhI8Xy22MIFgv1DttrrJ7mlGNNo4c+aFg6SQuUAAAOw==);
7851 }
7852
7853 div.help:hover {
7854 background-image: url(data:image/gif;base64,R0lGODlhDAANAKEAANR0AP///9R0ANR0ACH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEAAAIALAAAAAAMAA0AAAIdDGynCe3PgoxONntvwqz2/z2K2ImjR0KhmSIZUgAAOw==);
7855 }
7856
7857 p.spaced { line-height: 1.8em;}
7858
7859 span.citation { font-family: "Book Antiqua", "Palatino Linotype", serif; color: #004a4d;}
7860
7861 p.pad { padding-left: 30px; padding-top: 5px; padding-bottom: 10px;}
7862
7863 td.jump { font-size: 13px; color: #ffffff; background-color: #00666a;
7864 font-family: Georgia, "Times New Roman", Times, serif;}
7865
7866 a.jump { margin: 15px 0 0; font-style: normal; font-variant: small-caps;
7867 font-weight: bolder; font-family: Georgia, "Times New Roman", Times, serif;}
7868
7869 h2.mainh {font-size: 1.5em; font-style: normal; margin: 15px 0 0;
7870 font-variant: small-caps; font-family: Georgia, "Times New Roman", Times, serif;}
7871
7872 h2.line {border-bottom: 1px solid #CCCCCC; font-size: 1.5em; font-style: normal;
7873 margin: 15px 0 0; padding-bottom: 3px; font-variant: small-caps;
7874 font-family: Georgia, "Times New Roman", Times, serif;}
7875
7876 h4 {border-bottom: 1px solid #CCCCCC; font-size: 1.2em; font-style: normal;
7877 margin: 10px 0 0; padding-bottom: 3px; font-family: Georgia, "Times New Roman", Times, serif;}
7878
7879 h5 {margin: 0px}
7880
7881 a.help { font-size: 9px; font-style: normal; text-transform: uppercase;
7882 font-family: Georgia, "Times New Roman", Times, serif;}
7883
7884 div.pad { padding-left: 30px; padding-top: 5px; padding-bottom: 10px;}
7885
7886 div.pad1 { margin: 10px 5px;}
7887
7888 div.pad2 { margin: 25px 5px 5px;}
7889 h2.pad2 { padding: 25px 5px 5px;}
7890
7891 div.pad3 { padding: 5px 0px 10px 30px;}
7892
7893 div.box { border: 2px solid #CCCCCC; padding:10px; overflow: hidden;}
7894
7895 div.bar { border-left: 7px solid #00666a; padding:5px; margin-top:25px; }
7896
7897 div.subsection {margin:25px 0px;}
7898
7899 img {border:0px none;}
7900
7901 th.majorth {text-align:left;}
7902 th.minorth {font-weight:normal; text-align:left; width:8em; padding: 3px 0px;}
7903 th.actionth {font-weight:normal; text-align:left;}
7904
7905 .explain h5 {font-size:1em; margin-left: 1em;}
7906
7907 div.doc {margin-left: 2em; margin-bottom: 3em;}
7908
7909 th.trainingset {
7910 border-bottom: thin dashed black;
7911 font-weight:normal;
7912 padding:0px 10px;
7913 }
7914 div.pop_content {
7915 position:absolute;
7916 z-index:50;
7917 width:300px;
7918 padding: 5px;
7919 background: #E4ECEC;
7920 font-size: 12px;
7921 font-family: Arial;
7922 border-style: double;
7923 border-width: 3px;
7924 border-color: #AA2244;
7925 display:none;
7926 }
7927 div.pop_content_wide {
7928 position:absolute;
7929 z-index:1;
7930 width:700px;
7931 padding: 5px;
7932 background: #E4ECEC;
7933 font-size: 12px;
7934 font-family: Arial;
7935 border-style: double;
7936 border-width: 3px;
7937 border-color: #AA2244;
7938 display:none;
7939 }
7940
7941 div.pop_content > *:first-child {
7942 margin-top: 0px;
7943 }
7944
7945 div.pop_content h1, div.pop_content h2, div.pop_content h3, div.pop_content h4,
7946 div.pop_content h5, div.pop_content h6, div.pop_content p {
7947 margin: 0px;
7948 }
7949
7950 div.pop_content p + h1, div.pop_content p + h2, div.pop_content p + h3,
7951 div.pop_content p + h4, div.pop_content p + h5, div.pop_content p + h6 {
7952 margin-top: 5px;
7953 }
7954
7955 div.pop_content p + p {
7956 margin-top: 5px;
7957 }
7958
7959 div.pop_content > *:last-child {
7960 margin-bottom: 0px;
7961 }
7962
7963 div.pop_content div.pop_close {
7964 /* old definition */
7965 float:right;
7966 bottom: 0;
7967 }
7968
7969 div.pop_content span.pop_close, div.pop_content span.pop_back {
7970 display: inline-block;
7971 border: 2px outset #661429;
7972 background-color: #CCC;
7973 padding-left: 1px;
7974 padding-right: 1px;
7975 padding-top: 0px;
7976 padding-bottom: 0px;
7977 cursor: pointer;
7978 color: #AA2244; /*#661429;*/
7979 font-weight: bold;
7980 }
7981
7982 div.pop_content span.pop_close:active, div.pop_content span.pop_back:active {
7983 border-style: inset;
7984 }
7985
7986 div.pop_content span.pop_close {
7987 float:right;
7988 /*border: 2px outset #AA002B;*/
7989 /*color: #AA2244;*/
7990 }
7991
7992 div.pop_content:not(.nested) .nested_only {
7993 display: none;
7994 }
7995
7996 div.pop_back_sec {
7997 margin-bottom: 5px;
7998 }
7999
8000 div.pop_close_sec {
8001 margin-top: 5px;
8002 }
8003
8004 table.hide_advanced tr.advanced {
8005 display: none;
8006 }
8007 span.show_more {
8008 display: none;
8009 }
8010 table.hide_advanced span.show_more {
8011 display: inline;
8012 }
8013 table.hide_advanced span.show_less {
8014 display: none;
8015 }
8016
8017
8018 /*****************************************************************************
8019 * Program logo styling
8020 ****************************************************************************/
8021 div.prog_logo {
8022 border-bottom: 0.25em solid #0f5f60;
8023 height: 4.5em;
8024 width: 25em;
8025 display:inline-block;
8026 }
8027 div.prog_logo img {
8028 float:left;
8029 width: 4em;
8030 border-style: none;
8031 margin-right: 0.2em;
8032 }
8033 div.prog_logo h1, div.prog_logo h1:hover, div.prog_logo h1:active, div.prog_logo h1:visited {
8034 margin:0;
8035 padding:0;
8036 font-family: Arial, Helvetica, sans-serif;
8037 font-size: 3.2em;
8038 line-height: 1em;
8039 vertical-align: top;
8040 display: block;
8041 color: #026666;
8042 letter-spacing: -0.06em;
8043 text-shadow: 0.04em 0.06em 0.05em #666;
8044 }
8045 div.prog_logo h2, div.prog_logo h2:hover, div.prog_logo h2:active, div.prog_logo h2:visited {
8046 display: block;
8047 margin:0;
8048 padding:0;
8049 font-family: Helvetica, sans-serif;
8050 font-size: 0.9em;
8051 line-height: 1em;
8052 letter-spacing: -0.06em;
8053 color: black;
8054 }
8055 div.prog_logo h3, div.prog_logo h3:hover, div.prog_logo h3:active, div.prog_logo h3:visited {
8056 display: block;
8057 margin:0;
8058 padding:0;
8059 font-family: Helvetica, sans-serif;
8060 font-size: 0.9em;
8061 line-height: 1.5em;
8062 letter-spacing: -0.06em;
8063 color: black;
8064 }
8065
8066 div.big.prog_logo {
8067 font-size: 18px;
8068 }
8069
8070 /* These are for centered columns in tables */
8071 td.ctr {
8072 text-align: center;
8073 }
8074
8075 /* These are for the navigation bars at the top of outputs. */
8076 table.navigation {
8077 margin-top: 0px;
8078 border-collapse:collapse;
8079 }
8080 table.navigation * td
8081 {
8082 padding-left: 0px;
8083 padding-right: 10px;
8084 padding-top: 0px;
8085 padding-bottom: 0px;
8086 }
8087 </style>
8088 <style>
8089 .block_td {
8090 height:25px;
8091 }
8092 .block_container {
8093 position:relative;
8094 box-sizing: border-box;
8095 height: 25px;
8096 padding: 0px;
8097 margin: 0px;
8098 margin-left: 1em;
8099 }
8100 .block_label {
8101 position: absolute;
8102 display: inline-block;
8103 padding: 3px;
8104 z-index: 4;
8105 top: 6px;
8106 height: 12px;
8107 line-height: 12px;
8108 font-size: 12px;
8109 background-color: white;
8110 border: 1px solid black;
8111 -moz-border-radius: 12px;
8112 -webkit-border-radius: 12px;
8113 border-radius: 12px;
8114 transform: translateX(-50%);
8115 }
8116 .block_motif {
8117 position: absolute;
8118 z-index: 3;
8119 top: 0px;
8120 box-sizing: border-box;
8121 border: 1px solid black;
8122 height: 12px;
8123 background-color: cyan;
8124 }
8125 .block_motif.top {
8126 border-bottom-width: 0;
8127 }
8128 .block_motif.bottom {
8129 border-top-width: 0;
8130 }
8131 .block_motif.scanned_site {
8132 opacity: 0.3;
8133 }
8134 .block_motif.scanned_site.active {
8135 opacity: 0.9;
8136 }
8137 .block_region {
8138 position:absolute;
8139 z-index:6;
8140 height:25px;
8141 top:0px;
8142 }
8143 .block_region.main {
8144 z-index:8;
8145 }
8146 .block_region.scanned_site {
8147 z-index:5;
8148 }
8149 .block_region.scanned_site.main {
8150 z-index:7;
8151 }
8152 .block_region.top {
8153 height:13px;
8154 }
8155 .block_region.bottom {
8156 height:13px;
8157 top:12px;
8158 }
8159 .block_rule {
8160 position:absolute;
8161 z-index:2;
8162 width:100%;
8163 height:1px;
8164 top:12px;
8165 left:0px;
8166 background-color:gray;
8167 }
8168 .block_plus_sym {
8169 position:absolute;
8170 z-index:4;
8171 line-height:12px;
8172 top:0px;
8173 left:-1em;
8174 }
8175 .block_minus_sym {
8176 position:absolute;
8177 z-index:4;
8178 line-height:12px;
8179 top:13px;
8180 left:-1em;
8181 }
8182
8183 .tic_major {
8184 position:absolute;
8185 top:0em;
8186 height:0.5em;
8187 width: 2px;
8188 margin-left: -1px;
8189 background-color: blue;
8190 }
8191 .tic_minor {
8192 position:absolute;
8193 top:0em;
8194 height:0.2em;
8195 width: 1px;
8196 margin-left: -0.5px;
8197 background-color: blue;
8198 }
8199 .tic_label {
8200 position:absolute;
8201 display: inline-block;
8202 top:0.5em;
8203 height: 1em;
8204 color: blue;
8205 transform: translateX(-50%);
8206 }
8207
8208 .block_needle {
8209 position:absolute;
8210 z-index:4;
8211 height:30px;
8212 width:1px;
8213 top:-2px;
8214 background-color:gray;
8215 }
8216 .block_needle.right {
8217 height: 60px;
8218 }
8219 .block_handle {
8220 position: absolute;
8221 display: inline-block;
8222 z-index: 5;
8223 top: 27px;
8224 min-width: 3ex;
8225 text-align: center;
8226 font-size: 12px;
8227 line-height: 12px;
8228 transform: translateX(-50%);
8229 background-color: LightGrey;
8230 border:3px outset grey;
8231 cursor: pointer;
8232 -webkit-user-select: none; /* Chrome/Safari */
8233 -moz-user-select: none; /* Firefox */
8234 -ms-user-select: none; /* IE10+ */
8235 /* Rules below not implemented in browsers yet */
8236 -o-user-select: none;
8237 user-select: none;
8238 }
8239 .block_handle.right {
8240 top: 47px;
8241 }
8242
8243 .legend_container {
8244 text-align: right;
8245 }
8246 .legend_entry {
8247 display: inline-block;
8248 padding: 5px;
8249 }
8250 div.legend_swatch {
8251 box-sizing: border-box;
8252 width: 15px;
8253 height: 15px;
8254 border: 1px solid black;
8255 background-color: cyan;
8256 float: left;
8257 }
8258 div.legend_swatch input {
8259 display: none;
8260 }
8261 .legend_text {
8262 line-height: 15px;
8263 margin-left: 20px;
8264 }
8265 </style>
8266 <style>
8267 /* meme output specific css */
8268
8269 div.pop_block {
8270 position:absolute;
8271 z-index:5;
8272 padding: 5px;
8273 border: 1px solid black;
8274 display: inline-block;
8275 background-color: white;
8276 }
8277
8278 #measure_match {
8279 position: absolute;
8280 visibility: hidden;
8281 height: auto;
8282 width: auto;
8283 white-space: nowrap;
8284 }
8285
8286 div.template {
8287 position: absolute;
8288 z-index: 1;
8289 left: 0;
8290 top: 0;
8291 visibility: hidden;
8292 }
8293
8294 table.block_information {
8295 margin-left: auto;
8296 margin-right: auto;
8297 }
8298
8299 table.block_information * th {
8300 text-align: right;
8301 }
8302
8303 *.hide_empty_seqs * tr.empty_seq {
8304 display: none;
8305 }
8306
8307 *.hide_only_scan * tr.only_scan {
8308 display: none;
8309 }
8310
8311 *.hide_only_scan * div.scanned_site {
8312 display: none;
8313 }
8314
8315 td.symaction {
8316 text-align: center;
8317 text-decoration: underline;
8318 font-size: 20px;
8319 cursor: pointer;
8320 }
8321 div.sym_btn {
8322 display:inline-block;
8323 text-decoration: underline;
8324 cursor: pointer;
8325 font-size: 20px;
8326 line-height:20px;
8327 text-align: center;
8328 width: 20px;
8329 height: 20px;
8330 color: blue;
8331 }
8332 div.sym_btn:hover {
8333 color: white;
8334 background-color: blue;
8335 }
8336
8337 div.sym_btn.positioned {
8338 position: absolute;
8339 top: 0px;
8340 }
8341
8342 div.actionbutton {
8343 display:inline-block;
8344 cursor: pointer;
8345 font-size: 18px;
8346 line-height:20px;
8347 padding: 5px;
8348 margin: 10px 0;
8349 border: 1px solid black;
8350 }
8351
8352 div.actionbutton:hover {
8353 color:#FFF;
8354 background-color:#000;
8355 }
8356
8357 div.param_box {
8358 display: inline-block;
8359 margin-right: 20px;
8360 }
8361
8362 span.param {
8363 font-weight: bold;
8364 }
8365
8366 div.box + div.box {
8367 margin-top: 5px;
8368 }
8369
8370 div.sites_outer {
8371 position: relative;
8372 padding-top: 20px; /* height of header */
8373 display: inline-block;
8374 }
8375
8376 div.sites_inner {
8377 overflow-x: hidden;
8378 overflow-y: auto;
8379 max-height: 200px;
8380 }
8381 table.sites_tbl {
8382 border-collapse: collapse;
8383 }
8384
8385 div.sites_th_inner {
8386 position: absolute;
8387 top: 0;
8388 line-height: 20px; /* height of header */
8389 text-align: left;
8390 padding-left: 5px;
8391 }
8392 th.nopad div.sites_th_inner {
8393 padding-left: 0;
8394 }
8395 div.sites_th_hidden {
8396 visibility: hidden;
8397 height: 0;
8398 padding: 0 10px;
8399 }
8400 th.nopad div.sites_th_hidden {
8401 padding: 0;
8402 }
8403 div.sites_inner * th {
8404 height: 0;
8405 }
8406
8407 table.sites_tbl {
8408 overflow-x: hidden;
8409 overflow-y: auto;
8410 }
8411
8412 .site_num {
8413 text-align: right;
8414 }
8415 .site_name {
8416 padding:0px 5px;
8417 text-align:left;
8418 }
8419 .site_strand {
8420 padding:0px 5px;
8421 text-align:center;
8422 }
8423 .norc .site_strand, .norc .site_strand_title {
8424 display: none;
8425 }
8426 .site_start {
8427 padding:0px 15px;
8428 text-align: right;
8429 }
8430 .site_pvalue {
8431 text-align:center;
8432 padding:0px 15px;
8433 text-align:right;
8434 white-space: nowrap;
8435 }
8436 .lflank, .rflank, .match, .alpha_symbol {
8437 font-weight:bold;
8438 font-size:15px;
8439 font-family: 'Courier New', Courier, monospace;
8440 color:gray;
8441 }
8442
8443 .site.lflank {
8444 text-align:right;
8445 padding-right:5px;
8446 color:gray;
8447 }
8448 .site.match {
8449 text-align:center;
8450 }
8451 .site.rflank {
8452 text-align:left;
8453 padding-left:5px;
8454 padding-right: 20px;
8455 }
8456
8457 th.stop_reason {
8458 text-align: left;
8459 padding-right: 10px;
8460 }
8461
8462 th.motif_ordinal {
8463
8464 }
8465 td.motif_ordinal {
8466 text-align: right;
8467 padding-right: 10px;
8468 }
8469 th.motif_logo {
8470 padding-right: 10px;
8471 }
8472 td.motif_logo {
8473 padding-right: 10px;
8474 }
8475 th.motif_evalue {
8476 text-align:right;
8477 padding-right: 10px;
8478 }
8479 td.motif_evalue {
8480 text-align: right;
8481 white-space: nowrap;
8482 padding-right: 20px;
8483 }
8484 th.motif_nsites {
8485 text-align: right;
8486 padding-right: 10px;
8487 }
8488 td.motif_nsites {
8489 text-align: right;
8490 padding-right: 20px;
8491 }
8492 th.motif_width {
8493 text-align: right;
8494 padding-right: 5px;
8495 }
8496 td.motif_width {
8497 text-align: right;
8498 padding-right: 15px;
8499 }
8500 th.motif_more {
8501 padding: 0 5px;
8502 }
8503 td.motif_more {
8504 text-align: center;
8505 padding: 0 5px;
8506 }
8507 th.motif_submit {
8508 padding: 0 5px;
8509 }
8510 td.motif_submit {
8511 text-align: center;
8512 padding: 0 5px;
8513 }
8514 th.motif_download {
8515 padding-left: 5px;
8516 }
8517 td.motif_download {
8518 text-align: center;
8519 padding-left: 5px;
8520 }
8521
8522
8523 div.tabArea {
8524 font-size: 80%;
8525 font-weight: bold;
8526 }
8527
8528 .norc div.tabArea {
8529 display: none;
8530 }
8531
8532 span.tab, span.tab:visited {
8533 cursor: pointer;
8534 color: #888;
8535 background-color: #ddd;
8536 border: 2px solid #ccc;
8537 padding: 2px 1em;
8538 text-decoration: none;
8539 }
8540 span.tab.middle {
8541 border-left-width: 0px;
8542 }
8543 div.tabArea.base span.tab {
8544 border-top-width: 0px;
8545 }
8546 div.tabArea.top span.tab {
8547 border-bottom-width: 0px;
8548 }
8549
8550 span.tab:hover {
8551 background-color: #bbb;
8552 border-color: #bbb;
8553 color: #666;
8554 }
8555 span.tab.activeTab, span.tab.activeTab:hover, span.tab.activeTab:visited {
8556 background-color: white;
8557 color: black;
8558 cursor: default;
8559 }
8560 div.tabMain {
8561 border: 2px solid #ccc;
8562 background-color: white;
8563 padding: 10px;
8564 }
8565 div.tabMain.base {
8566 margin-top: 5px;
8567 display: inline-block;
8568 max-width: 98%;
8569 }
8570
8571 div.tabMain.top {
8572 margin-bottom: 5px;
8573 }
8574
8575 div.tabCenter {
8576 max-width: 100%;
8577 overflow-x: auto;
8578 height: 200px;
8579 overflow-y: hidden;
8580 }
8581
8582 canvas.logo_rc {
8583 display:none;
8584 }
8585 .show_rc_logo > canvas {
8586 display: none;
8587 }
8588 .show_rc_logo > canvas.logo_rc {
8589 display: block;
8590 }
8591
8592 canvas.scan_logo {
8593 margin-left: 10px;
8594 }
8595
8596 div.blocks_outer {
8597 position: relative;
8598 padding-top: 20px; /* height of header */
8599 }
8600
8601 div.blocks_inner {
8602 overflow-x: hidden;
8603 overflow-y: auto;
8604 max-height: 200px;
8605 }
8606 table.blocks_tbl {
8607 border-collapse: collapse;
8608 width: 100%;
8609 }
8610
8611 table.blocks_tbl .blockdiag_name {
8612 white-space: nowrap
8613 }
8614
8615 div.blocks_th_inner {
8616 position: absolute;
8617 top: 0;
8618 line-height: 20px; /* height of header */
8619 text-align: left;
8620 padding-left: 5px;
8621 }
8622 th.nopad div.blocks_th_inner {
8623 padding-left: 0;
8624 }
8625 div.blocks_th_hidden {
8626 visibility: hidden;
8627 height: 0;
8628 padding: 0 10px;
8629 }
8630 th.nopad div.blocks_th_hidden {
8631 padding: 0;
8632 }
8633 div.blocks_inner * th {
8634 height: 0;
8635 }
8636
8637 table.blocks_tbl {
8638 overflow-x: hidden;
8639 overflow-y: auto;
8640 }
8641 td.block_td {
8642 width: 99%;
8643 }
8644
8645 *.blockdiag_num {
8646 text-align: right;
8647 }
8648
8649 td.blockdiag_name {
8650 text-align: left;
8651 padding:0px 10px;
8652 }
8653
8654 td.blockdiag_pvalue {
8655 padding:0px 10px;
8656 text-align:right;
8657 white-space: nowrap;
8658 }
8659
8660 div.preview_btn {
8661 border: 2px solid white;
8662 height: 16px;
8663 width: 16px;
8664 font-size: 12px;
8665 line-height: 16px;
8666 text-align: center;
8667 cursor: pointer;
8668 }
8669 div.preview_btn + div.preview_btn {
8670 margin-top: 3px;
8671 }
8672
8673 div.preview_btn.active {
8674 border: 2px solid black;
8675 cursor: default;
8676 }
8677
8678 div.preview_btn:hover {
8679 background-color: black;
8680 color: white;
8681 border-color: black;
8682 }
8683
8684 div.preview_btn.active:hover {
8685 background-color: white;
8686 color: black;
8687 border-color: black;
8688 }
8689
8690
8691 div.preview_btn_box {
8692 position: absolute;
8693 left: 0px;
8694 top: 0px;
8695 padding: 3px;
8696 }
8697
8698 div.preview_logo_box {
8699 height: 50px;
8700 overflow-y: hidden;
8701 }
8702
8703 div.preview_btn_box + div.preview_logo_box {
8704 margin-left: 25px;
8705 }
8706
8707 div.preview_box {
8708 position: relative;
8709 }
8710
8711 div.grey_background {
8712 position:fixed;
8713 z-index: 8;
8714 background-color: #000;
8715 -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
8716 opacity: 0.5;
8717 left: 0;
8718 top: 0;
8719 width: 100%;
8720 height: 100%;
8721 }
8722
8723 div.popup_wrapper {
8724 position:fixed;
8725 z-index:9;
8726 width:100%;
8727 height:0;
8728 top:50%;
8729 left:0;
8730 }
8731
8732 div.popup {
8733 width: 600px;
8734 z-index:9;
8735 margin-left: auto;
8736 margin-right: auto;
8737 padding: 5px;
8738 background-color: #FFF;
8739 border-style: double;
8740 border-width: 5px;
8741 border-color: #00666a;
8742 position:relative;
8743 }
8744 div.close {
8745 cursor: pointer;
8746 border: 1px solid black;
8747 width:15px;
8748 height:15px;
8749 line-height:15px; /* this causes vertical centering */
8750 text-align:center;
8751 background-color:#FFF;
8752 color:#000;
8753 font-size:15px;
8754 font-family:monospace;
8755 }
8756
8757 div.close:hover {
8758 color:#FFF;
8759 background-color:#000;
8760 }
8761
8762 div.navnum {
8763 width:100%;
8764 height:20px;
8765 line-height:20px;
8766 text-align:center;
8767 font-size:medium;
8768 }
8769
8770 div.navarrow {
8771 font-size: 30px;
8772 text-decoration:none;
8773 cursor: pointer;
8774 -moz-user-select: none;
8775 -webkit-user-select: none;
8776 -ms-user-select: none;
8777 }
8778
8779 div.navarrow > span.inactive {
8780 display: inline;
8781 }
8782 div.navarrow > span.active {
8783 display: none;
8784 }
8785
8786 div.navarrow:hover > span.active {
8787 display: inline;
8788 }
8789 div.navarrow:hover > span.inactive {
8790 display: none;
8791 }
8792
8793 table.programs {
8794 width: 100%;
8795 }
8796
8797 table.programs tr {
8798 background-color: #EFE;
8799 }
8800
8801 table.programs tr.selected {
8802 background-color: #262;
8803 color: #FFF;
8804 }
8805
8806 table.programs tr.dna_only {
8807 display: none;
8808 }
8809
8810 table.programs.alphabet_dna tr.dna_only {
8811 display: table-row;
8812 }
8813
8814 div.programs_scroll {
8815 width: 100%;
8816 height: 90px;
8817 overflow-y: auto;
8818 overflow-x: hidden;
8819 margin: 0 auto;
8820 }
8821 table.inputs, table.alpha_bg_table {
8822 margin-top: 20px;
8823 border-collapse:collapse;
8824 }
8825 table.inputs * td, table.inputs * th, table.alpha_bg_table * td, table.alpha_bg_table * th {
8826 padding-left: 15px;
8827 padding-right: 15px;
8828 padding-top: 1px;
8829 padding-bottom: 1px;
8830 }
8831
8832 table.hide_psp td.col_psp, table.hide_psp th.col_psp {
8833 display: none;
8834 }
8835
8836 table.hide_control td.col_control, table.hide_control th.col_control {
8837 display: none;
8838 }
8839
8840 /* program settings */
8841 span.mod_oops, span.mod_zoops, span.mod_anr {
8842 display: none;
8843 }
8844 td.oops span.mod_oops,td.zoops span.mod_zoops, td.anr span.mod_anr {
8845 display: inline;
8846 }
8847 span.strand_none, span.strand_given, span.strand_both {
8848 display: none;
8849 }
8850 td.none span.strand_none, td.given span.strand_given, td.both span.strand_both {
8851 display: inline;
8852 }
8853 span.spmap_uni, span.spmap_pam {
8854 display: none;
8855 }
8856 td.uni span.spmap_uni, td.pam span.spmap_pam {
8857 display: inline;
8858 }
8859 span.prior_dirichlet, span.prior_dmix, span.prior_mega, span.prior_megap, span.prior_addone {
8860 display: none;
8861 }
8862 td.dirichlet span.prior_dirichlet, td.dmix span.prior_dmix, td.mega span.prior_mega,
8863 td.megap span.prior_megap, td.addone span.prior_addone {
8864 display: inline;
8865 }
8866 span.noendgaps_on, span.noendgaps_off {
8867 display: none;
8868 }
8869 td.on span.noendgaps_on, td.off span.noendgaps_off {
8870 display: inline;
8871 }
8872 span.substring_on, span.substring_off {
8873 display: none;
8874 }
8875 td.on span.substring_on, td.off span.substring_off {
8876 display: inline;
8877 }
8878 </style>
8879 </head>
8880 <body onload="page_loaded()" onpageshow="page_shown(event)" onresize="page_resized()">
8881 <!-- -->
8882 <div id="grey_out_page" class="grey_background" style="display:none;">
8883 </div>
8884 <!-- Help popups -->
8885 <div class="pop_content" id="pop_results_txt">
8886 <p>MEME results in plain text format.</p>
8887 <div style="float:right; bottom:0px;">[
8888 <a href="javascript:help_popup()">close</a> ]</div>
8889 </div>
8890 <div class="pop_content" id="pop_results_xml">
8891 <p>MEME results in XML format.</p>
8892 <div style="float:right; bottom:0px;">[
8893 <a href="javascript:help_popup()">close</a> ]</div>
8894 </div>
8895 <div class="pop_content" id="pop_">
8896 <p>Help poup.</p>
8897 <div style="float:right; bottom:0px;">[
8898 <a href="javascript:help_popup()">close</a> ]</div>
8899 </div>
8900 <div class="pop_content" id="pop_logo">
8901 <script>print_doc_para("pop_logo", "discovery", "motif_logo", site_url);</script>
8902 Click on the "+" or "-" buttons to the left of the motif to see
8903 the forward or reverse complement of the motif if available.
8904 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div>
8905 </div>
8906 <div class="pop_content" id="pop_ev">
8907 <p>The statistical significance of the motif. MEME usually finds the most
8908 statistically significant (low E-value) motifs first. It is unusual to
8909 consider a motif with an E-value larger than 0.05 significant so, as an
8910 additional indicator, MEME displays these greyed out.</p>
8911 <p>The E-value of a motif is based on its log likelihood ratio, width,
8912 sites, the background letter frequencies (given in the command line
8913 summary), and the size of the training set.</p>
8914 <p>The E-value is an estimate of the expected number of motifs with the
8915 given log likelihood ratio (or higher), and with the same width and site
8916 count, that one would find in a similarly sized set of random
8917 sequences (sequences where each position is independent and letters are
8918 chosen according to the background letter frequencies).</p>
8919 <div style="float:right; bottom:0px;">[
8920 <a href="javascript:help_popup()">close</a> ]</div>
8921 </div>
8922 <div class="pop_content" id="pop_sites">
8923 <p>The number of sites contributing to the construction of the motif.</p>
8924 <div style="float:right; bottom:0px;">[
8925 <a href="javascript:help_popup()">close</a> ]</div>
8926 </div>
8927 <div class="pop_content" id="pop_width">
8928 <p>The width of the motif. Each motif describes a pattern of a fixed
8929 width, as no gaps are allowed in MEME motifs.</p>
8930 <div style="float:right; bottom:0px;">[
8931 <a href="javascript:help_popup()">close</a> ]</div>
8932 </div>
8933 <div class="pop_content" id="pop_more">
8934 <script>print_doc_para("pop_more", "discovery", "more");</script>
8935 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div>
8936 </div>
8937 <div class="pop_content" id="pop_submit_dl">
8938 <script>print_doc_para("pop_submit_dl", "discovery", "submit_dl", "https://meme-suite.org/meme");</script>
8939 <div style="float:right; bottom:0px;">[
8940 <a href="javascript:help_popup()">close</a> ]</div>
8941 </div>
8942 <div class="pop_content" id="pop_llr">
8943 <p>The log likelihood ratio of the motif. The log likelihood ratio is the
8944 logarithm of the ratio of the probability of the occurrences of the motif
8945 given the motif model (likelihood given the motif) versus their
8946 probability given the background model (likelihood given the null model).
8947 (Normally the background model is a 0-order Markov model using the
8948 background letter frequencies, but higher order Markov models may be
8949 specified via the -bfile option to MEME.).</p>
8950 <div style="float:right; bottom:0px;">[
8951 <a href="javascript:help_popup()">close</a> ]</div>
8952 </div>
8953 <div class="pop_content" id="pop_ic">
8954 <p>The information content of the motif in bits. It is equal to the sum
8955 of the uncorrected information content, R(), in the columns of the motif.
8956 This is equal relative entropy of the motif relative to a uniform
8957 background frequency model.</p>
8958 <div style="float:right; bottom:0px;">[
8959 <a href="javascript:help_popup()">close</a> ]</div>
8960 </div>
8961 <div class="pop_content" id="pop_re">
8962 <p>The relative entropy of the motif.</p>
8963
8964 <p style="font-family: monospace;">re = llr / (sites * ln(2))</p>
8965 <div style="float:right; bottom:0px;">[
8966 <a href="javascript:help_popup()">close</a> ]</div>
8967 </div>
8968 <div class="pop_content" id="pop_bt">
8969 <p>The Bayes Threshold.</p>
8970 <div style="float:right; bottom:0px;">[
8971 <a href="javascript:help_popup()">close</a> ]</div>
8972 </div>
8973 <div class="pop_content" id="pop_site_strand">
8974 <p>The strand used for the motif site.</p>
8975 <dl>
8976 <dt>+</dt>
8977 <dd>The motif site was found in the sequence as it was supplied.</dd>
8978 <dt>-</dt>
8979 <dd>The motif site was found in the reverse complement of the supplied sequence.</dd>
8980 </dl>
8981 <div style="float:right; bottom:0px;">[
8982 <a href="javascript:help_popup()">close</a> ]</div>
8983 </div>
8984 <div class="pop_content" id="pop_site_start">
8985 <p>The position in the sequence where the motif site starts. If a motif
8986 started right at the beginning of a sequence it would be described as
8987 starting at position 1.</p>
8988 <div style="float:right; bottom:0px;">[
8989 <a href="javascript:help_popup()">close</a> ]</div>
8990 </div>
8991 <div class="pop_content" id="pop_site_pvalue">
8992 <p>The probability that an equal or better site would be found in a
8993 random sequence of the same length conforming to the background letter
8994 frequencies.</p>
8995 <div style="float:right; bottom:0px;">[
8996 <a href="javascript:help_popup()">close</a> ]</div>
8997 </div>
8998 <div class="pop_content" id="pop_site_match">
8999 <p>A motif site with the 10 flanking letters on either side.</p>
9000 <p>When the site is not on the given strand then the site
9001 and both flanks are reverse complemented so they align.</p>
9002 <div style="float:right; bottom:0px;">[
9003 <a href="javascript:help_popup()">close</a> ]</div>
9004 </div>
9005
9006 <div class="pop_content" id="pop_seq_name">
9007 <p>The name of the sequences as given in the FASTA file.</p>
9008 <p>The number to the left of the sequence name is the position
9009 of the sequence in the input sequence file.</p>
9010 <div style="float:right; bottom:0px;">[
9011 <a href="javascript:help_popup()">close</a> ]</div>
9012 </div>
9013
9014 <div class="pop_content" id="pop_motif_sites">
9015 <p>These are the motif sites predicted by MEME and used to build the motif.</p>
9016 <p>These sites are shown in solid color and hovering the cursor
9017 over a site will reveal details about the site. Only sequences
9018 that contain a motif site are shown.</p>
9019 <div style="float:right; bottom:0px;">[
9020 <a href="javascript:help_popup()">close</a> ]</div>
9021 </div>
9022
9023 <div class="pop_content" id="pop_scanned_sites">
9024 <p>These are the motif sites predicted by MEME plus
9025 any additional sites detected using a motif scanning
9026 algorithm.</p>
9027 <p>These MEME sites are shown in solid color and
9028 additional scanned sites are shown greyed out.
9029 Hovering the cursor over a site will reveal details about the site.
9030 Only sequences containing a predicted or scanned motif site are shown.</p>
9031 <p>The scanned sites are predicted using a
9032 log-odds scoring matrix constructed from the MEME sites.
9033 Only scanned sites with position <i>p</i>-values less
9034 than 0.0001 are shown.</p>
9035 <div style="float:right; bottom:0px;">[
9036 <a href="javascript:help_popup()">close</a> ]</div>
9037 </div>
9038
9039 <div class="pop_content" id="pop_all_sequences">
9040 <p>These are the same sites as shown by selecting the
9041 "Motif Sites + Scanned Sites" button except that all
9042 sequences, including those with no sites, are included
9043 in the diagram.</p>
9044 <div style="float:right; bottom:0px;">[
9045 <a href="javascript:help_popup()">close</a> ]</div>
9046 </div>
9047
9048 <div class="pop_content" id="pop_seq_pvalue">
9049 <p>This is the combined match <i>p</i>-value.</p>
9050 <p>The combined match <i>p</i>-value is defined as the probability that a
9051 random sequence (with the same length and conforming to the background)
9052 would have position <i>p</i>-values such that the product is smaller
9053 or equal to the value calculated for the sequence under test.</p>
9054 <p>The position <i>p</i>-value is defined as the probability that a
9055 random sequence (with the same length and conforming to the background)
9056 would have a match to the motif under test with a score greater or equal
9057 to the largest found in the sequence under test.</p>
9058 <p>Hovering your mouse over a motif site in the motif location
9059 block diagram will show its position <i>p</i>-value and other information
9060 about the site.</p>
9061 <div style="float:right; bottom:0px;">[
9062 <a href="javascript:help_popup()">close</a> ]</div>
9063 </div>
9064
9065 <div class="pop_content" id="pop_download_pdf_motif_locations">
9066 <p>Use this button to download the "Motif Locations" block diagrams
9067 as a PDF image suitable for publication.
9068 </p>
9069 <p>
9070 Only the block diagrams currently visible in the inner scrolling
9071 window (below) will be included in the image, and the numbers to
9072 the left of each sequence name will not be included in the image.
9073 You can change the size of the inner scrolling by moving the bottom
9074 of the main document window up and down. You can display more
9075 diagrams by making your browser's font size smaller.
9076 </p>
9077 <div style="float:right; bottom:0px;">[
9078 <a href="javascript:help_popup()">close</a> ]</div>
9079 </div>
9080
9081 <div class="pop_content" id="pop_download_svg_motif_locations">
9082 <p>Use this button to download the "Motif Locations" block diagrams
9083 as a SVG image use in HTML documents.
9084 </p>
9085 <p>
9086 Only the block diagrams currently visible in the inner scrolling
9087 window (below) will be included in the image, and the numbers to
9088 the left of each sequence name will not be included in the image.
9089 You can change the size of the inner scrolling by moving the bottom
9090 of the main document window up and down. You can display more
9091 diagrams by making your browser's font size smaller.
9092 </p>
9093 <div style="float:right; bottom:0px;">[
9094 <a href="javascript:help_popup()">close</a> ]</div>
9095 </div>
9096
9097 <div class="pop_content" id="pop_offline">
9098 <p>
9099 This button will only function if your browser was
9100 connected to the internet when you loaded this page.
9101 </p>
9102 <p>
9103 To use this button, make sure your browser is connected to the internet
9104 and then reload this page. (You may need to do a "hard refresh" to clear the cache.
9105 On Mac, hold down the Shift key and click the Reload button.
9106 On Windows/Linux, hold down Ctrl and press F5.)
9107 </p>
9108 <div style="float:right; bottom:0px;">[
9109 <a href="javascript:help_popup()">close</a> ]</div>
9110 </div>
9111
9112 <div class="pop_content" id="pop_motif_location">
9113 <p>This diagram shows the location of motif sites.</p>
9114 <p>Each block shows the position and strength of a motif
9115 site. The height of a block gives an indication of the
9116 significance of the site as taller blocks are more significant.
9117 The height is calculated to be proportional to the negative
9118 logarithm of the <i>p</i>-value of the site, truncated at
9119 the height for a <i>p</i>-value of 1e-10.</p>
9120 <p>For complementable alphabets (like DNA), sites on the
9121 positive strand are shown above the line,
9122 sites on the negative strand are shown below.</p>
9123 <p>Placing the cursor
9124 over a motif site will reveal more information about the site
9125 including its position <i>p</i>-value. (See the help
9126 for the <i>p</i>-value column for an explanation of position
9127 <i>p</i>-values.)</p>
9128 <div style="float:right; bottom:0px;">[
9129 <a href="javascript:help_popup()">close</a> ]</div>
9130 </div>
9131
9132 <div class="pop_content" id="pop_seq_source">
9133 <p>The name of the file(s) of sequences input to MEME.</p>
9134 <div style="float:right; bottom:0px;">[
9135 <a href="javascript:help_popup()">close</a> ]</div>
9136 </div>
9137 <div class="pop_content" id="pop_psp_source">
9138 <p>The position specific priors file used by MEME to find the motifs.</p>
9139 <div style="float:right; bottom:0px;">[
9140 <a href="javascript:help_popup()">close</a> ]</div>
9141 </div>
9142 <div class="pop_content" id="pop_seq_alph">
9143 <p>The alphabet used by the sequences.</p>
9144 <div style="float:right; bottom:0px;">[
9145 <a href="javascript:help_popup()">close</a> ]</div>
9146 </div>
9147 <div class="pop_content" id="pop_seq_count">
9148 <p>The number of FASTA sequences provided in this input file.</p>
9149 <div style="float:right; bottom:0px;">[
9150 <a href="javascript:help_popup()">close</a> ]</div>
9151 </div>
9152 <div class="pop_content" id="pop_num_positions">
9153 <p>The number of characters in the sequences provided in this FASTA input file.</p>
9154 <div style="float:right; bottom:0px;">[
9155 <a href="javascript:help_popup()">close</a> ]</div>
9156 </div>
9157
9158 <div class="pop_content" id="pop_alph_name">
9159 <p>The name of the alphabet symbol.</p>
9160 <div style="float:right; bottom:0px;">[
9161 <a href="javascript:help_popup()">close</a> ]</div>
9162 </div>
9163 <div class="pop_content" id="pop_alph_freq">
9164 <p>The frequency of the alphabet symbol in the dataset.</p>
9165 <div style="float:right; bottom:0px;">[
9166 <a href="javascript:help_popup()">close</a> ]</div>
9167 </div>
9168 <div class="pop_content" id="pop_alph_bg">
9169 <p>The frequency of the alphabet symbol as defined by the background model.</p>
9170 <div style="float:right; bottom:0px;">[
9171 <a href="javascript:help_popup()">close</a> ]</div>
9172 </div>
9173
9174 <!-- templates -->
9175 <div id="measure_match" class="match"></div>
9176 <div class="template pop_block" id="tmpl_block_info">
9177 <div>
9178 <span class="tvar_logo_pad lflank" style="visibility:hidden;"></span>
9179 <span class="tvar_logo"></span>
9180 </div>
9181 <div class="block_sequence_fragment">
9182 <span class="tvar_lflank lflank"></span>
9183 <span class="tvar_match match"></span>
9184 <span class="tvar_rflank rflank"></span>
9185 </div>
9186 <table class="block_information">
9187 <tr><th>Motif</th><td class="tvar_motif">1</td></tr>
9188 <tr><th><i>p</i>-value</th><td class="tvar_pvalue">8.23e-7</td></tr>
9189 <tr><th>Start</th><td class="tvar_start">23</td></tr>
9190 <tr><th>End</th><td class="tvar_end">33</td></tr>
9191 </table>
9192 </div>
9193
9194 <div class="template pop_block" id="tmpl_scan_info">
9195 <h5>Scanned Site</h5>
9196 <div class="tvar_logo"></div>
9197 <table class="block_information">
9198 <tr><th>Motif</th><td class="tvar_motif">1</td></tr>
9199 <tr><th><i>p</i>-value</th><td class="tvar_pvalue">8.23e-7</td></tr>
9200 <tr><th>Start</th><td class="tvar_start">23</td></tr>
9201 <tr><th>End</th><td class="tvar_end">33</td></tr>
9202 </table>
9203 </div>
9204
9205 <div class="template box expanded_motif" id="tmpl_motif_expanded">
9206 <div style="position: relative; min-height: 20px">
9207 <div class="param_box">
9208 <span class="param"><span class="tvar_ordinal"></span>.</span>
9209 </div>
9210 <div class="sym_btn positioned tvar_less" tabindex="0"
9211 title="Show less information.">&#8613;</div>
9212 <div class="sym_btn positioned tvar_submit" tabindex="0"
9213 title="Submit the motif to another MEME Suite program or download it.">&#8674;</div>
9214 </div>
9215 <div>
9216 <div class="param_box">
9217 <span class="param"><i>E</i>-value:</span>
9218 <span class="tvar_evalue"></span>
9219 <div class="help" data-topic="pop_ev"></div>
9220 </div>
9221 <div class="param_box">
9222 <span class="param">Site Count:</span>
9223 <span class="tvar_site_count"></span>
9224 <div class="help" data-topic="pop_sites"></div>
9225 </div>
9226 <div class="param_box">
9227 <span class="param">Width:</span>
9228 <span class="tvar_width"></span>
9229 <div class="help" data-topic="pop_width"></div>
9230 </div>
9231 </div>
9232 <div class="tabMain base">
9233 <div class="tabCenter tvar_logo"></div>
9234 </div>
9235 <div class="tabArea base">
9236 <span class="tvar_tab tab" tabindex="0">Standard</span><span
9237 class="tvar_tab_rc tab middle" tabindex="0">Reverse
9238 Complement</span>
9239 </div>
9240 <div style="padding: 10px 0">
9241 <div class="param_box">
9242 <span class="param">Log Likelihood Ratio:</span>
9243 <span class="tvar_llr"></span>
9244 <div class="help" data-topic="pop_llr"></div>
9245 </div>
9246 <div class="param_box">
9247 <span class="param">Information Content:</span>
9248 <span class="tvar_ic"></span>
9249 <div class="help" data-topic="pop_ic"></div>
9250 </div>
9251 <div class="param_box">
9252 <span class="param">Relative Entropy:</span>
9253 <span class="tvar_re"></span>
9254 <div class="help" data-topic="pop_re"></div>
9255 </div>
9256 <div class="param_box">
9257 <span class="param">Bayes Threshold:</span>
9258 <span class="tvar_bt"></span>
9259 <div class="help" data-topic="pop_bt"></div>
9260 </div>
9261 </div>
9262 <div class="tvar_sites"></div>
9263 </div>
9264
9265 <div id="tab_submit_or_download_motif"></div>
9266 <script> make_submit_or_download_motif_form("tab_submit_or_download_motif", site_url, "MEME"); </script>
9267
9268 <!-- Page starts here -->
9269 <div id="top" class="pad1">
9270 <div class="prog_logo big">
9271 <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAD4AAABGCAYAAACUsCfoAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9wLHAInL7an9JAAAA8eSURBVHja7ZpZbxv3ucZ/s3AZ7qQkypZELZS3WHKiRJbTFnHqOjZcGzCQBf0A7cUB8glOb89dz30vChRp7xK0aZo4TYq6dtKqSew6iuJWpmPJlrVFFCmKI+7LLJw5F81MFZyec5rTILJaP8AAnAty+Pzf933ebeAhHuIhHuIhHmJPo6TpaeGLfimTydhLS0sUCgWazSbVahXDMOh0Ong8HiKRCAMDAxw+fJhHH31UeFDJ/91/bHp62r558ya6rmOaJuvr6+TzeYrFIoZhAOD1epFlmWAwSDqdZnR0lCNHjjA+Ps7+/fuFPUU8k8nYV69epVKpEA6HuXPnDrOzs7RaLbxeL93d3fj9foLBIB6Ph0ajQaVSAUBRFNLpNI888ggHDhzg3Llzwp4g/vbbb9szMzPE43FyuRwffPAB2WyWnp4ehoaGiMVi2LZNvV6n3W7j8/mIRqN4vV4ajQa1Wo2trS0OHTqEaZo89dRTfO973xMeaOKvvfaaffv2bcLhMCsrK/z+979HVVWOHz/O0NAQxWIRVVVRVRW/348oihiGgSiKxGIxurq6CAQCBINB3n//fcbGxiiXyxw7dowXX3yx1Nvbm3igiBcKhY9u3rw5OTMzQzgc5vbt23zwwQfIsszExAS2bZPP55EkiVAoRCQSIRwOI8sy5XKZ7e1tWq0WnU6HTqfD4OAggUAAVVXpdDo0m01GRkb44Q9/KDxQxH/729/as7Oz+Hw+Pv30U37zm9/g9Xrp6emht7eXYDCI3+/H6/UiCALtdhvLsuh0OkiSRCAQQJZl6vU65XKZ1dVV+vv7SSQS5PN5vF4vhUKBgwcP8qMf/WjXyIs7b+7cuWMvLi4iCAL1ep133nkHXdcZHh4mlUoRDofp7+8nHo9jGAaCIBCPx+nr6yOdTtPX14fH40HTNGRZpqenh76+PrLZLMVikYGBARRFwTRN5ubm+PGPf2zvFnF5583c3BzlchlZlrl16xa1Wo2xsTGi0SiDg4OutUzTJBKJIIoiXq+XQCBAIpEgFAphmiaqqlIoFKjVaoyOjhKJRFhfXycajRKNRunp6WFhYYG33nqLQqHwUTKZPP5VE5ecD5cvX7bv37+Poijkcjl++ctfMjo6ytDQEMlkEo/HQ7lcpt1u4/F4kGUZQRDw+Xz09fVx4MABRkZGSKVSdHV10el0KJVK1Ot14vE4kiRRKpVQFIV4PI5t23z66ad0Op2+K1eu/MeuWVxVVUzTRNd17t27RzKZJJFIEI1GEUWRdruNaZqIoogkSYiiiCiK+P1+AoEA0WiUQ4cOCQCbm5vb5XI5Xi6XWVtbw7ZtZFnG7/e7D1YUhWAwSLVaJZvN2v39/cJXHuOLi4t2sVhEEAS2t7eZn59n//79JBIJwuEwlmXRbDbRdR1BEBAEAcuysCwL2/5LmIriX+Wit7c3EY/HSSQSeDweDMPA4/EQCoWwbRvbtgmFQoTDYUqlErlcbnfE7f79+64gbWxs0G63icViRCIRBEHANE0Mw0CSJLxer0vSUfVKpUKxWGRtbc12UmKr1cI0TQD3t4PBoHtwgUAAv99Ps9kkn8/vjrjlcjlkWabdbrO8vEx3dzfhcJhoNEq9XkfTNERRxOfz4fV6sW0bSZJcBS+VSqytrdFqtbh+/bq9sLBALpdje3sbXddd4jutLwh/8WzLslBVdXeI12o1RFFkc3PTLTFjsRixWAxVVbEsC1mWkSTJdVXnMgyDarWKrusUi0X8fj+WZVGtVimXyxiGgW3bblh4PB78fj/tdhvbthFF0a3tv3Liuq4jiiKFQgFZlolEInR3d6MoConEXytLp1Bx0Ol0sG0bXdfRdZ1arYYgCNi2jaZptFot1zOcQ3LSnxMGzsHvCnHH/RqNBtFoFEmSCIfDAAwNDWFZFu12m0ajgaZpWJbl/oDH4/ncoTii5xBz1N+2bTqdDrIs4/P53MxgWRaapu2exQOBgHsAnU6HdrvN5uYmgUCASCRCJBIhkUhgmibNZpNarUaj0XAt71jfET1RFN1YliTJfUa73SaZTLK1tUWtVkPTNKampnaHuFNGyrKM1+t141bTNGq1GrVazS1EIpEI8XjcLWJqtdrnYthNF58pv23beL1eJEly1VzXder1Oq1Wi2AwiK7ru0PcSSs73bBYLBIMBpEkCdM0qVQq6LpOq9UiHA673Vmn08E0TfdyRM85CNM08Xq9bkoMhULugRqGgd/v3z1Xl2X5c24aDodRVZV4PO7W406/raoqtVqNYDCIz+dDURQ3vp3LuXeEz4nlTqdDOBxG13U3TBRF2b0mJRAI0Gg0EAQBTdPw+/2Uy2VXoJzRkkPEifNWq0UgEHCLEsedLcty1X1nzIdCIRRFoVQqUSqVsCyLYDBIPB7fncotlUq5JaWu6xiGgWmabG1tuanINE0EQXAPwSlk6vU6zWaTdruNpmnud3eWsk6fnkwmMU2Tzc1NVFVFURSi0SjpdHp3iB89ehRRFOnt7XWHCNFolFwu58ar08AYhuEWNH6/H4/H8zlL71R4J5XJskwgECAWi5HP51lZWaHVajE8PEw8Hufw4cO7Q3zfvn0C4E5MNU2jq6uLarVKPp//nPA5hYgzcYlEIoRCIfcQHLd3SDvEPR4PHo+HfD5PPp9HEARGRkYIhUIMDQ0Ju0Ic4NixY3g8Hnp7e8lms8RiMcLhMNlsFk3TXHW3bZtgMOjmfScEvF6vW8s7qczxgGazSSAQ4M9//jOqqlKpVHj88cfx+XyMjY3t7ujp1KlTQrlcdicupVKJkZER1tbWuHfvHqqqEolEiMVitNtt6vW6q+iNRoNms4kkSfh8PtfC0WgURVHo7+9nc3OTjY0N3n33XQYGBkilUiSTSU6ePCnsKnGA559/nkqlwtGjR8nn8/j9fh5//HEsy+LWrVvMz8+j6zqhUAifz4dhGG7z0mq1UFUVXdeJRCIEg0F32loqlbh37x53796lq6sLRVGIRCJMTEzs2pT1c8Qfe+wxYWpqyv1j2WyWVCpFIpGgUqkwNzfHnTt33JTnDCUlSaK7u5v+/n66u7vxeDxun16r1SgUCly/fp3bt29z+vRpBgcHqdVqRKPRB2uh8LOf/cyemZlhYWGBYDBIJBIBoFgs0mw2CYfDxONxfD4fg4ODros7vXWhUKDT6bg5/v3332dlZYWhoSHS6TSnTp1i3759VKtVzpw5w1c9dvpfNymvvPKKff36dbLZLIqiuBVWs9mk0WhgWZbbn4fDYURRRNM0AoEAvb29+Hw+Go0Gly9fZmNjg4mJCcbGxti/f787mJyfn6darTI5OcmJEyeEB4J4sVj8wfT09L/Pzs6SzWbdpWEikXBJGoZBs9kkGo0iyzKmabrLhtXVVTKZDJubm5w9e5YjR44wMDDAd7/7XWF2dtZeXFzkyJEj7pZm//79pFIpenp6vhIP+D8f8NZbb9nr6+ssLy+ztbXl1t5er9fdhxeLRSRJoquri0qlQiaTYXt7m0gkwpkzZ5BlmbGxMV544QX3eR9//LE9Pz/PsWPHuHHjBouLi0iSxMDAAMePH2dqakrYVeIAKysr9u3bt1leXnZn65qmoeu6W9AYhkGj0aBYLGKaJocOHeLRRx8lkUgwOTnpjp4d5HI5++OPP6ZcLjM+Ps61a9dYWFig2Wzi9Xp5/vnnOX36tLCrxHfi1q1b9tLSkuv+9XrdbTEVRSEWi5FMJhkdHWV4eJiDBw/+j8/Y2tr6+WuvvfYdwzBIpVJIkoSqqszPz1Ov17lw4QJf+9rXziYSiau7TvxvoVQqpUul0n2n2/oibz+899579k9/+lPC4TCnTp3i61//Orlcjp/85CdomsbTTz/NiRMn/pvH/CMotrQfyF/GD8Xj8aX/7yEePnx4dnx8fPJ3v/sdiqIwODjI5OSkcOPGDfvNN9/kypUr5PN5MpmMPT4+/qWQ71Z835fZZSSTyeMrKyt2vV7n7t27vPrqq1y7ds1+8sknhT/+8Y/2pUuXuHnzJs1mk6WlJTudTn8p5EUeAAwPDwvPPfccBw4cYHV1lc+KJ3t0dPTVixcvcvDgQebn53n55ZeZmZn5h1fLalv/twfqTaRMJmP/4he/YH19nSNHjnDhwgVisRj37t3jnXfeYWFhgQMHDnD+/HmeeuopYc9b3MH4+LjwrW99C0EQuHXrFtPT09RqNfr7+zlx4gTpdJqNjQ3efPNN3njjDbtYLP5gV1X9y8bly5ft119/nUqlwsmTJzl9+jQej4ePPvqITz75hLW1NYLBIJOTk5w7d46+vr4vzEN+EImfO3dOuHr1qv3666/z3nvvoWkaZ8+e5YknnnBXU5qmcfPmTQzDYG1tzR4cHBT2vMUdTE9P25cuXaJQKDA5OcmFCxfQdZ3Z2VkWFhbY2NhAURRSqRTPPvssY2Njwp62uINvfvObwp/+9Cf7V7/6FTdu3KDT6XD+/HmmpqbcoaaqqszOzmIYxhfK9Q80cYCJiQlhbm7OFgSBubk5vF4vzzzzDBMTE+7CMRgMsrCwwEsvvcS1a9fsb3zjG8KedvWd+OSTT+y3336bubk5JicnOXnyJJZl8eGHH7K2tkahUKDRaLBv3z4uXrzI2bNnhT1tcQdHjx4VMpmM3el0WFxcJBQKMTU1xbFjx6hWqwC0221WVlb49a9/zY0bN+wnn3xS2BN5/O/J88899xzDw8NkMhnm5uaIx+M8/fTT9PT0YFkWg4ODrK6u8sorrzA/P2/veVffidXVVfvdd99lenqa8+fPMzo6iqZp/OEPf2BhYYFWq4Usy4yMjHDx4kX+luXlvUh8aGhImJmZsS3L4vr165imSSqVYt++fbTbbWq1GtVqlWq1yt27d7l//749Ojoq7HniAFNTU8LS0pJdLpe5e/cu6+vrRCIRhoeHqVarrK6uous66+vrZLNZSqVS+rP2eW8TB0in00I2m3XVHqCvr49YLEYikXBfRiqXy+Ryufs7Q1tkj6O/v1/49re/zTPPPIMgCMzPz7sraJ/Ph9/vd/d3pVIp/U9hcQdOnX7p0iU7k8kgiqK76lIUxX19dKerC/wTYXt7+8zm5uaV5eVlNjY2iMfj+P1+hoeH6enpmd35evg/FXEHm1uF7ZXltXgg6EcSPSR7u/+zO9H1ff6VsFWq/pyHeIiHeIh/FfwXjVDdIW9O2PAAAAAASUVORK5CYII=" alt="MEME Logo">
9272 <h1>MEME</h1>
9273 <h2>Multiple Em for Motif Elicitation</h2>
9274 </div>
9275 <p>
9276 For further information on how to interpret these results please access
9277 <a href="https://meme-suite.org/meme/doc/meme.html">https://meme-suite.org/meme/doc/meme.html</a>. <br />
9278 To get a copy of the MEME software please access
9279 <a href="https://meme-suite.org">https://meme-suite.org</a>.
9280 </p>
9281 <p id="citation"> <script>print_citation("citation", "MEME");</script></p>
9282 </div>
9283 <!-- navigation -->
9284 <div class="pad2">
9285 <a class="jump" href="#motifs_sec">Discovered Motifs</a>
9286 &nbsp;&nbsp;|&nbsp;&nbsp;
9287 <a class="jump" href="#sites_sec">Motif Locations</a>
9288 &nbsp;&nbsp;|&nbsp;&nbsp;
9289 <a class="jump" href="#inputs_sec">Inputs &amp; Settings</a>
9290 &nbsp;&nbsp;|&nbsp;&nbsp;
9291 <a class="jump" href="#info_sec">Program Information</a>
9292 &nbsp;&nbsp;|&nbsp;&nbsp;
9293 <a class="jump" href="meme.txt">Results in Text Format</a>&nbsp;<span id="results_txt_help"></span>
9294 &nbsp;&nbsp;|&nbsp;&nbsp;
9295 <a class="jump" href="meme.xml">Results in XML Format</a>&nbsp;<span id="results_xml_help"></span>
9296 <script>
9297 make_help_button($("results_txt_help"), "pop_results_txt");
9298 make_help_button($("results_xml_help"), "pop_results_xml");
9299 </script>
9300 </div>
9301 <!-- alert the user when their browser is not up to the task -->
9302 <noscript><h1 style="color:red">Javascript is required to view these results!</h1></noscript>
9303 <h1 id="html5_warning" style="color:red; display:none;">Your browser does not support canvas!</h1>
9304 <script>
9305 if (!window.HTMLCanvasElement) $("html5_warning").style.display = "block";
9306 </script>
9307 <h2 class="mainh pad2" id="motifs_sec">Discovered Motifs</h2>
9308 <div id="motifs" class="box">
9309 <p>Please wait... Loading...</p>
9310 <p>If the page has fully loaded and this message does not disappear then an error may have occurred.</p>
9311 </div>
9312 <h2 class="mainh pad2" id="sites_sec">Motif Locations</h2>
9313 <div id="blocks" class="box">
9314 <p>Please wait... Loading...</p>
9315 <p>If the page has fully loaded and this message does not disappear then an error may have occurred.</p>
9316 </div>
9317 <h2 class="mainh pad2" id="inputs_sec">Inputs &amp; Settings</h2>
9318 <div class="box">
9319 <h4>Sequences</h4>
9320 <table id="seq_info" class="inputs">
9321 <tr>
9322 <th>Role <div class="help" data-topic="pop_seq_role"></div></th>
9323 <th>Source <div class="help" data-topic="pop_seq_source"></div></th>
9324 <th class="col_psp">PSP Source <div class="help" data-topic="pop_psp_source"></div></th>
9325 <th>Alphabet <div class="help" data-topic="pop_seq_alph"></div></th>
9326 <th>Sequence Count <div class="help" data-topic="pop_seq_count"></div></th>
9327 <th>Total Size <div class="help" data-topic="pop_num_positions"></div></th>
9328 </tr>
9329 <tr>
9330 <td>Primary Sequences</td>
9331 <td id="ins_seq_source"></td>
9332 <td id="ins_seq_psp" class="col_psp"></td>
9333 <td id="ins_seq_alphabet"></td>
9334 <td id="ins_seq_count"></td>
9335 <td id="ins_num_positions"></td>
9336 </tr>
9337 <tr class="col_control">
9338 <td class="col_control">Control Sequences</td>
9339 <td id="ins_control_source" class="col_control"></td>
9340 <td id="ins_control_psp" class="col_control col_psp"></td>
9341 <td id="ins_control_alphabet" class="col_control"></td>
9342 <td id="ins_control_count" class="col_control"></td>
9343 <td id="ins_control_positions" class="col_control"></td>
9344 </tr>
9345 </table>
9346 <script>
9347 {
9348 var db = data.sequence_db;
9349 $("ins_seq_source").innerHTML = db.primary_source;
9350 $("ins_seq_alphabet").innerHTML = current_alphabet.get_alphabet_name();
9351 $("ins_seq_count").innerHTML = db.primary_count;
9352 $("ins_num_positions").innerHTML = db.primary_positions;
9353 $("ins_control_source").innerHTML = db.control_source;
9354 $("ins_control_alphabet").innerHTML = current_alphabet.get_alphabet_name();
9355 $("ins_control_count").innerHTML = db.control_count;
9356 $("ins_control_positions").innerHTML = db.control_positions;
9357 if (db.psp_source) {
9358 $("ins_seq_psp").innerHTML = db.psp_source;
9359 }
9360 toggle_class($("seq_info"), "hide_psp", !(db.psp_source));
9361 toggle_class($("seq_info"), "hide_control", (db.control_source == "--none--"));
9362 }
9363 </script>
9364 <h4>Background Model</h4>
9365 <span id="bg_source"></span>
9366 <span id="bg_order"></span>
9367 <span id="alpha_bg"></span>
9368 <script>
9369 {
9370 $("bg_source").appendChild(make_background_source("Source", data.background.source, false));
9371 $("bg_order").innerHTML = "&nbsp;&nbsp;&nbsp;&nbsp;<b>Order:</b> " + data.background.order +
9372 (data.background.order>0 ? " (only order-0 shown)" : "");
9373 $("alpha_bg").appendChild(make_alpha_bg_table(current_alphabet, data.sequence_db.freqs));
9374 }
9375 </script>
9376 <h4>Other Settings</h4>
9377 <table id="tbl_settings" class="inputs hide_advanced">
9378 <tr>
9379 <th>Motif Site Distribution</th>
9380 <td id="opt_mod">
9381 <span class="mod_zoops">ZOOPS: Zero or one site per sequence</span>
9382 <span class="mod_oops">OOPS: Exactly one site per sequence</span>
9383 <span class="mod_anr">ANR: Any number of sites per sequence</span>
9384 </td>
9385 </tr>
9386 <tr>
9387 <th>Objective Function</th>
9388 <td id=opt_objfun></td>
9389 </tr>
9390 <tr>
9391 <th>Starting Point Function</th>
9392 <td id=opt_spfun></td>
9393 </tr>
9394 <tr>
9395 <th>Site Strand Handling</th>
9396 <td id="opt_strand">
9397 <span class="strand_none">This alphabet only has one strand</span>
9398 <span class="strand_given">Sites must be on the given strand</span>
9399 <span class="strand_both">Sites may be on either strand</span>
9400 </td>
9401 </tr>
9402 <tr>
9403 <th>Maximum Number of Motifs</th>
9404 <td id="opt_nmotifs"></td>
9405 </tr>
9406 <tr>
9407 <th>Motif E-value Threshold</th>
9408 <td id="opt_evt"></td>
9409 </tr>
9410 <tr>
9411 <th>Minimum Motif Width</th>
9412 <td id="opt_minw"></td>
9413 </tr>
9414 <tr>
9415 <th>Maximum Motif Width</th>
9416 <td id="opt_maxw"></td>
9417 </tr>
9418 <tr>
9419 <th>Minimum Sites per Motif</th>
9420 <td id="opt_minsites"></td>
9421 </tr>
9422 <tr>
9423 <th>Maximum Sites per Motif</th>
9424 <td id="opt_maxsites"></td>
9425 </tr>
9426 <tr class="advanced">
9427 <th>Bias on Number of Sites</th>
9428 <td id="opt_wnsites"></td>
9429 </tr>
9430 <tr class="advanced">
9431 <th>Sequence Prior</th>
9432 <td id="opt_prior">
9433 <span class="prior_dirichlet">Simple Dirichlet</span>
9434 <span class="prior_dmix">Dirichlet Mixture</span>
9435 <span class="prior_mega">Mega-weight Dirichlet Mixture</span>
9436 <span class="prior_megap">Mega-weight Dirichlet Mixture Plus</span>
9437 <span class="prior_addone">Add One</span>
9438 </td>
9439 </tr>
9440 <tr class="advanced">
9441 <th>Sequence Prior Source</th>
9442 <td id="opt_prior_source"></td>
9443 </tr>
9444 <tr class="advanced">
9445 <th>Sequence Prior Strength</th>
9446 <td id="opt_b"></td>
9447 </tr>
9448 <tr class="advanced">
9449 <th>EM Starting Point Source</th>
9450 <td id="opt_substring">
9451 <span class="substring_on">From substrings in input sequences</span>
9452 <span class="substring_off">From strings on command line (-cons)</span>
9453 </td>
9454 </tr>
9455 <tr class="advanced">
9456 <th>EM Starting Point Map Type</th>
9457 <td id="opt_spmap">
9458 <span class="spmap_uni">Uniform</span>
9459 <span class="spmap_pam">Point Accepted Mutation</span>
9460 </td>
9461 </tr>
9462 <tr class="advanced">
9463 <th>EM Starting Point Fuzz</th>
9464 <td id="opt_spfuzz"></td>
9465 </tr>
9466 <tr class="advanced">
9467 <th>EM Maximum Iterations</th>
9468 <td id="opt_maxiter"></td>
9469 </tr>
9470 <tr class="advanced">
9471 <th>EM Improvement Threshold</th>
9472 <td id="opt_distance"></td>
9473 </tr>
9474 <tr class="advanced">
9475 <th>Maximum Search Size</th>
9476 <td id="opt_searchsize"></td>
9477 </tr>
9478 <tr class="advanced">
9479 <th>Maximum Number of Sites for E-values</th>
9480 <td id="opt_csites"></td>
9481 </tr>
9482 <tr class="advanced">
9483 <th>Trim Gap Open Cost</th>
9484 <td id="opt_wg"></td>
9485 </tr>
9486 <tr class="advanced">
9487 <th>Trim Gap Extend Cost</th>
9488 <td id="opt_ws"></td>
9489 </tr>
9490 <tr class="advanced">
9491 <th>End Gap Treatment</th>
9492 <td id="opt_noendgaps">
9493 <span class="noendgaps_on">No cost</span>
9494 <span class="noendgaps_off">Same cost as other gaps</span>
9495 </td>
9496 </tr>
9497 <tr>
9498 <td colspan="2" style="text-align: center">
9499 <a href="javascript:toggle_class(document.getElementById('tbl_settings'), 'hide_advanced')">
9500 <span class="show_more">Show Advanced Settings</span>
9501 <span class="show_less">Hide Advanced Settings</span>
9502 </a>
9503 </td>
9504 </tr>
9505 </table>
1 <script> 9506 <script>
2 { 9507 {
3 $("opt_mod").className = data.options.mod; 9508 $("opt_mod").className = data.options.mod;
4 $("opt_objfun").textContent = data.options.objfun; 9509 $("opt_objfun").textContent = data.options.objfun;
5 $("opt_spfun").textContent = data.options.spfun; 9510 $("opt_spfun").textContent = data.options.spfun;