comparison test-data/meme_output_test2.html @ 2:3e5c80594237 draft

"planemo upload for repository https://github.com/galaxyproject/tools-iuc/tree/master/tools/meme commit 86a94f48321780dbe18ef5b099434c347ec2f4d0"
author iuc
date Wed, 11 Dec 2019 18:04:55 -0500
parents cb8a2242bf03
children f33d77dcacce
comparison
equal deleted inserted replaced
1:cb8a2242bf03 2:3e5c80594237
5 <title>MEME</title> 5 <title>MEME</title>
6 <script> 6 <script>
7 // @JSON_VAR data 7 // @JSON_VAR data
8 var data = { 8 var data = {
9 "program": "MEME", 9 "program": "MEME",
10 "version": "4.12.0", 10 "version": "5.0.5",
11 "release": "Tue Jun 27 16:22:50 2017 -0700", 11 "release": "Mon Mar 18 20:12:19 2019 -0700",
12 "stop_reason": "Stopped because requested number of motifs (1) found.", 12 "stop_reason": "Stopped because requested number of motifs (1) found.",
13 "cmd": [ 13 "cmd": [
14 "meme", "meme_input_1.fasta", "-o", "meme_test2_out", "-nostatus", 14 "meme",
15 "-maxsize", "1000000", "-sf", "Galaxy_FASTA_Input", "-dna", "-mod", 15 "-o",
16 "zoops", "-nmotifs", "1", "-wnsites", "0.8", "-minw", "8", "-maxw", 16 "-nostatus", "-maxsize", "1000000", "-sf", "Galaxy_FASTA_Input",
17 "50", "-wg", "11", "-ws", "1", "-maxiter", "50", "-distance", 17 "-dna", "-mod", "zoops", "-nmotifs", "1", "-wnsites", "0.8",
18 "0.001", "-prior", "dirichlet", "-b", "0.01", "-plib", 18 "-minw", "8", "-maxw", "50", "-wg", "11", "-ws", "1", "-maxiter",
19 "prior30.plib", "-spmap", "uni", "-spfuzz", "0.5" 19 "50", "-distance", "0.001", "-prior", "dirichlet", "-b", "0.01",
20 "-plib",
21 "-spmap", "uni", "-spfuzz", "0.5"
20 ], 22 ],
21 "options": {
22 "mod": "zoops",
23 "revcomp": false,
24 "nmotifs": 1,
25 "minw": 8,
26 "maxw": 50,
27 "minsites": 2,
28 "maxsites": 30,
29 "wnsites": 0.8,
30 "spmap": "uni",
31 "spfuzz": 0.5,
32 "maxwords": -1,
33 "prior": "dirichlet",
34 "b": 0.01,
35 "maxiter": 50,
36 "distance": 0.001,
37 "wg": 11,
38 "ws": 1,
39 "noendgaps": false,
40 "substring": true
41 },
42 "alphabet": {
43 "name": "DNA",
44 "like": "dna",
45 "ncore": 4,
46 "symbols": [
47 {
48 "symbol": "A",
49 "name": "Adenine",
50 "colour": "CC0000",
51 "complement": "T"
52 }, {
53 "symbol": "C",
54 "name": "Cytosine",
55 "colour": "0000CC",
56 "complement": "G"
57 }, {
58 "symbol": "G",
59 "name": "Guanine",
60 "colour": "FFB300",
61 "complement": "C"
62 }, {
63 "symbol": "T",
64 "aliases": "U",
65 "name": "Thymine",
66 "colour": "008000",
67 "complement": "A"
68 }, {
69 "symbol": "N",
70 "aliases": "X.",
71 "name": "Any base",
72 "equals": "ACGT"
73 }, {
74 "symbol": "V",
75 "name": "Not T",
76 "equals": "ACG"
77 }, {
78 "symbol": "H",
79 "name": "Not G",
80 "equals": "ACT"
81 }, {
82 "symbol": "D",
83 "name": "Not C",
84 "equals": "AGT"
85 }, {
86 "symbol": "B",
87 "name": "Not A",
88 "equals": "CGT"
89 }, {
90 "symbol": "M",
91 "name": "Amino",
92 "equals": "AC"
93 }, {
94 "symbol": "R",
95 "name": "Purine",
96 "equals": "AG"
97 }, {
98 "symbol": "W",
99 "name": "Weak",
100 "equals": "AT"
101 }, {
102 "symbol": "S",
103 "name": "Strong",
104 "equals": "CG"
105 }, {
106 "symbol": "Y",
107 "name": "Pyrimidine",
108 "equals": "CT"
109 }, {
110 "symbol": "K",
111 "name": "Keto",
112 "equals": "GT"
113 }
114 ]
115 },
116 "background": {
117 "freqs": [0.294, 0.231, 0.257, 0.217]
118 },
119 "sequence_db": {
120 "source": "Galaxy_FASTA_Input",
121 "psp_source": "prior30.plib",
122 "freqs": [0.294, 0.231, 0.257, 0.217],
123 "sequences": [
124 {
125 "name": "chr21_19617074_19617124_+",
126 "length": 50,
127 "weight": 1.000000
128 }, {
129 "name": "chr21_26934381_26934431_+",
130 "length": 50,
131 "weight": 1.000000
132 }, {
133 "name": "chr21_28217753_28217803_-",
134 "length": 50,
135 "weight": 1.000000
136 }, {
137 "name": "chr21_31710037_31710087_-",
138 "length": 50,
139 "weight": 1.000000
140 }, {
141 "name": "chr21_31744582_31744632_-",
142 "length": 50,
143 "weight": 1.000000
144 }, {
145 "name": "chr21_31768316_31768366_+",
146 "length": 50,
147 "weight": 1.000000
148 }, {
149 "name": "chr21_31914206_31914256_-",
150 "length": 50,
151 "weight": 1.000000
152 }, {
153 "name": "chr21_31933633_31933683_-",
154 "length": 50,
155 "weight": 1.000000
156 }, {
157 "name": "chr21_31962741_31962791_-",
158 "length": 50,
159 "weight": 1.000000
160 }, {
161 "name": "chr21_31964683_31964733_+",
162 "length": 50,
163 "weight": 1.000000
164 }, {
165 "name": "chr21_31973364_31973414_+",
166 "length": 50,
167 "weight": 1.000000
168 }, {
169 "name": "chr21_31992870_31992920_+",
170 "length": 50,
171 "weight": 1.000000
172 }, {
173 "name": "chr21_32185595_32185645_-",
174 "length": 50,
175 "weight": 1.000000
176 }, {
177 "name": "chr21_32202076_32202126_-",
178 "length": 50,
179 "weight": 1.000000
180 }, {
181 "name": "chr21_32253899_32253949_-",
182 "length": 50,
183 "weight": 1.000000
184 }, {
185 "name": "chr21_32410820_32410870_-",
186 "length": 50,
187 "weight": 1.000000
188 }, {
189 "name": "chr21_36411748_36411798_-",
190 "length": 50,
191 "weight": 1.000000
192 }, {
193 "name": "chr21_37838750_37838800_-",
194 "length": 50,
195 "weight": 1.000000
196 }, {
197 "name": "chr21_45705687_45705737_+",
198 "length": 50,
199 "weight": 1.000000
200 }, {
201 "name": "chr21_45971413_45971463_-",
202 "length": 50,
203 "weight": 1.000000
204 }, {
205 "name": "chr21_45978668_45978718_-",
206 "length": 50,
207 "weight": 1.000000
208 }, {
209 "name": "chr21_45993530_45993580_+",
210 "length": 50,
211 "weight": 1.000000
212 }, {
213 "name": "chr21_46020421_46020471_+",
214 "length": 50,
215 "weight": 1.000000
216 }, {
217 "name": "chr21_46031920_46031970_+",
218 "length": 50,
219 "weight": 1.000000
220 }, {
221 "name": "chr21_46046964_46047014_+",
222 "length": 50,
223 "weight": 1.000000
224 }, {
225 "name": "chr21_46057197_46057247_+",
226 "length": 50,
227 "weight": 1.000000
228 }, {
229 "name": "chr21_46086869_46086919_-",
230 "length": 50,
231 "weight": 1.000000
232 }, {
233 "name": "chr21_46102103_46102153_-",
234 "length": 50,
235 "weight": 1.000000
236 }, {
237 "name": "chr21_47517957_47518007_+",
238 "length": 50,
239 "weight": 1.000000
240 }, {
241 "name": "chr21_47575506_47575556_-",
242 "length": 50,
243 "weight": 1.000000
244 }
245 ]
246 },
247 "motifs": [ 23 "motifs": [
248 {
249 "db": 0,
250 "id": "GGSRTATAAAA",
251 "alt": "MEME-1",
252 "len": 11,
253 "nsites": 30,
254 "evalue": "5.1e-040",
255 "ic": 13.0,
256 "re": 12.2,
257 "llr": 254,
258 "bt": 5.2854,
259 "time": 0.376000,
260 "psm": [
261 [-14, -179, 114, -112], [3, -1155, 137, -270],
262 [-114, 20, 86, -71], [3, -279, 122, -170],
263 [-1155, -1155, -295, 215], [156, -179, -1155, -170],
264 [-1155, -1155, -1155, 220], [172, -279, -1155, -1155],
265 [125, -1155, -1155, 46], [167, -179, -1155, -1155],
266 [144, -1155, -63, -270]
267 ],
268 "pwm": [
269 [0.266667, 0.066667, 0.566667, 0.100000],
270 [0.300000, 0.000000, 0.666667, 0.033333],
271 [0.133333, 0.266667, 0.466667, 0.133333],
272 [0.300000, 0.033333, 0.600000, 0.066667],
273 [0.000000, 0.000000, 0.033333, 0.966667],
274 [0.866667, 0.066667, 0.000000, 0.066667],
275 [0.000000, 0.000000, 0.000000, 1.000000],
276 [0.966667, 0.033333, 0.000000, 0.000000],
277 [0.700000, 0.000000, 0.000000, 0.300000],
278 [0.933333, 0.066667, 0.000000, 0.000000],
279 [0.800000, 0.000000, 0.166667, 0.033333]
280 ],
281 "sites": [
282 {
283 "seq": 24,
284 "pos": 12,
285 "rc": false,
286 "pvalue": 4.51e-07,
287 "lflank": "AAGGCCAGGA",
288 "match": "GGGGTATAAAA",
289 "rflank": "GCCTGAGAGC"
290 }, {
291 "seq": 23,
292 "pos": 15,
293 "rc": false,
294 "pvalue": 2.22e-06,
295 "lflank": "ATACCCAGGG",
296 "match": "AGGGTATAAAA",
297 "rflank": "CCTCAGCAGC"
298 }, {
299 "seq": 13,
300 "pos": 13,
301 "rc": false,
302 "pvalue": 2.74e-06,
303 "lflank": "CCACCAGCTT",
304 "match": "GAGGTATAAAA",
305 "rflank": "AGCCCTGTAC"
306 }, {
307 "seq": 25,
308 "pos": 36,
309 "rc": false,
310 "pvalue": 4.86e-06,
311 "lflank": "ACAGGCCCTG",
312 "match": "GGCATATAAAA",
313 "rflank": "GCC"
314 }, {
315 "seq": 21,
316 "pos": 7,
317 "rc": false,
318 "pvalue": 4.86e-06,
319 "lflank": "CCAAGGA",
320 "match": "GGAGTATAAAA",
321 "rflank": "GCCCCACAAA"
322 }, {
323 "seq": 19,
324 "pos": 9,
325 "rc": false,
326 "pvalue": 4.86e-06,
327 "lflank": "CAGGCCCTG",
328 "match": "GGCATATAAAA",
329 "rflank": "GCCCCAGCAG"
330 }, {
331 "seq": 9,
332 "pos": 13,
333 "rc": false,
334 "pvalue": 4.86e-06,
335 "lflank": "GATTCACTGA",
336 "match": "GGCATATAAAA",
337 "rflank": "GGCCCTCTGC"
338 }, {
339 "seq": 28,
340 "pos": 32,
341 "rc": false,
342 "pvalue": 6.48e-06,
343 "lflank": "CCGGCGGGGC",
344 "match": "GGGGTATAAAG",
345 "rflank": "GGGGCGG"
346 }, {
347 "seq": 20,
348 "pos": 4,
349 "rc": false,
350 "pvalue": 6.48e-06,
351 "lflank": "CAGA",
352 "match": "GGGGTATAAAG",
353 "rflank": "GTTCCGACCA"
354 }, {
355 "seq": 12,
356 "pos": 18,
357 "rc": false,
358 "pvalue": 6.48e-06,
359 "lflank": "CACCAGAGCT",
360 "match": "GGGATATATAA",
361 "rflank": "AGAAGGTTCT"
362 }, {
363 "seq": 15,
364 "pos": 21,
365 "rc": false,
366 "pvalue": 1.38e-05,
367 "lflank": "AATCACTGAG",
368 "match": "GATGTATAAAA",
369 "rflank": "GTCCCAGGGA"
370 }, {
371 "seq": 11,
372 "pos": 16,
373 "rc": false,
374 "pvalue": 1.38e-05,
375 "lflank": "CACTATTGAA",
376 "match": "GATGTATAAAA",
377 "rflank": "TTTCATTTGC"
378 }, {
379 "seq": 0,
380 "pos": 39,
381 "rc": false,
382 "pvalue": 1.41e-05,
383 "lflank": "CCTCGGGACG",
384 "match": "TGGGTATATAA",
385 "rflank": ""
386 }, {
387 "seq": 6,
388 "pos": 15,
389 "rc": false,
390 "pvalue": 1.61e-05,
391 "lflank": "CCCACTACTT",
392 "match": "AGAGTATAAAA",
393 "rflank": "TCATTCTGAG"
394 }, {
395 "seq": 22,
396 "pos": 2,
397 "rc": false,
398 "pvalue": 1.95e-05,
399 "lflank": "GA",
400 "match": "GACATATAAAA",
401 "rflank": "GCCAACATCC"
402 }, {
403 "seq": 14,
404 "pos": 17,
405 "rc": false,
406 "pvalue": 1.95e-05,
407 "lflank": "CCCACCAGCA",
408 "match": "AGGATATATAA",
409 "rflank": "AAGCTCAGGA"
410 }, {
411 "seq": 18,
412 "pos": 37,
413 "rc": false,
414 "pvalue": 2.16e-05,
415 "lflank": "CGTGGTCGCG",
416 "match": "GGGGTATAACA",
417 "rflank": "GC"
418 }, {
419 "seq": 29,
420 "pos": 30,
421 "rc": false,
422 "pvalue": 3.04e-05,
423 "lflank": "GCTGCCGGTG",
424 "match": "AGCGTATAAAG",
425 "rflank": "GCCCTGGCG"
426 }, {
427 "seq": 4,
428 "pos": 12,
429 "rc": false,
430 "pvalue": 3.04e-05,
431 "lflank": "CAGGTCTAAG",
432 "match": "AGCATATATAA",
433 "rflank": "CTTGGAGTCC"
434 }, {
435 "seq": 5,
436 "pos": 0,
437 "rc": false,
438 "pvalue": 3.67e-05,
439 "lflank": "",
440 "match": "AACGTATATAA",
441 "rflank": "ATGGTCCTGT"
442 }, {
443 "seq": 1,
444 "pos": 27,
445 "rc": false,
446 "pvalue": 3.93e-05,
447 "lflank": "AGTCACAAGT",
448 "match": "GAGTTATAAAA",
449 "rflank": "GGGTCGCACG"
450 }, {
451 "seq": 7,
452 "pos": 4,
453 "rc": false,
454 "pvalue": 5.65e-05,
455 "lflank": "TCAG",
456 "match": "AGTATATATAA",
457 "rflank": "ATGTTCCTGT"
458 }, {
459 "seq": 3,
460 "pos": 14,
461 "rc": false,
462 "pvalue": 6.24e-05,
463 "lflank": "CCCAGGTTTC",
464 "match": "TGAGTATATAA",
465 "rflank": "TCGCCGCACC"
466 }, {
467 "seq": 16,
468 "pos": 22,
469 "rc": false,
470 "pvalue": 7.15e-05,
471 "lflank": "AGTTTCAGTT",
472 "match": "GGCATCTAAAA",
473 "rflank": "attatataac"
474 }, {
475 "seq": 27,
476 "pos": 36,
477 "rc": false,
478 "pvalue": 1.39e-04,
479 "lflank": "TGCCTGGGTC",
480 "match": "CAGGTATAAAG",
481 "rflank": "GCT"
482 }, {
483 "seq": 26,
484 "pos": 37,
485 "rc": false,
486 "pvalue": 1.39e-04,
487 "lflank": "TGCCTGGGCC",
488 "match": "CAGGTATAAAG",
489 "rflank": "GC"
490 }, {
491 "seq": 17,
492 "pos": 2,
493 "rc": false,
494 "pvalue": 4.81e-04,
495 "lflank": "ga",
496 "match": "TGGTTTTATAA",
497 "rflank": "ggggcctcac"
498 }, {
499 "seq": 8,
500 "pos": 13,
501 "rc": false,
502 "pvalue": 8.57e-04,
503 "lflank": "TATAACTCAG",
504 "match": "GTTGGATAAAA",
505 "rflank": "TAATTTGTAC"
506 }, {
507 "seq": 10,
508 "pos": 7,
509 "rc": false,
510 "pvalue": 1.47e-03,
511 "lflank": "aaactta",
512 "match": "AAACTCTATAA",
513 "rflank": "acttaaaact"
514 }, {
515 "seq": 2,
516 "pos": 26,
517 "rc": false,
518 "pvalue": 2.64e-03,
519 "lflank": "GGTGGGGGTG",
520 "match": "GGGGTTTCACT",
521 "rflank": "GGTCCACTAT"
522 }
523 ]
524 }
525 ],
526 "scan": [
527 {
528 "pvalue": 5.63e-04,
529 "sites": [
530 {
531 "motif": 0,
532 "pos": 39,
533 "rc": false,
534 "pvalue": 1.41e-05
535 }
536 ]
537 }, {
538 "pvalue": 1.57e-03,
539 "sites": [
540 {
541 "motif": 0,
542 "pos": 27,
543 "rc": false,
544 "pvalue": 3.93e-05
545 }
546 ]
547 }, {
548 "pvalue": 1.00e-01,
549 "sites": []
550 }, {
551 "pvalue": 2.49e-03,
552 "sites": [
553 {
554 "motif": 0,
555 "pos": 14,
556 "rc": false,
557 "pvalue": 6.24e-05
558 }
559 ]
560 }, {
561 "pvalue": 1.22e-03,
562 "sites": [
563 {
564 "motif": 0,
565 "pos": 12,
566 "rc": false,
567 "pvalue": 3.04e-05
568 }
569 ]
570 }, {
571 "pvalue": 1.47e-03,
572 "sites": [
573 {
574 "motif": 0,
575 "pos": 0,
576 "rc": false,
577 "pvalue": 3.67e-05
578 }
579 ]
580 }, {
581 "pvalue": 6.45e-04,
582 "sites": [
583 {
584 "motif": 0,
585 "pos": 15,
586 "rc": false,
587 "pvalue": 1.61e-05
588 }
589 ]
590 }, {
591 "pvalue": 2.26e-03,
592 "sites": [
593 {
594 "motif": 0,
595 "pos": 4,
596 "rc": false,
597 "pvalue": 5.65e-05
598 }
599 ]
600 }, {
601 "pvalue": 3.37e-02,
602 "sites": []
603 }, {
604 "pvalue": 1.95e-04,
605 "sites": [
606 {
607 "motif": 0,
608 "pos": 13,
609 "rc": false,
610 "pvalue": 4.86e-06
611 }
612 ]
613 }, {
614 "pvalue": 5.73e-02,
615 "sites": []
616 }, {
617 "pvalue": 5.52e-04,
618 "sites": [
619 {
620 "motif": 0,
621 "pos": 16,
622 "rc": false,
623 "pvalue": 1.38e-05
624 }
625 ]
626 }, {
627 "pvalue": 2.59e-04,
628 "sites": [
629 {
630 "motif": 0,
631 "pos": 18,
632 "rc": false,
633 "pvalue": 6.48e-06
634 }
635 ]
636 }, {
637 "pvalue": 1.10e-04,
638 "sites": [
639 {
640 "motif": 0,
641 "pos": 13,
642 "rc": false,
643 "pvalue": 2.74e-06
644 }
645 ]
646 }, {
647 "pvalue": 7.78e-04,
648 "sites": [
649 {
650 "motif": 0,
651 "pos": 17,
652 "rc": false,
653 "pvalue": 1.95e-05
654 }
655 ]
656 }, {
657 "pvalue": 5.52e-04,
658 "sites": [
659 {
660 "motif": 0,
661 "pos": 21,
662 "rc": false,
663 "pvalue": 1.38e-05
664 }
665 ]
666 }, {
667 "pvalue": 2.85e-03,
668 "sites": [
669 {
670 "motif": 0,
671 "pos": 22,
672 "rc": false,
673 "pvalue": 7.15e-05
674 }
675 ]
676 }, {
677 "pvalue": 1.90e-02,
678 "sites": []
679 }, {
680 "pvalue": 8.63e-04,
681 "sites": [
682 {
683 "motif": 0,
684 "pos": 37,
685 "rc": false,
686 "pvalue": 2.16e-05
687 }
688 ]
689 }, {
690 "pvalue": 1.95e-04,
691 "sites": [
692 {
693 "motif": 0,
694 "pos": 9,
695 "rc": false,
696 "pvalue": 4.86e-06
697 }
698 ]
699 }, {
700 "pvalue": 2.59e-04,
701 "sites": [
702 {
703 "motif": 0,
704 "pos": 4,
705 "rc": false,
706 "pvalue": 6.48e-06
707 }
708 ]
709 }, {
710 "pvalue": 1.95e-04,
711 "sites": [
712 {
713 "motif": 0,
714 "pos": 7,
715 "rc": false,
716 "pvalue": 4.86e-06
717 }
718 ]
719 }, {
720 "pvalue": 7.78e-04,
721 "sites": [
722 {
723 "motif": 0,
724 "pos": 2,
725 "rc": false,
726 "pvalue": 1.95e-05
727 }
728 ]
729 }, {
730 "pvalue": 8.89e-05,
731 "sites": [
732 {
733 "motif": 0,
734 "pos": 15,
735 "rc": false,
736 "pvalue": 2.22e-06
737 }
738 ]
739 }, {
740 "pvalue": 1.80e-05,
741 "sites": [
742 {
743 "motif": 0,
744 "pos": 12,
745 "rc": false,
746 "pvalue": 4.51e-07
747 }
748 ]
749 }, {
750 "pvalue": 1.95e-04,
751 "sites": [
752 {
753 "motif": 0,
754 "pos": 36,
755 "rc": false,
756 "pvalue": 4.86e-06
757 }
758 ]
759 }, {
760 "pvalue": 5.54e-03,
761 "sites": []
762 }, {
763 "pvalue": 5.54e-03,
764 "sites": []
765 }, {
766 "pvalue": 2.59e-04,
767 "sites": [
768 {
769 "motif": 0,
770 "pos": 32,
771 "rc": false,
772 "pvalue": 6.48e-06
773 }
774 ]
775 }, {
776 "pvalue": 1.22e-03,
777 "sites": [
778 {
779 "motif": 0,
780 "pos": 30,
781 "rc": false,
782 "pvalue": 3.04e-05
783 }
784 ]
785 }
786 ] 24 ]
787 }; 25 };
788 </script> 26 </script>
789 <script>
790 var site_url = "http://meme-suite.org"; 27 var site_url = "http://meme-suite.org";
791 </script>
792 <script>
793
794 /*
795 * $
796 *
797 * Shorthand function for getElementById
798 */
799 function $(el) {
800 return document.getElementById(el);
801 }
802
803
804 /*
805 * See http://stackoverflow.com/a/5450113/66387
806 * Does string multiplication like the perl x operator.
807 */
808 function string_mult(pattern, count) {
809 if (count < 1) return '';
810 var result = '';
811 while (count > 1) {
812 if (count & 1) result += pattern;
813 count >>= 1, pattern += pattern;
814 }
815 return result + pattern;
816 }
817
818 /*
819 * See http://stackoverflow.com/questions/814613/how-to-read-get-data-from-a-url-using-javascript
820 * Slightly modified with information from
821 * https://developer.mozilla.org/en/DOM/window.location
822 */
823 function parse_params() {
824 "use strict";
825 var search, queryStart, queryEnd, query, params, nvPairs, i, nv, n, v;
826 search = window.location.search;
827 queryStart = search.indexOf("?") + 1;
828 queryEnd = search.indexOf("#") + 1 || search.length + 1;
829 query = search.slice(queryStart, queryEnd - 1);
830
831 if (query === search || query === "") return {};
832
833 params = {};
834 nvPairs = query.replace(/\+/g, " ").split("&");
835
836 for (i = 0; i < nvPairs.length; i++) {
837 nv = nvPairs[i].split("=");
838 n = decodeURIComponent(nv[0]);
839 v = decodeURIComponent(nv[1]);
840 // allow a name to be used multiple times
841 // storing each value in the array
842 if (!(n in params)) {
843 params[n] = [];
844 }
845 params[n].push(nv.length === 2 ? v : null);
846 }
847 return params;
848 }
849
850 /*
851 * coords
852 *
853 * Calculates the x and y offset of an element.
854 * From http://www.quirksmode.org/js/findpos.html
855 * with alterations to take into account scrolling regions
856 */
857 function coords(elem) {
858 var myX = myY = 0;
859 if (elem.getBoundingClientRect) {
860 var rect;
861 rect = elem.getBoundingClientRect();
862 myX = rect.left + ((typeof window.pageXOffset !== "undefined") ?
863 window.pageXOffset : document.body.scrollLeft);
864 myY = rect.top + ((typeof window.pageYOffset !== "undefined") ?
865 window.pageYOffset : document.body.scrollTop);
866 } else {
867 // this fall back doesn't properly handle absolutely positioned elements
868 // inside a scrollable box
869 var node;
870 if (elem.offsetParent) {
871 // subtract all scrolling
872 node = elem;
873 do {
874 myX -= node.scrollLeft ? node.scrollLeft : 0;
875 myY -= node.scrollTop ? node.scrollTop : 0;
876 } while (node = node.parentNode);
877 // this will include the page scrolling (which is unwanted) so add it back on
878 myX += (typeof window.pageXOffset !== "undefined") ? window.pageXOffset : document.body.scrollLeft;
879 myY += (typeof window.pageYOffset !== "undefined") ? window.pageYOffset : document.body.scrollTop;
880 // sum up offsets
881 node = elem;
882 do {
883 myX += node.offsetLeft;
884 myY += node.offsetTop;
885 } while (node = node.offsetParent);
886 }
887 }
888 return [myX, myY];
889 }
890
891 /*
892 * position_popup
893 *
894 * Positions a popup relative to an anchor element.
895 *
896 * The avaliable positions are:
897 * 0 - Centered below the anchor.
898 */
899 function position_popup(anchor, popup, position) {
900 "use strict";
901 var a_x, a_y, a_w, a_h, p_x, p_y, p_w, p_h;
902 var a_xy, spacer, margin, scrollbar, page_w;
903 // define constants
904 spacer = 5;
905 margin = 15;
906 scrollbar = 15;
907 // define the positions and widths
908 a_xy = coords(anchor);
909 a_x = a_xy[0];
910 a_y = a_xy[1];
911 a_w = anchor.offsetWidth;
912 a_h = anchor.offsetHeight;
913 p_w = popup.offsetWidth;
914 p_h = popup.offsetHeight;
915 page_w = null;
916 if (window.innerWidth) {
917 page_w = window.innerWidth;
918 } else if (document.body) {
919 page_w = document.body.clientWidth;
920 }
921 // check the position type is defined
922 if (typeof position !== "number") {
923 position = 0;
924 }
925 // calculate the popup position
926 switch (position) {
927 case 1:
928 p_x = a_x + a_w + spacer;
929 p_y = a_y + (a_h / 2) - (p_h / 2);
930 break;
931 case 0:
932 default:
933 p_x = a_x + (a_w / 2) - (p_w / 2);
934 p_y = a_y + a_h + spacer;
935 break;
936 }
937 // constrain the popup position
938 if (p_x < margin) {
939 p_x = margin;
940 } else if (page_w != null && (p_x + p_w) > (page_w - margin - scrollbar)) {
941 p_x = page_w - margin - scrollbar - p_w;
942 }
943 if (p_y < margin) {
944 p_y = margin;
945 }
946 // position the popup
947 popup.style.left = p_x + "px";
948 popup.style.top = p_y + "px";
949 }
950
951 function lookup_help_popup(popup_id) {
952 var _body, pop, info;
953 pop = document.getElementById(popup_id);
954 if (pop == null) {
955 _body = document.getElementsByTagName("body")[0];
956 pop = document.createElement("div");
957 pop.className = "pop_content";
958 pop.id = popup_id;
959 pop.style.backgroundColor = "#FFC";
960 pop.style.borderColor = "black";
961 info = document.createElement("p");
962 info.style.fontWeight = "bold";
963 info.appendChild(document.createTextNode("Error: No popup for topic \"" + popup_id + "\"."));
964 pop.appendChild(info);
965 // this might cause problems with the menu, but as this only happens
966 // when something is already wrong I don't think that's too much of a problem
967 _body.insertBefore(pop, _body.firstChild);
968 }
969 if (document.getElementsByTagName('body')[0].hasAttribute("data-autobtns")) {
970 if (!/\bauto_buttons\b/.test(pop.className)) {
971 pop.className += " auto_buttons";
972 var back_btn_sec = document.createElement("div");
973 back_btn_sec.className = "nested_only pop_back_sec";
974 var back_btn = document.createElement("span");
975 back_btn.className = "pop_back";
976 back_btn.appendChild(document.createTextNode("<< back"));
977 back_btn.addEventListener("click", function(e) {
978 help_return();
979 }, false);
980 back_btn_sec.appendChild(back_btn);
981 pop.insertBefore(back_btn_sec, pop.firstChild);
982 var close_btn_sec = document.createElement("div");
983 close_btn_sec.className = "pop_close_sec";
984 var close_btn = document.createElement("span");
985 close_btn.className = "pop_close";
986 close_btn.appendChild(document.createTextNode("close"));
987 close_btn.addEventListener("click", function(e) {
988 help_popup();
989 }, false);
990 close_btn_sec.appendChild(close_btn);
991 pop.appendChild(close_btn_sec);
992 }
993 }
994 return pop;
995 }
996
997 /*
998 * help_popup
999 *
1000 * Moves around help pop-ups so they appear
1001 * below an activator.
1002 */
1003 function help_popup(activator, popup_id) {
1004 "use strict";
1005 var pop;
1006 // set default values
1007 if (typeof help_popup.popup === "undefined") {
1008 help_popup.popup = [];
1009 }
1010 if (typeof help_popup.activator === "undefined") {
1011 help_popup.activator = null;
1012 }
1013 var last_pop = (help_popup.popup.length > 0 ? help_popup.popup[help_popup.popup.length - 1] : null);
1014 if (typeof(activator) == "undefined") { // no activator so hide
1015 if (last_pop != null) {
1016 last_pop.style.display = 'none';
1017 help_popup.popup = [];
1018 }
1019 return;
1020 }
1021 pop = lookup_help_popup(popup_id);
1022 if (pop == last_pop) {
1023 if (activator == help_popup.activator) {
1024 //hide popup (as we've already shown it for the current help button)
1025 last_pop.style.display = 'none';
1026 help_popup.popup = [];
1027 return; // toggling complete!
1028 }
1029 } else if (last_pop != null) {
1030 //activating different popup so hide current one
1031 last_pop.style.display = 'none';
1032 }
1033 help_popup.popup = [pop];
1034 help_popup.activator = activator;
1035 toggle_class(pop, "nested", false);
1036 //must make the popup visible to measure it or it has zero width
1037 pop.style.display = 'block';
1038 position_popup(activator, pop);
1039 }
1040
1041 /*
1042 * help_refine
1043 *
1044 * Intended for links within a help popup. Stores a stack of state so
1045 * you can go back.
1046 */
1047 function help_refine(popup_id) {
1048 if (help_popup.popup == null || help_popup.popup.length == 0 || help_popup.activator == null) {
1049 throw new Error("Can not refine a help popup when one is not shown!");
1050 }
1051 var pop = lookup_help_popup(popup_id);
1052 var last_pop = help_popup.popup[help_popup.popup.length - 1];
1053 if (pop == last_pop) return; // slightly odd, but no real cause for alarm
1054 help_popup.popup.push(pop);
1055 toggle_class(pop, "nested", true);
1056 last_pop.style.display = "none";
1057 //must make the popup visible to measure it or it has zero width
1058 pop.style.display = "block";
1059 position_popup(help_popup.activator, pop);
1060 }
1061
1062 /*
1063 * help_return
1064 *
1065 * Intended for links within a help popup. Stores a stack of state so
1066 * you can go back.
1067 */
1068 function help_return() {
1069 if (help_popup.popup == null || help_popup.popup.length == 0 || help_popup.activator == null) {
1070 throw new Error("Can not return to a earlier help popup when one is not shown!");
1071 }
1072 var last_pop = help_popup.popup.pop();
1073 last_pop.style.display = "none";
1074 var pop = (help_popup.popup.length > 0 ? help_popup.popup[help_popup.popup.length - 1] : null);
1075 if (pop != null) {
1076 toggle_class(pop, "nested", help_popup.popup.length > 1);
1077 pop.style.display = "block";
1078 position_popup(help_popup.activator, pop);
1079 } else {
1080 help_popup.activator = null;
1081 }
1082 }
1083
1084 /*
1085 * update_scroll_pad
1086 *
1087 * Creates padding at the bottom of the page to allow
1088 * scrolling of anything into view.
1089 */
1090 function update_scroll_pad() {
1091 var page, pad;
1092 page = (document.compatMode === "CSS1Compat") ? document.documentElement : document.body;
1093 pad = $("scrollpad");
1094 if (pad === null) {
1095 pad = document.createElement("div");
1096 pad.id = 'scrollpad';
1097 document.getElementsByTagName('body')[0].appendChild(pad);
1098 }
1099 pad.style.height = Math.abs(page.clientHeight - 100) + "px";
1100 }
1101
1102 function substitute_classes(node, remove, add) {
1103 "use strict";
1104 var list, all, i, cls, classes;
1105 list = node.className.split(/\s+/);
1106 all = {};
1107 for (i = 0; i < list.length; i++) {
1108 if (list[i].length > 0) all[list[i]] = true;
1109 }
1110 for (i = 0; i < remove.length; i++) {
1111 if (all.hasOwnProperty(remove[i])) {
1112 delete all[remove[i]];
1113 }
1114 }
1115 for (i = 0; i < add.length; i++) {
1116 all[add[i]] = true;
1117 }
1118 classes = "";
1119 for (cls in all) {
1120 classes += cls + " ";
1121 }
1122 node.className = classes;
1123 }
1124
1125 /*
1126 * toggle_class
1127 *
1128 * Adds or removes a class from the node. If the parameter 'enabled' is not
1129 * passed then the existence of the class will be toggled, otherwise it will be
1130 * included if enabled is true.
1131 */
1132 function toggle_class(node, cls, enabled) {
1133 var classes = node.className;
1134 var list = classes.replace(/^\s+/, '').replace(/\s+$/, '').split(/\s+/);
1135 var found = false;
1136 for (var i = 0; i < list.length; i++) {
1137 if (list[i] == cls) {
1138 list.splice(i, 1);
1139 i--;
1140 found = true;
1141 }
1142 }
1143 if (typeof enabled == "undefined") {
1144 if (!found) list.push(cls);
1145 } else {
1146 if (enabled) list.push(cls);
1147 }
1148 node.className = list.join(" ");
1149 }
1150
1151 /*
1152 * find_child
1153 *
1154 * Searches child nodes in depth first order and returns the first it finds
1155 * with the className specified.
1156 * TODO replace with querySelector
1157 */
1158 function find_child(node, className) {
1159 var pattern;
1160 if (node == null || typeof node !== "object") {
1161 return null;
1162 }
1163 if (typeof className === "string") {
1164 pattern = new RegExp("\\b" + className + "\\b");
1165 } else {
1166 pattern = className;
1167 }
1168 if (node.nodeType == Node.ELEMENT_NODE &&
1169 pattern.test(node.className)) {
1170 return node;
1171 } else {
1172 var result = null;
1173 for (var i = 0; i < node.childNodes.length; i++) {
1174 result = find_child(node.childNodes[i], pattern);
1175 if (result != null) break;
1176 }
1177 return result;
1178 }
1179 }
1180
1181 /*
1182 * find_parent
1183 *
1184 * Searches parent nodes outwards from the node and returns the first it finds
1185 * with the className specified.
1186 */
1187 function find_parent(node, className) {
1188 var pattern;
1189 pattern = new RegExp("\\b" + className + "\\b");
1190 do {
1191 if (node.nodeType == Node.ELEMENT_NODE &&
1192 pattern.test(node.className)) {
1193 return node;
1194 }
1195 } while (node = node.parentNode);
1196 return null;
1197 }
1198
1199 /*
1200 * find_parent_tag
1201 *
1202 * Searches parent nodes outwards from the node and returns the first it finds
1203 * with the tag name specified. HTML tags should be specified in upper case.
1204 */
1205 function find_parent_tag(node, tag_name) {
1206 do {
1207 if (node.nodeType == Node.ELEMENT_NODE && node.tagName == tag_name) {
1208 return node;
1209 }
1210 } while (node = node.parentNode);
1211 return null;
1212 }
1213
1214 /*
1215 * __toggle_help
1216 *
1217 * Uses the 'topic' property of the this object to
1218 * toggle display of a help topic.
1219 *
1220 * This function is not intended to be called directly.
1221 */
1222 function __toggle_help(e) {
1223 if (!e) e = window.event;
1224 if (e.type === "keydown") {
1225 if (e.keyCode !== 13 && e.keyCode !== 32) {
1226 return;
1227 }
1228 // stop a submit or something like that
1229 e.preventDefault();
1230 }
1231
1232 help_popup(this, this.getAttribute("data-topic"));
1233 }
1234
1235 function setup_help_button(button) {
1236 "use strict";
1237 var topic;
1238 if (button.hasAttribute("data-topic")) {
1239 topic = button.getAttribute("data-topic");
1240 if (document.getElementById(topic) != null) {
1241 button.tabIndex = "0"; // make keyboard selectable
1242 button.addEventListener("click", function() {
1243 help_popup(button, topic);
1244 }, false);
1245 button.addEventListener("keydown", function(e) {
1246 // toggle only on Enter or Spacebar, let other keys do their thing
1247 if (e.keyCode !== 13 && e.keyCode !== 32) return;
1248 // stop a submit or something like that
1249 e.preventDefault();
1250 help_popup(button, topic);
1251 }, false);
1252 } else {
1253 button.style.visibility = "hidden";
1254 }
1255 }
1256 button.className += " active";
1257 }
1258
1259 /*
1260 * help_button
1261 *
1262 * Makes a help button for the passed topic.
1263 */
1264 function help_button(topic) {
1265 var btn = document.createElement("div");
1266 btn.className = "help";
1267 btn.setAttribute("data-topic", topic);
1268 setup_help_button(btn);
1269 return btn;
1270 }
1271
1272 /*
1273 * prepare_download
1274 *
1275 * Sets the attributes of a link to setup a file download using the given content.
1276 * If no link is provided then create one and click it.
1277 */
1278 function prepare_download(content, mimetype, filename, link) {
1279 "use strict";
1280 // if no link is provided then create one and click it
1281 var click_link = false;
1282 if (!link) {
1283 link = document.createElement("a");
1284 click_link = true;
1285 }
1286 try {
1287 // Use a BLOB to convert the text into a data URL.
1288 // We could do this manually with a base 64 conversion.
1289 // This will only be supported on modern browsers,
1290 // hence the try block.
1291 var blob = new Blob([content], {type: mimetype});
1292 var reader = new FileReader();
1293 reader.onloadend = function() {
1294 // If we're lucky the browser will also support the download
1295 // attribute which will let us suggest a file name to save the link.
1296 // Otherwise it is likely that the filename will be unintelligible.
1297 link.setAttribute("download", filename);
1298 link.href = reader.result;
1299 if (click_link) {
1300 // must add the link to click it
1301 document.body.appendChild(link);
1302 link.click();
1303 document.body.removeChild(link);
1304 }
1305 }
1306 reader.readAsDataURL(blob);
1307 } catch (error) {
1308 if (console && console.log) console.log(error);
1309 // probably an old browser
1310 link.href = "";
1311 link.visible = false;
1312 }
1313 }
1314
1315 /*
1316 * add_cell
1317 *
1318 * Add a cell to the table row.
1319 */
1320 function add_cell(row, node, cls, click_action) {
1321 var cell = row.insertCell(row.cells.length);
1322 if (node) cell.appendChild(node);
1323 if (cls && cls !== "") cell.className = cls;
1324 if (click_action) cell.addEventListener("click", click_action, false);
1325 }
1326
1327 /*
1328 * add_header_cell
1329 *
1330 * Add a header cell to the table row.
1331 */
1332 function add_header_cell(row, node, help_topic, cls, colspan) {
1333 var th = document.createElement("th");
1334 if (node) th.appendChild(node);
1335 if (help_topic && help_topic !== "") th.appendChild(help_button(help_topic));
1336 if (cls && cls !== "") th.className = cls;
1337 if (typeof colspan == "number" && colspan > 1) th.colSpan = colspan;
1338 row.appendChild(th);
1339 }
1340
1341 /*
1342 * add_text_cell
1343 *
1344 * Add a text cell to the table row.
1345 */
1346 function add_text_cell(row, text, cls, action) {
1347 var node = null;
1348 if (typeof(text) != 'undefined') node = document.createTextNode(text);
1349 add_cell(row, node, cls, action);
1350 }
1351
1352 /*
1353 * add_text_header_cell
1354 *
1355 * Add a text header cell to the table row.
1356 */
1357 function add_text_header_cell(row, text, help_topic, cls, action, colspan) {
1358 var node = null;
1359 if (typeof(text) != 'undefined') {
1360 var nbsp = (help_topic ? "\u00A0" : "");
1361 var str = "" + text;
1362 var parts = str.split(/\n/);
1363 if (parts.length === 1) {
1364 if (action) {
1365 node = document.createElement("span");
1366 node.appendChild(document.createTextNode(str + nbsp));
1367 } else {
1368 node = document.createTextNode(str + nbsp);
1369 }
1370 } else {
1371 node = document.createElement("span");
1372 for (var i = 0; i < parts.length; i++) {
1373 if (i !== 0) {
1374 node.appendChild(document.createElement("br"));
1375 }
1376 node.appendChild(document.createTextNode(parts[i]));
1377 }
1378 }
1379 if (action) {
1380 node.addEventListener("click", action, false);
1381 node.style.cursor = "pointer";
1382 }
1383 }
1384 add_header_cell(row, node, help_topic, cls, colspan);
1385 }
1386
1387 function setup_help() {
1388 "use strict";
1389 var help_buttons, i;
1390 help_buttons = document.querySelectorAll(".help:not(.active)");
1391 for (i = 0; i < help_buttons.length; i++) {
1392 setup_help_button(help_buttons[i]);
1393 }
1394 }
1395
1396 function setup_scrollpad() {
1397 "use strict";
1398 if (document.getElementsByTagName('body')[0].hasAttribute("data-scrollpad") && document.getElementById("scrollpad") == null) {
1399 window.addEventListener("resize", update_scroll_pad, false);
1400 update_scroll_pad();
1401 }
1402 }
1403
1404 // anon function to avoid polluting global scope
1405 (function() {
1406 "use strict";
1407 window.addEventListener("load", function load(evt) {
1408 window.removeEventListener("load", load, false);
1409 setup_help();
1410 setup_scrollpad();
1411 }, false);
1412 })();
1413
1414 /*
1415 * make_link
1416 *
1417 * Creates a text node and if a URL is specified it surrounds it with a link.
1418 * If the URL doesn't begin with "http://" it automatically adds it, as
1419 * relative links don't make much sense in this context.
1420 */
1421 function make_link(text, url) {
1422 var textNode = null;
1423 var link = null;
1424 if (typeof text !== "undefined" && text !== null) textNode = document.createTextNode(text);
1425 if (typeof url === "string") {
1426 if (url.indexOf("//") == -1) {
1427 url = "http://" + url;
1428 }
1429 link = document.createElement('a');
1430 link.href = url;
1431 if (textNode) link.appendChild(textNode);
1432 return link;
1433 }
1434 return textNode;
1435 }
1436 </script>
1437 <script>
1438 //
1439 // return true if any part of the passed element is visible in the viewport
1440 //
1441 function element_in_viewport(elem) {
1442 var rect;
1443 try {
1444 rect = elem.getBoundingClientRect();
1445 } catch (e) {
1446 return false;
1447 }
1448 return (
1449 rect.top < (window.innerHeight || document.body.clientHeight) &&
1450 rect.bottom > 0 &&
1451 rect.left < (window.innerWidth || document.body.clientWidth) &&
1452 rect.right > 0
1453 );
1454 }
1455
1456 //
1457 // Functions to delay a drawing task until it is required or it would not lag the display to do so
1458 //
1459
1460 // a list of items still to be drawn
1461 var drawable_list = [];
1462 // the delay between drawing objects that are not currently visible
1463 var draw_delay = 1;
1464 // the delay after a user interaction
1465 var user_delay = 300;
1466 // the delay after a user has stopped scrolling and is viewing the stuff drawn on the current page
1467 var stop_delay = 300;
1468 // the timer handle; allows resetting of the timer after user interactions
1469 var draw_timer = null;
1470
1471 //
1472 // Drawable
1473 //
1474 // elem - a page element which defines the position on the page that drawing is to be done
1475 // task - an object with the method run which takes care of painting the object
1476 //
1477 var Drawable = function(elem, task) {
1478 this.elem = elem;
1479 this.task = task;
1480 }
1481
1482 //
1483 // Drawable.is_visible
1484 //
1485 // Determines if the element is visible in the viewport
1486 //
1487 Drawable.prototype.is_visible = function() {
1488 return element_in_viewport(this.elem);
1489 }
1490
1491 //
1492 // Drawable.run
1493 //
1494 // Run the task held by the drawable
1495 Drawable.prototype.run = function() {
1496 if (this.task) this.task.run();
1497 this.task = null;
1498 }
1499
1500 //
1501 // Drawable.run
1502 //
1503 // Run the task iff visible
1504 // returns true if the task ran or has already run
1505 Drawable.prototype.run_visible = function() {
1506 if (this.task) {
1507 if (element_in_viewport(this.elem)) {
1508 this.task.run();
1509 this.task = null;
1510 return true;
1511 }
1512 return false;
1513 } else {
1514 return true;
1515 }
1516 }
1517
1518 //
1519 // draw_on_screen
1520 //
1521 // Checks each drawable object and draws those on screen.
1522 //
1523 function draw_on_screen() {
1524 var found = false;
1525 for (var i = 0; i < drawable_list.length; i++) {
1526 if (drawable_list[i].run_visible()) {
1527 drawable_list.splice(i--, 1);
1528 found = true;
1529 }
1530 }
1531 return found;
1532 }
1533
1534 //
1535 // process_draw_tasks
1536 //
1537 // Called on a delay to process the next avaliable
1538 // draw task.
1539 //
1540 function process_draw_tasks() {
1541 var delay = draw_delay;
1542 draw_timer = null;
1543 if (drawable_list.length == 0) return; //no more tasks
1544 if (draw_on_screen()) {
1545 delay = stop_delay; //give the user a chance to scroll
1546 } else {
1547 //get next task
1548 var drawable = drawable_list.shift();
1549 drawable.task.run();
1550 }
1551 //allow UI updates between tasks
1552 draw_timer = window.setTimeout("process_draw_tasks()", delay);
1553 }
1554
1555 //
1556 // delayed_process_draw_tasks
1557 //
1558 // Call process_draw_tasks after a short delay.
1559 // The delay serves to group multiple redundant events.
1560 // Should be set as event handler for onscroll and onresize.
1561 //
1562 function delayed_process_draw_tasks() {
1563 //reset the timer
1564 if (drawable_list.length > 0) {
1565 if (draw_timer != null) clearTimeout(draw_timer);
1566 draw_timer = window.setTimeout("process_draw_tasks()", user_delay);
1567 }
1568 }
1569
1570 //
1571 // add_draw_task
1572 //
1573 // Add a drawing task to be called immediately if it is
1574 // visible, or to be called on a delay to reduce stuttering
1575 // effect on the web browser.
1576 function add_draw_task(elem, task) {
1577 drawable = new Drawable(elem, task);
1578 if (drawable.is_visible()) {
1579 task.run();
1580 } else {
1581 drawable_list.push(drawable);
1582 //reset timer
1583 if (draw_timer != null) clearTimeout(draw_timer);
1584 draw_timer = window.setTimeout("process_draw_tasks()", user_delay);
1585 }
1586 }
1587
1588 </script>
1589 <script>
1590 //======================================================================
1591 // start Alphabet object
1592 //======================================================================
1593 var Alphabet = function(alphabet, background) {
1594 "use strict";
1595 var i, j, sym, aliases, complement, comp_e_sym, ambigs, generate_background;
1596 generate_background = (background == null);
1597 if (generate_background) {
1598 background = [];
1599 for (i = 0; i < alphabet.ncore; i++) background[i] = 1.0 / alphabet.ncore;
1600 } else if (alphabet.ncore != background.length) {
1601 throw new Error("The background length does not match the alphabet length.");
1602 }
1603 this.name = alphabet.name;
1604 this.like = (alphabet.like != null ? alphabet.like.toUpperCase() : null);
1605 this.ncore = alphabet.ncore;
1606 this.symbols = alphabet.symbols;
1607 this.background = background;
1608 this.genbg = generate_background;
1609 this.encode = {};
1610 this.encode2core = {};
1611 this.complement = {};
1612 // check if all symbols are same case
1613 var seen_uc = false;
1614 var seen_lc = false;
1615 var check_case = function (syms) {
1616 var s, sym;
1617 if (typeof syms === "string") {
1618 for (s = 0; s < syms.length; s++) {
1619 sym = syms.charAt(s);
1620 if (sym >= 'a' && sym <= 'z') seen_lc = true;
1621 else if (sym >= 'A' && sym <= 'Z') seen_uc = true;
1622 }
1623 }
1624 };
1625 for (i = 0; i < this.symbols.length; i++) {
1626 check_case(this.symbols[i].symbol);
1627 check_case(this.symbols[i].aliases);
1628 }
1629 // now map symbols to indexes
1630 var update_array = function(array, syms, index) {
1631 var s, sym;
1632 if (typeof syms === "string") {
1633 for (s = 0; s < syms.length; s++) {
1634 sym = syms.charAt(s);
1635 array[sym] = index;
1636 // when only a single case is used, then encode as case insensitive
1637 if (seen_uc != seen_lc) {
1638 if (sym >= 'a' && sym <= 'z') {
1639 array[sym.toUpperCase()] = index;
1640 } else if (sym >= 'A' && sym <= 'Z') {
1641 array[sym.toLowerCase()] = index;
1642 }
1643 }
1644 }
1645 }
1646 }
1647 // map core symbols to index
1648 for (i = 0; i < this.ncore; i++) {
1649 update_array(this.encode2core, this.symbols[i].symbol, i);
1650 update_array(this.encode, this.symbols[i].symbol, i);
1651 update_array(this.encode2core, this.symbols[i].aliases, i);
1652 update_array(this.encode, this.symbols[i].aliases, i);
1653 }
1654 // map ambigous symbols to index
1655 ambigs = {};
1656 for (i = this.ncore; i < this.symbols.length; i++) {
1657 update_array(this.encode, this.symbols[i].symbol, i);
1658 update_array(this.encode, this.symbols[i].aliases, i);
1659 ambigs[this.symbols[i].equals] = i;
1660 }
1661 // determine complements
1662 for (i = 0; i < this.ncore; i++) {
1663 complement = this.symbols[i].complement;
1664 if (typeof complement === "string") {
1665 this.complement[i] = this.encode2core[complement];
1666 }
1667 }
1668 next_symbol:
1669 for (i = this.ncore; i < this.symbols.length; i++) {
1670 complement = "";
1671 for (j = 0; j < this.symbols[i].equals.length; j++) {
1672 comp_e_sym = this.complement[this.encode2core[this.symbols[i].equals.charAt(j)]];
1673 if (typeof comp_e_sym !== "number") continue next_symbol;
1674 complement += this.symbols[comp_e_sym].symbol;
1675 }
1676 complement = complement.split("").sort().join("");
1677 if (typeof ambigs[complement] === "number") {
1678 this.complement[i] = ambigs[complement];
1679 }
1680 }
1681 // determine case insensitivity
1682 this.case_insensitive = true;
1683 if (seen_uc == seen_lc) {
1684 // when there is a mixture of cases it probably won't
1685 // be case insensitive but we still need to check
1686 loop:
1687 for (i = 0; i < this.symbols.length; i++) {
1688 sym = this.symbols[i].symbol;
1689 if (sym >= 'A' && sym <= 'Z') {
1690 if (this.encode[sym.toLowerCase()] != i) {
1691 this.case_insensitive = false;
1692 break loop;
1693 }
1694 } else if (sym >= 'a' && sym <= 'z') {
1695 if (this.encode[sym.toUpperCase()] != i) {
1696 this.case_insensitive = false;
1697 break loop;
1698 }
1699 }
1700 aliases = this.symbols[i].aliases;
1701 if (aliases != null) {
1702 for (j = 0; j < aliases.length; j++) {
1703 sym = aliases.charAt(j);
1704 if (sym >= 'A' && sym <= 'Z') {
1705 if (this.encode[sym.toLowerCase()] != i) {
1706 this.case_insensitive = false;
1707 break loop;
1708 }
1709 } else if (sym >= 'a' && sym <= 'z') {
1710 if (this.encode[sym.toUpperCase()] != i) {
1711 this.case_insensitive = false;
1712 break loop;
1713 }
1714 }
1715 }
1716 }
1717 }
1718 }
1719 // normalise aliases to remove the prime symbol and eliminate
1720 // the alternate cases when the alphabet is case insensitive
1721 var seen, out;
1722 for (i = 0; i < this.symbols.length; i++) {
1723 sym = this.symbols[i].symbol;
1724 aliases = this.symbols[i].aliases;
1725 if (typeof aliases != "string") aliases = "";
1726 seen = {};
1727 out = [];
1728 if (this.case_insensitive) {
1729 sym = sym.toUpperCase();
1730 aliases = aliases.toUpperCase();
1731 }
1732 seen[sym] = true;
1733 for (j = 0; j < aliases.length; j++) {
1734 if (!seen[aliases.charAt(j)]) {
1735 seen[aliases.charAt(j)] = true;
1736 out.push(aliases.charAt(j));
1737 }
1738 }
1739 this.symbols[i].aliases = out.sort().join("");
1740 }
1741 };
1742 // return the name of the alphabet
1743 Alphabet.prototype.get_alphabet_name = function() {
1744 return this.name;
1745 };
1746 // return if the alphabet can be complemented
1747 Alphabet.prototype.has_complement = function() {
1748 return (typeof this.symbols[0].complement === "string");
1749 };
1750 // return true if an uppercase letter has the same meaning as the lowercase form
1751 Alphabet.prototype.is_case_insensitive = function() {
1752 return this.case_insensitive;
1753 };
1754 // return the information content of an alphabet letter
1755 Alphabet.prototype.get_ic = function() {
1756 return Math.log(this.ncore) / Math.LN2;
1757 };
1758 // return the count of the core alphabet symbols
1759 Alphabet.prototype.get_size_core = function() {
1760 return this.ncore;
1761 };
1762 // return the count of all alphabet symbols
1763 Alphabet.prototype.get_size_full = function() {
1764 return this.symbols.length;
1765 };
1766 // return the symbol for the given alphabet index
1767 Alphabet.prototype.get_symbol = function(alph_index) {
1768 "use strict";
1769 if (alph_index < 0 || alph_index >= this.symbols.length) {
1770 throw new Error("Alphabet index out of bounds");
1771 }
1772 return this.symbols[alph_index].symbol;
1773 };
1774 // return the aliases for the given alphabet index
1775 Alphabet.prototype.get_aliases = function(alph_index) {
1776 "use strict";
1777 if (alph_index < 0 || alph_index >= this.symbols.length) {
1778 throw new Error("Alphabet index out of bounds");
1779 }
1780 var sym_obj = this.symbols[alph_index];
1781 return (sym_obj.aliases != null ? sym_obj.aliases : "");
1782 };
1783 // return the name for the given alphabet index
1784 Alphabet.prototype.get_name = function(alph_index) {
1785 "use strict";
1786 var sym;
1787 if (alph_index < 0 || alph_index >= this.symbols.length) {
1788 throw new Error("Alphabet index out of bounds");
1789 }
1790 sym = this.symbols[alph_index];
1791 return (typeof sym.name === "string" ? sym.name : sym.symbol);
1792 };
1793 // return the alphabet it is like or null
1794 Alphabet.prototype.get_like = function() {
1795 "use strict";
1796 return this.like;
1797 };
1798 // return the index of the complement for the given alphabet index
1799 Alphabet.prototype.get_complement = function(alph_index) {
1800 var comp_e_sym = this.complement[alph_index];
1801 if (typeof comp_e_sym === "number") {
1802 return comp_e_sym;
1803 } else {
1804 return -1;
1805 }
1806 };
1807 // return a string containing the core symbols
1808 Alphabet.prototype.get_symbols = function() {
1809 "use strict";
1810 var i, core_symbols;
1811 core_symbols = "";
1812 for (i = 0; i < this.ncore; i++) {
1813 core_symbols += this.symbols[i].symbol;
1814 }
1815 return core_symbols;
1816 };
1817 // return if the background was not a uniform generated background
1818 Alphabet.prototype.has_bg = function() {
1819 "use strict";
1820 return !this.genbg;
1821 };
1822 // get the background frequency for the index
1823 Alphabet.prototype.get_bg_freq = function(alph_index) {
1824 "use strict";
1825 var freq, i, symbols;
1826 if (alph_index >= 0) {
1827 if (alph_index < this.ncore) {
1828 return this.background[alph_index];
1829 } else if (alph_index < this.symbols.length) {
1830 freq = 0;
1831 symbols = this.symbols[alph_index].equals;
1832 for (i = 0; i < symbols.length; i++) {
1833 freq += this.background[this.encode2core[symbols.charAt(i)]];
1834 }
1835 return freq;
1836 }
1837 }
1838 throw new Error("The alphabet index is out of range.");
1839 };
1840 // get the colour of the index
1841 Alphabet.prototype.get_colour = function(alph_index) {
1842 "use strict";
1843 if (alph_index < 0 || alph_index >= this.symbols.length) {
1844 throw new Error("BAD_ALPHABET_INDEX");
1845 }
1846 if (typeof this.symbols[alph_index].colour != "string") {
1847 return "black";
1848 }
1849 return "#" + this.symbols[alph_index].colour;
1850 };
1851 // get the rgb componets of the colour at the index
1852 Alphabet.prototype.get_rgb = function(alph_index) {
1853 "use strict";
1854 if (alph_index < 0 || alph_index >= this.symbols.length) {
1855 throw new Error("BAD_ALPHABET_INDEX");
1856 }
1857 if (typeof this.symbols[alph_index].colour != "string") {
1858 return {"red": 0, "green": 0, "blue": 0};
1859 }
1860 var colour = this.symbols[alph_index].colour;
1861 var red = parseInt(colour.substr(0, 2), 16) / 255;
1862 var green = parseInt(colour.substr(2, 2), 16) / 255;
1863 var blue = parseInt(colour.substr(4, 2), 16) / 255;
1864 return {"red": red, "green": green, "blue": blue};
1865 };
1866 // convert a symbol into the index
1867 Alphabet.prototype.get_index = function(letter) {
1868 "use strict";
1869 var alph_index;
1870 alph_index = this.encode[letter];
1871 if (typeof alph_index === "undefined") {
1872 return -1;
1873 }
1874 return alph_index;
1875 };
1876 // convert a symbol into the list of core indexes that it equals
1877 Alphabet.prototype.get_indexes = function(letter) {
1878 "use strict";
1879 var alph_index, comprise_str, i, comprise_list;
1880 alph_index = this.encode[letter];
1881 if (typeof alph_index === "undefined") {
1882 throw new Error("Unknown letter");
1883 }
1884 comprise_str = this.symbols[alph_index].equals;
1885 comprise_list = [];
1886 if (typeof comprise_str == "string") {
1887 for (i = 0; i < comprise_str.length; i++) {
1888 comprise_list.push(this.encode2core[comprise_str.charAt(i)]);
1889 }
1890 } else {
1891 comprise_list.push(alph_index);
1892 }
1893 return comprise_list;
1894 };
1895 // check if a symbol is the primary way of representing the symbol in the alphabet
1896 Alphabet.prototype.is_prime_symbol = function(letter) {
1897 var alph_index;
1898 alph_index = this.encode[letter];
1899 if (alph_index == null) return false;
1900 if (this.is_case_insensitive()) {
1901 return (this.symbols[alph_index].symbol.toUpperCase() == letter.toUpperCase());
1902 } else {
1903 return (this.symbols[alph_index].symbol == letter);
1904 }
1905 };
1906 // compare 2 alphabets
1907 Alphabet.prototype.equals = function(other) {
1908 "use strict";
1909 var i, sym1, sym2;
1910 // first check that it's actually an alphabet object
1911 if (!(typeof other === "object" && other != null && other instanceof Alphabet)) {
1912 return false;
1913 }
1914 // second shortcircuit if it's the same object
1915 if (this === other) return true;
1916 // compare
1917 if (this.name !== other.name) return false;
1918 if (this.ncore !== other.ncore) return false;
1919 if (this.symbols.length !== other.symbols.length) return false;
1920 for (i = 0; i < this.symbols.length; i++) {
1921 sym1 = this.symbols[i];
1922 sym2 = other.symbols[i];
1923 if (sym1.symbol !== sym2.symbol) return false;
1924 if (sym1.aliases !== sym2.aliases) return false;
1925 if (sym1.name !== sym2.name) return false;
1926 if (typeof sym1.colour !== typeof sym2.colour ||
1927 (typeof sym1.colour === "string" && typeof sym2.colour === "string" &&
1928 parseInt(sym1.colour, 16) != parseInt(sym2.colour, 16))) {
1929 return false;
1930 }
1931 if (sym1.complement !== sym2.complement) return false;
1932 if (sym1.equals !== sym2.equals) return false;
1933 }
1934 return true;
1935 };
1936 Alphabet.prototype.check_core_subset = function(super_alph) {
1937 var complement_same = true;
1938 var seen_set = {};
1939 var sub_i, sub_symbol, super_i, super_symbol;
1940 for (sub_i = 0; sub_i < this.ncore; sub_i++) {
1941 sub_symbol = this.symbols[sub_i];
1942 super_i = super_alph.encode[sub_symbol.symbol];
1943 if (super_i == null) return 0;
1944 super_symbol = super_alph.symbols[super_i];
1945 if (seen_set[super_i]) return 0;
1946 seen_set[super_i] = true;
1947 // check complement
1948 if (sub_symbol.complement != null && super_symbol.complement != null) {
1949 if (super_alph.encode[sub_symbol.complement] != super_alph.encode[super_symbol.complement]) {
1950 complement_same = false;
1951 }
1952 } else if (sub_symbol.complement != null || super_symbol.complement != null) {
1953 complement_same = false;
1954 }
1955 }
1956 return (complement_same ? 1 : -1);
1957 };
1958 // convert a sequence to its reverse complement
1959 Alphabet.prototype.invcomp_seq = function(seq) {
1960 "use strict";
1961 var syms, i, e_sym, comp_e_sym;
1962 if (!this.has_complement()) throw new Error("Alphabet must be complementable");
1963 syms = seq.split("");
1964 for (i = 0; i < syms.length; i++) {
1965 e_sym = this.encode[syms[i]];
1966 if (typeof e_sym === "undefined") {
1967 e_sym = this.ncore; // wildcard
1968 }
1969 comp_e_sym = this.complement[e_sym];
1970 if (typeof comp_e_sym === "undefined") {
1971 comp_e_sym = e_sym; // not complementable
1972 }
1973 syms[i] = this.symbols[comp_e_sym].symbol;
1974 }
1975 return syms.reverse().join("");
1976 };
1977 // convert the alphabet to the text version
1978 Alphabet.prototype.as_text = function() {
1979 "use strict";
1980 function name_as_text(name) {
1981 var i, c, out;
1982 out = "\"";
1983 for (i = 0; i < name.length; i++) {
1984 c = name.charAt(i);
1985 if (c == "\"") {
1986 out += "\\\"";
1987 } else if (c == "/") {
1988 out += "\\/";
1989 } else if (c == "\\") {
1990 out += "\\\\";
1991 } else {
1992 out += c;
1993 }
1994 }
1995 out += "\"";
1996 return out;
1997 }
1998 function symbol_as_text(sym) {
1999 var out;
2000 out = sym.symbol;
2001 if (typeof sym.name === "string" && sym.name != sym.symbol) {
2002 out += " " + name_as_text(sym.name);
2003 }
2004 if (typeof sym.colour === "string") {
2005 out += " " + sym.colour;
2006 }
2007 return out;
2008 }
2009 var out, i, j, c, sym;
2010 out = "";
2011 // output core symbols with 2 way complements
2012 for (i = 0; i < this.ncore; i++) {
2013 c = this.complement[i];
2014 if (typeof c === "number" && i < c && this.complement[c] === i) {
2015 out += symbol_as_text(this.symbols[i]) + " ~ " + symbol_as_text(this.symbols[c]) + "\n";
2016 }
2017 }
2018 // output core symbols with no complement
2019 for (i = 0; i < this.ncore; i++) {
2020 if (typeof this.complement[i] === "undefined") {
2021 out += symbol_as_text(this.symbols[i]) + "\n";
2022 }
2023 }
2024 // output ambiguous symbols that have comprising characters
2025 for (i = this.ncore; i < this.symbols.length; i++) {
2026 if (this.symbols[i].equals.length == 0) break;
2027 out += symbol_as_text(this.symbols[i]) + " = " + this.symbols[i].equals + "\n";
2028 if (typeof this.symbols[i].aliases === "string") {
2029 for (j = 0; j < this.symbols[i].aliases.length; j++) {
2030 if (this.symbols[i].aliases.charAt(j) == this.symbols[i].symbol) continue;
2031 out += this.symbols[i].aliases.charAt(j) + " = " + this.symbols[i].equals + "\n";
2032 }
2033 }
2034 }
2035 // output aliases of core symbols
2036 for (i = 0; i < this.ncore; i++) {
2037 if (typeof this.symbols[i].aliases === "string") {
2038 for (j = 0; j < this.symbols[i].aliases.length; j++) {
2039 if (this.symbols[i].aliases.charAt(j) == this.symbols[i].symbol) continue;
2040 out += this.symbols[i].aliases.charAt(j) + " = " + this.symbols[i].symbol + "\n";
2041 }
2042 }
2043 }
2044 // output gap symbols
2045 i = this.symbols.length - 1;
2046 if (this.symbols[i].equals.length == 0) {
2047 out += symbol_as_text(this.symbols[i]) + " =\n";
2048 if (typeof this.symbols[i].aliases === "string") {
2049 for (j = 0; j < this.symbols[i].aliases.length; j++) {
2050 if (this.symbols[i].aliases.charAt(j) == this.symbols[i].symbol) continue;
2051 out += this.symbols[i].aliases.charAt(j) + " =\n";
2052 }
2053 }
2054 }
2055 return out;
2056 };
2057 // output the alphabet as it appears in minimal MEME format
2058 Alphabet.prototype.as_meme = function() {
2059 "use strict";
2060 function name_as_text(name) {
2061 var i, c, out;
2062 out = "\"";
2063 for (i = 0; i < name.length; i++) {
2064 c = name.charAt(i);
2065 if (c == "\"") {
2066 out += "\\\"";
2067 } else if (c == "/") {
2068 out += "\\/";
2069 } else if (c == "\\") {
2070 out += "\\\\";
2071 } else {
2072 out += c;
2073 }
2074 }
2075 out += "\"";
2076 return out;
2077 }
2078 if (this.equals(AlphStd.DNA)) {
2079 return "ALPHABET= ACGT\n";
2080 } else if (this.equals(AlphStd.PROTEIN)) {
2081 return "ALPHABET= ACDEFGHIKLMNPQRSTVWY\n";
2082 } else {
2083 return "ALPHABET" +
2084 (this.name != null ? " " + name_as_text(this.name) : "") +
2085 (this.like != null ? " " + this.like + "-LIKE" : "") + "\n" +
2086 this.as_text() + "END ALPHABET\n";
2087 }
2088 };
2089
2090 // Returns a table showing all the letters in the alphabet
2091 Alphabet.prototype.as_table = function() {
2092 "use strict";
2093 var i, j, row, th, td, aliases, equals, sym;
2094 var table = document.createElement("table");
2095 // create the core symbol header
2096 row = table.insertRow(table.rows.length);
2097 th = document.createElement("th");
2098 th.appendChild(document.createTextNode("Symbol(s)"));
2099 row.appendChild(th);
2100 th = document.createElement("th");
2101 th.appendChild(document.createTextNode("Name"));
2102 row.appendChild(th);
2103 th = document.createElement("th");
2104 if (this.has_complement()) {
2105 th.appendChild(document.createTextNode("Complement"));
2106 }
2107 row.appendChild(th);
2108 // list the core symbols
2109 for (i = 0; i < this.ncore; i++) {
2110 row = table.insertRow(table.rows.length);
2111 td = document.createElement("td");
2112 if (this.symbols[i].colour != null) {
2113 td.style.color = '#' + this.symbols[i].colour;
2114 }
2115 td.appendChild(document.createTextNode(this.symbols[i].symbol));
2116 aliases = this.get_aliases(i);
2117 if (aliases.length > 0) {
2118 td.appendChild(document.createTextNode(' ' + aliases.split('').join(' ')));
2119 }
2120 row.appendChild(td);
2121 td = document.createElement("td");
2122 if (this.symbols[i].name != null) {
2123 td.appendChild(document.createTextNode(this.symbols[i].name));
2124 }
2125 row.appendChild(td);
2126 td = document.createElement("td");
2127 if (this.symbols[i].complement != null) {
2128 td.style.color = this.get_colour(this.get_index(this.symbols[i].complement));
2129 td.appendChild(document.createTextNode(this.symbols[i].complement));
2130 }
2131 row.appendChild(td);
2132 }
2133 // create the ambiguous symbol header
2134 row = table.insertRow(table.rows.length);
2135 th = document.createElement("th");
2136 th.appendChild(document.createTextNode("Symbol(s)"));
2137 row.appendChild(th);
2138 th = document.createElement("th");
2139 th.appendChild(document.createTextNode("Name"));
2140 row.appendChild(th);
2141 th = document.createElement("th");
2142 th.appendChild(document.createTextNode("Matches"));
2143 row.appendChild(th);
2144 // list the ambiguous symbols
2145 for (i = this.ncore; i < this.symbols.length; i++) {
2146 row = table.insertRow(table.rows.length);
2147 td = document.createElement("td");
2148 if (this.symbols[i].colour != null) {
2149 td.style.color = '#' + this.symbols[i].colour;
2150 }
2151 td.appendChild(document.createTextNode(this.symbols[i].symbol));
2152 aliases = this.get_aliases(i);
2153 if (aliases.length > 0) {
2154 td.appendChild(document.createTextNode(' ' + aliases.split('').join(' ')));
2155 }
2156 row.appendChild(td);
2157 td = document.createElement("td");
2158 if (this.symbols[i].name != null) {
2159 td.appendChild(document.createTextNode(this.symbols[i].name));
2160 }
2161 row.appendChild(td);
2162 td = document.createElement("td");
2163 equals = this.symbols[i].equals.split('');
2164 for (j = 0; j < equals.length; j++) {
2165 if (j != 0) td.appendChild(document.createTextNode(' '));
2166 sym = document.createElement("span");
2167 sym.style.color = this.get_colour(this.get_index(equals[j]));
2168 sym.appendChild(document.createTextNode(equals[j]));
2169 td.appendChild(sym);
2170 }
2171 row.appendChild(td);
2172 }
2173 return table;
2174 };
2175
2176 // returns a dictionary of the colours for EPS
2177 Alphabet.prototype._as_eps_dict = function() {
2178 "use strict";
2179 var i, sym, rgb;
2180 var out = "/fullColourDict <<\n";
2181 for (i = 0; i < this.ncore; i++) {
2182 sym = this.get_symbol(i);
2183 sym = sym.replace(/\\/g, "\\\\");
2184 sym = sym.replace(/\(/g, "\\(");
2185 sym = sym.replace(/\)/g, "\\)");
2186 rgb = this.get_rgb(i);
2187 out += " (" + sym + ") [" + rgb.red.toFixed(4) + " " + rgb.green.toFixed(4) + " " + rgb.blue.toFixed(4) + "]\n";
2188 }
2189 out += ">> def\n";
2190 out += "/mutedColourDict <<\n";
2191 for (i = 0; i < this.ncore; i++) {
2192 sym = this.get_symbol(i);
2193 sym = sym.replace(/\\/g, "\\\\");
2194 sym = sym.replace(/\(/g, "\\(");
2195 sym = sym.replace(/\)/g, "\\)");
2196 rgb = Alphabet.lighten_colour(this.get_rgb(i));
2197 out += " (" + sym + ") [" + rgb.red.toFixed(4) + " " + rgb.green.toFixed(4) + " " + rgb.blue.toFixed(4) + "]\n";
2198 }
2199 out += ">> def\n";
2200 return out;
2201 };
2202
2203 // return the alphabet name or a list of primary symbols
2204 Alphabet.prototype.toString = function() {
2205 "use strict";
2206 if (this.name != null) {
2207 return this.name;
2208 } else {
2209 return this.get_symbols();
2210 }
2211 };
2212
2213 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2214 // Helper functions
2215 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2216
2217 // Convert a colour specified in RGB colourspace values into LAB colourspace
2218 Alphabet.rgb2lab = function(rgb) {
2219 "use strict";
2220 var xyzHelper, labHelper;
2221 // XYZ helper
2222 xyzHelper = function(value) {
2223 if (value > 0.0445) {
2224 value = (value + 0.055) / 1.055;
2225 value = Math.pow(value, 2.4);
2226 } else {
2227 value /= 12.92;
2228 }
2229 value *= 100;
2230 return value;
2231 };
2232 // lab helper
2233 labHelper = function(value) {
2234 if (value > 0.008856) {
2235 value = Math.pow(value, 1.0 / 3.0);
2236 } else {
2237 value = (7.787 * value) + (16.0 / 116.0);
2238 }
2239 return value;
2240 };
2241 // convert into XYZ colourspace
2242 var c1, c2, c3;
2243 if (typeof rgb == "number") {
2244 c1 = xyzHelper(((rgb >> 16) & 0xFF) / 255.0);
2245 c2 = xyzHelper(((rgb >> 8) & 0xFF) / 255.0);
2246 c3 = xyzHelper((rgb & 0xFF) / 255.0);
2247 } else {
2248 c1 = xyzHelper(rgb.red);
2249 c2 = xyzHelper(rgb.green);
2250 c3 = xyzHelper(rgb.blue);
2251 }
2252 var x = (c1 * 0.4124) + (c2 * 0.3576) + (c3 * 0.1805);
2253 var y = (c1 * 0.2126) + (c2 * 0.7152) + (c3 * 0.0722);
2254 var z = (c1 * 0.0193) + (c2 * 0.1192) + (c3 * 0.9505);
2255 // convert into Lab colourspace
2256 c1 = labHelper(x / 95.047);
2257 c2 = labHelper(y / 100.0);
2258 c3 = labHelper(z / 108.883);
2259 var l = (116.0 * c2) - 16;
2260 var a = 500.0 * (c1 - c2);
2261 var b = 200.0 * (c2 - c3);
2262 return {"l": l, "a": a, "b": b};
2263 };
2264
2265 // Convert a colour specified in HSV colourspace into RGB colourspace
2266 Alphabet.hsv2rgb = function(hue, sat, value, output_object) {
2267 // achromatic (grey)
2268 var r = value;
2269 var g = value;
2270 var b = value;
2271 if (sat != 0) {
2272 var h = hue / 60.0;
2273 var i = Math.floor(h);
2274 var f = h - i;
2275 var p = value * (1.0 - sat);
2276 var q = value * (1.0 - (sat * f));
2277 var t = value * (1.0 - (sat * (1.0 - f)));
2278 if (i == 0) {
2279 r = value;
2280 g = t;
2281 b = p;
2282 } else if (i == 1) {
2283 r = q;
2284 g = value;
2285 b = p;
2286 } else if (i == 2) {
2287 r = p;
2288 g = value;
2289 b = t;
2290 } else if (i == 3) {
2291 r = p;
2292 g = q;
2293 b = value;
2294 } else if (i == 4) {
2295 r = t;
2296 g = p;
2297 b = value;
2298 } else {
2299 r = value;
2300 g = p;
2301 b = q;
2302 }
2303 }
2304 if (output_object) {
2305 return {"red": r, "green": g, "blue": b};
2306 } else {
2307 return (Math.floor(r * 255) << 15) | (Math.floor(g * 255) << 8) | (Math.floor(b * 255));
2308 }
2309 };
2310
2311 // Calculate a distance score between two colours in LAB colourspace
2312 Alphabet.lab_dist = function(lab1, lab2) {
2313 var c1 = Math.sqrt((lab1.l * lab1.l) + (lab1.a * lab1.a));
2314 var c2 = Math.sqrt((lab2.l * lab2.l) + (lab2.a * lab2.a));
2315 var dc = c1 - c2;
2316 var dl = lab1.l - lab2.l;
2317 var da = lab1.a - lab2.a;
2318 var db = lab1.b - lab2.b;
2319 // we don't want NaN due to rounding errors so fudge things a bit...
2320 var dh = 0;
2321 var dh_squared = (da * da) + (db * db) - (dc * dc);
2322 if (dh_squared > 0) {
2323 dh = Math.sqrt(dh_squared);
2324 }
2325 var first = dl;
2326 var second = dc / (1.0 + (0.045 * c1));
2327 var third = dh / (1.0 + (0.015 * c1));
2328 return Math.sqrt((first * first) + (second * second) + (third * third));
2329 };
2330
2331 // convert an RGB value into a HSL value
2332 Alphabet.rgb2hsl = function(rgb) {
2333 "use strict";
2334 var min, max, delta, h, s, l, r, g, b;
2335 if (typeof rgb == "number") {
2336 r = ((rgb >> 16) & 0xFF) / 255.0;
2337 g = ((rgb >> 8) & 0xFF) / 255.0;
2338 b = (rgb & 0xFF) / 255.0;
2339 } else {
2340 r = rgb.red;
2341 g = rgb.green;
2342 b = rgb.blue;
2343 }
2344 min = Math.min(r, g, b);
2345 max = Math.max(r, g, b);
2346 delta = max - min;
2347 l = min + (delta / 2);
2348 if (max == min) {
2349 h = 0; // achromatic (grayscale)
2350 s = 0;
2351 } else {
2352 if (l > 0.5) {
2353 s = delta / (2 - max - min);
2354 } else {
2355 s = delta / (max + min);
2356 }
2357 if (max == r) {
2358 h = (g - b) / delta;
2359 if (g < b) h += 6;
2360 } else if (max == g) {
2361 h = ((b - r) / delta) + 2;
2362 } else {
2363 h = ((r - g) / delta) + 4;
2364 }
2365 h /= 6;
2366 }
2367 return {"h": h, "s": s, "l": l};
2368 };
2369
2370 // convert a HSL value into an RGB value
2371 Alphabet.hsl2rgb = function(hsl, output_object) {
2372 "use strict";
2373 function _hue(p, q, t) {
2374 "use strict";
2375 if (t < 0) t += 1;
2376 else if (t > 1) t -= 1;
2377 if (t < (1.0 / 6.0)) {
2378 return p + ((q - p) * 6.0 * t);
2379 } else if (t < 0.5) {
2380 return q;
2381 } else if (t < (2.0 / 3.0)) {
2382 return p + ((q - p) * ((2.0 / 3.0) - t) * 6.0);
2383 } else {
2384 return p;
2385 }
2386 }
2387 var r, g, b, p, q;
2388 if (hsl.s == 0) {
2389 // achromatic (grayscale)
2390 r = hsl.l;
2391 g = hsl.l;
2392 b = hsl.l;
2393 } else {
2394 if (hsl.l < 0.5) {
2395 q = hsl.l * (1 + hsl.s);
2396 } else {
2397 q = hsl.l + hsl.s - (hsl.l * hsl.s);
2398 }
2399 p = (2 * hsl.l) - q;
2400 r = _hue(p, q, hsl.h + (1.0 / 3.0));
2401 g = _hue(p, q, hsl.h);
2402 b = _hue(p, q, hsl.h - (1.0 / 3.0));
2403 }
2404 if (output_object) {
2405 return {"red": r, "green": g, "blue": b};
2406 } else {
2407 return (Math.floor(r * 255) << 15) | (Math.floor(g * 255) << 8) | (Math.floor(b * 255));
2408 }
2409 };
2410
2411 Alphabet.lighten_colour = function(rgb) {
2412 "use strict";
2413 var hsl = Alphabet.rgb2hsl(rgb);
2414 hsl.l += (1.0 - hsl.l) * 2 / 3;
2415 return Alphabet.hsl2rgb(hsl, typeof rgb != "number");
2416 };
2417
2418 //======================================================================
2419 // end Alphabet object
2420 //======================================================================
2421
2422 //======================================================================
2423 // start StandardAlphabet object
2424 //======================================================================
2425
2426 // an extension of the alphabet object to support some additional fields
2427 // only present in standard alphabets.
2428 var StandardAlphabet = function(enum_code, enum_name, alphabet_data) {
2429 Alphabet.apply(this, [alphabet_data]);
2430 this.enum_code = enum_code;
2431 this.enum_name = enum_name;
2432 };
2433 StandardAlphabet.prototype = Alphabet.prototype;
2434 StandardAlphabet.prototype.constructor = StandardAlphabet;
2435
2436 // A unique code for this standard alphabet.
2437 // This code will be a power of 2 to enable creation of bitsets for
2438 // a selection of standard alphabets.
2439 StandardAlphabet.prototype.get_code = function() {
2440 return this.enum_code;
2441 };
2442
2443 // A unique name for this standard alphabet.
2444 // this name will be all upper case and the same as the property that
2445 // refers to this alphabet in the AlphStd collection.
2446 StandardAlphabet.prototype.get_enum = function() {
2447 return this.enum_name;
2448 };
2449
2450 //======================================================================
2451 // end StandardAlphabet object
2452 //======================================================================
2453
2454 // A collection of standard alphabets.
2455 var AlphStd = {
2456 RNA: new StandardAlphabet(1, "RNA", {
2457 "name": "RNA",
2458 "like": "RNA",
2459 "ncore": 4,
2460 "symbols": [
2461 {"symbol": "A", "name": "Adenine", "colour": "CC0000"},
2462 {"symbol": "C", "name": "Cytosine", "colour": "0000CC"},
2463 {"symbol": "G", "name": "Guanine", "colour": "FFB300"},
2464 {"symbol": "U", "name": "Uracil", "colour": "008000",
2465 "aliases": "T"},
2466 {"symbol": "N", "name": "Any base", "equals": "ACGU", "aliases": "X."},
2467 {"symbol": "V", "name": "Not U", "equals": "ACG"},
2468 {"symbol": "H", "name": "Not G", "equals": "ACU"},
2469 {"symbol": "D", "name": "Not C", "equals": "AGU"},
2470 {"symbol": "B", "name": "Not A", "equals": "CGU"},
2471 {"symbol": "M", "name": "Amino", "equals": "AC"},
2472 {"symbol": "R", "name": "Purine", "equals": "AG"},
2473 {"symbol": "W", "name": "Weak", "equals": "AU"},
2474 {"symbol": "S", "name": "Strong", "equals": "CG"},
2475 {"symbol": "Y", "name": "Pyrimidine", "equals": "CU"},
2476 {"symbol": "K", "name": "Keto", "equals": "GU"}
2477 ]
2478 }),
2479 DNA: new StandardAlphabet(2, "DNA", {
2480 "name": "DNA",
2481 "like": "DNA",
2482 "ncore": 4,
2483 "symbols": [
2484 {"symbol": "A", "name": "Adenine", "colour": "CC0000", "complement": "T"},
2485 {"symbol": "C", "name": "Cytosine", "colour": "0000CC", "complement": "G"},
2486 {"symbol": "G", "name": "Guanine", "colour": "FFB300", "complement": "C"},
2487 {"symbol": "T", "name": "Thymine", "colour": "008000", "complement": "A",
2488 "aliases": "U"},
2489 {"symbol": "N", "name": "Any base", "equals": "ACGT", "aliases": "X."},
2490 {"symbol": "V", "name": "Not T", "equals": "ACG"},
2491 {"symbol": "H", "name": "Not G", "equals": "ACT"},
2492 {"symbol": "D", "name": "Not C", "equals": "AGT"},
2493 {"symbol": "B", "name": "Not A", "equals": "CGT"},
2494 {"symbol": "M", "name": "Amino", "equals": "AC"},
2495 {"symbol": "R", "name": "Purine", "equals": "AG"},
2496 {"symbol": "W", "name": "Weak", "equals": "AT"},
2497 {"symbol": "S", "name": "Strong", "equals": "CG"},
2498 {"symbol": "Y", "name": "Pyrimidine", "equals": "CT"},
2499 {"symbol": "K", "name": "Keto", "equals": "GT"}
2500 ]
2501 }),
2502 PROTEIN: new StandardAlphabet(4, "PROTEIN", {
2503 "name": "Protein",
2504 "like": "PROTEIN",
2505 "ncore": 20,
2506 "symbols": [
2507 {"symbol": "A", "name": "Alanine", "colour": "0000CC"},
2508 {"symbol": "C", "name": "Cysteine", "colour": "0000CC"},
2509 {"symbol": "D", "name": "Aspartic acid", "colour": "FF00FF"},
2510 {"symbol": "E", "name": "Glutamic acid", "colour": "FF00FF"},
2511 {"symbol": "F", "name": "Phenylalanine", "colour": "0000CC"},
2512 {"symbol": "G", "name": "Glycine", "colour": "FFB300"},
2513 {"symbol": "H", "name": "Histidine", "colour": "FFCCCC"},
2514 {"symbol": "I", "name": "Isoleucine", "colour": "0000CC"},
2515 {"symbol": "K", "name": "Lysine", "colour": "CC0000"},
2516 {"symbol": "L", "name": "Leucine", "colour": "0000CC"},
2517 {"symbol": "M", "name": "Methionine", "colour": "0000CC"},
2518 {"symbol": "N", "name": "Asparagine", "colour": "008000"},
2519 {"symbol": "P", "name": "Proline", "colour": "FFFF00"},
2520 {"symbol": "Q", "name": "Glutamine", "colour": "008000"},
2521 {"symbol": "R", "name": "Arginine", "colour": "CC0000"},
2522 {"symbol": "S", "name": "Serine", "colour": "008000"},
2523 {"symbol": "T", "name": "Threonine", "colour": "008000"},
2524 {"symbol": "V", "name": "Valine", "colour": "0000CC"},
2525 {"symbol": "W", "name": "Tryptophan", "colour": "0000CC"},
2526 {"symbol": "Y", "name": "Tyrosine", "colour": "33E6CC"},
2527 {"symbol": "X", "name": "Any amino acid", "equals": "ACDEFGHIKLMNPQRSTVWY", "aliases": "*."},
2528 {"symbol": "B", "name": "Asparagine or Aspartic acid", "equals": "DN"},
2529 {"symbol": "Z", "name": "Glutamine or Glutamic acid", "equals": "EQ"},
2530 {"symbol": "J", "name": "Leucine or Isoleucine", "equals": "IL"}
2531 ]
2532 })
2533 };
2534
2535 //======================================================================
2536 // start Symbol object
2537 //======================================================================
2538 var Symbol = function(alph_index, scale, alphabet) {
2539 "use strict";
2540 //variable prototype
2541 this.symbol = alphabet.get_symbol(alph_index);
2542 this.scale = scale;
2543 this.colour = alphabet.get_colour(alph_index);
2544 };
2545
2546 Symbol.prototype.get_symbol = function() {
2547 "use strict";
2548 return this.symbol;
2549 };
2550
2551 Symbol.prototype.get_scale = function() {
2552 "use strict";
2553 return this.scale;
2554 };
2555
2556 Symbol.prototype.get_colour = function() {
2557 "use strict";
2558 return this.colour;
2559 };
2560
2561 Symbol.prototype.toString = function() {
2562 "use strict";
2563 return this.symbol + " " + (Math.round(this.scale*1000)/10) + "%";
2564 };
2565
2566 function compare_symbol(sym1, sym2) {
2567 "use strict";
2568 if (sym1.get_scale() < sym2.get_scale()) {
2569 return -1;
2570 } else if (sym1.get_scale() > sym2.get_scale()) {
2571 return 1;
2572 } else {
2573 return 0;
2574 }
2575 }
2576 //======================================================================
2577 // end Symbol object
2578 //======================================================================
2579
2580 //======================================================================
2581 // start Pspm object
2582 //======================================================================
2583 var Pspm = function(matrix, name, ltrim, rtrim, nsites, evalue, pssm, alt) {
2584 "use strict";
2585 var row, col, data, row_sum, delta, evalue_re;
2586 if (typeof name !== "string") {
2587 name = "";
2588 }
2589 this.name = name;
2590 //construct
2591 if (matrix instanceof Pspm) {
2592 // copy constructor
2593 this.alph_length = matrix.alph_length;
2594 this.motif_length = matrix.motif_length;
2595 this.name = matrix.name;
2596 this.alt = matrix.alt;
2597 this.nsites = matrix.nsites;
2598 this.evalue = matrix.evalue;
2599 this.ltrim = matrix.ltrim;
2600 this.rtrim = matrix.rtrim;
2601 this.pspm = [];
2602 for (row = 0; row < matrix.motif_length; row++) {
2603 this.pspm[row] = [];
2604 for (col = 0; col < matrix.alph_length; col++) {
2605 this.pspm[row][col] = matrix.pspm[row][col];
2606 }
2607 }
2608 if (matrix.pssm != null) {
2609 this.pssm = [];
2610 for (row = 0; row < matrix.motif_length; row++) {
2611 this.pspm[row] = [];
2612 for (col = 0; col < matrix.alph_length; col++) {
2613 this.pssm[row][col] = matrix.pssm[row][col];
2614 }
2615 }
2616 }
2617 } else {
2618 // check parameters
2619 if (ltrim == null) {
2620 ltrim = 0;
2621 } else if (typeof ltrim !== "number" || ltrim % 1 !== 0 || ltrim < 0) {
2622 throw new Error("ltrim must be a non-negative integer, got: " + ltrim);
2623 }
2624 if (rtrim == null) {
2625 rtrim = 0;
2626 } else if (typeof rtrim !== "number" || rtrim % 1 !== 0 || rtrim < 0) {
2627 throw new Error("rtrim must be a non-negative integer, got: " + rtrim);
2628 }
2629 if (nsites != null) {
2630 if (typeof nsites !== "number" || nsites < 0) {
2631 throw new Error("nsites must be a positive number, got: " + nsites);
2632 } else if (nsites == 0) {
2633 nsites = null;
2634 }
2635 }
2636 if (evalue != null) {
2637 if (typeof evalue === "number") {
2638 if (evalue < 0) {
2639 throw new Error("evalue must be a non-negative number, got: " + evalue);
2640 }
2641 } else if (typeof evalue === "string") {
2642 evalue_re = /^((?:[+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)|inf)$/;
2643 if (!evalue_re.test(evalue)) {
2644 throw new Error("evalue must be a non-negative number, got: " + evalue);
2645 }
2646 } else {
2647 throw new Error("evalue must be a non-negative number, got: " + evalue);
2648 }
2649 }
2650 // set properties
2651 this.name = name;
2652 this.alt = alt;
2653 this.nsites = nsites;
2654 this.evalue = evalue;
2655 this.ltrim = ltrim;
2656 this.rtrim = rtrim;
2657 if (typeof matrix === "string") {
2658 // string constructor
2659 data = parse_pspm_string(matrix);
2660 this.alph_length = data["alph_length"];
2661 this.motif_length = data["motif_length"];
2662 this.pspm = data["pspm"];
2663 if (this.evalue == null) {
2664 if (data["evalue"] != null) {
2665 this.evalue = data["evalue"];
2666 } else {
2667 this.evalue = 0;
2668 }
2669 }
2670 if (this.nsites == null) {
2671 if (typeof data["nsites"] === "number") {
2672 this.nsites = data["nsites"];
2673 } else {
2674 this.nsites = 20;
2675 }
2676 }
2677 } else {
2678 // assume pspm is a nested array
2679 this.motif_length = matrix.length;
2680 this.alph_length = (matrix.length > 0 ? matrix[0].length : 0);
2681 if (this.nsites == null) {
2682 this.nsites = 20;
2683 }
2684 if (this.evalue == null) {
2685 this.evalue = 0;
2686 }
2687 this.pspm = [];
2688 // copy pspm and check
2689 for (row = 0; row < this.motif_length; row++) {
2690 if (this.alph_length != matrix[row].length) {
2691 throw new Error("COLUMN_MISMATCH");
2692 }
2693 this.pspm[row] = [];
2694 row_sum = 0;
2695 for (col = 0; col < this.alph_length; col++) {
2696 this.pspm[row][col] = matrix[row][col];
2697 row_sum += this.pspm[row][col];
2698 }
2699 delta = 0.1;
2700 if (isNaN(row_sum) || (row_sum > 1 && (row_sum - 1) > delta) ||
2701 (row_sum < 1 && (1 - row_sum) > delta)) {
2702 throw new Error("INVALID_SUM");
2703 }
2704 }
2705 // copy pssm
2706 if (pssm != null) {
2707 this.pssm = [];
2708 for (row = 0; row < this.motif_length; row++) {
2709 this.pssm[row] = [];
2710 for (col = 0; col < this.alph_length; col++) {
2711 this.pssm[row][col] = pssm[row][col];
2712 }
2713 }
2714 }
2715 }
2716 }
2717 };
2718
2719 Pspm.prototype.copy = function() {
2720 "use strict";
2721 return new Pspm(this);
2722 };
2723
2724 Pspm.prototype.reverse = function() {
2725 "use strict";
2726 var x, y, temp, temp_trim;
2727 //reverse
2728 x = 0;
2729 y = this.motif_length-1;
2730 while (x < y) {
2731 temp = this.pspm[x];
2732 this.pspm[x] = this.pspm[y];
2733 this.pspm[y] = temp;
2734 x++;
2735 y--;
2736 }
2737 // reverse pssm (if defined)
2738 if (typeof this.pssm !== "undefined") {
2739 //reverse
2740 x = 0;
2741 y = this.motif_length-1;
2742 while (x < y) {
2743 temp = this.pssm[x];
2744 this.pspm[x] = this.pssm[y];
2745 this.pssm[y] = temp;
2746 x++;
2747 y--;
2748 }
2749 }
2750 //swap triming
2751 temp_trim = this.ltrim;
2752 this.ltrim = this.rtrim;
2753 this.rtrim = temp_trim;
2754 return this; //allow function chaining...
2755 };
2756
2757 Pspm.prototype.reverse_complement = function(alphabet) {
2758 "use strict";
2759 var x, y, temp, i, row, c, temp_trim;
2760 if (this.alph_length != alphabet.get_size_core()) {
2761 throw new Error("The alphabet size does not match the size of the pspm.");
2762 }
2763 if (!alphabet.has_complement()) {
2764 throw new Error("The specified alphabet can not be complemented.");
2765 }
2766 // reverse motif
2767 this.reverse();
2768 //complement
2769 for (x = 0; x < this.motif_length; x++) {
2770 row = this.pspm[x];
2771 for (i = 0; i < row.length; i++) {
2772 c = alphabet.get_complement(i);
2773 if (c < i) continue;
2774 temp = row[i];
2775 row[i] = row[c];
2776 row[c] = temp;
2777 }
2778 }
2779 // complement pssm (if defined)
2780 if (typeof this.pssm !== "undefined") {
2781 //complement
2782 for (x = 0; x < this.motif_length; x++) {
2783 row = this.pssm[x];
2784 for (i = 0; i < row.length; i++) {
2785 c = alphabet.get_complement(i);
2786 if (c < i) continue;
2787 temp = row[i];
2788 row[i] = row[c];
2789 row[c] = temp;
2790 }
2791 }
2792 }
2793 return this; //allow function chaining...
2794 };
2795
2796 Pspm.prototype.get_stack = function(position, alphabet, ssc) {
2797 "use strict";
2798 var row, stack_ic, alphabet_ic, stack, i, sym;
2799 if (this.alph_length != alphabet.get_size_core()) {
2800 throw new Error("The alphabet size does not match the size of the pspm.");
2801 }
2802 row = this.pspm[position];
2803 stack_ic = this.get_stack_ic(position, alphabet);
2804 if (ssc) stack_ic -= this.get_error(alphabet);
2805 alphabet_ic = alphabet.get_ic();
2806 stack = [];
2807 for (i = 0; i < this.alph_length; i++) {
2808 sym = new Symbol(i, row[i]*stack_ic/alphabet_ic, alphabet);
2809 if (sym.get_scale() <= 0) {
2810 continue;
2811 }
2812 stack.push(sym);
2813 }
2814 stack.sort(compare_symbol);
2815 return stack;
2816 };
2817
2818 Pspm.prototype.get_stack_ic = function(position, alphabet) {
2819 "use strict";
2820 var row, H, i;
2821 if (this.alph_length != alphabet.get_size_core()) {
2822 throw new Error("The alphabet size does not match the size fo the pspm.");
2823 }
2824 row = this.pspm[position];
2825 H = 0;
2826 for (i = 0; i < this.alph_length; i++) {
2827 if (row[i] === 0) {
2828 continue;
2829 }
2830 H -= (row[i] * (Math.log(row[i]) / Math.LN2));
2831 }
2832 return alphabet.get_ic() - H;
2833 };
2834
2835 Pspm.prototype.get_error = function(alphabet) {
2836 "use strict";
2837 if (this.nsites === 0) {
2838 return 0;
2839 }
2840 return (alphabet.get_size_core()-1) / (2 * Math.LN2 * this.nsites);
2841 };
2842
2843 Pspm.prototype.get_motif_length = function() {
2844 "use strict";
2845 return this.motif_length;
2846 };
2847
2848 Pspm.prototype.get_alph_length = function() {
2849 "use strict";
2850 return this.alph_length;
2851 };
2852
2853 Pspm.prototype.get_left_trim = function() {
2854 "use strict";
2855 return this.ltrim;
2856 };
2857
2858 Pspm.prototype.get_right_trim = function() {
2859 "use strict";
2860 return this.rtrim;
2861 };
2862
2863 Pspm.prototype.as_best_match = function(alphabet) {
2864 "use strict";
2865 var match, odds, best_odds, best_index;
2866 var i, j;
2867 match = "";
2868 for (i = 0; i < this.motif_length; i++) {
2869 best_index = 0;
2870 best_odds = this.pspm[i][0] / alphabet.get_bg_freq(0);
2871 for (j = 1; j < this.alph_length; j++) {
2872 odds = this.pspm[i][j] / alphabet.get_bg_freq(j);
2873 if (odds > best_odds) {
2874 best_odds = odds;
2875 best_index = j;
2876 }
2877 }
2878 match += alphabet.get_symbol(best_index);
2879 }
2880 return match;
2881 };
2882
2883 Pspm.prototype.as_count_matrix = function() {
2884 "use strict";
2885 var count, count_text, text;
2886 var i, j;
2887 text = "";
2888 for (i = 0; i < this.motif_length; i++) {
2889 if (i !== 0) {
2890 text += "\n";
2891 }
2892 for (j = 0; j < this.alph_length; j++) {
2893 if (j !== 0) {
2894 text += " ";
2895 }
2896 count = Math.round(this.nsites * this.pspm[i][j]);
2897 count_text = "" + count;
2898 // pad up to length of 4
2899 if (count_text.length < 4) {
2900 text += (new Array(5 - count_text.length)).join(" ") + count_text;
2901 } else {
2902 text += count_text;
2903 }
2904 }
2905 }
2906 return text;
2907 };
2908
2909 Pspm.prototype.as_probability_matrix = function() {
2910 "use strict";
2911 var text;
2912 var i, j;
2913 text = "";
2914 for (i = 0; i < this.motif_length; i++) {
2915 if (i !== 0) {
2916 text += "\n";
2917 }
2918 for (j = 0; j < this.alph_length; j++) {
2919 if (j !== 0) {
2920 text += " ";
2921 }
2922 text += this.pspm[i][j].toFixed(6);
2923 }
2924 }
2925 return text;
2926 };
2927
2928 Pspm.prototype.as_score_matrix = function(alphabet, pseudo) {
2929 "use strict";
2930 var me, score, out, row, col, score_text;
2931 me = this;
2932 if (typeof this.pssm === "undefined") {
2933 if (!(typeof alphabet === "object" && alphabet != null && alphabet instanceof Alphabet)) {
2934 throw new Error("The alphabet is required to generate the pssm.");
2935 }
2936 if (typeof pseudo === "undefined") {
2937 pseudo = 0.01;
2938 } else if (typeof pseudo !== "number" || pseudo < 0) {
2939 throw new Error("Expected positive number for pseudocount");
2940 }
2941 score = function(row, col) {
2942 "use strict";
2943 var p, bg, p2;
2944 p = me.pspm[row][col];
2945 bg = alphabet.get_bg_freq(col);
2946 p2 = (p * me.nsites + bg * pseudo) / (me.nsites + pseudo);
2947 return (p2 > 0 ? Math.round((Math.log(p2 / bg) / Math.LN2) * 100) : -10000);
2948 };
2949 } else {
2950 score = function(row, col) {
2951 "use strict";
2952 return me.pssm[row][col];
2953 };
2954 }
2955 out = "";
2956 for (row = 0; row < this.motif_length; row++) {
2957 for (col = 0; col < this.alph_length; col++) {
2958 if (col !== 0) {
2959 out += " ";
2960 }
2961 score_text = "" + score(row, col);
2962 // pad out to 6 characters
2963 if (score_text.length < 6) {
2964 out += (new Array(7 - score_text.length)).join(" ") + score_text;
2965 } else {
2966 out += score_text;
2967 }
2968 }
2969 out += "\n";
2970 }
2971 return out;
2972 }
2973
2974 Pspm.prototype.as_pspm = function() {
2975 "use strict";
2976 return "letter-probability matrix: alength= " + this.alph_length +
2977 " w= " + this.motif_length + " nsites= " + this.nsites +
2978 " E= " + (typeof this.evalue === "number" ?
2979 this.evalue.toExponential() : this.evalue) + "\n" +
2980 this.as_probability_matrix();
2981 };
2982
2983 Pspm.prototype.as_pssm = function(alphabet, pseudo) {
2984 "use strict";
2985 return "log-odds matrix: alength= " + this.alph_length +
2986 " w= " + this.motif_length +
2987 " E= " + (typeof this.evalue == "number" ?
2988 this.evalue.toExponential() : this.evalue) + "\n" +
2989 this.as_score_matrix(alphabet, pseudo);
2990 };
2991
2992 Pspm.prototype.as_meme = function(options) {
2993 var with_header, with_pspm, with_pssm, version, alphabet, bg_source, pseudocount, strands;
2994 var out, alen, i;
2995 // get the options
2996 if (typeof options !== "object" || options === null) {
2997 options = {};
2998 }
2999 with_header = (typeof options["with_header"] === "boolean" ? options["with_header"] : false);
3000 with_pspm = (typeof options["with_pspm"] === "boolean" ? options["with_pspm"] : false);
3001 with_pssm = (typeof options["with_pssm"] === "boolean" ? options["with_pssm"] : false);
3002 if (!with_pspm && !with_pssm) with_pspm = true;
3003 if (with_header) {
3004 if (typeof options["version"] === "string" && /^\d+(?:\.\d+){0,2}$/.test(options["version"])) {
3005 version = options["version"];
3006 } else if (typeof options["version"] === "number") {
3007 version = options["version"].toFixed(0);
3008 } else {
3009 version = "4";
3010 }
3011 if (typeof options["strands"] === "number" && options["strands"] === 1) {
3012 strands = 1;
3013 } else {
3014 strands = 2;
3015 }
3016 if (typeof options["bg_source"] === "string") {
3017 bg_source = options["bg_source"];
3018 } else {
3019 bg_source = "unknown source";
3020 }
3021 if (typeof options["alphabet"] === "object" && options["alphabet"] != null
3022 && options["alphabet"] instanceof Alphabet) {
3023 alphabet = options["alphabet"];
3024 } else {
3025 throw new Error("The alphabet is required to generate the header.");
3026 }
3027 }
3028 // now create the output
3029 out = "";
3030 if (with_header) {
3031 out = "MEME version " + version + "\n\n";
3032 out += alphabet.as_meme() + "\n";
3033 if (alphabet.has_complement()) { // assume DNA has both strands unless otherwise specified
3034 out += "strands: " + (strands === 1 ? "+" : "+ -") + "\n\n";
3035 }
3036 out += "Background letter frequencies (from " + bg_source + "):\n";
3037 alen = alphabet.get_size_core();
3038 for (i = 0; i < alen; i++) {
3039 if (i !== 0) {
3040 if (i % 9 === 0) { // maximum of nine entries per line
3041 out += "\n";
3042 } else {
3043 out += " ";
3044 }
3045 }
3046 out += alphabet.get_symbol(i) + " " + alphabet.get_bg_freq(i).toFixed(3);
3047 }
3048 }
3049 out += "\n\n";
3050 out += "MOTIF " + this.name + (this.alt == null ? "" : " " + this.alt);
3051 if (with_pssm) {
3052 out += "\n\n";
3053 out += this.as_pssm(options["alphabet"], options["pseudocount"]);
3054 }
3055 if (with_pspm) {
3056 out += "\n\n";
3057 out += this.as_pspm();
3058 }
3059 return out;
3060 }
3061
3062 Pspm.prototype.toString = function() {
3063 "use strict";
3064 var str, i, row;
3065 str = "";
3066 for (i = 0; i < this.pspm.length; i++) {
3067 row = this.pspm[i];
3068 str += row.join("\t") + "\n";
3069 }
3070 return str;
3071 };
3072
3073 function parse_pspm_properties(str) {
3074 "use strict";
3075 var parts, i, eqpos, before, after, properties, prop, num, num_re;
3076 num_re = /^((?:[+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)|inf)$/;
3077 parts = trim(str).split(/\s+/);
3078 // split up words containing =
3079 for (i = 0; i < parts.length;) {
3080 eqpos = parts[i].indexOf("=");
3081 if (eqpos != -1) {
3082 before = parts[i].substr(0, eqpos);
3083 after = parts[i].substr(eqpos+1);
3084 if (before.length > 0 && after.length > 0) {
3085 parts.splice(i, 1, before, "=", after);
3086 i += 3;
3087 } else if (before.length > 0) {
3088 parts.splice(i, 1, before, "=");
3089 i += 2;
3090 } else if (after.length > 0) {
3091 parts.splice(i, 1, "=", after);
3092 i += 2;
3093 } else {
3094 parts.splice(i, 1, "=");
3095 i++;
3096 }
3097 } else {
3098 i++;
3099 }
3100 }
3101 properties = {};
3102 for (i = 0; i < parts.length; i += 3) {
3103 if (parts.length - i < 3) {
3104 throw new Error("Expected PSPM property was incomplete. "+
3105 "Remaing parts are: " + parts.slice(i).join(" "));
3106 }
3107 if (parts[i+1] !== "=") {
3108 throw new Error("Expected '=' in PSPM property between key and " +
3109 "value but got " + parts[i+1]);
3110 }
3111 prop = parts[i].toLowerCase();
3112 num = parts[i+2];
3113 if (!num_re.test(num)) {
3114 throw new Error("Expected numeric value for PSPM property '" +
3115 prop + "' but got '" + num + "'");
3116 }
3117 properties[prop] = num;
3118 }
3119 return properties;
3120 }
3121
3122 function parse_pspm_string(pspm_string) {
3123 "use strict";
3124 var header_re, lines, first_line, line_num, col_num, alph_length,
3125 motif_length, nsites, evalue, pspm, i, line, match, props, parts,
3126 j, prob;
3127 header_re = /^letter-probability\s+matrix:(.*)$/i;
3128 lines = pspm_string.split(/\n/);
3129 first_line = true;
3130 line_num = 0;
3131 col_num = 0;
3132 alph_length;
3133 motif_length;
3134 nsites;
3135 evalue;
3136 pspm = [];
3137 for (i = 0; i < lines.length; i++) {
3138 line = trim(lines[i]);
3139 if (line.length === 0) {
3140 continue;
3141 }
3142 // check the first line for a header though allow matrices without it
3143 if (first_line) {
3144 first_line = false;
3145 match = header_re.exec(line);
3146 if (match !== null) {
3147 props = parse_pspm_properties(match[1]);
3148 if (props.hasOwnProperty("alength")) {
3149 alph_length = parseFloat(props["alength"]);
3150 if (alph_length != 4 && alph_length != 20) {
3151 throw new Error("PSPM property alength should be 4 or 20" +
3152 " but got " + alph_length);
3153 }
3154 }
3155 if (props.hasOwnProperty("w")) {
3156 motif_length = parseFloat(props["w"]);
3157 if (motif_length % 1 !== 0 || motif_length < 1) {
3158 throw new Error("PSPM property w should be an integer larger " +
3159 "than zero but got " + motif_length);
3160 }
3161 }
3162 if (props.hasOwnProperty("nsites")) {
3163 nsites = parseFloat(props["nsites"]);
3164 if (nsites <= 0) {
3165 throw new Error("PSPM property nsites should be larger than " +
3166 "zero but got " + nsites);
3167 }
3168 }
3169 if (props.hasOwnProperty("e")) {
3170 evalue = props["e"];
3171 if (evalue < 0) {
3172 throw new Error("PSPM property evalue should be " +
3173 "non-negative but got " + evalue);
3174 }
3175 }
3176 continue;
3177 }
3178 }
3179 pspm[line_num] = [];
3180 col_num = 0;
3181 parts = line.split(/\s+/);
3182 for (j = 0; j < parts.length; j++) {
3183 prob = parseFloat(parts[j]);
3184 if (prob != parts[j] || prob < 0 || prob > 1) {
3185 throw new Error("Expected probability but got '" + parts[j] + "'");
3186 }
3187 pspm[line_num][col_num] = prob;
3188 col_num++;
3189 }
3190 line_num++;
3191 }
3192 if (typeof motif_length === "number") {
3193 if (pspm.length != motif_length) {
3194 throw new Error("Expected PSPM to have a motif length of " +
3195 motif_length + " but it was actually " + pspm.length);
3196 }
3197 } else {
3198 motif_length = pspm.length;
3199 }
3200 if (typeof alph_length !== "number") {
3201 alph_length = pspm[0].length;
3202 if (alph_length != 4 && alph_length != 20) {
3203 throw new Error("Expected length of first row in the PSPM to be " +
3204 "either 4 or 20 but got " + alph_length);
3205 }
3206 }
3207 for (i = 0; i < pspm.length; i++) {
3208 if (pspm[i].length != alph_length) {
3209 throw new Error("Expected PSPM row " + i + " to have a length of " +
3210 alph_length + " but the length was " + pspm[i].length);
3211 }
3212 }
3213 return {"pspm": pspm, "motif_length": motif_length,
3214 "alph_length": alph_length, "nsites": nsites, "evalue": evalue};
3215 }
3216 //======================================================================
3217 // end Pspm object
3218 //======================================================================
3219
3220 //======================================================================
3221 // start Logo object
3222 //======================================================================
3223
3224 var Logo = function(alphabet, options) {
3225 "use strict";
3226 this.alphabet = alphabet;
3227 this.fine_text = "";
3228 this.x_axis = 1;
3229 this.y_axis = true;
3230 this.xlate_nsyms = 1;
3231 this.xlate_start = null;
3232 this.xlate_end = null;
3233 this.pspm_list = [];
3234 this.pspm_column = [];
3235 this.rows = 0;
3236 this.columns = 0;
3237 if (typeof options === "string") {
3238 // the old method signature had fine_text here so we support that
3239 this.fine_text = options;
3240 } else if (typeof options === "object" && options != null) {
3241 this.fine_text = (typeof options.fine_text === "string" ? options.fine_text : "");
3242 this.x_axis = (typeof options.x_axis === "boolean" ? (options.x_axis ? 1 : 0) : 1);
3243 if (options.x_axis_hidden != null && options.x_axis_hidden) this.x_axis = -1;
3244 this.y_axis = (typeof options.y_axis === "boolean" ? options.y_axis : true);
3245 this.xlate_nsyms = (typeof options.xlate_nsyms === "number" ? options.xlate_nsyms : this.xlate_nsyms);
3246 this.xlate_start = (typeof options.xlate_start === "number" ? options.xlate_start : this.xlate_start);
3247 this.xlate_end = (typeof options.xlate_end === "number" ? options.xlate_end : this.xlate_end);
3248 }
3249 };
3250
3251 Logo.prototype.add_pspm = function(pspm, column) {
3252 "use strict";
3253 var col;
3254 if (typeof column === "undefined") {
3255 column = 0;
3256 } else if (column < 0) {
3257 throw new Error("Column index out of bounds.");
3258 }
3259 this.pspm_list[this.rows] = pspm;
3260 this.pspm_column[this.rows] = column;
3261 this.rows++;
3262 col = column + pspm.get_motif_length();
3263 if (col > this.columns) {
3264 this.columns = col;
3265 }
3266 };
3267
3268 Logo.prototype.get_columns = function() {
3269 "use strict";
3270 return this.columns;
3271 };
3272
3273 Logo.prototype.get_xlate_nsyms = function() {
3274 "use strict";
3275 return this.xlate_nsyms;
3276 };
3277
3278 Logo.prototype.get_xlate_start = function() {
3279 "use strict";
3280 return (this.xlate_start != null ? this.xlate_start : 0);
3281 };
3282
3283 Logo.prototype.get_xlate_end = function() {
3284 "use strict";
3285 return (this.xlate_end != null ? this.xlate_end : this.columns * this.xlate_nsyms);
3286 };
3287
3288 Logo.prototype.get_xlate_columns = function() {
3289 "use strict";
3290 return this.get_xlate_end() - this.get_xlate_start();
3291 };
3292
3293 Logo.prototype.get_rows = function() {
3294 "use strict";
3295 return this.rows;
3296 };
3297
3298 Logo.prototype.get_pspm = function(row_index) {
3299 "use strict";
3300 if (row_index < 0 || row_index >= this.rows) {
3301 throw new Error("INDEX_OUT_OF_BOUNDS");
3302 }
3303 return this.pspm_list[row_index];
3304 };
3305
3306 Logo.prototype.get_offset = function(row_index) {
3307 "use strict";
3308 if (row_index < 0 || row_index >= this.rows) {
3309 throw new Error("INDEX_OUT_OF_BOUNDS");
3310 }
3311 return this.pspm_column[row_index];
3312 };
3313
3314 Logo.prototype._as_eps_data = function(ssc, errbars) {
3315 var i, j, pos, stack_pos, pspm, stack, sym, out;
3316 out = "";
3317 for (i = 0; i < this.rows; i++) {
3318 out += "\nStartLine\n";
3319 // Indent
3320 for (j = 0; j < this.pspm_column[i]; j++) {
3321 out += "() startstack\nendstack\n\n";
3322 }
3323 pspm = this.pspm_list[i];
3324 if (pspm.get_left_trim() > 0) {
3325 out += "MuteColour\nDrawTrimEdge\n" + pspm.get_left_trim() + " DrawTrimBg\n";
3326 }
3327 for (pos = 0; pos < pspm.get_motif_length(); pos++) {
3328 if (pos != 0 && pos == pspm.get_left_trim()) { // enable full colour
3329 out += "DrawTrimEdge\nRestoreColour\n";
3330 } else if (pos == (pspm.get_motif_length() - pspm.get_right_trim())) {
3331 out += "MuteColour\n" + pspm.get_right_trim() + " DrawTrimBg\n";
3332 }
3333 out += "(" + (pos + 1) + ") startstack\n";
3334 stack = pspm.get_stack(pos, this.alphabet, ssc);
3335 for (stack_pos = 0; stack_pos < stack.length; stack_pos++) {
3336 sym = stack[stack_pos];
3337 out += " " + (sym.get_scale() * this.alphabet.get_ic()) + " (" + sym.get_symbol() + ") numchar\n";
3338 }
3339 if (errbars) {
3340 out += " " + pspm.get_error(this.alphabet) + " Ibeam\n";
3341 }
3342 out += "endstack\n\n";
3343 }
3344 if (pspm.get_right_trim() > 0 || pspm.get_left_trim() == pspm.get_motif_length()) {
3345 out += "RestoreColour\n";
3346 }
3347 out += "EndLine\n";
3348 }
3349 return out;
3350 };
3351
3352 Logo.prototype.as_eps = function(options) {
3353 "use strict";
3354 if (this.xlate_nsyms != 1) throw new Error("Unsupported setting xlate_nsyms for EPS");
3355 if (this.xlate_start != null) throw new Error("Unsupported setting xlate_start for EPS");
3356 if (this.xlate_end != null) throw new Error("Unsupported setting xlate_end for EPS");
3357
3358 var LOGOHEIGHT = 7.5; // default height of line in cm
3359 var cm2pts, height, width, now, ssc, errbars;
3360 if (typeof options === "undefined") {
3361 options = {};
3362 }
3363 cm2pts = 72 / 2.54;
3364 if (typeof options.logo_height == "number") {
3365 height = options.logo_height;
3366 } else {
3367 height = LOGOHEIGHT * this.rows;
3368 }
3369 if (typeof options.logo_width == "number") {
3370 width = options.logo_width;
3371 } else {
3372 width = this.columns + 2;
3373 }
3374 now = new Date();
3375 ssc = (typeof options.ssc == "boolean" ? options.ssc : false);
3376 errbars = (typeof options.show_error_bar == "boolean" ? options.show_error_bar : ssc);
3377 var values = {
3378 "LOGOHEIGHT": height,
3379 "LOGOWIDTH": width,
3380 "BOUNDINGHEIGHT": Math.round(height * cm2pts),
3381 "BOUNDINGWIDTH": Math.round(width * cm2pts),
3382 "LOGOLINEHEIGHT": (height / this.rows),
3383 "CHARSPERLINE": this.columns,
3384 "BARBITS": this.alphabet.get_ic(),
3385 "LOGOTYPE": (this.alphabet.has_complement() ? "NA" : "AA"),
3386 "CREATIONDATE": now.getDate() + "." + (now.getMonth() + 1) + "." + now.getFullYear() + " " + now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds(),
3387 "ERRORBARFRACTION": (typeof options.error_bar_fraction == "number" ? options.error_bar_fraction : 1.0),
3388 "TICBITS": (typeof options.ticbits == "number" ? options.ticbits : 1.0),
3389 "TITLE": (typeof options.title == "string" ? options.title : ""),
3390 "FINEPRINT": (typeof options.fineprint == "string" ? options.fineprint : this.fine_text),
3391 "XAXISLABEL": (typeof options.xaxislabel == "string" ? options.xaxislabel : ""),
3392 "YAXISLABEL": (typeof options.yaxislabel == "string" ? options.yaxislabel : "bits"),
3393 "SSC": ssc,
3394 "YAXIS": (typeof options.show_y_axis == "boolean" ? options.show_y_axis : this.y_axis),
3395 "SHOWENDS": (typeof options.show_ends == "boolean" ? options.show_ends : false),
3396 "ERRBAR": errbars,
3397 "OUTLINE": (typeof options.show_outline == "boolean" ? options.show_outline : false),
3398 "NUMBERING": (typeof options.show_numbering == "boolean" ? options.show_numbering : this.x_axis != 0),
3399 "SHOWINGBOX": (typeof options.show_box == "boolean" ? options.show_box : false),
3400 "CREATOR": (typeof options.creator == "string" ? options.creator : "motif_logo.js"),
3401 "FONTSIZE": (typeof options.label_font_size == "number" ? options.label_font_size : 12),
3402 "TITLEFONTSIZE": (typeof options.title_font_size == "number" ? options.title_font_size : 12),
3403 "SMALLFONTSIZE": (typeof options.small_font_size == "number" ? options.small_font_size : 6),
3404 "TOPMARGIN" : (typeof options.top_margin == "number" ? options.top_margin : 0.9),
3405 "BOTTOMMARGIN": (typeof options.bottom_margin == "number" ? options.bottom_margin : 0.9),
3406 "COLORDICT": this.alphabet._as_eps_dict(),
3407 "DATA": this._as_eps_data(ssc, errbars)
3408 };
3409 // now this requires that the script containing the template has been imported!
3410 return motif_logo_template(values);
3411 };
3412
3413 //======================================================================
3414 // end Logo object
3415 //======================================================================
3416
3417 // calculate the exact size (in pixels) of an object drawn on the
3418 // canvas assuming that the background of the canvas is transparent.
3419 function canvas_bounds(ctx, cwidth, cheight) {
3420 "use strict";
3421 var data, r, c, top_line, bottom_line, left_line, right_line,
3422 txt_width, txt_height;
3423
3424 // extract the image data
3425 data = ctx.getImageData(0, 0, cwidth, cheight).data;
3426
3427 // set initial values
3428 top_line = -1; bottom_line = -1; left_line = -1; right_line = -1;
3429 txt_width = 0; txt_height = 0;
3430
3431 // Find the top-most line with a non-transparent pixel
3432 for (r = 0; r < cheight; r++) {
3433 for (c = 0; c < cwidth; c++) {
3434 if (data[r * cwidth * 4 + c * 4 + 3]) {
3435 top_line = r;
3436 break;
3437 }
3438 }
3439 if (top_line != -1) {
3440 break;
3441 }
3442 }
3443
3444 // Only bother looking if we found at least one set pixel...
3445 if (top_line != -1) {
3446
3447 //find the last line with a non-transparent pixel
3448 for (r = cheight-1; r >= top_line; r--) {
3449 for(c = 0; c < cwidth; c++) {
3450 if(data[r * cwidth * 4 + c * 4 + 3]) {
3451 bottom_line = r;
3452 break;
3453 }
3454 }
3455 if (bottom_line != -1) {
3456 break;
3457 }
3458 }
3459 // calculate height
3460 txt_height = bottom_line - top_line + 1;
3461
3462 // Find the left-most line with a non-transparent pixel
3463 for (c = 0; c < cwidth; c++) {
3464 for (r = top_line; r <= bottom_line; r++) {
3465 if (data[r * cwidth * 4 + c * 4 + 3]) {
3466 left_line = c;
3467 break;
3468 }
3469 }
3470 if (left_line != -1) {
3471 break;
3472 }
3473 }
3474
3475 //find the right most line with a non-transparent pixel
3476 for (c = cwidth-1; c >= left_line; c--) {
3477 for(r = top_line; r <= bottom_line; r++) {
3478 if(data[r * cwidth * 4 + c * 4 + 3]) {
3479 right_line = c;
3480 break;
3481 }
3482 }
3483 if (right_line != -1) {
3484 break;
3485 }
3486 }
3487 txt_width = right_line - left_line + 1;
3488 }
3489
3490 //return the bounds
3491 return {bound_top: top_line, bound_bottom: bottom_line,
3492 bound_left: left_line, bound_right: right_line, width: txt_width,
3493 height: txt_height};
3494 }
3495
3496 //======================================================================
3497 // start RasterizedAlphabet
3498 //======================================================================
3499
3500 // Rasterize Alphabet
3501 // 1) Measure width of text at default font for all symbols in alphabet
3502 // 2) sort in width ascending
3503 // 3) Drop the top and bottom 10% (designed to ignore outliers like 'W' and 'I')
3504 // 4) Calculate the average as the maximum scaling factor (designed to stop I becoming a rectangular blob).
3505 // 5) Assume scale of zero would result in width of zero, interpolate scale required to make perfect width font
3506 // 6) Draw text onto temp canvas at calculated scale
3507 // 7) Find bounds of drawn text
3508 // 8) Paint on to another canvas at the desired height (but only scaling width to fit if larger).
3509 var RasterizedAlphabet = function(alphabet, logo_scale, font, width) {
3510 "use strict";
3511 var default_size, safety_pad, canvas, ctx, middle, baseline, widths, sizes,
3512 i, sym, size, tenpercent, avg_width, scale,
3513 target_width, target_height;
3514 //variable prototypes
3515 this.alphabet = alphabet;
3516 this.scale = logo_scale;
3517 this.sym_cache = {};
3518 this.stack_num_cache = [];
3519 this.scale_num_cache = [];
3520 // size of canvas
3521 default_size = 60; // size of measuring canvas
3522 safety_pad = 20; // pixels to pad around so we don't miss the edges
3523 // create a canvas to do our measuring
3524 canvas = document.createElement("canvas");
3525 if (!canvas.getContext) throw new Error("No canvas support");
3526 canvas.width = default_size + 2 * safety_pad;
3527 canvas.height = default_size + 2 * safety_pad;
3528 middle = Math.round(canvas.width / 2);
3529 baseline = Math.round(canvas.height - safety_pad);
3530 ctx = canvas.getContext('2d');
3531 if (!supports_text(ctx)) throw new Error("Canvas does not support text");
3532 ctx.font = font;
3533 ctx.textAlign = "center";
3534 ctx.translate(middle, baseline);
3535 // list of widths
3536 widths = [];
3537 sizes = [];
3538 //now measure each letter in the alphabet
3539 for (i = 0; i < alphabet.get_size_core(); ++i) {
3540 // reset the canvas
3541 ctx.clearRect(0, 0, canvas.width, canvas.height);
3542 ctx.fillStyle = alphabet.get_colour(i);
3543 // draw the test text
3544 ctx.fillText(alphabet.get_symbol(i), 0, 0);
3545 //measure
3546 size = canvas_bounds(ctx, canvas.width, canvas.height);
3547 if (size.width === 0) throw new Error("Invisible symbol!");
3548 widths.push(size.width);
3549 sizes[i] = size;
3550 }
3551 //sort the widths
3552 widths.sort(function(a,b) {return a - b;});
3553 //drop 10% of the items off each end
3554 tenpercent = Math.floor(widths.length / 10);
3555 for (i = 0; i < tenpercent; ++i) {
3556 widths.pop();
3557 widths.shift();
3558 }
3559 //calculate average width
3560 avg_width = 0;
3561 for (i = 0; i < widths.length; ++i) {
3562 avg_width += widths[i];
3563 }
3564 avg_width /= widths.length;
3565 // calculate the target width
3566 target_width = width * this.scale * 2;
3567 // calculate scales
3568 for (i = 0; i < alphabet.get_size_core(); ++i) {
3569 sym = alphabet.get_symbol(i);
3570 size = sizes[i];
3571 // calculate scale
3572 scale = target_width / Math.max(avg_width, size.width);
3573 // estimate scaled height
3574 target_height = size.height * scale;
3575 // create an appropriately sized canvas
3576 canvas = document.createElement("canvas");
3577 canvas.width = target_width;
3578 canvas.height = target_height + safety_pad * 2;
3579 // calculate the middle
3580 middle = Math.round(canvas.width / 2);
3581 // calculate the baseline
3582 baseline = Math.round(canvas.height - safety_pad);
3583 // get the context and prepare to draw the rasterized text
3584 ctx = canvas.getContext('2d');
3585 ctx.font = font;
3586 ctx.fillStyle = alphabet.get_colour(i);
3587 ctx.textAlign = "center";
3588 ctx.translate(middle, baseline);
3589 ctx.save();
3590 ctx.scale(scale, scale);
3591 // draw the text
3592 ctx.fillText(sym, 0, 0);
3593 ctx.restore();
3594 this.sym_cache[sym] = {"image": canvas, "size": canvas_bounds(ctx, canvas.width, canvas.height)};
3595 }
3596 };
3597
3598 RasterizedAlphabet.prototype.get_alphabet = function() {
3599 return this.alphabet;
3600 };
3601
3602 RasterizedAlphabet.prototype.get_scale = function() {
3603 return this.scale;
3604 };
3605
3606 RasterizedAlphabet.prototype.draw_stack_sym = function(ctx, letter, dx, dy, dWidth, dHeight) {
3607 "use strict";
3608 var entry, image, size;
3609 entry = this.sym_cache[letter];
3610 image = entry.image;
3611 size = entry.size;
3612 ctx.drawImage(image, 0, size.bound_top -1, image.width, size.height+1, dx, dy, dWidth, dHeight);
3613 };
3614
3615 RasterizedAlphabet.prototype.draw_stack_num = function(ctx, font, stack_width, index) {
3616 var image, image_ctx, text_length;
3617 if (index >= this.stack_num_cache.length) {
3618 image = document.createElement("canvas");
3619 // measure the text
3620 image_ctx = image.getContext('2d');
3621 image_ctx.save();
3622 image_ctx.font = font;
3623 text_length = image_ctx.measureText("" + (index + 1)).width;
3624 image_ctx.restore();
3625 // resize the canvas to fit
3626 image.width = Math.ceil(stack_width);
3627 image.height = Math.ceil(text_length);
3628 // draw the text
3629 image_ctx = image.getContext('2d');
3630 image_ctx.translate(Math.round(stack_width / 2), 0);
3631 image_ctx.font = font;
3632 image_ctx.textBaseline = "middle";
3633 image_ctx.textAlign = "right";
3634 image_ctx.rotate(-(Math.PI / 2));
3635 image_ctx.fillText("" + (index + 1), 0, 0);
3636 this.stack_num_cache[index] = image;
3637 } else {
3638 image = this.stack_num_cache[index];
3639 }
3640 ctx.drawImage(image, 0, 0);
3641 }
3642
3643 RasterizedAlphabet.prototype.draw_scale_num = function(ctx, font, num) {
3644 var image, image_ctx, text_size, m_length;
3645 if (num >= this.scale_num_cache.length) {
3646 image = document.createElement("canvas");
3647 // measure the text
3648 image_ctx = image.getContext('2d');
3649 image_ctx.font = font;
3650 text_size = image_ctx.measureText("" + num);
3651 if (text_size.actualBoundingBoxAscent && text_size.actualBoundingBoxDesent) {
3652 // resize the canvas to fit
3653 image.width = Math.ceil(text_size.width);
3654 image.height = Math.ceil(text_size.actualBoundingBoxAscent + text_size.actualBoundingBoxDesent);
3655 // draw the text
3656 image_ctx = image.getContext('2d');
3657 image_ctx.font = font;
3658 image_ctx.textAlign = "right";
3659 image_ctx.fillText("" + num, image.width, text_size.actualBoundingBoxAscent);
3660 } else {
3661 // measure width of 'm' to approximate height, we double it later anyway
3662 m_length = image_ctx.measureText("m").width;
3663 // resize the canvas to fit
3664 image.width = Math.ceil(text_size.width);
3665 image.height = Math.ceil(2 * m_length);
3666 // draw the text
3667 image_ctx = image.getContext('2d');
3668 image_ctx.font = font;
3669 image_ctx.textAlign = "right";
3670 image_ctx.textBaseline = "middle";
3671 image_ctx.fillText("" + num, image.width, m_length);
3672 }
3673 this.scale_num_cache[num] = image;
3674 } else {
3675 image = this.scale_num_cache[num];
3676 }
3677 ctx.drawImage(image, -image.width, -Math.round(image.height / 2))
3678 }
3679
3680 //======================================================================
3681 // end RasterizedAlphabet
3682 //======================================================================
3683
3684 //======================================================================
3685 // start LogoMetrics object
3686 //======================================================================
3687
3688 var LogoMetrics = function(ctx, logo_columns, logo_rows, has_names, has_finetext, x_axis, y_axis) {
3689 "use strict";
3690 var i, row_height;
3691 //variable prototypes
3692 this.pad_top = (has_names ? 5 : 0);
3693 this.pad_left = (y_axis ? 10 : 0);
3694 this.pad_right = (has_finetext ? 15 : 0);
3695 this.pad_bottom = 0;
3696 this.pad_middle = 20;
3697 this.name_height = 14;
3698 this.name_font = "bold " + this.name_height + "px Times, sans-serif";
3699 this.name_spacer = 0;
3700 this.y_axis = y_axis;
3701 this.y_label = "bits";
3702 this.y_label_height = 12;
3703 this.y_label_font = "bold " + this.y_label_height + "px Helvetica, sans-serif";
3704 this.y_label_spacer = 3;
3705 this.y_num_height = 12;
3706 this.y_num_width = 0;
3707 this.y_num_font = "bold " + this.y_num_height + "px Helvetica, sans-serif";
3708 this.y_tic_width = 5;
3709 this.stack_pad_left = 0;
3710 this.stack_font = "bold 25px Helvetica, sans-serif";
3711 this.stack_height = 90;
3712 this.stack_width = 26;
3713 this.stacks_pad_right = 5;
3714 this.x_axis = x_axis;
3715 this.x_num_above = 2;
3716 this.x_num_height = 12;
3717 this.x_num_width = 0;
3718 this.x_num_font = "bold " + this.x_num_height + "px Helvetica, sans-serif";
3719 this.fine_txt_height = 6;
3720 this.fine_txt_above = 2;
3721 this.fine_txt_font = "normal " + this.fine_txt_height + "px Helvetica, sans-serif";
3722 this.letter_metrics = new Array();
3723 this.summed_width = 0;
3724 this.summed_height = 0;
3725 //calculate the width of the y axis numbers
3726 ctx.font = this.y_num_font;
3727 for (i = 0; i <= 2; i++) {
3728 this.y_num_width = Math.max(this.y_num_width, ctx.measureText("" + i).width);
3729 }
3730 //calculate the width of the x axis numbers (but they are rotated so it becomes height)
3731 if (x_axis == 1) {
3732 ctx.font = this.x_num_font;
3733 for (i = 1; i <= logo_columns; i++) {
3734 this.x_num_width = Math.max(this.x_num_width, ctx.measureText("" + i).width);
3735 }
3736 } else if (x_axis == 0) {
3737 this.x_num_height = 4;
3738 this.x_num_width = 4;
3739 } else {
3740 this.x_num_height = 0;
3741 this.x_num_width = 0;
3742 }
3743
3744 //calculate how much vertical space we want to draw this
3745 //first we add the padding at the top and bottom since that's always there
3746 this.summed_height += this.pad_top + this.pad_bottom;
3747 //all except the last row have the same amount of space allocated to them
3748 if (logo_rows > 1) {
3749 row_height = this.stack_height + this.pad_middle;
3750 if (has_names) {
3751 row_height += this.name_height;
3752 //the label is allowed to overlap into the spacer
3753 row_height += Math.max(this.y_num_height/2, this.name_spacer);
3754 //the label is allowed to overlap the space used by the other label
3755 row_height += Math.max(this.y_num_height/2, this.x_num_height + this.x_num_above);
3756 } else {
3757 row_height += this.y_num_height/2;
3758 //the label is allowed to overlap the space used by the other label
3759 row_height += Math.max(this.y_num_height/2, this.x_num_height + this.x_num_above);
3760 }
3761 this.summed_height += row_height * (logo_rows - 1);
3762 }
3763 //the last row has the name and fine text below it but no padding
3764 this.summed_height += this.stack_height + (this.y_axis ? this.y_num_height/2 : 0);
3765
3766 var fine_txt_total = (has_finetext ? this.fine_txt_height + this.fine_txt_above : 0);
3767 if (has_names) {
3768 this.summed_height += fine_txt_total + this.name_height;
3769 this.summed_height += Math.max((this.y_axis ? this.y_num_height/2 : 0),
3770 this.x_num_height + this.x_num_above + this.name_spacer);
3771 } else {
3772 this.summed_height += Math.max((this.y_axis ? this.y_num_height/2 : 0),
3773 this.x_num_height + this.x_num_above + fine_txt_total);
3774 }
3775
3776 //calculate how much horizontal space we want to draw this
3777 //first add the padding at the left and right since that's always there
3778 this.summed_width += this.pad_left + this.pad_right;
3779 if (this.y_axis) {
3780 //add on the space for the y-axis label
3781 this.summed_width += this.y_label_height + this.y_label_spacer;
3782 //add on the space for the y-axis
3783 this.summed_width += this.y_num_width + this.y_tic_width;
3784 }
3785 //add on the space for the stacks
3786 this.summed_width += (this.stack_pad_left + this.stack_width) * logo_columns;
3787 //add on the padding after the stacks (an offset from the fine text)
3788 this.summed_width += this.stacks_pad_right;
3789
3790 };
3791
3792 //======================================================================
3793 // end LogoMetrics object
3794 //======================================================================
3795
3796 //found this trick at http://talideon.com/weblog/2005/02/detecting-broken-images-js.cfm
3797 function image_ok(img) {
3798 "use strict";
3799 // During the onload event, IE correctly identifies any images that
3800 // weren't downloaded as not complete. Others should too. Gecko-based
3801 // browsers act like NS4 in that they report this incorrectly.
3802 if (!img.complete) {
3803 return false;
3804 }
3805 // However, they do have two very useful properties: naturalWidth and
3806 // naturalHeight. These give the true size of the image. If it failed
3807 // to load, either of these should be zero.
3808 if (typeof img.naturalWidth !== "undefined" && img.naturalWidth === 0) {
3809 return false;
3810 }
3811 // No other way of checking: assume it's ok.
3812 return true;
3813 }
3814
3815 function supports_text(ctx) {
3816 "use strict";
3817 if (!ctx.fillText) {
3818 return false;
3819 }
3820 if (!ctx.measureText) {
3821 return false;
3822 }
3823 return true;
3824 }
3825
3826 //draws the scale, returns the width
3827 function draw_scale(ctx, metrics, alphabet_ic, raster) {
3828 "use strict";
3829 var tic_height, i;
3830 tic_height = metrics.stack_height / alphabet_ic;
3831 ctx.save();
3832 ctx.translate(metrics.y_label_height, metrics.y_num_height/2);
3833 //draw the axis label
3834 ctx.save();
3835 ctx.font = metrics.y_label_font;
3836 ctx.translate(0, metrics.stack_height/2);
3837 ctx.rotate(-(Math.PI / 2));
3838 ctx.textAlign = "center";
3839 ctx.fillText("bits", 0, 0);
3840 ctx.restore();
3841
3842 ctx.translate(metrics.y_label_spacer + metrics.y_num_width, 0);
3843
3844 //draw the axis tics
3845 ctx.save();
3846 ctx.translate(0, metrics.stack_height);
3847 for (i = 0; i <= alphabet_ic; i++) {
3848 //draw the number
3849 ctx.save();
3850 ctx.translate(-1, 0);
3851 raster.draw_scale_num(ctx, metrics.y_num_font, i);
3852 ctx.restore();
3853 //draw the tic
3854 ctx.fillRect(0, -1, metrics.y_tic_width, 2);
3855 //prepare for next tic
3856 ctx.translate(0, -tic_height);
3857 }
3858 ctx.restore();
3859
3860 ctx.fillRect(metrics.y_tic_width - 2, 0, 2, metrics.stack_height)
3861
3862 ctx.restore();
3863 }
3864
3865 function draw_stack_num(ctx, metrics, row_index, raster) {
3866 "use strict";
3867 ctx.save();
3868 ctx.translate(0, Math.round(metrics.stack_height + metrics.x_num_above));
3869 if (metrics.x_axis == 1) {
3870 raster.draw_stack_num(ctx, metrics.x_num_font, metrics.stack_width, row_index);
3871 } else if (metrics.x_axis == 0) {
3872 // draw dots instead of the numbers (good for small logos)
3873 ctx.beginPath();
3874 var radius = Math.round(metrics.x_num_height / 2);
3875 ctx.arc(Math.round(metrics.stack_width / 2), radius, radius, 0, 2 * Math.PI, false);
3876 ctx.fill();
3877 }
3878 ctx.restore();
3879 }
3880
3881 function draw_stack(ctx, metrics, symbols, raster) {
3882 "use strict";
3883 var preferred_pad, sym_min, i, sym, sym_height, pad;
3884 preferred_pad = 0;
3885 sym_min = 5;
3886
3887 ctx.save();//1
3888 ctx.translate(0, metrics.stack_height);
3889 for (i = 0; i < symbols.length; i++) {
3890 sym = symbols[i];
3891 sym_height = metrics.stack_height * sym.get_scale();
3892
3893 pad = preferred_pad;
3894 if (sym_height - pad < sym_min) {
3895 pad = Math.min(pad, Math.max(0, sym_height - sym_min));
3896 }
3897 sym_height -= pad;
3898
3899 //translate to the correct position
3900 ctx.translate(0, -(pad/2 + sym_height));
3901
3902 //draw
3903 raster.draw_stack_sym(ctx, sym.get_symbol(), 0, 0, metrics.stack_width, sym_height);
3904 //translate past the padding
3905 ctx.translate(0, -(pad/2));
3906 }
3907 ctx.restore();//1
3908 }
3909
3910 function draw_dashed_line(ctx, pattern, start, x1, y1, x2, y2) {
3911 "use strict";
3912 var x, y, len, i, dx, dy, tlen, theta, mulx, muly, lx, ly;
3913 dx = x2 - x1;
3914 dy = y2 - y1;
3915 tlen = Math.pow(dx*dx + dy*dy, 0.5);
3916 theta = Math.atan2(dy,dx);
3917 mulx = Math.cos(theta);
3918 muly = Math.sin(theta);
3919 lx = [];
3920 ly = [];
3921 for (i = 0; i < pattern; ++i) {
3922 lx.push(pattern[i] * mulx);
3923 ly.push(pattern[i] * muly);
3924 }
3925 i = start;
3926 x = x1;
3927 y = y1;
3928 len = 0;
3929 ctx.beginPath();
3930 while (len + pattern[i] < tlen) {
3931 ctx.moveTo(x, y);
3932 x += lx[i];
3933 y += ly[i];
3934 ctx.lineTo(x, y);
3935 len += pattern[i];
3936 i = (i + 1) % pattern.length;
3937 x += lx[i];
3938 y += ly[i];
3939 len += pattern[i];
3940 i = (i + 1) % pattern.length;
3941 }
3942 if (len < tlen) {
3943 ctx.moveTo(x, y);
3944 x += mulx * (tlen - len);
3945 y += muly * (tlen - len);
3946 ctx.lineTo(x, y);
3947 }
3948 ctx.stroke();
3949 }
3950
3951 function draw_trim_background(ctx, metrics, left_start, left_end, left_divider, right_start, right_end, right_divider) {
3952 "use strict";
3953 var left_size = left_end - left_start;
3954 var right_size = right_end - right_start;
3955 var line_x;
3956
3957 ctx.save();//s8
3958 ctx.fillStyle = "rgb(240, 240, 240)";
3959 if (left_size > 0) {
3960 ctx.fillRect(left_start * metrics.stack_width, 0, left_size * metrics.stack_width, metrics.stack_height);
3961 }
3962 if (right_size > 0) {
3963 ctx.fillRect(right_start * metrics.stack_width, 0, right_size * metrics.stack_width, metrics.stack_height);
3964 }
3965 ctx.fillStyle = "rgb(51, 51, 51)";
3966 if (left_size > 0 && left_divider) {
3967 line_x = (left_end * metrics.stack_width) - 0.5;
3968 draw_dashed_line(ctx, [3], 0, line_x, 0, line_x, metrics.stack_height);
3969 }
3970 if (right_size > 0 && right_divider) {
3971 line_x = (right_start * metrics.stack_width) + 0.5;
3972 draw_dashed_line(ctx, [3], 0, line_x, 0, line_x, metrics.stack_height);
3973 }
3974 ctx.restore();//s8
3975 }
3976
3977 function size_logo_on_canvas(logo, canvas, show_names, scale) {
3978 "use strict";
3979 var draw_name, draw_finetext, metrics;
3980 draw_name = (typeof show_names === "boolean" ? show_names : (logo.get_rows() > 1));
3981 draw_finetext = (logo.fine_text.length > 0);
3982 if (canvas.width !== 0 && canvas.height !== 0) {
3983 return;
3984 }
3985 metrics = new LogoMetrics(canvas.getContext('2d'),
3986 logo.get_xlate_columns(), logo.get_rows(), draw_name, draw_finetext, logo.x_axis, logo.y_axis);
3987 if (typeof scale == "number") {
3988 //resize the canvas to fit the scaled logo
3989 canvas.width = metrics.summed_width * scale;
3990 canvas.height = metrics.summed_height * scale;
3991 } else {
3992 if (canvas.width === 0 && canvas.height === 0) {
3993 canvas.width = metrics.summed_width;
3994 canvas.height = metrics.summed_height;
3995 } else if (canvas.width === 0) {
3996 canvas.width = metrics.summed_width * (canvas.height / metrics.summed_height);
3997 } else if (canvas.height === 0) {
3998 canvas.height = metrics.summed_height * (canvas.width / metrics.summed_width);
3999 }
4000 }
4001 }
4002
4003 function draw_logo_on_canvas(logo, canvas, show_names, scale) {
4004 "use strict";
4005 var i, draw_name, draw_finetext, ctx, metrics, raster, pspm_i, pspm,
4006 offset, col_index, motif_position, ssc;
4007 ssc = false;
4008 draw_name = (typeof show_names === "boolean" ? show_names : (logo.get_rows() > 1));
4009 draw_finetext = (logo.fine_text.length > 0);
4010 ctx = canvas.getContext('2d');
4011 //assume that the user wants the canvas scaled equally so calculate what the best width for this image should be
4012 metrics = new LogoMetrics(ctx, logo.get_xlate_columns(), logo.get_rows(), draw_name, draw_finetext, logo.x_axis, logo.y_axis);
4013 if (typeof scale == "number") {
4014 //resize the canvas to fit the scaled logo
4015 canvas.width = metrics.summed_width * scale;
4016 canvas.height = metrics.summed_height * scale;
4017 } else {
4018 if (canvas.width === 0 && canvas.height === 0) {
4019 scale = 1;
4020 canvas.width = metrics.summed_width;
4021 canvas.height = metrics.summed_height;
4022 } else if (canvas.width === 0) {
4023 scale = canvas.height / metrics.summed_height;
4024 canvas.width = metrics.summed_width * scale;
4025 } else if (canvas.height === 0) {
4026 scale = canvas.width / metrics.summed_width;
4027 canvas.height = metrics.summed_height * scale;
4028 } else {
4029 scale = Math.min(canvas.width / metrics.summed_width, canvas.height / metrics.summed_height);
4030 }
4031 }
4032 // cache the raster based on the assumption that we will be drawing a lot
4033 // of logos the same size and alphabet
4034 if (typeof draw_logo_on_canvas.raster_cache === "undefined") {
4035 draw_logo_on_canvas.raster_cache = [];
4036 }
4037 for (i = 0; i < draw_logo_on_canvas.raster_cache.length; i++) {
4038 raster = draw_logo_on_canvas.raster_cache[i];
4039 if (raster.get_alphabet().equals(logo.alphabet) &&
4040 Math.abs(raster.get_scale() - scale) < 0.1) break;
4041 raster = null;
4042 }
4043 if (raster == null) {
4044 raster = new RasterizedAlphabet(logo.alphabet, scale, metrics.stack_font, metrics.stack_width);
4045 draw_logo_on_canvas.raster_cache.push(raster);
4046 }
4047 ctx = canvas.getContext('2d');
4048 ctx.save();//s1
4049 ctx.scale(scale, scale);
4050 ctx.save();//s2
4051 ctx.save();//s7
4052 //create margin
4053 ctx.translate(Math.round(metrics.pad_left), Math.round(metrics.pad_top));
4054 for (pspm_i = 0; pspm_i < logo.get_rows(); ++pspm_i) {
4055 pspm = logo.get_pspm(pspm_i);
4056 offset = logo.get_offset(pspm_i);
4057 //optionally draw name if this isn't the last row or is the only row
4058 if (draw_name && (logo.get_rows() == 1 || pspm_i != (logo.get_rows()-1))) {
4059 ctx.save();//s4
4060 ctx.translate(Math.round(metrics.summed_width/2), Math.round(metrics.name_height));
4061 ctx.font = metrics.name_font;
4062 ctx.textAlign = "center";
4063 ctx.fillText(pspm.name, 0, 0);
4064 ctx.restore();//s4
4065 ctx.translate(0, Math.round(metrics.name_height +
4066 Math.min(0, metrics.name_spacer - metrics.y_num_height/2)));
4067 }
4068 //draw scale
4069 if (logo.y_axis) draw_scale(ctx, metrics, logo.alphabet.get_ic(), raster);
4070 ctx.save();//s5
4071 //translate across past the scale
4072 if (logo.y_axis) {
4073 ctx.translate(Math.round(metrics.y_label_height + metrics.y_label_spacer +
4074 metrics.y_num_width + metrics.y_tic_width), Math.round(metrics.y_num_height / 2));
4075 }
4076 //draw the trimming background
4077 if (pspm.get_left_trim() > 0 || pspm.get_right_trim() > 0) {
4078 var left_start = offset * logo.get_xlate_nsyms();
4079 var left_end = (offset + pspm.get_left_trim()) * logo.get_xlate_nsyms();
4080 var left_divider = true;
4081 if (left_end < logo.get_xlate_start() || left_start > logo.get_xlate_end()) {
4082 // no overlap
4083 left_start = 0;
4084 left_end = 0;
4085 left_divider = false;
4086 } else {
4087 if (left_start < logo.get_xlate_start()) {
4088 left_start = logo.get_xlate_start();
4089 }
4090 if (left_end > logo.get_xlate_end()) {
4091 left_end = logo.get_xlate_end();
4092 left_divider = false;
4093 }
4094 left_start -= logo.get_xlate_start();
4095 left_end -= logo.get_xlate_start();
4096 if (left_end < left_start) {
4097 left_start = 0;
4098 left_end = 0;
4099 left_divider = false;
4100 }
4101 }
4102 var right_end = (offset + pspm.get_motif_length()) * logo.get_xlate_nsyms();
4103 //var right_start = right_end - (pspm.get_left_trim() * logo.get_xlate_nsyms());
4104 var right_start = right_end - (pspm.get_right_trim() * logo.get_xlate_nsyms());
4105 var right_divider = true;
4106 if (right_end < logo.get_xlate_start() || right_start > logo.get_xlate_end()) {
4107 // no overlap
4108 right_start = 0;
4109 right_end = 0;
4110 right_divider = false;
4111 } else {
4112 if (right_start < logo.get_xlate_start()) {
4113 right_start = logo.get_xlate_start();
4114 right_divider = false;
4115 }
4116 if (right_end > logo.get_xlate_end()) {
4117 right_end = logo.get_xlate_end();
4118 }
4119 right_start -= logo.get_xlate_start();
4120 right_end -= logo.get_xlate_start();
4121 if (right_end < right_start) {
4122 right_start = 0;
4123 right_end = 0;
4124 right_divider = false;
4125 }
4126 }
4127 draw_trim_background(ctx, metrics, left_start, left_end, left_divider, right_start, right_end, right_divider);
4128 }
4129 //draw letters
4130 var xlate_col;
4131 for (xlate_col = logo.get_xlate_start(); xlate_col < logo.get_xlate_end(); xlate_col++) {
4132 ctx.translate(metrics.stack_pad_left,0);
4133 col_index = Math.floor(xlate_col / logo.get_xlate_nsyms());
4134 if (xlate_col % logo.get_xlate_nsyms() == 0) {
4135 if (col_index >= offset && col_index < (offset + pspm.get_motif_length())) {
4136 motif_position = col_index - offset;
4137 draw_stack_num(ctx, metrics, motif_position, raster);
4138 draw_stack(ctx, metrics, pspm.get_stack(motif_position, logo.alphabet, ssc), raster);
4139 }
4140 } else {
4141 if (col_index >= offset && col_index < (offset + pspm.get_motif_length())) {
4142 ctx.save();// s5.1
4143 ctx.translate(0, Math.round(metrics.stack_height));
4144 // TODO draw a dot or dash or something to indicate continuity of the motif
4145 ctx.restore(); //s5.1
4146 }
4147 }
4148 ctx.translate(Math.round(metrics.stack_width), 0);
4149 }
4150 ctx.restore();//s5
4151 ////optionally draw name if this is the last row but isn't the only row
4152 if (draw_name && (logo.get_rows() != 1 && pspm_i == (logo.get_rows()-1))) {
4153 //translate vertically past the stack and axis's
4154 ctx.translate(0, metrics.y_num_height/2 + metrics.stack_height +
4155 Math.max(metrics.y_num_height/2, metrics.x_num_above + metrics.x_num_width + metrics.name_spacer));
4156
4157 ctx.save();//s6
4158 ctx.translate(metrics.summed_width/2, metrics.name_height);
4159 ctx.font = metrics.name_font;
4160 ctx.textAlign = "center";
4161 ctx.fillText(pspm.name, 0, 0);
4162 ctx.restore();//s6
4163 ctx.translate(0, metrics.name_height);
4164 } else {
4165 //translate vertically past the stack and axis's
4166 ctx.translate(0, metrics.y_num_height/2 + metrics.stack_height +
4167 Math.max(metrics.y_num_height/2, metrics.x_num_above + metrics.x_num_width));
4168 }
4169 //if not the last row then add middle padding
4170 if (pspm_i != (logo.get_rows() -1)) {
4171 ctx.translate(0, metrics.pad_middle);
4172 }
4173 }
4174 ctx.restore();//s7
4175 if (logo.fine_text.length > 0) {
4176 ctx.translate(metrics.summed_width - metrics.pad_right, metrics.summed_height - metrics.pad_bottom);
4177 ctx.font = metrics.fine_txt_font;
4178 ctx.textAlign = "right";
4179 ctx.fillText(logo.fine_text, 0,0);
4180 }
4181 ctx.restore();//s2
4182 ctx.restore();//s1
4183 }
4184
4185 function create_canvas(c_width, c_height, c_id, c_title, c_display) {
4186 "use strict";
4187 var canvas = document.createElement("canvas");
4188 //check for canvas support before attempting anything
4189 if (!canvas.getContext) {
4190 return null;
4191 }
4192 var ctx = canvas.getContext('2d');
4193 //check for html5 text drawing support
4194 if (!supports_text(ctx)) {
4195 return null;
4196 }
4197 //size the canvas
4198 canvas.width = c_width;
4199 canvas.height = c_height;
4200 canvas.id = c_id;
4201 canvas.title = c_title;
4202 canvas.style.display = c_display;
4203 return canvas;
4204 }
4205
4206 function logo_1(alphabet, fine_text, pspm) {
4207 "use strict";
4208 var logo = new Logo(alphabet, fine_text);
4209 logo.add_pspm(pspm);
4210 return logo;
4211 }
4212
4213 function logo_2(alphabet, fine_text, target, query, query_offset) {
4214 "use strict";
4215 var logo = new Logo(alphabet, fine_text);
4216 if (query_offset < 0) {
4217 logo.add_pspm(target, -query_offset);
4218 logo.add_pspm(query);
4219 } else {
4220 logo.add_pspm(target);
4221 logo.add_pspm(query, query_offset);
4222 }
4223 return logo;
4224 }
4225
4226 /*
4227 * Specifies an alternate source for an image.
4228 * If the image with the image_id specified has
4229 * not loaded then a generated logo will be used
4230 * to replace it.
4231 *
4232 * Note that the image must either have dimensions
4233 * or a scale must be set.
4234 */
4235 function alternate_logo(logo, image_id, scale) {
4236 "use strict";
4237 var image = document.getElementById(image_id);
4238 if (!image) {
4239 alert("Can't find specified image id (" + image_id + ")");
4240 return;
4241 }
4242 //if the image has loaded then there is no reason to use the canvas
4243 if (image_ok(image)) {
4244 return;
4245 }
4246 //the image has failed to load so replace it with a canvas if we can.
4247 var canvas = create_canvas(image.width, image.height, image_id, image.title, image.style.display);
4248 if (canvas === null) {
4249 return;
4250 }
4251 //draw the logo on the canvas
4252 draw_logo_on_canvas(logo, canvas, null, scale);
4253 //replace the image with the canvas
4254 image.parentNode.replaceChild(canvas, image);
4255 }
4256
4257 /*
4258 * Specifes that the element with the specified id
4259 * should be replaced with a generated logo.
4260 */
4261 function replace_logo(logo, replace_id, scale, title_txt, display_style) {
4262 "use strict";
4263 var element = document.getElementById(replace_id);
4264 if (!replace_id) {
4265 alert("Can't find specified id (" + replace_id + ")");
4266 return;
4267 }
4268 //found the element!
4269 var canvas = create_canvas(50, 120, replace_id, title_txt, display_style);
4270 if (canvas === null) {
4271 return;
4272 }
4273 //draw the logo on the canvas
4274 draw_logo_on_canvas(logo, canvas, null, scale);
4275 //replace the element with the canvas
4276 element.parentNode.replaceChild(canvas, element);
4277 }
4278
4279 /*
4280 * Fast string trimming implementation found at
4281 * http://blog.stevenlevithan.com/archives/faster-trim-javascript
4282 *
4283 * Note that regex is good at removing leading space but
4284 * bad at removing trailing space as it has to first go through
4285 * the whole string.
4286 */
4287 function trim (str) {
4288 "use strict";
4289 var ws, i;
4290 str = str.replace(/^\s\s*/, '');
4291 ws = /\s/; i = str.length;
4292 while (ws.test(str.charAt(--i)));
4293 return str.slice(0, i + 1);
4294 }
4295 </script>
4296 <script>
4297
4298 // PRIVATE GLOBAL (uhoh)
4299 var _block_colour_lookup = {};
4300
4301 function block_colour(index) {
4302 function hsl2rgb(hue, saturation, lightness) {
4303 "use strict";
4304 function _hue(p, q, t) {
4305 "use strict";
4306 if (t < 0) t += 1;
4307 else if (t > 1) t -= 1;
4308 if (t < (1.0 / 6.0)) {
4309 return p + ((q - p) * 6.0 * t);
4310 } else if (t < 0.5) {
4311 return q;
4312 } else if (t < (2.0 / 3.0)) {
4313 return p + ((q - p) * ((2.0 / 3.0) - t) * 6.0);
4314 } else {
4315 return p;
4316 }
4317 }
4318 function _pad_hex(value) {
4319 var hex = Math.round(value * 255).toString(16);
4320 if (hex.length < 2) hex = "0" + hex;
4321 return hex;
4322 }
4323 var r, g, b, p, q;
4324 if (saturation == 0) {
4325 // achromatic (grayscale)
4326 r = lightness;
4327 g = lightness;
4328 b = lightness;
4329 } else {
4330 if (lightness < 0.5) {
4331 q = lightness * (1 + saturation);
4332 } else {
4333 q = lightness + saturation - (lightness * saturation);
4334 }
4335 p = (2 * lightness) - q;
4336 r = _hue(p, q, hue + (1.0 / 3.0));
4337 g = _hue(p, q, hue);
4338 b = _hue(p, q, hue - (1.0 / 3.0));
4339 }
4340 return "#" + _pad_hex(r) + _pad_hex(g) + _pad_hex(b);
4341 }
4342 if (typeof index !== "number" || index % 1 !== 0 || index < 0) return "#000000";
4343 // check for override
4344 if (_block_colour_lookup[index] == null) {
4345 var start = 0; //red
4346 var sat = 100;
4347 var light = 50;
4348 var divisions = 1 << Math.ceil(Math.log(index + 1) / Math.LN2);
4349 hue = start + (360 / divisions) * ((index - (divisions >> 1)) * 2 + 1);
4350 // colour input fields only support values in the form #RRGGBB
4351 _block_colour_lookup[index] = hsl2rgb(hue / 360, sat / 100, light / 100);
4352 }
4353 return _block_colour_lookup[index];
4354 }
4355
4356 function set_block_colour(index, new_colour) {
4357 _block_colour_lookup[index] = new_colour;
4358 var blocks = document.querySelectorAll("div.block_motif[data-colour-index=\"" + index + "\"]");
4359 var i;
4360 for (i = 0; i < blocks.length; i++) {
4361 blocks[i].style.backgroundColor = new_colour;
4362 }
4363 var swatches = document.querySelectorAll("div.legend_swatch[data-colour-index=\"" + index + "\"]");
4364 var picker;
4365 for (i = 0; i < swatches.length; i++) {
4366 swatches[i].style.backgroundColor = new_colour;
4367 picker = swatches[i].querySelector("input[type=\"color\"]");
4368 if (picker != null) picker.value = new_colour;
4369 }
4370 }
4371
4372 function make_block_legend_entry(motif_name, motif_colour_index) {
4373 if (typeof make_block_legend_entry.has_colour_picker !== "boolean") {
4374 // test if colour picker is supported, based off Modernizer
4375 // see http://stackoverflow.com/a/7787648/66387
4376 make_block_legend_entry.has_colour_picker = (function() {
4377 var doc_ele = document.documentElement;
4378 // We first check to see if the type we give it sticks..
4379 var input_ele = document.createElement('input');
4380 input_ele.setAttribute('type', 'color');
4381 var value_ok = input_ele.type !== 'text';
4382 if (value_ok) {
4383 // If the type does, we feed it a textual value, which shouldn't be valid.
4384 // If the value doesn't stick, we know there's input sanitization which infers a custom UI
4385 var smile = ':)';
4386 input_ele.value = smile;
4387 input_ele.style.cssText = 'position:absolute;visibility:hidden;';
4388 // chuck into DOM and force reflow for Opera bug in 11.00
4389 // github.com/Modernizr/Modernizr/issues#issue/159
4390 doc_ele.appendChild(input_ele);
4391 doc_ele.offsetWidth;
4392 value_ok = input_ele.value != smile;
4393 doc_ele.removeChild(input_ele);
4394 }
4395 return value_ok;
4396 })();
4397 }
4398 var entry = document.createElement("div");
4399 entry.className = "legend_entry";
4400 var swatch;
4401 swatch = document.createElement("div");
4402 swatch.className = "legend_swatch";
4403 swatch.setAttribute("data-colour-index", motif_colour_index);
4404 swatch.style.backgroundColor = block_colour(motif_colour_index);
4405 if (make_block_legend_entry.has_colour_picker) {
4406 var picker = document.createElement("input");
4407 picker.type = "color";
4408 picker.value = block_colour(motif_colour_index);
4409 picker.addEventListener("change", function(e) {
4410 set_block_colour(motif_colour_index, picker.value);
4411 }, false);
4412 swatch.addEventListener("click", function(e) {
4413 picker.click();
4414 }, false);
4415 swatch.appendChild(picker);
4416 }
4417 entry.appendChild(swatch);
4418 var name = document.createElement("div");
4419 name.className = "legend_text";
4420 name.appendChild(document.createTextNode(motif_name));
4421 entry.appendChild(name);
4422 return entry;
4423 }
4424
4425 function make_block_ruler(max_len) {
4426 var container = document.createElement("div");
4427 container.className = "block_container";
4428 var step;
4429 if (max_len < 50) {
4430 step = 1;
4431 } else if (max_len < 100) {
4432 step = 2;
4433 } else if (max_len < 200) {
4434 step = 4;
4435 } else if (max_len < 500) {
4436 step = 10;
4437 } else if (max_len < 1000) {
4438 step = 20;
4439 } else if (max_len < 2000) {
4440 step = 40;
4441 } else if (max_len < 5000) {
4442 step = 100;
4443 } else if (max_len < 10000) {
4444 step = 200;
4445 } else if (max_len < 20000) {
4446 step = 400;
4447 } else {
4448 step = Math.floor(max_len / 20000) * 400;
4449 }
4450 var peroid;
4451 if (max_len < 10) {
4452 peroid = 1;
4453 } else if (max_len < 20) {
4454 peroid = 2;
4455 } else {
4456 peroid = 5;
4457 }
4458 var i, cycle, offset, tic, label;
4459 for (i = 0, cycle = 0; i < max_len; i += step, cycle = (cycle + 1) % peroid) {
4460 offset = "" + ((i / max_len) * 100) + "%";
4461 tic = document.createElement("div");
4462 tic.style.left = offset;
4463 tic.className = (cycle == 0 ? "tic_major" : "tic_minor");
4464 container.appendChild(tic);
4465 if (cycle == 0) {
4466 label = document.createElement("div");
4467 label.className = "tic_label";
4468 label.style.left = offset;
4469 label.appendChild(document.createTextNode(i));
4470 container.appendChild(label);
4471 }
4472 }
4473 return container;
4474 }
4475
4476 function _calculate_block_needle_drag_pos(e, data) {
4477 var mouse;
4478 e = e || window.event;
4479 if (e.pageX || ev.pageY) {
4480 mouse = {"x": e.pageX, "y": e.pageY};
4481 } else {
4482 mouse = {
4483 x:e.clientX + document.body.scrollLeft - document.body.clientLeft,
4484 y:e.clientY + document.body.scrollTop - document.body.clientTop
4485 };
4486 }
4487 var cont = data.container;
4488 var dragable_length = cont.clientWidth -
4489 (cont.style.paddingLeft ? cont.style.paddingLeft : 0) -
4490 (cont.style.paddingRight ? cont.style.paddingRight : 0);
4491 //I believe that the offset parent is the body
4492 //otherwise I would need to make this recursive
4493 //maybe clientLeft would work, but the explanation of
4494 //it is hard to understand and it apparently doesn't work
4495 //in firefox 2.
4496 var diff = mouse.x - cont.offsetLeft;
4497 if (diff < 0) diff = 0;
4498 if (diff > dragable_length) diff = dragable_length;
4499 var pos = Math.round(diff / dragable_length * data.max);
4500 if (pos > data.len) pos = data.len;
4501 return pos;
4502 }
4503
4504 function _update_block_needle_drag(e, data, done) {
4505 "use strict";
4506 var pos = _calculate_block_needle_drag_pos(e, data);
4507 // read the needle positions
4508 var left = parseInt(data.llabel.textContent, 10) - data.off - 1;
4509 var right = parseInt(data.rlabel.textContent, 10) - data.off;
4510 // validate needle positions
4511 if (left >= data.len) left = data.len - 1;
4512 if (left < 0) left = 0;
4513 if (right > data.len) right = data.len;
4514 if (right <= left) right = left + 1;
4515 // calculate the new needle positions
4516 if (data.moveboth) {
4517 var size = right - left;
4518 if (data.isleft) {
4519 if ((pos + size) > data.len) pos = data.len - size;
4520 left = pos;
4521 right = pos + size;
4522 } else {
4523 if ((pos - size) < 0) pos = size;
4524 left = pos - size;
4525 right = pos;
4526 }
4527 } else {
4528 if (data.isleft) {
4529 if (pos >= right) pos = right - 1;
4530 left = pos;
4531 } else {
4532 if (pos <= left) pos = left + 1;
4533 right = pos;
4534 }
4535 }
4536 // update the needle positions
4537 data.lneedle.style.left = "" + (left / data.max * 100) + "%";
4538 data.llabel.textContent = "" + (left + data.off + 1);
4539 data.rneedle.style.left = "" + (right / data.max * 100) + "%";
4540 data.rlabel.textContent = "" + (right + data.off);
4541 data.handler(left, right, done);
4542 }
4543
4544 function _make_block_needle_drag_start_handler(isleft, data) {
4545 return function (e) {
4546 data.isleft = isleft;
4547 data.moveboth = !(e.shiftKey);
4548 document.addEventListener("mousemove", data.drag_during, false);
4549 document.addEventListener("mouseup", data.drag_end, false);
4550 };
4551 }
4552
4553 function _make_block_needle_drag_end_handler(data) {
4554 return function (e) {
4555 document.removeEventListener("mousemove", data.drag_during, false);
4556 document.removeEventListener("mouseup", data.drag_end, false);
4557 _update_block_needle_drag(e, data, true);
4558 };
4559 }
4560
4561 function _make_block_needle_drag_during_handler(data) {
4562 return function (e) {
4563 _update_block_needle_drag(e, data, false);
4564 };
4565 }
4566
4567 // private function used by make_block_container
4568 function _make_block_needle(isleft, value, data) {
4569 var vbar = document.createElement('div');
4570 vbar.className = "block_needle " + (isleft ? "left" : "right");
4571 vbar.style.left = "" + (value / data.max * 100)+ "%";
4572 var label = document.createElement('div');
4573 label.className = "block_handle " + (isleft ? "left" : "right");
4574 // The needles sit between the sequence positions, so the left one sits at the
4575 // start and the right at the end. This is why 1 is added to the displayed
4576 // value for a left handle as the user doesn't need to know about this detail
4577 label.textContent = "" + (isleft ? value + data.off + 1 : value + data.off);
4578 label.unselectable = "on"; // so IE and Opera don't select the text, others are done in css
4579 label.title = "Drag to move the displayed range. Hold shift and drag to change " + (isleft ? "lower" : "upper") + " bound of the range.";
4580 vbar.appendChild(label);
4581 if (isleft) {
4582 data.lneedle = vbar;
4583 data.llabel = label;
4584 } else {
4585 data.rneedle = vbar;
4586 data.rlabel = label;
4587 }
4588 label.addEventListener("mousedown", _make_block_needle_drag_start_handler(isleft, data), false);
4589 return vbar;
4590 }
4591
4592 function make_block_container(is_stranded, has_both_strands, max_len, show_len, offset, range_handler) {
4593 offset = (offset != null ? offset : 0);
4594 // make the container for the block diagram
4595 var container = document.createElement("div");
4596 container.className = "block_container";
4597 container.setAttribute("data-max", max_len);
4598 container.setAttribute("data-off", offset);
4599 if (is_stranded) {
4600 var plus = document.createElement("div");
4601 plus.appendChild(document.createTextNode("+"));
4602 plus.className = "block_plus_sym";
4603 container.appendChild(plus);
4604 if (has_both_strands) {
4605 var minus = document.createElement("div");
4606 minus.appendChild(document.createTextNode("-"));
4607 minus.className = "block_minus_sym";
4608 container.appendChild(minus);
4609 }
4610 }
4611 var rule = document.createElement("div");
4612 rule.className = "block_rule";
4613 rule.style.width = ((show_len / max_len) * 100) + "%";
4614 container.appendChild(rule);
4615 if (range_handler != null) {
4616 var range_data = {
4617 "max": max_len,
4618 "len": show_len,
4619 "off": offset,
4620 "handler": range_handler,
4621 "container": container,
4622 "lneedle": null, "llabel": null,
4623 "rneedle": null, "rlabel": null,
4624 "isleft": false, "moveboth" : false
4625 };
4626 range_data.drag_during = _make_block_needle_drag_during_handler(range_data);
4627 range_data.drag_end = _make_block_needle_drag_end_handler(range_data);
4628 container.appendChild(_make_block_needle(false, 1, range_data)); // add right first so z-index works
4629 container.appendChild(_make_block_needle(true, 0, range_data));
4630 }
4631 return container;
4632 }
4633
4634 function make_block_label(container, max_len, pos, length, message) {
4635 "use strict";
4636 var label = document.createElement("div");
4637 label.className = "block_label";
4638 label.style.left = (((pos + (length / 2)) / max_len) * 100) + "%";
4639 label.appendChild(document.createTextNode(message));
4640 container.appendChild(label);
4641 }
4642
4643 function make_block(container, max_len,
4644 site_pos, site_len, site_pvalue, site_rc, site_colour_index, site_secondary) {
4645 "use strict";
4646 var block_height, block, block_region1, block_region2;
4647 var max_block_height = 12;
4648 var max_pvalue = 1e-10;
4649 // calculate the height of the block
4650 block_height = (site_pvalue < max_pvalue ? max_block_height :
4651 (Math.log(site_pvalue) / Math.log(max_pvalue)) * max_block_height);
4652 if (block_height < 1) block_height = 1;
4653 // create a block to represent the motif
4654 block = document.createElement("div");
4655 block.className = "block_motif" + (site_secondary ? " scanned_site" : "") + (site_rc ? " bottom" : " top");
4656 block.style.left = ((site_pos / max_len) * 100) + "%";
4657 block.style.top = (!site_rc ? max_block_height - block_height :
4658 max_block_height + 1) + "px";
4659 block.style.width = ((site_len / max_len) * 100) + "%";
4660 block.style.height = block_height + "px";
4661 block.style.backgroundColor = block_colour(site_colour_index);
4662 block.setAttribute("data-colour-index", site_colour_index);
4663 // add to container
4664 container.appendChild(block);
4665 var activator = function (e) {
4666 toggle_class(block, "active", true);
4667 var new_e = new e.constructor(e.type, e);
4668 block.dispatchEvent(new_e);
4669 };
4670 var deactivator = function (e) {
4671 toggle_class(block, "active", false);
4672 var new_e = new e.constructor(e.type, e);
4673 block.dispatchEvent(new_e);
4674 }
4675 // create a larger region to detect mouseover for the block
4676 block_region1 = document.createElement("div");
4677 block_region1.className = "block_region top" +
4678 (site_secondary ? " scanned_site" : "") + (site_rc ? "" : " main");
4679 block_region1.style.left = block.style.left;
4680 block_region1.style.width = block.style.width;
4681 block_region1.addEventListener('mouseover', activator, false);
4682 block_region1.addEventListener('mouseout', deactivator, false);
4683 container.appendChild(block_region1);
4684 block_region2 = document.createElement("div");
4685 block_region2.className = "block_region bottom" +
4686 (site_secondary ? " scanned_site" : "") + (site_rc ? " main" : "");
4687 block_region2.style.left = block.style.left;
4688 block_region2.style.width = block.style.width;
4689 block_region2.addEventListener('mouseover', activator, false);
4690 block_region2.addEventListener('mouseout', deactivator, false);
4691 container.appendChild(block_region2);
4692 return block;
4693 }
4694
4695 function set_block_needle_positions(containingNode, start, end) {
4696 var container, lneedle, llabel, rneedle, rlabel, max, off, left, right;
4697 container = (/\bblock_container\b/.test(containingNode.className) ? containingNode : containingNode.querySelector(".block_container"));
4698 max = parseInt(container.getAttribute("data-max"), 10);
4699 off = parseInt(container.getAttribute("data-off"), 10);
4700 left = start - off;
4701 right = end - off;
4702 lneedle = containingNode.querySelector(".block_needle.left");
4703 llabel = lneedle.querySelector(".block_handle.left");
4704 rneedle = containingNode.querySelector(".block_needle.right");
4705 rlabel = rneedle.querySelector(".block_handle.right");
4706 // update the needle positions
4707 lneedle.style.left = "" + (left / max * 100) + "%";
4708 llabel.textContent = "" + (left + off + 1);
4709 rneedle.style.left = "" + (right / max * 100) + "%";
4710 rlabel.textContent = "" + (right + off);
4711 }
4712
4713 function get_block_needle_positions(containingNode) {
4714 var container, llabel, rlabel, max, off, left, right;
4715 container = (/\bblock_container\b/.test(containingNode.className) ? containingNode : containingNode.querySelector(".block_container"));
4716 max = parseInt(container.getAttribute("data-max"), 10);
4717 off = parseInt(container.getAttribute("data-off"), 10);
4718 llabel = containingNode.querySelector(".block_needle.left > .block_handle.left");
4719 rlabel = containingNode.querySelector(".block_needle.right > .block_handle.right");
4720 left = parseInt(llabel.textContent, 10) - off - 1;
4721 right = parseInt(rlabel.textContent, 10) - off;
4722 return {"start": left + off, "end": right + off};
4723 }
4724 </script>
4725 <script>
4726 function make_alpha_bg_table(alph, freqs) {
4727 function colour_symbol(index) {
4728 var span = document.createElement("span");
4729 span.appendChild(document.createTextNode(alph.get_symbol(index)));
4730 span.style.color = alph.get_colour(index);
4731 span.className = "alpha_symbol";
4732 return span;
4733 }
4734 var table, thead, tbody, row, th, span, i;
4735 // create table
4736 table = document.createElement("table");
4737 table.className = "alpha_bg_table";
4738 // create header
4739 thead = document.createElement("thead");
4740 table.appendChild(thead);
4741 row = thead.insertRow(thead.rows.length);
4742 if (alph.has_complement()) {
4743 add_text_header_cell(row, "Name", "pop_alph_name");
4744 if (freqs != null) add_text_header_cell(row, "Freq.", "pop_alph_freq");
4745 if (alph.has_bg()) add_text_header_cell(row, "Bg.", "pop_alph_bg");
4746 add_text_header_cell(row, "");
4747 add_text_header_cell(row, "");
4748 add_text_header_cell(row, "");
4749 if (alph.has_bg()) add_text_header_cell(row, "Bg.", "pop_alph_bg");
4750 if (freqs != null) add_text_header_cell(row, "Freq.", "pop_alph_freq");
4751 add_text_header_cell(row, "Name", "pop_alph_name");
4752 } else {
4753 add_text_header_cell(row, "");
4754 add_text_header_cell(row, "Name", "pop_alph_name");
4755 if (freqs != null) add_text_header_cell(row, "Freq.", "pop_alph_freq");
4756 if (alph.has_bg()) add_text_header_cell(row, "Bg.", "pop_alph_bg");
4757 }
4758 // add alphabet entries
4759 tbody = document.createElement("tbody");
4760 table.appendChild(tbody);
4761 if (alph.has_complement()) {
4762 for (i = 0; i < alph.get_size_core(); i++) {
4763 var c = alph.get_complement(i);
4764 if (i > c) continue;
4765 row = tbody.insertRow(tbody.rows.length);
4766 add_text_cell(row, alph.get_name(i));
4767 if (freqs != null) add_text_cell(row, "" + freqs[i].toFixed(3));
4768 if (alph.has_bg()) add_text_cell(row, "" + alph.get_bg_freq(i).toFixed(3));
4769 add_cell(row, colour_symbol(i));
4770 add_text_cell(row, "~");
4771 add_cell(row, colour_symbol(c));
4772 if (alph.has_bg()) add_text_cell(row, "" + alph.get_bg_freq(c).toFixed(3));
4773 if (freqs != null) add_text_cell(row, "" + freqs[c].toFixed(3));
4774 add_text_cell(row, alph.get_name(c));
4775 }
4776 } else {
4777 for (i = 0; i < alph.get_size_core(); i++) {
4778 row = tbody.insertRow(tbody.rows.length);
4779 add_cell(row, colour_symbol(i));
4780 add_text_cell(row, alph.get_name(i));
4781 if (freqs != null) add_text_cell(row, "" + freqs[i].toFixed(3));
4782 if (alph.has_bg()) add_text_cell(row, "" + alph.get_bg_freq(i).toFixed(3));
4783 }
4784 }
4785 return table;
4786 }
4787
4788 </script>
4789 <script>
4790 var current_motif = 0;
4791 var meme_alphabet = new Alphabet(data.alphabet, data.background.freqs);
4792
4793 var DelayLogoTask = function(logo, canvas) {
4794 this.logo = logo;
4795 this.canvas = canvas;
4796 };
4797
4798 DelayLogoTask.prototype.run = function () {
4799 draw_logo_on_canvas(this.logo, this.canvas, false);
4800 };
4801
4802 function motif_pspm(index) {
4803 var motif, pwm, psm, name, ltrim, rtrim, nsites, evalue;
4804 // get motif
4805 motif = data["motifs"][index];
4806 // get motif paramters
4807 pwm = motif["pwm"];
4808 psm = motif["psm"];
4809 name = "" + (index + 1); ltrim = 0; rtrim = 0;
4810 nsites = motif["nsites"]; evalue = motif["evalue"];
4811 // make pspm
4812 return new Pspm(pwm, name, ltrim, rtrim, nsites, evalue, psm);
4813 }
4814
4815 function motif_count_matrix(index) {
4816 return motif_pspm(index).as_count_matrix();
4817 }
4818
4819 function motif_prob_matrix(index) {
4820 return motif_pspm(index).as_probability_matrix();
4821 }
4822
4823 function motif_minimal_meme(index) {
4824 return motif_pspm(index).as_meme({
4825 "with_header": true,
4826 "with_pspm": true,
4827 "with_pssm": true,
4828 "version": data["version"],
4829 "alphabet": meme_alphabet,
4830 "strands": (meme_alphabet.has_complement() && data.options.revcomp ? 2 : 1)
4831 });
4832 }
4833
4834 function motif_fasta(index) {
4835 "use strict";
4836 var motif, sites, site, seq, sequences, sequence, i, num, counter, out;
4837 counter = {};
4838 sequences = data["sequence_db"]["sequences"];
4839 motif = data["motifs"][index];
4840 sites = motif["sites"];
4841 out = "";
4842 for (i = 0; i < sites.length; i++) {
4843 site = sites[i];
4844 seq = site["seq"];
4845 sequence = sequences[seq];
4846 counter[seq] = (num = counter[seq]) ? (++num) : (num = 1); // inc counter
4847 if (i !== 0) {out += "\n";}
4848 out += ">" + sequence["name"] + "_site_" + num + " offset= " + site["pos"] +
4849 (site["rc"] ? " RC\n" : "\n");
4850 out += site["match"];
4851 }
4852 return out;
4853 }
4854
4855 function motif_raw(index) {
4856 "use strict";
4857 var sites, i, out;
4858 sites = data["motifs"][index]["sites"];
4859 out = "";
4860 for (i = 0; i < sites.length; i++) {
4861 if (i !== 0) {out += "\n";}
4862 out += sites[i]["match"];
4863 }
4864 return out;
4865 }
4866
4867 function clone_template(template) {
4868 "use strict";
4869 var node, help_btns, i, button;
4870 node = $(template).cloneNode(true);
4871 toggle_class(node, "template", false);
4872 node.id = "";
4873 help_btns = node.querySelectorAll(".help");
4874 for (i = 0; i < help_btns.length; i++) {
4875 button = help_btns[i];
4876 if (button.hasAttribute("data-topic")) {
4877 button.tabIndex = "0";
4878 button.addEventListener("click", __toggle_help, false);
4879 button.addEventListener("keydown", __toggle_help, false);
4880 }
4881 }
4882 return node;
4883 }
4884
4885 function set_tvar(template, tvar, value) {
4886 var node;
4887 node = find_child(template, tvar);
4888 if (node === null) {
4889 throw new Error("Template does not contain variable " + tvar);
4890 }
4891 node.innerHTML = "";
4892 if (typeof value !== "object") {
4893 node.appendChild(document.createTextNode(value));
4894 } else {
4895 node.appendChild(value);
4896 }
4897 }
4898
4899 function make_logo(alphabet, pspm, rc, offset, className) {
4900 if (rc) pspm = pspm.copy().reverse_complement(alphabet);
4901 var logo = new Logo(alphabet, "");
4902 logo.add_pspm(pspm, offset);
4903 var canvas = document.createElement('canvas');
4904 canvas.height = 50;
4905 canvas.width = 0;
4906 canvas.className = className;
4907 size_logo_on_canvas(logo, canvas, false);
4908 add_draw_task(canvas, new DelayLogoTask(logo, canvas));
4909 return canvas;
4910 }
4911
4912 function make_small_logo(alphabet, pspm, options) {
4913 if (typeof options === "undefined") options = {};
4914 if (options.rc) pspm = pspm.copy().reverse_complement(alphabet);
4915 var logo = new Logo(alphabet, {x_axis: false, y_axis: false});
4916 logo.add_pspm(pspm, (typeof options.offset === "number" ? options.offset : 0));
4917 var canvas = document.createElement('canvas');
4918 if (typeof options.className === "string") canvas.className = options.className;
4919 if (typeof options.width === "number" && options.width > 0) {
4920 canvas.height = 0;
4921 canvas.width = options.width;
4922 draw_logo_on_canvas(logo, canvas, false);
4923 } else {
4924 draw_logo_on_canvas(logo, canvas, false, 1/3);
4925 }
4926 return canvas;
4927 }
4928
4929 function make_large_logo(alphabet, pspm, rc, offset, className) {
4930 if (rc) pspm = pspm.copy().reverse_complement(alphabet);
4931 var logo = new Logo(alphabet, "");
4932 logo.add_pspm(pspm, offset);
4933 var canvas = document.createElement('canvas');
4934 canvas.height = 200;
4935 canvas.width = 0;
4936 canvas.className = className;
4937 size_logo_on_canvas(logo, canvas, false);
4938 add_draw_task(canvas, new DelayLogoTask(logo, canvas));
4939 return canvas;
4940 }
4941
4942 function make_sym_btn(symbol, title, action) {
4943 var box;
4944 box = document.createElement("div");
4945 box.tabIndex = 0;
4946 box.className = "sym_btn";
4947 box.appendChild(document.createTextNode(symbol));
4948 box.title = title;
4949 box.addEventListener('click', action, false);
4950 box.addEventListener('keydown', action, false);
4951 return box;
4952 }
4953
4954 function make_seq(alphabet, seq) {
4955 var i, j, letter, lbox, sbox;
4956 sbox = document.createElement("span");
4957 for (i = 0; i < seq.length; i = j) {
4958 letter = seq.charAt(i);
4959 for (j = i+1; j < seq.length; j++) {
4960 if (seq.charAt(j) !== letter) {
4961 break;
4962 }
4963 }
4964 lbox = document.createElement("span");
4965 lbox.style.color = alphabet.get_colour(alphabet.get_index(letter));
4966 lbox.appendChild(document.createTextNode(seq.substring(i, j)));
4967 sbox.appendChild(lbox);
4968 }
4969 return sbox;
4970 }
4971
4972 //
4973 // make_pv_text
4974 //
4975 // Returns the string p-value, with the p italicised.
4976 ///
4977 function make_pv_text() {
4978 var pv_text = document.createElement("span");
4979 var pv_italic_text = document.createElement("span");
4980 pv_italic_text.appendChild(document.createTextNode("p"));
4981 pv_italic_text.style.fontStyle = "italic";
4982 pv_text.appendChild(pv_italic_text);
4983 pv_text.appendChild(document.createTextNode("-value"));
4984 return pv_text;
4985 }
4986
4987 function append_site_entries(tbody, motif, site_index, count) {
4988 "use strict";
4989 var i, end;
4990 var sites, site, sequences, sequence;
4991 var rbody;
4992 if (typeof count !== "number") {
4993 count = 20;
4994 }
4995 sequences = data["sequence_db"]["sequences"];
4996 sites = motif["sites"];
4997 end = Math.min(site_index + count, sites.length);
4998 for (i = site_index; i < end; i++) {
4999 site = sites[i];
5000 sequence = sequences[site["seq"]];
5001
5002 rbody = tbody.insertRow(tbody.rows.length);
5003 add_text_cell(rbody, "" + (site["seq"] + 1) + ".", "site_num");
5004 add_text_cell(rbody, sequence["name"], "site_name");
5005 add_text_cell(rbody, site["rc"] ? "-" : "+", "site_strand");
5006 add_text_cell(rbody, site["pos"] + 1, "site_start");
5007 add_text_cell(rbody, site["pvalue"].toExponential(2), "site_pvalue");
5008 add_text_cell(rbody, site["lflank"], "site lflank");
5009 add_cell(rbody, make_seq(meme_alphabet, site["match"]), "site match");
5010 add_text_cell(rbody, site["rflank"], "site rflank");
5011 }
5012 return i;
5013 }
5014
5015 function make_site_entries() {
5016 "use strict";
5017 var region;
5018 region = this;
5019 if (region.data_site_index >= region.data_motif["sites"].length) {
5020 // all sites created
5021 region.removeEventListener('scroll', make_site_entries, false);
5022 return;
5023 }
5024 // if there's still 100 pixels to scroll than don't do anything yet
5025 if (region.scrollHeight - (region.scrollTop + region.offsetHeight) > 100) {
5026 return;
5027 }
5028
5029 region.data_site_index = append_site_entries(
5030 find_child(region, "sites_tbl").tBodies[0],
5031 region.data_motif, region.data_site_index, 20
5032 );
5033 }
5034
5035 function make_sites(motif) {
5036 "use strict";
5037 function add_site_header(row, title, nopad, help_topic, tag_class) {
5038 var div, divcp, th;
5039 th = document.createElement("th");
5040 div = document.createElement("div");
5041 div.className = "sites_th_inner";
5042 if (typeof title !== "object") {
5043 title = document.createTextNode("" + title);
5044 }
5045 div.appendChild(title);
5046 if (help_topic) {
5047 div.appendChild(document.createTextNode("\xA0"));
5048 div.appendChild(help_button(help_topic));
5049 }
5050 divcp = div.cloneNode(true);
5051 divcp.className = "sites_th_hidden";
5052 th.appendChild(div);
5053 th.appendChild(divcp);
5054 if (nopad) {
5055 th.className = "nopad";
5056 }
5057 if (tag_class) {
5058 th.className += " " + tag_class;
5059 }
5060 row.appendChild(th);
5061 }
5062 var outer_tbl, inner_tbl, tbl, thead, tbody, rhead;
5063
5064 outer_tbl = document.createElement("div");
5065 outer_tbl.className = "sites_outer";
5066
5067 inner_tbl = document.createElement("div");
5068 inner_tbl.className = "sites_inner";
5069 outer_tbl.appendChild(inner_tbl);
5070
5071 tbl = document.createElement("table");
5072 tbl.className = "sites_tbl";
5073 inner_tbl.appendChild(tbl);
5074
5075 thead = document.createElement("thead");
5076 tbl.appendChild(thead);
5077 tbody = document.createElement("tbody");
5078 tbl.appendChild(tbody);
5079
5080 rhead = thead.insertRow(thead.rows.length);
5081 add_site_header(rhead, "", true);
5082 add_site_header(rhead, "Name", false, "pop_seq_name");
5083 add_site_header(rhead, "Strand", false, "pop_site_strand", "site_strand_title");
5084 add_site_header(rhead, "Start", false, "pop_site_start");
5085 add_site_header(rhead, make_pv_text(), false, "pop_site_pvalue");
5086 add_site_header(rhead, "", false);
5087 add_site_header(rhead, "Sites", true, "pop_site_match");
5088 add_site_header(rhead, "", false);
5089
5090 inner_tbl.data_motif = motif;
5091 inner_tbl.data_site_index = append_site_entries(tbody, motif, 0, 20);
5092 if (inner_tbl.data_site_index < motif["sites"].length) {
5093 inner_tbl.addEventListener('scroll', make_site_entries, false);
5094 }
5095 return outer_tbl;
5096 }
5097
5098 function make_motif_table_entry(row, alphabet, ordinal, motif, colw) {
5099 "use strict";
5100 function ev_sig(evalue_str) {
5101 "use strict";
5102 var ev_re, match, sig, exp, num;
5103 ev_re = /^(.*)e(.*)$/;
5104 if (match = ev_re.exec(evalue_str)) {
5105 sig = parseFloat(match[1]);
5106 exp = parseInt(match[2]);
5107 if (exp >= 0) {
5108 return false;
5109 } else if (exp <= -3) {
5110 return true;
5111 } else {
5112 return sig * Math.pow(10, exp) <= 0.05;
5113 }
5114 }
5115 return true;
5116 }
5117 function make_preview(alphabet, motif) {
5118 "use strict";
5119 var pspm, preview, preview_rc;
5120 var box, btn_box, logo_box, btn_plus, btn_minus;
5121 if (motif["preview_logo"]) {
5122 preview = motif["preview_logo"];
5123 preview_rc = motif["preview_logo_rc"];
5124 } else {
5125 pspm = new Pspm(motif["pwm"]);
5126 preview = make_logo(alphabet, pspm);
5127 motif["preview_logo"] = preview;
5128 if (alphabet.has_complement()) {
5129 preview_rc = make_logo(alphabet, pspm, true, 0, "logo_rc");
5130 motif["preview_logo_rc"] = preview_rc;
5131 }
5132 }
5133 if (preview_rc) {
5134 btn_plus = document.createElement("div");
5135 btn_plus.appendChild(document.createTextNode("+"));
5136 btn_plus.className = "preview_btn plus";
5137 btn_plus.tabIndex = "0";
5138 btn_plus.addEventListener("click", action_btn_rc, false);
5139 btn_plus.addEventListener("keydown", action_btn_rc, false);
5140 btn_minus = document.createElement("div");
5141 btn_minus.appendChild(document.createTextNode("-"));
5142 btn_minus.className = "preview_btn minus";
5143 btn_minus.tabIndex = "0";
5144 btn_minus.addEventListener("click", action_btn_rc, false);
5145 btn_minus.addEventListener("keydown", action_btn_rc, false);
5146 btn_box = document.createElement("div");
5147 btn_box.className = "preview_btn_box";
5148 btn_box.appendChild(btn_plus);
5149 btn_box.appendChild(btn_minus);
5150 }
5151 logo_box = document.createElement("div");
5152 logo_box.className = "preview_logo_box";
5153 logo_box.appendChild(preview);
5154 if (preview_rc) logo_box.appendChild(preview_rc);
5155 box = document.createElement("div");
5156 box.className = "preview_box";
5157 if (preview_rc) box.appendChild(btn_box);
5158 box.appendChild(logo_box);
5159 if (preview_rc) {
5160 if (motif["rc"]) {
5161 btn_minus.className += " active";
5162 logo_box.className += " show_rc_logo";
5163 } else {
5164 btn_plus.className += " active";
5165 }
5166 }
5167 return box;
5168 }
5169 var pspm, preview, preview_rc, c;
5170 row.data_motif = motif;
5171 row.data_ordinal = ordinal;
5172 if (!ev_sig(motif["evalue"])) {
5173 row.style.opacity = 0.4;
5174 }
5175 add_text_cell(row, "" + ordinal + ".", "motif_ordinal");
5176 add_cell(row, make_preview(alphabet, motif), "motif_logo");
5177 add_text_cell(row, motif["evalue"], "motif_evalue");
5178 add_text_cell(row, motif["nsites"], "motif_nsites");
5179 add_text_cell(row, motif["len"], "motif_width");
5180 add_cell(row, make_sym_btn("\u21A7", "Show more information.",
5181 action_show_more), "motif_more");
5182 add_cell(row,
5183 make_sym_btn("\u21E2",
5184 "Submit the motif to another MEME Suite program or download it.",
5185 action_show_outpop),
5186 "motif_submit");
5187 if (colw) {
5188 for (c = 0; c < row.cells.length; c++) {
5189 row.cells[c].style.minWidth = colw[c] + "px";
5190 }
5191 }
5192 }
5193
5194 function make_motifs_table(alphabet, start_ordinal, motifs, colw, stop_reason) {
5195 var i, j;
5196 var tbl, thead, tbody, tfoot, row, preview;
5197 var motif, pspm;
5198
5199 tbl = document.createElement("table");
5200
5201 thead = document.createElement("thead");
5202 tbl.appendChild(thead);
5203 tbody = document.createElement("tbody");
5204 tbl.appendChild(tbody);
5205 tfoot = document.createElement("tfoot");
5206 tbl.appendChild(tfoot);
5207
5208 row = thead.insertRow(thead.rows.length);
5209 add_text_header_cell(row, "", "", "motif_ordinal");
5210 add_text_header_cell(row, "Logo", "", "motif_logo");
5211 add_text_header_cell(row, "E-value", "pop_ev", "motif_evalue");
5212 add_text_header_cell(row, "Sites", "pop_sites", "motif_nsites");
5213 add_text_header_cell(row, "Width", "pop_width", "motif_width");
5214 add_text_header_cell(row, "More", "pop_more", "motif_more");
5215 add_text_header_cell(row, "Submit/Download", "pop_submit_dl", "motif_submit");
5216
5217 for (i = 0; i < motifs.length; i++) {
5218 row = tbody.insertRow(tbody.rows.length);
5219 make_motif_table_entry(row, alphabet, start_ordinal + i, motifs[i], colw);
5220 }
5221
5222 row = tfoot.insertRow(tfoot.rows.length);
5223 add_text_header_cell(row, stop_reason, "", "stop_reason", "", 6);
5224
5225 return tbl;
5226 }
5227
5228 function make_expanded_motif(alphabet, ordinal, motif, less_x, submit_x) {
5229 "use strict";
5230 var box, pspm, logo_box, large_logo, large_logo_rc, tab_logo, tab_logo_rc;
5231 var btn, offset, norc;
5232
5233 box = clone_template("tmpl_motif_expanded");
5234 box.data_motif = motif;
5235 box.data_ordinal = ordinal;
5236
5237 pspm = new Pspm(motif["pwm"]);
5238 if (typeof motif["rc"] !== "boolean") {
5239 motif["rc"] = false;
5240 }
5241 if (motif["large_logo"]) {
5242 large_logo = motif["large_logo"];
5243 large_logo_rc = motif["large_logo_rc"];
5244 } else {
5245 large_logo = make_large_logo(alphabet, pspm, false, 0);
5246 motif["large_logo"] = large_logo;
5247 if (alphabet.has_complement()) {
5248 large_logo_rc = make_large_logo(alphabet, pspm, true, 0, "logo_rc");
5249 motif["large_logo_rc"] = large_logo_rc;
5250 }
5251 }
5252 norc = (large_logo_rc == null);
5253 toggle_class(box, "norc", norc);
5254
5255 logo_box = find_child(box, "tvar_logo");
5256 logo_box.appendChild(large_logo);
5257 if (large_logo_rc) logo_box.appendChild(large_logo_rc);
5258 toggle_class(logo_box, "show_rc_logo", motif["rc"]);
5259
5260 tab_logo = find_child(box, "tvar_tab");
5261 tab_logo_rc = find_child(box, "tvar_tab_rc");
5262
5263 toggle_class(tab_logo, "activeTab", !motif["rc"]);
5264 toggle_class(tab_logo_rc, "activeTab", motif["rc"]);
5265
5266 tab_logo.addEventListener('click', action_rc_tab, false);
5267 tab_logo.addEventListener('keydown', action_rc_tab, false);
5268 tab_logo_rc.addEventListener('click', action_rc_tab, false);
5269 tab_logo_rc.addEventListener('keydown', action_rc_tab, false);
5270
5271 set_tvar(box, "tvar_ordinal", ordinal);
5272 set_tvar(box, "tvar_evalue", motif["evalue"]);
5273 set_tvar(box, "tvar_width", motif["len"]);
5274 set_tvar(box, "tvar_site_count", motif["nsites"]);
5275 set_tvar(box, "tvar_llr", motif["llr"]);
5276 set_tvar(box, "tvar_ic", motif["ic"]);
5277 set_tvar(box, "tvar_re", motif["re"]);
5278 set_tvar(box, "tvar_bt", motif["bt"]);
5279 set_tvar(box, "tvar_sites", make_sites(motif));
5280
5281 offset = 32; // 1* 5px padding + 2 * 10px padding + 2 * 2px border + 3px ??
5282
5283 btn = find_child(box, "tvar_less");
5284 btn.style.left = (less_x - offset) + "px";
5285 btn.addEventListener('click', action_show_less, false);
5286 btn.addEventListener('keydown', action_show_less, false);
5287 btn = find_child(box, "tvar_submit");
5288 btn.style.left = (submit_x - offset) + "px";
5289 btn.addEventListener('click', action_show_outpop, false);
5290 btn.addEventListener('keydown', action_show_outpop, false);
5291 return box;
5292 }
5293
5294
5295 //
5296 //
5297 ///
5298 function make_motifs() {
5299 "use strict";
5300 function pixel_value(str_in) {
5301 "use strict";
5302 var px_re, match;
5303 px_re = /^(\d+)px$/;
5304 if (match = px_re.exec(str_in)) {
5305 return parseInt(match[1], 10);
5306 }
5307 return 0;
5308 }
5309 var container, tbl;
5310 var colw, r, row, c, cell, cell_style, pad_left, pad_right;
5311
5312 // make the motifs table
5313 container = $("motifs");
5314 container.innerHTML = ""; // clear content
5315
5316 tbl = make_motifs_table(meme_alphabet, 1, data["motifs"], colw, data["stop_reason"]);
5317 container.appendChild(tbl);
5318
5319 // measure table column widths
5320 colw = [];
5321 row = tbl.tBodies[0].rows[0];
5322 for (c = 0; c < row.cells.length; c++) {
5323 var padLeft, padRight;
5324 cell = row.cells[c];
5325 cell_style = window.getComputedStyle(cell, null);
5326 pad_left = pixel_value(cell_style.getPropertyValue("padding-left"));
5327 pad_right = pixel_value(cell_style.getPropertyValue("padding-right"));
5328 colw[c] = cell.clientWidth - pad_left - pad_right;
5329 if (typeof colw[c] !== "number" || colw[c] < 0) {
5330 colw[c] = 1;
5331 }
5332 }
5333
5334 // set minimum table column widths on each row so later when we remove rows it still aligns
5335 for (r = 0; r < tbl.tBodies[0].rows.length; r++) {
5336 row = tbl.tBodies[0].rows[r];
5337 for (c = 0; c < row.cells.length; c++) {
5338 row.cells[c].style.minWidth = colw[c] + "px";
5339 }
5340 }
5341
5342 // store the table column widths so we can create rows latter with the same minimums
5343 container.data_colw = colw;
5344
5345 // calculate the x offset for the buttons
5346 row = tbl.tBodies[0].rows[0];
5347 container.data_more_x = coords(find_child(find_child(row, "motif_more"), "sym_btn"))[0];
5348 container.data_submit_x = coords(find_child(find_child(row, "motif_submit"), "sym_btn"))[0];
5349
5350 draw_on_screen();
5351 }
5352
5353 function make_meme_block(container, max_seq_len, is_scan, site) {
5354 "use strict";
5355 var motif = data.motifs[site.motif];
5356 var block = make_block(container, max_seq_len, site.pos, motif.len,
5357 site.pvalue, site.rc, site.motif, is_scan);
5358 var handler = (is_scan ?
5359 make_scan_popup(site, motif, block) :
5360 make_block_popup(site, motif, block));
5361 block.addEventListener("mouseover", handler, false);
5362 block.addEventListener("mouseout", handler, false);
5363 }
5364
5365 function append_blocks_entries(tbody, seq_index, count) {
5366 "use strict";
5367 var i, end, j;
5368 var max_pvalue, max_block_height, max_seq_len, sequences;
5369 var sequence, sites, scans, scan;
5370 var container, plus, minus, rule, row;
5371 // define some constants
5372 max_seq_len = data.sequence_db.max_length;
5373 // determine how many to load
5374 end = Math.min(seq_index + count, data.sequence_db.sequences.length);
5375 for (i = seq_index; i < end; i++) {
5376 // get the sequence
5377 sequence = data.sequence_db.sequences[i];
5378 // make the containers for the block diagram
5379 container = make_block_container(meme_alphabet.has_complement(),
5380 data.options.revcomp, max_seq_len, sequence.length);
5381 // create blocks for the motif sites
5382 sites = sequence["sites"];
5383 for (j = 0; j < sites.length; j++)
5384 make_meme_block(container, max_seq_len, false, sites[j]);
5385 // create blocks for the scanned sites
5386 scan = data.scan[i];
5387 for (j = 0; j < scan.sites.length; j++)
5388 make_meme_block(container, max_seq_len, true, scan.sites[j]);
5389 // create a row for the sequence
5390 row = tbody.insertRow(tbody.rows.length);
5391 toggle_class(row, "empty_seq", sites.length == 0 && scan.sites.length == 0);
5392 toggle_class(row, "only_scan", sites.length == 0 && scan.sites.length > 0);
5393 add_text_cell(row, (i + 1) + ".", "blockdiag_num");
5394 add_text_cell(row, sequence["name"], "blockdiag_name");
5395 add_text_cell(row, scan["pvalue"].toExponential(2), "blockdiag_pvalue");
5396 add_cell(row, container, "block_td");
5397 }
5398 return end;
5399 }
5400
5401 function make_blocks_entries() {
5402 "use strict";
5403 var region;
5404 region = this;
5405 if (region.data_blocks_index >= data["sequence_db"]["sequences"].length) {
5406 // all sites created
5407 region.removeEventListener('scroll', make_blocks_entries, false);
5408 return;
5409 }
5410 // if there's still 100 pixels to scroll than don't do anything yet
5411 if (region.scrollHeight - (region.scrollTop + region.offsetHeight) > 100) {
5412 return;
5413 }
5414
5415 region.data_blocks_index = append_blocks_entries(
5416 find_child(region, "blocks_tbl").tBodies[0],
5417 region.data_blocks_index, 20
5418 );
5419 }
5420
5421 function make_blocks() {
5422 "use strict";
5423 function add_seqs_filter(container, id, checked, label_text, help_topic) {
5424 "use strict";
5425 var label, radio;
5426 radio = document.createElement("input");
5427 radio.type = "radio";
5428 radio.name = "seqs_display";
5429 radio.id = id;
5430 radio.checked = checked;
5431 radio.addEventListener('click', action_seqs_filter, false);
5432 label = document.createElement("label");
5433 label.appendChild(document.createTextNode(label_text));
5434 label.htmlFor = id;
5435 container.appendChild(radio);
5436 container.appendChild(label);
5437 if (help_topic) {
5438 container.appendChild(document.createTextNode("\xA0"));
5439 container.appendChild(help_button(help_topic));
5440 }
5441 }
5442 function add_blocks_header(row, title, nopad, help_topic) {
5443 "use strict";
5444 var div, divcp, th;
5445 th = document.createElement("th");
5446 div = document.createElement("div");
5447 div.className = "blocks_th_inner";
5448 if (typeof title !== "object") {
5449 title = document.createTextNode("" + title);
5450 }
5451 div.appendChild(title);
5452 if (help_topic) {
5453 div.appendChild(document.createTextNode("\xA0"));
5454 div.appendChild(help_button(help_topic));
5455 }
5456 divcp = div.cloneNode(true);
5457 divcp.className = "blocks_th_hidden";
5458 th.appendChild(div);
5459 th.appendChild(divcp);
5460 if (nopad) {
5461 th.className = "nopad";
5462 }
5463 row.appendChild(th);
5464 }
5465 var container;
5466 var page, view_height, outer_tbl, inner_tbl, tbl, thead, tbody, rhead;
5467 var in_view, i, seq_count;
5468
5469 page = (document.compatMode === "CSS1Compat") ? document.documentElement : document.body;
5470 view_height = Math.max(page.clientHeight - 300, 300);
5471
5472 container = $("blocks");
5473 toggle_class(container, "hide_empty_seqs", true);
5474 toggle_class(container, "hide_only_scan", true);
5475 container.innerHTML = "";
5476 add_seqs_filter(container, "rdo_sites_only", true, "Only Motif Sites", "pop_motif_sites");
5477 add_seqs_filter(container, "rdo_sites_and_scan", false, "Motif Sites+Scanned Sites", "pop_scanned_sites");
5478 add_seqs_filter(container, "rdo_all_seqs", false, "All Sequences", "pop_all_sequences");
5479
5480 outer_tbl = document.createElement("div");
5481 outer_tbl.className = "blocks_outer";
5482
5483 inner_tbl = document.createElement("div");
5484 inner_tbl.id = "blocks_scroll";
5485 inner_tbl.className = "blocks_inner";
5486 inner_tbl.style.maxHeight = view_height + "px";
5487 outer_tbl.appendChild(inner_tbl);
5488
5489 tbl = document.createElement("table");
5490 tbl.className = "blocks_tbl";
5491 inner_tbl.appendChild(tbl);
5492
5493 thead = document.createElement("thead");
5494 tbl.appendChild(thead);
5495 tbody = document.createElement("tbody");
5496 tbl.appendChild(tbody);
5497
5498 rhead = thead.insertRow(thead.rows.length);
5499 add_blocks_header(rhead, "", true);
5500 add_blocks_header(rhead, "Name", false, "pop_seq_name");
5501 add_blocks_header(rhead, make_pv_text(), false, "pop_seq_pvalue");
5502 add_blocks_header(rhead, "Motif Location", false, "pop_motif_location");
5503
5504 container.appendChild(outer_tbl);
5505
5506
5507 seq_count = data["sequence_db"]["sequences"].length;
5508 in_view = Math.max(Math.ceil(view_height / 25), 1);
5509 i = append_blocks_entries(tbody, 0, in_view);
5510
5511 while (i < seq_count && inner_tbl.scrollHeight - (inner_tbl.scrollTop + inner_tbl.offsetHeight) < 400) {
5512 i = append_blocks_entries(tbody, i, 20);
5513 }
5514 inner_tbl.data_blocks_index = i;
5515 if (i < seq_count) {
5516 inner_tbl.addEventListener('scroll', make_blocks_entries, false);
5517 }
5518 }
5519
5520 function make_scan_popup(site, motif) {
5521 return function (e) {
5522 "use strict";
5523 var pop, xy, padding, edge_padding, pop_left, pop_top, page_width;
5524 var lflank, match, rflank, pspm;
5525 if (!e) var e = window.event;
5526 pop = make_scan_popup.pop;
5527 if (e.type === "mouseover") {
5528 if (pop) return;
5529 pop = clone_template("tmpl_scan_info");
5530 pspm = new Pspm(motif.pwm);
5531 if (site.rc) pspm.reverse_complement(meme_alphabet);
5532 set_tvar(pop, "tvar_logo", make_small_logo(meme_alphabet, pspm, {"className": "scan_logo"}));
5533 set_tvar(pop, "tvar_motif", motif.id);
5534 set_tvar(pop, "tvar_pvalue", site.pvalue.toExponential(2));
5535 set_tvar(pop, "tvar_start", site.pos + 1);
5536 set_tvar(pop, "tvar_end", site.pos + motif.len);
5537
5538 document.body.appendChild(pop);
5539 position_popup(this, pop);
5540 make_scan_popup.pop = pop;
5541 } else if (e.type === "mouseout") {
5542 if (pop) {
5543 pop.parentNode.removeChild(pop);
5544 make_scan_popup.pop = null;
5545 }
5546 }
5547 };
5548 }
5549
5550 function make_block_popup(site, motif, block) {
5551 return function (e) {
5552 "use strict";
5553 var pop;
5554 var lflank, match, rflank, pspm, ruler, match_seq, match_width;
5555 if (!e) var e = window.event;
5556 pop = make_block_popup.pop;
5557 if (e.type === "mouseover") {
5558 if (pop) return;
5559 pop = clone_template("tmpl_block_info");
5560 pspm = new Pspm(motif.pwm);
5561 if (site.rc) { // must be dna
5562 pspm.reverse_complement(meme_alphabet);
5563 lflank = meme_alphabet.invcomp_seq(site.rflank);
5564 match = meme_alphabet.invcomp_seq(site.match);
5565 rflank = meme_alphabet.invcomp_seq(site.lflank);
5566 } else {
5567 lflank = site.lflank;
5568 match = site.match;
5569 rflank = site.rflank;
5570 }
5571 ruler = document.getElementById("measure_match");
5572 match_seq = make_seq(meme_alphabet, match);
5573 ruler.innerHTML = "";
5574 ruler.appendChild(match_seq);
5575 match_width = ruler.clientWidth;
5576 ruler.removeChild(match_seq);
5577 set_tvar(pop, "tvar_lflank", lflank);
5578 set_tvar(pop, "tvar_match", match_seq);
5579 set_tvar(pop, "tvar_rflank", rflank);
5580 set_tvar(pop, "tvar_logo_pad", lflank);
5581 set_tvar(pop, "tvar_logo", make_small_logo(meme_alphabet, pspm, {"width": match_width}));
5582 set_tvar(pop, "tvar_motif", motif.id);
5583 set_tvar(pop, "tvar_pvalue", site.pvalue.toExponential(2));
5584 set_tvar(pop, "tvar_start", site.pos + 1);
5585 set_tvar(pop, "tvar_end", site.pos + motif.len);
5586
5587 document.body.appendChild(pop);
5588 position_popup(block, pop);
5589 make_block_popup.pop = pop;
5590 } else if (e.type === "mouseout") {
5591 if (pop) {
5592 pop.parentNode.removeChild(pop);
5593 make_block_popup.pop = null;
5594 }
5595 }
5596 };
5597 }
5598
5599 function update_outpop_format(index) {
5600 switch(parseInt($("text_format").value)) {
5601 case 0: // count matrix
5602 $("outpop_text").value = motif_count_matrix(index);
5603 $("text_name").value = "motif_" + (index + 1) + "_counts.txt";
5604 break;
5605 case 1: // prob matrix
5606 $("outpop_text").value = motif_prob_matrix(index);
5607 $("text_name").value = "motif_" + (index + 1) + "_freqs.txt";
5608 break;
5609 case 2: // minimal meme
5610 $("outpop_text").value = motif_minimal_meme(index);
5611 $("text_name").value = "motif_" + (index + 1) + ".txt";
5612 break;
5613 case 3: // fasta
5614 $("outpop_text").value = motif_fasta(index);
5615 $("text_name").value = "motif_" + (index + 1) + "_fasta.txt";
5616 break;
5617 case 4: // raw
5618 $("outpop_text").value = motif_raw(index);
5619 $("text_name").value = "motif_" + (index + 1) + "_raw.txt";
5620 break;
5621 default:
5622 throw new Error("Unknown motif format");
5623 }
5624 }
5625
5626 function update_outpop_motif(index) {
5627 "use strict";
5628 var motifs, motif, pspm, logo, canvas, num;
5629 motifs = data["motifs"];
5630 if (index < 0 || index >= motifs.length) {return;}
5631 current_motif = index;
5632 motif = motifs[index];
5633 pspm = new Pspm(motif["pwm"]);
5634 logo = new Logo(meme_alphabet, "");
5635 logo.add_pspm(pspm, 0);
5636 canvas = $("outpop_logo");
5637 canvas.width = canvas.width; // clear canvas
5638 draw_logo_on_canvas(logo, canvas, false);
5639 if (meme_alphabet.has_complement()) {
5640 pspm.reverse_complement(meme_alphabet);
5641 logo = new Logo(meme_alphabet, "");
5642 canvas = $("outpop_logo_rc");
5643 canvas.width = canvas.width; // clear canvas
5644 draw_logo_on_canvas(logo, canvas, false);
5645 }
5646 num = $("outpop_num");
5647 num.innerHTML = "";
5648 num.appendChild(document.createTextNode("" + (index + 1)));
5649 update_outpop_format(index);
5650 }
5651
5652 //
5653 // action_show_more
5654 //
5655 // Show more information on the motif.
5656 ///
5657 function action_show_more(e) {
5658 var node, tr, tbody, table, container, motif, ordinal;
5659 var expanded_motif;
5660 if (!e) e = window.event;
5661 if (e.type === "keydown") {
5662 if (e.keyCode !== 13 && e.keyCode !== 32) {
5663 return;
5664 }
5665 // stop a submit or something like that
5666 e.preventDefault();
5667 }
5668 // find the row that contains the cell
5669 node = this;
5670 do {
5671 if (node.tagName === "TR") break;
5672 } while (node = node.parentNode);
5673 if (!node) throw new Error("Expected to find row!?");
5674 tr = node;
5675 // get info
5676 motif = tr.data_motif;
5677 ordinal = tr.data_ordinal;
5678 // find tbody
5679 do {
5680 if (node.tagName === "TBODY") break;
5681 } while (node = node.parentNode);
5682 if (!node) throw new Error("Expected to find tbody!?");
5683 tbody = node;
5684 // find table
5685 do {
5686 if (node.tagName === "TABLE") break;
5687 } while (node = node.parentNode);
5688 if (!node) throw new Error("Expected to find table!?");
5689 table = node;
5690 // find container
5691 container = node.parentNode;
5692 // make a expanded motif
5693 motif["expanded"] = true;
5694 expanded_motif = make_expanded_motif(meme_alphabet, ordinal, motif,
5695 container.data_more_x, container.data_submit_x);
5696 // now determine how to place it
5697 if (tbody.rows.length === 1) {
5698 // only us in the table so the table can be replaced
5699 container.replaceChild(expanded_motif, table);
5700 } else if (tbody.rows[0] === tr) {
5701 // first row, so remove and insert an expanded motif before
5702 table.deleteRow(tr.rowIndex);
5703 container.insertBefore(expanded_motif, table);
5704 } else if (tbody.rows[tbody.rows.length -1] === tr) {
5705 // last row, so remove and insert an expanded motif after
5706 table.deleteRow(tr.rowIndex);
5707 container.insertBefore(expanded_motif, table.nextSibling);
5708 } else {
5709 var table2, tbody2;
5710 table2 = table.cloneNode(false);
5711 table2.appendChild(table.tHead.cloneNode(true));
5712 tbody2 = table.tBodies[0].cloneNode(false);
5713 table2.appendChild(tbody2);
5714 container.insertBefore(table2, table.nextSibling);
5715 for (i = tbody.rows.length - 1; i >= 0; i--) {
5716 row = tbody.rows[i];
5717 row.parentNode.removeChild(row);
5718 if (row === tr) {
5719 break;
5720 }
5721 tbody2.insertBefore(row, tbody2.rows[0]);
5722 }
5723 container.insertBefore(expanded_motif, table2);
5724 }
5725 find_child(expanded_motif, "tvar_less").focus();
5726 }
5727
5728 //
5729 // action_show_less
5730 //
5731 // Show less information on the motif.
5732 ///
5733 function action_show_less(e) {
5734 var btn;
5735 var expanded_motif, container, motif, ordinal, colw, focus_target;
5736 var table, tbody, tbody2, row, table_before, table_after;
5737 if (!e) e = window.event;
5738 if (e.type === "keydown") {
5739 if (e.keyCode !== 13 && e.keyCode !== 32) {
5740 return;
5741 }
5742 // stop a submit or something like that
5743 e.preventDefault();
5744 }
5745 btn = this;
5746 // find expanded motif
5747 expanded_motif = find_parent(btn, "expanded_motif");
5748 if (!expanded_motif) throw new Error("Expected expanded motif.");
5749 // find the container
5750 container = expanded_motif.parentNode;
5751 // get data
5752 motif = expanded_motif.data_motif;
5753 ordinal = expanded_motif.data_ordinal;
5754 colw = container.data_colw;
5755 // get the table before
5756 table_before = expanded_motif.previousSibling;
5757 if (table_before && table_before.tagName !== "TABLE") {
5758 table_before = null;
5759 }
5760 // get the table after
5761 table_after = expanded_motif.nextSibling;
5762 if (table_after && table_after.tagName !== "TABLE") {
5763 table_after = null;
5764 }
5765 // see if there is a table below or above that we can put this in.
5766 // if there is a table both below and above then add this motif and
5767 // all ones below to the above table
5768 motif["expanded"] = false;
5769 if (table_before && table_after) {
5770 tbody = table_before.tBodies[0];
5771 row = tbody.insertRow(tbody.rows.length);
5772 make_motif_table_entry(row, meme_alphabet, ordinal, motif, colw);
5773 focus_target = find_child(row.cells[5], "sym_btn");
5774 container.removeChild(expanded_motif);
5775 tbody2 = table_after.tBodies[0];
5776 while (tbody2.rows.length > 0) {
5777 row = tbody2.rows[0];
5778 row.parentNode.removeChild(row);
5779 tbody.appendChild(row);
5780 }
5781 container.removeChild(table_after);
5782 } else if (table_before) {
5783 tbody = table_before.tBodies[0];
5784 row = tbody.insertRow(tbody.rows.length);
5785 make_motif_table_entry(row, meme_alphabet, ordinal, motif, colw);
5786 focus_target = find_child(row.cells[5], "sym_btn");
5787 container.removeChild(expanded_motif);
5788 } else if (table_after) {
5789 tbody = table_after.tBodies[0];
5790 row = tbody.insertRow(0);
5791 make_motif_table_entry(row, meme_alphabet, ordinal, motif, colw);
5792 focus_target = find_child(row.cells[5], "sym_btn");
5793 container.removeChild(expanded_motif);
5794 } else {
5795 //no table above or below!
5796 // make a new table
5797 table = make_motifs_table(meme_alphabet, ordinal, [motif], colw, data["stop_reason"]);
5798 focus_target = find_child(table.tBodies[0].rows[0].cells[5], "sym_btn");
5799 container.replaceChild(table, expanded_motif);
5800 }
5801 focus_target.focus();
5802 }
5803
5804 function action_show_outpop(e) {
5805 "use strict";
5806 function init() {
5807 "use strict";
5808 var close_btn, next_btn, prev_btn, cancel_btn, do_btn;
5809 var tab1, tab2, tab3;
5810 var pnl1, pnl2, pnl3;
5811 var format_list;
5812 var tbl_submit, inputs, i, default_prog;
5813 close_btn = $("outpop_close");
5814 close_btn.addEventListener("click", action_hide_outpop, false);
5815 close_btn.addEventListener("keydown", action_hide_outpop, false);
5816 next_btn = $("outpop_next");
5817 next_btn.addEventListener("click", action_outpop_next, false);
5818 next_btn.addEventListener("keydown", action_outpop_next, false);
5819 prev_btn = $("outpop_prev");
5820 prev_btn.addEventListener("click", action_outpop_prev, false);
5821 prev_btn.addEventListener("keydown", action_outpop_prev, false);
5822 cancel_btn = $("outpop_cancel");
5823 cancel_btn.addEventListener("click", action_hide_outpop, false);
5824 do_btn = $("outpop_do");
5825 do_btn.addEventListener("click", action_outpop_submit, false);
5826 tab1 = $("outpop_tab_1");
5827 tab1.tabIndex = 0;
5828 tab1.addEventListener("click", action_outpop_tab, false);
5829 tab1.addEventListener("keydown", action_outpop_tab, false);
5830 tab2 = $("outpop_tab_2");
5831 tab2.tabIndex = 0;
5832 tab2.addEventListener("click", action_outpop_tab, false);
5833 tab2.addEventListener("keydown", action_outpop_tab, false);
5834 tab3 = $("outpop_tab_3");
5835 tab3.tabIndex = 0;
5836 tab3.addEventListener("click", action_outpop_tab, false);
5837 tab3.addEventListener("keydown", action_outpop_tab, false);
5838 pnl1 = $("outpop_pnl_1");
5839 pnl2 = $("outpop_pnl_2");
5840 pnl3 = $("outpop_pnl_3");
5841 toggle_class(tab1, "activeTab", true);
5842 toggle_class(tab2, "activeTab", false);
5843 toggle_class(tab3, "activeTab", false);
5844 pnl1.style.display = "block";
5845 pnl2.style.display = "none";
5846 pnl3.style.display = "none";
5847 format_list = $("text_format");
5848 format_list.addEventListener("change", action_outpop_format, false);
5849 // setup program selection
5850 tbl_submit = $("programs");
5851 // when not dna, hide the inputs for programs that require dna motifs
5852 toggle_class(tbl_submit, "alphabet_dna", meme_alphabet.has_complement());//TODO FIXME alphabet_dna is a bad name for a field when allowing custom alphabets
5853 // add a click listener for the radio buttons
5854 inputs = tbl_submit.querySelectorAll("input[type='radio']");
5855 for (i = 0; i < inputs.length; i++) {
5856 inputs[i].addEventListener("click", action_outpop_program, false);
5857 }
5858 // ensure that a default program option is selected for DNA and Protein
5859 default_prog = document.getElementById(meme_alphabet.has_complement() ? "submit_tomtom" : "submit_fimo"); //TODO FIXME Tomtom might require a more strict definition of DNA
5860 default_prog.checked = true;
5861 action_outpop_program.call(default_prog);
5862 // disable reverse-complement when not DNA
5863 $("logo_rc_option").disabled = !meme_alphabet.has_complement();
5864 // set errorbars on when ssc is on
5865 $("logo_ssc").addEventListener("change", action_outpop_ssc, false);
5866 }
5867 var node;
5868 // store the focused element
5869 action_hide_outpop.last_active = document.activeElement;
5870 if (!e) e = window.event;
5871 if (e.type === "keydown") {
5872 if (e.keyCode !== 13 && e.keyCode !== 32) {
5873 return;
5874 }
5875 // stop a submit or something like that
5876 e.preventDefault();
5877 }
5878 // hide the help popup
5879 help_popup();
5880 // on first load initilize the popup
5881 if (!action_show_outpop.ready) {
5882 init();
5883 action_show_outpop.ready = true;
5884 }
5885 // load the motif logo
5886 node = this;
5887 do {
5888 if (/\bexpanded_motif\b/.test(node.className) || node.tagName === "TR") break;
5889 } while (node = node.parentNode);
5890 if (node === null) throw new Error("Expected node!");
5891 update_outpop_motif(node.data_ordinal - 1);
5892 // display the download popup
5893 $("grey_out_page").style.display = "block";
5894 $("download").style.display = "block";
5895 $("outpop_close").focus();
5896 }
5897
5898 function action_hide_outpop(e) {
5899 if (!e) e = window.event;
5900 if (e.type === "keydown") {
5901 if (e.keyCode !== 13 && e.keyCode !== 32) {
5902 return;
5903 }
5904 // stop a submit or something like that
5905 e.preventDefault();
5906 }
5907 $("download").style.display = "none";
5908 $("grey_out_page").style.display = "none";
5909 if (typeof action_hide_outpop.last_active !== "undefined") {
5910 action_hide_outpop.last_active.focus();
5911 }
5912 }
5913
5914 function action_outpop_next(e) {
5915 if (!e) e = window.event;
5916 if (e.type === "keydown") {
5917 if (e.keyCode !== 13 && e.keyCode !== 32) {
5918 return;
5919 }
5920 // stop a submit or something like that
5921 e.preventDefault();
5922 }
5923 update_outpop_motif(current_motif + 1);
5924 }
5925
5926 function action_outpop_prev(e) {
5927 if (!e) e = window.event;
5928 if (e.type === "keydown") {
5929 if (e.keyCode !== 13 && e.keyCode !== 32) {
5930 return;
5931 }
5932 // stop a submit or something like that
5933 e.preventDefault();
5934 }
5935 update_outpop_motif(current_motif - 1);
5936 }
5937
5938 function action_outpop_program() {
5939 "use strict";
5940 var table, tr, rows, i;
5941 tr = find_parent_tag(this, "TR");
5942 table = find_parent_tag(tr, "TABLE");
5943 rows = table.querySelectorAll("tr");
5944 for (i = 0; i < rows.length; i++) {
5945 toggle_class(rows[i], "selected", rows[i] === tr);
5946 }
5947 }
5948
5949 function action_outpop_ssc() {
5950 "use strict";
5951 $("logo_err").value = $("logo_ssc").value;
5952 }
5953
5954 function action_outpop_submit(e) {
5955 "use strict";
5956 var form, input, program, motifs;
5957 // find out which program is selected
5958 var radios, i;
5959 radios = document.getElementsByName("program");
5960 program = "fimo"; // default to fimo, since it works with all alphabet types
5961 for (i = 0; i < radios.length; i++) {
5962 if (radios[i].checked) program = radios[i].value;
5963 }
5964
5965 motifs = motif_minimal_meme(current_motif);
5966 form = document.createElement("form");
5967 form.setAttribute("method", "post");
5968 form.setAttribute("action", site_url + "/tools/" + program);
5969
5970 input = document.createElement("input");
5971 input.setAttribute("type", "hidden");
5972 input.setAttribute("name", "motifs_embed");
5973 input.setAttribute("value", motifs);
5974 form.appendChild(input);
5975
5976 document.body.appendChild(form);
5977 form.submit();
5978 document.body.removeChild(form);
5979 }
5980
5981 function action_outpop_download_motif(e) {
5982 $("text_form").submit();
5983 }
5984
5985 function action_outpop_download_logo(e) {
5986 "use strict";
5987 $("logo_motifs").value = motif_minimal_meme(current_motif);
5988 $("logo_form").submit();
5989 }
5990
5991 function action_btn_rc(e) {
5992 "use strict";
5993 var node, tr, motif, box, logo_box, tab_st, tab_rc, rc;
5994 if (!e) e = window.event;
5995 if (e.type === "keydown") {
5996 if (e.keyCode !== 13 && e.keyCode !== 32) {
5997 return;
5998 }
5999 // stop a submit or something like that
6000 e.preventDefault();
6001 }
6002 node = this;
6003 do {
6004 if (node.tagName === "TR") break;
6005 } while (node = node.parentNode);
6006 if (!node) throw new Error("Expected to find row!?");
6007 tr = node;
6008 // get info
6009 motif = tr.data_motif;
6010 box = find_parent(this, "preview_box");
6011 logo_box = find_child(box, "preview_logo_box");
6012 tab_st = find_child(box, "plus");
6013 tab_rc = find_child(box, "minus");
6014 rc = (this === tab_rc);
6015 motif["rc"] = rc;
6016 toggle_class(logo_box, "show_rc_logo", rc);
6017 toggle_class(tab_st, "active", !rc);
6018 toggle_class(tab_rc, "active", rc);
6019 }
6020
6021 function action_rc_tab(e) {
6022 "use strict";
6023 var box, logo_box, tab_st, tab_rc, rc;
6024 if (!e) e = window.event;
6025 if (e.type === "keydown") {
6026 if (e.keyCode !== 13 && e.keyCode !== 32) {
6027 return;
6028 }
6029 // stop a submit or something like that
6030 e.preventDefault();
6031 }
6032 box = find_parent(this, "expanded_motif");
6033 logo_box = find_child(box, "tvar_logo");
6034 tab_st = find_child(box, "tvar_tab");
6035 tab_rc = find_child(box, "tvar_tab_rc");
6036 rc = (this === tab_rc);
6037 box.data_motif["rc"] = rc;
6038 toggle_class(logo_box, "show_rc_logo", rc);
6039 toggle_class(tab_st, "activeTab", !rc);
6040 toggle_class(tab_rc, "activeTab", rc);
6041 }
6042
6043 function action_outpop_tab(e) {
6044 "use strict";
6045 var tab1, tab2, tab3, pnl1, pnl2, pnl3, do_btn;
6046 if (!e) e = window.event;
6047 if (e.type === "keydown") {
6048 if (e.keyCode !== 13 && e.keyCode !== 32) {
6049 return;
6050 }
6051 // stop a submit or something like that
6052 e.preventDefault();
6053 }
6054 tab1 = $("outpop_tab_1");
6055 tab2 = $("outpop_tab_2");
6056 tab3 = $("outpop_tab_3");
6057 pnl1 = $("outpop_pnl_1");
6058 pnl2 = $("outpop_pnl_2");
6059 pnl3 = $("outpop_pnl_3");
6060 do_btn = $("outpop_do");
6061
6062 toggle_class(tab1, "activeTab", (this === tab1));
6063 toggle_class(tab2, "activeTab", (this === tab2));
6064 toggle_class(tab3, "activeTab", (this === tab3));
6065 pnl1.style.display = ((this === tab1) ? "block" : "none");
6066 pnl2.style.display = ((this === tab2) ? "block" : "none");
6067 pnl3.style.display = ((this === tab3) ? "block" : "none");
6068 do_btn.value = ((this === tab1) ? "Submit" : "Download");
6069 do_btn.removeEventListener("click", action_outpop_submit, false);
6070 do_btn.removeEventListener("click", action_outpop_download_logo, false);
6071 do_btn.removeEventListener("click", action_outpop_download_motif, false);
6072 if (this === tab1) {
6073 do_btn.addEventListener("click", action_outpop_submit, false);
6074 } else if (this === tab2) {
6075 do_btn.addEventListener("click", action_outpop_download_motif, false);
6076 } else {
6077 do_btn.addEventListener("click", action_outpop_download_logo, false);
6078 }
6079 }
6080
6081 function action_seqs_filter() {
6082 "use strict";
6083 var block_container;
6084 block_container = $("blocks");
6085 if ($("rdo_all_seqs").checked) {
6086 toggle_class(block_container, "hide_empty_seqs", false);
6087 toggle_class(block_container, "hide_only_scan", false);
6088 } else if ($("rdo_sites_and_scan").checked) {
6089 toggle_class(block_container, "hide_empty_seqs", true);
6090 toggle_class(block_container, "hide_only_scan", false);
6091 } else if ($("rdo_sites_only").checked) {
6092 toggle_class(block_container, "hide_empty_seqs", true);
6093 toggle_class(block_container, "hide_only_scan", true);
6094 }
6095 }
6096
6097 function action_outpop_format() {
6098 update_outpop_format(current_motif);
6099 }
6100
6101 //
6102 // page_loaded
6103 //
6104 // Called when the page has loaded for the first time.
6105 ///
6106 function page_loaded() {
6107 post_load_setup();
6108 }
6109
6110 //
6111 // page_loaded
6112 //
6113 // Called when a cached page is reshown.
6114 ///
6115 function page_shown(e) {
6116 if (e.persisted) post_load_setup();
6117 }
6118
6119 //
6120 // page_loaded
6121 //
6122 // Called when the page is resized
6123 ///
6124 function page_resized() {
6125 var page, blocks_scroll;
6126 update_scroll_pad();
6127 page = (document.compatMode === "CSS1Compat") ? document.documentElement : document.body;
6128 blocks_scroll = $("blocks_scroll");
6129 if (blocks_scroll) {
6130 blocks_scroll.style.maxHeight = Math.max(page.clientHeight - 300, 300) + "px";
6131 }
6132 }
6133
6134 //
6135 // pre_load_setup
6136 //
6137 // Run before the page is displayed
6138 ///
6139 function pre_load_setup() {
6140 var start, hue, sat, light, divisions;
6141 var i, j, motifs, motif, sites, site, sequences, sequence;
6142 var max_seq_len;
6143 motifs = data["motifs"];
6144 sequences = data["sequence_db"]["sequences"];
6145 max_seq_len = 1;
6146 for (i = 0; i < sequences.length; i++) {
6147 sequence = sequences[i];
6148 sequence["sites"] = [];
6149 if (sequence["length"] > max_seq_len) {
6150 max_seq_len = sequence["length"];
6151 }
6152 }
6153 data["sequence_db"]["max_length"] = max_seq_len;
6154 // use hsl colours
6155 start = 0; //red
6156 sat = 100;
6157 light = 50;
6158 for (i = 0; i < motifs.length; i++) {
6159 motif = motifs[i];
6160 // give the motif a colour
6161 divisions = 1 << Math.ceil(Math.log(i + 1) / Math.LN2);
6162 hue = start + (360 / divisions) * ((i - (divisions >> 1)) * 2 + 1);
6163 motif["colour"] = "hsl(" + hue + ", " + sat + "%, " + light + "%)";
6164 // associate sites with sequences as well
6165 // to make generating the block diagram easier
6166 sites = motif["sites"];
6167 for (j = 0; j < sites.length; j++) {
6168 site = sites[j];
6169 sequence = sequences[site["seq"]];
6170 // record the motif index
6171 site["motif"] = i;
6172 // add the site to the sequence
6173 sequence["sites"].push(site);
6174 }
6175 }
6176 }
6177
6178 //
6179 // post_load_setup
6180 //
6181 // Run when the page has loaded, or been reloaded.
6182 //
6183 function post_load_setup() {
6184 update_scroll_pad();
6185 if (data["motifs"].length > 0) {
6186 make_motifs();
6187 make_blocks();
6188 } else {
6189 $("motifs").innerHTML = "<p>No significant motifs found!</p>"; // clear content
6190 $("motifs").innerHTML += "<p><b>" + data["stop_reason"] + "</b></p>";
6191 $("blocks").innerHTML = "<p>No significant motifs found!</p>";
6192 }
6193 }
6194
6195 pre_load_setup();
6196 </script>
6197 <style>
6198 /* The following is the content of meme.css */
6199 body { background-color:white; font-size: 12px; font-family: Verdana, Arial, Helvetica, sans-serif;}
6200
6201 div.help {
6202 display: inline-block;
6203 margin: 0px;
6204 padding: 0px;
6205 width: 12px;
6206 height: 13px;
6207 cursor: pointer;
6208 background-image: url(data:image/gif;base64,R0lGODlhDAANAIABANR0AP///yH5BAEAAAEALAAAAAAMAA0AAAIdhI8Xy22MIFgv1DttrrJ7mlGNNo4c+aFg6SQuUAAAOw==);
6209 }
6210
6211 div.help:hover {
6212 background-image: url(data:image/gif;base64,R0lGODlhDAANAKEAANR0AP///9R0ANR0ACH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEAAAIALAAAAAAMAA0AAAIdDGynCe3PgoxONntvwqz2/z2K2ImjR0KhmSIZUgAAOw==);
6213 }
6214
6215 p.spaced { line-height: 1.8em;}
6216
6217 span.citation { font-family: "Book Antiqua", "Palatino Linotype", serif; color: #004a4d;}
6218
6219 p.pad { padding-left: 30px; padding-top: 5px; padding-bottom: 10px;}
6220
6221 td.jump { font-size: 13px; color: #ffffff; background-color: #00666a;
6222 font-family: Georgia, "Times New Roman", Times, serif;}
6223
6224 a.jump { margin: 15px 0 0; font-style: normal; font-variant: small-caps;
6225 font-weight: bolder; font-family: Georgia, "Times New Roman", Times, serif;}
6226
6227 h2.mainh {font-size: 1.5em; font-style: normal; margin: 15px 0 0;
6228 font-variant: small-caps; font-family: Georgia, "Times New Roman", Times, serif;}
6229
6230 h2.line {border-bottom: 1px solid #CCCCCC; font-size: 1.5em; font-style: normal;
6231 margin: 15px 0 0; padding-bottom: 3px; font-variant: small-caps;
6232 font-family: Georgia, "Times New Roman", Times, serif;}
6233
6234 h4 {border-bottom: 1px solid #CCCCCC; font-size: 1.2em; font-style: normal;
6235 margin: 10px 0 0; padding-bottom: 3px; font-family: Georgia, "Times New Roman", Times, serif;}
6236
6237 h5 {margin: 0px}
6238
6239 a.help { font-size: 9px; font-style: normal; text-transform: uppercase;
6240 font-family: Georgia, "Times New Roman", Times, serif;}
6241
6242 div.pad { padding-left: 30px; padding-top: 5px; padding-bottom: 10px;}
6243
6244 div.pad1 { margin: 10px 5px;}
6245
6246 div.pad2 { margin: 25px 5px 5px;}
6247 h2.pad2 { padding: 25px 5px 5px;}
6248
6249 div.pad3 { padding: 5px 0px 10px 30px;}
6250
6251 div.box { border: 2px solid #CCCCCC; padding:10px; overflow: hidden;}
6252
6253 div.bar { border-left: 7px solid #00666a; padding:5px; margin-top:25px; }
6254
6255 div.subsection {margin:25px 0px;}
6256
6257 img {border:0px none;}
6258
6259 th.majorth {text-align:left;}
6260 th.minorth {font-weight:normal; text-align:left; width:8em; padding: 3px 0px;}
6261 th.actionth {font-weight:normal; text-align:left;}
6262
6263 .explain h5 {font-size:1em; margin-left: 1em;}
6264
6265 div.doc {margin-left: 2em; margin-bottom: 3em;}
6266
6267 th.trainingset {
6268 border-bottom: thin dashed black;
6269 font-weight:normal;
6270 padding:0px 10px;
6271 }
6272 div.pop_content {
6273 position:absolute;
6274 z-index:50;
6275 width:300px;
6276 padding: 5px;
6277 background: #E4ECEC;
6278 font-size: 12px;
6279 font-family: Arial;
6280 border-style: double;
6281 border-width: 3px;
6282 border-color: #AA2244;
6283 display:none;
6284 }
6285
6286 div.pop_content > *:first-child {
6287 margin-top: 0px;
6288 }
6289
6290 div.pop_content h1, div.pop_content h2, div.pop_content h3, div.pop_content h4,
6291 div.pop_content h5, div.pop_content h6, div.pop_content p {
6292 margin: 0px;
6293 }
6294
6295 div.pop_content p + h1, div.pop_content p + h2, div.pop_content p + h3,
6296 div.pop_content p + h4, div.pop_content p + h5, div.pop_content p + h6 {
6297 margin-top: 5px;
6298 }
6299
6300 div.pop_content p + p {
6301 margin-top: 5px;
6302 }
6303
6304 div.pop_content > *:last-child {
6305 margin-bottom: 0px;
6306 }
6307
6308 div.pop_content div.pop_close {
6309 /* old definition */
6310 float:right;
6311 bottom: 0;
6312 }
6313
6314 div.pop_content span.pop_close, div.pop_content span.pop_back {
6315 display: inline-block;
6316 border: 2px outset #661429;
6317 background-color: #CCC;
6318 padding-left: 1px;
6319 padding-right: 1px;
6320 padding-top: 0px;
6321 padding-bottom: 0px;
6322 cursor: pointer;
6323 color: #AA2244; /*#661429;*/
6324 font-weight: bold;
6325 }
6326
6327 div.pop_content span.pop_close:active, div.pop_content span.pop_back:active {
6328 border-style: inset;
6329 }
6330
6331 div.pop_content span.pop_close {
6332 float:right;
6333 /*border: 2px outset #AA002B;*/
6334 /*color: #AA2244;*/
6335 }
6336
6337 div.pop_content:not(.nested) .nested_only {
6338 display: none;
6339 }
6340
6341 div.pop_back_sec {
6342 margin-bottom: 5px;
6343 }
6344
6345 div.pop_close_sec {
6346 margin-top: 5px;
6347 }
6348
6349 table.hide_advanced tr.advanced {
6350 display: none;
6351 }
6352 span.show_more {
6353 display: none;
6354 }
6355 table.hide_advanced span.show_more {
6356 display: inline;
6357 }
6358 table.hide_advanced span.show_less {
6359 display: none;
6360 }
6361
6362
6363 /*****************************************************************************
6364 * Program logo styling
6365 ****************************************************************************/
6366 div.prog_logo {
6367 border-bottom: 0.25em solid #0f5f60;
6368 height: 4.5em;
6369 width: 24em;
6370 display:inline-block;
6371 }
6372 div.prog_logo img {
6373 float:left;
6374 width: 4em;
6375 border-style: none;
6376 margin-right: 0.2em;
6377 }
6378 div.prog_logo h1, div.prog_logo h1:hover, div.prog_logo h1:active, div.prog_logo h1:visited {
6379 margin:0;
6380 padding:0;
6381 font-family: Arial, Helvetica, sans-serif;
6382 font-size: 3.2em;
6383 line-height: 1em;
6384 vertical-align: top;
6385 display: block;
6386 color: #026666;
6387 letter-spacing: -0.06em;
6388 text-shadow: 0.04em 0.06em 0.05em #666;
6389 }
6390 div.prog_logo h2, div.prog_logo h2:hover, div.prog_logo h2:active, div.prog_logo h2:visited {
6391 display: block;
6392 margin:0;
6393 padding:0;
6394 font-family: Helvetica, sans-serif;
6395 font-size: 0.9em;
6396 line-height: 1em;
6397 letter-spacing: -0.06em;
6398 color: black;
6399 }
6400
6401 div.big.prog_logo {
6402 font-size: 18px;
6403 }
6404
6405 </style>
6406 <style>
6407 .block_td {
6408 height:25px;
6409 }
6410 .block_container {
6411 position:relative;
6412 box-sizing: border-box;
6413 height: 25px;
6414 padding: 0px;
6415 margin: 0px;
6416 margin-left: 1em;
6417 }
6418 .block_label {
6419 position: absolute;
6420 display: inline-block;
6421 padding: 3px;
6422 z-index: 4;
6423 top: 6px;
6424 height: 12px;
6425 line-height: 12px;
6426 font-size: 12px;
6427 background-color: white;
6428 border: 1px solid black;
6429 -moz-border-radius: 12px;
6430 -webkit-border-radius: 12px;
6431 border-radius: 12px;
6432 transform: translateX(-50%);
6433 }
6434 .block_motif {
6435 position: absolute;
6436 z-index: 3;
6437 top: 0px;
6438 box-sizing: border-box;
6439 border: 1px solid black;
6440 height: 12px;
6441 background-color: cyan;
6442 }
6443 .block_motif.top {
6444 border-bottom-width: 0;
6445 }
6446 .block_motif.bottom {
6447 border-top-width: 0;
6448 }
6449 .block_motif.scanned_site {
6450 opacity: 0.3;
6451 }
6452 .block_motif.scanned_site.active {
6453 opacity: 0.9;
6454 }
6455 .block_region {
6456 position:absolute;
6457 z-index:6;
6458 height:25px;
6459 top:0px;
6460 }
6461 .block_region.main {
6462 z-index:8;
6463 }
6464 .block_region.scanned_site {
6465 z-index:5;
6466 }
6467 .block_region.scanned_site.main {
6468 z-index:7;
6469 }
6470 .block_region.top {
6471 height:13px;
6472 }
6473 .block_region.bottom {
6474 height:13px;
6475 top:12px;
6476 }
6477 .block_rule {
6478 position:absolute;
6479 z-index:2;
6480 width:100%;
6481 height:1px;
6482 top:12px;
6483 left:0px;
6484 background-color:gray;
6485 }
6486 .block_plus_sym {
6487 position:absolute;
6488 z-index:4;
6489 line-height:12px;
6490 top:0px;
6491 left:-1em;
6492 }
6493 .block_minus_sym {
6494 position:absolute;
6495 z-index:4;
6496 line-height:12px;
6497 top:13px;
6498 left:-1em;
6499 }
6500
6501 .tic_major {
6502 position:absolute;
6503 top:0em;
6504 height:0.5em;
6505 width: 2px;
6506 margin-left: -1px;
6507 background-color: blue;
6508 }
6509 .tic_minor {
6510 position:absolute;
6511 top:0em;
6512 height:0.2em;
6513 width: 1px;
6514 margin-left: -0.5px;
6515 background-color: blue;
6516 }
6517 .tic_label {
6518 position:absolute;
6519 display: inline-block;
6520 top:0.5em;
6521 height: 1em;
6522 color: blue;
6523 transform: translateX(-50%);
6524 }
6525
6526 .block_needle {
6527 position:absolute;
6528 z-index:4;
6529 height:30px;
6530 width:1px;
6531 top:-2px;
6532 background-color:gray;
6533 }
6534 .block_needle.right {
6535 height: 60px;
6536 }
6537 .block_handle {
6538 position: absolute;
6539 display: inline-block;
6540 z-index: 5;
6541 top: 27px;
6542 min-width: 3ex;
6543 text-align: center;
6544 font-size: 12px;
6545 line-height: 12px;
6546 transform: translateX(-50%);
6547 background-color: LightGrey;
6548 border:3px outset grey;
6549 cursor: pointer;
6550 -webkit-user-select: none; /* Chrome/Safari */
6551 -moz-user-select: none; /* Firefox */
6552 -ms-user-select: none; /* IE10+ */
6553 /* Rules below not implemented in browsers yet */
6554 -o-user-select: none;
6555 user-select: none;
6556 }
6557 .block_handle.right {
6558 top: 47px;
6559 }
6560
6561 .legend_container {
6562 text-align: right;
6563 }
6564 .legend_entry {
6565 display: inline-block;
6566 padding: 5px;
6567 }
6568 div.legend_swatch {
6569 box-sizing: border-box;
6570 width: 15px;
6571 height: 15px;
6572 border: 1px solid black;
6573 background-color: cyan;
6574 float: left;
6575 }
6576 div.legend_swatch input {
6577 display: none;
6578 }
6579 .legend_text {
6580 line-height: 15px;
6581 margin-left: 20px;
6582 }
6583 </style>
6584 <style>
6585 /* meme output specific css */
6586
6587 div.pop_block {
6588 position:absolute;
6589 z-index:5;
6590 padding: 5px;
6591 border: 1px solid black;
6592 display: inline-block;
6593 background-color: white;
6594 }
6595
6596 #measure_match {
6597 position: absolute;
6598 visibility: hidden;
6599 height: auto;
6600 width: auto;
6601 white-space: nowrap;
6602 }
6603
6604 div.template {
6605 position: absolute;
6606 z-index: 1;
6607 left: 0;
6608 top: 0;
6609 visibility: hidden;
6610 }
6611
6612 table.block_information {
6613 margin-left: auto;
6614 margin-right: auto;
6615 }
6616
6617 table.block_information * th {
6618 text-align: right;
6619 }
6620
6621 *.hide_empty_seqs * tr.empty_seq {
6622 display: none;
6623 }
6624
6625 *.hide_only_scan * tr.only_scan {
6626 display: none;
6627 }
6628
6629 *.hide_only_scan * div.scanned_site {
6630 display: none;
6631 }
6632
6633 td.symaction {
6634 text-align: center;
6635 text-decoration: underline;
6636 font-size: 20px;
6637 cursor: pointer;
6638 }
6639 div.sym_btn {
6640 display:inline-block;
6641 text-decoration: underline;
6642 cursor: pointer;
6643 font-size: 20px;
6644 line-height:20px;
6645 text-align: center;
6646 width: 20px;
6647 height: 20px;
6648 color: blue;
6649 }
6650 div.sym_btn:hover {
6651 color: white;
6652 background-color: blue;
6653 }
6654
6655 div.sym_btn.positioned {
6656 position: absolute;
6657 top: 0px;
6658 }
6659
6660 div.actionbutton {
6661 display:inline-block;
6662 cursor: pointer;
6663 font-size: 18px;
6664 line-height:20px;
6665 padding: 5px;
6666 margin: 10px 0;
6667 border: 1px solid black;
6668 }
6669
6670 div.actionbutton:hover {
6671 color:#FFF;
6672 background-color:#000;
6673 }
6674
6675 div.param_box {
6676 display: inline-block;
6677 margin-right: 20px;
6678 }
6679
6680 span.param {
6681 font-weight: bold;
6682 }
6683
6684 div.box + div.box {
6685 margin-top: 5px;
6686 }
6687
6688 div.sites_outer {
6689 position: relative;
6690 padding-top: 20px; /* height of header */
6691 display: inline-block;
6692 }
6693
6694 div.sites_inner {
6695 overflow-x: hidden;
6696 overflow-y: auto;
6697 max-height: 200px;
6698 }
6699 table.sites_tbl {
6700 border-collapse: collapse;
6701 }
6702
6703 div.sites_th_inner {
6704 position: absolute;
6705 top: 0;
6706 line-height: 20px; /* height of header */
6707 text-align: left;
6708 padding-left: 5px;
6709 }
6710 th.nopad div.sites_th_inner {
6711 padding-left: 0;
6712 }
6713 div.sites_th_hidden {
6714 visibility: hidden;
6715 height: 0;
6716 padding: 0 10px;
6717 }
6718 th.nopad div.sites_th_hidden {
6719 padding: 0;
6720 }
6721 div.sites_inner * th {
6722 height: 0;
6723 }
6724
6725 table.sites_tbl {
6726 overflow-x: hidden;
6727 overflow-y: auto;
6728 }
6729
6730 .site_num {
6731 text-align: right;
6732 }
6733 .site_name {
6734 padding:0px 5px;
6735 text-align:left;
6736 }
6737 .site_strand {
6738 padding:0px 5px;
6739 text-align:center;
6740 }
6741 .norc .site_strand, .norc .site_strand_title {
6742 display: none;
6743 }
6744 .site_start {
6745 padding:0px 15px;
6746 text-align: right;
6747 }
6748 .site_pvalue {
6749 text-align:center;
6750 padding:0px 15px;
6751 text-align:right;
6752 white-space: nowrap;
6753 }
6754 .lflank, .rflank, .match, .alpha_symbol {
6755 font-weight:bold;
6756 font-size:15px;
6757 font-family: 'Courier New', Courier, monospace;
6758 color:gray;
6759 }
6760
6761 .site.lflank {
6762 text-align:right;
6763 padding-right:5px;
6764 color:gray;
6765 }
6766 .site.match {
6767 text-align:center;
6768 }
6769 .site.rflank {
6770 text-align:left;
6771 padding-left:5px;
6772 padding-right: 20px;
6773 }
6774
6775 th.stop_reason {
6776 text-align: left;
6777 padding-right: 10px;
6778 }
6779
6780 th.motif_ordinal {
6781
6782 }
6783 td.motif_ordinal {
6784 text-align: right;
6785 padding-right: 10px;
6786 }
6787 th.motif_logo {
6788 padding-right: 10px;
6789 }
6790 td.motif_logo {
6791 padding-right: 10px;
6792 }
6793 th.motif_evalue {
6794 text-align:right;
6795 padding-right: 10px;
6796 }
6797 td.motif_evalue {
6798 text-align: right;
6799 white-space: nowrap;
6800 padding-right: 20px;
6801 }
6802 th.motif_nsites {
6803 text-align: right;
6804 padding-right: 10px;
6805 }
6806 td.motif_nsites {
6807 text-align: right;
6808 padding-right: 20px;
6809 }
6810 th.motif_width {
6811 text-align: right;
6812 padding-right: 5px;
6813 }
6814 td.motif_width {
6815 text-align: right;
6816 padding-right: 15px;
6817 }
6818 th.motif_more {
6819 padding: 0 5px;
6820 }
6821 td.motif_more {
6822 text-align: center;
6823 padding: 0 5px;
6824 }
6825 th.motif_submit {
6826 padding: 0 5px;
6827 }
6828 td.motif_submit {
6829 text-align: center;
6830 padding: 0 5px;
6831 }
6832 th.motif_download {
6833 padding-left: 5px;
6834 }
6835 td.motif_download {
6836 text-align: center;
6837 padding-left: 5px;
6838 }
6839
6840
6841 div.tabArea {
6842 font-size: 80%;
6843 font-weight: bold;
6844 }
6845
6846 .norc div.tabArea {
6847 display: none;
6848 }
6849
6850 span.tab, span.tab:visited {
6851 cursor: pointer;
6852 color: #888;
6853 background-color: #ddd;
6854 border: 2px solid #ccc;
6855 padding: 2px 1em;
6856 text-decoration: none;
6857 }
6858 span.tab.middle {
6859 border-left-width: 0px;
6860 }
6861 div.tabArea.base span.tab {
6862 border-top-width: 0px;
6863 }
6864 div.tabArea.top span.tab {
6865 border-bottom-width: 0px;
6866 }
6867
6868 span.tab:hover {
6869 background-color: #bbb;
6870 border-color: #bbb;
6871 color: #666;
6872 }
6873 span.tab.activeTab, span.tab.activeTab:hover, span.tab.activeTab:visited {
6874 background-color: white;
6875 color: black;
6876 cursor: default;
6877 }
6878 div.tabMain {
6879 border: 2px solid #ccc;
6880 background-color: white;
6881 padding: 10px;
6882 }
6883 div.tabMain.base {
6884 margin-top: 5px;
6885 display: inline-block;
6886 max-width: 98%;
6887 }
6888
6889 div.tabMain.top {
6890 margin-bottom: 5px;
6891 }
6892
6893 div.tabCenter {
6894 max-width: 100%;
6895 overflow-x: auto;
6896 height: 200px;
6897 overflow-y: hidden;
6898 }
6899
6900 canvas.logo_rc {
6901 display:none;
6902 }
6903 .show_rc_logo > canvas {
6904 display: none;
6905 }
6906 .show_rc_logo > canvas.logo_rc {
6907 display: block;
6908 }
6909
6910 canvas.scan_logo {
6911 margin-left: 10px;
6912 }
6913
6914 div.blocks_outer {
6915 position: relative;
6916 padding-top: 20px; /* height of header */
6917 }
6918
6919 div.blocks_inner {
6920 overflow-x: hidden;
6921 overflow-y: auto;
6922 max-height: 200px;
6923 }
6924 table.blocks_tbl {
6925 border-collapse: collapse;
6926 width: 100%;
6927 }
6928
6929 div.blocks_th_inner {
6930 position: absolute;
6931 top: 0;
6932 line-height: 20px; /* height of header */
6933 text-align: left;
6934 padding-left: 5px;
6935 }
6936 th.nopad div.blocks_th_inner {
6937 padding-left: 0;
6938 }
6939 div.blocks_th_hidden {
6940 visibility: hidden;
6941 height: 0;
6942 padding: 0 10px;
6943 }
6944 th.nopad div.blocks_th_hidden {
6945 padding: 0;
6946 }
6947 div.blocks_inner * th {
6948 height: 0;
6949 }
6950
6951 table.blocks_tbl {
6952 overflow-x: hidden;
6953 overflow-y: auto;
6954 }
6955 td.block_td {
6956 width: 99%;
6957 }
6958
6959 *.blockdiag_num {
6960 text-align: right;
6961 }
6962
6963 td.blockdiag_name {
6964 text-align: left;
6965 padding:0px 10px;
6966 }
6967
6968 td.blockdiag_pvalue {
6969 padding:0px 10px;
6970 text-align:right;
6971 white-space: nowrap;
6972 }
6973
6974 div.preview_btn {
6975 border: 2px solid white;
6976 height: 16px;
6977 width: 16px;
6978 font-size: 12px;
6979 line-height: 16px;
6980 text-align: center;
6981 cursor: pointer;
6982 }
6983 div.preview_btn + div.preview_btn {
6984 margin-top: 3px;
6985 }
6986
6987 div.preview_btn.active {
6988 border: 2px solid black;
6989 cursor: default;
6990 }
6991
6992 div.preview_btn:hover {
6993 background-color: black;
6994 color: white;
6995 border-color: black;
6996 }
6997
6998 div.preview_btn.active:hover {
6999 background-color: white;
7000 color: black;
7001 border-color: black;
7002 }
7003
7004
7005 div.preview_btn_box {
7006 position: absolute;
7007 left: 0px;
7008 top: 0px;
7009 padding: 3px;
7010 }
7011
7012 div.preview_logo_box {
7013 height: 50px;
7014 overflow-y: hidden;
7015 }
7016
7017 div.preview_btn_box + div.preview_logo_box {
7018 margin-left: 25px;
7019 }
7020
7021 div.preview_box {
7022 position: relative;
7023 }
7024
7025 div.grey_background {
7026 position:fixed;
7027 z-index: 8;
7028 background-color: #000;
7029 -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
7030 opacity: 0.5;
7031 left: 0;
7032 top: 0;
7033 width: 100%;
7034 height: 100%;
7035 }
7036
7037 div.popup_wrapper {
7038 position:fixed;
7039 z-index:9;
7040 width:100%;
7041 height:0;
7042 top:50%;
7043 left:0;
7044 }
7045
7046 div.popup {
7047 width: 600px;
7048 z-index:9;
7049 margin-left: auto;
7050 margin-right: auto;
7051 padding: 5px;
7052 background-color: #FFF;
7053 border-style: double;
7054 border-width: 5px;
7055 border-color: #00666a;
7056 position:relative;
7057 }
7058 div.close {
7059 cursor: pointer;
7060 border: 1px solid black;
7061 width:15px;
7062 height:15px;
7063 line-height:15px; /* this causes vertical centering */
7064 text-align:center;
7065 background-color:#FFF;
7066 color:#000;
7067 font-size:15px;
7068 font-family:monospace;
7069 }
7070
7071 div.close:hover {
7072 color:#FFF;
7073 background-color:#000;
7074 }
7075
7076 div.navnum {
7077 width:100%;
7078 height:20px;
7079 line-height:20px;
7080 text-align:center;
7081 font-size:medium;
7082 }
7083
7084 div.navarrow {
7085 font-size: 30px;
7086 text-decoration:none;
7087 cursor: pointer;
7088 -moz-user-select: none;
7089 -webkit-user-select: none;
7090 -ms-user-select: none;
7091 }
7092
7093 div.navarrow > span.inactive {
7094 display: inline;
7095 }
7096 div.navarrow > span.active {
7097 display: none;
7098 }
7099
7100 div.navarrow:hover > span.active {
7101 display: inline;
7102 }
7103 div.navarrow:hover > span.inactive {
7104 display: none;
7105 }
7106
7107 table.programs {
7108 width: 100%;
7109 }
7110
7111 table.programs tr {
7112 background-color: #EFE;
7113 }
7114
7115 table.programs tr.selected {
7116 background-color: #262;
7117 color: #FFF;
7118 }
7119
7120 table.programs tr.dna_only {
7121 display: none;
7122 }
7123
7124 table.programs.alphabet_dna tr.dna_only {
7125 display: table-row;
7126 }
7127
7128 div.programs_scroll {
7129 width: 100%;
7130 height: 90px;
7131 overflow-y: auto;
7132 overflow-x: hidden;
7133 margin: 0 auto;
7134 }
7135 table.inputs, table.alpha_bg_table {
7136 margin-top: 20px;
7137 border-collapse:collapse;
7138 }
7139 table.inputs * td, table.inputs * th, table.alpha_bg_table * td, table.alpha_bg_table * th {
7140 padding-left: 15px;
7141 padding-right: 15px;
7142 padding-top: 1px;
7143 padding-bottom: 1px;
7144 }
7145
7146 table.hide_psp td.col_psp, table.hide_psp th.col_psp {
7147 display: none;
7148 }
7149
7150 /* program settings */
7151 span.mod_oops, span.mod_zoops, span.mod_anr {
7152 display: none;
7153 }
7154 td.oops span.mod_oops,td.zoops span.mod_zoops, td.anr span.mod_anr {
7155 display: inline;
7156 }
7157 span.strand_none, span.strand_given, span.strand_both {
7158 display: none;
7159 }
7160 td.none span.strand_none, td.given span.strand_given, td.both span.strand_both {
7161 display: inline;
7162 }
7163 span.spmap_uni, span.spmap_pam {
7164 display: none;
7165 }
7166 td.uni span.spmap_uni, td.pam span.spmap_pam {
7167 display: inline;
7168 }
7169 span.prior_dirichlet, span.prior_dmix, span.prior_mega, span.prior_megap, span.prior_addone {
7170 display: none;
7171 }
7172 td.dirichlet span.prior_dirichlet, td.dmix span.prior_dmix, td.mega span.prior_mega,
7173 td.megap span.prior_megap, td.addone span.prior_addone {
7174 display: inline;
7175 }
7176 span.noendgaps_on, span.noendgaps_off {
7177 display: none;
7178 }
7179 td.on span.noendgaps_on, td.off span.noendgaps_off {
7180 display: inline;
7181 }
7182 span.substring_on, span.substring_off {
7183 display: none;
7184 }
7185 td.on span.substring_on, td.off span.substring_off {
7186 display: inline;
7187 }
7188 </style>
7189 </head>
7190 <body onload="page_loaded()" onpageshow="page_shown(event)" onresize="page_resized()">
7191 <!-- -->
7192 <div id="grey_out_page" class="grey_background" style="display:none;">
7193 </div>
7194 <!-- Help popups -->
7195 <div class="pop_content" id="pop_">
7196 <p>Help poup.</p>
7197 <div style="float:right; bottom:0px;">[
7198 <a href="javascript:help_popup()">close</a> ]</div>
7199 </div>
7200 <div class="pop_content" id="pop_ev">
7201 <p>The statistical significance of the motif. MEME usually finds the most
7202 statistically significant (low E-value) motifs first. It is unusual to
7203 consider a motif with an E-value larger than 0.05 significant so, as an
7204 additional indicator, MEME displays these partially transparent.</p>
7205 <p>The E-value of a motif is based on its log likelihood ratio, width,
7206 sites, the background letter frequencies (given in the command line
7207 summary), and the size of the training set.</p>
7208 <p>The E-value is an estimate of the expected number of motifs with the
7209 given log likelihood ratio (or higher), and with the same width and site
7210 count, that one would find in a similarly sized set of random
7211 sequences (sequences where each position is independent and letters are
7212 chosen according to the background letter frequencies).</p>
7213 <div style="float:right; bottom:0px;">[
7214 <a href="javascript:help_popup()">close</a> ]</div>
7215 </div>
7216 <div class="pop_content" id="pop_sites">
7217 <p>The number of sites contributing to the construction of the motif.</p>
7218 <div style="float:right; bottom:0px;">[
7219 <a href="javascript:help_popup()">close</a> ]</div>
7220 </div>
7221 <div class="pop_content" id="pop_width">
7222 <p>The width of the motif. Each motif describes a pattern of a fixed
7223 width, as no gaps are allowed in MEME motifs.</p>
7224 <div style="float:right; bottom:0px;">[
7225 <a href="javascript:help_popup()">close</a> ]</div>
7226 </div>
7227 <div class="pop_content" id="pop_more">
7228 <p>Click on the blue symbol below to reveal more information about this motif.</p>
7229 <div style="float:right; bottom:0px;">[
7230 <a href="javascript:help_popup()">close</a> ]</div>
7231 </div>
7232 <div class="pop_content" id="pop_submit_dl">
7233 <p>Click on the blue symbol below to reveal options allowing you
7234 to submit this motif to another MEME Suite motif analysis program, to download this
7235 motif in various text formats, or to download a sequence "logo" of
7236 this motif PNG or EPS format.</p>
7237 <h5>Supported Programs</h5>
7238 <dl>
7239 <dt>Tomtom</dt>
7240 <dd>Tomtom is a tool for searching for similar known motifs.
7241 [<a href="http://meme-suite.org/doc/tomtom.html?man_type=web">manual</a>]</dd>
7242 <dt>MAST</dt>
7243 <dd>MAST is a tool for searching biological sequence databases for
7244 sequences that contain one or more of a group of known motifs.
7245 [<a href="http://meme-suite.org/doc/mast.html?man_type=web">manual</a>]</dd>
7246 <dt>FIMO</dt>
7247 <dd>FIMO is a tool for searching biological sequence databases for
7248 sequences that contain one or more known motifs.
7249 [<a href="http://meme-suite.org/doc/fimo.html?man_type=web">manual</a>]</dd>
7250 <dt>GOMO</dt>
7251 <dd>GOMO is a tool for identifying possible roles (Gene Ontology
7252 terms) for DNA binding motifs.
7253 [<a href="http://meme-suite.org/doc/gomo.html?man_type=web">manual</a>]</dd>
7254 <dt>SpaMo</dt>
7255 <dd>SpaMo is a tool for inferring possible transcription factor
7256 complexes by finding motifs with enriched spacings.
7257 [<a href="http://meme-suite.org/doc/spamo.html?man_type=web">manual</a>]</dd>
7258 </dl>
7259 <div style="float:right; bottom:0px;">[
7260 <a href="javascript:help_popup()">close</a> ]</div>
7261 </div>
7262 <div class="pop_content" id="pop_llr">
7263 <p>The log likelihood ratio of the motif.The log likelihood ratio is the
7264 logarithm of the ratio of the probability of the occurrences of the motif
7265 given the motif model (likelihood given the motif) versus their
7266 probability given the background model (likelihood given the null model).
7267 (Normally the background model is a 0-order Markov model using the
7268 background letter frequencies, but higher order Markov models may be
7269 specified via the -bfile option to MEME.).</p>
7270 <div style="float:right; bottom:0px;">[
7271 <a href="javascript:help_popup()">close</a> ]</div>
7272 </div>
7273 <div class="pop_content" id="pop_ic">
7274 <p>The information content of the motif in bits. It is equal to the sum
7275 of the uncorrected information content, R(), in the columns of the pwm.
7276 This is equal relative entropy of the motif relative to a uniform
7277 background frequency model.</p>
7278 <div style="float:right; bottom:0px;">[
7279 <a href="javascript:help_popup()">close</a> ]</div>
7280 </div>
7281 <div class="pop_content" id="pop_re">
7282 <p>The relative entropy of the motif.</p>
7283
7284 <p style="font-family: monospace;">re = llr / (sites * ln(2))</p>
7285 <div style="float:right; bottom:0px;">[
7286 <a href="javascript:help_popup()">close</a> ]</div>
7287 </div>
7288 <div class="pop_content" id="pop_bt">
7289 <p>The Bayes Threshold.</p>
7290 <div style="float:right; bottom:0px;">[
7291 <a href="javascript:help_popup()">close</a> ]</div>
7292 </div>
7293 <div class="pop_content" id="pop_site_strand">
7294 <p>The strand used for the motif site.</p>
7295 <dl>
7296 <dt>+</dt>
7297 <dd>The motif site was found in the sequence as it was supplied.</dd>
7298 <dt>-</dt>
7299 <dd>The motif site was found in the reverse complement of the supplied sequence.</dd>
7300 </dl>
7301 <div style="float:right; bottom:0px;">[
7302 <a href="javascript:help_popup()">close</a> ]</div>
7303 </div>
7304 <div class="pop_content" id="pop_site_start">
7305 <p>The position in the sequence where the motif site starts. If a motif
7306 started right at the begining of a sequence it would be described as
7307 starting at position 1.</p>
7308 <div style="float:right; bottom:0px;">[
7309 <a href="javascript:help_popup()">close</a> ]</div>
7310 </div>
7311 <div class="pop_content" id="pop_site_pvalue">
7312 <p>The probability that an equal or better site would be found in a
7313 random sequence of the same length conforming to the background letter
7314 frequencies.</p>
7315 <div style="float:right; bottom:0px;">[
7316 <a href="javascript:help_popup()">close</a> ]</div>
7317 </div>
7318 <div class="pop_content" id="pop_site_match">
7319 <p>A motif site with the 10 flanking letters on either side.</p>
7320 <p>When the site is not on the given strand then the site
7321 and both flanks are reverse complemented so they align.</p>
7322 <div style="float:right; bottom:0px;">[
7323 <a href="javascript:help_popup()">close</a> ]</div>
7324 </div>
7325
7326 <div class="pop_content" id="pop_seq_name">
7327 <p>The name of the sequences as given in the FASTA file.</p>
7328 <p>The number to the left of the sequence name is the ordinal
7329 of the sequence.</p>
7330 <div style="float:right; bottom:0px;">[
7331 <a href="javascript:help_popup()">close</a> ]</div>
7332 </div>
7333
7334 <div class="pop_content" id="pop_motif_sites">
7335 <p>These are the motif sites predicted by MEME and used to build the motif.</p>
7336 <p>These sites are shown in solid color and hovering the cursor
7337 over a site will reveal details about the site. Only sequences
7338 that contain a motif site are shown.</p>
7339 <div style="float:right; bottom:0px;">[
7340 <a href="javascript:help_popup()">close</a> ]</div>
7341 </div>
7342
7343 <div class="pop_content" id="pop_scanned_sites">
7344 <p>These are the motif sites predicted by MEME plus
7345 any additional sites detected using a motif scanning
7346 algorithm.</p>
7347 <p>These MEME sites are shown in solid color and
7348 additional scanned sites are shown in transparent color.
7349 Hovering the cursor over a site will reveal details about the site.
7350 Only sequences containing a predicted or scanned motif site are shown.</p>
7351 <p>The scanned sites are predicted using a
7352 log-odds scoring matrix constructed from the MEME sites.
7353 Only scanned sites with position <i>p</i>-values less
7354 than 0.0001 are shown.</p>
7355 <div style="float:right; bottom:0px;">[
7356 <a href="javascript:help_popup()">close</a> ]</div>
7357 </div>
7358
7359 <div class="pop_content" id="pop_all_sequences">
7360 <p>These are the same sites as shown by selecting the
7361 "Motif Sites + Scanned Sites" button except that all
7362 sequences, including those with no sites, are included
7363 in the diagram.</p>
7364 <div style="float:right; bottom:0px;">[
7365 <a href="javascript:help_popup()">close</a> ]</div>
7366 </div>
7367
7368 <div class="pop_content" id="pop_seq_pvalue">
7369 <p>This is the combined match <i>p</i>-value.</p>
7370 <p>The combined match <i>p</i>-value is defined as the probability that a
7371 random sequence (with the same length and conforming to the background)
7372 would have position <i>p</i>-values such that the product is smaller
7373 or equal to the value calulated for the sequence under test.</p>
7374 <p>The position <i>p</i>-value is defined as the probability that a
7375 random sequence (with the same length and conforming to the background)
7376 would have a match to the motif under test with a score greater or equal
7377 to the largest found in the sequence under test.</p>
7378 <p>Hovering your mouse over a motif site in the motif location
7379 block diagram will show its position <i>p</i>-value and other information
7380 about the site.</p>
7381
7382 <div style="float:right; bottom:0px;">[
7383 <a href="javascript:help_popup()">close</a> ]</div>
7384 </div>
7385 <div class="pop_content" id="pop_motif_location">
7386 <p>This diagram shows the location of motif sites.</p>
7387 <p>Each block shows the position and strength of a motif
7388 site. The height of a block gives an indication of the
7389 significance of the site as taller blocks are more significant.
7390 The height is calculated to be proportional to the negative
7391 logarithm of the <i>p</i>-value of the site, truncated at
7392 the height for a <i>p</i>-value of 1e-10.</p>
7393 <p>For complementable alphabets (like DNA), sites on the
7394 positive strand are shown above the line,
7395 sites on the negative strand are shown below.</p>
7396 <p>Placing the cursor
7397 over a motif site will reveal more information about the site
7398 including its position <i>p</i>-value. (See the help
7399 for the <i>p</i>-value column for an explanation of position
7400 <i>p</i>-values.)</p>
7401 <div style="float:right; bottom:0px;">[
7402 <a href="javascript:help_popup()">close</a> ]</div>
7403 </div>
7404
7405 <div class="pop_content" id="pop_seq_source">
7406 <p>The name of the file of sequences input to MEME.</p>
7407 <div style="float:right; bottom:0px;">[
7408 <a href="javascript:help_popup()">close</a> ]</div>
7409 </div>
7410 <div class="pop_content" id="pop_psp_source">
7411 <p>The position specific priors file used by MEME to find the motifs.</p>
7412 <div style="float:right; bottom:0px;">[
7413 <a href="javascript:help_popup()">close</a> ]</div>
7414 </div>
7415 <div class="pop_content" id="pop_seq_alph">
7416 <p>The alphabet used by the sequences.</p>
7417 <div style="float:right; bottom:0px;">[
7418 <a href="javascript:help_popup()">close</a> ]</div>
7419 </div>
7420 <div class="pop_content" id="pop_seq_count">
7421 <p>The number of sequences provided as input to MEME.</p>
7422 <div style="float:right; bottom:0px;">[
7423 <a href="javascript:help_popup()">close</a> ]</div>
7424 </div>
7425
7426 <div class="pop_content" id="pop_alph_name">
7427 <p>The name of the alphabet symbol.</p>
7428 <div style="float:right; bottom:0px;">[
7429 <a href="javascript:help_popup()">close</a> ]</div>
7430 </div>
7431 <div class="pop_content" id="pop_alph_freq">
7432 <p>The frequency of the alphabet symbol in the dataset with a pseudocount
7433 so it is never zero.</p>
7434 <div style="float:right; bottom:0px;">[
7435 <a href="javascript:help_popup()">close</a> ]</div>
7436 </div>
7437 <div class="pop_content" id="pop_alph_bg">
7438 <p>The frequency of the alphabet symbol as defined by the background model.</p>
7439 <div style="float:right; bottom:0px;">[
7440 <a href="javascript:help_popup()">close</a> ]</div>
7441 </div>
7442
7443 <!-- templates -->
7444 <div id="measure_match" class="match"></div>
7445 <div class="template pop_block" id="tmpl_block_info">
7446 <div>
7447 <span class="tvar_logo_pad lflank" style="visibility:hidden;"></span>
7448 <span class="tvar_logo"></span>
7449 </div>
7450 <div class="block_sequence_fragment">
7451 <span class="tvar_lflank lflank"></span>
7452 <span class="tvar_match match"></span>
7453 <span class="tvar_rflank rflank"></span>
7454 </div>
7455 <table class="block_information">
7456 <tr><th>Motif</th><td class="tvar_motif">1</td></tr>
7457 <tr><th><i>p</i>-value</th><td class="tvar_pvalue">8.23e-7</td></tr>
7458 <tr><th>Start</th><td class="tvar_start">23</td></tr>
7459 <tr><th>End</th><td class="tvar_end">33</td></tr>
7460 </table>
7461 </div>
7462
7463 <div class="template pop_block" id="tmpl_scan_info">
7464 <h5>Scanned Site</h5>
7465 <div class="tvar_logo"></div>
7466 <table class="block_information">
7467 <tr><th>Motif</th><td class="tvar_motif">1</td></tr>
7468 <tr><th><i>p</i>-value</th><td class="tvar_pvalue">8.23e-7</td></tr>
7469 <tr><th>Start</th><td class="tvar_start">23</td></tr>
7470 <tr><th>End</th><td class="tvar_end">33</td></tr>
7471 </table>
7472 </div>
7473
7474 <div class="template box expanded_motif" id="tmpl_motif_expanded">
7475 <div style="position: relative; min-height: 20px">
7476 <div class="param_box">
7477 <span class="param"><span class="tvar_ordinal"></span>.</span>
7478 </div>
7479 <div class="sym_btn positioned tvar_less" tabindex="0"
7480 title="Show less information.">&#8613;</div>
7481 <div class="sym_btn positioned tvar_submit" tabindex="0"
7482 title="Submit the motif to another MEME Suite program or download it.">&#8674;</div>
7483 </div>
7484 <div>
7485 <div class="param_box">
7486 <span class="param"><i>E</i>-value:</span>
7487 <span class="tvar_evalue"></span>
7488 <div class="help" data-topic="pop_ev"></div>
7489 </div>
7490 <div class="param_box">
7491 <span class="param">Site Count:</span>
7492 <span class="tvar_site_count"></span>
7493 <div class="help" data-topic="pop_sites"></div>
7494 </div>
7495 <div class="param_box">
7496 <span class="param">Width:</span>
7497 <span class="tvar_width"></span>
7498 <div class="help" data-topic="pop_width"></div>
7499 </div>
7500 </div>
7501 <div class="tabMain base">
7502 <div class="tabCenter tvar_logo"></div>
7503 </div>
7504 <div class="tabArea base">
7505 <span class="tvar_tab tab" tabindex="0">Standard</span><span
7506 class="tvar_tab_rc tab middle" tabindex="0">Reverse
7507 Complement</span>
7508 </div>
7509 <div style="padding: 10px 0">
7510 <div class="param_box">
7511 <span class="param">Log Likelihood Ratio:</span>
7512 <span class="tvar_llr"></span>
7513 <div class="help" data-topic="pop_llr"></div>
7514 </div>
7515 <div class="param_box">
7516 <span class="param">Information Content:</span>
7517 <span class="tvar_ic"></span>
7518 <div class="help" data-topic="pop_ic"></div>
7519 </div>
7520 <div class="param_box">
7521 <span class="param">Relative Entropy:</span>
7522 <span class="tvar_re"></span>
7523 <div class="help" data-topic="pop_re"></div>
7524 </div>
7525 <div class="param_box">
7526 <span class="param">Bayes Threshold:</span>
7527 <span class="tvar_bt"></span>
7528 <div class="help" data-topic="pop_bt"></div>
7529 </div>
7530 </div>
7531 <div class="tvar_sites"></div>
7532 </div>
7533
7534
7535 <div class="popup_wrapper">
7536 <div class="popup" style="display:none; top: -150px;" id="download">
7537 <div>
7538 <div style="float:right; ">
7539 <div id="outpop_close" class="close" tabindex="0">x</div>
7540 </div>
7541 <h2 class="mainh" style="margin:0; padding:0;">Submit or Download</h2>
7542 <div style="clear:both"></div>
7543 </div>
7544 <div style="height:100px">
7545 <div style="float:right; width: 30px;">
7546 <div id="outpop_prev" class="navarrow" tabindex="0">
7547 <span class="inactive">&#8679;</span><span class="active">&#11014;</span>
7548 </div>
7549 <div id="outpop_num" class="navnum"></div>
7550 <div id="outpop_next" class="navarrow" tabindex="0">
7551 <span class="inactive">&#8681;</span><span class="active">&#11015;</span>
7552 </div>
7553 </div>
7554 <div id="logo_box" style="height: 100px; margin-right: 40px;">
7555 <canvas id="outpop_logo" height="100" width="580"></canvas>
7556 <canvas id="outpop_logo_rc" class="logo_rc" height="100" width="580"></canvas>
7557 </div>
7558 </div>
7559 <div>
7560 <!-- tabs start -->
7561 <div class="tabArea top">
7562 <span id="outpop_tab_1" class="tab">Submit Motif</span><span
7563 id="outpop_tab_2" class="tab middle">Download Motif</span><span
7564 id="outpop_tab_3" class="tab middle">Download Logo</span>
7565 </div>
7566 <div class="tabMain top">
7567 <!-- Submit to another program -->
7568 <div id="outpop_pnl_1">
7569 <h4 class="compact">Submit to program</h4>
7570 <table id="programs" class="programs">
7571 <tr class="dna_only">
7572 <td><input type="radio" name="program" value="tomtom" id="submit_tomtom"></td>
7573 <td><label for="submit_tomtom">Tomtom</label></td>
7574 <td><label for="submit_tomtom">Find similar motifs in
7575 published libraries or a library you supply.</label></td>
7576 </tr>
7577 <tr>
7578 <td><input type="radio" name="program" value="fimo" id="submit_fimo"></td>
7579 <td><label for="submit_fimo">FIMO</label></td>
7580 <td><label for="submit_fimo">Find motif occurrences in
7581 sequence data.</label></td>
7582 </tr>
7583 <tr>
7584 <td><input type="radio" name="program" value="mast" id="submit_mast"></td>
7585 <td><label for="submit_mast">MAST</label></td>
7586 <td><label for="submit_mast">Rank sequences by affinity to
7587 groups of motifs.</label></td>
7588 </tr>
7589 <tr class="dna_only">
7590 <td><input type="radio" name="program" value="gomo" id="submit_gomo"></td>
7591 <td><label for="submit_gomo">GOMo</label></td>
7592 <td><label for="submit_gomo">Identify possible roles (Gene
7593 Ontology terms) for motifs.</label></td>
7594 </tr>
7595 <tr class="dna_only">
7596 <td><input type="radio" name="program" value="spamo" id="submit_spamo"></td>
7597 <td><label for="submit_spamo">SpaMo</label></td>
7598 <td><label for="submit_spamo">Find other motifs that are
7599 enriched at specific close spacings which might imply the existance of a complex.</label></td>
7600 </tr>
7601 </table>
7602 </div>
7603 <!-- download text format -->
7604 <div id="outpop_pnl_2">
7605 <div>
7606 <label for="text_format">Format:</label>
7607 <select id="text_format">
7608 <option value="0">Count Matrix</option>
7609 <option value="1">Probability Matrix</option>
7610 <option value="2">Minimal MEME</option>
7611 <option value="3">FASTA</option>
7612 <option value="4">Raw</option>
7613 </select>
7614 </div>
7615 <form id="text_form" method="post" action="">
7616 <script>$("text_form").action = site_url + "/utilities/save_generated_file";</script>
7617 <input type="hidden" id="text_name" name="name" value="motif.txt">
7618 <input type="hidden" name="mime_type" value="text/plain">
7619 <textarea id="outpop_text" name="content"
7620 style="width:99%; white-space: pre; word-wrap: normal; overflow-x: scroll;"
7621 rows="8" readonly="readonly" wrap="off"></textarea>
7622 </form>
7623 </div>
7624 <!-- download logo format -->
7625 <div id="outpop_pnl_3">
7626 <form id="logo_form" method="post" action="">
7627 <script>$("logo_form").action = site_url + "/utilities/generate_logo";</script>
7628 <input type="hidden" name="program" value="MEME"/>
7629 <input type="hidden" id="logo_motifs" name="motifs" value=""/>
7630 <table>
7631 <tr>
7632 <td><label for="logo_format">Format:</label></td>
7633 <td>
7634 <select id="logo_format" name="png">
7635 <option value="1">PNG (for web)</option>
7636 <option value="0">EPS (for publication)</option>
7637 </select>
7638 </td>
7639 </tr>
7640 <tr>
7641 <td><label for="logo_rc">Orientation:</label></td>
7642 <td>
7643 <select id="logo_rc" name="rc1">
7644 <option value="0">Normal</option>
7645 <option value="1" id="logo_rc_option">Reverse Complement</option>
7646 </select>
7647 </td>
7648 </tr>
7649 <tr>
7650 <td><label for="logo_ssc">Small Sample Correction:</label></td>
7651 <td>
7652 <input type="hidden" id="logo_err" name="errbars" value="0"/>
7653 <select id="logo_ssc" name="ssc">
7654 <option value="0">Off</option>
7655 <option value="1">On</option>
7656 </select>
7657 </td>
7658 </tr>
7659 <tr>
7660 <td><label for="logo_width">Width:</label></td>
7661 <td>
7662 <input type="text" id="logo_width" size="4" placeholder="default" name="width"/>&nbsp;cm
7663 </td>
7664 </tr>
7665 <tr>
7666 <td><label for="logo_height">Height:</label></td>
7667 <td>
7668 <input type="text" id="logo_height" size="4" placeholder="default" name="height"/>&nbsp;cm
7669 </td>
7670 </tr>
7671 </table>
7672 </form>
7673 </div>
7674 <!-- Buttons -->
7675 <div>
7676 <div style="float:left;">
7677 <input type="button" id="outpop_do" value="Submit" />
7678 </div>
7679 <div style="float:right;">
7680 <input id="outpop_cancel" type="button" value="Cancel" />
7681 </div>
7682 <div style="clear:both;"></div>
7683 </div>
7684 </div>
7685 </div>
7686 </div>
7687 </div>
7688
7689
7690
7691 <!-- Page starts here -->
7692 <div id="top" class="pad1">
7693 <div class="prog_logo big">
7694 <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">
7695 <h1>MEME</h1>
7696 <h2>Multiple Em for Motif Elicitation</h2>
7697 </div>
7698 <p>
7699 For further information on how to interpret these results or to get a
7700 copy of the MEME software please access
7701 <a href="http://meme-suite.org/">http://meme-suite.org</a>.
7702 </p>
7703 <p>If you use MEME in your research, please cite the following paper:<br />
7704 <span class="citation">
7705 Timothy L. Bailey and Charles Elkan,
7706 "Fitting a mixture model by expectation maximization to discover motifs in biopolymers",
7707 <em>Proceedings of the Second International Conference on Intelligent Systems
7708 for Molecular Biology</em>, pp. 28-36, AAAI Press, Menlo Park, California, 1994.
7709 <a href="http://meme-suite.org/doc/ismb94.pdf">[pdf]</a>
7710 </span>
7711 </p>
7712 </div>
7713 <!-- navigation -->
7714 <div class="pad2">
7715 <a class="jump" href="#motifs_sec">Discovered Motifs</a>
7716 &nbsp;&nbsp;|&nbsp;&nbsp;
7717 <a class="jump" href="#sites_sec">Motif Locations</a>
7718 &nbsp;&nbsp;|&nbsp;&nbsp;
7719 <a class="jump" href="#inputs_sec">Inputs &amp; Settings</a>
7720 &nbsp;&nbsp;|&nbsp;&nbsp;
7721 <a class="jump" href="#info_sec">Program information</a>
7722 </div>
7723 <!-- alert the user when their browser is not up to the task -->
7724 <noscript><h1 style="color:red">Javascript is required to view these results!</h1></noscript>
7725 <h1 id="html5_warning" style="color:red; display:none;">Your browser does not support canvas!</h1>
7726 <script>
7727 if (!window.HTMLCanvasElement) $("html5_warning").style.display = "block";
7728 </script>
7729 <h2 class="mainh pad2" id="motifs_sec">Discovered Motifs</h2>
7730 <div id="motifs" class="box">
7731 <p>Please wait... Loading...</p>
7732 <p>If the page has fully loaded and this message does not disappear then an error may have occurred.</p>
7733 </div>
7734 <h2 class="mainh pad2" id="sites_sec">Motif Locations</h2>
7735 <div id="blocks" class="box">
7736 <p>Please wait... Loading...</p>
7737 <p>If the page has fully loaded and this message does not disappear then an error may have occurred.</p>
7738 </div>
7739 <h2 class="mainh pad2" id="inputs_sec">Inputs &amp; Settings</h2>
7740 <div class="box">
7741 <h4>Sequences</h4>
7742 <table id="seq_info" class="inputs">
7743 <tr><th>Source <div class="help" data-topic="pop_seq_source"></div></th>
7744 <th class="col_psp">PSP Source <div class="help" data-topic="pop_psp_source"></div></th>
7745 <th>Alphabet <div class="help" data-topic="pop_seq_alph"></div></th>
7746 <th>Sequence Count <div class="help" data-topic="pop_seq_count"></div></th>
7747 </tr>
7748 <tr>
7749 <td id="ins_seq_source"></td>
7750 <td id="ins_seq_psp" class="col_psp"></td>
7751 <td id="ins_seq_alphabet"></td>
7752 <td id="ins_seq_count"></td>
7753 </tr>
7754 </table>
7755 <script>
7756 {
7757 var db = data.sequence_db;
7758 $("ins_seq_source").innerHTML = db.source;
7759 $("ins_seq_alphabet").innerHTML = meme_alphabet.get_alphabet_name();
7760 $("ins_seq_count").innerHTML = db.sequences.length;
7761 if (db.psp) {
7762 $("ins_seq_psp").innerHTML = db.psp;
7763 }
7764 toggle_class($("seq_info"), "hide_psp", !(db.psp));
7765 }
7766 </script>
7767 <h4>Background</h4>
7768 <span id="alpha_bg"></span>
7769 <script>
7770 {
7771 $("alpha_bg").appendChild(make_alpha_bg_table(meme_alphabet, data.sequence_db.freqs));
7772 }
7773 </script>
7774 <h4>Other Settings</h4>
7775 <table id="tbl_settings" class="inputs hide_advanced">
7776 <tr>
7777 <th>Motif Site Distribution</th>
7778 <td id="opt_mod">
7779 <span class="mod_zoops">ZOOPS: Zero or one site per sequence</span>
7780 <span class="mod_oops">OOPS: Exactly one site per sequence</span>
7781 <span class="mod_anr">ANR: Any number of sites per sequence</span>
7782 </td>
7783 </tr>
7784 <tr>
7785 <th>Site Strand Handling</th>
7786 <td id="opt_strand">
7787 <span class="strand_none">This alphabet only has one strand</span>
7788 <span class="strand_given">Sites must be on the given strand</span>
7789 <span class="strand_both">Sites may be on either strand</span>
7790 </td>
7791 </tr>
7792 <tr>
7793 <th>Maximum Number of Motifs</th>
7794 <td id="opt_nmotifs"></td>
7795 </tr>
7796 <tr>
7797 <th>Motif E-value Threshold</th>
7798 <td id="opt_evt"></td>
7799 </tr>
7800 <tr>
7801 <th>Minimum Motif Width</th>
7802 <td id="opt_minw"></td>
7803 </tr>
7804 <tr>
7805 <th>Maximum Motif Width</th>
7806 <td id="opt_maxw"></td>
7807 </tr>
7808 <tr>
7809 <th>Minimum Sites per Motif</th>
7810 <td id="opt_minsites"></td>
7811 </tr>
7812 <tr>
7813 <th>Maximum Sites per Motif</th>
7814 <td id="opt_maxsites"></td>
7815 </tr>
7816 <tr class="advanced">
7817 <th>Bias on Number of Sites</th>
7818 <td id="opt_wnsites"></td>
7819 </tr>
7820 <tr class="advanced">
7821 <th>Sequence Prior</th>
7822 <td id="opt_prior">
7823 <span class="prior_dirichlet">Simple Dirichlet</span>
7824 <span class="prior_dmix">Dirichlets Mix</span>
7825 <span class="prior_mega">Mega-weight Dirichlets Mix</span>
7826 <span class="prior_megap">Mega-weight Dirichlets Mix Plus</span>
7827 <span class="prior_addone">Add One</span>
7828 </td>
7829 </tr>
7830 <tr class="advanced">
7831 <th>Sequence Prior Strength</th>
7832 <td id="opt_b"></td>
7833 </tr>
7834 <tr class="advanced">
7835 <th>EM Starting Point Source</th>
7836 <td id="opt_substring">
7837 <span class="substring_on">From substrings in input sequences</span>
7838 <span class="substring_off">From strings on command line (-cons)</span>
7839 </td>
7840 </tr>
7841 <tr class="advanced">
7842 <th>EM Starting Point Map Type</th>
7843 <td id="opt_spmap">
7844 <span class="spmap_uni">Uniform</span>
7845 <span class="spmap_pam">Point Accepted Mutation</span>
7846 </td>
7847 </tr>
7848 <tr class="advanced">
7849 <th>EM Starting Point Fuzz</th>
7850 <td id="opt_spfuzz"></td>
7851 </tr>
7852 <tr class="advanced">
7853 <th>EM Maximum Iterations</th>
7854 <td id="opt_maxiter"></td>
7855 </tr>
7856 <tr class="advanced">
7857 <th>EM Improvement Threshold</th>
7858 <td id="opt_distance"></td>
7859 </tr>
7860 <tr class="advanced">
7861 <th>Trim Gap Open Cost</th>
7862 <td id="opt_wg"></td>
7863 </tr>
7864 <tr class="advanced">
7865 <th>Trim Gap Extend Cost</th>
7866 <td id="opt_ws"></td>
7867 </tr>
7868 <tr class="advanced">
7869 <th>End Gap Treatment</th>
7870 <td id="opt_noendgaps">
7871 <span class="noendgaps_on">No cost</span>
7872 <span class="noendgaps_off">Same cost as other gaps</span>
7873 </td>
7874 </tr>
7875 <tr>
7876 <td colspan="2" style="text-align: center">
7877 <a href="javascript:toggle_class(document.getElementById('tbl_settings'), 'hide_advanced')">
7878 <span class="show_more">Show Advanced Settings</span>
7879 <span class="show_less">Hide Advanced Settings</span>
7880 </a>
7881 </td>
7882 </tr>
7883 </table>
7884 <script>
7885 {
7886 $("opt_mod").className = data.options.mod;
7887 $("opt_strand").className = (meme_alphabet.has_complement() ? (data.options.revcomp ? "both" : "given") : "none");
7888 $("opt_nmotifs").textContent = data.options.nmotifs;
7889 $("opt_evt").textContent = (typeof data.options.evt === "number" ? data.options.evt : "no limit");
7890 $("opt_minw").textContent = data.options.minw;
7891 $("opt_maxw").textContent = data.options.maxw;
7892 $("opt_minsites").textContent = data.options.minsites;
7893 $("opt_maxsites").textContent = data.options.maxsites;
7894 $("opt_wnsites").textContent = data.options.wnsites;
7895 $("opt_spmap").className = data.options.spmap;
7896 $("opt_spfuzz").textContent = data.options.spfuzz;
7897 $("opt_prior").className = data.options.prior;
7898 $("opt_b").textContent = data.options.b;
7899 $("opt_maxiter").textContent = data.options.maxiter;
7900 $("opt_distance").textContent = data.options.distance;
7901 $("opt_wg").textContent = data.options.wg;
7902 $("opt_ws").textContent = data.options.ws;
7903 $("opt_noendgaps").className = (data.options.noendgaps ? "on" : "off");
7904 $("opt_substring").className = (data.options.substring ? "on" : "off");
7905 }
7906 </script>
7907 </div>
7908 <!-- list information on this program -->
7909 <div id="info_sec" class="bar">
7910 <div class="subsection">
7911 <h5 id="version">MEME version</h5>
7912 <span id="ins_version"></span>
7913 (Release date: <span id="ins_release"></span>)<br>
7914 </div>
7915 <script>
7916 $("ins_version").innerHTML = data["version"];
7917 $("ins_release").innerHTML = data["release"];
7918 </script>
7919 <div class="subsection">
7920 <h5 id="reference">Reference</h5>
7921 <span class="citation">
7922 Timothy L. Bailey and Charles Elkan,
7923 "Fitting a mixture model by expectation maximization to discover motifs in biopolymers",
7924 <em>Proceedings of the Second International Conference on Intelligent Systems
7925 for Molecular Biology</em>, pp. 28-36, AAAI Press, Menlo Park, California, 1994.
7926 </span>
7927 </div>
7928 <div class="subsection">
7929 <h5 id="command">Command line</h5>
7930 <textarea id="cmd" rows="5" style="width:100%;" readonly="readonly">
7931 </textarea>
7932 <script>$("cmd").value = data["cmd"].join(" ");</script>
7933 </div>
7934 </div>
7935
7936 </body>
7937 </html>