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