7624 lines
245 KiB
HTML
Executable File
7624 lines
245 KiB
HTML
Executable File
<!DOCTYPE HTML>
|
|
<html>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>MEME</title>
|
|
<script>
|
|
// @JSON_VAR data
|
|
var data = {
|
|
"program": "MEME",
|
|
"version": "4.11.2",
|
|
"release": "Mon Oct 24 12:27:06 2016 -0700",
|
|
"stop_reason": "Stopped because requested number of motifs (1) found.",
|
|
"cmd": [
|
|
"meme",
|
|
"/data/project_roots/PlantAI-dev/.tmp_meme_parallel_test.fa",
|
|
"-mod", "anr", "-protein", "-nmotifs", "1", "-minw", "6", "-maxw",
|
|
"12", "-oc",
|
|
"/data/project_roots/PlantAI-dev/.tmp_meme_parallel_test_out",
|
|
"-nostatus"
|
|
],
|
|
"options": {
|
|
"mod": "anr",
|
|
"revcomp": false,
|
|
"nmotifs": 1,
|
|
"minw": 6,
|
|
"maxw": 12,
|
|
"minsites": 2,
|
|
"maxsites": 20,
|
|
"wnsites": 0.8,
|
|
"spmap": "pam",
|
|
"spfuzz": 120,
|
|
"maxwords": -1,
|
|
"prior": "megap",
|
|
"b": 840,
|
|
"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.048, 0.005, 0.069, 0.005, 0.069, 0.027, 0.048, 0.090, 0.069,
|
|
0.048, 0.027, 0.027, 0.027, 0.112, 0.048, 0.069, 0.048, 0.027,
|
|
0.048, 0.090
|
|
]
|
|
},
|
|
"sequence_db": {
|
|
"source": "/data/project_roots/PlantAI-dev/.tmp_meme_parallel_test.fa",
|
|
"psp_source": "prior30.plib",
|
|
"freqs": [
|
|
0.048, 0.000, 0.071, 0.000, 0.071, 0.024, 0.048, 0.095, 0.071,
|
|
0.048, 0.024, 0.024, 0.024, 0.119, 0.048, 0.071, 0.048, 0.024,
|
|
0.048, 0.095
|
|
],
|
|
"sequences": [
|
|
{
|
|
"name": "seq1",
|
|
"length": 42,
|
|
"weight": 1.000000
|
|
}, {
|
|
"name": "seq2",
|
|
"length": 42,
|
|
"weight": 1.000000
|
|
}, {
|
|
"name": "seq3",
|
|
"length": 42,
|
|
"weight": 1.000000
|
|
}, {
|
|
"name": "seq4",
|
|
"length": 42,
|
|
"weight": 1.000000
|
|
}
|
|
]
|
|
},
|
|
"motifs": [
|
|
{
|
|
"db": 0,
|
|
"id": "1",
|
|
"alt": "MEME",
|
|
"len": 11,
|
|
"nsites": 12,
|
|
"evalue": "5.1e-031",
|
|
"ic": 33.7,
|
|
"re": 30.2,
|
|
"llr": 251,
|
|
"bt": 4.9542,
|
|
"time": 0.024795,
|
|
"psm": [
|
|
[
|
|
-180, -169, -198, 145, -461, -128, 258, -507, -175, -328,
|
|
-368, -28, -278, 132, -178, 204, -224, -259, -580, -435
|
|
], [
|
|
-270, -225, -495, -44, -606, -278, -423, -563, -180, -386,
|
|
-438, -277, -387, -416, 371, -351, 265, -323, -650, -597
|
|
], [
|
|
-311, -224, -491, 112, -576, -354, -237, -540, -405, -298,
|
|
-296, -274, -332, 305, -308, -398, -379, -334, -581, -622
|
|
], [
|
|
-205, -170, 213, 120, -516, 349, -370, 169, -337, -326, -394,
|
|
-55, -319, -439, -362, -230, -263, -181, -664, -544
|
|
], [
|
|
-226, -115, -510, -107, -170, -281, -334, 170, -442, -165,
|
|
-243, -290, -371, -521, -389, 206, -288, -81, -355, 178
|
|
], [
|
|
-377, -179, -645, -233, 319, -450, -500, -241, -597, 263,
|
|
-125, -465, -443, -625, -523, -501, -424, -128, -446, -306
|
|
], [
|
|
-171, -152, 203, 172, -495, -190, -420, -351, -341, -283,
|
|
-355, -154, 361, -428, -358, -256, -265, 341, -653, -558
|
|
], [
|
|
-212, -220, 201, 232, -496, -183, -306, -438, 211, 251, -315,
|
|
-87, -284, -292, -104, -245, -239, -217, -589, -490
|
|
], [
|
|
-269, -221, -585, -253, -436, -293, -549, -658, -599, -457,
|
|
-542, -412, -435, -680, -533, 221, -413, -382, 375, -441
|
|
], [
|
|
-222, -148, -357, 92, -296, -238, 256, 162, -147, -160, -233,
|
|
-171, -321, 132, -139, -291, -252, -115, -462, -314
|
|
], [
|
|
-452, -230, -587, -209, 222, -415, -290, -491, -540, -276,
|
|
-379, 346, -456, -571, -449, -468, -457, -302, -299, 190
|
|
]
|
|
],
|
|
"pwm": [
|
|
[
|
|
0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000,
|
|
0.333333, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000,
|
|
0.000000, 0.333333, 0.000000, 0.333333, 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.666667, 0.000000, 0.333333, 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, 0.000000,
|
|
0.000000, 0.000000
|
|
], [
|
|
0.000000, 0.000000, 0.333333, 0.000000, 0.000000, 0.333333,
|
|
0.000000, 0.333333, 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.333333, 0.000000, 0.000000, 0.000000, 0.000000,
|
|
0.000000, 0.000000, 0.000000, 0.333333, 0.000000, 0.000000,
|
|
0.000000, 0.333333
|
|
], [
|
|
0.000000, 0.000000, 0.000000, 0.000000, 0.666667, 0.000000,
|
|
0.000000, 0.000000, 0.000000, 0.333333, 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.333333, 0.000000, 0.000000, 0.000000,
|
|
0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000,
|
|
0.333333, 0.000000, 0.000000, 0.000000, 0.000000, 0.333333,
|
|
0.000000, 0.000000
|
|
], [
|
|
0.000000, 0.000000, 0.333333, 0.000000, 0.000000, 0.000000,
|
|
0.000000, 0.000000, 0.333333, 0.333333, 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.333333, 0.000000, 0.000000,
|
|
0.666667, 0.000000
|
|
], [
|
|
0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000,
|
|
0.333333, 0.333333, 0.000000, 0.000000, 0.000000, 0.000000,
|
|
0.000000, 0.333333, 0.000000, 0.000000, 0.000000, 0.000000,
|
|
0.000000, 0.000000
|
|
], [
|
|
0.000000, 0.000000, 0.000000, 0.000000, 0.333333, 0.000000,
|
|
0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.333333,
|
|
0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000,
|
|
0.000000, 0.333333
|
|
]
|
|
],
|
|
"sites": [
|
|
{
|
|
"seq": 3,
|
|
"pos": 30,
|
|
"rc": false,
|
|
"pvalue": 8.89e-12,
|
|
"lflank": "RQDILDLWIY",
|
|
"match": "HTQGYFPDWQN",
|
|
"rflank": "Y"
|
|
}, {
|
|
"seq": 2,
|
|
"pos": 30,
|
|
"rc": false,
|
|
"pvalue": 8.89e-12,
|
|
"lflank": "RQDILDLWIY",
|
|
"match": "HTQGYFPDWQN",
|
|
"rflank": "Y"
|
|
}, {
|
|
"seq": 1,
|
|
"pos": 30,
|
|
"rc": false,
|
|
"pvalue": 8.89e-12,
|
|
"lflank": "RQDILDLWIY",
|
|
"match": "HTQGYFPDWQN",
|
|
"rflank": "Y"
|
|
}, {
|
|
"seq": 0,
|
|
"pos": 30,
|
|
"rc": false,
|
|
"pvalue": 8.89e-12,
|
|
"lflank": "RQDILDLWIY",
|
|
"match": "HTQGYFPDWQN",
|
|
"rflank": "Y"
|
|
}, {
|
|
"seq": 3,
|
|
"pos": 8,
|
|
"rc": false,
|
|
"pvalue": 5.96e-10,
|
|
"lflank": "MKTAYIAK",
|
|
"match": "QRQISFVKSHF",
|
|
"rflank": "SRQDILDLWI"
|
|
}, {
|
|
"seq": 2,
|
|
"pos": 8,
|
|
"rc": false,
|
|
"pvalue": 5.96e-10,
|
|
"lflank": "MKTAYIAK",
|
|
"match": "QRQISFVKSHF",
|
|
"rflank": "SRQDILDLWI"
|
|
}, {
|
|
"seq": 1,
|
|
"pos": 8,
|
|
"rc": false,
|
|
"pvalue": 5.96e-10,
|
|
"lflank": "MKTAYIAK",
|
|
"match": "QRQISFVKSHF",
|
|
"rflank": "SRQDILDLWI"
|
|
}, {
|
|
"seq": 0,
|
|
"pos": 8,
|
|
"rc": false,
|
|
"pvalue": 5.96e-10,
|
|
"lflank": "MKTAYIAK",
|
|
"match": "QRQISFVKSHF",
|
|
"rflank": "SRQDILDLWI"
|
|
}, {
|
|
"seq": 3,
|
|
"pos": 19,
|
|
"rc": false,
|
|
"pvalue": 7.58e-10,
|
|
"lflank": "RQISFVKSHF",
|
|
"match": "SRQDILDLWIY",
|
|
"rflank": "HTQGYFPDWQ"
|
|
}, {
|
|
"seq": 2,
|
|
"pos": 19,
|
|
"rc": false,
|
|
"pvalue": 7.58e-10,
|
|
"lflank": "RQISFVKSHF",
|
|
"match": "SRQDILDLWIY",
|
|
"rflank": "HTQGYFPDWQ"
|
|
}, {
|
|
"seq": 1,
|
|
"pos": 19,
|
|
"rc": false,
|
|
"pvalue": 7.58e-10,
|
|
"lflank": "RQISFVKSHF",
|
|
"match": "SRQDILDLWIY",
|
|
"rflank": "HTQGYFPDWQ"
|
|
}, {
|
|
"seq": 0,
|
|
"pos": 19,
|
|
"rc": false,
|
|
"pvalue": 7.58e-10,
|
|
"lflank": "RQISFVKSHF",
|
|
"match": "SRQDILDLWIY",
|
|
"rflank": "HTQGYFPDWQ"
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"scan": [
|
|
{
|
|
"pvalue": 2.84e-10,
|
|
"sites": [
|
|
{
|
|
"motif": 0,
|
|
"pos": 8,
|
|
"rc": false,
|
|
"pvalue": 5.96e-10
|
|
}, {
|
|
"motif": 0,
|
|
"pos": 19,
|
|
"rc": false,
|
|
"pvalue": 7.58e-10
|
|
}, {
|
|
"motif": 0,
|
|
"pos": 30,
|
|
"rc": false,
|
|
"pvalue": 8.89e-12
|
|
}
|
|
]
|
|
}, {
|
|
"pvalue": 2.84e-10,
|
|
"sites": [
|
|
{
|
|
"motif": 0,
|
|
"pos": 8,
|
|
"rc": false,
|
|
"pvalue": 5.96e-10
|
|
}, {
|
|
"motif": 0,
|
|
"pos": 19,
|
|
"rc": false,
|
|
"pvalue": 7.58e-10
|
|
}, {
|
|
"motif": 0,
|
|
"pos": 30,
|
|
"rc": false,
|
|
"pvalue": 8.89e-12
|
|
}
|
|
]
|
|
}, {
|
|
"pvalue": 2.84e-10,
|
|
"sites": [
|
|
{
|
|
"motif": 0,
|
|
"pos": 8,
|
|
"rc": false,
|
|
"pvalue": 5.96e-10
|
|
}, {
|
|
"motif": 0,
|
|
"pos": 19,
|
|
"rc": false,
|
|
"pvalue": 7.58e-10
|
|
}, {
|
|
"motif": 0,
|
|
"pos": 30,
|
|
"rc": false,
|
|
"pvalue": 8.89e-12
|
|
}
|
|
]
|
|
}, {
|
|
"pvalue": 2.84e-10,
|
|
"sites": [
|
|
{
|
|
"motif": 0,
|
|
"pos": 8,
|
|
"rc": false,
|
|
"pvalue": 5.96e-10
|
|
}, {
|
|
"motif": 0,
|
|
"pos": 19,
|
|
"rc": false,
|
|
"pvalue": 7.58e-10
|
|
}, {
|
|
"motif": 0,
|
|
"pos": 30,
|
|
"rc": false,
|
|
"pvalue": 8.89e-12
|
|
}
|
|
]
|
|
}
|
|
]
|
|
};
|
|
</script>
|
|
<script>
|
|
var site_url = "";
|
|
</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>
|