Mercurial > repos > iuc > meme_psp_gen
view test-data/meme_output_test1.html @ 2:b48e673af4e8 draft
planemo upload for repository https://github.com/galaxyproject/tools-iuc/tree/master/tools/meme commit e2cf796f991cbe8c96e0cc5a0056b7255ac3ad6b
author | iuc |
---|---|
date | Thu, 17 May 2018 14:11:15 -0400 |
parents | |
children | ff2f53a32d0e |
line wrap: on
line source
<!DOCTYPE HTML> <html> <head> <meta charset="UTF-8"> <title>MEME</title> <script> // @JSON_VAR data var data = { "program": "MEME", "version": "4.12.0", "release": "Tue Jun 27 16:22:50 2017 -0700", "stop_reason": "Stopped because requested number of motifs (1) found.", "cmd": [ "meme", "meme_input_1.fasta", "-o", "meme_test1_out", "-nostatus", "-maxsize", "1000000" ], "options": { "mod": "zoops", "revcomp": false, "nmotifs": 1, "minw": 8, "maxw": 50, "minsites": 2, "maxsites": 30, "wnsites": 0.8, "spmap": "pam", "spfuzz": 120, "maxwords": -1, "prior": "megap", "b": 7500, "maxiter": 50, "distance": 1e-05, "wg": 11, "ws": 1, "noendgaps": false, "substring": true }, "alphabet": { "name": "Protein", "like": "protein", "ncore": 20, "symbols": [ { "symbol": "A", "name": "Alanine", "colour": "0000CC" }, { "symbol": "C", "name": "Cysteine", "colour": "0000CC" }, { "symbol": "D", "name": "Aspartic acid", "colour": "FF00FF" }, { "symbol": "E", "name": "Glutamic acid", "colour": "FF00FF" }, { "symbol": "F", "name": "Phenylalanine", "colour": "0000CC" }, { "symbol": "G", "name": "Glycine", "colour": "FFB300" }, { "symbol": "H", "name": "Histidine", "colour": "FFCCCC" }, { "symbol": "I", "name": "Isoleucine", "colour": "0000CC" }, { "symbol": "K", "name": "Lysine", "colour": "CC0000" }, { "symbol": "L", "name": "Leucine", "colour": "0000CC" }, { "symbol": "M", "name": "Methionine", "colour": "0000CC" }, { "symbol": "N", "name": "Asparagine", "colour": "008000" }, { "symbol": "P", "name": "Proline", "colour": "FFFF00" }, { "symbol": "Q", "name": "Glutamine", "colour": "008000" }, { "symbol": "R", "name": "Arginine", "colour": "CC0000" }, { "symbol": "S", "name": "Serine", "colour": "008000" }, { "symbol": "T", "name": "Threonine", "colour": "008000" }, { "symbol": "V", "name": "Valine", "colour": "0000CC" }, { "symbol": "W", "name": "Tryptophan", "colour": "0000CC" }, { "symbol": "Y", "name": "Tyrosine", "colour": "33E6CC" }, { "symbol": "X", "aliases": "*.", "name": "Any amino acid", "equals": "ACDEFGHIKLMNPQRSTVWY" }, { "symbol": "B", "name": "Asparagine or Aspartic acid", "equals": "DN" }, { "symbol": "Z", "name": "Glutamine or Glutamic acid", "equals": "EQ" }, { "symbol": "J", "name": "Leucine or Isoleucine", "equals": "IL" } ] }, "background": { "freqs": [ 0.291, 0.229, 0.001, 0.001, 0.001, 0.255, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.001, 0.215, 0.001, 0.001, 0.001 ] }, "sequence_db": { "source": "meme_input_1.fasta", "psp_source": "prior30.plib", "freqs": [ 0.294, 0.231, 0.000, 0.000, 0.000, 0.257, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, 0.217, 0.000, 0.000, 0.000 ], "sequences": [ { "name": "chr21_19617074_19617124_+", "length": 50, "weight": 1.000000 }, { "name": "chr21_26934381_26934431_+", "length": 50, "weight": 1.000000 }, { "name": "chr21_28217753_28217803_-", "length": 50, "weight": 1.000000 }, { "name": "chr21_31710037_31710087_-", "length": 50, "weight": 1.000000 }, { "name": "chr21_31744582_31744632_-", "length": 50, "weight": 1.000000 }, { "name": "chr21_31768316_31768366_+", "length": 50, "weight": 1.000000 }, { "name": "chr21_31914206_31914256_-", "length": 50, "weight": 1.000000 }, { "name": "chr21_31933633_31933683_-", "length": 50, "weight": 1.000000 }, { "name": "chr21_31962741_31962791_-", "length": 50, "weight": 1.000000 }, { "name": "chr21_31964683_31964733_+", "length": 50, "weight": 1.000000 }, { "name": "chr21_31973364_31973414_+", "length": 50, "weight": 1.000000 }, { "name": "chr21_31992870_31992920_+", "length": 50, "weight": 1.000000 }, { "name": "chr21_32185595_32185645_-", "length": 50, "weight": 1.000000 }, { "name": "chr21_32202076_32202126_-", "length": 50, "weight": 1.000000 }, { "name": "chr21_32253899_32253949_-", "length": 50, "weight": 1.000000 }, { "name": "chr21_32410820_32410870_-", "length": 50, "weight": 1.000000 }, { "name": "chr21_36411748_36411798_-", "length": 50, "weight": 1.000000 }, { "name": "chr21_37838750_37838800_-", "length": 50, "weight": 1.000000 }, { "name": "chr21_45705687_45705737_+", "length": 50, "weight": 1.000000 }, { "name": "chr21_45971413_45971463_-", "length": 50, "weight": 1.000000 }, { "name": "chr21_45978668_45978718_-", "length": 50, "weight": 1.000000 }, { "name": "chr21_45993530_45993580_+", "length": 50, "weight": 1.000000 }, { "name": "chr21_46020421_46020471_+", "length": 50, "weight": 1.000000 }, { "name": "chr21_46031920_46031970_+", "length": 50, "weight": 1.000000 }, { "name": "chr21_46046964_46047014_+", "length": 50, "weight": 1.000000 }, { "name": "chr21_46057197_46057247_+", "length": 50, "weight": 1.000000 }, { "name": "chr21_46086869_46086919_-", "length": 50, "weight": 1.000000 }, { "name": "chr21_46102103_46102153_-", "length": 50, "weight": 1.000000 }, { "name": "chr21_47517957_47518007_+", "length": 50, "weight": 1.000000 }, { "name": "chr21_47575506_47575556_-", "length": 50, "weight": 1.000000 } ] }, "motifs": [ { "db": 0, "id": "GGGGTATAAAA", "alt": "MEME-1", "len": 11, "nsites": 25, "evalue": "2.4e-011", "ic": 40.0, "re": 13.8, "llr": 239, "bt": 5.33554, "time": 0.772000, "psm": [ [ -32, -680, 91, 77, 7, 138, -20, 55, 64, 107, 11, 150, 142, 72, 87, 396, -148, 221, -140, -36 ], [ -11, -680, 89, 76, 7, 137, -21, 55, 63, 107, 10, 149, 141, 71, 87, 396, -239, 220, -140, -36 ], [ -79, 41, 4, 21, -7, 44, -62, 42, -5, 99, 0, 99, 138, 52, 42, 399, -46, 223, -173, -68 ], [ 11, -677, 48, 47, -2, 127, -43, 46, 27, 101, 3, 124, 138, 60, 62, 397, -235, 220, -160, -55 ], [ -596, -820, 12, -21, -53, -267, -74, 37, 16, 44, -37, 98, 31, 9, 19, 319, 212, 127, -193, -95 ], [ 165, -261, 70, 110, 77, -521, -4, 147, 95, 201, 90, 121, 124, 91, 107, 425, -527, 314, -95, 8 ], [ -838, -990, -89, -149, -151, -841, -161, -117, -113, -66, -209, -68, -69, -129, -91, 111, 221, -55, -255, -173 ], [ 176, -858, -79, -103, -115, -717, -148, -95, -108, -17, -162, -61, -12, -95, -69, 193, -737, 52, -240, -153 ], [ 134, -686, 0, 16, -12, -553, -68, 44, -8, 96, -9, 88, 124, 41, 36, 384, 11, 216, -177, -71 ], [ 165, -261, 70, 110, 77, -521, -4, 147, 95, 201, 90, 121, 124, 91, 107, 425, -527, 314, -95, 8 ], [ 147, -614, 89, 129, 93, -121, 12, 160, 113, 217, 108, 144, 144, 111, 125, 447, -241, 332, -81, 22 ] ], "pwm": [ [ 0.240000, 0.000000, 0.000000, 0.000000, 0.000000, 0.680000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.080000, 0.000000, 0.000000, 0.000000 ], [ 0.280000, 0.000000, 0.000000, 0.000000, 0.000000, 0.680000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.040000, 0.000000, 0.000000, 0.000000 ], [ 0.160000, 0.320000, 0.000000, 0.000000, 0.000000, 0.360000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.160000, 0.000000, 0.000000, 0.000000 ], [ 0.320000, 0.000000, 0.000000, 0.000000, 0.000000, 0.640000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.040000, 0.000000, 0.000000, 0.000000 ], [ 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.040000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.960000, 0.000000, 0.000000, 0.000000 ], [ 0.960000, 0.040000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000 ], [ 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000, 0.000000, 0.000000, 0.000000 ], [ 1.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000 ], [ 0.760000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.240000, 0.000000, 0.000000, 0.000000 ], [ 0.960000, 0.040000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000 ], [ 0.840000, 0.000000, 0.000000, 0.000000, 0.000000, 0.120000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.040000, 0.000000, 0.000000, 0.000000 ] ], "sites": [ { "seq": 24, "pos": 12, "rc": false, "pvalue": 1.06e-06, "lflank": "AAGGCCAGGA", "match": "GGGGTATAAAA", "rflank": "GCCTGAGAGC" }, { "seq": 25, "pos": 36, "rc": false, "pvalue": 3.41e-06, "lflank": "ACAGGCCCTG", "match": "GGCATATAAAA", "rflank": "GCC" }, { "seq": 19, "pos": 9, "rc": false, "pvalue": 3.41e-06, "lflank": "CAGGCCCTG", "match": "GGCATATAAAA", "rflank": "GCCCCAGCAG" }, { "seq": 9, "pos": 13, "rc": false, "pvalue": 3.41e-06, "lflank": "GATTCACTGA", "match": "GGCATATAAAA", "rflank": "GGCCCTCTGC" }, { "seq": 21, "pos": 7, "rc": false, "pvalue": 4.00e-06, "lflank": "CCAAGGA", "match": "GGAGTATAAAA", "rflank": "GCCCCACAAA" }, { "seq": 13, "pos": 13, "rc": false, "pvalue": 5.01e-06, "lflank": "CCACCAGCTT", "match": "GAGGTATAAAA", "rflank": "AGCCCTGTAC" }, { "seq": 23, "pos": 15, "rc": false, "pvalue": 6.06e-06, "lflank": "ATACCCAGGG", "match": "AGGGTATAAAA", "rflank": "CCTCAGCAGC" }, { "seq": 15, "pos": 21, "rc": false, "pvalue": 8.67e-06, "lflank": "AATCACTGAG", "match": "GATGTATAAAA", "rflank": "GTCCCAGGGA" }, { "seq": 12, "pos": 18, "rc": false, "pvalue": 8.67e-06, "lflank": "CACCAGAGCT", "match": "GGGATATATAA", "rflank": "AGAAGGTTCT" }, { "seq": 11, "pos": 16, "rc": false, "pvalue": 8.67e-06, "lflank": "CACTATTGAA", "match": "GATGTATAAAA", "rflank": "TTTCATTTGC" }, { "seq": 22, "pos": 2, "rc": false, "pvalue": 1.21e-05, "lflank": "GA", "match": "GACATATAAAA", "rflank": "GCCAACATCC" }, { "seq": 28, "pos": 32, "rc": false, "pvalue": 1.59e-05, "lflank": "CCGGCGGGGC", "match": "GGGGTATAAAG", "rflank": "GGGGCGG" }, { "seq": 20, "pos": 4, "rc": false, "pvalue": 1.59e-05, "lflank": "CAGA", "match": "GGGGTATAAAG", "rflank": "GTTCCGACCA" }, { "seq": 6, "pos": 15, "rc": false, "pvalue": 1.68e-05, "lflank": "CCCACTACTT", "match": "AGAGTATAAAA", "rflank": "TCATTCTGAG" }, { "seq": 14, "pos": 19, "rc": false, "pvalue": 2.03e-05, "lflank": "CACCAGCAAG", "match": "GATATATAAAA", "rflank": "GCTCAGGAGT" }, { "seq": 4, "pos": 12, "rc": false, "pvalue": 3.06e-05, "lflank": "CAGGTCTAAG", "match": "AGCATATATAA", "rflank": "CTTGGAGTCC" }, { "seq": 0, "pos": 39, "rc": false, "pvalue": 3.06e-05, "lflank": "CCTCGGGACG", "match": "TGGGTATATAA", "rflank": "" }, { "seq": 18, "pos": 37, "rc": false, "pvalue": 3.82e-05, "lflank": "CGTGGTCGCG", "match": "GGGGTATAACA", "rflank": "GC" }, { "seq": 5, "pos": 0, "rc": false, "pvalue": 3.82e-05, "lflank": "", "match": "AACGTATATAA", "rflank": "ATGGTCCTGT" }, { "seq": 29, "pos": 30, "rc": false, "pvalue": 4.02e-05, "lflank": "GCTGCCGGTG", "match": "AGCGTATAAAG", "rflank": "GCCCTGGCG" }, { "seq": 1, "pos": 27, "rc": false, "pvalue": 5.52e-05, "lflank": "AGTCACAAGT", "match": "GAGTTATAAAA", "rflank": "GGGTCGCACG" }, { "seq": 3, "pos": 14, "rc": false, "pvalue": 5.94e-05, "lflank": "CCCAGGTTTC", "match": "TGAGTATATAA", "rflank": "TCGCCGCACC" }, { "seq": 16, "pos": 22, "rc": false, "pvalue": 6.78e-05, "lflank": "AGTTTCAGTT", "match": "GGCATCTAAAA", "rflank": "attatataac" }, { "seq": 7, "pos": 2, "rc": false, "pvalue": 2.08e-04, "lflank": "TC", "match": "AGAGTATATAT", "rflank": "AAATGTTCCT" }, { "seq": 8, "pos": 13, "rc": false, "pvalue": 4.05e-04, "lflank": "TATAACTCAG", "match": "GTTGGATAAAA", "rflank": "TAATTTGTAC" } ] } ], "scan": [ { "pvalue": 1.22e-03, "sites": [ { "motif": 0, "pos": 39, "rc": false, "pvalue": 3.06e-05 } ] }, { "pvalue": 2.21e-03, "sites": [ { "motif": 0, "pos": 27, "rc": false, "pvalue": 5.52e-05 } ] }, { "pvalue": 7.29e-01, "sites": [] }, { "pvalue": 2.37e-03, "sites": [ { "motif": 0, "pos": 14, "rc": false, "pvalue": 5.94e-05 } ] }, { "pvalue": 1.22e-03, "sites": [ { "motif": 0, "pos": 12, "rc": false, "pvalue": 3.06e-05 } ] }, { "pvalue": 1.53e-03, "sites": [ { "motif": 0, "pos": 0, "rc": false, "pvalue": 3.82e-05 } ] }, { "pvalue": 6.70e-04, "sites": [ { "motif": 0, "pos": 15, "rc": false, "pvalue": 1.68e-05 } ] }, { "pvalue": 1.81e-03, "sites": [ { "motif": 0, "pos": 4, "rc": false, "pvalue": 4.54e-05 } ] }, { "pvalue": 1.61e-02, "sites": [] }, { "pvalue": 1.36e-04, "sites": [ { "motif": 0, "pos": 13, "rc": false, "pvalue": 3.41e-06 } ] }, { "pvalue": 1.99e-01, "sites": [] }, { "pvalue": 3.47e-04, "sites": [ { "motif": 0, "pos": 16, "rc": false, "pvalue": 8.67e-06 } ] }, { "pvalue": 3.47e-04, "sites": [ { "motif": 0, "pos": 18, "rc": false, "pvalue": 8.67e-06 } ] }, { "pvalue": 2.01e-04, "sites": [ { "motif": 0, "pos": 13, "rc": false, "pvalue": 5.01e-06 } ] }, { "pvalue": 8.11e-04, "sites": [ { "motif": 0, "pos": 19, "rc": false, "pvalue": 2.03e-05 } ] }, { "pvalue": 3.47e-04, "sites": [ { "motif": 0, "pos": 21, "rc": false, "pvalue": 8.67e-06 } ] }, { "pvalue": 2.71e-03, "sites": [ { "motif": 0, "pos": 22, "rc": false, "pvalue": 6.78e-05 } ] }, { "pvalue": 8.23e-02, "sites": [] }, { "pvalue": 1.53e-03, "sites": [ { "motif": 0, "pos": 37, "rc": false, "pvalue": 3.82e-05 } ] }, { "pvalue": 1.36e-04, "sites": [ { "motif": 0, "pos": 9, "rc": false, "pvalue": 3.41e-06 } ] }, { "pvalue": 6.37e-04, "sites": [ { "motif": 0, "pos": 4, "rc": false, "pvalue": 1.59e-05 } ] }, { "pvalue": 1.60e-04, "sites": [ { "motif": 0, "pos": 7, "rc": false, "pvalue": 4.00e-06 } ] }, { "pvalue": 4.83e-04, "sites": [ { "motif": 0, "pos": 2, "rc": false, "pvalue": 1.21e-05 } ] }, { "pvalue": 2.43e-04, "sites": [ { "motif": 0, "pos": 15, "rc": false, "pvalue": 6.06e-06 } ] }, { "pvalue": 4.26e-05, "sites": [ { "motif": 0, "pos": 12, "rc": false, "pvalue": 1.06e-06 } ] }, { "pvalue": 1.36e-04, "sites": [ { "motif": 0, "pos": 36, "rc": false, "pvalue": 3.41e-06 } ] }, { "pvalue": 4.30e-02, "sites": [] }, { "pvalue": 4.30e-02, "sites": [] }, { "pvalue": 6.37e-04, "sites": [ { "motif": 0, "pos": 32, "rc": false, "pvalue": 1.59e-05 } ] }, { "pvalue": 1.61e-03, "sites": [ { "motif": 0, "pos": 30, "rc": false, "pvalue": 4.02e-05 } ] } ] }; </script> <script> var site_url = "http://meme-suite.org"; </script> <script> /* * $ * * Shorthand function for getElementById */ function $(el) { return document.getElementById(el); } /* * See http://stackoverflow.com/a/5450113/66387 * Does string multiplication like the perl x operator. */ function string_mult(pattern, count) { if (count < 1) return ''; var result = ''; while (count > 1) { if (count & 1) result += pattern; count >>= 1, pattern += pattern; } return result + pattern; } /* * See http://stackoverflow.com/questions/814613/how-to-read-get-data-from-a-url-using-javascript * Slightly modified with information from * https://developer.mozilla.org/en/DOM/window.location */ function parse_params() { "use strict"; var search, queryStart, queryEnd, query, params, nvPairs, i, nv, n, v; search = window.location.search; queryStart = search.indexOf("?") + 1; queryEnd = search.indexOf("#") + 1 || search.length + 1; query = search.slice(queryStart, queryEnd - 1); if (query === search || query === "") return {}; params = {}; nvPairs = query.replace(/\+/g, " ").split("&"); for (i = 0; i < nvPairs.length; i++) { nv = nvPairs[i].split("="); n = decodeURIComponent(nv[0]); v = decodeURIComponent(nv[1]); // allow a name to be used multiple times // storing each value in the array if (!(n in params)) { params[n] = []; } params[n].push(nv.length === 2 ? v : null); } return params; } /* * coords * * Calculates the x and y offset of an element. * From http://www.quirksmode.org/js/findpos.html * with alterations to take into account scrolling regions */ function coords(elem) { var myX = myY = 0; if (elem.getBoundingClientRect) { var rect; rect = elem.getBoundingClientRect(); myX = rect.left + ((typeof window.pageXOffset !== "undefined") ? window.pageXOffset : document.body.scrollLeft); myY = rect.top + ((typeof window.pageYOffset !== "undefined") ? window.pageYOffset : document.body.scrollTop); } else { // this fall back doesn't properly handle absolutely positioned elements // inside a scrollable box var node; if (elem.offsetParent) { // subtract all scrolling node = elem; do { myX -= node.scrollLeft ? node.scrollLeft : 0; myY -= node.scrollTop ? node.scrollTop : 0; } while (node = node.parentNode); // this will include the page scrolling (which is unwanted) so add it back on myX += (typeof window.pageXOffset !== "undefined") ? window.pageXOffset : document.body.scrollLeft; myY += (typeof window.pageYOffset !== "undefined") ? window.pageYOffset : document.body.scrollTop; // sum up offsets node = elem; do { myX += node.offsetLeft; myY += node.offsetTop; } while (node = node.offsetParent); } } return [myX, myY]; } /* * position_popup * * Positions a popup relative to an anchor element. * * The avaliable positions are: * 0 - Centered below the anchor. */ function position_popup(anchor, popup, position) { "use strict"; var a_x, a_y, a_w, a_h, p_x, p_y, p_w, p_h; var a_xy, spacer, margin, scrollbar, page_w; // define constants spacer = 5; margin = 15; scrollbar = 15; // define the positions and widths a_xy = coords(anchor); a_x = a_xy[0]; a_y = a_xy[1]; a_w = anchor.offsetWidth; a_h = anchor.offsetHeight; p_w = popup.offsetWidth; p_h = popup.offsetHeight; page_w = null; if (window.innerWidth) { page_w = window.innerWidth; } else if (document.body) { page_w = document.body.clientWidth; } // check the position type is defined if (typeof position !== "number") { position = 0; } // calculate the popup position switch (position) { case 1: p_x = a_x + a_w + spacer; p_y = a_y + (a_h / 2) - (p_h / 2); break; case 0: default: p_x = a_x + (a_w / 2) - (p_w / 2); p_y = a_y + a_h + spacer; break; } // constrain the popup position if (p_x < margin) { p_x = margin; } else if (page_w != null && (p_x + p_w) > (page_w - margin - scrollbar)) { p_x = page_w - margin - scrollbar - p_w; } if (p_y < margin) { p_y = margin; } // position the popup popup.style.left = p_x + "px"; popup.style.top = p_y + "px"; } function lookup_help_popup(popup_id) { var _body, pop, info; pop = document.getElementById(popup_id); if (pop == null) { _body = document.getElementsByTagName("body")[0]; pop = document.createElement("div"); pop.className = "pop_content"; pop.id = popup_id; pop.style.backgroundColor = "#FFC"; pop.style.borderColor = "black"; info = document.createElement("p"); info.style.fontWeight = "bold"; info.appendChild(document.createTextNode("Error: No popup for topic \"" + popup_id + "\".")); pop.appendChild(info); // this might cause problems with the menu, but as this only happens // when something is already wrong I don't think that's too much of a problem _body.insertBefore(pop, _body.firstChild); } if (document.getElementsByTagName('body')[0].hasAttribute("data-autobtns")) { if (!/\bauto_buttons\b/.test(pop.className)) { pop.className += " auto_buttons"; var back_btn_sec = document.createElement("div"); back_btn_sec.className = "nested_only pop_back_sec"; var back_btn = document.createElement("span"); back_btn.className = "pop_back"; back_btn.appendChild(document.createTextNode("<< back")); back_btn.addEventListener("click", function(e) { help_return(); }, false); back_btn_sec.appendChild(back_btn); pop.insertBefore(back_btn_sec, pop.firstChild); var close_btn_sec = document.createElement("div"); close_btn_sec.className = "pop_close_sec"; var close_btn = document.createElement("span"); close_btn.className = "pop_close"; close_btn.appendChild(document.createTextNode("close")); close_btn.addEventListener("click", function(e) { help_popup(); }, false); close_btn_sec.appendChild(close_btn); pop.appendChild(close_btn_sec); } } return pop; } /* * help_popup * * Moves around help pop-ups so they appear * below an activator. */ function help_popup(activator, popup_id) { "use strict"; var pop; // set default values if (typeof help_popup.popup === "undefined") { help_popup.popup = []; } if (typeof help_popup.activator === "undefined") { help_popup.activator = null; } var last_pop = (help_popup.popup.length > 0 ? help_popup.popup[help_popup.popup.length - 1] : null); if (typeof(activator) == "undefined") { // no activator so hide if (last_pop != null) { last_pop.style.display = 'none'; help_popup.popup = []; } return; } pop = lookup_help_popup(popup_id); if (pop == last_pop) { if (activator == help_popup.activator) { //hide popup (as we've already shown it for the current help button) last_pop.style.display = 'none'; help_popup.popup = []; return; // toggling complete! } } else if (last_pop != null) { //activating different popup so hide current one last_pop.style.display = 'none'; } help_popup.popup = [pop]; help_popup.activator = activator; toggle_class(pop, "nested", false); //must make the popup visible to measure it or it has zero width pop.style.display = 'block'; position_popup(activator, pop); } /* * help_refine * * Intended for links within a help popup. Stores a stack of state so * you can go back. */ function help_refine(popup_id) { if (help_popup.popup == null || help_popup.popup.length == 0 || help_popup.activator == null) { throw new Error("Can not refine a help popup when one is not shown!"); } var pop = lookup_help_popup(popup_id); var last_pop = help_popup.popup[help_popup.popup.length - 1]; if (pop == last_pop) return; // slightly odd, but no real cause for alarm help_popup.popup.push(pop); toggle_class(pop, "nested", true); last_pop.style.display = "none"; //must make the popup visible to measure it or it has zero width pop.style.display = "block"; position_popup(help_popup.activator, pop); } /* * help_return * * Intended for links within a help popup. Stores a stack of state so * you can go back. */ function help_return() { if (help_popup.popup == null || help_popup.popup.length == 0 || help_popup.activator == null) { throw new Error("Can not return to a earlier help popup when one is not shown!"); } var last_pop = help_popup.popup.pop(); last_pop.style.display = "none"; var pop = (help_popup.popup.length > 0 ? help_popup.popup[help_popup.popup.length - 1] : null); if (pop != null) { toggle_class(pop, "nested", help_popup.popup.length > 1); pop.style.display = "block"; position_popup(help_popup.activator, pop); } else { help_popup.activator = null; } } /* * update_scroll_pad * * Creates padding at the bottom of the page to allow * scrolling of anything into view. */ function update_scroll_pad() { var page, pad; page = (document.compatMode === "CSS1Compat") ? document.documentElement : document.body; pad = $("scrollpad"); if (pad === null) { pad = document.createElement("div"); pad.id = 'scrollpad'; document.getElementsByTagName('body')[0].appendChild(pad); } pad.style.height = Math.abs(page.clientHeight - 100) + "px"; } function substitute_classes(node, remove, add) { "use strict"; var list, all, i, cls, classes; list = node.className.split(/\s+/); all = {}; for (i = 0; i < list.length; i++) { if (list[i].length > 0) all[list[i]] = true; } for (i = 0; i < remove.length; i++) { if (all.hasOwnProperty(remove[i])) { delete all[remove[i]]; } } for (i = 0; i < add.length; i++) { all[add[i]] = true; } classes = ""; for (cls in all) { classes += cls + " "; } node.className = classes; } /* * toggle_class * * Adds or removes a class from the node. If the parameter 'enabled' is not * passed then the existence of the class will be toggled, otherwise it will be * included if enabled is true. */ function toggle_class(node, cls, enabled) { var classes = node.className; var list = classes.replace(/^\s+/, '').replace(/\s+$/, '').split(/\s+/); var found = false; for (var i = 0; i < list.length; i++) { if (list[i] == cls) { list.splice(i, 1); i--; found = true; } } if (typeof enabled == "undefined") { if (!found) list.push(cls); } else { if (enabled) list.push(cls); } node.className = list.join(" "); } /* * find_child * * Searches child nodes in depth first order and returns the first it finds * with the className specified. * TODO replace with querySelector */ function find_child(node, className) { var pattern; if (node == null || typeof node !== "object") { return null; } if (typeof className === "string") { pattern = new RegExp("\\b" + className + "\\b"); } else { pattern = className; } if (node.nodeType == Node.ELEMENT_NODE && pattern.test(node.className)) { return node; } else { var result = null; for (var i = 0; i < node.childNodes.length; i++) { result = find_child(node.childNodes[i], pattern); if (result != null) break; } return result; } } /* * find_parent * * Searches parent nodes outwards from the node and returns the first it finds * with the className specified. */ function find_parent(node, className) { var pattern; pattern = new RegExp("\\b" + className + "\\b"); do { if (node.nodeType == Node.ELEMENT_NODE && pattern.test(node.className)) { return node; } } while (node = node.parentNode); return null; } /* * find_parent_tag * * Searches parent nodes outwards from the node and returns the first it finds * with the tag name specified. HTML tags should be specified in upper case. */ function find_parent_tag(node, tag_name) { do { if (node.nodeType == Node.ELEMENT_NODE && node.tagName == tag_name) { return node; } } while (node = node.parentNode); return null; } /* * __toggle_help * * Uses the 'topic' property of the this object to * toggle display of a help topic. * * This function is not intended to be called directly. */ function __toggle_help(e) { if (!e) e = window.event; if (e.type === "keydown") { if (e.keyCode !== 13 && e.keyCode !== 32) { return; } // stop a submit or something like that e.preventDefault(); } help_popup(this, this.getAttribute("data-topic")); } function setup_help_button(button) { "use strict"; var topic; if (button.hasAttribute("data-topic")) { topic = button.getAttribute("data-topic"); if (document.getElementById(topic) != null) { button.tabIndex = "0"; // make keyboard selectable button.addEventListener("click", function() { help_popup(button, topic); }, false); button.addEventListener("keydown", function(e) { // toggle only on Enter or Spacebar, let other keys do their thing if (e.keyCode !== 13 && e.keyCode !== 32) return; // stop a submit or something like that e.preventDefault(); help_popup(button, topic); }, false); } else { button.style.visibility = "hidden"; } } button.className += " active"; } /* * help_button * * Makes a help button for the passed topic. */ function help_button(topic) { var btn = document.createElement("div"); btn.className = "help"; btn.setAttribute("data-topic", topic); setup_help_button(btn); return btn; } /* * prepare_download * * Sets the attributes of a link to setup a file download using the given content. * If no link is provided then create one and click it. */ function prepare_download(content, mimetype, filename, link) { "use strict"; // if no link is provided then create one and click it var click_link = false; if (!link) { link = document.createElement("a"); click_link = true; } try { // Use a BLOB to convert the text into a data URL. // We could do this manually with a base 64 conversion. // This will only be supported on modern browsers, // hence the try block. var blob = new Blob([content], {type: mimetype}); var reader = new FileReader(); reader.onloadend = function() { // If we're lucky the browser will also support the download // attribute which will let us suggest a file name to save the link. // Otherwise it is likely that the filename will be unintelligible. link.setAttribute("download", filename); link.href = reader.result; if (click_link) { // must add the link to click it document.body.appendChild(link); link.click(); document.body.removeChild(link); } } reader.readAsDataURL(blob); } catch (error) { if (console && console.log) console.log(error); // probably an old browser link.href = ""; link.visible = false; } } /* * add_cell * * Add a cell to the table row. */ function add_cell(row, node, cls, click_action) { var cell = row.insertCell(row.cells.length); if (node) cell.appendChild(node); if (cls && cls !== "") cell.className = cls; if (click_action) cell.addEventListener("click", click_action, false); } /* * add_header_cell * * Add a header cell to the table row. */ function add_header_cell(row, node, help_topic, cls, colspan) { var th = document.createElement("th"); if (node) th.appendChild(node); if (help_topic && help_topic !== "") th.appendChild(help_button(help_topic)); if (cls && cls !== "") th.className = cls; if (typeof colspan == "number" && colspan > 1) th.colSpan = colspan; row.appendChild(th); } /* * add_text_cell * * Add a text cell to the table row. */ function add_text_cell(row, text, cls, action) { var node = null; if (typeof(text) != 'undefined') node = document.createTextNode(text); add_cell(row, node, cls, action); } /* * add_text_header_cell * * Add a text header cell to the table row. */ function add_text_header_cell(row, text, help_topic, cls, action, colspan) { var node = null; if (typeof(text) != 'undefined') { var nbsp = (help_topic ? "\u00A0" : ""); var str = "" + text; var parts = str.split(/\n/); if (parts.length === 1) { if (action) { node = document.createElement("span"); node.appendChild(document.createTextNode(str + nbsp)); } else { node = document.createTextNode(str + nbsp); } } else { node = document.createElement("span"); for (var i = 0; i < parts.length; i++) { if (i !== 0) { node.appendChild(document.createElement("br")); } node.appendChild(document.createTextNode(parts[i])); } } if (action) { node.addEventListener("click", action, false); node.style.cursor = "pointer"; } } add_header_cell(row, node, help_topic, cls, colspan); } function setup_help() { "use strict"; var help_buttons, i; help_buttons = document.querySelectorAll(".help:not(.active)"); for (i = 0; i < help_buttons.length; i++) { setup_help_button(help_buttons[i]); } } function setup_scrollpad() { "use strict"; if (document.getElementsByTagName('body')[0].hasAttribute("data-scrollpad") && document.getElementById("scrollpad") == null) { window.addEventListener("resize", update_scroll_pad, false); update_scroll_pad(); } } // anon function to avoid polluting global scope (function() { "use strict"; window.addEventListener("load", function load(evt) { window.removeEventListener("load", load, false); setup_help(); setup_scrollpad(); }, false); })(); /* * make_link * * Creates a text node and if a URL is specified it surrounds it with a link. * If the URL doesn't begin with "http://" it automatically adds it, as * relative links don't make much sense in this context. */ function make_link(text, url) { var textNode = null; var link = null; if (typeof text !== "undefined" && text !== null) textNode = document.createTextNode(text); if (typeof url === "string") { if (url.indexOf("//") == -1) { url = "http://" + url; } link = document.createElement('a'); link.href = url; if (textNode) link.appendChild(textNode); return link; } return textNode; } </script> <script> // // return true if any part of the passed element is visible in the viewport // function element_in_viewport(elem) { var rect; try { rect = elem.getBoundingClientRect(); } catch (e) { return false; } return ( rect.top < (window.innerHeight || document.body.clientHeight) && rect.bottom > 0 && rect.left < (window.innerWidth || document.body.clientWidth) && rect.right > 0 ); } // // Functions to delay a drawing task until it is required or it would not lag the display to do so // // a list of items still to be drawn var drawable_list = []; // the delay between drawing objects that are not currently visible var draw_delay = 1; // the delay after a user interaction var user_delay = 300; // the delay after a user has stopped scrolling and is viewing the stuff drawn on the current page var stop_delay = 300; // the timer handle; allows resetting of the timer after user interactions var draw_timer = null; // // Drawable // // elem - a page element which defines the position on the page that drawing is to be done // task - an object with the method run which takes care of painting the object // var Drawable = function(elem, task) { this.elem = elem; this.task = task; } // // Drawable.is_visible // // Determines if the element is visible in the viewport // Drawable.prototype.is_visible = function() { return element_in_viewport(this.elem); } // // Drawable.run // // Run the task held by the drawable Drawable.prototype.run = function() { if (this.task) this.task.run(); this.task = null; } // // Drawable.run // // Run the task iff visible // returns true if the task ran or has already run Drawable.prototype.run_visible = function() { if (this.task) { if (element_in_viewport(this.elem)) { this.task.run(); this.task = null; return true; } return false; } else { return true; } } // // draw_on_screen // // Checks each drawable object and draws those on screen. // function draw_on_screen() { var found = false; for (var i = 0; i < drawable_list.length; i++) { if (drawable_list[i].run_visible()) { drawable_list.splice(i--, 1); found = true; } } return found; } // // process_draw_tasks // // Called on a delay to process the next avaliable // draw task. // function process_draw_tasks() { var delay = draw_delay; draw_timer = null; if (drawable_list.length == 0) return; //no more tasks if (draw_on_screen()) { delay = stop_delay; //give the user a chance to scroll } else { //get next task var drawable = drawable_list.shift(); drawable.task.run(); } //allow UI updates between tasks draw_timer = window.setTimeout("process_draw_tasks()", delay); } // // delayed_process_draw_tasks // // Call process_draw_tasks after a short delay. // The delay serves to group multiple redundant events. // Should be set as event handler for onscroll and onresize. // function delayed_process_draw_tasks() { //reset the timer if (drawable_list.length > 0) { if (draw_timer != null) clearTimeout(draw_timer); draw_timer = window.setTimeout("process_draw_tasks()", user_delay); } } // // add_draw_task // // Add a drawing task to be called immediately if it is // visible, or to be called on a delay to reduce stuttering // effect on the web browser. function add_draw_task(elem, task) { drawable = new Drawable(elem, task); if (drawable.is_visible()) { task.run(); } else { drawable_list.push(drawable); //reset timer if (draw_timer != null) clearTimeout(draw_timer); draw_timer = window.setTimeout("process_draw_tasks()", user_delay); } } </script> <script> //====================================================================== // start Alphabet object //====================================================================== var Alphabet = function(alphabet, background) { "use strict"; var i, j, sym, aliases, complement, comp_e_sym, ambigs, generate_background; generate_background = (background == null); if (generate_background) { background = []; for (i = 0; i < alphabet.ncore; i++) background[i] = 1.0 / alphabet.ncore; } else if (alphabet.ncore != background.length) { throw new Error("The background length does not match the alphabet length."); } this.name = alphabet.name; this.like = (alphabet.like != null ? alphabet.like.toUpperCase() : null); this.ncore = alphabet.ncore; this.symbols = alphabet.symbols; this.background = background; this.genbg = generate_background; this.encode = {}; this.encode2core = {}; this.complement = {}; // check if all symbols are same case var seen_uc = false; var seen_lc = false; var check_case = function (syms) { var s, sym; if (typeof syms === "string") { for (s = 0; s < syms.length; s++) { sym = syms.charAt(s); if (sym >= 'a' && sym <= 'z') seen_lc = true; else if (sym >= 'A' && sym <= 'Z') seen_uc = true; } } }; for (i = 0; i < this.symbols.length; i++) { check_case(this.symbols[i].symbol); check_case(this.symbols[i].aliases); } // now map symbols to indexes var update_array = function(array, syms, index) { var s, sym; if (typeof syms === "string") { for (s = 0; s < syms.length; s++) { sym = syms.charAt(s); array[sym] = index; // when only a single case is used, then encode as case insensitive if (seen_uc != seen_lc) { if (sym >= 'a' && sym <= 'z') { array[sym.toUpperCase()] = index; } else if (sym >= 'A' && sym <= 'Z') { array[sym.toLowerCase()] = index; } } } } } // map core symbols to index for (i = 0; i < this.ncore; i++) { update_array(this.encode2core, this.symbols[i].symbol, i); update_array(this.encode, this.symbols[i].symbol, i); update_array(this.encode2core, this.symbols[i].aliases, i); update_array(this.encode, this.symbols[i].aliases, i); } // map ambigous symbols to index ambigs = {}; for (i = this.ncore; i < this.symbols.length; i++) { update_array(this.encode, this.symbols[i].symbol, i); update_array(this.encode, this.symbols[i].aliases, i); ambigs[this.symbols[i].equals] = i; } // determine complements for (i = 0; i < this.ncore; i++) { complement = this.symbols[i].complement; if (typeof complement === "string") { this.complement[i] = this.encode2core[complement]; } } next_symbol: for (i = this.ncore; i < this.symbols.length; i++) { complement = ""; for (j = 0; j < this.symbols[i].equals.length; j++) { comp_e_sym = this.complement[this.encode2core[this.symbols[i].equals.charAt(j)]]; if (typeof comp_e_sym !== "number") continue next_symbol; complement += this.symbols[comp_e_sym].symbol; } complement = complement.split("").sort().join(""); if (typeof ambigs[complement] === "number") { this.complement[i] = ambigs[complement]; } } // determine case insensitivity this.case_insensitive = true; if (seen_uc == seen_lc) { // when there is a mixture of cases it probably won't // be case insensitive but we still need to check loop: for (i = 0; i < this.symbols.length; i++) { sym = this.symbols[i].symbol; if (sym >= 'A' && sym <= 'Z') { if (this.encode[sym.toLowerCase()] != i) { this.case_insensitive = false; break loop; } } else if (sym >= 'a' && sym <= 'z') { if (this.encode[sym.toUpperCase()] != i) { this.case_insensitive = false; break loop; } } aliases = this.symbols[i].aliases; if (aliases != null) { for (j = 0; j < aliases.length; j++) { sym = aliases.charAt(j); if (sym >= 'A' && sym <= 'Z') { if (this.encode[sym.toLowerCase()] != i) { this.case_insensitive = false; break loop; } } else if (sym >= 'a' && sym <= 'z') { if (this.encode[sym.toUpperCase()] != i) { this.case_insensitive = false; break loop; } } } } } } // normalise aliases to remove the prime symbol and eliminate // the alternate cases when the alphabet is case insensitive var seen, out; for (i = 0; i < this.symbols.length; i++) { sym = this.symbols[i].symbol; aliases = this.symbols[i].aliases; if (typeof aliases != "string") aliases = ""; seen = {}; out = []; if (this.case_insensitive) { sym = sym.toUpperCase(); aliases = aliases.toUpperCase(); } seen[sym] = true; for (j = 0; j < aliases.length; j++) { if (!seen[aliases.charAt(j)]) { seen[aliases.charAt(j)] = true; out.push(aliases.charAt(j)); } } this.symbols[i].aliases = out.sort().join(""); } }; // return the name of the alphabet Alphabet.prototype.get_alphabet_name = function() { return this.name; }; // return if the alphabet can be complemented Alphabet.prototype.has_complement = function() { return (typeof this.symbols[0].complement === "string"); }; // return true if an uppercase letter has the same meaning as the lowercase form Alphabet.prototype.is_case_insensitive = function() { return this.case_insensitive; }; // return the information content of an alphabet letter Alphabet.prototype.get_ic = function() { return Math.log(this.ncore) / Math.LN2; }; // return the count of the core alphabet symbols Alphabet.prototype.get_size_core = function() { return this.ncore; }; // return the count of all alphabet symbols Alphabet.prototype.get_size_full = function() { return this.symbols.length; }; // return the symbol for the given alphabet index Alphabet.prototype.get_symbol = function(alph_index) { "use strict"; if (alph_index < 0 || alph_index >= this.symbols.length) { throw new Error("Alphabet index out of bounds"); } return this.symbols[alph_index].symbol; }; // return the aliases for the given alphabet index Alphabet.prototype.get_aliases = function(alph_index) { "use strict"; if (alph_index < 0 || alph_index >= this.symbols.length) { throw new Error("Alphabet index out of bounds"); } var sym_obj = this.symbols[alph_index]; return (sym_obj.aliases != null ? sym_obj.aliases : ""); }; // return the name for the given alphabet index Alphabet.prototype.get_name = function(alph_index) { "use strict"; var sym; if (alph_index < 0 || alph_index >= this.symbols.length) { throw new Error("Alphabet index out of bounds"); } sym = this.symbols[alph_index]; return (typeof sym.name === "string" ? sym.name : sym.symbol); }; // return the alphabet it is like or null Alphabet.prototype.get_like = function() { "use strict"; return this.like; }; // return the index of the complement for the given alphabet index Alphabet.prototype.get_complement = function(alph_index) { var comp_e_sym = this.complement[alph_index]; if (typeof comp_e_sym === "number") { return comp_e_sym; } else { return -1; } }; // return a string containing the core symbols Alphabet.prototype.get_symbols = function() { "use strict"; var i, core_symbols; core_symbols = ""; for (i = 0; i < this.ncore; i++) { core_symbols += this.symbols[i].symbol; } return core_symbols; }; // return if the background was not a uniform generated background Alphabet.prototype.has_bg = function() { "use strict"; return !this.genbg; }; // get the background frequency for the index Alphabet.prototype.get_bg_freq = function(alph_index) { "use strict"; var freq, i, symbols; if (alph_index >= 0) { if (alph_index < this.ncore) { return this.background[alph_index]; } else if (alph_index < this.symbols.length) { freq = 0; symbols = this.symbols[alph_index].equals; for (i = 0; i < symbols.length; i++) { freq += this.background[this.encode2core[symbols.charAt(i)]]; } return freq; } } throw new Error("The alphabet index is out of range."); }; // get the colour of the index Alphabet.prototype.get_colour = function(alph_index) { "use strict"; if (alph_index < 0 || alph_index >= this.symbols.length) { throw new Error("BAD_ALPHABET_INDEX"); } if (typeof this.symbols[alph_index].colour != "string") { return "black"; } return "#" + this.symbols[alph_index].colour; }; // get the rgb componets of the colour at the index Alphabet.prototype.get_rgb = function(alph_index) { "use strict"; if (alph_index < 0 || alph_index >= this.symbols.length) { throw new Error("BAD_ALPHABET_INDEX"); } if (typeof this.symbols[alph_index].colour != "string") { return {"red": 0, "green": 0, "blue": 0}; } var colour = this.symbols[alph_index].colour; var red = parseInt(colour.substr(0, 2), 16) / 255; var green = parseInt(colour.substr(2, 2), 16) / 255; var blue = parseInt(colour.substr(4, 2), 16) / 255; return {"red": red, "green": green, "blue": blue}; }; // convert a symbol into the index Alphabet.prototype.get_index = function(letter) { "use strict"; var alph_index; alph_index = this.encode[letter]; if (typeof alph_index === "undefined") { return -1; } return alph_index; }; // convert a symbol into the list of core indexes that it equals Alphabet.prototype.get_indexes = function(letter) { "use strict"; var alph_index, comprise_str, i, comprise_list; alph_index = this.encode[letter]; if (typeof alph_index === "undefined") { throw new Error("Unknown letter"); } comprise_str = this.symbols[alph_index].equals; comprise_list = []; if (typeof comprise_str == "string") { for (i = 0; i < comprise_str.length; i++) { comprise_list.push(this.encode2core[comprise_str.charAt(i)]); } } else { comprise_list.push(alph_index); } return comprise_list; }; // check if a symbol is the primary way of representing the symbol in the alphabet Alphabet.prototype.is_prime_symbol = function(letter) { var alph_index; alph_index = this.encode[letter]; if (alph_index == null) return false; if (this.is_case_insensitive()) { return (this.symbols[alph_index].symbol.toUpperCase() == letter.toUpperCase()); } else { return (this.symbols[alph_index].symbol == letter); } }; // compare 2 alphabets Alphabet.prototype.equals = function(other) { "use strict"; var i, sym1, sym2; // first check that it's actually an alphabet object if (!(typeof other === "object" && other != null && other instanceof Alphabet)) { return false; } // second shortcircuit if it's the same object if (this === other) return true; // compare if (this.name !== other.name) return false; if (this.ncore !== other.ncore) return false; if (this.symbols.length !== other.symbols.length) return false; for (i = 0; i < this.symbols.length; i++) { sym1 = this.symbols[i]; sym2 = other.symbols[i]; if (sym1.symbol !== sym2.symbol) return false; if (sym1.aliases !== sym2.aliases) return false; if (sym1.name !== sym2.name) return false; if (typeof sym1.colour !== typeof sym2.colour || (typeof sym1.colour === "string" && typeof sym2.colour === "string" && parseInt(sym1.colour, 16) != parseInt(sym2.colour, 16))) { return false; } if (sym1.complement !== sym2.complement) return false; if (sym1.equals !== sym2.equals) return false; } return true; }; Alphabet.prototype.check_core_subset = function(super_alph) { var complement_same = true; var seen_set = {}; var sub_i, sub_symbol, super_i, super_symbol; for (sub_i = 0; sub_i < this.ncore; sub_i++) { sub_symbol = this.symbols[sub_i]; super_i = super_alph.encode[sub_symbol.symbol]; if (super_i == null) return 0; super_symbol = super_alph.symbols[super_i]; if (seen_set[super_i]) return 0; seen_set[super_i] = true; // check complement if (sub_symbol.complement != null && super_symbol.complement != null) { if (super_alph.encode[sub_symbol.complement] != super_alph.encode[super_symbol.complement]) { complement_same = false; } } else if (sub_symbol.complement != null || super_symbol.complement != null) { complement_same = false; } } return (complement_same ? 1 : -1); }; // convert a sequence to its reverse complement Alphabet.prototype.invcomp_seq = function(seq) { "use strict"; var syms, i, e_sym, comp_e_sym; if (!this.has_complement()) throw new Error("Alphabet must be complementable"); syms = seq.split(""); for (i = 0; i < syms.length; i++) { e_sym = this.encode[syms[i]]; if (typeof e_sym === "undefined") { e_sym = this.ncore; // wildcard } comp_e_sym = this.complement[e_sym]; if (typeof comp_e_sym === "undefined") { comp_e_sym = e_sym; // not complementable } syms[i] = this.symbols[comp_e_sym].symbol; } return syms.reverse().join(""); }; // convert the alphabet to the text version Alphabet.prototype.as_text = function() { "use strict"; function name_as_text(name) { var i, c, out; out = "\""; for (i = 0; i < name.length; i++) { c = name.charAt(i); if (c == "\"") { out += "\\\""; } else if (c == "/") { out += "\\/"; } else if (c == "\\") { out += "\\\\"; } else { out += c; } } out += "\""; return out; } function symbol_as_text(sym) { var out; out = sym.symbol; if (typeof sym.name === "string" && sym.name != sym.symbol) { out += " " + name_as_text(sym.name); } if (typeof sym.colour === "string") { out += " " + sym.colour; } return out; } var out, i, j, c, sym; out = ""; // output core symbols with 2 way complements for (i = 0; i < this.ncore; i++) { c = this.complement[i]; if (typeof c === "number" && i < c && this.complement[c] === i) { out += symbol_as_text(this.symbols[i]) + " ~ " + symbol_as_text(this.symbols[c]) + "\n"; } } // output core symbols with no complement for (i = 0; i < this.ncore; i++) { if (typeof this.complement[i] === "undefined") { out += symbol_as_text(this.symbols[i]) + "\n"; } } // output ambiguous symbols that have comprising characters for (i = this.ncore; i < this.symbols.length; i++) { if (this.symbols[i].equals.length == 0) break; out += symbol_as_text(this.symbols[i]) + " = " + this.symbols[i].equals + "\n"; if (typeof this.symbols[i].aliases === "string") { for (j = 0; j < this.symbols[i].aliases.length; j++) { if (this.symbols[i].aliases.charAt(j) == this.symbols[i].symbol) continue; out += this.symbols[i].aliases.charAt(j) + " = " + this.symbols[i].equals + "\n"; } } } // output aliases of core symbols for (i = 0; i < this.ncore; i++) { if (typeof this.symbols[i].aliases === "string") { for (j = 0; j < this.symbols[i].aliases.length; j++) { if (this.symbols[i].aliases.charAt(j) == this.symbols[i].symbol) continue; out += this.symbols[i].aliases.charAt(j) + " = " + this.symbols[i].symbol + "\n"; } } } // output gap symbols i = this.symbols.length - 1; if (this.symbols[i].equals.length == 0) { out += symbol_as_text(this.symbols[i]) + " =\n"; if (typeof this.symbols[i].aliases === "string") { for (j = 0; j < this.symbols[i].aliases.length; j++) { if (this.symbols[i].aliases.charAt(j) == this.symbols[i].symbol) continue; out += this.symbols[i].aliases.charAt(j) + " =\n"; } } } return out; }; // output the alphabet as it appears in minimal MEME format Alphabet.prototype.as_meme = function() { "use strict"; function name_as_text(name) { var i, c, out; out = "\""; for (i = 0; i < name.length; i++) { c = name.charAt(i); if (c == "\"") { out += "\\\""; } else if (c == "/") { out += "\\/"; } else if (c == "\\") { out += "\\\\"; } else { out += c; } } out += "\""; return out; } if (this.equals(AlphStd.DNA)) { return "ALPHABET= ACGT\n"; } else if (this.equals(AlphStd.PROTEIN)) { return "ALPHABET= ACDEFGHIKLMNPQRSTVWY\n"; } else { return "ALPHABET" + (this.name != null ? " " + name_as_text(this.name) : "") + (this.like != null ? " " + this.like + "-LIKE" : "") + "\n" + this.as_text() + "END ALPHABET\n"; } }; // Returns a table showing all the letters in the alphabet Alphabet.prototype.as_table = function() { "use strict"; var i, j, row, th, td, aliases, equals, sym; var table = document.createElement("table"); // create the core symbol header row = table.insertRow(table.rows.length); th = document.createElement("th"); th.appendChild(document.createTextNode("Symbol(s)")); row.appendChild(th); th = document.createElement("th"); th.appendChild(document.createTextNode("Name")); row.appendChild(th); th = document.createElement("th"); if (this.has_complement()) { th.appendChild(document.createTextNode("Complement")); } row.appendChild(th); // list the core symbols for (i = 0; i < this.ncore; i++) { row = table.insertRow(table.rows.length); td = document.createElement("td"); if (this.symbols[i].colour != null) { td.style.color = '#' + this.symbols[i].colour; } td.appendChild(document.createTextNode(this.symbols[i].symbol)); aliases = this.get_aliases(i); if (aliases.length > 0) { td.appendChild(document.createTextNode(' ' + aliases.split('').join(' '))); } row.appendChild(td); td = document.createElement("td"); if (this.symbols[i].name != null) { td.appendChild(document.createTextNode(this.symbols[i].name)); } row.appendChild(td); td = document.createElement("td"); if (this.symbols[i].complement != null) { td.style.color = this.get_colour(this.get_index(this.symbols[i].complement)); td.appendChild(document.createTextNode(this.symbols[i].complement)); } row.appendChild(td); } // create the ambiguous symbol header row = table.insertRow(table.rows.length); th = document.createElement("th"); th.appendChild(document.createTextNode("Symbol(s)")); row.appendChild(th); th = document.createElement("th"); th.appendChild(document.createTextNode("Name")); row.appendChild(th); th = document.createElement("th"); th.appendChild(document.createTextNode("Matches")); row.appendChild(th); // list the ambiguous symbols for (i = this.ncore; i < this.symbols.length; i++) { row = table.insertRow(table.rows.length); td = document.createElement("td"); if (this.symbols[i].colour != null) { td.style.color = '#' + this.symbols[i].colour; } td.appendChild(document.createTextNode(this.symbols[i].symbol)); aliases = this.get_aliases(i); if (aliases.length > 0) { td.appendChild(document.createTextNode(' ' + aliases.split('').join(' '))); } row.appendChild(td); td = document.createElement("td"); if (this.symbols[i].name != null) { td.appendChild(document.createTextNode(this.symbols[i].name)); } row.appendChild(td); td = document.createElement("td"); equals = this.symbols[i].equals.split(''); for (j = 0; j < equals.length; j++) { if (j != 0) td.appendChild(document.createTextNode(' ')); sym = document.createElement("span"); sym.style.color = this.get_colour(this.get_index(equals[j])); sym.appendChild(document.createTextNode(equals[j])); td.appendChild(sym); } row.appendChild(td); } return table; }; // returns a dictionary of the colours for EPS Alphabet.prototype._as_eps_dict = function() { "use strict"; var i, sym, rgb; var out = "/fullColourDict <<\n"; for (i = 0; i < this.ncore; i++) { sym = this.get_symbol(i); sym = sym.replace(/\\/g, "\\\\"); sym = sym.replace(/\(/g, "\\("); sym = sym.replace(/\)/g, "\\)"); rgb = this.get_rgb(i); out += " (" + sym + ") [" + rgb.red.toFixed(4) + " " + rgb.green.toFixed(4) + " " + rgb.blue.toFixed(4) + "]\n"; } out += ">> def\n"; out += "/mutedColourDict <<\n"; for (i = 0; i < this.ncore; i++) { sym = this.get_symbol(i); sym = sym.replace(/\\/g, "\\\\"); sym = sym.replace(/\(/g, "\\("); sym = sym.replace(/\)/g, "\\)"); rgb = Alphabet.lighten_colour(this.get_rgb(i)); out += " (" + sym + ") [" + rgb.red.toFixed(4) + " " + rgb.green.toFixed(4) + " " + rgb.blue.toFixed(4) + "]\n"; } out += ">> def\n"; return out; }; // return the alphabet name or a list of primary symbols Alphabet.prototype.toString = function() { "use strict"; if (this.name != null) { return this.name; } else { return this.get_symbols(); } }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Helper functions //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Convert a colour specified in RGB colourspace values into LAB colourspace Alphabet.rgb2lab = function(rgb) { "use strict"; var xyzHelper, labHelper; // XYZ helper xyzHelper = function(value) { if (value > 0.0445) { value = (value + 0.055) / 1.055; value = Math.pow(value, 2.4); } else { value /= 12.92; } value *= 100; return value; }; // lab helper labHelper = function(value) { if (value > 0.008856) { value = Math.pow(value, 1.0 / 3.0); } else { value = (7.787 * value) + (16.0 / 116.0); } return value; }; // convert into XYZ colourspace var c1, c2, c3; if (typeof rgb == "number") { c1 = xyzHelper(((rgb >> 16) & 0xFF) / 255.0); c2 = xyzHelper(((rgb >> 8) & 0xFF) / 255.0); c3 = xyzHelper((rgb & 0xFF) / 255.0); } else { c1 = xyzHelper(rgb.red); c2 = xyzHelper(rgb.green); c3 = xyzHelper(rgb.blue); } var x = (c1 * 0.4124) + (c2 * 0.3576) + (c3 * 0.1805); var y = (c1 * 0.2126) + (c2 * 0.7152) + (c3 * 0.0722); var z = (c1 * 0.0193) + (c2 * 0.1192) + (c3 * 0.9505); // convert into Lab colourspace c1 = labHelper(x / 95.047); c2 = labHelper(y / 100.0); c3 = labHelper(z / 108.883); var l = (116.0 * c2) - 16; var a = 500.0 * (c1 - c2); var b = 200.0 * (c2 - c3); return {"l": l, "a": a, "b": b}; }; // Convert a colour specified in HSV colourspace into RGB colourspace Alphabet.hsv2rgb = function(hue, sat, value, output_object) { // achromatic (grey) var r = value; var g = value; var b = value; if (sat != 0) { var h = hue / 60.0; var i = Math.floor(h); var f = h - i; var p = value * (1.0 - sat); var q = value * (1.0 - (sat * f)); var t = value * (1.0 - (sat * (1.0 - f))); if (i == 0) { r = value; g = t; b = p; } else if (i == 1) { r = q; g = value; b = p; } else if (i == 2) { r = p; g = value; b = t; } else if (i == 3) { r = p; g = q; b = value; } else if (i == 4) { r = t; g = p; b = value; } else { r = value; g = p; b = q; } } if (output_object) { return {"red": r, "green": g, "blue": b}; } else { return (Math.floor(r * 255) << 15) | (Math.floor(g * 255) << 8) | (Math.floor(b * 255)); } }; // Calculate a distance score between two colours in LAB colourspace Alphabet.lab_dist = function(lab1, lab2) { var c1 = Math.sqrt((lab1.l * lab1.l) + (lab1.a * lab1.a)); var c2 = Math.sqrt((lab2.l * lab2.l) + (lab2.a * lab2.a)); var dc = c1 - c2; var dl = lab1.l - lab2.l; var da = lab1.a - lab2.a; var db = lab1.b - lab2.b; // we don't want NaN due to rounding errors so fudge things a bit... var dh = 0; var dh_squared = (da * da) + (db * db) - (dc * dc); if (dh_squared > 0) { dh = Math.sqrt(dh_squared); } var first = dl; var second = dc / (1.0 + (0.045 * c1)); var third = dh / (1.0 + (0.015 * c1)); return Math.sqrt((first * first) + (second * second) + (third * third)); }; // convert an RGB value into a HSL value Alphabet.rgb2hsl = function(rgb) { "use strict"; var min, max, delta, h, s, l, r, g, b; if (typeof rgb == "number") { r = ((rgb >> 16) & 0xFF) / 255.0; g = ((rgb >> 8) & 0xFF) / 255.0; b = (rgb & 0xFF) / 255.0; } else { r = rgb.red; g = rgb.green; b = rgb.blue; } min = Math.min(r, g, b); max = Math.max(r, g, b); delta = max - min; l = min + (delta / 2); if (max == min) { h = 0; // achromatic (grayscale) s = 0; } else { if (l > 0.5) { s = delta / (2 - max - min); } else { s = delta / (max + min); } if (max == r) { h = (g - b) / delta; if (g < b) h += 6; } else if (max == g) { h = ((b - r) / delta) + 2; } else { h = ((r - g) / delta) + 4; } h /= 6; } return {"h": h, "s": s, "l": l}; }; // convert a HSL value into an RGB value Alphabet.hsl2rgb = function(hsl, output_object) { "use strict"; function _hue(p, q, t) { "use strict"; if (t < 0) t += 1; else if (t > 1) t -= 1; if (t < (1.0 / 6.0)) { return p + ((q - p) * 6.0 * t); } else if (t < 0.5) { return q; } else if (t < (2.0 / 3.0)) { return p + ((q - p) * ((2.0 / 3.0) - t) * 6.0); } else { return p; } } var r, g, b, p, q; if (hsl.s == 0) { // achromatic (grayscale) r = hsl.l; g = hsl.l; b = hsl.l; } else { if (hsl.l < 0.5) { q = hsl.l * (1 + hsl.s); } else { q = hsl.l + hsl.s - (hsl.l * hsl.s); } p = (2 * hsl.l) - q; r = _hue(p, q, hsl.h + (1.0 / 3.0)); g = _hue(p, q, hsl.h); b = _hue(p, q, hsl.h - (1.0 / 3.0)); } if (output_object) { return {"red": r, "green": g, "blue": b}; } else { return (Math.floor(r * 255) << 15) | (Math.floor(g * 255) << 8) | (Math.floor(b * 255)); } }; Alphabet.lighten_colour = function(rgb) { "use strict"; var hsl = Alphabet.rgb2hsl(rgb); hsl.l += (1.0 - hsl.l) * 2 / 3; return Alphabet.hsl2rgb(hsl, typeof rgb != "number"); }; //====================================================================== // end Alphabet object //====================================================================== //====================================================================== // start StandardAlphabet object //====================================================================== // an extension of the alphabet object to support some additional fields // only present in standard alphabets. var StandardAlphabet = function(enum_code, enum_name, alphabet_data) { Alphabet.apply(this, [alphabet_data]); this.enum_code = enum_code; this.enum_name = enum_name; }; StandardAlphabet.prototype = Alphabet.prototype; StandardAlphabet.prototype.constructor = StandardAlphabet; // A unique code for this standard alphabet. // This code will be a power of 2 to enable creation of bitsets for // a selection of standard alphabets. StandardAlphabet.prototype.get_code = function() { return this.enum_code; }; // A unique name for this standard alphabet. // this name will be all upper case and the same as the property that // refers to this alphabet in the AlphStd collection. StandardAlphabet.prototype.get_enum = function() { return this.enum_name; }; //====================================================================== // end StandardAlphabet object //====================================================================== // A collection of standard alphabets. var AlphStd = { RNA: new StandardAlphabet(1, "RNA", { "name": "RNA", "like": "RNA", "ncore": 4, "symbols": [ {"symbol": "A", "name": "Adenine", "colour": "CC0000"}, {"symbol": "C", "name": "Cytosine", "colour": "0000CC"}, {"symbol": "G", "name": "Guanine", "colour": "FFB300"}, {"symbol": "U", "name": "Uracil", "colour": "008000", "aliases": "T"}, {"symbol": "N", "name": "Any base", "equals": "ACGU", "aliases": "X."}, {"symbol": "V", "name": "Not U", "equals": "ACG"}, {"symbol": "H", "name": "Not G", "equals": "ACU"}, {"symbol": "D", "name": "Not C", "equals": "AGU"}, {"symbol": "B", "name": "Not A", "equals": "CGU"}, {"symbol": "M", "name": "Amino", "equals": "AC"}, {"symbol": "R", "name": "Purine", "equals": "AG"}, {"symbol": "W", "name": "Weak", "equals": "AU"}, {"symbol": "S", "name": "Strong", "equals": "CG"}, {"symbol": "Y", "name": "Pyrimidine", "equals": "CU"}, {"symbol": "K", "name": "Keto", "equals": "GU"} ] }), DNA: new StandardAlphabet(2, "DNA", { "name": "DNA", "like": "DNA", "ncore": 4, "symbols": [ {"symbol": "A", "name": "Adenine", "colour": "CC0000", "complement": "T"}, {"symbol": "C", "name": "Cytosine", "colour": "0000CC", "complement": "G"}, {"symbol": "G", "name": "Guanine", "colour": "FFB300", "complement": "C"}, {"symbol": "T", "name": "Thymine", "colour": "008000", "complement": "A", "aliases": "U"}, {"symbol": "N", "name": "Any base", "equals": "ACGT", "aliases": "X."}, {"symbol": "V", "name": "Not T", "equals": "ACG"}, {"symbol": "H", "name": "Not G", "equals": "ACT"}, {"symbol": "D", "name": "Not C", "equals": "AGT"}, {"symbol": "B", "name": "Not A", "equals": "CGT"}, {"symbol": "M", "name": "Amino", "equals": "AC"}, {"symbol": "R", "name": "Purine", "equals": "AG"}, {"symbol": "W", "name": "Weak", "equals": "AT"}, {"symbol": "S", "name": "Strong", "equals": "CG"}, {"symbol": "Y", "name": "Pyrimidine", "equals": "CT"}, {"symbol": "K", "name": "Keto", "equals": "GT"} ] }), PROTEIN: new StandardAlphabet(4, "PROTEIN", { "name": "Protein", "like": "PROTEIN", "ncore": 20, "symbols": [ {"symbol": "A", "name": "Alanine", "colour": "0000CC"}, {"symbol": "C", "name": "Cysteine", "colour": "0000CC"}, {"symbol": "D", "name": "Aspartic acid", "colour": "FF00FF"}, {"symbol": "E", "name": "Glutamic acid", "colour": "FF00FF"}, {"symbol": "F", "name": "Phenylalanine", "colour": "0000CC"}, {"symbol": "G", "name": "Glycine", "colour": "FFB300"}, {"symbol": "H", "name": "Histidine", "colour": "FFCCCC"}, {"symbol": "I", "name": "Isoleucine", "colour": "0000CC"}, {"symbol": "K", "name": "Lysine", "colour": "CC0000"}, {"symbol": "L", "name": "Leucine", "colour": "0000CC"}, {"symbol": "M", "name": "Methionine", "colour": "0000CC"}, {"symbol": "N", "name": "Asparagine", "colour": "008000"}, {"symbol": "P", "name": "Proline", "colour": "FFFF00"}, {"symbol": "Q", "name": "Glutamine", "colour": "008000"}, {"symbol": "R", "name": "Arginine", "colour": "CC0000"}, {"symbol": "S", "name": "Serine", "colour": "008000"}, {"symbol": "T", "name": "Threonine", "colour": "008000"}, {"symbol": "V", "name": "Valine", "colour": "0000CC"}, {"symbol": "W", "name": "Tryptophan", "colour": "0000CC"}, {"symbol": "Y", "name": "Tyrosine", "colour": "33E6CC"}, {"symbol": "X", "name": "Any amino acid", "equals": "ACDEFGHIKLMNPQRSTVWY", "aliases": "*."}, {"symbol": "B", "name": "Asparagine or Aspartic acid", "equals": "DN"}, {"symbol": "Z", "name": "Glutamine or Glutamic acid", "equals": "EQ"}, {"symbol": "J", "name": "Leucine or Isoleucine", "equals": "IL"} ] }) }; //====================================================================== // start Symbol object //====================================================================== var Symbol = function(alph_index, scale, alphabet) { "use strict"; //variable prototype this.symbol = alphabet.get_symbol(alph_index); this.scale = scale; this.colour = alphabet.get_colour(alph_index); }; Symbol.prototype.get_symbol = function() { "use strict"; return this.symbol; }; Symbol.prototype.get_scale = function() { "use strict"; return this.scale; }; Symbol.prototype.get_colour = function() { "use strict"; return this.colour; }; Symbol.prototype.toString = function() { "use strict"; return this.symbol + " " + (Math.round(this.scale*1000)/10) + "%"; }; function compare_symbol(sym1, sym2) { "use strict"; if (sym1.get_scale() < sym2.get_scale()) { return -1; } else if (sym1.get_scale() > sym2.get_scale()) { return 1; } else { return 0; } } //====================================================================== // end Symbol object //====================================================================== //====================================================================== // start Pspm object //====================================================================== var Pspm = function(matrix, name, ltrim, rtrim, nsites, evalue, pssm, alt) { "use strict"; var row, col, data, row_sum, delta, evalue_re; if (typeof name !== "string") { name = ""; } this.name = name; //construct if (matrix instanceof Pspm) { // copy constructor this.alph_length = matrix.alph_length; this.motif_length = matrix.motif_length; this.name = matrix.name; this.alt = matrix.alt; this.nsites = matrix.nsites; this.evalue = matrix.evalue; this.ltrim = matrix.ltrim; this.rtrim = matrix.rtrim; this.pspm = []; for (row = 0; row < matrix.motif_length; row++) { this.pspm[row] = []; for (col = 0; col < matrix.alph_length; col++) { this.pspm[row][col] = matrix.pspm[row][col]; } } if (matrix.pssm != null) { this.pssm = []; for (row = 0; row < matrix.motif_length; row++) { this.pspm[row] = []; for (col = 0; col < matrix.alph_length; col++) { this.pssm[row][col] = matrix.pssm[row][col]; } } } } else { // check parameters if (ltrim == null) { ltrim = 0; } else if (typeof ltrim !== "number" || ltrim % 1 !== 0 || ltrim < 0) { throw new Error("ltrim must be a non-negative integer, got: " + ltrim); } if (rtrim == null) { rtrim = 0; } else if (typeof rtrim !== "number" || rtrim % 1 !== 0 || rtrim < 0) { throw new Error("rtrim must be a non-negative integer, got: " + rtrim); } if (nsites != null) { if (typeof nsites !== "number" || nsites < 0) { throw new Error("nsites must be a positive number, got: " + nsites); } else if (nsites == 0) { nsites = null; } } if (evalue != null) { if (typeof evalue === "number") { if (evalue < 0) { throw new Error("evalue must be a non-negative number, got: " + evalue); } } else if (typeof evalue === "string") { evalue_re = /^((?:[+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)|inf)$/; if (!evalue_re.test(evalue)) { throw new Error("evalue must be a non-negative number, got: " + evalue); } } else { throw new Error("evalue must be a non-negative number, got: " + evalue); } } // set properties this.name = name; this.alt = alt; this.nsites = nsites; this.evalue = evalue; this.ltrim = ltrim; this.rtrim = rtrim; if (typeof matrix === "string") { // string constructor data = parse_pspm_string(matrix); this.alph_length = data["alph_length"]; this.motif_length = data["motif_length"]; this.pspm = data["pspm"]; if (this.evalue == null) { if (data["evalue"] != null) { this.evalue = data["evalue"]; } else { this.evalue = 0; } } if (this.nsites == null) { if (typeof data["nsites"] === "number") { this.nsites = data["nsites"]; } else { this.nsites = 20; } } } else { // assume pspm is a nested array this.motif_length = matrix.length; this.alph_length = (matrix.length > 0 ? matrix[0].length : 0); if (this.nsites == null) { this.nsites = 20; } if (this.evalue == null) { this.evalue = 0; } this.pspm = []; // copy pspm and check for (row = 0; row < this.motif_length; row++) { if (this.alph_length != matrix[row].length) { throw new Error("COLUMN_MISMATCH"); } this.pspm[row] = []; row_sum = 0; for (col = 0; col < this.alph_length; col++) { this.pspm[row][col] = matrix[row][col]; row_sum += this.pspm[row][col]; } delta = 0.1; if (isNaN(row_sum) || (row_sum > 1 && (row_sum - 1) > delta) || (row_sum < 1 && (1 - row_sum) > delta)) { throw new Error("INVALID_SUM"); } } // copy pssm if (pssm != null) { this.pssm = []; for (row = 0; row < this.motif_length; row++) { this.pssm[row] = []; for (col = 0; col < this.alph_length; col++) { this.pssm[row][col] = pssm[row][col]; } } } } } }; Pspm.prototype.copy = function() { "use strict"; return new Pspm(this); }; Pspm.prototype.reverse = function() { "use strict"; var x, y, temp, temp_trim; //reverse x = 0; y = this.motif_length-1; while (x < y) { temp = this.pspm[x]; this.pspm[x] = this.pspm[y]; this.pspm[y] = temp; x++; y--; } // reverse pssm (if defined) if (typeof this.pssm !== "undefined") { //reverse x = 0; y = this.motif_length-1; while (x < y) { temp = this.pssm[x]; this.pspm[x] = this.pssm[y]; this.pssm[y] = temp; x++; y--; } } //swap triming temp_trim = this.ltrim; this.ltrim = this.rtrim; this.rtrim = temp_trim; return this; //allow function chaining... }; Pspm.prototype.reverse_complement = function(alphabet) { "use strict"; var x, y, temp, i, row, c, temp_trim; if (this.alph_length != alphabet.get_size_core()) { throw new Error("The alphabet size does not match the size of the pspm."); } if (!alphabet.has_complement()) { throw new Error("The specified alphabet can not be complemented."); } // reverse motif this.reverse(); //complement for (x = 0; x < this.motif_length; x++) { row = this.pspm[x]; for (i = 0; i < row.length; i++) { c = alphabet.get_complement(i); if (c < i) continue; temp = row[i]; row[i] = row[c]; row[c] = temp; } } // complement pssm (if defined) if (typeof this.pssm !== "undefined") { //complement for (x = 0; x < this.motif_length; x++) { row = this.pssm[x]; for (i = 0; i < row.length; i++) { c = alphabet.get_complement(i); if (c < i) continue; temp = row[i]; row[i] = row[c]; row[c] = temp; } } } return this; //allow function chaining... }; Pspm.prototype.get_stack = function(position, alphabet, ssc) { "use strict"; var row, stack_ic, alphabet_ic, stack, i, sym; if (this.alph_length != alphabet.get_size_core()) { throw new Error("The alphabet size does not match the size of the pspm."); } row = this.pspm[position]; stack_ic = this.get_stack_ic(position, alphabet); if (ssc) stack_ic -= this.get_error(alphabet); alphabet_ic = alphabet.get_ic(); stack = []; for (i = 0; i < this.alph_length; i++) { sym = new Symbol(i, row[i]*stack_ic/alphabet_ic, alphabet); if (sym.get_scale() <= 0) { continue; } stack.push(sym); } stack.sort(compare_symbol); return stack; }; Pspm.prototype.get_stack_ic = function(position, alphabet) { "use strict"; var row, H, i; if (this.alph_length != alphabet.get_size_core()) { throw new Error("The alphabet size does not match the size fo the pspm."); } row = this.pspm[position]; H = 0; for (i = 0; i < this.alph_length; i++) { if (row[i] === 0) { continue; } H -= (row[i] * (Math.log(row[i]) / Math.LN2)); } return alphabet.get_ic() - H; }; Pspm.prototype.get_error = function(alphabet) { "use strict"; if (this.nsites === 0) { return 0; } return (alphabet.get_size_core()-1) / (2 * Math.LN2 * this.nsites); }; Pspm.prototype.get_motif_length = function() { "use strict"; return this.motif_length; }; Pspm.prototype.get_alph_length = function() { "use strict"; return this.alph_length; }; Pspm.prototype.get_left_trim = function() { "use strict"; return this.ltrim; }; Pspm.prototype.get_right_trim = function() { "use strict"; return this.rtrim; }; Pspm.prototype.as_best_match = function(alphabet) { "use strict"; var match, odds, best_odds, best_index; var i, j; match = ""; for (i = 0; i < this.motif_length; i++) { best_index = 0; best_odds = this.pspm[i][0] / alphabet.get_bg_freq(0); for (j = 1; j < this.alph_length; j++) { odds = this.pspm[i][j] / alphabet.get_bg_freq(j); if (odds > best_odds) { best_odds = odds; best_index = j; } } match += alphabet.get_symbol(best_index); } return match; }; Pspm.prototype.as_count_matrix = function() { "use strict"; var count, count_text, text; var i, j; text = ""; for (i = 0; i < this.motif_length; i++) { if (i !== 0) { text += "\n"; } for (j = 0; j < this.alph_length; j++) { if (j !== 0) { text += " "; } count = Math.round(this.nsites * this.pspm[i][j]); count_text = "" + count; // pad up to length of 4 if (count_text.length < 4) { text += (new Array(5 - count_text.length)).join(" ") + count_text; } else { text += count_text; } } } return text; }; Pspm.prototype.as_probability_matrix = function() { "use strict"; var text; var i, j; text = ""; for (i = 0; i < this.motif_length; i++) { if (i !== 0) { text += "\n"; } for (j = 0; j < this.alph_length; j++) { if (j !== 0) { text += " "; } text += this.pspm[i][j].toFixed(6); } } return text; }; Pspm.prototype.as_score_matrix = function(alphabet, pseudo) { "use strict"; var me, score, out, row, col, score_text; me = this; if (typeof this.pssm === "undefined") { if (!(typeof alphabet === "object" && alphabet != null && alphabet instanceof Alphabet)) { throw new Error("The alphabet is required to generate the pssm."); } if (typeof pseudo === "undefined") { pseudo = 0.01; } else if (typeof pseudo !== "number" || pseudo < 0) { throw new Error("Expected positive number for pseudocount"); } score = function(row, col) { "use strict"; var p, bg, p2; p = me.pspm[row][col]; bg = alphabet.get_bg_freq(col); p2 = (p * me.nsites + bg * pseudo) / (me.nsites + pseudo); return (p2 > 0 ? Math.round((Math.log(p2 / bg) / Math.LN2) * 100) : -10000); }; } else { score = function(row, col) { "use strict"; return me.pssm[row][col]; }; } out = ""; for (row = 0; row < this.motif_length; row++) { for (col = 0; col < this.alph_length; col++) { if (col !== 0) { out += " "; } score_text = "" + score(row, col); // pad out to 6 characters if (score_text.length < 6) { out += (new Array(7 - score_text.length)).join(" ") + score_text; } else { out += score_text; } } out += "\n"; } return out; } Pspm.prototype.as_pspm = function() { "use strict"; return "letter-probability matrix: alength= " + this.alph_length + " w= " + this.motif_length + " nsites= " + this.nsites + " E= " + (typeof this.evalue === "number" ? this.evalue.toExponential() : this.evalue) + "\n" + this.as_probability_matrix(); }; Pspm.prototype.as_pssm = function(alphabet, pseudo) { "use strict"; return "log-odds matrix: alength= " + this.alph_length + " w= " + this.motif_length + " E= " + (typeof this.evalue == "number" ? this.evalue.toExponential() : this.evalue) + "\n" + this.as_score_matrix(alphabet, pseudo); }; Pspm.prototype.as_meme = function(options) { var with_header, with_pspm, with_pssm, version, alphabet, bg_source, pseudocount, strands; var out, alen, i; // get the options if (typeof options !== "object" || options === null) { options = {}; } with_header = (typeof options["with_header"] === "boolean" ? options["with_header"] : false); with_pspm = (typeof options["with_pspm"] === "boolean" ? options["with_pspm"] : false); with_pssm = (typeof options["with_pssm"] === "boolean" ? options["with_pssm"] : false); if (!with_pspm && !with_pssm) with_pspm = true; if (with_header) { if (typeof options["version"] === "string" && /^\d+(?:\.\d+){0,2}$/.test(options["version"])) { version = options["version"]; } else if (typeof options["version"] === "number") { version = options["version"].toFixed(0); } else { version = "4"; } if (typeof options["strands"] === "number" && options["strands"] === 1) { strands = 1; } else { strands = 2; } if (typeof options["bg_source"] === "string") { bg_source = options["bg_source"]; } else { bg_source = "unknown source"; } if (typeof options["alphabet"] === "object" && options["alphabet"] != null && options["alphabet"] instanceof Alphabet) { alphabet = options["alphabet"]; } else { throw new Error("The alphabet is required to generate the header."); } } // now create the output out = ""; if (with_header) { out = "MEME version " + version + "\n\n"; out += alphabet.as_meme() + "\n"; if (alphabet.has_complement()) { // assume DNA has both strands unless otherwise specified out += "strands: " + (strands === 1 ? "+" : "+ -") + "\n\n"; } out += "Background letter frequencies (from " + bg_source + "):\n"; alen = alphabet.get_size_core(); for (i = 0; i < alen; i++) { if (i !== 0) { if (i % 9 === 0) { // maximum of nine entries per line out += "\n"; } else { out += " "; } } out += alphabet.get_symbol(i) + " " + alphabet.get_bg_freq(i).toFixed(3); } } out += "\n\n"; out += "MOTIF " + this.name + (this.alt == null ? "" : " " + this.alt); if (with_pssm) { out += "\n\n"; out += this.as_pssm(options["alphabet"], options["pseudocount"]); } if (with_pspm) { out += "\n\n"; out += this.as_pspm(); } return out; } Pspm.prototype.toString = function() { "use strict"; var str, i, row; str = ""; for (i = 0; i < this.pspm.length; i++) { row = this.pspm[i]; str += row.join("\t") + "\n"; } return str; }; function parse_pspm_properties(str) { "use strict"; var parts, i, eqpos, before, after, properties, prop, num, num_re; num_re = /^((?:[+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)|inf)$/; parts = trim(str).split(/\s+/); // split up words containing = for (i = 0; i < parts.length;) { eqpos = parts[i].indexOf("="); if (eqpos != -1) { before = parts[i].substr(0, eqpos); after = parts[i].substr(eqpos+1); if (before.length > 0 && after.length > 0) { parts.splice(i, 1, before, "=", after); i += 3; } else if (before.length > 0) { parts.splice(i, 1, before, "="); i += 2; } else if (after.length > 0) { parts.splice(i, 1, "=", after); i += 2; } else { parts.splice(i, 1, "="); i++; } } else { i++; } } properties = {}; for (i = 0; i < parts.length; i += 3) { if (parts.length - i < 3) { throw new Error("Expected PSPM property was incomplete. "+ "Remaing parts are: " + parts.slice(i).join(" ")); } if (parts[i+1] !== "=") { throw new Error("Expected '=' in PSPM property between key and " + "value but got " + parts[i+1]); } prop = parts[i].toLowerCase(); num = parts[i+2]; if (!num_re.test(num)) { throw new Error("Expected numeric value for PSPM property '" + prop + "' but got '" + num + "'"); } properties[prop] = num; } return properties; } function parse_pspm_string(pspm_string) { "use strict"; var header_re, lines, first_line, line_num, col_num, alph_length, motif_length, nsites, evalue, pspm, i, line, match, props, parts, j, prob; header_re = /^letter-probability\s+matrix:(.*)$/i; lines = pspm_string.split(/\n/); first_line = true; line_num = 0; col_num = 0; alph_length; motif_length; nsites; evalue; pspm = []; for (i = 0; i < lines.length; i++) { line = trim(lines[i]); if (line.length === 0) { continue; } // check the first line for a header though allow matrices without it if (first_line) { first_line = false; match = header_re.exec(line); if (match !== null) { props = parse_pspm_properties(match[1]); if (props.hasOwnProperty("alength")) { alph_length = parseFloat(props["alength"]); if (alph_length != 4 && alph_length != 20) { throw new Error("PSPM property alength should be 4 or 20" + " but got " + alph_length); } } if (props.hasOwnProperty("w")) { motif_length = parseFloat(props["w"]); if (motif_length % 1 !== 0 || motif_length < 1) { throw new Error("PSPM property w should be an integer larger " + "than zero but got " + motif_length); } } if (props.hasOwnProperty("nsites")) { nsites = parseFloat(props["nsites"]); if (nsites <= 0) { throw new Error("PSPM property nsites should be larger than " + "zero but got " + nsites); } } if (props.hasOwnProperty("e")) { evalue = props["e"]; if (evalue < 0) { throw new Error("PSPM property evalue should be " + "non-negative but got " + evalue); } } continue; } } pspm[line_num] = []; col_num = 0; parts = line.split(/\s+/); for (j = 0; j < parts.length; j++) { prob = parseFloat(parts[j]); if (prob != parts[j] || prob < 0 || prob > 1) { throw new Error("Expected probability but got '" + parts[j] + "'"); } pspm[line_num][col_num] = prob; col_num++; } line_num++; } if (typeof motif_length === "number") { if (pspm.length != motif_length) { throw new Error("Expected PSPM to have a motif length of " + motif_length + " but it was actually " + pspm.length); } } else { motif_length = pspm.length; } if (typeof alph_length !== "number") { alph_length = pspm[0].length; if (alph_length != 4 && alph_length != 20) { throw new Error("Expected length of first row in the PSPM to be " + "either 4 or 20 but got " + alph_length); } } for (i = 0; i < pspm.length; i++) { if (pspm[i].length != alph_length) { throw new Error("Expected PSPM row " + i + " to have a length of " + alph_length + " but the length was " + pspm[i].length); } } return {"pspm": pspm, "motif_length": motif_length, "alph_length": alph_length, "nsites": nsites, "evalue": evalue}; } //====================================================================== // end Pspm object //====================================================================== //====================================================================== // start Logo object //====================================================================== var Logo = function(alphabet, options) { "use strict"; this.alphabet = alphabet; this.fine_text = ""; this.x_axis = 1; this.y_axis = true; this.xlate_nsyms = 1; this.xlate_start = null; this.xlate_end = null; this.pspm_list = []; this.pspm_column = []; this.rows = 0; this.columns = 0; if (typeof options === "string") { // the old method signature had fine_text here so we support that this.fine_text = options; } else if (typeof options === "object" && options != null) { this.fine_text = (typeof options.fine_text === "string" ? options.fine_text : ""); this.x_axis = (typeof options.x_axis === "boolean" ? (options.x_axis ? 1 : 0) : 1); if (options.x_axis_hidden != null && options.x_axis_hidden) this.x_axis = -1; this.y_axis = (typeof options.y_axis === "boolean" ? options.y_axis : true); this.xlate_nsyms = (typeof options.xlate_nsyms === "number" ? options.xlate_nsyms : this.xlate_nsyms); this.xlate_start = (typeof options.xlate_start === "number" ? options.xlate_start : this.xlate_start); this.xlate_end = (typeof options.xlate_end === "number" ? options.xlate_end : this.xlate_end); } }; Logo.prototype.add_pspm = function(pspm, column) { "use strict"; var col; if (typeof column === "undefined") { column = 0; } else if (column < 0) { throw new Error("Column index out of bounds."); } this.pspm_list[this.rows] = pspm; this.pspm_column[this.rows] = column; this.rows++; col = column + pspm.get_motif_length(); if (col > this.columns) { this.columns = col; } }; Logo.prototype.get_columns = function() { "use strict"; return this.columns; }; Logo.prototype.get_xlate_nsyms = function() { "use strict"; return this.xlate_nsyms; }; Logo.prototype.get_xlate_start = function() { "use strict"; return (this.xlate_start != null ? this.xlate_start : 0); }; Logo.prototype.get_xlate_end = function() { "use strict"; return (this.xlate_end != null ? this.xlate_end : this.columns * this.xlate_nsyms); }; Logo.prototype.get_xlate_columns = function() { "use strict"; return this.get_xlate_end() - this.get_xlate_start(); }; Logo.prototype.get_rows = function() { "use strict"; return this.rows; }; Logo.prototype.get_pspm = function(row_index) { "use strict"; if (row_index < 0 || row_index >= this.rows) { throw new Error("INDEX_OUT_OF_BOUNDS"); } return this.pspm_list[row_index]; }; Logo.prototype.get_offset = function(row_index) { "use strict"; if (row_index < 0 || row_index >= this.rows) { throw new Error("INDEX_OUT_OF_BOUNDS"); } return this.pspm_column[row_index]; }; Logo.prototype._as_eps_data = function(ssc, errbars) { var i, j, pos, stack_pos, pspm, stack, sym, out; out = ""; for (i = 0; i < this.rows; i++) { out += "\nStartLine\n"; // Indent for (j = 0; j < this.pspm_column[i]; j++) { out += "() startstack\nendstack\n\n"; } pspm = this.pspm_list[i]; if (pspm.get_left_trim() > 0) { out += "MuteColour\nDrawTrimEdge\n" + pspm.get_left_trim() + " DrawTrimBg\n"; } for (pos = 0; pos < pspm.get_motif_length(); pos++) { if (pos != 0 && pos == pspm.get_left_trim()) { // enable full colour out += "DrawTrimEdge\nRestoreColour\n"; } else if (pos == (pspm.get_motif_length() - pspm.get_right_trim())) { out += "MuteColour\n" + pspm.get_right_trim() + " DrawTrimBg\n"; } out += "(" + (pos + 1) + ") startstack\n"; stack = pspm.get_stack(pos, this.alphabet, ssc); for (stack_pos = 0; stack_pos < stack.length; stack_pos++) { sym = stack[stack_pos]; out += " " + (sym.get_scale() * this.alphabet.get_ic()) + " (" + sym.get_symbol() + ") numchar\n"; } if (errbars) { out += " " + pspm.get_error(this.alphabet) + " Ibeam\n"; } out += "endstack\n\n"; } if (pspm.get_right_trim() > 0 || pspm.get_left_trim() == pspm.get_motif_length()) { out += "RestoreColour\n"; } out += "EndLine\n"; } return out; }; Logo.prototype.as_eps = function(options) { "use strict"; if (this.xlate_nsyms != 1) throw new Error("Unsupported setting xlate_nsyms for EPS"); if (this.xlate_start != null) throw new Error("Unsupported setting xlate_start for EPS"); if (this.xlate_end != null) throw new Error("Unsupported setting xlate_end for EPS"); var LOGOHEIGHT = 7.5; // default height of line in cm var cm2pts, height, width, now, ssc, errbars; if (typeof options === "undefined") { options = {}; } cm2pts = 72 / 2.54; if (typeof options.logo_height == "number") { height = options.logo_height; } else { height = LOGOHEIGHT * this.rows; } if (typeof options.logo_width == "number") { width = options.logo_width; } else { width = this.columns + 2; } now = new Date(); ssc = (typeof options.ssc == "boolean" ? options.ssc : false); errbars = (typeof options.show_error_bar == "boolean" ? options.show_error_bar : ssc); var values = { "LOGOHEIGHT": height, "LOGOWIDTH": width, "BOUNDINGHEIGHT": Math.round(height * cm2pts), "BOUNDINGWIDTH": Math.round(width * cm2pts), "LOGOLINEHEIGHT": (height / this.rows), "CHARSPERLINE": this.columns, "BARBITS": this.alphabet.get_ic(), "LOGOTYPE": (this.alphabet.has_complement() ? "NA" : "AA"), "CREATIONDATE": now.getDate() + "." + (now.getMonth() + 1) + "." + now.getFullYear() + " " + now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds(), "ERRORBARFRACTION": (typeof options.error_bar_fraction == "number" ? options.error_bar_fraction : 1.0), "TICBITS": (typeof options.ticbits == "number" ? options.ticbits : 1.0), "TITLE": (typeof options.title == "string" ? options.title : ""), "FINEPRINT": (typeof options.fineprint == "string" ? options.fineprint : this.fine_text), "XAXISLABEL": (typeof options.xaxislabel == "string" ? options.xaxislabel : ""), "YAXISLABEL": (typeof options.yaxislabel == "string" ? options.yaxislabel : "bits"), "SSC": ssc, "YAXIS": (typeof options.show_y_axis == "boolean" ? options.show_y_axis : this.y_axis), "SHOWENDS": (typeof options.show_ends == "boolean" ? options.show_ends : false), "ERRBAR": errbars, "OUTLINE": (typeof options.show_outline == "boolean" ? options.show_outline : false), "NUMBERING": (typeof options.show_numbering == "boolean" ? options.show_numbering : this.x_axis != 0), "SHOWINGBOX": (typeof options.show_box == "boolean" ? options.show_box : false), "CREATOR": (typeof options.creator == "string" ? options.creator : "motif_logo.js"), "FONTSIZE": (typeof options.label_font_size == "number" ? options.label_font_size : 12), "TITLEFONTSIZE": (typeof options.title_font_size == "number" ? options.title_font_size : 12), "SMALLFONTSIZE": (typeof options.small_font_size == "number" ? options.small_font_size : 6), "TOPMARGIN" : (typeof options.top_margin == "number" ? options.top_margin : 0.9), "BOTTOMMARGIN": (typeof options.bottom_margin == "number" ? options.bottom_margin : 0.9), "COLORDICT": this.alphabet._as_eps_dict(), "DATA": this._as_eps_data(ssc, errbars) }; // now this requires that the script containing the template has been imported! return motif_logo_template(values); }; //====================================================================== // end Logo object //====================================================================== // calculate the exact size (in pixels) of an object drawn on the // canvas assuming that the background of the canvas is transparent. function canvas_bounds(ctx, cwidth, cheight) { "use strict"; var data, r, c, top_line, bottom_line, left_line, right_line, txt_width, txt_height; // extract the image data data = ctx.getImageData(0, 0, cwidth, cheight).data; // set initial values top_line = -1; bottom_line = -1; left_line = -1; right_line = -1; txt_width = 0; txt_height = 0; // Find the top-most line with a non-transparent pixel for (r = 0; r < cheight; r++) { for (c = 0; c < cwidth; c++) { if (data[r * cwidth * 4 + c * 4 + 3]) { top_line = r; break; } } if (top_line != -1) { break; } } // Only bother looking if we found at least one set pixel... if (top_line != -1) { //find the last line with a non-transparent pixel for (r = cheight-1; r >= top_line; r--) { for(c = 0; c < cwidth; c++) { if(data[r * cwidth * 4 + c * 4 + 3]) { bottom_line = r; break; } } if (bottom_line != -1) { break; } } // calculate height txt_height = bottom_line - top_line + 1; // Find the left-most line with a non-transparent pixel for (c = 0; c < cwidth; c++) { for (r = top_line; r <= bottom_line; r++) { if (data[r * cwidth * 4 + c * 4 + 3]) { left_line = c; break; } } if (left_line != -1) { break; } } //find the right most line with a non-transparent pixel for (c = cwidth-1; c >= left_line; c--) { for(r = top_line; r <= bottom_line; r++) { if(data[r * cwidth * 4 + c * 4 + 3]) { right_line = c; break; } } if (right_line != -1) { break; } } txt_width = right_line - left_line + 1; } //return the bounds return {bound_top: top_line, bound_bottom: bottom_line, bound_left: left_line, bound_right: right_line, width: txt_width, height: txt_height}; } //====================================================================== // start RasterizedAlphabet //====================================================================== // Rasterize Alphabet // 1) Measure width of text at default font for all symbols in alphabet // 2) sort in width ascending // 3) Drop the top and bottom 10% (designed to ignore outliers like 'W' and 'I') // 4) Calculate the average as the maximum scaling factor (designed to stop I becoming a rectangular blob). // 5) Assume scale of zero would result in width of zero, interpolate scale required to make perfect width font // 6) Draw text onto temp canvas at calculated scale // 7) Find bounds of drawn text // 8) Paint on to another canvas at the desired height (but only scaling width to fit if larger). var RasterizedAlphabet = function(alphabet, logo_scale, font, width) { "use strict"; var default_size, safety_pad, canvas, ctx, middle, baseline, widths, sizes, i, sym, size, tenpercent, avg_width, scale, target_width, target_height; //variable prototypes this.alphabet = alphabet; this.scale = logo_scale; this.sym_cache = {}; this.stack_num_cache = []; this.scale_num_cache = []; // size of canvas default_size = 60; // size of measuring canvas safety_pad = 20; // pixels to pad around so we don't miss the edges // create a canvas to do our measuring canvas = document.createElement("canvas"); if (!canvas.getContext) throw new Error("No canvas support"); canvas.width = default_size + 2 * safety_pad; canvas.height = default_size + 2 * safety_pad; middle = Math.round(canvas.width / 2); baseline = Math.round(canvas.height - safety_pad); ctx = canvas.getContext('2d'); if (!supports_text(ctx)) throw new Error("Canvas does not support text"); ctx.font = font; ctx.textAlign = "center"; ctx.translate(middle, baseline); // list of widths widths = []; sizes = []; //now measure each letter in the alphabet for (i = 0; i < alphabet.get_size_core(); ++i) { // reset the canvas ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.fillStyle = alphabet.get_colour(i); // draw the test text ctx.fillText(alphabet.get_symbol(i), 0, 0); //measure size = canvas_bounds(ctx, canvas.width, canvas.height); if (size.width === 0) throw new Error("Invisible symbol!"); widths.push(size.width); sizes[i] = size; } //sort the widths widths.sort(function(a,b) {return a - b;}); //drop 10% of the items off each end tenpercent = Math.floor(widths.length / 10); for (i = 0; i < tenpercent; ++i) { widths.pop(); widths.shift(); } //calculate average width avg_width = 0; for (i = 0; i < widths.length; ++i) { avg_width += widths[i]; } avg_width /= widths.length; // calculate the target width target_width = width * this.scale * 2; // calculate scales for (i = 0; i < alphabet.get_size_core(); ++i) { sym = alphabet.get_symbol(i); size = sizes[i]; // calculate scale scale = target_width / Math.max(avg_width, size.width); // estimate scaled height target_height = size.height * scale; // create an appropriately sized canvas canvas = document.createElement("canvas"); canvas.width = target_width; canvas.height = target_height + safety_pad * 2; // calculate the middle middle = Math.round(canvas.width / 2); // calculate the baseline baseline = Math.round(canvas.height - safety_pad); // get the context and prepare to draw the rasterized text ctx = canvas.getContext('2d'); ctx.font = font; ctx.fillStyle = alphabet.get_colour(i); ctx.textAlign = "center"; ctx.translate(middle, baseline); ctx.save(); ctx.scale(scale, scale); // draw the text ctx.fillText(sym, 0, 0); ctx.restore(); this.sym_cache[sym] = {"image": canvas, "size": canvas_bounds(ctx, canvas.width, canvas.height)}; } }; RasterizedAlphabet.prototype.get_alphabet = function() { return this.alphabet; }; RasterizedAlphabet.prototype.get_scale = function() { return this.scale; }; RasterizedAlphabet.prototype.draw_stack_sym = function(ctx, letter, dx, dy, dWidth, dHeight) { "use strict"; var entry, image, size; entry = this.sym_cache[letter]; image = entry.image; size = entry.size; ctx.drawImage(image, 0, size.bound_top -1, image.width, size.height+1, dx, dy, dWidth, dHeight); }; RasterizedAlphabet.prototype.draw_stack_num = function(ctx, font, stack_width, index) { var image, image_ctx, text_length; if (index >= this.stack_num_cache.length) { image = document.createElement("canvas"); // measure the text image_ctx = image.getContext('2d'); image_ctx.save(); image_ctx.font = font; text_length = image_ctx.measureText("" + (index + 1)).width; image_ctx.restore(); // resize the canvas to fit image.width = Math.ceil(stack_width); image.height = Math.ceil(text_length); // draw the text image_ctx = image.getContext('2d'); image_ctx.translate(Math.round(stack_width / 2), 0); image_ctx.font = font; image_ctx.textBaseline = "middle"; image_ctx.textAlign = "right"; image_ctx.rotate(-(Math.PI / 2)); image_ctx.fillText("" + (index + 1), 0, 0); this.stack_num_cache[index] = image; } else { image = this.stack_num_cache[index]; } ctx.drawImage(image, 0, 0); } RasterizedAlphabet.prototype.draw_scale_num = function(ctx, font, num) { var image, image_ctx, text_size, m_length; if (num >= this.scale_num_cache.length) { image = document.createElement("canvas"); // measure the text image_ctx = image.getContext('2d'); image_ctx.font = font; text_size = image_ctx.measureText("" + num); if (text_size.actualBoundingBoxAscent && text_size.actualBoundingBoxDesent) { // resize the canvas to fit image.width = Math.ceil(text_size.width); image.height = Math.ceil(text_size.actualBoundingBoxAscent + text_size.actualBoundingBoxDesent); // draw the text image_ctx = image.getContext('2d'); image_ctx.font = font; image_ctx.textAlign = "right"; image_ctx.fillText("" + num, image.width, text_size.actualBoundingBoxAscent); } else { // measure width of 'm' to approximate height, we double it later anyway m_length = image_ctx.measureText("m").width; // resize the canvas to fit image.width = Math.ceil(text_size.width); image.height = Math.ceil(2 * m_length); // draw the text image_ctx = image.getContext('2d'); image_ctx.font = font; image_ctx.textAlign = "right"; image_ctx.textBaseline = "middle"; image_ctx.fillText("" + num, image.width, m_length); } this.scale_num_cache[num] = image; } else { image = this.scale_num_cache[num]; } ctx.drawImage(image, -image.width, -Math.round(image.height / 2)) } //====================================================================== // end RasterizedAlphabet //====================================================================== //====================================================================== // start LogoMetrics object //====================================================================== var LogoMetrics = function(ctx, logo_columns, logo_rows, has_names, has_finetext, x_axis, y_axis) { "use strict"; var i, row_height; //variable prototypes this.pad_top = (has_names ? 5 : 0); this.pad_left = (y_axis ? 10 : 0); this.pad_right = (has_finetext ? 15 : 0); this.pad_bottom = 0; this.pad_middle = 20; this.name_height = 14; this.name_font = "bold " + this.name_height + "px Times, sans-serif"; this.name_spacer = 0; this.y_axis = y_axis; this.y_label = "bits"; this.y_label_height = 12; this.y_label_font = "bold " + this.y_label_height + "px Helvetica, sans-serif"; this.y_label_spacer = 3; this.y_num_height = 12; this.y_num_width = 0; this.y_num_font = "bold " + this.y_num_height + "px Helvetica, sans-serif"; this.y_tic_width = 5; this.stack_pad_left = 0; this.stack_font = "bold 25px Helvetica, sans-serif"; this.stack_height = 90; this.stack_width = 26; this.stacks_pad_right = 5; this.x_axis = x_axis; this.x_num_above = 2; this.x_num_height = 12; this.x_num_width = 0; this.x_num_font = "bold " + this.x_num_height + "px Helvetica, sans-serif"; this.fine_txt_height = 6; this.fine_txt_above = 2; this.fine_txt_font = "normal " + this.fine_txt_height + "px Helvetica, sans-serif"; this.letter_metrics = new Array(); this.summed_width = 0; this.summed_height = 0; //calculate the width of the y axis numbers ctx.font = this.y_num_font; for (i = 0; i <= 2; i++) { this.y_num_width = Math.max(this.y_num_width, ctx.measureText("" + i).width); } //calculate the width of the x axis numbers (but they are rotated so it becomes height) if (x_axis == 1) { ctx.font = this.x_num_font; for (i = 1; i <= logo_columns; i++) { this.x_num_width = Math.max(this.x_num_width, ctx.measureText("" + i).width); } } else if (x_axis == 0) { this.x_num_height = 4; this.x_num_width = 4; } else { this.x_num_height = 0; this.x_num_width = 0; } //calculate how much vertical space we want to draw this //first we add the padding at the top and bottom since that's always there this.summed_height += this.pad_top + this.pad_bottom; //all except the last row have the same amount of space allocated to them if (logo_rows > 1) { row_height = this.stack_height + this.pad_middle; if (has_names) { row_height += this.name_height; //the label is allowed to overlap into the spacer row_height += Math.max(this.y_num_height/2, this.name_spacer); //the label is allowed to overlap the space used by the other label row_height += Math.max(this.y_num_height/2, this.x_num_height + this.x_num_above); } else { row_height += this.y_num_height/2; //the label is allowed to overlap the space used by the other label row_height += Math.max(this.y_num_height/2, this.x_num_height + this.x_num_above); } this.summed_height += row_height * (logo_rows - 1); } //the last row has the name and fine text below it but no padding this.summed_height += this.stack_height + (this.y_axis ? this.y_num_height/2 : 0); var fine_txt_total = (has_finetext ? this.fine_txt_height + this.fine_txt_above : 0); if (has_names) { this.summed_height += fine_txt_total + this.name_height; this.summed_height += Math.max((this.y_axis ? this.y_num_height/2 : 0), this.x_num_height + this.x_num_above + this.name_spacer); } else { this.summed_height += Math.max((this.y_axis ? this.y_num_height/2 : 0), this.x_num_height + this.x_num_above + fine_txt_total); } //calculate how much horizontal space we want to draw this //first add the padding at the left and right since that's always there this.summed_width += this.pad_left + this.pad_right; if (this.y_axis) { //add on the space for the y-axis label this.summed_width += this.y_label_height + this.y_label_spacer; //add on the space for the y-axis this.summed_width += this.y_num_width + this.y_tic_width; } //add on the space for the stacks this.summed_width += (this.stack_pad_left + this.stack_width) * logo_columns; //add on the padding after the stacks (an offset from the fine text) this.summed_width += this.stacks_pad_right; }; //====================================================================== // end LogoMetrics object //====================================================================== //found this trick at http://talideon.com/weblog/2005/02/detecting-broken-images-js.cfm function image_ok(img) { "use strict"; // During the onload event, IE correctly identifies any images that // weren't downloaded as not complete. Others should too. Gecko-based // browsers act like NS4 in that they report this incorrectly. if (!img.complete) { return false; } // However, they do have two very useful properties: naturalWidth and // naturalHeight. These give the true size of the image. If it failed // to load, either of these should be zero. if (typeof img.naturalWidth !== "undefined" && img.naturalWidth === 0) { return false; } // No other way of checking: assume it's ok. return true; } function supports_text(ctx) { "use strict"; if (!ctx.fillText) { return false; } if (!ctx.measureText) { return false; } return true; } //draws the scale, returns the width function draw_scale(ctx, metrics, alphabet_ic, raster) { "use strict"; var tic_height, i; tic_height = metrics.stack_height / alphabet_ic; ctx.save(); ctx.translate(metrics.y_label_height, metrics.y_num_height/2); //draw the axis label ctx.save(); ctx.font = metrics.y_label_font; ctx.translate(0, metrics.stack_height/2); ctx.rotate(-(Math.PI / 2)); ctx.textAlign = "center"; ctx.fillText("bits", 0, 0); ctx.restore(); ctx.translate(metrics.y_label_spacer + metrics.y_num_width, 0); //draw the axis tics ctx.save(); ctx.translate(0, metrics.stack_height); for (i = 0; i <= alphabet_ic; i++) { //draw the number ctx.save(); ctx.translate(-1, 0); raster.draw_scale_num(ctx, metrics.y_num_font, i); ctx.restore(); //draw the tic ctx.fillRect(0, -1, metrics.y_tic_width, 2); //prepare for next tic ctx.translate(0, -tic_height); } ctx.restore(); ctx.fillRect(metrics.y_tic_width - 2, 0, 2, metrics.stack_height) ctx.restore(); } function draw_stack_num(ctx, metrics, row_index, raster) { "use strict"; ctx.save(); ctx.translate(0, Math.round(metrics.stack_height + metrics.x_num_above)); if (metrics.x_axis == 1) { raster.draw_stack_num(ctx, metrics.x_num_font, metrics.stack_width, row_index); } else if (metrics.x_axis == 0) { // draw dots instead of the numbers (good for small logos) ctx.beginPath(); var radius = Math.round(metrics.x_num_height / 2); ctx.arc(Math.round(metrics.stack_width / 2), radius, radius, 0, 2 * Math.PI, false); ctx.fill(); } ctx.restore(); } function draw_stack(ctx, metrics, symbols, raster) { "use strict"; var preferred_pad, sym_min, i, sym, sym_height, pad; preferred_pad = 0; sym_min = 5; ctx.save();//1 ctx.translate(0, metrics.stack_height); for (i = 0; i < symbols.length; i++) { sym = symbols[i]; sym_height = metrics.stack_height * sym.get_scale(); pad = preferred_pad; if (sym_height - pad < sym_min) { pad = Math.min(pad, Math.max(0, sym_height - sym_min)); } sym_height -= pad; //translate to the correct position ctx.translate(0, -(pad/2 + sym_height)); //draw raster.draw_stack_sym(ctx, sym.get_symbol(), 0, 0, metrics.stack_width, sym_height); //translate past the padding ctx.translate(0, -(pad/2)); } ctx.restore();//1 } function draw_dashed_line(ctx, pattern, start, x1, y1, x2, y2) { "use strict"; var x, y, len, i, dx, dy, tlen, theta, mulx, muly, lx, ly; dx = x2 - x1; dy = y2 - y1; tlen = Math.pow(dx*dx + dy*dy, 0.5); theta = Math.atan2(dy,dx); mulx = Math.cos(theta); muly = Math.sin(theta); lx = []; ly = []; for (i = 0; i < pattern; ++i) { lx.push(pattern[i] * mulx); ly.push(pattern[i] * muly); } i = start; x = x1; y = y1; len = 0; ctx.beginPath(); while (len + pattern[i] < tlen) { ctx.moveTo(x, y); x += lx[i]; y += ly[i]; ctx.lineTo(x, y); len += pattern[i]; i = (i + 1) % pattern.length; x += lx[i]; y += ly[i]; len += pattern[i]; i = (i + 1) % pattern.length; } if (len < tlen) { ctx.moveTo(x, y); x += mulx * (tlen - len); y += muly * (tlen - len); ctx.lineTo(x, y); } ctx.stroke(); } function draw_trim_background(ctx, metrics, left_start, left_end, left_divider, right_start, right_end, right_divider) { "use strict"; var left_size = left_end - left_start; var right_size = right_end - right_start; var line_x; ctx.save();//s8 ctx.fillStyle = "rgb(240, 240, 240)"; if (left_size > 0) { ctx.fillRect(left_start * metrics.stack_width, 0, left_size * metrics.stack_width, metrics.stack_height); } if (right_size > 0) { ctx.fillRect(right_start * metrics.stack_width, 0, right_size * metrics.stack_width, metrics.stack_height); } ctx.fillStyle = "rgb(51, 51, 51)"; if (left_size > 0 && left_divider) { line_x = (left_end * metrics.stack_width) - 0.5; draw_dashed_line(ctx, [3], 0, line_x, 0, line_x, metrics.stack_height); } if (right_size > 0 && right_divider) { line_x = (right_start * metrics.stack_width) + 0.5; draw_dashed_line(ctx, [3], 0, line_x, 0, line_x, metrics.stack_height); } ctx.restore();//s8 } function size_logo_on_canvas(logo, canvas, show_names, scale) { "use strict"; var draw_name, draw_finetext, metrics; draw_name = (typeof show_names === "boolean" ? show_names : (logo.get_rows() > 1)); draw_finetext = (logo.fine_text.length > 0); if (canvas.width !== 0 && canvas.height !== 0) { return; } metrics = new LogoMetrics(canvas.getContext('2d'), logo.get_xlate_columns(), logo.get_rows(), draw_name, draw_finetext, logo.x_axis, logo.y_axis); if (typeof scale == "number") { //resize the canvas to fit the scaled logo canvas.width = metrics.summed_width * scale; canvas.height = metrics.summed_height * scale; } else { if (canvas.width === 0 && canvas.height === 0) { canvas.width = metrics.summed_width; canvas.height = metrics.summed_height; } else if (canvas.width === 0) { canvas.width = metrics.summed_width * (canvas.height / metrics.summed_height); } else if (canvas.height === 0) { canvas.height = metrics.summed_height * (canvas.width / metrics.summed_width); } } } function draw_logo_on_canvas(logo, canvas, show_names, scale) { "use strict"; var i, draw_name, draw_finetext, ctx, metrics, raster, pspm_i, pspm, offset, col_index, motif_position, ssc; ssc = false; draw_name = (typeof show_names === "boolean" ? show_names : (logo.get_rows() > 1)); draw_finetext = (logo.fine_text.length > 0); ctx = canvas.getContext('2d'); //assume that the user wants the canvas scaled equally so calculate what the best width for this image should be metrics = new LogoMetrics(ctx, logo.get_xlate_columns(), logo.get_rows(), draw_name, draw_finetext, logo.x_axis, logo.y_axis); if (typeof scale == "number") { //resize the canvas to fit the scaled logo canvas.width = metrics.summed_width * scale; canvas.height = metrics.summed_height * scale; } else { if (canvas.width === 0 && canvas.height === 0) { scale = 1; canvas.width = metrics.summed_width; canvas.height = metrics.summed_height; } else if (canvas.width === 0) { scale = canvas.height / metrics.summed_height; canvas.width = metrics.summed_width * scale; } else if (canvas.height === 0) { scale = canvas.width / metrics.summed_width; canvas.height = metrics.summed_height * scale; } else { scale = Math.min(canvas.width / metrics.summed_width, canvas.height / metrics.summed_height); } } // cache the raster based on the assumption that we will be drawing a lot // of logos the same size and alphabet if (typeof draw_logo_on_canvas.raster_cache === "undefined") { draw_logo_on_canvas.raster_cache = []; } for (i = 0; i < draw_logo_on_canvas.raster_cache.length; i++) { raster = draw_logo_on_canvas.raster_cache[i]; if (raster.get_alphabet().equals(logo.alphabet) && Math.abs(raster.get_scale() - scale) < 0.1) break; raster = null; } if (raster == null) { raster = new RasterizedAlphabet(logo.alphabet, scale, metrics.stack_font, metrics.stack_width); draw_logo_on_canvas.raster_cache.push(raster); } ctx = canvas.getContext('2d'); ctx.save();//s1 ctx.scale(scale, scale); ctx.save();//s2 ctx.save();//s7 //create margin ctx.translate(Math.round(metrics.pad_left), Math.round(metrics.pad_top)); for (pspm_i = 0; pspm_i < logo.get_rows(); ++pspm_i) { pspm = logo.get_pspm(pspm_i); offset = logo.get_offset(pspm_i); //optionally draw name if this isn't the last row or is the only row if (draw_name && (logo.get_rows() == 1 || pspm_i != (logo.get_rows()-1))) { ctx.save();//s4 ctx.translate(Math.round(metrics.summed_width/2), Math.round(metrics.name_height)); ctx.font = metrics.name_font; ctx.textAlign = "center"; ctx.fillText(pspm.name, 0, 0); ctx.restore();//s4 ctx.translate(0, Math.round(metrics.name_height + Math.min(0, metrics.name_spacer - metrics.y_num_height/2))); } //draw scale if (logo.y_axis) draw_scale(ctx, metrics, logo.alphabet.get_ic(), raster); ctx.save();//s5 //translate across past the scale if (logo.y_axis) { ctx.translate(Math.round(metrics.y_label_height + metrics.y_label_spacer + metrics.y_num_width + metrics.y_tic_width), Math.round(metrics.y_num_height / 2)); } //draw the trimming background if (pspm.get_left_trim() > 0 || pspm.get_right_trim() > 0) { var left_start = offset * logo.get_xlate_nsyms(); var left_end = (offset + pspm.get_left_trim()) * logo.get_xlate_nsyms(); var left_divider = true; if (left_end < logo.get_xlate_start() || left_start > logo.get_xlate_end()) { // no overlap left_start = 0; left_end = 0; left_divider = false; } else { if (left_start < logo.get_xlate_start()) { left_start = logo.get_xlate_start(); } if (left_end > logo.get_xlate_end()) { left_end = logo.get_xlate_end(); left_divider = false; } left_start -= logo.get_xlate_start(); left_end -= logo.get_xlate_start(); if (left_end < left_start) { left_start = 0; left_end = 0; left_divider = false; } } var right_end = (offset + pspm.get_motif_length()) * logo.get_xlate_nsyms(); //var right_start = right_end - (pspm.get_left_trim() * logo.get_xlate_nsyms()); var right_start = right_end - (pspm.get_right_trim() * logo.get_xlate_nsyms()); var right_divider = true; if (right_end < logo.get_xlate_start() || right_start > logo.get_xlate_end()) { // no overlap right_start = 0; right_end = 0; right_divider = false; } else { if (right_start < logo.get_xlate_start()) { right_start = logo.get_xlate_start(); right_divider = false; } if (right_end > logo.get_xlate_end()) { right_end = logo.get_xlate_end(); } right_start -= logo.get_xlate_start(); right_end -= logo.get_xlate_start(); if (right_end < right_start) { right_start = 0; right_end = 0; right_divider = false; } } draw_trim_background(ctx, metrics, left_start, left_end, left_divider, right_start, right_end, right_divider); } //draw letters var xlate_col; for (xlate_col = logo.get_xlate_start(); xlate_col < logo.get_xlate_end(); xlate_col++) { ctx.translate(metrics.stack_pad_left,0); col_index = Math.floor(xlate_col / logo.get_xlate_nsyms()); if (xlate_col % logo.get_xlate_nsyms() == 0) { if (col_index >= offset && col_index < (offset + pspm.get_motif_length())) { motif_position = col_index - offset; draw_stack_num(ctx, metrics, motif_position, raster); draw_stack(ctx, metrics, pspm.get_stack(motif_position, logo.alphabet, ssc), raster); } } else { if (col_index >= offset && col_index < (offset + pspm.get_motif_length())) { ctx.save();// s5.1 ctx.translate(0, Math.round(metrics.stack_height)); // TODO draw a dot or dash or something to indicate continuity of the motif ctx.restore(); //s5.1 } } ctx.translate(Math.round(metrics.stack_width), 0); } ctx.restore();//s5 ////optionally draw name if this is the last row but isn't the only row if (draw_name && (logo.get_rows() != 1 && pspm_i == (logo.get_rows()-1))) { //translate vertically past the stack and axis's ctx.translate(0, metrics.y_num_height/2 + metrics.stack_height + Math.max(metrics.y_num_height/2, metrics.x_num_above + metrics.x_num_width + metrics.name_spacer)); ctx.save();//s6 ctx.translate(metrics.summed_width/2, metrics.name_height); ctx.font = metrics.name_font; ctx.textAlign = "center"; ctx.fillText(pspm.name, 0, 0); ctx.restore();//s6 ctx.translate(0, metrics.name_height); } else { //translate vertically past the stack and axis's ctx.translate(0, metrics.y_num_height/2 + metrics.stack_height + Math.max(metrics.y_num_height/2, metrics.x_num_above + metrics.x_num_width)); } //if not the last row then add middle padding if (pspm_i != (logo.get_rows() -1)) { ctx.translate(0, metrics.pad_middle); } } ctx.restore();//s7 if (logo.fine_text.length > 0) { ctx.translate(metrics.summed_width - metrics.pad_right, metrics.summed_height - metrics.pad_bottom); ctx.font = metrics.fine_txt_font; ctx.textAlign = "right"; ctx.fillText(logo.fine_text, 0,0); } ctx.restore();//s2 ctx.restore();//s1 } function create_canvas(c_width, c_height, c_id, c_title, c_display) { "use strict"; var canvas = document.createElement("canvas"); //check for canvas support before attempting anything if (!canvas.getContext) { return null; } var ctx = canvas.getContext('2d'); //check for html5 text drawing support if (!supports_text(ctx)) { return null; } //size the canvas canvas.width = c_width; canvas.height = c_height; canvas.id = c_id; canvas.title = c_title; canvas.style.display = c_display; return canvas; } function logo_1(alphabet, fine_text, pspm) { "use strict"; var logo = new Logo(alphabet, fine_text); logo.add_pspm(pspm); return logo; } function logo_2(alphabet, fine_text, target, query, query_offset) { "use strict"; var logo = new Logo(alphabet, fine_text); if (query_offset < 0) { logo.add_pspm(target, -query_offset); logo.add_pspm(query); } else { logo.add_pspm(target); logo.add_pspm(query, query_offset); } return logo; } /* * Specifies an alternate source for an image. * If the image with the image_id specified has * not loaded then a generated logo will be used * to replace it. * * Note that the image must either have dimensions * or a scale must be set. */ function alternate_logo(logo, image_id, scale) { "use strict"; var image = document.getElementById(image_id); if (!image) { alert("Can't find specified image id (" + image_id + ")"); return; } //if the image has loaded then there is no reason to use the canvas if (image_ok(image)) { return; } //the image has failed to load so replace it with a canvas if we can. var canvas = create_canvas(image.width, image.height, image_id, image.title, image.style.display); if (canvas === null) { return; } //draw the logo on the canvas draw_logo_on_canvas(logo, canvas, null, scale); //replace the image with the canvas image.parentNode.replaceChild(canvas, image); } /* * Specifes that the element with the specified id * should be replaced with a generated logo. */ function replace_logo(logo, replace_id, scale, title_txt, display_style) { "use strict"; var element = document.getElementById(replace_id); if (!replace_id) { alert("Can't find specified id (" + replace_id + ")"); return; } //found the element! var canvas = create_canvas(50, 120, replace_id, title_txt, display_style); if (canvas === null) { return; } //draw the logo on the canvas draw_logo_on_canvas(logo, canvas, null, scale); //replace the element with the canvas element.parentNode.replaceChild(canvas, element); } /* * Fast string trimming implementation found at * http://blog.stevenlevithan.com/archives/faster-trim-javascript * * Note that regex is good at removing leading space but * bad at removing trailing space as it has to first go through * the whole string. */ function trim (str) { "use strict"; var ws, i; str = str.replace(/^\s\s*/, ''); ws = /\s/; i = str.length; while (ws.test(str.charAt(--i))); return str.slice(0, i + 1); } </script> <script> // PRIVATE GLOBAL (uhoh) var _block_colour_lookup = {}; function block_colour(index) { function hsl2rgb(hue, saturation, lightness) { "use strict"; function _hue(p, q, t) { "use strict"; if (t < 0) t += 1; else if (t > 1) t -= 1; if (t < (1.0 / 6.0)) { return p + ((q - p) * 6.0 * t); } else if (t < 0.5) { return q; } else if (t < (2.0 / 3.0)) { return p + ((q - p) * ((2.0 / 3.0) - t) * 6.0); } else { return p; } } function _pad_hex(value) { var hex = Math.round(value * 255).toString(16); if (hex.length < 2) hex = "0" + hex; return hex; } var r, g, b, p, q; if (saturation == 0) { // achromatic (grayscale) r = lightness; g = lightness; b = lightness; } else { if (lightness < 0.5) { q = lightness * (1 + saturation); } else { q = lightness + saturation - (lightness * saturation); } p = (2 * lightness) - q; r = _hue(p, q, hue + (1.0 / 3.0)); g = _hue(p, q, hue); b = _hue(p, q, hue - (1.0 / 3.0)); } return "#" + _pad_hex(r) + _pad_hex(g) + _pad_hex(b); } if (typeof index !== "number" || index % 1 !== 0 || index < 0) return "#000000"; // check for override if (_block_colour_lookup[index] == null) { var start = 0; //red var sat = 100; var light = 50; var divisions = 1 << Math.ceil(Math.log(index + 1) / Math.LN2); hue = start + (360 / divisions) * ((index - (divisions >> 1)) * 2 + 1); // colour input fields only support values in the form #RRGGBB _block_colour_lookup[index] = hsl2rgb(hue / 360, sat / 100, light / 100); } return _block_colour_lookup[index]; } function set_block_colour(index, new_colour) { _block_colour_lookup[index] = new_colour; var blocks = document.querySelectorAll("div.block_motif[data-colour-index=\"" + index + "\"]"); var i; for (i = 0; i < blocks.length; i++) { blocks[i].style.backgroundColor = new_colour; } var swatches = document.querySelectorAll("div.legend_swatch[data-colour-index=\"" + index + "\"]"); var picker; for (i = 0; i < swatches.length; i++) { swatches[i].style.backgroundColor = new_colour; picker = swatches[i].querySelector("input[type=\"color\"]"); if (picker != null) picker.value = new_colour; } } function make_block_legend_entry(motif_name, motif_colour_index) { if (typeof make_block_legend_entry.has_colour_picker !== "boolean") { // test if colour picker is supported, based off Modernizer // see http://stackoverflow.com/a/7787648/66387 make_block_legend_entry.has_colour_picker = (function() { var doc_ele = document.documentElement; // We first check to see if the type we give it sticks.. var input_ele = document.createElement('input'); input_ele.setAttribute('type', 'color'); var value_ok = input_ele.type !== 'text'; if (value_ok) { // If the type does, we feed it a textual value, which shouldn't be valid. // If the value doesn't stick, we know there's input sanitization which infers a custom UI var smile = ':)'; input_ele.value = smile; input_ele.style.cssText = 'position:absolute;visibility:hidden;'; // chuck into DOM and force reflow for Opera bug in 11.00 // github.com/Modernizr/Modernizr/issues#issue/159 doc_ele.appendChild(input_ele); doc_ele.offsetWidth; value_ok = input_ele.value != smile; doc_ele.removeChild(input_ele); } return value_ok; })(); } var entry = document.createElement("div"); entry.className = "legend_entry"; var swatch; swatch = document.createElement("div"); swatch.className = "legend_swatch"; swatch.setAttribute("data-colour-index", motif_colour_index); swatch.style.backgroundColor = block_colour(motif_colour_index); if (make_block_legend_entry.has_colour_picker) { var picker = document.createElement("input"); picker.type = "color"; picker.value = block_colour(motif_colour_index); picker.addEventListener("change", function(e) { set_block_colour(motif_colour_index, picker.value); }, false); swatch.addEventListener("click", function(e) { picker.click(); }, false); swatch.appendChild(picker); } entry.appendChild(swatch); var name = document.createElement("div"); name.className = "legend_text"; name.appendChild(document.createTextNode(motif_name)); entry.appendChild(name); return entry; } function make_block_ruler(max_len) { var container = document.createElement("div"); container.className = "block_container"; var step; if (max_len < 50) { step = 1; } else if (max_len < 100) { step = 2; } else if (max_len < 200) { step = 4; } else if (max_len < 500) { step = 10; } else if (max_len < 1000) { step = 20; } else if (max_len < 2000) { step = 40; } else if (max_len < 5000) { step = 100; } else if (max_len < 10000) { step = 200; } else if (max_len < 20000) { step = 400; } else { step = Math.floor(max_len / 20000) * 400; } var peroid; if (max_len < 10) { peroid = 1; } else if (max_len < 20) { peroid = 2; } else { peroid = 5; } var i, cycle, offset, tic, label; for (i = 0, cycle = 0; i < max_len; i += step, cycle = (cycle + 1) % peroid) { offset = "" + ((i / max_len) * 100) + "%"; tic = document.createElement("div"); tic.style.left = offset; tic.className = (cycle == 0 ? "tic_major" : "tic_minor"); container.appendChild(tic); if (cycle == 0) { label = document.createElement("div"); label.className = "tic_label"; label.style.left = offset; label.appendChild(document.createTextNode(i)); container.appendChild(label); } } return container; } function _calculate_block_needle_drag_pos(e, data) { var mouse; e = e || window.event; if (e.pageX || ev.pageY) { mouse = {"x": e.pageX, "y": e.pageY}; } else { mouse = { x:e.clientX + document.body.scrollLeft - document.body.clientLeft, y:e.clientY + document.body.scrollTop - document.body.clientTop }; } var cont = data.container; var dragable_length = cont.clientWidth - (cont.style.paddingLeft ? cont.style.paddingLeft : 0) - (cont.style.paddingRight ? cont.style.paddingRight : 0); //I believe that the offset parent is the body //otherwise I would need to make this recursive //maybe clientLeft would work, but the explanation of //it is hard to understand and it apparently doesn't work //in firefox 2. var diff = mouse.x - cont.offsetLeft; if (diff < 0) diff = 0; if (diff > dragable_length) diff = dragable_length; var pos = Math.round(diff / dragable_length * data.max); if (pos > data.len) pos = data.len; return pos; } function _update_block_needle_drag(e, data, done) { "use strict"; var pos = _calculate_block_needle_drag_pos(e, data); // read the needle positions var left = parseInt(data.llabel.textContent, 10) - data.off - 1; var right = parseInt(data.rlabel.textContent, 10) - data.off; // validate needle positions if (left >= data.len) left = data.len - 1; if (left < 0) left = 0; if (right > data.len) right = data.len; if (right <= left) right = left + 1; // calculate the new needle positions if (data.moveboth) { var size = right - left; if (data.isleft) { if ((pos + size) > data.len) pos = data.len - size; left = pos; right = pos + size; } else { if ((pos - size) < 0) pos = size; left = pos - size; right = pos; } } else { if (data.isleft) { if (pos >= right) pos = right - 1; left = pos; } else { if (pos <= left) pos = left + 1; right = pos; } } // update the needle positions data.lneedle.style.left = "" + (left / data.max * 100) + "%"; data.llabel.textContent = "" + (left + data.off + 1); data.rneedle.style.left = "" + (right / data.max * 100) + "%"; data.rlabel.textContent = "" + (right + data.off); data.handler(left, right, done); } function _make_block_needle_drag_start_handler(isleft, data) { return function (e) { data.isleft = isleft; data.moveboth = !(e.shiftKey); document.addEventListener("mousemove", data.drag_during, false); document.addEventListener("mouseup", data.drag_end, false); }; } function _make_block_needle_drag_end_handler(data) { return function (e) { document.removeEventListener("mousemove", data.drag_during, false); document.removeEventListener("mouseup", data.drag_end, false); _update_block_needle_drag(e, data, true); }; } function _make_block_needle_drag_during_handler(data) { return function (e) { _update_block_needle_drag(e, data, false); }; } // private function used by make_block_container function _make_block_needle(isleft, value, data) { var vbar = document.createElement('div'); vbar.className = "block_needle " + (isleft ? "left" : "right"); vbar.style.left = "" + (value / data.max * 100)+ "%"; var label = document.createElement('div'); label.className = "block_handle " + (isleft ? "left" : "right"); // The needles sit between the sequence positions, so the left one sits at the // start and the right at the end. This is why 1 is added to the displayed // value for a left handle as the user doesn't need to know about this detail label.textContent = "" + (isleft ? value + data.off + 1 : value + data.off); label.unselectable = "on"; // so IE and Opera don't select the text, others are done in css label.title = "Drag to move the displayed range. Hold shift and drag to change " + (isleft ? "lower" : "upper") + " bound of the range."; vbar.appendChild(label); if (isleft) { data.lneedle = vbar; data.llabel = label; } else { data.rneedle = vbar; data.rlabel = label; } label.addEventListener("mousedown", _make_block_needle_drag_start_handler(isleft, data), false); return vbar; } function make_block_container(is_stranded, has_both_strands, max_len, show_len, offset, range_handler) { offset = (offset != null ? offset : 0); // make the container for the block diagram var container = document.createElement("div"); container.className = "block_container"; container.setAttribute("data-max", max_len); container.setAttribute("data-off", offset); if (is_stranded) { var plus = document.createElement("div"); plus.appendChild(document.createTextNode("+")); plus.className = "block_plus_sym"; container.appendChild(plus); if (has_both_strands) { var minus = document.createElement("div"); minus.appendChild(document.createTextNode("-")); minus.className = "block_minus_sym"; container.appendChild(minus); } } var rule = document.createElement("div"); rule.className = "block_rule"; rule.style.width = ((show_len / max_len) * 100) + "%"; container.appendChild(rule); if (range_handler != null) { var range_data = { "max": max_len, "len": show_len, "off": offset, "handler": range_handler, "container": container, "lneedle": null, "llabel": null, "rneedle": null, "rlabel": null, "isleft": false, "moveboth" : false }; range_data.drag_during = _make_block_needle_drag_during_handler(range_data); range_data.drag_end = _make_block_needle_drag_end_handler(range_data); container.appendChild(_make_block_needle(false, 1, range_data)); // add right first so z-index works container.appendChild(_make_block_needle(true, 0, range_data)); } return container; } function make_block_label(container, max_len, pos, length, message) { "use strict"; var label = document.createElement("div"); label.className = "block_label"; label.style.left = (((pos + (length / 2)) / max_len) * 100) + "%"; label.appendChild(document.createTextNode(message)); container.appendChild(label); } function make_block(container, max_len, site_pos, site_len, site_pvalue, site_rc, site_colour_index, site_secondary) { "use strict"; var block_height, block, block_region1, block_region2; var max_block_height = 12; var max_pvalue = 1e-10; // calculate the height of the block block_height = (site_pvalue < max_pvalue ? max_block_height : (Math.log(site_pvalue) / Math.log(max_pvalue)) * max_block_height); if (block_height < 1) block_height = 1; // create a block to represent the motif block = document.createElement("div"); block.className = "block_motif" + (site_secondary ? " scanned_site" : "") + (site_rc ? " bottom" : " top"); block.style.left = ((site_pos / max_len) * 100) + "%"; block.style.top = (!site_rc ? max_block_height - block_height : max_block_height + 1) + "px"; block.style.width = ((site_len / max_len) * 100) + "%"; block.style.height = block_height + "px"; block.style.backgroundColor = block_colour(site_colour_index); block.setAttribute("data-colour-index", site_colour_index); // add to container container.appendChild(block); var activator = function (e) { toggle_class(block, "active", true); var new_e = new e.constructor(e.type, e); block.dispatchEvent(new_e); }; var deactivator = function (e) { toggle_class(block, "active", false); var new_e = new e.constructor(e.type, e); block.dispatchEvent(new_e); } // create a larger region to detect mouseover for the block block_region1 = document.createElement("div"); block_region1.className = "block_region top" + (site_secondary ? " scanned_site" : "") + (site_rc ? "" : " main"); block_region1.style.left = block.style.left; block_region1.style.width = block.style.width; block_region1.addEventListener('mouseover', activator, false); block_region1.addEventListener('mouseout', deactivator, false); container.appendChild(block_region1); block_region2 = document.createElement("div"); block_region2.className = "block_region bottom" + (site_secondary ? " scanned_site" : "") + (site_rc ? " main" : ""); block_region2.style.left = block.style.left; block_region2.style.width = block.style.width; block_region2.addEventListener('mouseover', activator, false); block_region2.addEventListener('mouseout', deactivator, false); container.appendChild(block_region2); return block; } function set_block_needle_positions(containingNode, start, end) { var container, lneedle, llabel, rneedle, rlabel, max, off, left, right; container = (/\bblock_container\b/.test(containingNode.className) ? containingNode : containingNode.querySelector(".block_container")); max = parseInt(container.getAttribute("data-max"), 10); off = parseInt(container.getAttribute("data-off"), 10); left = start - off; right = end - off; lneedle = containingNode.querySelector(".block_needle.left"); llabel = lneedle.querySelector(".block_handle.left"); rneedle = containingNode.querySelector(".block_needle.right"); rlabel = rneedle.querySelector(".block_handle.right"); // update the needle positions lneedle.style.left = "" + (left / max * 100) + "%"; llabel.textContent = "" + (left + off + 1); rneedle.style.left = "" + (right / max * 100) + "%"; rlabel.textContent = "" + (right + off); } function get_block_needle_positions(containingNode) { var container, llabel, rlabel, max, off, left, right; container = (/\bblock_container\b/.test(containingNode.className) ? containingNode : containingNode.querySelector(".block_container")); max = parseInt(container.getAttribute("data-max"), 10); off = parseInt(container.getAttribute("data-off"), 10); llabel = containingNode.querySelector(".block_needle.left > .block_handle.left"); rlabel = containingNode.querySelector(".block_needle.right > .block_handle.right"); left = parseInt(llabel.textContent, 10) - off - 1; right = parseInt(rlabel.textContent, 10) - off; return {"start": left + off, "end": right + off}; } </script> <script> function make_alpha_bg_table(alph, freqs) { function colour_symbol(index) { var span = document.createElement("span"); span.appendChild(document.createTextNode(alph.get_symbol(index))); span.style.color = alph.get_colour(index); span.className = "alpha_symbol"; return span; } var table, thead, tbody, row, th, span, i; // create table table = document.createElement("table"); table.className = "alpha_bg_table"; // create header thead = document.createElement("thead"); table.appendChild(thead); row = thead.insertRow(thead.rows.length); if (alph.has_complement()) { add_text_header_cell(row, "Name", "pop_alph_name"); if (freqs != null) add_text_header_cell(row, "Freq.", "pop_alph_freq"); if (alph.has_bg()) add_text_header_cell(row, "Bg.", "pop_alph_bg"); add_text_header_cell(row, ""); add_text_header_cell(row, ""); add_text_header_cell(row, ""); if (alph.has_bg()) add_text_header_cell(row, "Bg.", "pop_alph_bg"); if (freqs != null) add_text_header_cell(row, "Freq.", "pop_alph_freq"); add_text_header_cell(row, "Name", "pop_alph_name"); } else { add_text_header_cell(row, ""); add_text_header_cell(row, "Name", "pop_alph_name"); if (freqs != null) add_text_header_cell(row, "Freq.", "pop_alph_freq"); if (alph.has_bg()) add_text_header_cell(row, "Bg.", "pop_alph_bg"); } // add alphabet entries tbody = document.createElement("tbody"); table.appendChild(tbody); if (alph.has_complement()) { for (i = 0; i < alph.get_size_core(); i++) { var c = alph.get_complement(i); if (i > c) continue; row = tbody.insertRow(tbody.rows.length); add_text_cell(row, alph.get_name(i)); if (freqs != null) add_text_cell(row, "" + freqs[i].toFixed(3)); if (alph.has_bg()) add_text_cell(row, "" + alph.get_bg_freq(i).toFixed(3)); add_cell(row, colour_symbol(i)); add_text_cell(row, "~"); add_cell(row, colour_symbol(c)); if (alph.has_bg()) add_text_cell(row, "" + alph.get_bg_freq(c).toFixed(3)); if (freqs != null) add_text_cell(row, "" + freqs[c].toFixed(3)); add_text_cell(row, alph.get_name(c)); } } else { for (i = 0; i < alph.get_size_core(); i++) { row = tbody.insertRow(tbody.rows.length); add_cell(row, colour_symbol(i)); add_text_cell(row, alph.get_name(i)); if (freqs != null) add_text_cell(row, "" + freqs[i].toFixed(3)); if (alph.has_bg()) add_text_cell(row, "" + alph.get_bg_freq(i).toFixed(3)); } } return table; } </script> <script> var current_motif = 0; var meme_alphabet = new Alphabet(data.alphabet, data.background.freqs); var DelayLogoTask = function(logo, canvas) { this.logo = logo; this.canvas = canvas; }; DelayLogoTask.prototype.run = function () { draw_logo_on_canvas(this.logo, this.canvas, false); }; function motif_pspm(index) { var motif, pwm, psm, name, ltrim, rtrim, nsites, evalue; // get motif motif = data["motifs"][index]; // get motif paramters pwm = motif["pwm"]; psm = motif["psm"]; name = "" + (index + 1); ltrim = 0; rtrim = 0; nsites = motif["nsites"]; evalue = motif["evalue"]; // make pspm return new Pspm(pwm, name, ltrim, rtrim, nsites, evalue, psm); } function motif_count_matrix(index) { return motif_pspm(index).as_count_matrix(); } function motif_prob_matrix(index) { return motif_pspm(index).as_probability_matrix(); } function motif_minimal_meme(index) { return motif_pspm(index).as_meme({ "with_header": true, "with_pspm": true, "with_pssm": true, "version": data["version"], "alphabet": meme_alphabet, "strands": (meme_alphabet.has_complement() && data.options.revcomp ? 2 : 1) }); } function motif_fasta(index) { "use strict"; var motif, sites, site, seq, sequences, sequence, i, num, counter, out; counter = {}; sequences = data["sequence_db"]["sequences"]; motif = data["motifs"][index]; sites = motif["sites"]; out = ""; for (i = 0; i < sites.length; i++) { site = sites[i]; seq = site["seq"]; sequence = sequences[seq]; counter[seq] = (num = counter[seq]) ? (++num) : (num = 1); // inc counter if (i !== 0) {out += "\n";} out += ">" + sequence["name"] + "_site_" + num + " offset= " + site["pos"] + (site["rc"] ? " RC\n" : "\n"); out += site["match"]; } return out; } function motif_raw(index) { "use strict"; var sites, i, out; sites = data["motifs"][index]["sites"]; out = ""; for (i = 0; i < sites.length; i++) { if (i !== 0) {out += "\n";} out += sites[i]["match"]; } return out; } function clone_template(template) { "use strict"; var node, help_btns, i, button; node = $(template).cloneNode(true); toggle_class(node, "template", false); node.id = ""; help_btns = node.querySelectorAll(".help"); for (i = 0; i < help_btns.length; i++) { button = help_btns[i]; if (button.hasAttribute("data-topic")) { button.tabIndex = "0"; button.addEventListener("click", __toggle_help, false); button.addEventListener("keydown", __toggle_help, false); } } return node; } function set_tvar(template, tvar, value) { var node; node = find_child(template, tvar); if (node === null) { throw new Error("Template does not contain variable " + tvar); } node.innerHTML = ""; if (typeof value !== "object") { node.appendChild(document.createTextNode(value)); } else { node.appendChild(value); } } function make_logo(alphabet, pspm, rc, offset, className) { if (rc) pspm = pspm.copy().reverse_complement(alphabet); var logo = new Logo(alphabet, ""); logo.add_pspm(pspm, offset); var canvas = document.createElement('canvas'); canvas.height = 50; canvas.width = 0; canvas.className = className; size_logo_on_canvas(logo, canvas, false); add_draw_task(canvas, new DelayLogoTask(logo, canvas)); return canvas; } function make_small_logo(alphabet, pspm, options) { if (typeof options === "undefined") options = {}; if (options.rc) pspm = pspm.copy().reverse_complement(alphabet); var logo = new Logo(alphabet, {x_axis: false, y_axis: false}); logo.add_pspm(pspm, (typeof options.offset === "number" ? options.offset : 0)); var canvas = document.createElement('canvas'); if (typeof options.className === "string") canvas.className = options.className; if (typeof options.width === "number" && options.width > 0) { canvas.height = 0; canvas.width = options.width; draw_logo_on_canvas(logo, canvas, false); } else { draw_logo_on_canvas(logo, canvas, false, 1/3); } return canvas; } function make_large_logo(alphabet, pspm, rc, offset, className) { if (rc) pspm = pspm.copy().reverse_complement(alphabet); var logo = new Logo(alphabet, ""); logo.add_pspm(pspm, offset); var canvas = document.createElement('canvas'); canvas.height = 200; canvas.width = 0; canvas.className = className; size_logo_on_canvas(logo, canvas, false); add_draw_task(canvas, new DelayLogoTask(logo, canvas)); return canvas; } function make_sym_btn(symbol, title, action) { var box; box = document.createElement("div"); box.tabIndex = 0; box.className = "sym_btn"; box.appendChild(document.createTextNode(symbol)); box.title = title; box.addEventListener('click', action, false); box.addEventListener('keydown', action, false); return box; } function make_seq(alphabet, seq) { var i, j, letter, lbox, sbox; sbox = document.createElement("span"); for (i = 0; i < seq.length; i = j) { letter = seq.charAt(i); for (j = i+1; j < seq.length; j++) { if (seq.charAt(j) !== letter) { break; } } lbox = document.createElement("span"); lbox.style.color = alphabet.get_colour(alphabet.get_index(letter)); lbox.appendChild(document.createTextNode(seq.substring(i, j))); sbox.appendChild(lbox); } return sbox; } // // make_pv_text // // Returns the string p-value, with the p italicised. /// function make_pv_text() { var pv_text = document.createElement("span"); var pv_italic_text = document.createElement("span"); pv_italic_text.appendChild(document.createTextNode("p")); pv_italic_text.style.fontStyle = "italic"; pv_text.appendChild(pv_italic_text); pv_text.appendChild(document.createTextNode("-value")); return pv_text; } function append_site_entries(tbody, motif, site_index, count) { "use strict"; var i, end; var sites, site, sequences, sequence; var rbody; if (typeof count !== "number") { count = 20; } sequences = data["sequence_db"]["sequences"]; sites = motif["sites"]; end = Math.min(site_index + count, sites.length); for (i = site_index; i < end; i++) { site = sites[i]; sequence = sequences[site["seq"]]; rbody = tbody.insertRow(tbody.rows.length); add_text_cell(rbody, "" + (site["seq"] + 1) + ".", "site_num"); add_text_cell(rbody, sequence["name"], "site_name"); add_text_cell(rbody, site["rc"] ? "-" : "+", "site_strand"); add_text_cell(rbody, site["pos"] + 1, "site_start"); add_text_cell(rbody, site["pvalue"].toExponential(2), "site_pvalue"); add_text_cell(rbody, site["lflank"], "site lflank"); add_cell(rbody, make_seq(meme_alphabet, site["match"]), "site match"); add_text_cell(rbody, site["rflank"], "site rflank"); } return i; } function make_site_entries() { "use strict"; var region; region = this; if (region.data_site_index >= region.data_motif["sites"].length) { // all sites created region.removeEventListener('scroll', make_site_entries, false); return; } // if there's still 100 pixels to scroll than don't do anything yet if (region.scrollHeight - (region.scrollTop + region.offsetHeight) > 100) { return; } region.data_site_index = append_site_entries( find_child(region, "sites_tbl").tBodies[0], region.data_motif, region.data_site_index, 20 ); } function make_sites(motif) { "use strict"; function add_site_header(row, title, nopad, help_topic, tag_class) { var div, divcp, th; th = document.createElement("th"); div = document.createElement("div"); div.className = "sites_th_inner"; if (typeof title !== "object") { title = document.createTextNode("" + title); } div.appendChild(title); if (help_topic) { div.appendChild(document.createTextNode("\xA0")); div.appendChild(help_button(help_topic)); } divcp = div.cloneNode(true); divcp.className = "sites_th_hidden"; th.appendChild(div); th.appendChild(divcp); if (nopad) { th.className = "nopad"; } if (tag_class) { th.className += " " + tag_class; } row.appendChild(th); } var outer_tbl, inner_tbl, tbl, thead, tbody, rhead; outer_tbl = document.createElement("div"); outer_tbl.className = "sites_outer"; inner_tbl = document.createElement("div"); inner_tbl.className = "sites_inner"; outer_tbl.appendChild(inner_tbl); tbl = document.createElement("table"); tbl.className = "sites_tbl"; inner_tbl.appendChild(tbl); thead = document.createElement("thead"); tbl.appendChild(thead); tbody = document.createElement("tbody"); tbl.appendChild(tbody); rhead = thead.insertRow(thead.rows.length); add_site_header(rhead, "", true); add_site_header(rhead, "Name", false, "pop_seq_name"); add_site_header(rhead, "Strand", false, "pop_site_strand", "site_strand_title"); add_site_header(rhead, "Start", false, "pop_site_start"); add_site_header(rhead, make_pv_text(), false, "pop_site_pvalue"); add_site_header(rhead, "", false); add_site_header(rhead, "Sites", true, "pop_site_match"); add_site_header(rhead, "", false); inner_tbl.data_motif = motif; inner_tbl.data_site_index = append_site_entries(tbody, motif, 0, 20); if (inner_tbl.data_site_index < motif["sites"].length) { inner_tbl.addEventListener('scroll', make_site_entries, false); } return outer_tbl; } function make_motif_table_entry(row, alphabet, ordinal, motif, colw) { "use strict"; function ev_sig(evalue_str) { "use strict"; var ev_re, match, sig, exp, num; ev_re = /^(.*)e(.*)$/; if (match = ev_re.exec(evalue_str)) { sig = parseFloat(match[1]); exp = parseInt(match[2]); if (exp >= 0) { return false; } else if (exp <= -3) { return true; } else { return sig * Math.pow(10, exp) <= 0.05; } } return true; } function make_preview(alphabet, motif) { "use strict"; var pspm, preview, preview_rc; var box, btn_box, logo_box, btn_plus, btn_minus; if (motif["preview_logo"]) { preview = motif["preview_logo"]; preview_rc = motif["preview_logo_rc"]; } else { pspm = new Pspm(motif["pwm"]); preview = make_logo(alphabet, pspm); motif["preview_logo"] = preview; if (alphabet.has_complement()) { preview_rc = make_logo(alphabet, pspm, true, 0, "logo_rc"); motif["preview_logo_rc"] = preview_rc; } } if (preview_rc) { btn_plus = document.createElement("div"); btn_plus.appendChild(document.createTextNode("+")); btn_plus.className = "preview_btn plus"; btn_plus.tabIndex = "0"; btn_plus.addEventListener("click", action_btn_rc, false); btn_plus.addEventListener("keydown", action_btn_rc, false); btn_minus = document.createElement("div"); btn_minus.appendChild(document.createTextNode("-")); btn_minus.className = "preview_btn minus"; btn_minus.tabIndex = "0"; btn_minus.addEventListener("click", action_btn_rc, false); btn_minus.addEventListener("keydown", action_btn_rc, false); btn_box = document.createElement("div"); btn_box.className = "preview_btn_box"; btn_box.appendChild(btn_plus); btn_box.appendChild(btn_minus); } logo_box = document.createElement("div"); logo_box.className = "preview_logo_box"; logo_box.appendChild(preview); if (preview_rc) logo_box.appendChild(preview_rc); box = document.createElement("div"); box.className = "preview_box"; if (preview_rc) box.appendChild(btn_box); box.appendChild(logo_box); if (preview_rc) { if (motif["rc"]) { btn_minus.className += " active"; logo_box.className += " show_rc_logo"; } else { btn_plus.className += " active"; } } return box; } var pspm, preview, preview_rc, c; row.data_motif = motif; row.data_ordinal = ordinal; if (!ev_sig(motif["evalue"])) { row.style.opacity = 0.4; } add_text_cell(row, "" + ordinal + ".", "motif_ordinal"); add_cell(row, make_preview(alphabet, motif), "motif_logo"); add_text_cell(row, motif["evalue"], "motif_evalue"); add_text_cell(row, motif["nsites"], "motif_nsites"); add_text_cell(row, motif["len"], "motif_width"); add_cell(row, make_sym_btn("\u21A7", "Show more information.", action_show_more), "motif_more"); add_cell(row, make_sym_btn("\u21E2", "Submit the motif to another MEME Suite program or download it.", action_show_outpop), "motif_submit"); if (colw) { for (c = 0; c < row.cells.length; c++) { row.cells[c].style.minWidth = colw[c] + "px"; } } } function make_motifs_table(alphabet, start_ordinal, motifs, colw, stop_reason) { var i, j; var tbl, thead, tbody, tfoot, row, preview; var motif, pspm; tbl = document.createElement("table"); thead = document.createElement("thead"); tbl.appendChild(thead); tbody = document.createElement("tbody"); tbl.appendChild(tbody); tfoot = document.createElement("tfoot"); tbl.appendChild(tfoot); row = thead.insertRow(thead.rows.length); add_text_header_cell(row, "", "", "motif_ordinal"); add_text_header_cell(row, "Logo", "", "motif_logo"); add_text_header_cell(row, "E-value", "pop_ev", "motif_evalue"); add_text_header_cell(row, "Sites", "pop_sites", "motif_nsites"); add_text_header_cell(row, "Width", "pop_width", "motif_width"); add_text_header_cell(row, "More", "pop_more", "motif_more"); add_text_header_cell(row, "Submit/Download", "pop_submit_dl", "motif_submit"); for (i = 0; i < motifs.length; i++) { row = tbody.insertRow(tbody.rows.length); make_motif_table_entry(row, alphabet, start_ordinal + i, motifs[i], colw); } row = tfoot.insertRow(tfoot.rows.length); add_text_header_cell(row, stop_reason, "", "stop_reason", "", 6); return tbl; } function make_expanded_motif(alphabet, ordinal, motif, less_x, submit_x) { "use strict"; var box, pspm, logo_box, large_logo, large_logo_rc, tab_logo, tab_logo_rc; var btn, offset, norc; box = clone_template("tmpl_motif_expanded"); box.data_motif = motif; box.data_ordinal = ordinal; pspm = new Pspm(motif["pwm"]); if (typeof motif["rc"] !== "boolean") { motif["rc"] = false; } if (motif["large_logo"]) { large_logo = motif["large_logo"]; large_logo_rc = motif["large_logo_rc"]; } else { large_logo = make_large_logo(alphabet, pspm, false, 0); motif["large_logo"] = large_logo; if (alphabet.has_complement()) { large_logo_rc = make_large_logo(alphabet, pspm, true, 0, "logo_rc"); motif["large_logo_rc"] = large_logo_rc; } } norc = (large_logo_rc == null); toggle_class(box, "norc", norc); logo_box = find_child(box, "tvar_logo"); logo_box.appendChild(large_logo); if (large_logo_rc) logo_box.appendChild(large_logo_rc); toggle_class(logo_box, "show_rc_logo", motif["rc"]); tab_logo = find_child(box, "tvar_tab"); tab_logo_rc = find_child(box, "tvar_tab_rc"); toggle_class(tab_logo, "activeTab", !motif["rc"]); toggle_class(tab_logo_rc, "activeTab", motif["rc"]); tab_logo.addEventListener('click', action_rc_tab, false); tab_logo.addEventListener('keydown', action_rc_tab, false); tab_logo_rc.addEventListener('click', action_rc_tab, false); tab_logo_rc.addEventListener('keydown', action_rc_tab, false); set_tvar(box, "tvar_ordinal", ordinal); set_tvar(box, "tvar_evalue", motif["evalue"]); set_tvar(box, "tvar_width", motif["len"]); set_tvar(box, "tvar_site_count", motif["nsites"]); set_tvar(box, "tvar_llr", motif["llr"]); set_tvar(box, "tvar_ic", motif["ic"]); set_tvar(box, "tvar_re", motif["re"]); set_tvar(box, "tvar_bt", motif["bt"]); set_tvar(box, "tvar_sites", make_sites(motif)); offset = 32; // 1* 5px padding + 2 * 10px padding + 2 * 2px border + 3px ?? btn = find_child(box, "tvar_less"); btn.style.left = (less_x - offset) + "px"; btn.addEventListener('click', action_show_less, false); btn.addEventListener('keydown', action_show_less, false); btn = find_child(box, "tvar_submit"); btn.style.left = (submit_x - offset) + "px"; btn.addEventListener('click', action_show_outpop, false); btn.addEventListener('keydown', action_show_outpop, false); return box; } // // /// function make_motifs() { "use strict"; function pixel_value(str_in) { "use strict"; var px_re, match; px_re = /^(\d+)px$/; if (match = px_re.exec(str_in)) { return parseInt(match[1], 10); } return 0; } var container, tbl; var colw, r, row, c, cell, cell_style, pad_left, pad_right; // make the motifs table container = $("motifs"); container.innerHTML = ""; // clear content tbl = make_motifs_table(meme_alphabet, 1, data["motifs"], colw, data["stop_reason"]); container.appendChild(tbl); // measure table column widths colw = []; row = tbl.tBodies[0].rows[0]; for (c = 0; c < row.cells.length; c++) { var padLeft, padRight; cell = row.cells[c]; cell_style = window.getComputedStyle(cell, null); pad_left = pixel_value(cell_style.getPropertyValue("padding-left")); pad_right = pixel_value(cell_style.getPropertyValue("padding-right")); colw[c] = cell.clientWidth - pad_left - pad_right; if (typeof colw[c] !== "number" || colw[c] < 0) { colw[c] = 1; } } // set minimum table column widths on each row so later when we remove rows it still aligns for (r = 0; r < tbl.tBodies[0].rows.length; r++) { row = tbl.tBodies[0].rows[r]; for (c = 0; c < row.cells.length; c++) { row.cells[c].style.minWidth = colw[c] + "px"; } } // store the table column widths so we can create rows latter with the same minimums container.data_colw = colw; // calculate the x offset for the buttons row = tbl.tBodies[0].rows[0]; container.data_more_x = coords(find_child(find_child(row, "motif_more"), "sym_btn"))[0]; container.data_submit_x = coords(find_child(find_child(row, "motif_submit"), "sym_btn"))[0]; draw_on_screen(); } function make_meme_block(container, max_seq_len, is_scan, site) { "use strict"; var motif = data.motifs[site.motif]; var block = make_block(container, max_seq_len, site.pos, motif.len, site.pvalue, site.rc, site.motif, is_scan); var handler = (is_scan ? make_scan_popup(site, motif, block) : make_block_popup(site, motif, block)); block.addEventListener("mouseover", handler, false); block.addEventListener("mouseout", handler, false); } function append_blocks_entries(tbody, seq_index, count) { "use strict"; var i, end, j; var max_pvalue, max_block_height, max_seq_len, sequences; var sequence, sites, scans, scan; var container, plus, minus, rule, row; // define some constants max_seq_len = data.sequence_db.max_length; // determine how many to load end = Math.min(seq_index + count, data.sequence_db.sequences.length); for (i = seq_index; i < end; i++) { // get the sequence sequence = data.sequence_db.sequences[i]; // make the containers for the block diagram container = make_block_container(meme_alphabet.has_complement(), data.options.revcomp, max_seq_len, sequence.length); // create blocks for the motif sites sites = sequence["sites"]; for (j = 0; j < sites.length; j++) make_meme_block(container, max_seq_len, false, sites[j]); // create blocks for the scanned sites scan = data.scan[i]; for (j = 0; j < scan.sites.length; j++) make_meme_block(container, max_seq_len, true, scan.sites[j]); // create a row for the sequence row = tbody.insertRow(tbody.rows.length); toggle_class(row, "empty_seq", sites.length == 0 && scan.sites.length == 0); toggle_class(row, "only_scan", sites.length == 0 && scan.sites.length > 0); add_text_cell(row, (i + 1) + ".", "blockdiag_num"); add_text_cell(row, sequence["name"], "blockdiag_name"); add_text_cell(row, scan["pvalue"].toExponential(2), "blockdiag_pvalue"); add_cell(row, container, "block_td"); } return end; } function make_blocks_entries() { "use strict"; var region; region = this; if (region.data_blocks_index >= data["sequence_db"]["sequences"].length) { // all sites created region.removeEventListener('scroll', make_blocks_entries, false); return; } // if there's still 100 pixels to scroll than don't do anything yet if (region.scrollHeight - (region.scrollTop + region.offsetHeight) > 100) { return; } region.data_blocks_index = append_blocks_entries( find_child(region, "blocks_tbl").tBodies[0], region.data_blocks_index, 20 ); } function make_blocks() { "use strict"; function add_seqs_filter(container, id, checked, label_text, help_topic) { "use strict"; var label, radio; radio = document.createElement("input"); radio.type = "radio"; radio.name = "seqs_display"; radio.id = id; radio.checked = checked; radio.addEventListener('click', action_seqs_filter, false); label = document.createElement("label"); label.appendChild(document.createTextNode(label_text)); label.htmlFor = id; container.appendChild(radio); container.appendChild(label); if (help_topic) { container.appendChild(document.createTextNode("\xA0")); container.appendChild(help_button(help_topic)); } } function add_blocks_header(row, title, nopad, help_topic) { "use strict"; var div, divcp, th; th = document.createElement("th"); div = document.createElement("div"); div.className = "blocks_th_inner"; if (typeof title !== "object") { title = document.createTextNode("" + title); } div.appendChild(title); if (help_topic) { div.appendChild(document.createTextNode("\xA0")); div.appendChild(help_button(help_topic)); } divcp = div.cloneNode(true); divcp.className = "blocks_th_hidden"; th.appendChild(div); th.appendChild(divcp); if (nopad) { th.className = "nopad"; } row.appendChild(th); } var container; var page, view_height, outer_tbl, inner_tbl, tbl, thead, tbody, rhead; var in_view, i, seq_count; page = (document.compatMode === "CSS1Compat") ? document.documentElement : document.body; view_height = Math.max(page.clientHeight - 300, 300); container = $("blocks"); toggle_class(container, "hide_empty_seqs", true); toggle_class(container, "hide_only_scan", true); container.innerHTML = ""; add_seqs_filter(container, "rdo_sites_only", true, "Only Motif Sites", "pop_motif_sites"); add_seqs_filter(container, "rdo_sites_and_scan", false, "Motif Sites+Scanned Sites", "pop_scanned_sites"); add_seqs_filter(container, "rdo_all_seqs", false, "All Sequences", "pop_all_sequences"); outer_tbl = document.createElement("div"); outer_tbl.className = "blocks_outer"; inner_tbl = document.createElement("div"); inner_tbl.id = "blocks_scroll"; inner_tbl.className = "blocks_inner"; inner_tbl.style.maxHeight = view_height + "px"; outer_tbl.appendChild(inner_tbl); tbl = document.createElement("table"); tbl.className = "blocks_tbl"; inner_tbl.appendChild(tbl); thead = document.createElement("thead"); tbl.appendChild(thead); tbody = document.createElement("tbody"); tbl.appendChild(tbody); rhead = thead.insertRow(thead.rows.length); add_blocks_header(rhead, "", true); add_blocks_header(rhead, "Name", false, "pop_seq_name"); add_blocks_header(rhead, make_pv_text(), false, "pop_seq_pvalue"); add_blocks_header(rhead, "Motif Location", false, "pop_motif_location"); container.appendChild(outer_tbl); seq_count = data["sequence_db"]["sequences"].length; in_view = Math.max(Math.ceil(view_height / 25), 1); i = append_blocks_entries(tbody, 0, in_view); while (i < seq_count && inner_tbl.scrollHeight - (inner_tbl.scrollTop + inner_tbl.offsetHeight) < 400) { i = append_blocks_entries(tbody, i, 20); } inner_tbl.data_blocks_index = i; if (i < seq_count) { inner_tbl.addEventListener('scroll', make_blocks_entries, false); } } function make_scan_popup(site, motif) { return function (e) { "use strict"; var pop, xy, padding, edge_padding, pop_left, pop_top, page_width; var lflank, match, rflank, pspm; if (!e) var e = window.event; pop = make_scan_popup.pop; if (e.type === "mouseover") { if (pop) return; pop = clone_template("tmpl_scan_info"); pspm = new Pspm(motif.pwm); if (site.rc) pspm.reverse_complement(meme_alphabet); set_tvar(pop, "tvar_logo", make_small_logo(meme_alphabet, pspm, {"className": "scan_logo"})); set_tvar(pop, "tvar_motif", motif.id); set_tvar(pop, "tvar_pvalue", site.pvalue.toExponential(2)); set_tvar(pop, "tvar_start", site.pos + 1); set_tvar(pop, "tvar_end", site.pos + motif.len); document.body.appendChild(pop); position_popup(this, pop); make_scan_popup.pop = pop; } else if (e.type === "mouseout") { if (pop) { pop.parentNode.removeChild(pop); make_scan_popup.pop = null; } } }; } function make_block_popup(site, motif, block) { return function (e) { "use strict"; var pop; var lflank, match, rflank, pspm, ruler, match_seq, match_width; if (!e) var e = window.event; pop = make_block_popup.pop; if (e.type === "mouseover") { if (pop) return; pop = clone_template("tmpl_block_info"); pspm = new Pspm(motif.pwm); if (site.rc) { // must be dna pspm.reverse_complement(meme_alphabet); lflank = meme_alphabet.invcomp_seq(site.rflank); match = meme_alphabet.invcomp_seq(site.match); rflank = meme_alphabet.invcomp_seq(site.lflank); } else { lflank = site.lflank; match = site.match; rflank = site.rflank; } ruler = document.getElementById("measure_match"); match_seq = make_seq(meme_alphabet, match); ruler.innerHTML = ""; ruler.appendChild(match_seq); match_width = ruler.clientWidth; ruler.removeChild(match_seq); set_tvar(pop, "tvar_lflank", lflank); set_tvar(pop, "tvar_match", match_seq); set_tvar(pop, "tvar_rflank", rflank); set_tvar(pop, "tvar_logo_pad", lflank); set_tvar(pop, "tvar_logo", make_small_logo(meme_alphabet, pspm, {"width": match_width})); set_tvar(pop, "tvar_motif", motif.id); set_tvar(pop, "tvar_pvalue", site.pvalue.toExponential(2)); set_tvar(pop, "tvar_start", site.pos + 1); set_tvar(pop, "tvar_end", site.pos + motif.len); document.body.appendChild(pop); position_popup(block, pop); make_block_popup.pop = pop; } else if (e.type === "mouseout") { if (pop) { pop.parentNode.removeChild(pop); make_block_popup.pop = null; } } }; } function update_outpop_format(index) { switch(parseInt($("text_format").value)) { case 0: // count matrix $("outpop_text").value = motif_count_matrix(index); $("text_name").value = "motif_" + (index + 1) + "_counts.txt"; break; case 1: // prob matrix $("outpop_text").value = motif_prob_matrix(index); $("text_name").value = "motif_" + (index + 1) + "_freqs.txt"; break; case 2: // minimal meme $("outpop_text").value = motif_minimal_meme(index); $("text_name").value = "motif_" + (index + 1) + ".txt"; break; case 3: // fasta $("outpop_text").value = motif_fasta(index); $("text_name").value = "motif_" + (index + 1) + "_fasta.txt"; break; case 4: // raw $("outpop_text").value = motif_raw(index); $("text_name").value = "motif_" + (index + 1) + "_raw.txt"; break; default: throw new Error("Unknown motif format"); } } function update_outpop_motif(index) { "use strict"; var motifs, motif, pspm, logo, canvas, num; motifs = data["motifs"]; if (index < 0 || index >= motifs.length) {return;} current_motif = index; motif = motifs[index]; pspm = new Pspm(motif["pwm"]); logo = new Logo(meme_alphabet, ""); logo.add_pspm(pspm, 0); canvas = $("outpop_logo"); canvas.width = canvas.width; // clear canvas draw_logo_on_canvas(logo, canvas, false); if (meme_alphabet.has_complement()) { pspm.reverse_complement(meme_alphabet); logo = new Logo(meme_alphabet, ""); canvas = $("outpop_logo_rc"); canvas.width = canvas.width; // clear canvas draw_logo_on_canvas(logo, canvas, false); } num = $("outpop_num"); num.innerHTML = ""; num.appendChild(document.createTextNode("" + (index + 1))); update_outpop_format(index); } // // action_show_more // // Show more information on the motif. /// function action_show_more(e) { var node, tr, tbody, table, container, motif, ordinal; var expanded_motif; if (!e) e = window.event; if (e.type === "keydown") { if (e.keyCode !== 13 && e.keyCode !== 32) { return; } // stop a submit or something like that e.preventDefault(); } // find the row that contains the cell node = this; do { if (node.tagName === "TR") break; } while (node = node.parentNode); if (!node) throw new Error("Expected to find row!?"); tr = node; // get info motif = tr.data_motif; ordinal = tr.data_ordinal; // find tbody do { if (node.tagName === "TBODY") break; } while (node = node.parentNode); if (!node) throw new Error("Expected to find tbody!?"); tbody = node; // find table do { if (node.tagName === "TABLE") break; } while (node = node.parentNode); if (!node) throw new Error("Expected to find table!?"); table = node; // find container container = node.parentNode; // make a expanded motif motif["expanded"] = true; expanded_motif = make_expanded_motif(meme_alphabet, ordinal, motif, container.data_more_x, container.data_submit_x); // now determine how to place it if (tbody.rows.length === 1) { // only us in the table so the table can be replaced container.replaceChild(expanded_motif, table); } else if (tbody.rows[0] === tr) { // first row, so remove and insert an expanded motif before table.deleteRow(tr.rowIndex); container.insertBefore(expanded_motif, table); } else if (tbody.rows[tbody.rows.length -1] === tr) { // last row, so remove and insert an expanded motif after table.deleteRow(tr.rowIndex); container.insertBefore(expanded_motif, table.nextSibling); } else { var table2, tbody2; table2 = table.cloneNode(false); table2.appendChild(table.tHead.cloneNode(true)); tbody2 = table.tBodies[0].cloneNode(false); table2.appendChild(tbody2); container.insertBefore(table2, table.nextSibling); for (i = tbody.rows.length - 1; i >= 0; i--) { row = tbody.rows[i]; row.parentNode.removeChild(row); if (row === tr) { break; } tbody2.insertBefore(row, tbody2.rows[0]); } container.insertBefore(expanded_motif, table2); } find_child(expanded_motif, "tvar_less").focus(); } // // action_show_less // // Show less information on the motif. /// function action_show_less(e) { var btn; var expanded_motif, container, motif, ordinal, colw, focus_target; var table, tbody, tbody2, row, table_before, table_after; if (!e) e = window.event; if (e.type === "keydown") { if (e.keyCode !== 13 && e.keyCode !== 32) { return; } // stop a submit or something like that e.preventDefault(); } btn = this; // find expanded motif expanded_motif = find_parent(btn, "expanded_motif"); if (!expanded_motif) throw new Error("Expected expanded motif."); // find the container container = expanded_motif.parentNode; // get data motif = expanded_motif.data_motif; ordinal = expanded_motif.data_ordinal; colw = container.data_colw; // get the table before table_before = expanded_motif.previousSibling; if (table_before && table_before.tagName !== "TABLE") { table_before = null; } // get the table after table_after = expanded_motif.nextSibling; if (table_after && table_after.tagName !== "TABLE") { table_after = null; } // see if there is a table below or above that we can put this in. // if there is a table both below and above then add this motif and // all ones below to the above table motif["expanded"] = false; if (table_before && table_after) { tbody = table_before.tBodies[0]; row = tbody.insertRow(tbody.rows.length); make_motif_table_entry(row, meme_alphabet, ordinal, motif, colw); focus_target = find_child(row.cells[5], "sym_btn"); container.removeChild(expanded_motif); tbody2 = table_after.tBodies[0]; while (tbody2.rows.length > 0) { row = tbody2.rows[0]; row.parentNode.removeChild(row); tbody.appendChild(row); } container.removeChild(table_after); } else if (table_before) { tbody = table_before.tBodies[0]; row = tbody.insertRow(tbody.rows.length); make_motif_table_entry(row, meme_alphabet, ordinal, motif, colw); focus_target = find_child(row.cells[5], "sym_btn"); container.removeChild(expanded_motif); } else if (table_after) { tbody = table_after.tBodies[0]; row = tbody.insertRow(0); make_motif_table_entry(row, meme_alphabet, ordinal, motif, colw); focus_target = find_child(row.cells[5], "sym_btn"); container.removeChild(expanded_motif); } else { //no table above or below! // make a new table table = make_motifs_table(meme_alphabet, ordinal, [motif], colw, data["stop_reason"]); focus_target = find_child(table.tBodies[0].rows[0].cells[5], "sym_btn"); container.replaceChild(table, expanded_motif); } focus_target.focus(); } function action_show_outpop(e) { "use strict"; function init() { "use strict"; var close_btn, next_btn, prev_btn, cancel_btn, do_btn; var tab1, tab2, tab3; var pnl1, pnl2, pnl3; var format_list; var tbl_submit, inputs, i, default_prog; close_btn = $("outpop_close"); close_btn.addEventListener("click", action_hide_outpop, false); close_btn.addEventListener("keydown", action_hide_outpop, false); next_btn = $("outpop_next"); next_btn.addEventListener("click", action_outpop_next, false); next_btn.addEventListener("keydown", action_outpop_next, false); prev_btn = $("outpop_prev"); prev_btn.addEventListener("click", action_outpop_prev, false); prev_btn.addEventListener("keydown", action_outpop_prev, false); cancel_btn = $("outpop_cancel"); cancel_btn.addEventListener("click", action_hide_outpop, false); do_btn = $("outpop_do"); do_btn.addEventListener("click", action_outpop_submit, false); tab1 = $("outpop_tab_1"); tab1.tabIndex = 0; tab1.addEventListener("click", action_outpop_tab, false); tab1.addEventListener("keydown", action_outpop_tab, false); tab2 = $("outpop_tab_2"); tab2.tabIndex = 0; tab2.addEventListener("click", action_outpop_tab, false); tab2.addEventListener("keydown", action_outpop_tab, false); tab3 = $("outpop_tab_3"); tab3.tabIndex = 0; tab3.addEventListener("click", action_outpop_tab, false); tab3.addEventListener("keydown", action_outpop_tab, false); pnl1 = $("outpop_pnl_1"); pnl2 = $("outpop_pnl_2"); pnl3 = $("outpop_pnl_3"); toggle_class(tab1, "activeTab", true); toggle_class(tab2, "activeTab", false); toggle_class(tab3, "activeTab", false); pnl1.style.display = "block"; pnl2.style.display = "none"; pnl3.style.display = "none"; format_list = $("text_format"); format_list.addEventListener("change", action_outpop_format, false); // setup program selection tbl_submit = $("programs"); // when not dna, hide the inputs for programs that require dna motifs 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 // add a click listener for the radio buttons inputs = tbl_submit.querySelectorAll("input[type='radio']"); for (i = 0; i < inputs.length; i++) { inputs[i].addEventListener("click", action_outpop_program, false); } // ensure that a default program option is selected for DNA and Protein default_prog = document.getElementById(meme_alphabet.has_complement() ? "submit_tomtom" : "submit_fimo"); //TODO FIXME Tomtom might require a more strict definition of DNA default_prog.checked = true; action_outpop_program.call(default_prog); // disable reverse-complement when not DNA $("logo_rc_option").disabled = !meme_alphabet.has_complement(); // set errorbars on when ssc is on $("logo_ssc").addEventListener("change", action_outpop_ssc, false); } var node; // store the focused element action_hide_outpop.last_active = document.activeElement; if (!e) e = window.event; if (e.type === "keydown") { if (e.keyCode !== 13 && e.keyCode !== 32) { return; } // stop a submit or something like that e.preventDefault(); } // hide the help popup help_popup(); // on first load initilize the popup if (!action_show_outpop.ready) { init(); action_show_outpop.ready = true; } // load the motif logo node = this; do { if (/\bexpanded_motif\b/.test(node.className) || node.tagName === "TR") break; } while (node = node.parentNode); if (node === null) throw new Error("Expected node!"); update_outpop_motif(node.data_ordinal - 1); // display the download popup $("grey_out_page").style.display = "block"; $("download").style.display = "block"; $("outpop_close").focus(); } function action_hide_outpop(e) { if (!e) e = window.event; if (e.type === "keydown") { if (e.keyCode !== 13 && e.keyCode !== 32) { return; } // stop a submit or something like that e.preventDefault(); } $("download").style.display = "none"; $("grey_out_page").style.display = "none"; if (typeof action_hide_outpop.last_active !== "undefined") { action_hide_outpop.last_active.focus(); } } function action_outpop_next(e) { if (!e) e = window.event; if (e.type === "keydown") { if (e.keyCode !== 13 && e.keyCode !== 32) { return; } // stop a submit or something like that e.preventDefault(); } update_outpop_motif(current_motif + 1); } function action_outpop_prev(e) { if (!e) e = window.event; if (e.type === "keydown") { if (e.keyCode !== 13 && e.keyCode !== 32) { return; } // stop a submit or something like that e.preventDefault(); } update_outpop_motif(current_motif - 1); } function action_outpop_program() { "use strict"; var table, tr, rows, i; tr = find_parent_tag(this, "TR"); table = find_parent_tag(tr, "TABLE"); rows = table.querySelectorAll("tr"); for (i = 0; i < rows.length; i++) { toggle_class(rows[i], "selected", rows[i] === tr); } } function action_outpop_ssc() { "use strict"; $("logo_err").value = $("logo_ssc").value; } function action_outpop_submit(e) { "use strict"; var form, input, program, motifs; // find out which program is selected var radios, i; radios = document.getElementsByName("program"); program = "fimo"; // default to fimo, since it works with all alphabet types for (i = 0; i < radios.length; i++) { if (radios[i].checked) program = radios[i].value; } motifs = motif_minimal_meme(current_motif); form = document.createElement("form"); form.setAttribute("method", "post"); form.setAttribute("action", site_url + "/tools/" + program); input = document.createElement("input"); input.setAttribute("type", "hidden"); input.setAttribute("name", "motifs_embed"); input.setAttribute("value", motifs); form.appendChild(input); document.body.appendChild(form); form.submit(); document.body.removeChild(form); } function action_outpop_download_motif(e) { $("text_form").submit(); } function action_outpop_download_logo(e) { "use strict"; $("logo_motifs").value = motif_minimal_meme(current_motif); $("logo_form").submit(); } function action_btn_rc(e) { "use strict"; var node, tr, motif, box, logo_box, tab_st, tab_rc, rc; if (!e) e = window.event; if (e.type === "keydown") { if (e.keyCode !== 13 && e.keyCode !== 32) { return; } // stop a submit or something like that e.preventDefault(); } node = this; do { if (node.tagName === "TR") break; } while (node = node.parentNode); if (!node) throw new Error("Expected to find row!?"); tr = node; // get info motif = tr.data_motif; box = find_parent(this, "preview_box"); logo_box = find_child(box, "preview_logo_box"); tab_st = find_child(box, "plus"); tab_rc = find_child(box, "minus"); rc = (this === tab_rc); motif["rc"] = rc; toggle_class(logo_box, "show_rc_logo", rc); toggle_class(tab_st, "active", !rc); toggle_class(tab_rc, "active", rc); } function action_rc_tab(e) { "use strict"; var box, logo_box, tab_st, tab_rc, rc; if (!e) e = window.event; if (e.type === "keydown") { if (e.keyCode !== 13 && e.keyCode !== 32) { return; } // stop a submit or something like that e.preventDefault(); } box = find_parent(this, "expanded_motif"); logo_box = find_child(box, "tvar_logo"); tab_st = find_child(box, "tvar_tab"); tab_rc = find_child(box, "tvar_tab_rc"); rc = (this === tab_rc); box.data_motif["rc"] = rc; toggle_class(logo_box, "show_rc_logo", rc); toggle_class(tab_st, "activeTab", !rc); toggle_class(tab_rc, "activeTab", rc); } function action_outpop_tab(e) { "use strict"; var tab1, tab2, tab3, pnl1, pnl2, pnl3, do_btn; if (!e) e = window.event; if (e.type === "keydown") { if (e.keyCode !== 13 && e.keyCode !== 32) { return; } // stop a submit or something like that e.preventDefault(); } tab1 = $("outpop_tab_1"); tab2 = $("outpop_tab_2"); tab3 = $("outpop_tab_3"); pnl1 = $("outpop_pnl_1"); pnl2 = $("outpop_pnl_2"); pnl3 = $("outpop_pnl_3"); do_btn = $("outpop_do"); toggle_class(tab1, "activeTab", (this === tab1)); toggle_class(tab2, "activeTab", (this === tab2)); toggle_class(tab3, "activeTab", (this === tab3)); pnl1.style.display = ((this === tab1) ? "block" : "none"); pnl2.style.display = ((this === tab2) ? "block" : "none"); pnl3.style.display = ((this === tab3) ? "block" : "none"); do_btn.value = ((this === tab1) ? "Submit" : "Download"); do_btn.removeEventListener("click", action_outpop_submit, false); do_btn.removeEventListener("click", action_outpop_download_logo, false); do_btn.removeEventListener("click", action_outpop_download_motif, false); if (this === tab1) { do_btn.addEventListener("click", action_outpop_submit, false); } else if (this === tab2) { do_btn.addEventListener("click", action_outpop_download_motif, false); } else { do_btn.addEventListener("click", action_outpop_download_logo, false); } } function action_seqs_filter() { "use strict"; var block_container; block_container = $("blocks"); if ($("rdo_all_seqs").checked) { toggle_class(block_container, "hide_empty_seqs", false); toggle_class(block_container, "hide_only_scan", false); } else if ($("rdo_sites_and_scan").checked) { toggle_class(block_container, "hide_empty_seqs", true); toggle_class(block_container, "hide_only_scan", false); } else if ($("rdo_sites_only").checked) { toggle_class(block_container, "hide_empty_seqs", true); toggle_class(block_container, "hide_only_scan", true); } } function action_outpop_format() { update_outpop_format(current_motif); } // // page_loaded // // Called when the page has loaded for the first time. /// function page_loaded() { post_load_setup(); } // // page_loaded // // Called when a cached page is reshown. /// function page_shown(e) { if (e.persisted) post_load_setup(); } // // page_loaded // // Called when the page is resized /// function page_resized() { var page, blocks_scroll; update_scroll_pad(); page = (document.compatMode === "CSS1Compat") ? document.documentElement : document.body; blocks_scroll = $("blocks_scroll"); if (blocks_scroll) { blocks_scroll.style.maxHeight = Math.max(page.clientHeight - 300, 300) + "px"; } } // // pre_load_setup // // Run before the page is displayed /// function pre_load_setup() { var start, hue, sat, light, divisions; var i, j, motifs, motif, sites, site, sequences, sequence; var max_seq_len; motifs = data["motifs"]; sequences = data["sequence_db"]["sequences"]; max_seq_len = 1; for (i = 0; i < sequences.length; i++) { sequence = sequences[i]; sequence["sites"] = []; if (sequence["length"] > max_seq_len) { max_seq_len = sequence["length"]; } } data["sequence_db"]["max_length"] = max_seq_len; // use hsl colours start = 0; //red sat = 100; light = 50; for (i = 0; i < motifs.length; i++) { motif = motifs[i]; // give the motif a colour divisions = 1 << Math.ceil(Math.log(i + 1) / Math.LN2); hue = start + (360 / divisions) * ((i - (divisions >> 1)) * 2 + 1); motif["colour"] = "hsl(" + hue + ", " + sat + "%, " + light + "%)"; // associate sites with sequences as well // to make generating the block diagram easier sites = motif["sites"]; for (j = 0; j < sites.length; j++) { site = sites[j]; sequence = sequences[site["seq"]]; // record the motif index site["motif"] = i; // add the site to the sequence sequence["sites"].push(site); } } } // // post_load_setup // // Run when the page has loaded, or been reloaded. // function post_load_setup() { update_scroll_pad(); if (data["motifs"].length > 0) { make_motifs(); make_blocks(); } else { $("motifs").innerHTML = "<p>No significant motifs found!</p>"; // clear content $("motifs").innerHTML += "<p><b>" + data["stop_reason"] + "</b></p>"; $("blocks").innerHTML = "<p>No significant motifs found!</p>"; } } pre_load_setup(); </script> <style> /* The following is the content of meme.css */ body { background-color:white; font-size: 12px; font-family: Verdana, Arial, Helvetica, sans-serif;} div.help { display: inline-block; margin: 0px; padding: 0px; width: 12px; height: 13px; cursor: pointer; background-image: url(data:image/gif;base64,R0lGODlhDAANAIABANR0AP///yH5BAEAAAEALAAAAAAMAA0AAAIdhI8Xy22MIFgv1DttrrJ7mlGNNo4c+aFg6SQuUAAAOw==); } div.help:hover { background-image: url(data:image/gif;base64,R0lGODlhDAANAKEAANR0AP///9R0ANR0ACH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEAAAIALAAAAAAMAA0AAAIdDGynCe3PgoxONntvwqz2/z2K2ImjR0KhmSIZUgAAOw==); } p.spaced { line-height: 1.8em;} span.citation { font-family: "Book Antiqua", "Palatino Linotype", serif; color: #004a4d;} p.pad { padding-left: 30px; padding-top: 5px; padding-bottom: 10px;} td.jump { font-size: 13px; color: #ffffff; background-color: #00666a; font-family: Georgia, "Times New Roman", Times, serif;} a.jump { margin: 15px 0 0; font-style: normal; font-variant: small-caps; font-weight: bolder; font-family: Georgia, "Times New Roman", Times, serif;} h2.mainh {font-size: 1.5em; font-style: normal; margin: 15px 0 0; font-variant: small-caps; font-family: Georgia, "Times New Roman", Times, serif;} h2.line {border-bottom: 1px solid #CCCCCC; font-size: 1.5em; font-style: normal; margin: 15px 0 0; padding-bottom: 3px; font-variant: small-caps; font-family: Georgia, "Times New Roman", Times, serif;} h4 {border-bottom: 1px solid #CCCCCC; font-size: 1.2em; font-style: normal; margin: 10px 0 0; padding-bottom: 3px; font-family: Georgia, "Times New Roman", Times, serif;} h5 {margin: 0px} a.help { font-size: 9px; font-style: normal; text-transform: uppercase; font-family: Georgia, "Times New Roman", Times, serif;} div.pad { padding-left: 30px; padding-top: 5px; padding-bottom: 10px;} div.pad1 { margin: 10px 5px;} div.pad2 { margin: 25px 5px 5px;} h2.pad2 { padding: 25px 5px 5px;} div.pad3 { padding: 5px 0px 10px 30px;} div.box { border: 2px solid #CCCCCC; padding:10px; overflow: hidden;} div.bar { border-left: 7px solid #00666a; padding:5px; margin-top:25px; } div.subsection {margin:25px 0px;} img {border:0px none;} th.majorth {text-align:left;} th.minorth {font-weight:normal; text-align:left; width:8em; padding: 3px 0px;} th.actionth {font-weight:normal; text-align:left;} .explain h5 {font-size:1em; margin-left: 1em;} div.doc {margin-left: 2em; margin-bottom: 3em;} th.trainingset { border-bottom: thin dashed black; font-weight:normal; padding:0px 10px; } div.pop_content { position:absolute; z-index:50; width:300px; padding: 5px; background: #E4ECEC; font-size: 12px; font-family: Arial; border-style: double; border-width: 3px; border-color: #AA2244; display:none; } div.pop_content > *:first-child { margin-top: 0px; } div.pop_content h1, div.pop_content h2, div.pop_content h3, div.pop_content h4, div.pop_content h5, div.pop_content h6, div.pop_content p { margin: 0px; } div.pop_content p + h1, div.pop_content p + h2, div.pop_content p + h3, div.pop_content p + h4, div.pop_content p + h5, div.pop_content p + h6 { margin-top: 5px; } div.pop_content p + p { margin-top: 5px; } div.pop_content > *:last-child { margin-bottom: 0px; } div.pop_content div.pop_close { /* old definition */ float:right; bottom: 0; } div.pop_content span.pop_close, div.pop_content span.pop_back { display: inline-block; border: 2px outset #661429; background-color: #CCC; padding-left: 1px; padding-right: 1px; padding-top: 0px; padding-bottom: 0px; cursor: pointer; color: #AA2244; /*#661429;*/ font-weight: bold; } div.pop_content span.pop_close:active, div.pop_content span.pop_back:active { border-style: inset; } div.pop_content span.pop_close { float:right; /*border: 2px outset #AA002B;*/ /*color: #AA2244;*/ } div.pop_content:not(.nested) .nested_only { display: none; } div.pop_back_sec { margin-bottom: 5px; } div.pop_close_sec { margin-top: 5px; } table.hide_advanced tr.advanced { display: none; } span.show_more { display: none; } table.hide_advanced span.show_more { display: inline; } table.hide_advanced span.show_less { display: none; } /***************************************************************************** * Program logo styling ****************************************************************************/ div.prog_logo { border-bottom: 0.25em solid #0f5f60; height: 4.5em; width: 24em; display:inline-block; } div.prog_logo img { float:left; width: 4em; border-style: none; margin-right: 0.2em; } div.prog_logo h1, div.prog_logo h1:hover, div.prog_logo h1:active, div.prog_logo h1:visited { margin:0; padding:0; font-family: Arial, Helvetica, sans-serif; font-size: 3.2em; line-height: 1em; vertical-align: top; display: block; color: #026666; letter-spacing: -0.06em; text-shadow: 0.04em 0.06em 0.05em #666; } div.prog_logo h2, div.prog_logo h2:hover, div.prog_logo h2:active, div.prog_logo h2:visited { display: block; margin:0; padding:0; font-family: Helvetica, sans-serif; font-size: 0.9em; line-height: 1em; letter-spacing: -0.06em; color: black; } div.big.prog_logo { font-size: 18px; } </style> <style> .block_td { height:25px; } .block_container { position:relative; box-sizing: border-box; height: 25px; padding: 0px; margin: 0px; margin-left: 1em; } .block_label { position: absolute; display: inline-block; padding: 3px; z-index: 4; top: 6px; height: 12px; line-height: 12px; font-size: 12px; background-color: white; border: 1px solid black; -moz-border-radius: 12px; -webkit-border-radius: 12px; border-radius: 12px; transform: translateX(-50%); } .block_motif { position: absolute; z-index: 3; top: 0px; box-sizing: border-box; border: 1px solid black; height: 12px; background-color: cyan; } .block_motif.top { border-bottom-width: 0; } .block_motif.bottom { border-top-width: 0; } .block_motif.scanned_site { opacity: 0.3; } .block_motif.scanned_site.active { opacity: 0.9; } .block_region { position:absolute; z-index:6; height:25px; top:0px; } .block_region.main { z-index:8; } .block_region.scanned_site { z-index:5; } .block_region.scanned_site.main { z-index:7; } .block_region.top { height:13px; } .block_region.bottom { height:13px; top:12px; } .block_rule { position:absolute; z-index:2; width:100%; height:1px; top:12px; left:0px; background-color:gray; } .block_plus_sym { position:absolute; z-index:4; line-height:12px; top:0px; left:-1em; } .block_minus_sym { position:absolute; z-index:4; line-height:12px; top:13px; left:-1em; } .tic_major { position:absolute; top:0em; height:0.5em; width: 2px; margin-left: -1px; background-color: blue; } .tic_minor { position:absolute; top:0em; height:0.2em; width: 1px; margin-left: -0.5px; background-color: blue; } .tic_label { position:absolute; display: inline-block; top:0.5em; height: 1em; color: blue; transform: translateX(-50%); } .block_needle { position:absolute; z-index:4; height:30px; width:1px; top:-2px; background-color:gray; } .block_needle.right { height: 60px; } .block_handle { position: absolute; display: inline-block; z-index: 5; top: 27px; min-width: 3ex; text-align: center; font-size: 12px; line-height: 12px; transform: translateX(-50%); background-color: LightGrey; border:3px outset grey; cursor: pointer; -webkit-user-select: none; /* Chrome/Safari */ -moz-user-select: none; /* Firefox */ -ms-user-select: none; /* IE10+ */ /* Rules below not implemented in browsers yet */ -o-user-select: none; user-select: none; } .block_handle.right { top: 47px; } .legend_container { text-align: right; } .legend_entry { display: inline-block; padding: 5px; } div.legend_swatch { box-sizing: border-box; width: 15px; height: 15px; border: 1px solid black; background-color: cyan; float: left; } div.legend_swatch input { display: none; } .legend_text { line-height: 15px; margin-left: 20px; } </style> <style> /* meme output specific css */ div.pop_block { position:absolute; z-index:5; padding: 5px; border: 1px solid black; display: inline-block; background-color: white; } #measure_match { position: absolute; visibility: hidden; height: auto; width: auto; white-space: nowrap; } div.template { position: absolute; z-index: 1; left: 0; top: 0; visibility: hidden; } table.block_information { margin-left: auto; margin-right: auto; } table.block_information * th { text-align: right; } *.hide_empty_seqs * tr.empty_seq { display: none; } *.hide_only_scan * tr.only_scan { display: none; } *.hide_only_scan * div.scanned_site { display: none; } td.symaction { text-align: center; text-decoration: underline; font-size: 20px; cursor: pointer; } div.sym_btn { display:inline-block; text-decoration: underline; cursor: pointer; font-size: 20px; line-height:20px; text-align: center; width: 20px; height: 20px; color: blue; } div.sym_btn:hover { color: white; background-color: blue; } div.sym_btn.positioned { position: absolute; top: 0px; } div.actionbutton { display:inline-block; cursor: pointer; font-size: 18px; line-height:20px; padding: 5px; margin: 10px 0; border: 1px solid black; } div.actionbutton:hover { color:#FFF; background-color:#000; } div.param_box { display: inline-block; margin-right: 20px; } span.param { font-weight: bold; } div.box + div.box { margin-top: 5px; } div.sites_outer { position: relative; padding-top: 20px; /* height of header */ display: inline-block; } div.sites_inner { overflow-x: hidden; overflow-y: auto; max-height: 200px; } table.sites_tbl { border-collapse: collapse; } div.sites_th_inner { position: absolute; top: 0; line-height: 20px; /* height of header */ text-align: left; padding-left: 5px; } th.nopad div.sites_th_inner { padding-left: 0; } div.sites_th_hidden { visibility: hidden; height: 0; padding: 0 10px; } th.nopad div.sites_th_hidden { padding: 0; } div.sites_inner * th { height: 0; } table.sites_tbl { overflow-x: hidden; overflow-y: auto; } .site_num { text-align: right; } .site_name { padding:0px 5px; text-align:left; } .site_strand { padding:0px 5px; text-align:center; } .norc .site_strand, .norc .site_strand_title { display: none; } .site_start { padding:0px 15px; text-align: right; } .site_pvalue { text-align:center; padding:0px 15px; text-align:right; white-space: nowrap; } .lflank, .rflank, .match, .alpha_symbol { font-weight:bold; font-size:15px; font-family: 'Courier New', Courier, monospace; color:gray; } .site.lflank { text-align:right; padding-right:5px; color:gray; } .site.match { text-align:center; } .site.rflank { text-align:left; padding-left:5px; padding-right: 20px; } th.stop_reason { text-align: left; padding-right: 10px; } th.motif_ordinal { } td.motif_ordinal { text-align: right; padding-right: 10px; } th.motif_logo { padding-right: 10px; } td.motif_logo { padding-right: 10px; } th.motif_evalue { text-align:right; padding-right: 10px; } td.motif_evalue { text-align: right; white-space: nowrap; padding-right: 20px; } th.motif_nsites { text-align: right; padding-right: 10px; } td.motif_nsites { text-align: right; padding-right: 20px; } th.motif_width { text-align: right; padding-right: 5px; } td.motif_width { text-align: right; padding-right: 15px; } th.motif_more { padding: 0 5px; } td.motif_more { text-align: center; padding: 0 5px; } th.motif_submit { padding: 0 5px; } td.motif_submit { text-align: center; padding: 0 5px; } th.motif_download { padding-left: 5px; } td.motif_download { text-align: center; padding-left: 5px; } div.tabArea { font-size: 80%; font-weight: bold; } .norc div.tabArea { display: none; } span.tab, span.tab:visited { cursor: pointer; color: #888; background-color: #ddd; border: 2px solid #ccc; padding: 2px 1em; text-decoration: none; } span.tab.middle { border-left-width: 0px; } div.tabArea.base span.tab { border-top-width: 0px; } div.tabArea.top span.tab { border-bottom-width: 0px; } span.tab:hover { background-color: #bbb; border-color: #bbb; color: #666; } span.tab.activeTab, span.tab.activeTab:hover, span.tab.activeTab:visited { background-color: white; color: black; cursor: default; } div.tabMain { border: 2px solid #ccc; background-color: white; padding: 10px; } div.tabMain.base { margin-top: 5px; display: inline-block; max-width: 98%; } div.tabMain.top { margin-bottom: 5px; } div.tabCenter { max-width: 100%; overflow-x: auto; height: 200px; overflow-y: hidden; } canvas.logo_rc { display:none; } .show_rc_logo > canvas { display: none; } .show_rc_logo > canvas.logo_rc { display: block; } canvas.scan_logo { margin-left: 10px; } div.blocks_outer { position: relative; padding-top: 20px; /* height of header */ } div.blocks_inner { overflow-x: hidden; overflow-y: auto; max-height: 200px; } table.blocks_tbl { border-collapse: collapse; width: 100%; } div.blocks_th_inner { position: absolute; top: 0; line-height: 20px; /* height of header */ text-align: left; padding-left: 5px; } th.nopad div.blocks_th_inner { padding-left: 0; } div.blocks_th_hidden { visibility: hidden; height: 0; padding: 0 10px; } th.nopad div.blocks_th_hidden { padding: 0; } div.blocks_inner * th { height: 0; } table.blocks_tbl { overflow-x: hidden; overflow-y: auto; } td.block_td { width: 99%; } *.blockdiag_num { text-align: right; } td.blockdiag_name { text-align: left; padding:0px 10px; } td.blockdiag_pvalue { padding:0px 10px; text-align:right; white-space: nowrap; } div.preview_btn { border: 2px solid white; height: 16px; width: 16px; font-size: 12px; line-height: 16px; text-align: center; cursor: pointer; } div.preview_btn + div.preview_btn { margin-top: 3px; } div.preview_btn.active { border: 2px solid black; cursor: default; } div.preview_btn:hover { background-color: black; color: white; border-color: black; } div.preview_btn.active:hover { background-color: white; color: black; border-color: black; } div.preview_btn_box { position: absolute; left: 0px; top: 0px; padding: 3px; } div.preview_logo_box { height: 50px; overflow-y: hidden; } div.preview_btn_box + div.preview_logo_box { margin-left: 25px; } div.preview_box { position: relative; } div.grey_background { position:fixed; z-index: 8; background-color: #000; -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; opacity: 0.5; left: 0; top: 0; width: 100%; height: 100%; } div.popup_wrapper { position:fixed; z-index:9; width:100%; height:0; top:50%; left:0; } div.popup { width: 600px; z-index:9; margin-left: auto; margin-right: auto; padding: 5px; background-color: #FFF; border-style: double; border-width: 5px; border-color: #00666a; position:relative; } div.close { cursor: pointer; border: 1px solid black; width:15px; height:15px; line-height:15px; /* this causes vertical centering */ text-align:center; background-color:#FFF; color:#000; font-size:15px; font-family:monospace; } div.close:hover { color:#FFF; background-color:#000; } div.navnum { width:100%; height:20px; line-height:20px; text-align:center; font-size:medium; } div.navarrow { font-size: 30px; text-decoration:none; cursor: pointer; -moz-user-select: none; -webkit-user-select: none; -ms-user-select: none; } div.navarrow > span.inactive { display: inline; } div.navarrow > span.active { display: none; } div.navarrow:hover > span.active { display: inline; } div.navarrow:hover > span.inactive { display: none; } table.programs { width: 100%; } table.programs tr { background-color: #EFE; } table.programs tr.selected { background-color: #262; color: #FFF; } table.programs tr.dna_only { display: none; } table.programs.alphabet_dna tr.dna_only { display: table-row; } div.programs_scroll { width: 100%; height: 90px; overflow-y: auto; overflow-x: hidden; margin: 0 auto; } table.inputs, table.alpha_bg_table { margin-top: 20px; border-collapse:collapse; } table.inputs * td, table.inputs * th, table.alpha_bg_table * td, table.alpha_bg_table * th { padding-left: 15px; padding-right: 15px; padding-top: 1px; padding-bottom: 1px; } table.hide_psp td.col_psp, table.hide_psp th.col_psp { display: none; } /* program settings */ span.mod_oops, span.mod_zoops, span.mod_anr { display: none; } td.oops span.mod_oops,td.zoops span.mod_zoops, td.anr span.mod_anr { display: inline; } span.strand_none, span.strand_given, span.strand_both { display: none; } td.none span.strand_none, td.given span.strand_given, td.both span.strand_both { display: inline; } span.spmap_uni, span.spmap_pam { display: none; } td.uni span.spmap_uni, td.pam span.spmap_pam { display: inline; } span.prior_dirichlet, span.prior_dmix, span.prior_mega, span.prior_megap, span.prior_addone { display: none; } td.dirichlet span.prior_dirichlet, td.dmix span.prior_dmix, td.mega span.prior_mega, td.megap span.prior_megap, td.addone span.prior_addone { display: inline; } span.noendgaps_on, span.noendgaps_off { display: none; } td.on span.noendgaps_on, td.off span.noendgaps_off { display: inline; } span.substring_on, span.substring_off { display: none; } td.on span.substring_on, td.off span.substring_off { display: inline; } </style> </head> <body onload="page_loaded()" onpageshow="page_shown(event)" onresize="page_resized()"> <!-- --> <div id="grey_out_page" class="grey_background" style="display:none;"> </div> <!-- Help popups --> <div class="pop_content" id="pop_"> <p>Help poup.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_ev"> <p>The statistical significance of the motif. MEME usually finds the most statistically significant (low E-value) motifs first. It is unusual to consider a motif with an E-value larger than 0.05 significant so, as an additional indicator, MEME displays these partially transparent.</p> <p>The E-value of a motif is based on its log likelihood ratio, width, sites, the background letter frequencies (given in the command line summary), and the size of the training set.</p> <p>The E-value is an estimate of the expected number of motifs with the given log likelihood ratio (or higher), and with the same width and site count, that one would find in a similarly sized set of random sequences (sequences where each position is independent and letters are chosen according to the background letter frequencies).</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_sites"> <p>The number of sites contributing to the construction of the motif.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_width"> <p>The width of the motif. Each motif describes a pattern of a fixed width, as no gaps are allowed in MEME motifs.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_more"> <p>Click on the blue symbol below to reveal more information about this motif.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_submit_dl"> <p>Click on the blue symbol below to reveal options allowing you to submit this motif to another MEME Suite motif analysis program, to download this motif in various text formats, or to download a sequence "logo" of this motif PNG or EPS format.</p> <h5>Supported Programs</h5> <dl> <dt>Tomtom</dt> <dd>Tomtom is a tool for searching for similar known motifs. [<a href="http://meme-suite.org/doc/tomtom.html?man_type=web">manual</a>]</dd> <dt>MAST</dt> <dd>MAST is a tool for searching biological sequence databases for sequences that contain one or more of a group of known motifs. [<a href="http://meme-suite.org/doc/mast.html?man_type=web">manual</a>]</dd> <dt>FIMO</dt> <dd>FIMO is a tool for searching biological sequence databases for sequences that contain one or more known motifs. [<a href="http://meme-suite.org/doc/fimo.html?man_type=web">manual</a>]</dd> <dt>GOMO</dt> <dd>GOMO is a tool for identifying possible roles (Gene Ontology terms) for DNA binding motifs. [<a href="http://meme-suite.org/doc/gomo.html?man_type=web">manual</a>]</dd> <dt>SpaMo</dt> <dd>SpaMo is a tool for inferring possible transcription factor complexes by finding motifs with enriched spacings. [<a href="http://meme-suite.org/doc/spamo.html?man_type=web">manual</a>]</dd> </dl> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_llr"> <p>The log likelihood ratio of the motif.The log likelihood ratio is the logarithm of the ratio of the probability of the occurrences of the motif given the motif model (likelihood given the motif) versus their probability given the background model (likelihood given the null model). (Normally the background model is a 0-order Markov model using the background letter frequencies, but higher order Markov models may be specified via the -bfile option to MEME.).</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_ic"> <p>The information content of the motif in bits. It is equal to the sum of the uncorrected information content, R(), in the columns of the pwm. This is equal relative entropy of the motif relative to a uniform background frequency model.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_re"> <p>The relative entropy of the motif.</p> <p style="font-family: monospace;">re = llr / (sites * ln(2))</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_bt"> <p>The Bayes Threshold.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_site_strand"> <p>The strand used for the motif site.</p> <dl> <dt>+</dt> <dd>The motif site was found in the sequence as it was supplied.</dd> <dt>-</dt> <dd>The motif site was found in the reverse complement of the supplied sequence.</dd> </dl> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_site_start"> <p>The position in the sequence where the motif site starts. If a motif started right at the begining of a sequence it would be described as starting at position 1.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_site_pvalue"> <p>The probability that an equal or better site would be found in a random sequence of the same length conforming to the background letter frequencies.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_site_match"> <p>A motif site with the 10 flanking letters on either side.</p> <p>When the site is not on the given strand then the site and both flanks are reverse complemented so they align.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_seq_name"> <p>The name of the sequences as given in the FASTA file.</p> <p>The number to the left of the sequence name is the ordinal of the sequence.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_motif_sites"> <p>These are the motif sites predicted by MEME and used to build the motif.</p> <p>These sites are shown in solid color and hovering the cursor over a site will reveal details about the site. Only sequences that contain a motif site are shown.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_scanned_sites"> <p>These are the motif sites predicted by MEME plus any additional sites detected using a motif scanning algorithm.</p> <p>These MEME sites are shown in solid color and additional scanned sites are shown in transparent color. Hovering the cursor over a site will reveal details about the site. Only sequences containing a predicted or scanned motif site are shown.</p> <p>The scanned sites are predicted using a log-odds scoring matrix constructed from the MEME sites. Only scanned sites with position <i>p</i>-values less than 0.0001 are shown.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_all_sequences"> <p>These are the same sites as shown by selecting the "Motif Sites + Scanned Sites" button except that all sequences, including those with no sites, are included in the diagram.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_seq_pvalue"> <p>This is the combined match <i>p</i>-value.</p> <p>The combined match <i>p</i>-value is defined as the probability that a random sequence (with the same length and conforming to the background) would have position <i>p</i>-values such that the product is smaller or equal to the value calulated for the sequence under test.</p> <p>The position <i>p</i>-value is defined as the probability that a random sequence (with the same length and conforming to the background) would have a match to the motif under test with a score greater or equal to the largest found in the sequence under test.</p> <p>Hovering your mouse over a motif site in the motif location block diagram will show its position <i>p</i>-value and other information about the site.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_motif_location"> <p>This diagram shows the location of motif sites.</p> <p>Each block shows the position and strength of a motif site. The height of a block gives an indication of the significance of the site as taller blocks are more significant. The height is calculated to be proportional to the negative logarithm of the <i>p</i>-value of the site, truncated at the height for a <i>p</i>-value of 1e-10.</p> <p>For complementable alphabets (like DNA), sites on the positive strand are shown above the line, sites on the negative strand are shown below.</p> <p>Placing the cursor over a motif site will reveal more information about the site including its position <i>p</i>-value. (See the help for the <i>p</i>-value column for an explanation of position <i>p</i>-values.)</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_seq_source"> <p>The name of the file of sequences input to MEME.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_psp_source"> <p>The position specific priors file used by MEME to find the motifs.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_seq_alph"> <p>The alphabet used by the sequences.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_seq_count"> <p>The number of sequences provided as input to MEME.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_alph_name"> <p>The name of the alphabet symbol.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_alph_freq"> <p>The frequency of the alphabet symbol in the dataset with a pseudocount so it is never zero.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <div class="pop_content" id="pop_alph_bg"> <p>The frequency of the alphabet symbol as defined by the background model.</p> <div style="float:right; bottom:0px;">[ <a href="javascript:help_popup()">close</a> ]</div> </div> <!-- templates --> <div id="measure_match" class="match"></div> <div class="template pop_block" id="tmpl_block_info"> <div> <span class="tvar_logo_pad lflank" style="visibility:hidden;"></span> <span class="tvar_logo"></span> </div> <div class="block_sequence_fragment"> <span class="tvar_lflank lflank"></span> <span class="tvar_match match"></span> <span class="tvar_rflank rflank"></span> </div> <table class="block_information"> <tr><th>Motif</th><td class="tvar_motif">1</td></tr> <tr><th><i>p</i>-value</th><td class="tvar_pvalue">8.23e-7</td></tr> <tr><th>Start</th><td class="tvar_start">23</td></tr> <tr><th>End</th><td class="tvar_end">33</td></tr> </table> </div> <div class="template pop_block" id="tmpl_scan_info"> <h5>Scanned Site</h5> <div class="tvar_logo"></div> <table class="block_information"> <tr><th>Motif</th><td class="tvar_motif">1</td></tr> <tr><th><i>p</i>-value</th><td class="tvar_pvalue">8.23e-7</td></tr> <tr><th>Start</th><td class="tvar_start">23</td></tr> <tr><th>End</th><td class="tvar_end">33</td></tr> </table> </div> <div class="template box expanded_motif" id="tmpl_motif_expanded"> <div style="position: relative; min-height: 20px"> <div class="param_box"> <span class="param"><span class="tvar_ordinal"></span>.</span> </div> <div class="sym_btn positioned tvar_less" tabindex="0" title="Show less information.">↥</div> <div class="sym_btn positioned tvar_submit" tabindex="0" title="Submit the motif to another MEME Suite program or download it.">⇢</div> </div> <div> <div class="param_box"> <span class="param"><i>E</i>-value:</span> <span class="tvar_evalue"></span> <div class="help" data-topic="pop_ev"></div> </div> <div class="param_box"> <span class="param">Site Count:</span> <span class="tvar_site_count"></span> <div class="help" data-topic="pop_sites"></div> </div> <div class="param_box"> <span class="param">Width:</span> <span class="tvar_width"></span> <div class="help" data-topic="pop_width"></div> </div> </div> <div class="tabMain base"> <div class="tabCenter tvar_logo"></div> </div> <div class="tabArea base"> <span class="tvar_tab tab" tabindex="0">Standard</span><span class="tvar_tab_rc tab middle" tabindex="0">Reverse Complement</span> </div> <div style="padding: 10px 0"> <div class="param_box"> <span class="param">Log Likelihood Ratio:</span> <span class="tvar_llr"></span> <div class="help" data-topic="pop_llr"></div> </div> <div class="param_box"> <span class="param">Information Content:</span> <span class="tvar_ic"></span> <div class="help" data-topic="pop_ic"></div> </div> <div class="param_box"> <span class="param">Relative Entropy:</span> <span class="tvar_re"></span> <div class="help" data-topic="pop_re"></div> </div> <div class="param_box"> <span class="param">Bayes Threshold:</span> <span class="tvar_bt"></span> <div class="help" data-topic="pop_bt"></div> </div> </div> <div class="tvar_sites"></div> </div> <div class="popup_wrapper"> <div class="popup" style="display:none; top: -150px;" id="download"> <div> <div style="float:right; "> <div id="outpop_close" class="close" tabindex="0">x</div> </div> <h2 class="mainh" style="margin:0; padding:0;">Submit or Download</h2> <div style="clear:both"></div> </div> <div style="height:100px"> <div style="float:right; width: 30px;"> <div id="outpop_prev" class="navarrow" tabindex="0"> <span class="inactive">⇧</span><span class="active">⬆</span> </div> <div id="outpop_num" class="navnum"></div> <div id="outpop_next" class="navarrow" tabindex="0"> <span class="inactive">⇩</span><span class="active">⬇</span> </div> </div> <div id="logo_box" style="height: 100px; margin-right: 40px;"> <canvas id="outpop_logo" height="100" width="580"></canvas> <canvas id="outpop_logo_rc" class="logo_rc" height="100" width="580"></canvas> </div> </div> <div> <!-- tabs start --> <div class="tabArea top"> <span id="outpop_tab_1" class="tab">Submit Motif</span><span id="outpop_tab_2" class="tab middle">Download Motif</span><span id="outpop_tab_3" class="tab middle">Download Logo</span> </div> <div class="tabMain top"> <!-- Submit to another program --> <div id="outpop_pnl_1"> <h4 class="compact">Submit to program</h4> <table id="programs" class="programs"> <tr class="dna_only"> <td><input type="radio" name="program" value="tomtom" id="submit_tomtom"></td> <td><label for="submit_tomtom">Tomtom</label></td> <td><label for="submit_tomtom">Find similar motifs in published libraries or a library you supply.</label></td> </tr> <tr> <td><input type="radio" name="program" value="fimo" id="submit_fimo"></td> <td><label for="submit_fimo">FIMO</label></td> <td><label for="submit_fimo">Find motif occurrences in sequence data.</label></td> </tr> <tr> <td><input type="radio" name="program" value="mast" id="submit_mast"></td> <td><label for="submit_mast">MAST</label></td> <td><label for="submit_mast">Rank sequences by affinity to groups of motifs.</label></td> </tr> <tr class="dna_only"> <td><input type="radio" name="program" value="gomo" id="submit_gomo"></td> <td><label for="submit_gomo">GOMo</label></td> <td><label for="submit_gomo">Identify possible roles (Gene Ontology terms) for motifs.</label></td> </tr> <tr class="dna_only"> <td><input type="radio" name="program" value="spamo" id="submit_spamo"></td> <td><label for="submit_spamo">SpaMo</label></td> <td><label for="submit_spamo">Find other motifs that are enriched at specific close spacings which might imply the existance of a complex.</label></td> </tr> </table> </div> <!-- download text format --> <div id="outpop_pnl_2"> <div> <label for="text_format">Format:</label> <select id="text_format"> <option value="0">Count Matrix</option> <option value="1">Probability Matrix</option> <option value="2">Minimal MEME</option> <option value="3">FASTA</option> <option value="4">Raw</option> </select> </div> <form id="text_form" method="post" action=""> <script>$("text_form").action = site_url + "/utilities/save_generated_file";</script> <input type="hidden" id="text_name" name="name" value="motif.txt"> <input type="hidden" name="mime_type" value="text/plain"> <textarea id="outpop_text" name="content" style="width:99%; white-space: pre; word-wrap: normal; overflow-x: scroll;" rows="8" readonly="readonly" wrap="off"></textarea> </form> </div> <!-- download logo format --> <div id="outpop_pnl_3"> <form id="logo_form" method="post" action=""> <script>$("logo_form").action = site_url + "/utilities/generate_logo";</script> <input type="hidden" name="program" value="MEME"/> <input type="hidden" id="logo_motifs" name="motifs" value=""/> <table> <tr> <td><label for="logo_format">Format:</label></td> <td> <select id="logo_format" name="png"> <option value="1">PNG (for web)</option> <option value="0">EPS (for publication)</option> </select> </td> </tr> <tr> <td><label for="logo_rc">Orientation:</label></td> <td> <select id="logo_rc" name="rc1"> <option value="0">Normal</option> <option value="1" id="logo_rc_option">Reverse Complement</option> </select> </td> </tr> <tr> <td><label for="logo_ssc">Small Sample Correction:</label></td> <td> <input type="hidden" id="logo_err" name="errbars" value="0"/> <select id="logo_ssc" name="ssc"> <option value="0">Off</option> <option value="1">On</option> </select> </td> </tr> <tr> <td><label for="logo_width">Width:</label></td> <td> <input type="text" id="logo_width" size="4" placeholder="default" name="width"/> cm </td> </tr> <tr> <td><label for="logo_height">Height:</label></td> <td> <input type="text" id="logo_height" size="4" placeholder="default" name="height"/> cm </td> </tr> </table> </form> </div> <!-- Buttons --> <div> <div style="float:left;"> <input type="button" id="outpop_do" value="Submit" /> </div> <div style="float:right;"> <input id="outpop_cancel" type="button" value="Cancel" /> </div> <div style="clear:both;"></div> </div> </div> </div> </div> </div> <!-- Page starts here --> <div id="top" class="pad1"> <div class="prog_logo big"> <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"> <h1>MEME</h1> <h2>Multiple Em for Motif Elicitation</h2> </div> <p> For further information on how to interpret these results or to get a copy of the MEME software please access <a href="http://meme-suite.org/">http://meme-suite.org</a>. </p> <p>If you use MEME in your research, please cite the following paper:<br /> <span class="citation"> Timothy L. Bailey and Charles Elkan, "Fitting a mixture model by expectation maximization to discover motifs in biopolymers", <em>Proceedings of the Second International Conference on Intelligent Systems for Molecular Biology</em>, pp. 28-36, AAAI Press, Menlo Park, California, 1994. <a href="http://meme-suite.org/doc/ismb94.pdf">[pdf]</a> </span> </p> </div> <!-- navigation --> <div class="pad2"> <a class="jump" href="#motifs_sec">Discovered Motifs</a> | <a class="jump" href="#sites_sec">Motif Locations</a> | <a class="jump" href="#inputs_sec">Inputs & Settings</a> | <a class="jump" href="#info_sec">Program information</a> </div> <!-- alert the user when their browser is not up to the task --> <noscript><h1 style="color:red">Javascript is required to view these results!</h1></noscript> <h1 id="html5_warning" style="color:red; display:none;">Your browser does not support canvas!</h1> <script> if (!window.HTMLCanvasElement) $("html5_warning").style.display = "block"; </script> <h2 class="mainh pad2" id="motifs_sec">Discovered Motifs</h2> <div id="motifs" class="box"> <p>Please wait... Loading...</p> <p>If the page has fully loaded and this message does not disappear then an error may have occurred.</p> </div> <h2 class="mainh pad2" id="sites_sec">Motif Locations</h2> <div id="blocks" class="box"> <p>Please wait... Loading...</p> <p>If the page has fully loaded and this message does not disappear then an error may have occurred.</p> </div> <h2 class="mainh pad2" id="inputs_sec">Inputs & Settings</h2> <div class="box"> <h4>Sequences</h4> <table id="seq_info" class="inputs"> <tr><th>Source <div class="help" data-topic="pop_seq_source"></div></th> <th class="col_psp">PSP Source <div class="help" data-topic="pop_psp_source"></div></th> <th>Alphabet <div class="help" data-topic="pop_seq_alph"></div></th> <th>Sequence Count <div class="help" data-topic="pop_seq_count"></div></th> </tr> <tr> <td id="ins_seq_source"></td> <td id="ins_seq_psp" class="col_psp"></td> <td id="ins_seq_alphabet"></td> <td id="ins_seq_count"></td> </tr> </table> <script> { var db = data.sequence_db; $("ins_seq_source").innerHTML = db.source; $("ins_seq_alphabet").innerHTML = meme_alphabet.get_alphabet_name(); $("ins_seq_count").innerHTML = db.sequences.length; if (db.psp) { $("ins_seq_psp").innerHTML = db.psp; } toggle_class($("seq_info"), "hide_psp", !(db.psp)); } </script> <h4>Background</h4> <span id="alpha_bg"></span> <script> { $("alpha_bg").appendChild(make_alpha_bg_table(meme_alphabet, data.sequence_db.freqs)); } </script> <h4>Other Settings</h4> <table id="tbl_settings" class="inputs hide_advanced"> <tr> <th>Motif Site Distribution</th> <td id="opt_mod"> <span class="mod_zoops">ZOOPS: Zero or one site per sequence</span> <span class="mod_oops">OOPS: Exactly one site per sequence</span> <span class="mod_anr">ANR: Any number of sites per sequence</span> </td> </tr> <tr> <th>Site Strand Handling</th> <td id="opt_strand"> <span class="strand_none">This alphabet only has one strand</span> <span class="strand_given">Sites must be on the given strand</span> <span class="strand_both">Sites may be on either strand</span> </td> </tr> <tr> <th>Maximum Number of Motifs</th> <td id="opt_nmotifs"></td> </tr> <tr> <th>Motif E-value Threshold</th> <td id="opt_evt"></td> </tr> <tr> <th>Minimum Motif Width</th> <td id="opt_minw"></td> </tr> <tr> <th>Maximum Motif Width</th> <td id="opt_maxw"></td> </tr> <tr> <th>Minimum Sites per Motif</th> <td id="opt_minsites"></td> </tr> <tr> <th>Maximum Sites per Motif</th> <td id="opt_maxsites"></td> </tr> <tr class="advanced"> <th>Bias on Number of Sites</th> <td id="opt_wnsites"></td> </tr> <tr class="advanced"> <th>Sequence Prior</th> <td id="opt_prior"> <span class="prior_dirichlet">Simple Dirichlet</span> <span class="prior_dmix">Dirichlets Mix</span> <span class="prior_mega">Mega-weight Dirichlets Mix</span> <span class="prior_megap">Mega-weight Dirichlets Mix Plus</span> <span class="prior_addone">Add One</span> </td> </tr> <tr class="advanced"> <th>Sequence Prior Strength</th> <td id="opt_b"></td> </tr> <tr class="advanced"> <th>EM Starting Point Source</th> <td id="opt_substring"> <span class="substring_on">From substrings in input sequences</span> <span class="substring_off">From strings on command line (-cons)</span> </td> </tr> <tr class="advanced"> <th>EM Starting Point Map Type</th> <td id="opt_spmap"> <span class="spmap_uni">Uniform</span> <span class="spmap_pam">Point Accepted Mutation</span> </td> </tr> <tr class="advanced"> <th>EM Starting Point Fuzz</th> <td id="opt_spfuzz"></td> </tr> <tr class="advanced"> <th>EM Maximum Iterations</th> <td id="opt_maxiter"></td> </tr> <tr class="advanced"> <th>EM Improvement Threshold</th> <td id="opt_distance"></td> </tr> <tr class="advanced"> <th>Trim Gap Open Cost</th> <td id="opt_wg"></td> </tr> <tr class="advanced"> <th>Trim Gap Extend Cost</th> <td id="opt_ws"></td> </tr> <tr class="advanced"> <th>End Gap Treatment</th> <td id="opt_noendgaps"> <span class="noendgaps_on">No cost</span> <span class="noendgaps_off">Same cost as other gaps</span> </td> </tr> <tr> <td colspan="2" style="text-align: center"> <a href="javascript:toggle_class(document.getElementById('tbl_settings'), 'hide_advanced')"> <span class="show_more">Show Advanced Settings</span> <span class="show_less">Hide Advanced Settings</span> </a> </td> </tr> </table> <script> { $("opt_mod").className = data.options.mod; $("opt_strand").className = (meme_alphabet.has_complement() ? (data.options.revcomp ? "both" : "given") : "none"); $("opt_nmotifs").textContent = data.options.nmotifs; $("opt_evt").textContent = (typeof data.options.evt === "number" ? data.options.evt : "no limit"); $("opt_minw").textContent = data.options.minw; $("opt_maxw").textContent = data.options.maxw; $("opt_minsites").textContent = data.options.minsites; $("opt_maxsites").textContent = data.options.maxsites; $("opt_wnsites").textContent = data.options.wnsites; $("opt_spmap").className = data.options.spmap; $("opt_spfuzz").textContent = data.options.spfuzz; $("opt_prior").className = data.options.prior; $("opt_b").textContent = data.options.b; $("opt_maxiter").textContent = data.options.maxiter; $("opt_distance").textContent = data.options.distance; $("opt_wg").textContent = data.options.wg; $("opt_ws").textContent = data.options.ws; $("opt_noendgaps").className = (data.options.noendgaps ? "on" : "off"); $("opt_substring").className = (data.options.substring ? "on" : "off"); } </script> </div> <!-- list information on this program --> <div id="info_sec" class="bar"> <div class="subsection"> <h5 id="version">MEME version</h5> <span id="ins_version"></span> (Release date: <span id="ins_release"></span>)<br> </div> <script> $("ins_version").innerHTML = data["version"]; $("ins_release").innerHTML = data["release"]; </script> <div class="subsection"> <h5 id="reference">Reference</h5> <span class="citation"> Timothy L. Bailey and Charles Elkan, "Fitting a mixture model by expectation maximization to discover motifs in biopolymers", <em>Proceedings of the Second International Conference on Intelligent Systems for Molecular Biology</em>, pp. 28-36, AAAI Press, Menlo Park, California, 1994. </span> </div> <div class="subsection"> <h5 id="command">Command line</h5> <textarea id="cmd" rows="5" style="width:100%;" readonly="readonly"> </textarea> <script>$("cmd").value = data["cmd"].join(" ");</script> </div> </div> </body> </html>