Mercurial > repos > iuc > meme_meme
comparison test-data/dreme_output_test2.html @ 14:3f0dd362b755 draft
"planemo upload for repository https://github.com/galaxyproject/tools-iuc/tree/master/tools/meme commit 86a94f48321780dbe18ef5b099434c347ec2f4d0"
author | iuc |
---|---|
date | Wed, 11 Dec 2019 18:05:15 -0500 |
parents | 57e5d9382f36 |
children | 19237efe98d1 |
comparison
equal
deleted
inserted
replaced
13:57e5d9382f36 | 14:3f0dd362b755 |
---|---|
5 <title>DREME</title> | 5 <title>DREME</title> |
6 <script> | 6 <script> |
7 // @JSON_VAR data | 7 // @JSON_VAR data |
8 var data = { | 8 var data = { |
9 "program": "dreme", | 9 "program": "dreme", |
10 "version": "4.12.0", | 10 "version": "5.0.5", |
11 "release": "Tue Jun 27 16:22:50 2017 -0700", | 11 "release": "Mon Mar 18 20:12:19 2019 -0700", |
12 "cmd": [ | 12 "cmd": [ |
13 "dreme", "-o", "./dreme_test2_out", "-p", "dreme_test_sites.fa", | 13 "dreme", "-o", "./dreme_out", "-p", |
14 "-norc", "-rna", "-s", "1", "-e", "1e-05", "-g", "100", "-mink", | 14 "-norc", "-rna", "-s", "1", "-e", "1e-05", "-g", "100", "-mink", |
15 "4", "-maxk", "10" | 15 "4", "-maxk", "10" |
16 ], | 16 ], |
17 "options": { | 17 "options": { |
18 "revcomp": false, | 18 "revcomp": false, |
34 "colour": "CC0000" | 34 "colour": "CC0000" |
35 }, { | 35 }, { |
36 "symbol": "C", | 36 "symbol": "C", |
37 "name": "Cytosine", | 37 "name": "Cytosine", |
38 "colour": "0000CC" | 38 "colour": "0000CC" |
39 }, { | |
40 "symbol": "G", | 39 "symbol": "G", |
41 "name": "Guanine", | 40 "name": "Guanine", |
42 "colour": "FFB300" | 41 "colour": "FFB300" |
43 }, { | |
44 "symbol": "U", | 42 "symbol": "U", |
45 "aliases": "T", | 43 "aliases": "T", |
46 "name": "Uracil", | 44 "name": "Uracil", |
47 "colour": "008000" | 45 "colour": "008000" |
48 }, { | |
49 "symbol": "N", | 46 "symbol": "N", |
50 "aliases": "X.", | 47 "aliases": "X.", |
51 "name": "Any base", | 48 "name": "Any base", |
52 "equals": "ACGU" | 49 "equals": "ACGU" |
53 }, { | |
54 "symbol": "V", | 50 "symbol": "V", |
55 "name": "Not U", | 51 "name": "Not U", |
56 "equals": "ACG" | 52 "equals": "ACG" |
57 }, { | |
58 "symbol": "H", | 53 "symbol": "H", |
59 "name": "Not G", | 54 "name": "Not G", |
60 "equals": "ACU" | 55 "equals": "ACU" |
61 }, { | |
62 "symbol": "D", | 56 "symbol": "D", |
63 "name": "Not C", | 57 "name": "Not C", |
64 "equals": "AGU" | 58 "equals": "AGU" |
65 }, { | |
66 "symbol": "B", | 59 "symbol": "B", |
67 "name": "Not A", | 60 "name": "Not A", |
68 "equals": "CGU" | 61 "equals": "CGU" |
69 }, { | |
70 "symbol": "M", | 62 "symbol": "M", |
71 "name": "Amino", | 63 "name": "Amino", |
72 "equals": "AC" | 64 "equals": "AC" |
73 }, { | |
74 "symbol": "R", | 65 "symbol": "R", |
75 "name": "Purine", | 66 "name": "Purine", |
76 "equals": "AG" | 67 "equals": "AG" |
77 }, { | |
78 "symbol": "W", | 68 "symbol": "W", |
79 "name": "Weak", | 69 "name": "Weak", |
80 "equals": "AU" | 70 "equals": "AU" |
81 }, { | |
82 "symbol": "S", | 71 "symbol": "S", |
83 "name": "Strong", | 72 "name": "Strong", |
84 "equals": "CG" | 73 "equals": "CG" |
85 }, { | |
86 "symbol": "Y", | 74 "symbol": "Y", |
87 "name": "Pyrimidine", | 75 "name": "Pyrimidine", |
88 "equals": "CU" | 76 "equals": "CU" |
89 }, { | |
90 "symbol": "K", | 77 "symbol": "K", |
91 "name": "Keto", | 78 "name": "Keto", |
92 "equals": "GU" | 79 "equals": "GU" |
93 } | |
94 ] | |
95 }, | |
96 "background": { | 80 "background": { |
97 "freqs": [0.221, 0.245, 0.221, 0.312] | 81 "freqs": [0.221, 0.245, 0.221, 0.312] |
98 }, | |
99 "sequence_db": { | 82 "sequence_db": { |
100 "name": "dreme test sites", | |
101 "file": "dreme_test_sites.fa", | |
102 "lmod": "Thu Apr 26 15:09:03 CEST 2018", | |
103 "count": 1000 | 83 "count": 1000 |
104 }, | |
105 "control_db": { | 84 "control_db": { |
106 "name": "shuffled positive sequences", | 85 "name": "shuffled positive sequences", |
107 "from": "shuffled", | 86 "from": "shuffled", |
108 "count": 1000, | 87 "count": 1000, |
109 "freqs": [0.221, 0.245, 0.221, 0.312] | 88 "freqs": [0.221, 0.245, 0.221, 0.312] |
110 }, | |
111 "motifs": [ | 89 "motifs": [ |
112 { | |
113 "db": 0, | 90 "db": 0, |
114 "id": "UUYUCY", | 91 "id": "UUYUCY", |
115 "alt": "DREME-1", | 92 "alt": "DREME-1", |
116 "len": 6, | 93 "len": 6, |
117 "nsites": 459, | 94 "nsites": 459, |
125 [0.000000, 0.000000, 0.000000, 1.000000], | 102 [0.000000, 0.000000, 0.000000, 1.000000], |
126 [0.000000, 0.294118, 0.000000, 0.705882], | 103 [0.000000, 0.294118, 0.000000, 0.705882], |
127 [0.000000, 0.000000, 0.000000, 1.000000], | 104 [0.000000, 0.000000, 0.000000, 1.000000], |
128 [0.000000, 1.000000, 0.000000, 0.000000], | 105 [0.000000, 1.000000, 0.000000, 0.000000], |
129 [0.000000, 0.474946, 0.000000, 0.525054] | 106 [0.000000, 0.474946, 0.000000, 0.525054] |
130 ], | |
131 "matches": [ | 107 "matches": [ |
132 { | |
133 "seq": "UUUUCC", | 108 "seq": "UUUUCC", |
134 "p": 147, | 109 "p": 147, |
135 "n": 75, | 110 "n": 75, |
136 "pvalue": "1.8e-007", | 111 "pvalue": "1.8e-007", |
137 "evalue": "2.2e-002" | 112 "evalue": "2.2e-002" |
138 }, { | |
139 "seq": "UUUUCU", | |
140 "p": 155, | |
141 "n": 94, | |
142 "pvalue": "2.2e-005", | |
143 "evalue": "2.8e+000" | |
144 }, { | |
145 "seq": "UUCUCU", | |
146 "p": 94, | |
147 "n": 51, | |
148 "pvalue": "1.3e-004", | |
149 "evalue": "1.7e+001" | |
150 }, { | |
151 "seq": "UUCUCC", | |
152 "p": 75, | |
153 "n": 42, | |
154 "pvalue": "1.1e-003", | |
155 "evalue": "1.4e+002" | |
156 } | |
157 ] | |
158 }, { | |
159 "db": 0, | |
160 "id": "YAGG", | |
161 "alt": "DREME-2", | |
162 "len": 4, | |
163 "nsites": 793, | |
164 "evalue": "1.4e-011", | |
165 "p": 600, | |
166 "n": 416, | |
167 "pvalue": "1.1e-016", | |
168 "unerased_evalue": "6.3e-012", | |
169 "pwm": [ | |
170 [0.000000, 0.692308, 0.000000, 0.307692], | |
171 [1.000000, 0.000000, 0.000000, 0.000000], | |
172 [0.000000, 0.000000, 1.000000, 0.000000], | |
173 [0.000000, 0.000000, 1.000000, 0.000000] | |
174 ], | |
175 "matches": [ | |
176 { | |
177 "seq": "CAGG", | |
178 "p": 441, | |
179 "n": 304, | |
180 "pvalue": "1.5e-010", | |
181 "evalue": "1.8e-005" | |
182 }, { | |
183 "seq": "UAGG", | |
184 "p": 232, | |
185 "n": 165, | |
186 "pvalue": "1.1e-004", | |
187 "evalue": "1.3e+001" | |
188 } | |
189 ] | |
190 } | |
191 ], | |
192 "runtime": { | |
193 "host": "ThinkPad-T450s", | |
194 "when": "Thu May 03 13:22:11 CEST 2018", | |
195 "cpu": 15.97, | |
196 "real": 15.97, | |
197 "stop": "evalue" | |
198 } | |
199 }; | |
200 </script> | 113 </script> |
201 <script> | |
202 var site_url = "http://meme-suite.org"; | 114 var site_url = "http://meme-suite.org"; |
203 </script> | |
204 <script> | |
205 | |
206 /* | |
207 * $ | |
208 * | |
209 * Shorthand function for getElementById | |
210 */ | |
211 function $(el) { | |
212 return document.getElementById(el); | |
213 } | |
214 | |
215 | |
216 /* | |
217 * See http://stackoverflow.com/a/5450113/66387 | |
218 * Does string multiplication like the perl x operator. | |
219 */ | |
220 function string_mult(pattern, count) { | |
221 if (count < 1) return ''; | |
222 var result = ''; | |
223 while (count > 1) { | |
224 if (count & 1) result += pattern; | |
225 count >>= 1, pattern += pattern; | |
226 } | |
227 return result + pattern; | |
228 } | |
229 | |
230 /* | |
231 * See http://stackoverflow.com/questions/814613/how-to-read-get-data-from-a-url-using-javascript | |
232 * Slightly modified with information from | |
233 * https://developer.mozilla.org/en/DOM/window.location | |
234 */ | |
235 function parse_params() { | |
236 "use strict"; | |
237 var search, queryStart, queryEnd, query, params, nvPairs, i, nv, n, v; | |
238 search = window.location.search; | |
239 queryStart = search.indexOf("?") + 1; | |
240 queryEnd = search.indexOf("#") + 1 || search.length + 1; | |
241 query = search.slice(queryStart, queryEnd - 1); | |
242 | |
243 if (query === search || query === "") return {}; | |
244 | |
245 params = {}; | |
246 nvPairs = query.replace(/\+/g, " ").split("&"); | |
247 | |
248 for (i = 0; i < nvPairs.length; i++) { | |
249 nv = nvPairs[i].split("="); | |
250 n = decodeURIComponent(nv[0]); | |
251 v = decodeURIComponent(nv[1]); | |
252 // allow a name to be used multiple times | |
253 // storing each value in the array | |
254 if (!(n in params)) { | |
255 params[n] = []; | |
256 } | |
257 params[n].push(nv.length === 2 ? v : null); | |
258 } | |
259 return params; | |
260 } | |
261 | |
262 /* | |
263 * coords | |
264 * | |
265 * Calculates the x and y offset of an element. | |
266 * From http://www.quirksmode.org/js/findpos.html | |
267 * with alterations to take into account scrolling regions | |
268 */ | |
269 function coords(elem) { | |
270 var myX = myY = 0; | |
271 if (elem.getBoundingClientRect) { | |
272 var rect; | |
273 rect = elem.getBoundingClientRect(); | |
274 myX = rect.left + ((typeof window.pageXOffset !== "undefined") ? | |
275 window.pageXOffset : document.body.scrollLeft); | |
276 myY = rect.top + ((typeof window.pageYOffset !== "undefined") ? | |
277 window.pageYOffset : document.body.scrollTop); | |
278 } else { | |
279 // this fall back doesn't properly handle absolutely positioned elements | |
280 // inside a scrollable box | |
281 var node; | |
282 if (elem.offsetParent) { | |
283 // subtract all scrolling | |
284 node = elem; | |
285 do { | |
286 myX -= node.scrollLeft ? node.scrollLeft : 0; | |
287 myY -= node.scrollTop ? node.scrollTop : 0; | |
288 } while (node = node.parentNode); | |
289 // this will include the page scrolling (which is unwanted) so add it back on | |
290 myX += (typeof window.pageXOffset !== "undefined") ? window.pageXOffset : document.body.scrollLeft; | |
291 myY += (typeof window.pageYOffset !== "undefined") ? window.pageYOffset : document.body.scrollTop; | |
292 // sum up offsets | |
293 node = elem; | |
294 do { | |
295 myX += node.offsetLeft; | |
296 myY += node.offsetTop; | |
297 } while (node = node.offsetParent); | |
298 } | |
299 } | |
300 return [myX, myY]; | |
301 } | |
302 | |
303 /* | |
304 * position_popup | |
305 * | |
306 * Positions a popup relative to an anchor element. | |
307 * | |
308 * The avaliable positions are: | |
309 * 0 - Centered below the anchor. | |
310 */ | |
311 function position_popup(anchor, popup, position) { | |
312 "use strict"; | |
313 var a_x, a_y, a_w, a_h, p_x, p_y, p_w, p_h; | |
314 var a_xy, spacer, margin, scrollbar, page_w; | |
315 // define constants | |
316 spacer = 5; | |
317 margin = 15; | |
318 scrollbar = 15; | |
319 // define the positions and widths | |
320 a_xy = coords(anchor); | |
321 a_x = a_xy[0]; | |
322 a_y = a_xy[1]; | |
323 a_w = anchor.offsetWidth; | |
324 a_h = anchor.offsetHeight; | |
325 p_w = popup.offsetWidth; | |
326 p_h = popup.offsetHeight; | |
327 page_w = null; | |
328 if (window.innerWidth) { | |
329 page_w = window.innerWidth; | |
330 } else if (document.body) { | |
331 page_w = document.body.clientWidth; | |
332 } | |
333 // check the position type is defined | |
334 if (typeof position !== "number") { | |
335 position = 0; | |
336 } | |
337 // calculate the popup position | |
338 switch (position) { | |
339 case 1: | |
340 p_x = a_x + a_w + spacer; | |
341 p_y = a_y + (a_h / 2) - (p_h / 2); | |
342 break; | |
343 case 0: | |
344 default: | |
345 p_x = a_x + (a_w / 2) - (p_w / 2); | |
346 p_y = a_y + a_h + spacer; | |
347 break; | |
348 } | |
349 // constrain the popup position | |
350 if (p_x < margin) { | |
351 p_x = margin; | |
352 } else if (page_w != null && (p_x + p_w) > (page_w - margin - scrollbar)) { | |
353 p_x = page_w - margin - scrollbar - p_w; | |
354 } | |
355 if (p_y < margin) { | |
356 p_y = margin; | |
357 } | |
358 // position the popup | |
359 popup.style.left = p_x + "px"; | |
360 popup.style.top = p_y + "px"; | |
361 } | |
362 | |
363 function lookup_help_popup(popup_id) { | |
364 var _body, pop, info; | |
365 pop = document.getElementById(popup_id); | |
366 if (pop == null) { | |
367 _body = document.getElementsByTagName("body")[0]; | |
368 pop = document.createElement("div"); | |
369 pop.className = "pop_content"; | |
370 pop.id = popup_id; | |
371 pop.style.backgroundColor = "#FFC"; | |
372 pop.style.borderColor = "black"; | |
373 info = document.createElement("p"); | |
374 info.style.fontWeight = "bold"; | |
375 info.appendChild(document.createTextNode("Error: No popup for topic \"" + popup_id + "\".")); | |
376 pop.appendChild(info); | |
377 // this might cause problems with the menu, but as this only happens | |
378 // when something is already wrong I don't think that's too much of a problem | |
379 _body.insertBefore(pop, _body.firstChild); | |
380 } | |
381 if (document.getElementsByTagName('body')[0].hasAttribute("data-autobtns")) { | |
382 if (!/\bauto_buttons\b/.test(pop.className)) { | |
383 pop.className += " auto_buttons"; | |
384 var back_btn_sec = document.createElement("div"); | |
385 back_btn_sec.className = "nested_only pop_back_sec"; | |
386 var back_btn = document.createElement("span"); | |
387 back_btn.className = "pop_back"; | |
388 back_btn.appendChild(document.createTextNode("<< back")); | |
389 back_btn.addEventListener("click", function(e) { | |
390 help_return(); | |
391 }, false); | |
392 back_btn_sec.appendChild(back_btn); | |
393 pop.insertBefore(back_btn_sec, pop.firstChild); | |
394 var close_btn_sec = document.createElement("div"); | |
395 close_btn_sec.className = "pop_close_sec"; | |
396 var close_btn = document.createElement("span"); | |
397 close_btn.className = "pop_close"; | |
398 close_btn.appendChild(document.createTextNode("close")); | |
399 close_btn.addEventListener("click", function(e) { | |
400 help_popup(); | |
401 }, false); | |
402 close_btn_sec.appendChild(close_btn); | |
403 pop.appendChild(close_btn_sec); | |
404 } | |
405 } | |
406 return pop; | |
407 } | |
408 | |
409 /* | |
410 * help_popup | |
411 * | |
412 * Moves around help pop-ups so they appear | |
413 * below an activator. | |
414 */ | |
415 function help_popup(activator, popup_id) { | |
416 "use strict"; | |
417 var pop; | |
418 // set default values | |
419 if (typeof help_popup.popup === "undefined") { | |
420 help_popup.popup = []; | |
421 } | |
422 if (typeof help_popup.activator === "undefined") { | |
423 help_popup.activator = null; | |
424 } | |
425 var last_pop = (help_popup.popup.length > 0 ? help_popup.popup[help_popup.popup.length - 1] : null); | |
426 if (typeof(activator) == "undefined") { // no activator so hide | |
427 if (last_pop != null) { | |
428 last_pop.style.display = 'none'; | |
429 help_popup.popup = []; | |
430 } | |
431 return; | |
432 } | |
433 pop = lookup_help_popup(popup_id); | |
434 if (pop == last_pop) { | |
435 if (activator == help_popup.activator) { | |
436 //hide popup (as we've already shown it for the current help button) | |
437 last_pop.style.display = 'none'; | |
438 help_popup.popup = []; | |
439 return; // toggling complete! | |
440 } | |
441 } else if (last_pop != null) { | |
442 //activating different popup so hide current one | |
443 last_pop.style.display = 'none'; | |
444 } | |
445 help_popup.popup = [pop]; | |
446 help_popup.activator = activator; | |
447 toggle_class(pop, "nested", false); | |
448 //must make the popup visible to measure it or it has zero width | |
449 pop.style.display = 'block'; | |
450 position_popup(activator, pop); | |
451 } | |
452 | |
453 /* | |
454 * help_refine | |
455 * | |
456 * Intended for links within a help popup. Stores a stack of state so | |
457 * you can go back. | |
458 */ | |
459 function help_refine(popup_id) { | |
460 if (help_popup.popup == null || help_popup.popup.length == 0 || help_popup.activator == null) { | |
461 throw new Error("Can not refine a help popup when one is not shown!"); | |
462 } | |
463 var pop = lookup_help_popup(popup_id); | |
464 var last_pop = help_popup.popup[help_popup.popup.length - 1]; | |
465 if (pop == last_pop) return; // slightly odd, but no real cause for alarm | |
466 help_popup.popup.push(pop); | |
467 toggle_class(pop, "nested", true); | |
468 last_pop.style.display = "none"; | |
469 //must make the popup visible to measure it or it has zero width | |
470 pop.style.display = "block"; | |
471 position_popup(help_popup.activator, pop); | |
472 } | |
473 | |
474 /* | |
475 * help_return | |
476 * | |
477 * Intended for links within a help popup. Stores a stack of state so | |
478 * you can go back. | |
479 */ | |
480 function help_return() { | |
481 if (help_popup.popup == null || help_popup.popup.length == 0 || help_popup.activator == null) { | |
482 throw new Error("Can not return to a earlier help popup when one is not shown!"); | |
483 } | |
484 var last_pop = help_popup.popup.pop(); | |
485 last_pop.style.display = "none"; | |
486 var pop = (help_popup.popup.length > 0 ? help_popup.popup[help_popup.popup.length - 1] : null); | |
487 if (pop != null) { | |
488 toggle_class(pop, "nested", help_popup.popup.length > 1); | |
489 pop.style.display = "block"; | |
490 position_popup(help_popup.activator, pop); | |
491 } else { | |
492 help_popup.activator = null; | |
493 } | |
494 } | |
495 | |
496 /* | |
497 * update_scroll_pad | |
498 * | |
499 * Creates padding at the bottom of the page to allow | |
500 * scrolling of anything into view. | |
501 */ | |
502 function update_scroll_pad() { | |
503 var page, pad; | |
504 page = (document.compatMode === "CSS1Compat") ? document.documentElement : document.body; | |
505 pad = $("scrollpad"); | |
506 if (pad === null) { | |
507 pad = document.createElement("div"); | |
508 pad.id = 'scrollpad'; | |
509 document.getElementsByTagName('body')[0].appendChild(pad); | |
510 } | |
511 pad.style.height = Math.abs(page.clientHeight - 100) + "px"; | |
512 } | |
513 | |
514 function substitute_classes(node, remove, add) { | |
515 "use strict"; | |
516 var list, all, i, cls, classes; | |
517 list = node.className.split(/\s+/); | |
518 all = {}; | |
519 for (i = 0; i < list.length; i++) { | |
520 if (list[i].length > 0) all[list[i]] = true; | |
521 } | |
522 for (i = 0; i < remove.length; i++) { | |
523 if (all.hasOwnProperty(remove[i])) { | |
524 delete all[remove[i]]; | |
525 } | |
526 } | |
527 for (i = 0; i < add.length; i++) { | |
528 all[add[i]] = true; | |
529 } | |
530 classes = ""; | |
531 for (cls in all) { | |
532 classes += cls + " "; | |
533 } | |
534 node.className = classes; | |
535 } | |
536 | |
537 /* | |
538 * toggle_class | |
539 * | |
540 * Adds or removes a class from the node. If the parameter 'enabled' is not | |
541 * passed then the existence of the class will be toggled, otherwise it will be | |
542 * included if enabled is true. | |
543 */ | |
544 function toggle_class(node, cls, enabled) { | |
545 var classes = node.className; | |
546 var list = classes.replace(/^\s+/, '').replace(/\s+$/, '').split(/\s+/); | |
547 var found = false; | |
548 for (var i = 0; i < list.length; i++) { | |
549 if (list[i] == cls) { | |
550 list.splice(i, 1); | |
551 i--; | |
552 found = true; | |
553 } | |
554 } | |
555 if (typeof enabled == "undefined") { | |
556 if (!found) list.push(cls); | |
557 } else { | |
558 if (enabled) list.push(cls); | |
559 } | |
560 node.className = list.join(" "); | |
561 } | |
562 | |
563 /* | |
564 * find_child | |
565 * | |
566 * Searches child nodes in depth first order and returns the first it finds | |
567 * with the className specified. | |
568 * TODO replace with querySelector | |
569 */ | |
570 function find_child(node, className) { | |
571 var pattern; | |
572 if (node == null || typeof node !== "object") { | |
573 return null; | |
574 } | |
575 if (typeof className === "string") { | |
576 pattern = new RegExp("\\b" + className + "\\b"); | |
577 } else { | |
578 pattern = className; | |
579 } | |
580 if (node.nodeType == Node.ELEMENT_NODE && | |
581 pattern.test(node.className)) { | |
582 return node; | |
583 } else { | |
584 var result = null; | |
585 for (var i = 0; i < node.childNodes.length; i++) { | |
586 result = find_child(node.childNodes[i], pattern); | |
587 if (result != null) break; | |
588 } | |
589 return result; | |
590 } | |
591 } | |
592 | |
593 /* | |
594 * find_parent | |
595 * | |
596 * Searches parent nodes outwards from the node and returns the first it finds | |
597 * with the className specified. | |
598 */ | |
599 function find_parent(node, className) { | |
600 var pattern; | |
601 pattern = new RegExp("\\b" + className + "\\b"); | |
602 do { | |
603 if (node.nodeType == Node.ELEMENT_NODE && | |
604 pattern.test(node.className)) { | |
605 return node; | |
606 } | |
607 } while (node = node.parentNode); | |
608 return null; | |
609 } | |
610 | |
611 /* | |
612 * find_parent_tag | |
613 * | |
614 * Searches parent nodes outwards from the node and returns the first it finds | |
615 * with the tag name specified. HTML tags should be specified in upper case. | |
616 */ | |
617 function find_parent_tag(node, tag_name) { | |
618 do { | |
619 if (node.nodeType == Node.ELEMENT_NODE && node.tagName == tag_name) { | |
620 return node; | |
621 } | |
622 } while (node = node.parentNode); | |
623 return null; | |
624 } | |
625 | |
626 /* | |
627 * __toggle_help | |
628 * | |
629 * Uses the 'topic' property of the this object to | |
630 * toggle display of a help topic. | |
631 * | |
632 * This function is not intended to be called directly. | |
633 */ | |
634 function __toggle_help(e) { | |
635 if (!e) e = window.event; | |
636 if (e.type === "keydown") { | |
637 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
638 return; | |
639 } | |
640 // stop a submit or something like that | |
641 e.preventDefault(); | |
642 } | |
643 | |
644 help_popup(this, this.getAttribute("data-topic")); | |
645 } | |
646 | |
647 function setup_help_button(button) { | |
648 "use strict"; | |
649 var topic; | |
650 if (button.hasAttribute("data-topic")) { | |
651 topic = button.getAttribute("data-topic"); | |
652 if (document.getElementById(topic) != null) { | |
653 button.tabIndex = "0"; // make keyboard selectable | |
654 button.addEventListener("click", function() { | |
655 help_popup(button, topic); | |
656 }, false); | |
657 button.addEventListener("keydown", function(e) { | |
658 // toggle only on Enter or Spacebar, let other keys do their thing | |
659 if (e.keyCode !== 13 && e.keyCode !== 32) return; | |
660 // stop a submit or something like that | |
661 e.preventDefault(); | |
662 help_popup(button, topic); | |
663 }, false); | |
664 } else { | |
665 button.style.visibility = "hidden"; | |
666 } | |
667 } | |
668 button.className += " active"; | |
669 } | |
670 | |
671 /* | |
672 * help_button | |
673 * | |
674 * Makes a help button for the passed topic. | |
675 */ | |
676 function help_button(topic) { | |
677 var btn = document.createElement("div"); | |
678 btn.className = "help"; | |
679 btn.setAttribute("data-topic", topic); | |
680 setup_help_button(btn); | |
681 return btn; | |
682 } | |
683 | |
684 /* | |
685 * prepare_download | |
686 * | |
687 * Sets the attributes of a link to setup a file download using the given content. | |
688 * If no link is provided then create one and click it. | |
689 */ | |
690 function prepare_download(content, mimetype, filename, link) { | |
691 "use strict"; | |
692 // if no link is provided then create one and click it | |
693 var click_link = false; | |
694 if (!link) { | |
695 link = document.createElement("a"); | |
696 click_link = true; | |
697 } | |
698 try { | |
699 // Use a BLOB to convert the text into a data URL. | |
700 // We could do this manually with a base 64 conversion. | |
701 // This will only be supported on modern browsers, | |
702 // hence the try block. | |
703 var blob = new Blob([content], {type: mimetype}); | |
704 var reader = new FileReader(); | |
705 reader.onloadend = function() { | |
706 // If we're lucky the browser will also support the download | |
707 // attribute which will let us suggest a file name to save the link. | |
708 // Otherwise it is likely that the filename will be unintelligible. | |
709 link.setAttribute("download", filename); | |
710 link.href = reader.result; | |
711 if (click_link) { | |
712 // must add the link to click it | |
713 document.body.appendChild(link); | |
714 link.click(); | |
715 document.body.removeChild(link); | |
716 } | |
717 } | |
718 reader.readAsDataURL(blob); | |
719 } catch (error) { | |
720 if (console && console.log) console.log(error); | |
721 // probably an old browser | |
722 link.href = ""; | |
723 link.visible = false; | |
724 } | |
725 } | |
726 | |
727 /* | |
728 * add_cell | |
729 * | |
730 * Add a cell to the table row. | |
731 */ | |
732 function add_cell(row, node, cls, click_action) { | |
733 var cell = row.insertCell(row.cells.length); | |
734 if (node) cell.appendChild(node); | |
735 if (cls && cls !== "") cell.className = cls; | |
736 if (click_action) cell.addEventListener("click", click_action, false); | |
737 } | |
738 | |
739 /* | |
740 * add_header_cell | |
741 * | |
742 * Add a header cell to the table row. | |
743 */ | |
744 function add_header_cell(row, node, help_topic, cls, colspan) { | |
745 var th = document.createElement("th"); | |
746 if (node) th.appendChild(node); | |
747 if (help_topic && help_topic !== "") th.appendChild(help_button(help_topic)); | |
748 if (cls && cls !== "") th.className = cls; | |
749 if (typeof colspan == "number" && colspan > 1) th.colSpan = colspan; | |
750 row.appendChild(th); | |
751 } | |
752 | |
753 /* | |
754 * add_text_cell | |
755 * | |
756 * Add a text cell to the table row. | |
757 */ | |
758 function add_text_cell(row, text, cls, action) { | |
759 var node = null; | |
760 if (typeof(text) != 'undefined') node = document.createTextNode(text); | |
761 add_cell(row, node, cls, action); | |
762 } | |
763 | |
764 /* | |
765 * add_text_header_cell | |
766 * | |
767 * Add a text header cell to the table row. | |
768 */ | |
769 function add_text_header_cell(row, text, help_topic, cls, action, colspan) { | |
770 var node = null; | |
771 if (typeof(text) != 'undefined') { | |
772 var nbsp = (help_topic ? "\u00A0" : ""); | |
773 var str = "" + text; | |
774 var parts = str.split(/\n/); | |
775 if (parts.length === 1) { | |
776 if (action) { | |
777 node = document.createElement("span"); | |
778 node.appendChild(document.createTextNode(str + nbsp)); | |
779 } else { | |
780 node = document.createTextNode(str + nbsp); | |
781 } | |
782 } else { | |
783 node = document.createElement("span"); | |
784 for (var i = 0; i < parts.length; i++) { | |
785 if (i !== 0) { | |
786 node.appendChild(document.createElement("br")); | |
787 } | |
788 node.appendChild(document.createTextNode(parts[i])); | |
789 } | |
790 } | |
791 if (action) { | |
792 node.addEventListener("click", action, false); | |
793 node.style.cursor = "pointer"; | |
794 } | |
795 } | |
796 add_header_cell(row, node, help_topic, cls, colspan); | |
797 } | |
798 | |
799 function setup_help() { | |
800 "use strict"; | |
801 var help_buttons, i; | |
802 help_buttons = document.querySelectorAll(".help:not(.active)"); | |
803 for (i = 0; i < help_buttons.length; i++) { | |
804 setup_help_button(help_buttons[i]); | |
805 } | |
806 } | |
807 | |
808 function setup_scrollpad() { | |
809 "use strict"; | |
810 if (document.getElementsByTagName('body')[0].hasAttribute("data-scrollpad") && document.getElementById("scrollpad") == null) { | |
811 window.addEventListener("resize", update_scroll_pad, false); | |
812 update_scroll_pad(); | |
813 } | |
814 } | |
815 | |
816 // anon function to avoid polluting global scope | |
817 (function() { | |
818 "use strict"; | |
819 window.addEventListener("load", function load(evt) { | |
820 window.removeEventListener("load", load, false); | |
821 setup_help(); | |
822 setup_scrollpad(); | |
823 }, false); | |
824 })(); | |
825 | |
826 /* | |
827 * make_link | |
828 * | |
829 * Creates a text node and if a URL is specified it surrounds it with a link. | |
830 * If the URL doesn't begin with "http://" it automatically adds it, as | |
831 * relative links don't make much sense in this context. | |
832 */ | |
833 function make_link(text, url) { | |
834 var textNode = null; | |
835 var link = null; | |
836 if (typeof text !== "undefined" && text !== null) textNode = document.createTextNode(text); | |
837 if (typeof url === "string") { | |
838 if (url.indexOf("//") == -1) { | |
839 url = "http://" + url; | |
840 } | |
841 link = document.createElement('a'); | |
842 link.href = url; | |
843 if (textNode) link.appendChild(textNode); | |
844 return link; | |
845 } | |
846 return textNode; | |
847 } | |
848 </script> | |
849 <script> | |
850 function motif_logo_template(inputs) { | |
851 function _input(name) { | |
852 if (typeof inputs[name] === "undefined") { | |
853 throw new Error("Missing template variable: " + name); | |
854 } | |
855 return inputs[name]; | |
856 } | |
857 return ( | |
858 "%!PS-Adobe-3.0 EPSF-3.0\n" + | |
859 "%%Title: Sequence Logo : " + _input("TITLE") + "\n" + | |
860 "%%Creator: " + _input("CREATOR") + "\n" + | |
861 "%%CreationDate: " + _input("CREATIONDATE") + "\n" + | |
862 "%%BoundingBox: 0 0 " + _input("BOUNDINGWIDTH") + " " + _input("BOUNDINGHEIGHT") + " \n" + | |
863 "%%Pages: 0\n" + | |
864 "%%DocumentFonts: \n" + | |
865 "%%EndComments\n" + | |
866 "\n" + | |
867 "% ---- CONSTANTS ----\n" + | |
868 "\/cmfactor 72 2.54 div def % defines points -> cm conversion\n" + | |
869 "\/cm {cmfactor mul} bind def % defines centimeters\n" + | |
870 "\n" + | |
871 "% ---- VARIABLES ----\n" + | |
872 "\n" + | |
873 "% NA = Nucleic Acid, AA = Amino Acid\n" + | |
874 "\/logoType (" + _input("LOGOTYPE") + ") def \n" + | |
875 "\n" + | |
876 "\/logoTitle (" + _input("TITLE") + ") def\n" + | |
877 "\n" + | |
878 "% Dimensions in cm\n" + | |
879 "\/logoWidth " + _input("LOGOWIDTH") + " cm def\n" + | |
880 "\/logoHeight " + _input("LOGOLINEHEIGHT") + " cm def\n" + | |
881 "\/totalHeight " + _input("LOGOHEIGHT") + " cm def\n" + | |
882 "\n" + | |
883 "\/yaxis " + _input("YAXIS") + " def\n" + | |
884 "\/yaxisLabel (" + _input("YAXISLABEL") + ") def\n" + | |
885 "\/yaxisBits " + _input("BARBITS") + " def % bits\n" + | |
886 "\/yaxisTicBits " + _input("TICBITS") + " def\n" + | |
887 "\n" + | |
888 "\/xaxis " + _input("NUMBERING") + " def\n" + | |
889 "\/xaxisLabel (" + _input("XAXISLABEL") + ") def\n" + | |
890 "\/showEnds (" + _input("SHOWENDS") + ") def \n" + | |
891 "\n" + | |
892 "\/showFineprint true def\n" + | |
893 "\/fineprint (" + _input("FINEPRINT") + ") def\n" + | |
894 "\n" + | |
895 "\/charsPerLine " + _input("CHARSPERLINE") + " def\n" + | |
896 "\n" + | |
897 "\/showingBox " + _input("SHOWINGBOX") + " def \n" + | |
898 "\/shrinking false def % true falses\n" + | |
899 "\/shrink 1.0 def\n" + | |
900 "\/outline " + _input("OUTLINE") + " def\n" + | |
901 "\n" + | |
902 "\/IbeamFraction " + _input("ERRORBARFRACTION") + " def\n" + | |
903 "\/IbeamGray 0.50 def\n" + | |
904 "\/IbeamLineWidth 0.5 def\n" + | |
905 "\n" + | |
906 "\/fontsize " + _input("FONTSIZE") + " def\n" + | |
907 "\/titleFontsize " + _input("TITLEFONTSIZE") + " def\n" + | |
908 "\/smallFontsize " + _input("SMALLFONTSIZE") + " def\n" + | |
909 "\n" + | |
910 "\/topMargin " + _input("TOPMARGIN") + " cm def\n" + | |
911 "\/bottomMargin " + _input("BOTTOMMARGIN") + " cm def\n" + | |
912 "\n" + | |
913 "\/defaultColor [0 0 0] def \n" + | |
914 "\n" + | |
915 _input("COLORDICT") + "\n" + | |
916 "\n" + | |
917 "\/colorDict fullColourDict def\n" + | |
918 "\n" + | |
919 "% ---- DERIVED PARAMETERS ----\n" + | |
920 "\n" + | |
921 "\/leftMargin\n" + | |
922 " fontsize 3.5 mul\n" + | |
923 "\n" + | |
924 "def \n" + | |
925 "\n" + | |
926 "\/rightMargin \n" + | |
927 " %Add extra room if showing ends\n" + | |
928 " showEnds (false) eq { fontsize}{fontsize 1.5 mul} ifelse\n" + | |
929 "def\n" + | |
930 "\n" + | |
931 "\/yaxisHeight \n" + | |
932 " logoHeight \n" + | |
933 " bottomMargin sub \n" + | |
934 " topMargin sub\n" + | |
935 "def\n" + | |
936 "\n" + | |
937 "\/ticWidth fontsize 2 div def\n" + | |
938 "\n" + | |
939 "\/pointsPerBit yaxisHeight yaxisBits div def\n" + | |
940 "\n" + | |
941 "\/stackMargin 1 def\n" + | |
942 "\n" + | |
943 "% Do not add space aroung characters if characters are boxed\n" + | |
944 "\/charRightMargin \n" + | |
945 " showingBox { 0.0 } {stackMargin} ifelse\n" + | |
946 "def\n" + | |
947 "\n" + | |
948 "\/charTopMargin \n" + | |
949 " showingBox { 0.0 } {stackMargin} ifelse\n" + | |
950 "def\n" + | |
951 "\n" + | |
952 "\/charWidth\n" + | |
953 " logoWidth\n" + | |
954 " leftMargin sub\n" + | |
955 " rightMargin sub\n" + | |
956 " charsPerLine div\n" + | |
957 " charRightMargin sub\n" + | |
958 "def\n" + | |
959 "\n" + | |
960 "\/charWidth4 charWidth 4 div def\n" + | |
961 "\/charWidth2 charWidth 2 div def\n" + | |
962 "\n" + | |
963 "\/stackWidth \n" + | |
964 " charWidth charRightMargin add\n" + | |
965 "def\n" + | |
966 " \n" + | |
967 "\/numberFontsize \n" + | |
968 " fontsize charWidth lt {fontsize}{charWidth} ifelse\n" + | |
969 "def\n" + | |
970 "\n" + | |
971 "% movements to place 5'\/N and 3'\/C symbols\n" + | |
972 "\/leftEndDeltaX fontsize neg def\n" + | |
973 "\/leftEndDeltaY fontsize 1.5 mul neg def\n" + | |
974 "\/rightEndDeltaX fontsize 0.25 mul def\n" + | |
975 "\/rightEndDeltaY leftEndDeltaY def\n" + | |
976 "\n" + | |
977 "% Outline width is proporional to charWidth, \n" + | |
978 "% but no less that 1 point\n" + | |
979 "\/outlinewidth \n" + | |
980 " charWidth 32 div dup 1 gt {}{pop 1} ifelse\n" + | |
981 "def\n" + | |
982 "\n" + | |
983 "\n" + | |
984 "% ---- PROCEDURES ----\n" + | |
985 "\n" + | |
986 "\/StartLogo { \n" + | |
987 " % Save state\n" + | |
988 " save \n" + | |
989 " gsave \n" + | |
990 "\n" + | |
991 " % Print Logo Title, top center \n" + | |
992 " gsave \n" + | |
993 " SetStringFont\n" + | |
994 "\n" + | |
995 " logoWidth 2 div\n" + | |
996 " logoTitle\n" + | |
997 " stringwidth pop 2 div sub\n" + | |
998 " totalHeight\n" + | |
999 " titleFontsize sub\n" + | |
1000 " moveto\n" + | |
1001 "\n" + | |
1002 " logoTitle\n" + | |
1003 " show\n" + | |
1004 " grestore\n" + | |
1005 "\n" + | |
1006 " % Print X-axis label, bottom center\n" + | |
1007 " gsave\n" + | |
1008 " SetStringFont\n" + | |
1009 "\n" + | |
1010 " logoWidth 2 div\n" + | |
1011 " xaxisLabel\n" + | |
1012 " stringwidth pop 2 div sub\n" + | |
1013 " 0\n" + | |
1014 " titleFontsize 3 div\n" + | |
1015 " add\n" + | |
1016 " moveto\n" + | |
1017 "\n" + | |
1018 " xaxisLabel\n" + | |
1019 " show\n" + | |
1020 " grestore\n" + | |
1021 "\n" + | |
1022 " % Show Fine Print\n" + | |
1023 " showFineprint {\n" + | |
1024 " gsave\n" + | |
1025 " SetSmallFont\n" + | |
1026 " logoWidth\n" + | |
1027 " fineprint stringwidth pop sub\n" + | |
1028 " smallFontsize sub\n" + | |
1029 " smallFontsize 3 div\n" + | |
1030 " moveto\n" + | |
1031 " \n" + | |
1032 " fineprint show\n" + | |
1033 " grestore\n" + | |
1034 " } if\n" + | |
1035 "\n" + | |
1036 " % Move to lower left corner of last line, first stack\n" + | |
1037 " leftMargin bottomMargin translate\n" + | |
1038 "\n" + | |
1039 " % Move above first line ready for StartLine \n" + | |
1040 " 0 totalHeight translate\n" + | |
1041 "\n" + | |
1042 " SetLogoFont\n" + | |
1043 "} bind def\n" + | |
1044 "\n" + | |
1045 "\/EndLogo { \n" + | |
1046 " grestore \n" + | |
1047 " showpage \n" + | |
1048 " restore \n" + | |
1049 "} bind def\n" + | |
1050 "\n" + | |
1051 "\n" + | |
1052 "\/StartLine { \n" + | |
1053 " % move down to the bottom of the line:\n" + | |
1054 " 0 logoHeight neg translate\n" + | |
1055 " \n" + | |
1056 " gsave \n" + | |
1057 " yaxis { MakeYaxis } if\n" + | |
1058 " xaxis { showEnds (true) eq {ShowLeftEnd} if } if\n" + | |
1059 "} bind def\n" + | |
1060 "\n" + | |
1061 "\/EndLine{ \n" + | |
1062 " xaxis { showEnds (true) eq {ShowRightEnd} if } if\n" + | |
1063 " grestore \n" + | |
1064 "} bind def\n" + | |
1065 "\n" + | |
1066 "\n" + | |
1067 "\/MakeYaxis {\n" + | |
1068 " gsave \n" + | |
1069 " stackMargin neg 0 translate\n" + | |
1070 " ShowYaxisBar\n" + | |
1071 " ShowYaxisLabel\n" + | |
1072 " grestore\n" + | |
1073 "} bind def\n" + | |
1074 "\n" + | |
1075 "\n" + | |
1076 "\/ShowYaxisBar { \n" + | |
1077 " gsave \n" + | |
1078 " SetStringFont\n" + | |
1079 "\n" + | |
1080 " \/str 10 string def % string to hold number \n" + | |
1081 " \/smallgap stackMargin 2 div def\n" + | |
1082 "\n" + | |
1083 " % Draw first tic and bar\n" + | |
1084 " gsave \n" + | |
1085 " ticWidth neg 0 moveto \n" + | |
1086 " ticWidth 0 rlineto \n" + | |
1087 " 0 yaxisHeight rlineto\n" + | |
1088 " stroke\n" + | |
1089 " grestore\n" + | |
1090 "\n" + | |
1091 " \n" + | |
1092 " % Draw the tics\n" + | |
1093 " % initial increment limit proc for\n" + | |
1094 " 0 yaxisTicBits yaxisBits abs %cvi\n" + | |
1095 " {\/loopnumber exch def\n" + | |
1096 "\n" + | |
1097 " % convert the number coming from the loop to a string\n" + | |
1098 " % and find its width\n" + | |
1099 " loopnumber 10 str cvrs\n" + | |
1100 " \/stringnumber exch def % string representing the number\n" + | |
1101 "\n" + | |
1102 " stringnumber stringwidth pop\n" + | |
1103 " \/numberwidth exch def % width of number to show\n" + | |
1104 "\n" + | |
1105 " \/halfnumberheight\n" + | |
1106 " stringnumber CharBoxHeight 2 div\n" + | |
1107 " def\n" + | |
1108 "\n" + | |
1109 " numberwidth % move back width of number\n" + | |
1110 " neg loopnumber pointsPerBit mul % shift on y axis\n" + | |
1111 " halfnumberheight sub % down half the digit\n" + | |
1112 "\n" + | |
1113 " moveto % move back the width of the string\n" + | |
1114 "\n" + | |
1115 " ticWidth neg smallgap sub % Move back a bit more \n" + | |
1116 " 0 rmoveto % move back the width of the tic \n" + | |
1117 "\n" + | |
1118 " stringnumber show\n" + | |
1119 " smallgap 0 rmoveto % Make a small gap \n" + | |
1120 "\n" + | |
1121 " % now show the tic mark\n" + | |
1122 " 0 halfnumberheight rmoveto % shift up again\n" + | |
1123 " ticWidth 0 rlineto\n" + | |
1124 " stroke\n" + | |
1125 " } for\n" + | |
1126 " grestore\n" + | |
1127 "} bind def\n" + | |
1128 "\n" + | |
1129 "\/ShowYaxisLabel {\n" + | |
1130 " gsave\n" + | |
1131 " SetStringFont\n" + | |
1132 "\n" + | |
1133 " % How far we move left depends on the size of\n" + | |
1134 " % the tic labels.\n" + | |
1135 " \/str 10 string def % string to hold number \n" + | |
1136 " yaxisBits yaxisTicBits div cvi yaxisTicBits mul \n" + | |
1137 " str cvs stringwidth pop\n" + | |
1138 " ticWidth 1.5 mul add neg \n" + | |
1139 "\n" + | |
1140 "\n" + | |
1141 " yaxisHeight\n" + | |
1142 " yaxisLabel stringwidth pop\n" + | |
1143 " sub 2 div\n" + | |
1144 "\n" + | |
1145 " translate\n" + | |
1146 " 90 rotate\n" + | |
1147 " 0 0 moveto\n" + | |
1148 " yaxisLabel show\n" + | |
1149 " grestore\n" + | |
1150 "} bind def\n" + | |
1151 "\n" + | |
1152 "\n" + | |
1153 "\/StartStack { % <stackNumber> startstack\n" + | |
1154 " xaxis {MakeNumber}{pop} ifelse\n" + | |
1155 " gsave\n" + | |
1156 "} bind def\n" + | |
1157 "\n" + | |
1158 "\/EndStack {\n" + | |
1159 " grestore\n" + | |
1160 " stackWidth 0 translate\n" + | |
1161 "} bind def\n" + | |
1162 "\n" + | |
1163 "\n" + | |
1164 "% Draw a character whose height is proportional to symbol bits\n" + | |
1165 "\/MakeSymbol{ % charbits character MakeSymbol\n" + | |
1166 " gsave\n" + | |
1167 " \/char exch def\n" + | |
1168 " \/bits exch def\n" + | |
1169 "\n" + | |
1170 " \/bitsHeight \n" + | |
1171 " bits pointsPerBit mul \n" + | |
1172 " def\n" + | |
1173 "\n" + | |
1174 " \/charHeight \n" + | |
1175 " bitsHeight charTopMargin sub\n" + | |
1176 " dup \n" + | |
1177 " 0.0 gt {}{pop 0.0} ifelse % if neg replace with zero \n" + | |
1178 " def \n" + | |
1179 " \n" + | |
1180 " charHeight 0.0 gt {\n" + | |
1181 " char SetColor\n" + | |
1182 " charWidth charHeight char ShowChar\n" + | |
1183 "\n" + | |
1184 " showingBox { % Unfilled box\n" + | |
1185 " 0 0 charWidth charHeight false ShowBox\n" + | |
1186 " } if\n" + | |
1187 "\n" + | |
1188 "\n" + | |
1189 " } if\n" + | |
1190 "\n" + | |
1191 " grestore\n" + | |
1192 "\n" + | |
1193 " 0 bitsHeight translate \n" + | |
1194 "} bind def\n" + | |
1195 "\n" + | |
1196 "\n" + | |
1197 "\/ShowChar { % <width> <height> <char> ShowChar\n" + | |
1198 " gsave\n" + | |
1199 " \/tc exch def % The character\n" + | |
1200 " \/ysize exch def % the y size of the character\n" + | |
1201 " \/xsize exch def % the x size of the character\n" + | |
1202 "\n" + | |
1203 " \/xmulfactor 1 def \n" + | |
1204 " \/ymulfactor 1 def\n" + | |
1205 " \/limmulfactor 0.01 def\n" + | |
1206 " \/drawable true def\n" + | |
1207 "\n" + | |
1208 " \n" + | |
1209 " % if ysize is negative, make everything upside down!\n" + | |
1210 " ysize 0 lt {\n" + | |
1211 " % put ysize normal in this orientation\n" + | |
1212 " \/ysize ysize abs def\n" + | |
1213 " xsize ysize translate\n" + | |
1214 " 180 rotate\n" + | |
1215 " } if\n" + | |
1216 "\n" + | |
1217 " shrinking {\n" + | |
1218 " xsize 1 shrink sub 2 div mul\n" + | |
1219 " ysize 1 shrink sub 2 div mul translate \n" + | |
1220 "\n" + | |
1221 " shrink shrink scale\n" + | |
1222 " } if\n" + | |
1223 "\n" + | |
1224 " % Calculate the font scaling factors\n" + | |
1225 " % Loop twice to catch small correction due to first scaling\n" + | |
1226 " 2 {\n" + | |
1227 " gsave\n" + | |
1228 " xmulfactor ymulfactor scale\n" + | |
1229 " \n" + | |
1230 " ysize % desired size of character in points\n" + | |
1231 " tc CharBoxHeight \n" + | |
1232 " dup 0.0 ne {\n" + | |
1233 " div % factor by which to scale up the character\n" + | |
1234 " \/ymulfactor exch def\n" + | |
1235 " } % end if\n" + | |
1236 " {pop pop}\n" + | |
1237 " ifelse\n" + | |
1238 "\n" + | |
1239 " xsize % desired size of character in points\n" + | |
1240 " tc CharBoxWidth \n" + | |
1241 " dup 0.0 ne {\n" + | |
1242 " div % factor by which to scale up the character\n" + | |
1243 " \/xmulfactor exch def\n" + | |
1244 " } % end if\n" + | |
1245 " {pop pop}\n" + | |
1246 " ifelse\n" + | |
1247 " grestore\n" + | |
1248 " % if the multiplication factors get too small we need to avoid a crash\n" + | |
1249 " xmulfactor limmulfactor lt {\n" + | |
1250 " \/xmulfactor 1 def\n" + | |
1251 " \/drawable false def\n" + | |
1252 " } if\n" + | |
1253 " ymulfactor limmulfactor lt {\n" + | |
1254 " \/ymulfactor 1 def\n" + | |
1255 " \/drawable false def\n" + | |
1256 " } if\n" + | |
1257 " } repeat\n" + | |
1258 "\n" + | |
1259 " % Adjust horizontal position if the symbol is an I\n" + | |
1260 " tc (I) eq {\n" + | |
1261 " charWidth 2 div % half of requested character width\n" + | |
1262 " tc CharBoxWidth 2 div % half of the actual character\n" + | |
1263 " sub 0 translate\n" + | |
1264 " % Avoid x scaling for I \n" + | |
1265 " \/xmulfactor 1 def \n" + | |
1266 " } if\n" + | |
1267 "\n" + | |
1268 "\n" + | |
1269 " % ---- Finally, draw the character\n" + | |
1270 " drawable { \n" + | |
1271 " newpath\n" + | |
1272 " xmulfactor ymulfactor scale\n" + | |
1273 "\n" + | |
1274 " % Move lower left corner of character to start point\n" + | |
1275 " tc CharBox pop pop % llx lly : Lower left corner\n" + | |
1276 " exch neg exch neg\n" + | |
1277 " moveto\n" + | |
1278 "\n" + | |
1279 " outline { % outline characters:\n" + | |
1280 " outlinewidth setlinewidth\n" + | |
1281 " tc true charpath\n" + | |
1282 " gsave 1 setgray fill grestore\n" + | |
1283 " clip stroke\n" + | |
1284 " } { % regular characters\n" + | |
1285 " tc show\n" + | |
1286 " } ifelse\n" + | |
1287 " } if\n" + | |
1288 "\n" + | |
1289 " grestore\n" + | |
1290 "} bind def\n" + | |
1291 "\n" + | |
1292 "\n" + | |
1293 "\/ShowBox { % x1 y1 x2 y2 filled ShowBox\n" + | |
1294 " gsave\n" + | |
1295 " \/filled exch def \n" + | |
1296 " \/y2 exch def\n" + | |
1297 " \/x2 exch def\n" + | |
1298 " \/y1 exch def\n" + | |
1299 " \/x1 exch def\n" + | |
1300 " newpath\n" + | |
1301 " x1 y1 moveto\n" + | |
1302 " x2 y1 lineto\n" + | |
1303 " x2 y2 lineto\n" + | |
1304 " x1 y2 lineto\n" + | |
1305 " closepath\n" + | |
1306 "\n" + | |
1307 " clip\n" + | |
1308 " \n" + | |
1309 " filled {\n" + | |
1310 " fill\n" + | |
1311 " }{ \n" + | |
1312 " 0 setgray stroke \n" + | |
1313 " } ifelse\n" + | |
1314 "\n" + | |
1315 " grestore\n" + | |
1316 "} bind def\n" + | |
1317 "\n" + | |
1318 "\n" + | |
1319 "\/MakeNumber { % number MakeNumber\n" + | |
1320 " gsave\n" + | |
1321 " SetNumberFont\n" + | |
1322 " stackWidth 0 translate\n" + | |
1323 " 90 rotate % rotate so the number fits\n" + | |
1324 " dup stringwidth pop % find the length of the number\n" + | |
1325 " neg % prepare for move\n" + | |
1326 " stackMargin sub % Move back a bit\n" + | |
1327 " charWidth (0) CharBoxHeight % height of numbers\n" + | |
1328 " sub 2 div %\n" + | |
1329 " moveto % move back to provide space\n" + | |
1330 " show\n" + | |
1331 " grestore\n" + | |
1332 "} bind def\n" + | |
1333 "\n" + | |
1334 "\n" + | |
1335 "\/Ibeam{ % heightInBits Ibeam\n" + | |
1336 " gsave\n" + | |
1337 " % Make an Ibeam of twice the given height in bits\n" + | |
1338 " \/height exch pointsPerBit mul def \n" + | |
1339 " \/heightDRAW height IbeamFraction mul def\n" + | |
1340 "\n" + | |
1341 " IbeamLineWidth setlinewidth\n" + | |
1342 " IbeamGray setgray \n" + | |
1343 "\n" + | |
1344 " charWidth2 height neg translate\n" + | |
1345 " ShowIbar\n" + | |
1346 " newpath\n" + | |
1347 " 0 0 moveto\n" + | |
1348 " 0 heightDRAW rlineto\n" + | |
1349 " stroke\n" + | |
1350 " newpath\n" + | |
1351 " 0 height moveto\n" + | |
1352 " 0 height rmoveto\n" + | |
1353 " currentpoint translate\n" + | |
1354 " ShowIbar\n" + | |
1355 " newpath\n" + | |
1356 " 0 0 moveto\n" + | |
1357 " 0 heightDRAW neg rlineto\n" + | |
1358 " currentpoint translate\n" + | |
1359 " stroke\n" + | |
1360 " grestore\n" + | |
1361 "} bind def\n" + | |
1362 "\n" + | |
1363 "\n" + | |
1364 "\/ShowIbar { % make a horizontal bar\n" + | |
1365 " gsave\n" + | |
1366 " newpath\n" + | |
1367 " charWidth4 neg 0 moveto\n" + | |
1368 " charWidth4 0 lineto\n" + | |
1369 " stroke\n" + | |
1370 " grestore\n" + | |
1371 "} bind def\n" + | |
1372 "\n" + | |
1373 "\n" + | |
1374 "\/ShowLeftEnd {\n" + | |
1375 " gsave\n" + | |
1376 " SetStringFont\n" + | |
1377 " leftEndDeltaX leftEndDeltaY moveto\n" + | |
1378 " logoType (NA) eq {(5) show ShowPrime} if\n" + | |
1379 " logoType (AA) eq {(N) show} if\n" + | |
1380 " grestore\n" + | |
1381 "} bind def\n" + | |
1382 "\n" + | |
1383 "\n" + | |
1384 "\/ShowRightEnd { \n" + | |
1385 " gsave\n" + | |
1386 " SetStringFont\n" + | |
1387 " rightEndDeltaX rightEndDeltaY moveto\n" + | |
1388 " logoType (NA) eq {(3) show ShowPrime} if\n" + | |
1389 " logoType (AA) eq {(C) show} if\n" + | |
1390 " grestore\n" + | |
1391 "} bind def\n" + | |
1392 "\n" + | |
1393 "\n" + | |
1394 "\/ShowPrime {\n" + | |
1395 " gsave\n" + | |
1396 " SetPrimeFont\n" + | |
1397 " (\\242) show \n" + | |
1398 " grestore\n" + | |
1399 "} bind def\n" + | |
1400 "\n" + | |
1401 " \n" + | |
1402 "\/SetColor{ % <char> SetColor\n" + | |
1403 " dup colorDict exch known {\n" + | |
1404 " colorDict exch get aload pop setrgbcolor\n" + | |
1405 " } {\n" + | |
1406 " pop\n" + | |
1407 " defaultColor aload pop setrgbcolor\n" + | |
1408 " } ifelse \n" + | |
1409 "} bind def\n" + | |
1410 "\n" + | |
1411 "% define fonts\n" + | |
1412 "\/SetTitleFont {\/Times-Bold findfont titleFontsize scalefont setfont} bind def\n" + | |
1413 "\/SetLogoFont {\/Helvetica-Bold findfont charWidth scalefont setfont} bind def\n" + | |
1414 "\/SetStringFont{\/Helvetica-Bold findfont fontsize scalefont setfont} bind def\n" + | |
1415 "\/SetPrimeFont {\/Symbol findfont fontsize scalefont setfont} bind def\n" + | |
1416 "\/SetSmallFont {\/Helvetica findfont smallFontsize scalefont setfont} bind def\n" + | |
1417 "\n" + | |
1418 "\/SetNumberFont {\n" + | |
1419 " \/Helvetica-Bold findfont \n" + | |
1420 " numberFontsize\n" + | |
1421 " scalefont\n" + | |
1422 " setfont\n" + | |
1423 "} bind def\n" + | |
1424 "\n" + | |
1425 "%Take a single character and return the bounding box\n" + | |
1426 "\/CharBox { % <char> CharBox <lx> <ly> <ux> <uy>\n" + | |
1427 " gsave\n" + | |
1428 " newpath\n" + | |
1429 " 0 0 moveto\n" + | |
1430 " % take the character off the stack and use it here:\n" + | |
1431 " true charpath \n" + | |
1432 " flattenpath \n" + | |
1433 " pathbbox % compute bounding box of 1 pt. char => lx ly ux uy\n" + | |
1434 " % the path is here, but toss it away ...\n" + | |
1435 " grestore\n" + | |
1436 "} bind def\n" + | |
1437 "\n" + | |
1438 "\n" + | |
1439 "% The height of a characters bounding box\n" + | |
1440 "\/CharBoxHeight { % <char> CharBoxHeight <num>\n" + | |
1441 " CharBox\n" + | |
1442 " exch pop sub neg exch pop\n" + | |
1443 "} bind def\n" + | |
1444 "\n" + | |
1445 "\n" + | |
1446 "% The width of a characters bounding box\n" + | |
1447 "\/CharBoxWidth { % <char> CharBoxHeight <num>\n" + | |
1448 " CharBox\n" + | |
1449 " pop exch pop sub neg \n" + | |
1450 "} bind def\n" + | |
1451 "\n" + | |
1452 "% Set the colour scheme to be faded to indicate trimming\n" + | |
1453 "\/MuteColour {\n" + | |
1454 " \/colorDict mutedColourDict def\n" + | |
1455 "} def\n" + | |
1456 "\n" + | |
1457 "% Restore the colour scheme to the normal colours\n" + | |
1458 "\/RestoreColour {\n" + | |
1459 " \/colorDict fullColourDict def\n" + | |
1460 "} def\n" + | |
1461 "\n" + | |
1462 "% Draw the background for a trimmed section\n" + | |
1463 "% takes the number of columns as a parameter\n" + | |
1464 "\/DrawTrimBg { % <num> DrawTrimBox\n" + | |
1465 " \/col exch def\n" + | |
1466 " \n" + | |
1467 " \/boxwidth \n" + | |
1468 " col stackWidth mul \n" + | |
1469 " def\n" + | |
1470 " \n" + | |
1471 " gsave\n" + | |
1472 " 0.97 setgray\n" + | |
1473 "\n" + | |
1474 " newpath\n" + | |
1475 " 0 0 moveto\n" + | |
1476 " boxwidth 0 rlineto\n" + | |
1477 " 0 yaxisHeight rlineto\n" + | |
1478 " 0 yaxisHeight lineto\n" + | |
1479 " closepath\n" + | |
1480 " \n" + | |
1481 " fill\n" + | |
1482 " grestore\n" + | |
1483 "} def\n" + | |
1484 "\n" + | |
1485 "\/DrawTrimEdge {\n" + | |
1486 " gsave\n" + | |
1487 " 0.2 setgray\n" + | |
1488 " [2] 0 setdash\n" + | |
1489 "\n" + | |
1490 " newpath\n" + | |
1491 " 0 0 moveto\n" + | |
1492 " 0 yaxisHeight lineto\n" + | |
1493 " \n" + | |
1494 " stroke\n" + | |
1495 "\n" + | |
1496 "} def\n" + | |
1497 "\n" + | |
1498 "\n" + | |
1499 "% Deprecated names\n" + | |
1500 "\/startstack {StartStack} bind def\n" + | |
1501 "\/endstack {EndStack} bind def\n" + | |
1502 "\/makenumber {MakeNumber} bind def\n" + | |
1503 "\/numchar { MakeSymbol } bind def\n" + | |
1504 "\n" + | |
1505 "%%EndProlog\n" + | |
1506 "\n" + | |
1507 "%%Page: 1 1\n" + | |
1508 "StartLogo\n" + | |
1509 "\n" + | |
1510 _input("DATA") + "\n" + | |
1511 "\n" + | |
1512 "EndLogo\n" + | |
1513 "\n" + | |
1514 "%%EOF\n" | |
1515 ); | |
1516 }</script> | |
1517 <script> | |
1518 //====================================================================== | |
1519 // start Alphabet object | |
1520 //====================================================================== | |
1521 var Alphabet = function(alphabet, background) { | |
1522 "use strict"; | |
1523 var i, j, sym, aliases, complement, comp_e_sym, ambigs, generate_background; | |
1524 generate_background = (background == null); | |
1525 if (generate_background) { | |
1526 background = []; | |
1527 for (i = 0; i < alphabet.ncore; i++) background[i] = 1.0 / alphabet.ncore; | |
1528 } else if (alphabet.ncore != background.length) { | |
1529 throw new Error("The background length does not match the alphabet length."); | |
1530 } | |
1531 this.name = alphabet.name; | |
1532 this.like = (alphabet.like != null ? alphabet.like.toUpperCase() : null); | |
1533 this.ncore = alphabet.ncore; | |
1534 this.symbols = alphabet.symbols; | |
1535 this.background = background; | |
1536 this.genbg = generate_background; | |
1537 this.encode = {}; | |
1538 this.encode2core = {}; | |
1539 this.complement = {}; | |
1540 // check if all symbols are same case | |
1541 var seen_uc = false; | |
1542 var seen_lc = false; | |
1543 var check_case = function (syms) { | |
1544 var s, sym; | |
1545 if (typeof syms === "string") { | |
1546 for (s = 0; s < syms.length; s++) { | |
1547 sym = syms.charAt(s); | |
1548 if (sym >= 'a' && sym <= 'z') seen_lc = true; | |
1549 else if (sym >= 'A' && sym <= 'Z') seen_uc = true; | |
1550 } | |
1551 } | |
1552 }; | |
1553 for (i = 0; i < this.symbols.length; i++) { | |
1554 check_case(this.symbols[i].symbol); | |
1555 check_case(this.symbols[i].aliases); | |
1556 } | |
1557 // now map symbols to indexes | |
1558 var update_array = function(array, syms, index) { | |
1559 var s, sym; | |
1560 if (typeof syms === "string") { | |
1561 for (s = 0; s < syms.length; s++) { | |
1562 sym = syms.charAt(s); | |
1563 array[sym] = index; | |
1564 // when only a single case is used, then encode as case insensitive | |
1565 if (seen_uc != seen_lc) { | |
1566 if (sym >= 'a' && sym <= 'z') { | |
1567 array[sym.toUpperCase()] = index; | |
1568 } else if (sym >= 'A' && sym <= 'Z') { | |
1569 array[sym.toLowerCase()] = index; | |
1570 } | |
1571 } | |
1572 } | |
1573 } | |
1574 } | |
1575 // map core symbols to index | |
1576 for (i = 0; i < this.ncore; i++) { | |
1577 update_array(this.encode2core, this.symbols[i].symbol, i); | |
1578 update_array(this.encode, this.symbols[i].symbol, i); | |
1579 update_array(this.encode2core, this.symbols[i].aliases, i); | |
1580 update_array(this.encode, this.symbols[i].aliases, i); | |
1581 } | |
1582 // map ambigous symbols to index | |
1583 ambigs = {}; | |
1584 for (i = this.ncore; i < this.symbols.length; i++) { | |
1585 update_array(this.encode, this.symbols[i].symbol, i); | |
1586 update_array(this.encode, this.symbols[i].aliases, i); | |
1587 ambigs[this.symbols[i].equals] = i; | |
1588 } | |
1589 // determine complements | |
1590 for (i = 0; i < this.ncore; i++) { | |
1591 complement = this.symbols[i].complement; | |
1592 if (typeof complement === "string") { | |
1593 this.complement[i] = this.encode2core[complement]; | |
1594 } | |
1595 } | |
1596 next_symbol: | |
1597 for (i = this.ncore; i < this.symbols.length; i++) { | |
1598 complement = ""; | |
1599 for (j = 0; j < this.symbols[i].equals.length; j++) { | |
1600 comp_e_sym = this.complement[this.encode2core[this.symbols[i].equals.charAt(j)]]; | |
1601 if (typeof comp_e_sym !== "number") continue next_symbol; | |
1602 complement += this.symbols[comp_e_sym].symbol; | |
1603 } | |
1604 complement = complement.split("").sort().join(""); | |
1605 if (typeof ambigs[complement] === "number") { | |
1606 this.complement[i] = ambigs[complement]; | |
1607 } | |
1608 } | |
1609 // determine case insensitivity | |
1610 this.case_insensitive = true; | |
1611 if (seen_uc == seen_lc) { | |
1612 // when there is a mixture of cases it probably won't | |
1613 // be case insensitive but we still need to check | |
1614 loop: | |
1615 for (i = 0; i < this.symbols.length; i++) { | |
1616 sym = this.symbols[i].symbol; | |
1617 if (sym >= 'A' && sym <= 'Z') { | |
1618 if (this.encode[sym.toLowerCase()] != i) { | |
1619 this.case_insensitive = false; | |
1620 break loop; | |
1621 } | |
1622 } else if (sym >= 'a' && sym <= 'z') { | |
1623 if (this.encode[sym.toUpperCase()] != i) { | |
1624 this.case_insensitive = false; | |
1625 break loop; | |
1626 } | |
1627 } | |
1628 aliases = this.symbols[i].aliases; | |
1629 if (aliases != null) { | |
1630 for (j = 0; j < aliases.length; j++) { | |
1631 sym = aliases.charAt(j); | |
1632 if (sym >= 'A' && sym <= 'Z') { | |
1633 if (this.encode[sym.toLowerCase()] != i) { | |
1634 this.case_insensitive = false; | |
1635 break loop; | |
1636 } | |
1637 } else if (sym >= 'a' && sym <= 'z') { | |
1638 if (this.encode[sym.toUpperCase()] != i) { | |
1639 this.case_insensitive = false; | |
1640 break loop; | |
1641 } | |
1642 } | |
1643 } | |
1644 } | |
1645 } | |
1646 } | |
1647 // normalise aliases to remove the prime symbol and eliminate | |
1648 // the alternate cases when the alphabet is case insensitive | |
1649 var seen, out; | |
1650 for (i = 0; i < this.symbols.length; i++) { | |
1651 sym = this.symbols[i].symbol; | |
1652 aliases = this.symbols[i].aliases; | |
1653 if (typeof aliases != "string") aliases = ""; | |
1654 seen = {}; | |
1655 out = []; | |
1656 if (this.case_insensitive) { | |
1657 sym = sym.toUpperCase(); | |
1658 aliases = aliases.toUpperCase(); | |
1659 } | |
1660 seen[sym] = true; | |
1661 for (j = 0; j < aliases.length; j++) { | |
1662 if (!seen[aliases.charAt(j)]) { | |
1663 seen[aliases.charAt(j)] = true; | |
1664 out.push(aliases.charAt(j)); | |
1665 } | |
1666 } | |
1667 this.symbols[i].aliases = out.sort().join(""); | |
1668 } | |
1669 }; | |
1670 // return the name of the alphabet | |
1671 Alphabet.prototype.get_alphabet_name = function() { | |
1672 return this.name; | |
1673 }; | |
1674 // return if the alphabet can be complemented | |
1675 Alphabet.prototype.has_complement = function() { | |
1676 return (typeof this.symbols[0].complement === "string"); | |
1677 }; | |
1678 // return true if an uppercase letter has the same meaning as the lowercase form | |
1679 Alphabet.prototype.is_case_insensitive = function() { | |
1680 return this.case_insensitive; | |
1681 }; | |
1682 // return the information content of an alphabet letter | |
1683 Alphabet.prototype.get_ic = function() { | |
1684 return Math.log(this.ncore) / Math.LN2; | |
1685 }; | |
1686 // return the count of the core alphabet symbols | |
1687 Alphabet.prototype.get_size_core = function() { | |
1688 return this.ncore; | |
1689 }; | |
1690 // return the count of all alphabet symbols | |
1691 Alphabet.prototype.get_size_full = function() { | |
1692 return this.symbols.length; | |
1693 }; | |
1694 // return the symbol for the given alphabet index | |
1695 Alphabet.prototype.get_symbol = function(alph_index) { | |
1696 "use strict"; | |
1697 if (alph_index < 0 || alph_index >= this.symbols.length) { | |
1698 throw new Error("Alphabet index out of bounds"); | |
1699 } | |
1700 return this.symbols[alph_index].symbol; | |
1701 }; | |
1702 // return the aliases for the given alphabet index | |
1703 Alphabet.prototype.get_aliases = function(alph_index) { | |
1704 "use strict"; | |
1705 if (alph_index < 0 || alph_index >= this.symbols.length) { | |
1706 throw new Error("Alphabet index out of bounds"); | |
1707 } | |
1708 var sym_obj = this.symbols[alph_index]; | |
1709 return (sym_obj.aliases != null ? sym_obj.aliases : ""); | |
1710 }; | |
1711 // return the name for the given alphabet index | |
1712 Alphabet.prototype.get_name = function(alph_index) { | |
1713 "use strict"; | |
1714 var sym; | |
1715 if (alph_index < 0 || alph_index >= this.symbols.length) { | |
1716 throw new Error("Alphabet index out of bounds"); | |
1717 } | |
1718 sym = this.symbols[alph_index]; | |
1719 return (typeof sym.name === "string" ? sym.name : sym.symbol); | |
1720 }; | |
1721 // return the alphabet it is like or null | |
1722 Alphabet.prototype.get_like = function() { | |
1723 "use strict"; | |
1724 return this.like; | |
1725 }; | |
1726 // return the index of the complement for the given alphabet index | |
1727 Alphabet.prototype.get_complement = function(alph_index) { | |
1728 var comp_e_sym = this.complement[alph_index]; | |
1729 if (typeof comp_e_sym === "number") { | |
1730 return comp_e_sym; | |
1731 } else { | |
1732 return -1; | |
1733 } | |
1734 }; | |
1735 // return a string containing the core symbols | |
1736 Alphabet.prototype.get_symbols = function() { | |
1737 "use strict"; | |
1738 var i, core_symbols; | |
1739 core_symbols = ""; | |
1740 for (i = 0; i < this.ncore; i++) { | |
1741 core_symbols += this.symbols[i].symbol; | |
1742 } | |
1743 return core_symbols; | |
1744 }; | |
1745 // return if the background was not a uniform generated background | |
1746 Alphabet.prototype.has_bg = function() { | |
1747 "use strict"; | |
1748 return !this.genbg; | |
1749 }; | |
1750 // get the background frequency for the index | |
1751 Alphabet.prototype.get_bg_freq = function(alph_index) { | |
1752 "use strict"; | |
1753 var freq, i, symbols; | |
1754 if (alph_index >= 0) { | |
1755 if (alph_index < this.ncore) { | |
1756 return this.background[alph_index]; | |
1757 } else if (alph_index < this.symbols.length) { | |
1758 freq = 0; | |
1759 symbols = this.symbols[alph_index].equals; | |
1760 for (i = 0; i < symbols.length; i++) { | |
1761 freq += this.background[this.encode2core[symbols.charAt(i)]]; | |
1762 } | |
1763 return freq; | |
1764 } | |
1765 } | |
1766 throw new Error("The alphabet index is out of range."); | |
1767 }; | |
1768 // get the colour of the index | |
1769 Alphabet.prototype.get_colour = function(alph_index) { | |
1770 "use strict"; | |
1771 if (alph_index < 0 || alph_index >= this.symbols.length) { | |
1772 throw new Error("BAD_ALPHABET_INDEX"); | |
1773 } | |
1774 if (typeof this.symbols[alph_index].colour != "string") { | |
1775 return "black"; | |
1776 } | |
1777 return "#" + this.symbols[alph_index].colour; | |
1778 }; | |
1779 // get the rgb componets of the colour at the index | |
1780 Alphabet.prototype.get_rgb = function(alph_index) { | |
1781 "use strict"; | |
1782 if (alph_index < 0 || alph_index >= this.symbols.length) { | |
1783 throw new Error("BAD_ALPHABET_INDEX"); | |
1784 } | |
1785 if (typeof this.symbols[alph_index].colour != "string") { | |
1786 return {"red": 0, "green": 0, "blue": 0}; | |
1787 } | |
1788 var colour = this.symbols[alph_index].colour; | |
1789 var red = parseInt(colour.substr(0, 2), 16) / 255; | |
1790 var green = parseInt(colour.substr(2, 2), 16) / 255; | |
1791 var blue = parseInt(colour.substr(4, 2), 16) / 255; | |
1792 return {"red": red, "green": green, "blue": blue}; | |
1793 }; | |
1794 // convert a symbol into the index | |
1795 Alphabet.prototype.get_index = function(letter) { | |
1796 "use strict"; | |
1797 var alph_index; | |
1798 alph_index = this.encode[letter]; | |
1799 if (typeof alph_index === "undefined") { | |
1800 return -1; | |
1801 } | |
1802 return alph_index; | |
1803 }; | |
1804 // convert a symbol into the list of core indexes that it equals | |
1805 Alphabet.prototype.get_indexes = function(letter) { | |
1806 "use strict"; | |
1807 var alph_index, comprise_str, i, comprise_list; | |
1808 alph_index = this.encode[letter]; | |
1809 if (typeof alph_index === "undefined") { | |
1810 throw new Error("Unknown letter"); | |
1811 } | |
1812 comprise_str = this.symbols[alph_index].equals; | |
1813 comprise_list = []; | |
1814 if (typeof comprise_str == "string") { | |
1815 for (i = 0; i < comprise_str.length; i++) { | |
1816 comprise_list.push(this.encode2core[comprise_str.charAt(i)]); | |
1817 } | |
1818 } else { | |
1819 comprise_list.push(alph_index); | |
1820 } | |
1821 return comprise_list; | |
1822 }; | |
1823 // check if a symbol is the primary way of representing the symbol in the alphabet | |
1824 Alphabet.prototype.is_prime_symbol = function(letter) { | |
1825 var alph_index; | |
1826 alph_index = this.encode[letter]; | |
1827 if (alph_index == null) return false; | |
1828 if (this.is_case_insensitive()) { | |
1829 return (this.symbols[alph_index].symbol.toUpperCase() == letter.toUpperCase()); | |
1830 } else { | |
1831 return (this.symbols[alph_index].symbol == letter); | |
1832 } | |
1833 }; | |
1834 // compare 2 alphabets | |
1835 Alphabet.prototype.equals = function(other) { | |
1836 "use strict"; | |
1837 var i, sym1, sym2; | |
1838 // first check that it's actually an alphabet object | |
1839 if (!(typeof other === "object" && other != null && other instanceof Alphabet)) { | |
1840 return false; | |
1841 } | |
1842 // second shortcircuit if it's the same object | |
1843 if (this === other) return true; | |
1844 // compare | |
1845 if (this.name !== other.name) return false; | |
1846 if (this.ncore !== other.ncore) return false; | |
1847 if (this.symbols.length !== other.symbols.length) return false; | |
1848 for (i = 0; i < this.symbols.length; i++) { | |
1849 sym1 = this.symbols[i]; | |
1850 sym2 = other.symbols[i]; | |
1851 if (sym1.symbol !== sym2.symbol) return false; | |
1852 if (sym1.aliases !== sym2.aliases) return false; | |
1853 if (sym1.name !== sym2.name) return false; | |
1854 if (typeof sym1.colour !== typeof sym2.colour || | |
1855 (typeof sym1.colour === "string" && typeof sym2.colour === "string" && | |
1856 parseInt(sym1.colour, 16) != parseInt(sym2.colour, 16))) { | |
1857 return false; | |
1858 } | |
1859 if (sym1.complement !== sym2.complement) return false; | |
1860 if (sym1.equals !== sym2.equals) return false; | |
1861 } | |
1862 return true; | |
1863 }; | |
1864 Alphabet.prototype.check_core_subset = function(super_alph) { | |
1865 var complement_same = true; | |
1866 var seen_set = {}; | |
1867 var sub_i, sub_symbol, super_i, super_symbol; | |
1868 for (sub_i = 0; sub_i < this.ncore; sub_i++) { | |
1869 sub_symbol = this.symbols[sub_i]; | |
1870 super_i = super_alph.encode[sub_symbol.symbol]; | |
1871 if (super_i == null) return 0; | |
1872 super_symbol = super_alph.symbols[super_i]; | |
1873 if (seen_set[super_i]) return 0; | |
1874 seen_set[super_i] = true; | |
1875 // check complement | |
1876 if (sub_symbol.complement != null && super_symbol.complement != null) { | |
1877 if (super_alph.encode[sub_symbol.complement] != super_alph.encode[super_symbol.complement]) { | |
1878 complement_same = false; | |
1879 } | |
1880 } else if (sub_symbol.complement != null || super_symbol.complement != null) { | |
1881 complement_same = false; | |
1882 } | |
1883 } | |
1884 return (complement_same ? 1 : -1); | |
1885 }; | |
1886 // convert a sequence to its reverse complement | |
1887 Alphabet.prototype.invcomp_seq = function(seq) { | |
1888 "use strict"; | |
1889 var syms, i, e_sym, comp_e_sym; | |
1890 if (!this.has_complement()) throw new Error("Alphabet must be complementable"); | |
1891 syms = seq.split(""); | |
1892 for (i = 0; i < syms.length; i++) { | |
1893 e_sym = this.encode[syms[i]]; | |
1894 if (typeof e_sym === "undefined") { | |
1895 e_sym = this.ncore; // wildcard | |
1896 } | |
1897 comp_e_sym = this.complement[e_sym]; | |
1898 if (typeof comp_e_sym === "undefined") { | |
1899 comp_e_sym = e_sym; // not complementable | |
1900 } | |
1901 syms[i] = this.symbols[comp_e_sym].symbol; | |
1902 } | |
1903 return syms.reverse().join(""); | |
1904 }; | |
1905 // convert the alphabet to the text version | |
1906 Alphabet.prototype.as_text = function() { | |
1907 "use strict"; | |
1908 function name_as_text(name) { | |
1909 var i, c, out; | |
1910 out = "\""; | |
1911 for (i = 0; i < name.length; i++) { | |
1912 c = name.charAt(i); | |
1913 if (c == "\"") { | |
1914 out += "\\\""; | |
1915 } else if (c == "/") { | |
1916 out += "\\/"; | |
1917 } else if (c == "\\") { | |
1918 out += "\\\\"; | |
1919 } else { | |
1920 out += c; | |
1921 } | |
1922 } | |
1923 out += "\""; | |
1924 return out; | |
1925 } | |
1926 function symbol_as_text(sym) { | |
1927 var out; | |
1928 out = sym.symbol; | |
1929 if (typeof sym.name === "string" && sym.name != sym.symbol) { | |
1930 out += " " + name_as_text(sym.name); | |
1931 } | |
1932 if (typeof sym.colour === "string") { | |
1933 out += " " + sym.colour; | |
1934 } | |
1935 return out; | |
1936 } | |
1937 var out, i, j, c, sym; | |
1938 out = ""; | |
1939 // output core symbols with 2 way complements | |
1940 for (i = 0; i < this.ncore; i++) { | |
1941 c = this.complement[i]; | |
1942 if (typeof c === "number" && i < c && this.complement[c] === i) { | |
1943 out += symbol_as_text(this.symbols[i]) + " ~ " + symbol_as_text(this.symbols[c]) + "\n"; | |
1944 } | |
1945 } | |
1946 // output core symbols with no complement | |
1947 for (i = 0; i < this.ncore; i++) { | |
1948 if (typeof this.complement[i] === "undefined") { | |
1949 out += symbol_as_text(this.symbols[i]) + "\n"; | |
1950 } | |
1951 } | |
1952 // output ambiguous symbols that have comprising characters | |
1953 for (i = this.ncore; i < this.symbols.length; i++) { | |
1954 if (this.symbols[i].equals.length == 0) break; | |
1955 out += symbol_as_text(this.symbols[i]) + " = " + this.symbols[i].equals + "\n"; | |
1956 if (typeof this.symbols[i].aliases === "string") { | |
1957 for (j = 0; j < this.symbols[i].aliases.length; j++) { | |
1958 if (this.symbols[i].aliases.charAt(j) == this.symbols[i].symbol) continue; | |
1959 out += this.symbols[i].aliases.charAt(j) + " = " + this.symbols[i].equals + "\n"; | |
1960 } | |
1961 } | |
1962 } | |
1963 // output aliases of core symbols | |
1964 for (i = 0; i < this.ncore; i++) { | |
1965 if (typeof this.symbols[i].aliases === "string") { | |
1966 for (j = 0; j < this.symbols[i].aliases.length; j++) { | |
1967 if (this.symbols[i].aliases.charAt(j) == this.symbols[i].symbol) continue; | |
1968 out += this.symbols[i].aliases.charAt(j) + " = " + this.symbols[i].symbol + "\n"; | |
1969 } | |
1970 } | |
1971 } | |
1972 // output gap symbols | |
1973 i = this.symbols.length - 1; | |
1974 if (this.symbols[i].equals.length == 0) { | |
1975 out += symbol_as_text(this.symbols[i]) + " =\n"; | |
1976 if (typeof this.symbols[i].aliases === "string") { | |
1977 for (j = 0; j < this.symbols[i].aliases.length; j++) { | |
1978 if (this.symbols[i].aliases.charAt(j) == this.symbols[i].symbol) continue; | |
1979 out += this.symbols[i].aliases.charAt(j) + " =\n"; | |
1980 } | |
1981 } | |
1982 } | |
1983 return out; | |
1984 }; | |
1985 // output the alphabet as it appears in minimal MEME format | |
1986 Alphabet.prototype.as_meme = function() { | |
1987 "use strict"; | |
1988 function name_as_text(name) { | |
1989 var i, c, out; | |
1990 out = "\""; | |
1991 for (i = 0; i < name.length; i++) { | |
1992 c = name.charAt(i); | |
1993 if (c == "\"") { | |
1994 out += "\\\""; | |
1995 } else if (c == "/") { | |
1996 out += "\\/"; | |
1997 } else if (c == "\\") { | |
1998 out += "\\\\"; | |
1999 } else { | |
2000 out += c; | |
2001 } | |
2002 } | |
2003 out += "\""; | |
2004 return out; | |
2005 } | |
2006 if (this.equals(AlphStd.DNA)) { | |
2007 return "ALPHABET= ACGT\n"; | |
2008 } else if (this.equals(AlphStd.PROTEIN)) { | |
2009 return "ALPHABET= ACDEFGHIKLMNPQRSTVWY\n"; | |
2010 } else { | |
2011 return "ALPHABET" + | |
2012 (this.name != null ? " " + name_as_text(this.name) : "") + | |
2013 (this.like != null ? " " + this.like + "-LIKE" : "") + "\n" + | |
2014 this.as_text() + "END ALPHABET\n"; | |
2015 } | |
2016 }; | |
2017 | |
2018 // Returns a table showing all the letters in the alphabet | |
2019 Alphabet.prototype.as_table = function() { | |
2020 "use strict"; | |
2021 var i, j, row, th, td, aliases, equals, sym; | |
2022 var table = document.createElement("table"); | |
2023 // create the core symbol header | |
2024 row = table.insertRow(table.rows.length); | |
2025 th = document.createElement("th"); | |
2026 th.appendChild(document.createTextNode("Symbol(s)")); | |
2027 row.appendChild(th); | |
2028 th = document.createElement("th"); | |
2029 th.appendChild(document.createTextNode("Name")); | |
2030 row.appendChild(th); | |
2031 th = document.createElement("th"); | |
2032 if (this.has_complement()) { | |
2033 th.appendChild(document.createTextNode("Complement")); | |
2034 } | |
2035 row.appendChild(th); | |
2036 // list the core symbols | |
2037 for (i = 0; i < this.ncore; i++) { | |
2038 row = table.insertRow(table.rows.length); | |
2039 td = document.createElement("td"); | |
2040 if (this.symbols[i].colour != null) { | |
2041 td.style.color = '#' + this.symbols[i].colour; | |
2042 } | |
2043 td.appendChild(document.createTextNode(this.symbols[i].symbol)); | |
2044 aliases = this.get_aliases(i); | |
2045 if (aliases.length > 0) { | |
2046 td.appendChild(document.createTextNode(' ' + aliases.split('').join(' '))); | |
2047 } | |
2048 row.appendChild(td); | |
2049 td = document.createElement("td"); | |
2050 if (this.symbols[i].name != null) { | |
2051 td.appendChild(document.createTextNode(this.symbols[i].name)); | |
2052 } | |
2053 row.appendChild(td); | |
2054 td = document.createElement("td"); | |
2055 if (this.symbols[i].complement != null) { | |
2056 td.style.color = this.get_colour(this.get_index(this.symbols[i].complement)); | |
2057 td.appendChild(document.createTextNode(this.symbols[i].complement)); | |
2058 } | |
2059 row.appendChild(td); | |
2060 } | |
2061 // create the ambiguous symbol header | |
2062 row = table.insertRow(table.rows.length); | |
2063 th = document.createElement("th"); | |
2064 th.appendChild(document.createTextNode("Symbol(s)")); | |
2065 row.appendChild(th); | |
2066 th = document.createElement("th"); | |
2067 th.appendChild(document.createTextNode("Name")); | |
2068 row.appendChild(th); | |
2069 th = document.createElement("th"); | |
2070 th.appendChild(document.createTextNode("Matches")); | |
2071 row.appendChild(th); | |
2072 // list the ambiguous symbols | |
2073 for (i = this.ncore; i < this.symbols.length; i++) { | |
2074 row = table.insertRow(table.rows.length); | |
2075 td = document.createElement("td"); | |
2076 if (this.symbols[i].colour != null) { | |
2077 td.style.color = '#' + this.symbols[i].colour; | |
2078 } | |
2079 td.appendChild(document.createTextNode(this.symbols[i].symbol)); | |
2080 aliases = this.get_aliases(i); | |
2081 if (aliases.length > 0) { | |
2082 td.appendChild(document.createTextNode(' ' + aliases.split('').join(' '))); | |
2083 } | |
2084 row.appendChild(td); | |
2085 td = document.createElement("td"); | |
2086 if (this.symbols[i].name != null) { | |
2087 td.appendChild(document.createTextNode(this.symbols[i].name)); | |
2088 } | |
2089 row.appendChild(td); | |
2090 td = document.createElement("td"); | |
2091 equals = this.symbols[i].equals.split(''); | |
2092 for (j = 0; j < equals.length; j++) { | |
2093 if (j != 0) td.appendChild(document.createTextNode(' ')); | |
2094 sym = document.createElement("span"); | |
2095 sym.style.color = this.get_colour(this.get_index(equals[j])); | |
2096 sym.appendChild(document.createTextNode(equals[j])); | |
2097 td.appendChild(sym); | |
2098 } | |
2099 row.appendChild(td); | |
2100 } | |
2101 return table; | |
2102 }; | |
2103 | |
2104 // returns a dictionary of the colours for EPS | |
2105 Alphabet.prototype._as_eps_dict = function() { | |
2106 "use strict"; | |
2107 var i, sym, rgb; | |
2108 var out = "/fullColourDict <<\n"; | |
2109 for (i = 0; i < this.ncore; i++) { | |
2110 sym = this.get_symbol(i); | |
2111 sym = sym.replace(/\\/g, "\\\\"); | |
2112 sym = sym.replace(/\(/g, "\\("); | |
2113 sym = sym.replace(/\)/g, "\\)"); | |
2114 rgb = this.get_rgb(i); | |
2115 out += " (" + sym + ") [" + rgb.red.toFixed(4) + " " + rgb.green.toFixed(4) + " " + rgb.blue.toFixed(4) + "]\n"; | |
2116 } | |
2117 out += ">> def\n"; | |
2118 out += "/mutedColourDict <<\n"; | |
2119 for (i = 0; i < this.ncore; i++) { | |
2120 sym = this.get_symbol(i); | |
2121 sym = sym.replace(/\\/g, "\\\\"); | |
2122 sym = sym.replace(/\(/g, "\\("); | |
2123 sym = sym.replace(/\)/g, "\\)"); | |
2124 rgb = Alphabet.lighten_colour(this.get_rgb(i)); | |
2125 out += " (" + sym + ") [" + rgb.red.toFixed(4) + " " + rgb.green.toFixed(4) + " " + rgb.blue.toFixed(4) + "]\n"; | |
2126 } | |
2127 out += ">> def\n"; | |
2128 return out; | |
2129 }; | |
2130 | |
2131 // return the alphabet name or a list of primary symbols | |
2132 Alphabet.prototype.toString = function() { | |
2133 "use strict"; | |
2134 if (this.name != null) { | |
2135 return this.name; | |
2136 } else { | |
2137 return this.get_symbols(); | |
2138 } | |
2139 }; | |
2140 | |
2141 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
2142 // Helper functions | |
2143 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
2144 | |
2145 // Convert a colour specified in RGB colourspace values into LAB colourspace | |
2146 Alphabet.rgb2lab = function(rgb) { | |
2147 "use strict"; | |
2148 var xyzHelper, labHelper; | |
2149 // XYZ helper | |
2150 xyzHelper = function(value) { | |
2151 if (value > 0.0445) { | |
2152 value = (value + 0.055) / 1.055; | |
2153 value = Math.pow(value, 2.4); | |
2154 } else { | |
2155 value /= 12.92; | |
2156 } | |
2157 value *= 100; | |
2158 return value; | |
2159 }; | |
2160 // lab helper | |
2161 labHelper = function(value) { | |
2162 if (value > 0.008856) { | |
2163 value = Math.pow(value, 1.0 / 3.0); | |
2164 } else { | |
2165 value = (7.787 * value) + (16.0 / 116.0); | |
2166 } | |
2167 return value; | |
2168 }; | |
2169 // convert into XYZ colourspace | |
2170 var c1, c2, c3; | |
2171 if (typeof rgb == "number") { | |
2172 c1 = xyzHelper(((rgb >> 16) & 0xFF) / 255.0); | |
2173 c2 = xyzHelper(((rgb >> 8) & 0xFF) / 255.0); | |
2174 c3 = xyzHelper((rgb & 0xFF) / 255.0); | |
2175 } else { | |
2176 c1 = xyzHelper(rgb.red); | |
2177 c2 = xyzHelper(rgb.green); | |
2178 c3 = xyzHelper(rgb.blue); | |
2179 } | |
2180 var x = (c1 * 0.4124) + (c2 * 0.3576) + (c3 * 0.1805); | |
2181 var y = (c1 * 0.2126) + (c2 * 0.7152) + (c3 * 0.0722); | |
2182 var z = (c1 * 0.0193) + (c2 * 0.1192) + (c3 * 0.9505); | |
2183 // convert into Lab colourspace | |
2184 c1 = labHelper(x / 95.047); | |
2185 c2 = labHelper(y / 100.0); | |
2186 c3 = labHelper(z / 108.883); | |
2187 var l = (116.0 * c2) - 16; | |
2188 var a = 500.0 * (c1 - c2); | |
2189 var b = 200.0 * (c2 - c3); | |
2190 return {"l": l, "a": a, "b": b}; | |
2191 }; | |
2192 | |
2193 // Convert a colour specified in HSV colourspace into RGB colourspace | |
2194 Alphabet.hsv2rgb = function(hue, sat, value, output_object) { | |
2195 // achromatic (grey) | |
2196 var r = value; | |
2197 var g = value; | |
2198 var b = value; | |
2199 if (sat != 0) { | |
2200 var h = hue / 60.0; | |
2201 var i = Math.floor(h); | |
2202 var f = h - i; | |
2203 var p = value * (1.0 - sat); | |
2204 var q = value * (1.0 - (sat * f)); | |
2205 var t = value * (1.0 - (sat * (1.0 - f))); | |
2206 if (i == 0) { | |
2207 r = value; | |
2208 g = t; | |
2209 b = p; | |
2210 } else if (i == 1) { | |
2211 r = q; | |
2212 g = value; | |
2213 b = p; | |
2214 } else if (i == 2) { | |
2215 r = p; | |
2216 g = value; | |
2217 b = t; | |
2218 } else if (i == 3) { | |
2219 r = p; | |
2220 g = q; | |
2221 b = value; | |
2222 } else if (i == 4) { | |
2223 r = t; | |
2224 g = p; | |
2225 b = value; | |
2226 } else { | |
2227 r = value; | |
2228 g = p; | |
2229 b = q; | |
2230 } | |
2231 } | |
2232 if (output_object) { | |
2233 return {"red": r, "green": g, "blue": b}; | |
2234 } else { | |
2235 return (Math.floor(r * 255) << 15) | (Math.floor(g * 255) << 8) | (Math.floor(b * 255)); | |
2236 } | |
2237 }; | |
2238 | |
2239 // Calculate a distance score between two colours in LAB colourspace | |
2240 Alphabet.lab_dist = function(lab1, lab2) { | |
2241 var c1 = Math.sqrt((lab1.l * lab1.l) + (lab1.a * lab1.a)); | |
2242 var c2 = Math.sqrt((lab2.l * lab2.l) + (lab2.a * lab2.a)); | |
2243 var dc = c1 - c2; | |
2244 var dl = lab1.l - lab2.l; | |
2245 var da = lab1.a - lab2.a; | |
2246 var db = lab1.b - lab2.b; | |
2247 // we don't want NaN due to rounding errors so fudge things a bit... | |
2248 var dh = 0; | |
2249 var dh_squared = (da * da) + (db * db) - (dc * dc); | |
2250 if (dh_squared > 0) { | |
2251 dh = Math.sqrt(dh_squared); | |
2252 } | |
2253 var first = dl; | |
2254 var second = dc / (1.0 + (0.045 * c1)); | |
2255 var third = dh / (1.0 + (0.015 * c1)); | |
2256 return Math.sqrt((first * first) + (second * second) + (third * third)); | |
2257 }; | |
2258 | |
2259 // convert an RGB value into a HSL value | |
2260 Alphabet.rgb2hsl = function(rgb) { | |
2261 "use strict"; | |
2262 var min, max, delta, h, s, l, r, g, b; | |
2263 if (typeof rgb == "number") { | |
2264 r = ((rgb >> 16) & 0xFF) / 255.0; | |
2265 g = ((rgb >> 8) & 0xFF) / 255.0; | |
2266 b = (rgb & 0xFF) / 255.0; | |
2267 } else { | |
2268 r = rgb.red; | |
2269 g = rgb.green; | |
2270 b = rgb.blue; | |
2271 } | |
2272 min = Math.min(r, g, b); | |
2273 max = Math.max(r, g, b); | |
2274 delta = max - min; | |
2275 l = min + (delta / 2); | |
2276 if (max == min) { | |
2277 h = 0; // achromatic (grayscale) | |
2278 s = 0; | |
2279 } else { | |
2280 if (l > 0.5) { | |
2281 s = delta / (2 - max - min); | |
2282 } else { | |
2283 s = delta / (max + min); | |
2284 } | |
2285 if (max == r) { | |
2286 h = (g - b) / delta; | |
2287 if (g < b) h += 6; | |
2288 } else if (max == g) { | |
2289 h = ((b - r) / delta) + 2; | |
2290 } else { | |
2291 h = ((r - g) / delta) + 4; | |
2292 } | |
2293 h /= 6; | |
2294 } | |
2295 return {"h": h, "s": s, "l": l}; | |
2296 }; | |
2297 | |
2298 // convert a HSL value into an RGB value | |
2299 Alphabet.hsl2rgb = function(hsl, output_object) { | |
2300 "use strict"; | |
2301 function _hue(p, q, t) { | |
2302 "use strict"; | |
2303 if (t < 0) t += 1; | |
2304 else if (t > 1) t -= 1; | |
2305 if (t < (1.0 / 6.0)) { | |
2306 return p + ((q - p) * 6.0 * t); | |
2307 } else if (t < 0.5) { | |
2308 return q; | |
2309 } else if (t < (2.0 / 3.0)) { | |
2310 return p + ((q - p) * ((2.0 / 3.0) - t) * 6.0); | |
2311 } else { | |
2312 return p; | |
2313 } | |
2314 } | |
2315 var r, g, b, p, q; | |
2316 if (hsl.s == 0) { | |
2317 // achromatic (grayscale) | |
2318 r = hsl.l; | |
2319 g = hsl.l; | |
2320 b = hsl.l; | |
2321 } else { | |
2322 if (hsl.l < 0.5) { | |
2323 q = hsl.l * (1 + hsl.s); | |
2324 } else { | |
2325 q = hsl.l + hsl.s - (hsl.l * hsl.s); | |
2326 } | |
2327 p = (2 * hsl.l) - q; | |
2328 r = _hue(p, q, hsl.h + (1.0 / 3.0)); | |
2329 g = _hue(p, q, hsl.h); | |
2330 b = _hue(p, q, hsl.h - (1.0 / 3.0)); | |
2331 } | |
2332 if (output_object) { | |
2333 return {"red": r, "green": g, "blue": b}; | |
2334 } else { | |
2335 return (Math.floor(r * 255) << 15) | (Math.floor(g * 255) << 8) | (Math.floor(b * 255)); | |
2336 } | |
2337 }; | |
2338 | |
2339 Alphabet.lighten_colour = function(rgb) { | |
2340 "use strict"; | |
2341 var hsl = Alphabet.rgb2hsl(rgb); | |
2342 hsl.l += (1.0 - hsl.l) * 2 / 3; | |
2343 return Alphabet.hsl2rgb(hsl, typeof rgb != "number"); | |
2344 }; | |
2345 | |
2346 //====================================================================== | |
2347 // end Alphabet object | |
2348 //====================================================================== | |
2349 | |
2350 //====================================================================== | |
2351 // start StandardAlphabet object | |
2352 //====================================================================== | |
2353 | |
2354 // an extension of the alphabet object to support some additional fields | |
2355 // only present in standard alphabets. | |
2356 var StandardAlphabet = function(enum_code, enum_name, alphabet_data) { | |
2357 Alphabet.apply(this, [alphabet_data]); | |
2358 this.enum_code = enum_code; | |
2359 this.enum_name = enum_name; | |
2360 }; | |
2361 StandardAlphabet.prototype = Alphabet.prototype; | |
2362 StandardAlphabet.prototype.constructor = StandardAlphabet; | |
2363 | |
2364 // A unique code for this standard alphabet. | |
2365 // This code will be a power of 2 to enable creation of bitsets for | |
2366 // a selection of standard alphabets. | |
2367 StandardAlphabet.prototype.get_code = function() { | |
2368 return this.enum_code; | |
2369 }; | |
2370 | |
2371 // A unique name for this standard alphabet. | |
2372 // this name will be all upper case and the same as the property that | |
2373 // refers to this alphabet in the AlphStd collection. | |
2374 StandardAlphabet.prototype.get_enum = function() { | |
2375 return this.enum_name; | |
2376 }; | |
2377 | |
2378 //====================================================================== | |
2379 // end StandardAlphabet object | |
2380 //====================================================================== | |
2381 | |
2382 // A collection of standard alphabets. | |
2383 var AlphStd = { | |
2384 RNA: new StandardAlphabet(1, "RNA", { | |
2385 "name": "RNA", | |
2386 "like": "RNA", | |
2387 "ncore": 4, | |
2388 "symbols": [ | |
2389 {"symbol": "A", "name": "Adenine", "colour": "CC0000"}, | |
2390 {"symbol": "C", "name": "Cytosine", "colour": "0000CC"}, | |
2391 {"symbol": "G", "name": "Guanine", "colour": "FFB300"}, | |
2392 {"symbol": "U", "name": "Uracil", "colour": "008000", | |
2393 "aliases": "T"}, | |
2394 {"symbol": "N", "name": "Any base", "equals": "ACGU", "aliases": "X."}, | |
2395 {"symbol": "V", "name": "Not U", "equals": "ACG"}, | |
2396 {"symbol": "H", "name": "Not G", "equals": "ACU"}, | |
2397 {"symbol": "D", "name": "Not C", "equals": "AGU"}, | |
2398 {"symbol": "B", "name": "Not A", "equals": "CGU"}, | |
2399 {"symbol": "M", "name": "Amino", "equals": "AC"}, | |
2400 {"symbol": "R", "name": "Purine", "equals": "AG"}, | |
2401 {"symbol": "W", "name": "Weak", "equals": "AU"}, | |
2402 {"symbol": "S", "name": "Strong", "equals": "CG"}, | |
2403 {"symbol": "Y", "name": "Pyrimidine", "equals": "CU"}, | |
2404 {"symbol": "K", "name": "Keto", "equals": "GU"} | |
2405 ] | |
2406 }), | |
2407 DNA: new StandardAlphabet(2, "DNA", { | |
2408 "name": "DNA", | |
2409 "like": "DNA", | |
2410 "ncore": 4, | |
2411 "symbols": [ | |
2412 {"symbol": "A", "name": "Adenine", "colour": "CC0000", "complement": "T"}, | |
2413 {"symbol": "C", "name": "Cytosine", "colour": "0000CC", "complement": "G"}, | |
2414 {"symbol": "G", "name": "Guanine", "colour": "FFB300", "complement": "C"}, | |
2415 {"symbol": "T", "name": "Thymine", "colour": "008000", "complement": "A", | |
2416 "aliases": "U"}, | |
2417 {"symbol": "N", "name": "Any base", "equals": "ACGT", "aliases": "X."}, | |
2418 {"symbol": "V", "name": "Not T", "equals": "ACG"}, | |
2419 {"symbol": "H", "name": "Not G", "equals": "ACT"}, | |
2420 {"symbol": "D", "name": "Not C", "equals": "AGT"}, | |
2421 {"symbol": "B", "name": "Not A", "equals": "CGT"}, | |
2422 {"symbol": "M", "name": "Amino", "equals": "AC"}, | |
2423 {"symbol": "R", "name": "Purine", "equals": "AG"}, | |
2424 {"symbol": "W", "name": "Weak", "equals": "AT"}, | |
2425 {"symbol": "S", "name": "Strong", "equals": "CG"}, | |
2426 {"symbol": "Y", "name": "Pyrimidine", "equals": "CT"}, | |
2427 {"symbol": "K", "name": "Keto", "equals": "GT"} | |
2428 ] | |
2429 }), | |
2430 PROTEIN: new StandardAlphabet(4, "PROTEIN", { | |
2431 "name": "Protein", | |
2432 "like": "PROTEIN", | |
2433 "ncore": 20, | |
2434 "symbols": [ | |
2435 {"symbol": "A", "name": "Alanine", "colour": "0000CC"}, | |
2436 {"symbol": "C", "name": "Cysteine", "colour": "0000CC"}, | |
2437 {"symbol": "D", "name": "Aspartic acid", "colour": "FF00FF"}, | |
2438 {"symbol": "E", "name": "Glutamic acid", "colour": "FF00FF"}, | |
2439 {"symbol": "F", "name": "Phenylalanine", "colour": "0000CC"}, | |
2440 {"symbol": "G", "name": "Glycine", "colour": "FFB300"}, | |
2441 {"symbol": "H", "name": "Histidine", "colour": "FFCCCC"}, | |
2442 {"symbol": "I", "name": "Isoleucine", "colour": "0000CC"}, | |
2443 {"symbol": "K", "name": "Lysine", "colour": "CC0000"}, | |
2444 {"symbol": "L", "name": "Leucine", "colour": "0000CC"}, | |
2445 {"symbol": "M", "name": "Methionine", "colour": "0000CC"}, | |
2446 {"symbol": "N", "name": "Asparagine", "colour": "008000"}, | |
2447 {"symbol": "P", "name": "Proline", "colour": "FFFF00"}, | |
2448 {"symbol": "Q", "name": "Glutamine", "colour": "008000"}, | |
2449 {"symbol": "R", "name": "Arginine", "colour": "CC0000"}, | |
2450 {"symbol": "S", "name": "Serine", "colour": "008000"}, | |
2451 {"symbol": "T", "name": "Threonine", "colour": "008000"}, | |
2452 {"symbol": "V", "name": "Valine", "colour": "0000CC"}, | |
2453 {"symbol": "W", "name": "Tryptophan", "colour": "0000CC"}, | |
2454 {"symbol": "Y", "name": "Tyrosine", "colour": "33E6CC"}, | |
2455 {"symbol": "X", "name": "Any amino acid", "equals": "ACDEFGHIKLMNPQRSTVWY", "aliases": "*."}, | |
2456 {"symbol": "B", "name": "Asparagine or Aspartic acid", "equals": "DN"}, | |
2457 {"symbol": "Z", "name": "Glutamine or Glutamic acid", "equals": "EQ"}, | |
2458 {"symbol": "J", "name": "Leucine or Isoleucine", "equals": "IL"} | |
2459 ] | |
2460 }) | |
2461 }; | |
2462 | |
2463 //====================================================================== | |
2464 // start Symbol object | |
2465 //====================================================================== | |
2466 var Symbol = function(alph_index, scale, alphabet) { | |
2467 "use strict"; | |
2468 //variable prototype | |
2469 this.symbol = alphabet.get_symbol(alph_index); | |
2470 this.scale = scale; | |
2471 this.colour = alphabet.get_colour(alph_index); | |
2472 }; | |
2473 | |
2474 Symbol.prototype.get_symbol = function() { | |
2475 "use strict"; | |
2476 return this.symbol; | |
2477 }; | |
2478 | |
2479 Symbol.prototype.get_scale = function() { | |
2480 "use strict"; | |
2481 return this.scale; | |
2482 }; | |
2483 | |
2484 Symbol.prototype.get_colour = function() { | |
2485 "use strict"; | |
2486 return this.colour; | |
2487 }; | |
2488 | |
2489 Symbol.prototype.toString = function() { | |
2490 "use strict"; | |
2491 return this.symbol + " " + (Math.round(this.scale*1000)/10) + "%"; | |
2492 }; | |
2493 | |
2494 function compare_symbol(sym1, sym2) { | |
2495 "use strict"; | |
2496 if (sym1.get_scale() < sym2.get_scale()) { | |
2497 return -1; | |
2498 } else if (sym1.get_scale() > sym2.get_scale()) { | |
2499 return 1; | |
2500 } else { | |
2501 return 0; | |
2502 } | |
2503 } | |
2504 //====================================================================== | |
2505 // end Symbol object | |
2506 //====================================================================== | |
2507 | |
2508 //====================================================================== | |
2509 // start Pspm object | |
2510 //====================================================================== | |
2511 var Pspm = function(matrix, name, ltrim, rtrim, nsites, evalue, pssm, alt) { | |
2512 "use strict"; | |
2513 var row, col, data, row_sum, delta, evalue_re; | |
2514 if (typeof name !== "string") { | |
2515 name = ""; | |
2516 } | |
2517 this.name = name; | |
2518 //construct | |
2519 if (matrix instanceof Pspm) { | |
2520 // copy constructor | |
2521 this.alph_length = matrix.alph_length; | |
2522 this.motif_length = matrix.motif_length; | |
2523 this.name = matrix.name; | |
2524 this.alt = matrix.alt; | |
2525 this.nsites = matrix.nsites; | |
2526 this.evalue = matrix.evalue; | |
2527 this.ltrim = matrix.ltrim; | |
2528 this.rtrim = matrix.rtrim; | |
2529 this.pspm = []; | |
2530 for (row = 0; row < matrix.motif_length; row++) { | |
2531 this.pspm[row] = []; | |
2532 for (col = 0; col < matrix.alph_length; col++) { | |
2533 this.pspm[row][col] = matrix.pspm[row][col]; | |
2534 } | |
2535 } | |
2536 if (matrix.pssm != null) { | |
2537 this.pssm = []; | |
2538 for (row = 0; row < matrix.motif_length; row++) { | |
2539 this.pspm[row] = []; | |
2540 for (col = 0; col < matrix.alph_length; col++) { | |
2541 this.pssm[row][col] = matrix.pssm[row][col]; | |
2542 } | |
2543 } | |
2544 } | |
2545 } else { | |
2546 // check parameters | |
2547 if (ltrim == null) { | |
2548 ltrim = 0; | |
2549 } else if (typeof ltrim !== "number" || ltrim % 1 !== 0 || ltrim < 0) { | |
2550 throw new Error("ltrim must be a non-negative integer, got: " + ltrim); | |
2551 } | |
2552 if (rtrim == null) { | |
2553 rtrim = 0; | |
2554 } else if (typeof rtrim !== "number" || rtrim % 1 !== 0 || rtrim < 0) { | |
2555 throw new Error("rtrim must be a non-negative integer, got: " + rtrim); | |
2556 } | |
2557 if (nsites != null) { | |
2558 if (typeof nsites !== "number" || nsites < 0) { | |
2559 throw new Error("nsites must be a positive number, got: " + nsites); | |
2560 } else if (nsites == 0) { | |
2561 nsites = null; | |
2562 } | |
2563 } | |
2564 if (evalue != null) { | |
2565 if (typeof evalue === "number") { | |
2566 if (evalue < 0) { | |
2567 throw new Error("evalue must be a non-negative number, got: " + evalue); | |
2568 } | |
2569 } else if (typeof evalue === "string") { | |
2570 evalue_re = /^((?:[+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)|inf)$/; | |
2571 if (!evalue_re.test(evalue)) { | |
2572 throw new Error("evalue must be a non-negative number, got: " + evalue); | |
2573 } | |
2574 } else { | |
2575 throw new Error("evalue must be a non-negative number, got: " + evalue); | |
2576 } | |
2577 } | |
2578 // set properties | |
2579 this.name = name; | |
2580 this.alt = alt; | |
2581 this.nsites = nsites; | |
2582 this.evalue = evalue; | |
2583 this.ltrim = ltrim; | |
2584 this.rtrim = rtrim; | |
2585 if (typeof matrix === "string") { | |
2586 // string constructor | |
2587 data = parse_pspm_string(matrix); | |
2588 this.alph_length = data["alph_length"]; | |
2589 this.motif_length = data["motif_length"]; | |
2590 this.pspm = data["pspm"]; | |
2591 if (this.evalue == null) { | |
2592 if (data["evalue"] != null) { | |
2593 this.evalue = data["evalue"]; | |
2594 } else { | |
2595 this.evalue = 0; | |
2596 } | |
2597 } | |
2598 if (this.nsites == null) { | |
2599 if (typeof data["nsites"] === "number") { | |
2600 this.nsites = data["nsites"]; | |
2601 } else { | |
2602 this.nsites = 20; | |
2603 } | |
2604 } | |
2605 } else { | |
2606 // assume pspm is a nested array | |
2607 this.motif_length = matrix.length; | |
2608 this.alph_length = (matrix.length > 0 ? matrix[0].length : 0); | |
2609 if (this.nsites == null) { | |
2610 this.nsites = 20; | |
2611 } | |
2612 if (this.evalue == null) { | |
2613 this.evalue = 0; | |
2614 } | |
2615 this.pspm = []; | |
2616 // copy pspm and check | |
2617 for (row = 0; row < this.motif_length; row++) { | |
2618 if (this.alph_length != matrix[row].length) { | |
2619 throw new Error("COLUMN_MISMATCH"); | |
2620 } | |
2621 this.pspm[row] = []; | |
2622 row_sum = 0; | |
2623 for (col = 0; col < this.alph_length; col++) { | |
2624 this.pspm[row][col] = matrix[row][col]; | |
2625 row_sum += this.pspm[row][col]; | |
2626 } | |
2627 delta = 0.1; | |
2628 if (isNaN(row_sum) || (row_sum > 1 && (row_sum - 1) > delta) || | |
2629 (row_sum < 1 && (1 - row_sum) > delta)) { | |
2630 throw new Error("INVALID_SUM"); | |
2631 } | |
2632 } | |
2633 // copy pssm | |
2634 if (pssm != null) { | |
2635 this.pssm = []; | |
2636 for (row = 0; row < this.motif_length; row++) { | |
2637 this.pssm[row] = []; | |
2638 for (col = 0; col < this.alph_length; col++) { | |
2639 this.pssm[row][col] = pssm[row][col]; | |
2640 } | |
2641 } | |
2642 } | |
2643 } | |
2644 } | |
2645 }; | |
2646 | |
2647 Pspm.prototype.copy = function() { | |
2648 "use strict"; | |
2649 return new Pspm(this); | |
2650 }; | |
2651 | |
2652 Pspm.prototype.reverse = function() { | |
2653 "use strict"; | |
2654 var x, y, temp, temp_trim; | |
2655 //reverse | |
2656 x = 0; | |
2657 y = this.motif_length-1; | |
2658 while (x < y) { | |
2659 temp = this.pspm[x]; | |
2660 this.pspm[x] = this.pspm[y]; | |
2661 this.pspm[y] = temp; | |
2662 x++; | |
2663 y--; | |
2664 } | |
2665 // reverse pssm (if defined) | |
2666 if (typeof this.pssm !== "undefined") { | |
2667 //reverse | |
2668 x = 0; | |
2669 y = this.motif_length-1; | |
2670 while (x < y) { | |
2671 temp = this.pssm[x]; | |
2672 this.pspm[x] = this.pssm[y]; | |
2673 this.pssm[y] = temp; | |
2674 x++; | |
2675 y--; | |
2676 } | |
2677 } | |
2678 //swap triming | |
2679 temp_trim = this.ltrim; | |
2680 this.ltrim = this.rtrim; | |
2681 this.rtrim = temp_trim; | |
2682 return this; //allow function chaining... | |
2683 }; | |
2684 | |
2685 Pspm.prototype.reverse_complement = function(alphabet) { | |
2686 "use strict"; | |
2687 var x, y, temp, i, row, c, temp_trim; | |
2688 if (this.alph_length != alphabet.get_size_core()) { | |
2689 throw new Error("The alphabet size does not match the size of the pspm."); | |
2690 } | |
2691 if (!alphabet.has_complement()) { | |
2692 throw new Error("The specified alphabet can not be complemented."); | |
2693 } | |
2694 // reverse motif | |
2695 this.reverse(); | |
2696 //complement | |
2697 for (x = 0; x < this.motif_length; x++) { | |
2698 row = this.pspm[x]; | |
2699 for (i = 0; i < row.length; i++) { | |
2700 c = alphabet.get_complement(i); | |
2701 if (c < i) continue; | |
2702 temp = row[i]; | |
2703 row[i] = row[c]; | |
2704 row[c] = temp; | |
2705 } | |
2706 } | |
2707 // complement pssm (if defined) | |
2708 if (typeof this.pssm !== "undefined") { | |
2709 //complement | |
2710 for (x = 0; x < this.motif_length; x++) { | |
2711 row = this.pssm[x]; | |
2712 for (i = 0; i < row.length; i++) { | |
2713 c = alphabet.get_complement(i); | |
2714 if (c < i) continue; | |
2715 temp = row[i]; | |
2716 row[i] = row[c]; | |
2717 row[c] = temp; | |
2718 } | |
2719 } | |
2720 } | |
2721 return this; //allow function chaining... | |
2722 }; | |
2723 | |
2724 Pspm.prototype.get_stack = function(position, alphabet, ssc) { | |
2725 "use strict"; | |
2726 var row, stack_ic, alphabet_ic, stack, i, sym; | |
2727 if (this.alph_length != alphabet.get_size_core()) { | |
2728 throw new Error("The alphabet size does not match the size of the pspm."); | |
2729 } | |
2730 row = this.pspm[position]; | |
2731 stack_ic = this.get_stack_ic(position, alphabet); | |
2732 if (ssc) stack_ic -= this.get_error(alphabet); | |
2733 alphabet_ic = alphabet.get_ic(); | |
2734 stack = []; | |
2735 for (i = 0; i < this.alph_length; i++) { | |
2736 sym = new Symbol(i, row[i]*stack_ic/alphabet_ic, alphabet); | |
2737 if (sym.get_scale() <= 0) { | |
2738 continue; | |
2739 } | |
2740 stack.push(sym); | |
2741 } | |
2742 stack.sort(compare_symbol); | |
2743 return stack; | |
2744 }; | |
2745 | |
2746 Pspm.prototype.get_stack_ic = function(position, alphabet) { | |
2747 "use strict"; | |
2748 var row, H, i; | |
2749 if (this.alph_length != alphabet.get_size_core()) { | |
2750 throw new Error("The alphabet size does not match the size fo the pspm."); | |
2751 } | |
2752 row = this.pspm[position]; | |
2753 H = 0; | |
2754 for (i = 0; i < this.alph_length; i++) { | |
2755 if (row[i] === 0) { | |
2756 continue; | |
2757 } | |
2758 H -= (row[i] * (Math.log(row[i]) / Math.LN2)); | |
2759 } | |
2760 return alphabet.get_ic() - H; | |
2761 }; | |
2762 | |
2763 Pspm.prototype.get_error = function(alphabet) { | |
2764 "use strict"; | |
2765 if (this.nsites === 0) { | |
2766 return 0; | |
2767 } | |
2768 return (alphabet.get_size_core()-1) / (2 * Math.LN2 * this.nsites); | |
2769 }; | |
2770 | |
2771 Pspm.prototype.get_motif_length = function() { | |
2772 "use strict"; | |
2773 return this.motif_length; | |
2774 }; | |
2775 | |
2776 Pspm.prototype.get_alph_length = function() { | |
2777 "use strict"; | |
2778 return this.alph_length; | |
2779 }; | |
2780 | |
2781 Pspm.prototype.get_left_trim = function() { | |
2782 "use strict"; | |
2783 return this.ltrim; | |
2784 }; | |
2785 | |
2786 Pspm.prototype.get_right_trim = function() { | |
2787 "use strict"; | |
2788 return this.rtrim; | |
2789 }; | |
2790 | |
2791 Pspm.prototype.as_best_match = function(alphabet) { | |
2792 "use strict"; | |
2793 var match, odds, best_odds, best_index; | |
2794 var i, j; | |
2795 match = ""; | |
2796 for (i = 0; i < this.motif_length; i++) { | |
2797 best_index = 0; | |
2798 best_odds = this.pspm[i][0] / alphabet.get_bg_freq(0); | |
2799 for (j = 1; j < this.alph_length; j++) { | |
2800 odds = this.pspm[i][j] / alphabet.get_bg_freq(j); | |
2801 if (odds > best_odds) { | |
2802 best_odds = odds; | |
2803 best_index = j; | |
2804 } | |
2805 } | |
2806 match += alphabet.get_symbol(best_index); | |
2807 } | |
2808 return match; | |
2809 }; | |
2810 | |
2811 Pspm.prototype.as_count_matrix = function() { | |
2812 "use strict"; | |
2813 var count, count_text, text; | |
2814 var i, j; | |
2815 text = ""; | |
2816 for (i = 0; i < this.motif_length; i++) { | |
2817 if (i !== 0) { | |
2818 text += "\n"; | |
2819 } | |
2820 for (j = 0; j < this.alph_length; j++) { | |
2821 if (j !== 0) { | |
2822 text += " "; | |
2823 } | |
2824 count = Math.round(this.nsites * this.pspm[i][j]); | |
2825 count_text = "" + count; | |
2826 // pad up to length of 4 | |
2827 if (count_text.length < 4) { | |
2828 text += (new Array(5 - count_text.length)).join(" ") + count_text; | |
2829 } else { | |
2830 text += count_text; | |
2831 } | |
2832 } | |
2833 } | |
2834 return text; | |
2835 }; | |
2836 | |
2837 Pspm.prototype.as_probability_matrix = function() { | |
2838 "use strict"; | |
2839 var text; | |
2840 var i, j; | |
2841 text = ""; | |
2842 for (i = 0; i < this.motif_length; i++) { | |
2843 if (i !== 0) { | |
2844 text += "\n"; | |
2845 } | |
2846 for (j = 0; j < this.alph_length; j++) { | |
2847 if (j !== 0) { | |
2848 text += " "; | |
2849 } | |
2850 text += this.pspm[i][j].toFixed(6); | |
2851 } | |
2852 } | |
2853 return text; | |
2854 }; | |
2855 | |
2856 Pspm.prototype.as_score_matrix = function(alphabet, pseudo) { | |
2857 "use strict"; | |
2858 var me, score, out, row, col, score_text; | |
2859 me = this; | |
2860 if (typeof this.pssm === "undefined") { | |
2861 if (!(typeof alphabet === "object" && alphabet != null && alphabet instanceof Alphabet)) { | |
2862 throw new Error("The alphabet is required to generate the pssm."); | |
2863 } | |
2864 if (typeof pseudo === "undefined") { | |
2865 pseudo = 0.01; | |
2866 } else if (typeof pseudo !== "number" || pseudo < 0) { | |
2867 throw new Error("Expected positive number for pseudocount"); | |
2868 } | |
2869 score = function(row, col) { | |
2870 "use strict"; | |
2871 var p, bg, p2; | |
2872 p = me.pspm[row][col]; | |
2873 bg = alphabet.get_bg_freq(col); | |
2874 p2 = (p * me.nsites + bg * pseudo) / (me.nsites + pseudo); | |
2875 return (p2 > 0 ? Math.round((Math.log(p2 / bg) / Math.LN2) * 100) : -10000); | |
2876 }; | |
2877 } else { | |
2878 score = function(row, col) { | |
2879 "use strict"; | |
2880 return me.pssm[row][col]; | |
2881 }; | |
2882 } | |
2883 out = ""; | |
2884 for (row = 0; row < this.motif_length; row++) { | |
2885 for (col = 0; col < this.alph_length; col++) { | |
2886 if (col !== 0) { | |
2887 out += " "; | |
2888 } | |
2889 score_text = "" + score(row, col); | |
2890 // pad out to 6 characters | |
2891 if (score_text.length < 6) { | |
2892 out += (new Array(7 - score_text.length)).join(" ") + score_text; | |
2893 } else { | |
2894 out += score_text; | |
2895 } | |
2896 } | |
2897 out += "\n"; | |
2898 } | |
2899 return out; | |
2900 } | |
2901 | |
2902 Pspm.prototype.as_pspm = function() { | |
2903 "use strict"; | |
2904 return "letter-probability matrix: alength= " + this.alph_length + | |
2905 " w= " + this.motif_length + " nsites= " + this.nsites + | |
2906 " E= " + (typeof this.evalue === "number" ? | |
2907 this.evalue.toExponential() : this.evalue) + "\n" + | |
2908 this.as_probability_matrix(); | |
2909 }; | |
2910 | |
2911 Pspm.prototype.as_pssm = function(alphabet, pseudo) { | |
2912 "use strict"; | |
2913 return "log-odds matrix: alength= " + this.alph_length + | |
2914 " w= " + this.motif_length + | |
2915 " E= " + (typeof this.evalue == "number" ? | |
2916 this.evalue.toExponential() : this.evalue) + "\n" + | |
2917 this.as_score_matrix(alphabet, pseudo); | |
2918 }; | |
2919 | |
2920 Pspm.prototype.as_meme = function(options) { | |
2921 var with_header, with_pspm, with_pssm, version, alphabet, bg_source, pseudocount, strands; | |
2922 var out, alen, i; | |
2923 // get the options | |
2924 if (typeof options !== "object" || options === null) { | |
2925 options = {}; | |
2926 } | |
2927 with_header = (typeof options["with_header"] === "boolean" ? options["with_header"] : false); | |
2928 with_pspm = (typeof options["with_pspm"] === "boolean" ? options["with_pspm"] : false); | |
2929 with_pssm = (typeof options["with_pssm"] === "boolean" ? options["with_pssm"] : false); | |
2930 if (!with_pspm && !with_pssm) with_pspm = true; | |
2931 if (with_header) { | |
2932 if (typeof options["version"] === "string" && /^\d+(?:\.\d+){0,2}$/.test(options["version"])) { | |
2933 version = options["version"]; | |
2934 } else if (typeof options["version"] === "number") { | |
2935 version = options["version"].toFixed(0); | |
2936 } else { | |
2937 version = "4"; | |
2938 } | |
2939 if (typeof options["strands"] === "number" && options["strands"] === 1) { | |
2940 strands = 1; | |
2941 } else { | |
2942 strands = 2; | |
2943 } | |
2944 if (typeof options["bg_source"] === "string") { | |
2945 bg_source = options["bg_source"]; | |
2946 } else { | |
2947 bg_source = "unknown source"; | |
2948 } | |
2949 if (typeof options["alphabet"] === "object" && options["alphabet"] != null | |
2950 && options["alphabet"] instanceof Alphabet) { | |
2951 alphabet = options["alphabet"]; | |
2952 } else { | |
2953 throw new Error("The alphabet is required to generate the header."); | |
2954 } | |
2955 } | |
2956 // now create the output | |
2957 out = ""; | |
2958 if (with_header) { | |
2959 out = "MEME version " + version + "\n\n"; | |
2960 out += alphabet.as_meme() + "\n"; | |
2961 if (alphabet.has_complement()) { // assume DNA has both strands unless otherwise specified | |
2962 out += "strands: " + (strands === 1 ? "+" : "+ -") + "\n\n"; | |
2963 } | |
2964 out += "Background letter frequencies (from " + bg_source + "):\n"; | |
2965 alen = alphabet.get_size_core(); | |
2966 for (i = 0; i < alen; i++) { | |
2967 if (i !== 0) { | |
2968 if (i % 9 === 0) { // maximum of nine entries per line | |
2969 out += "\n"; | |
2970 } else { | |
2971 out += " "; | |
2972 } | |
2973 } | |
2974 out += alphabet.get_symbol(i) + " " + alphabet.get_bg_freq(i).toFixed(3); | |
2975 } | |
2976 } | |
2977 out += "\n\n"; | |
2978 out += "MOTIF " + this.name + (this.alt == null ? "" : " " + this.alt); | |
2979 if (with_pssm) { | |
2980 out += "\n\n"; | |
2981 out += this.as_pssm(options["alphabet"], options["pseudocount"]); | |
2982 } | |
2983 if (with_pspm) { | |
2984 out += "\n\n"; | |
2985 out += this.as_pspm(); | |
2986 } | |
2987 return out; | |
2988 } | |
2989 | |
2990 Pspm.prototype.toString = function() { | |
2991 "use strict"; | |
2992 var str, i, row; | |
2993 str = ""; | |
2994 for (i = 0; i < this.pspm.length; i++) { | |
2995 row = this.pspm[i]; | |
2996 str += row.join("\t") + "\n"; | |
2997 } | |
2998 return str; | |
2999 }; | |
3000 | |
3001 function parse_pspm_properties(str) { | |
3002 "use strict"; | |
3003 var parts, i, eqpos, before, after, properties, prop, num, num_re; | |
3004 num_re = /^((?:[+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?)|inf)$/; | |
3005 parts = trim(str).split(/\s+/); | |
3006 // split up words containing = | |
3007 for (i = 0; i < parts.length;) { | |
3008 eqpos = parts[i].indexOf("="); | |
3009 if (eqpos != -1) { | |
3010 before = parts[i].substr(0, eqpos); | |
3011 after = parts[i].substr(eqpos+1); | |
3012 if (before.length > 0 && after.length > 0) { | |
3013 parts.splice(i, 1, before, "=", after); | |
3014 i += 3; | |
3015 } else if (before.length > 0) { | |
3016 parts.splice(i, 1, before, "="); | |
3017 i += 2; | |
3018 } else if (after.length > 0) { | |
3019 parts.splice(i, 1, "=", after); | |
3020 i += 2; | |
3021 } else { | |
3022 parts.splice(i, 1, "="); | |
3023 i++; | |
3024 } | |
3025 } else { | |
3026 i++; | |
3027 } | |
3028 } | |
3029 properties = {}; | |
3030 for (i = 0; i < parts.length; i += 3) { | |
3031 if (parts.length - i < 3) { | |
3032 throw new Error("Expected PSPM property was incomplete. "+ | |
3033 "Remaing parts are: " + parts.slice(i).join(" ")); | |
3034 } | |
3035 if (parts[i+1] !== "=") { | |
3036 throw new Error("Expected '=' in PSPM property between key and " + | |
3037 "value but got " + parts[i+1]); | |
3038 } | |
3039 prop = parts[i].toLowerCase(); | |
3040 num = parts[i+2]; | |
3041 if (!num_re.test(num)) { | |
3042 throw new Error("Expected numeric value for PSPM property '" + | |
3043 prop + "' but got '" + num + "'"); | |
3044 } | |
3045 properties[prop] = num; | |
3046 } | |
3047 return properties; | |
3048 } | |
3049 | |
3050 function parse_pspm_string(pspm_string) { | |
3051 "use strict"; | |
3052 var header_re, lines, first_line, line_num, col_num, alph_length, | |
3053 motif_length, nsites, evalue, pspm, i, line, match, props, parts, | |
3054 j, prob; | |
3055 header_re = /^letter-probability\s+matrix:(.*)$/i; | |
3056 lines = pspm_string.split(/\n/); | |
3057 first_line = true; | |
3058 line_num = 0; | |
3059 col_num = 0; | |
3060 alph_length; | |
3061 motif_length; | |
3062 nsites; | |
3063 evalue; | |
3064 pspm = []; | |
3065 for (i = 0; i < lines.length; i++) { | |
3066 line = trim(lines[i]); | |
3067 if (line.length === 0) { | |
3068 continue; | |
3069 } | |
3070 // check the first line for a header though allow matrices without it | |
3071 if (first_line) { | |
3072 first_line = false; | |
3073 match = header_re.exec(line); | |
3074 if (match !== null) { | |
3075 props = parse_pspm_properties(match[1]); | |
3076 if (props.hasOwnProperty("alength")) { | |
3077 alph_length = parseFloat(props["alength"]); | |
3078 if (alph_length != 4 && alph_length != 20) { | |
3079 throw new Error("PSPM property alength should be 4 or 20" + | |
3080 " but got " + alph_length); | |
3081 } | |
3082 } | |
3083 if (props.hasOwnProperty("w")) { | |
3084 motif_length = parseFloat(props["w"]); | |
3085 if (motif_length % 1 !== 0 || motif_length < 1) { | |
3086 throw new Error("PSPM property w should be an integer larger " + | |
3087 "than zero but got " + motif_length); | |
3088 } | |
3089 } | |
3090 if (props.hasOwnProperty("nsites")) { | |
3091 nsites = parseFloat(props["nsites"]); | |
3092 if (nsites <= 0) { | |
3093 throw new Error("PSPM property nsites should be larger than " + | |
3094 "zero but got " + nsites); | |
3095 } | |
3096 } | |
3097 if (props.hasOwnProperty("e")) { | |
3098 evalue = props["e"]; | |
3099 if (evalue < 0) { | |
3100 throw new Error("PSPM property evalue should be " + | |
3101 "non-negative but got " + evalue); | |
3102 } | |
3103 } | |
3104 continue; | |
3105 } | |
3106 } | |
3107 pspm[line_num] = []; | |
3108 col_num = 0; | |
3109 parts = line.split(/\s+/); | |
3110 for (j = 0; j < parts.length; j++) { | |
3111 prob = parseFloat(parts[j]); | |
3112 if (prob != parts[j] || prob < 0 || prob > 1) { | |
3113 throw new Error("Expected probability but got '" + parts[j] + "'"); | |
3114 } | |
3115 pspm[line_num][col_num] = prob; | |
3116 col_num++; | |
3117 } | |
3118 line_num++; | |
3119 } | |
3120 if (typeof motif_length === "number") { | |
3121 if (pspm.length != motif_length) { | |
3122 throw new Error("Expected PSPM to have a motif length of " + | |
3123 motif_length + " but it was actually " + pspm.length); | |
3124 } | |
3125 } else { | |
3126 motif_length = pspm.length; | |
3127 } | |
3128 if (typeof alph_length !== "number") { | |
3129 alph_length = pspm[0].length; | |
3130 if (alph_length != 4 && alph_length != 20) { | |
3131 throw new Error("Expected length of first row in the PSPM to be " + | |
3132 "either 4 or 20 but got " + alph_length); | |
3133 } | |
3134 } | |
3135 for (i = 0; i < pspm.length; i++) { | |
3136 if (pspm[i].length != alph_length) { | |
3137 throw new Error("Expected PSPM row " + i + " to have a length of " + | |
3138 alph_length + " but the length was " + pspm[i].length); | |
3139 } | |
3140 } | |
3141 return {"pspm": pspm, "motif_length": motif_length, | |
3142 "alph_length": alph_length, "nsites": nsites, "evalue": evalue}; | |
3143 } | |
3144 //====================================================================== | |
3145 // end Pspm object | |
3146 //====================================================================== | |
3147 | |
3148 //====================================================================== | |
3149 // start Logo object | |
3150 //====================================================================== | |
3151 | |
3152 var Logo = function(alphabet, options) { | |
3153 "use strict"; | |
3154 this.alphabet = alphabet; | |
3155 this.fine_text = ""; | |
3156 this.x_axis = 1; | |
3157 this.y_axis = true; | |
3158 this.xlate_nsyms = 1; | |
3159 this.xlate_start = null; | |
3160 this.xlate_end = null; | |
3161 this.pspm_list = []; | |
3162 this.pspm_column = []; | |
3163 this.rows = 0; | |
3164 this.columns = 0; | |
3165 if (typeof options === "string") { | |
3166 // the old method signature had fine_text here so we support that | |
3167 this.fine_text = options; | |
3168 } else if (typeof options === "object" && options != null) { | |
3169 this.fine_text = (typeof options.fine_text === "string" ? options.fine_text : ""); | |
3170 this.x_axis = (typeof options.x_axis === "boolean" ? (options.x_axis ? 1 : 0) : 1); | |
3171 if (options.x_axis_hidden != null && options.x_axis_hidden) this.x_axis = -1; | |
3172 this.y_axis = (typeof options.y_axis === "boolean" ? options.y_axis : true); | |
3173 this.xlate_nsyms = (typeof options.xlate_nsyms === "number" ? options.xlate_nsyms : this.xlate_nsyms); | |
3174 this.xlate_start = (typeof options.xlate_start === "number" ? options.xlate_start : this.xlate_start); | |
3175 this.xlate_end = (typeof options.xlate_end === "number" ? options.xlate_end : this.xlate_end); | |
3176 } | |
3177 }; | |
3178 | |
3179 Logo.prototype.add_pspm = function(pspm, column) { | |
3180 "use strict"; | |
3181 var col; | |
3182 if (typeof column === "undefined") { | |
3183 column = 0; | |
3184 } else if (column < 0) { | |
3185 throw new Error("Column index out of bounds."); | |
3186 } | |
3187 this.pspm_list[this.rows] = pspm; | |
3188 this.pspm_column[this.rows] = column; | |
3189 this.rows++; | |
3190 col = column + pspm.get_motif_length(); | |
3191 if (col > this.columns) { | |
3192 this.columns = col; | |
3193 } | |
3194 }; | |
3195 | |
3196 Logo.prototype.get_columns = function() { | |
3197 "use strict"; | |
3198 return this.columns; | |
3199 }; | |
3200 | |
3201 Logo.prototype.get_xlate_nsyms = function() { | |
3202 "use strict"; | |
3203 return this.xlate_nsyms; | |
3204 }; | |
3205 | |
3206 Logo.prototype.get_xlate_start = function() { | |
3207 "use strict"; | |
3208 return (this.xlate_start != null ? this.xlate_start : 0); | |
3209 }; | |
3210 | |
3211 Logo.prototype.get_xlate_end = function() { | |
3212 "use strict"; | |
3213 return (this.xlate_end != null ? this.xlate_end : this.columns * this.xlate_nsyms); | |
3214 }; | |
3215 | |
3216 Logo.prototype.get_xlate_columns = function() { | |
3217 "use strict"; | |
3218 return this.get_xlate_end() - this.get_xlate_start(); | |
3219 }; | |
3220 | |
3221 Logo.prototype.get_rows = function() { | |
3222 "use strict"; | |
3223 return this.rows; | |
3224 }; | |
3225 | |
3226 Logo.prototype.get_pspm = function(row_index) { | |
3227 "use strict"; | |
3228 if (row_index < 0 || row_index >= this.rows) { | |
3229 throw new Error("INDEX_OUT_OF_BOUNDS"); | |
3230 } | |
3231 return this.pspm_list[row_index]; | |
3232 }; | |
3233 | |
3234 Logo.prototype.get_offset = function(row_index) { | |
3235 "use strict"; | |
3236 if (row_index < 0 || row_index >= this.rows) { | |
3237 throw new Error("INDEX_OUT_OF_BOUNDS"); | |
3238 } | |
3239 return this.pspm_column[row_index]; | |
3240 }; | |
3241 | |
3242 Logo.prototype._as_eps_data = function(ssc, errbars) { | |
3243 var i, j, pos, stack_pos, pspm, stack, sym, out; | |
3244 out = ""; | |
3245 for (i = 0; i < this.rows; i++) { | |
3246 out += "\nStartLine\n"; | |
3247 // Indent | |
3248 for (j = 0; j < this.pspm_column[i]; j++) { | |
3249 out += "() startstack\nendstack\n\n"; | |
3250 } | |
3251 pspm = this.pspm_list[i]; | |
3252 if (pspm.get_left_trim() > 0) { | |
3253 out += "MuteColour\nDrawTrimEdge\n" + pspm.get_left_trim() + " DrawTrimBg\n"; | |
3254 } | |
3255 for (pos = 0; pos < pspm.get_motif_length(); pos++) { | |
3256 if (pos != 0 && pos == pspm.get_left_trim()) { // enable full colour | |
3257 out += "DrawTrimEdge\nRestoreColour\n"; | |
3258 } else if (pos == (pspm.get_motif_length() - pspm.get_right_trim())) { | |
3259 out += "MuteColour\n" + pspm.get_right_trim() + " DrawTrimBg\n"; | |
3260 } | |
3261 out += "(" + (pos + 1) + ") startstack\n"; | |
3262 stack = pspm.get_stack(pos, this.alphabet, ssc); | |
3263 for (stack_pos = 0; stack_pos < stack.length; stack_pos++) { | |
3264 sym = stack[stack_pos]; | |
3265 out += " " + (sym.get_scale() * this.alphabet.get_ic()) + " (" + sym.get_symbol() + ") numchar\n"; | |
3266 } | |
3267 if (errbars) { | |
3268 out += " " + pspm.get_error(this.alphabet) + " Ibeam\n"; | |
3269 } | |
3270 out += "endstack\n\n"; | |
3271 } | |
3272 if (pspm.get_right_trim() > 0 || pspm.get_left_trim() == pspm.get_motif_length()) { | |
3273 out += "RestoreColour\n"; | |
3274 } | |
3275 out += "EndLine\n"; | |
3276 } | |
3277 return out; | |
3278 }; | |
3279 | |
3280 Logo.prototype.as_eps = function(options) { | |
3281 "use strict"; | |
3282 if (this.xlate_nsyms != 1) throw new Error("Unsupported setting xlate_nsyms for EPS"); | |
3283 if (this.xlate_start != null) throw new Error("Unsupported setting xlate_start for EPS"); | |
3284 if (this.xlate_end != null) throw new Error("Unsupported setting xlate_end for EPS"); | |
3285 | |
3286 var LOGOHEIGHT = 7.5; // default height of line in cm | |
3287 var cm2pts, height, width, now, ssc, errbars; | |
3288 if (typeof options === "undefined") { | |
3289 options = {}; | |
3290 } | |
3291 cm2pts = 72 / 2.54; | |
3292 if (typeof options.logo_height == "number") { | |
3293 height = options.logo_height; | |
3294 } else { | |
3295 height = LOGOHEIGHT * this.rows; | |
3296 } | |
3297 if (typeof options.logo_width == "number") { | |
3298 width = options.logo_width; | |
3299 } else { | |
3300 width = this.columns + 2; | |
3301 } | |
3302 now = new Date(); | |
3303 ssc = (typeof options.ssc == "boolean" ? options.ssc : false); | |
3304 errbars = (typeof options.show_error_bar == "boolean" ? options.show_error_bar : ssc); | |
3305 var values = { | |
3306 "LOGOHEIGHT": height, | |
3307 "LOGOWIDTH": width, | |
3308 "BOUNDINGHEIGHT": Math.round(height * cm2pts), | |
3309 "BOUNDINGWIDTH": Math.round(width * cm2pts), | |
3310 "LOGOLINEHEIGHT": (height / this.rows), | |
3311 "CHARSPERLINE": this.columns, | |
3312 "BARBITS": this.alphabet.get_ic(), | |
3313 "LOGOTYPE": (this.alphabet.has_complement() ? "NA" : "AA"), | |
3314 "CREATIONDATE": now.getDate() + "." + (now.getMonth() + 1) + "." + now.getFullYear() + " " + now.getHours() + ":" + now.getMinutes() + ":" + now.getSeconds(), | |
3315 "ERRORBARFRACTION": (typeof options.error_bar_fraction == "number" ? options.error_bar_fraction : 1.0), | |
3316 "TICBITS": (typeof options.ticbits == "number" ? options.ticbits : 1.0), | |
3317 "TITLE": (typeof options.title == "string" ? options.title : ""), | |
3318 "FINEPRINT": (typeof options.fineprint == "string" ? options.fineprint : this.fine_text), | |
3319 "XAXISLABEL": (typeof options.xaxislabel == "string" ? options.xaxislabel : ""), | |
3320 "YAXISLABEL": (typeof options.yaxislabel == "string" ? options.yaxislabel : "bits"), | |
3321 "SSC": ssc, | |
3322 "YAXIS": (typeof options.show_y_axis == "boolean" ? options.show_y_axis : this.y_axis), | |
3323 "SHOWENDS": (typeof options.show_ends == "boolean" ? options.show_ends : false), | |
3324 "ERRBAR": errbars, | |
3325 "OUTLINE": (typeof options.show_outline == "boolean" ? options.show_outline : false), | |
3326 "NUMBERING": (typeof options.show_numbering == "boolean" ? options.show_numbering : this.x_axis != 0), | |
3327 "SHOWINGBOX": (typeof options.show_box == "boolean" ? options.show_box : false), | |
3328 "CREATOR": (typeof options.creator == "string" ? options.creator : "motif_logo.js"), | |
3329 "FONTSIZE": (typeof options.label_font_size == "number" ? options.label_font_size : 12), | |
3330 "TITLEFONTSIZE": (typeof options.title_font_size == "number" ? options.title_font_size : 12), | |
3331 "SMALLFONTSIZE": (typeof options.small_font_size == "number" ? options.small_font_size : 6), | |
3332 "TOPMARGIN" : (typeof options.top_margin == "number" ? options.top_margin : 0.9), | |
3333 "BOTTOMMARGIN": (typeof options.bottom_margin == "number" ? options.bottom_margin : 0.9), | |
3334 "COLORDICT": this.alphabet._as_eps_dict(), | |
3335 "DATA": this._as_eps_data(ssc, errbars) | |
3336 }; | |
3337 // now this requires that the script containing the template has been imported! | |
3338 return motif_logo_template(values); | |
3339 }; | |
3340 | |
3341 //====================================================================== | |
3342 // end Logo object | |
3343 //====================================================================== | |
3344 | |
3345 // calculate the exact size (in pixels) of an object drawn on the | |
3346 // canvas assuming that the background of the canvas is transparent. | |
3347 function canvas_bounds(ctx, cwidth, cheight) { | |
3348 "use strict"; | |
3349 var data, r, c, top_line, bottom_line, left_line, right_line, | |
3350 txt_width, txt_height; | |
3351 | |
3352 // extract the image data | |
3353 data = ctx.getImageData(0, 0, cwidth, cheight).data; | |
3354 | |
3355 // set initial values | |
3356 top_line = -1; bottom_line = -1; left_line = -1; right_line = -1; | |
3357 txt_width = 0; txt_height = 0; | |
3358 | |
3359 // Find the top-most line with a non-transparent pixel | |
3360 for (r = 0; r < cheight; r++) { | |
3361 for (c = 0; c < cwidth; c++) { | |
3362 if (data[r * cwidth * 4 + c * 4 + 3]) { | |
3363 top_line = r; | |
3364 break; | |
3365 } | |
3366 } | |
3367 if (top_line != -1) { | |
3368 break; | |
3369 } | |
3370 } | |
3371 | |
3372 // Only bother looking if we found at least one set pixel... | |
3373 if (top_line != -1) { | |
3374 | |
3375 //find the last line with a non-transparent pixel | |
3376 for (r = cheight-1; r >= top_line; r--) { | |
3377 for(c = 0; c < cwidth; c++) { | |
3378 if(data[r * cwidth * 4 + c * 4 + 3]) { | |
3379 bottom_line = r; | |
3380 break; | |
3381 } | |
3382 } | |
3383 if (bottom_line != -1) { | |
3384 break; | |
3385 } | |
3386 } | |
3387 // calculate height | |
3388 txt_height = bottom_line - top_line + 1; | |
3389 | |
3390 // Find the left-most line with a non-transparent pixel | |
3391 for (c = 0; c < cwidth; c++) { | |
3392 for (r = top_line; r <= bottom_line; r++) { | |
3393 if (data[r * cwidth * 4 + c * 4 + 3]) { | |
3394 left_line = c; | |
3395 break; | |
3396 } | |
3397 } | |
3398 if (left_line != -1) { | |
3399 break; | |
3400 } | |
3401 } | |
3402 | |
3403 //find the right most line with a non-transparent pixel | |
3404 for (c = cwidth-1; c >= left_line; c--) { | |
3405 for(r = top_line; r <= bottom_line; r++) { | |
3406 if(data[r * cwidth * 4 + c * 4 + 3]) { | |
3407 right_line = c; | |
3408 break; | |
3409 } | |
3410 } | |
3411 if (right_line != -1) { | |
3412 break; | |
3413 } | |
3414 } | |
3415 txt_width = right_line - left_line + 1; | |
3416 } | |
3417 | |
3418 //return the bounds | |
3419 return {bound_top: top_line, bound_bottom: bottom_line, | |
3420 bound_left: left_line, bound_right: right_line, width: txt_width, | |
3421 height: txt_height}; | |
3422 } | |
3423 | |
3424 //====================================================================== | |
3425 // start RasterizedAlphabet | |
3426 //====================================================================== | |
3427 | |
3428 // Rasterize Alphabet | |
3429 // 1) Measure width of text at default font for all symbols in alphabet | |
3430 // 2) sort in width ascending | |
3431 // 3) Drop the top and bottom 10% (designed to ignore outliers like 'W' and 'I') | |
3432 // 4) Calculate the average as the maximum scaling factor (designed to stop I becoming a rectangular blob). | |
3433 // 5) Assume scale of zero would result in width of zero, interpolate scale required to make perfect width font | |
3434 // 6) Draw text onto temp canvas at calculated scale | |
3435 // 7) Find bounds of drawn text | |
3436 // 8) Paint on to another canvas at the desired height (but only scaling width to fit if larger). | |
3437 var RasterizedAlphabet = function(alphabet, logo_scale, font, width) { | |
3438 "use strict"; | |
3439 var default_size, safety_pad, canvas, ctx, middle, baseline, widths, sizes, | |
3440 i, sym, size, tenpercent, avg_width, scale, | |
3441 target_width, target_height; | |
3442 //variable prototypes | |
3443 this.alphabet = alphabet; | |
3444 this.scale = logo_scale; | |
3445 this.sym_cache = {}; | |
3446 this.stack_num_cache = []; | |
3447 this.scale_num_cache = []; | |
3448 // size of canvas | |
3449 default_size = 60; // size of measuring canvas | |
3450 safety_pad = 20; // pixels to pad around so we don't miss the edges | |
3451 // create a canvas to do our measuring | |
3452 canvas = document.createElement("canvas"); | |
3453 if (!canvas.getContext) throw new Error("No canvas support"); | |
3454 canvas.width = default_size + 2 * safety_pad; | |
3455 canvas.height = default_size + 2 * safety_pad; | |
3456 middle = Math.round(canvas.width / 2); | |
3457 baseline = Math.round(canvas.height - safety_pad); | |
3458 ctx = canvas.getContext('2d'); | |
3459 if (!supports_text(ctx)) throw new Error("Canvas does not support text"); | |
3460 ctx.font = font; | |
3461 ctx.textAlign = "center"; | |
3462 ctx.translate(middle, baseline); | |
3463 // list of widths | |
3464 widths = []; | |
3465 sizes = []; | |
3466 //now measure each letter in the alphabet | |
3467 for (i = 0; i < alphabet.get_size_core(); ++i) { | |
3468 // reset the canvas | |
3469 ctx.clearRect(0, 0, canvas.width, canvas.height); | |
3470 ctx.fillStyle = alphabet.get_colour(i); | |
3471 // draw the test text | |
3472 ctx.fillText(alphabet.get_symbol(i), 0, 0); | |
3473 //measure | |
3474 size = canvas_bounds(ctx, canvas.width, canvas.height); | |
3475 if (size.width === 0) throw new Error("Invisible symbol!"); | |
3476 widths.push(size.width); | |
3477 sizes[i] = size; | |
3478 } | |
3479 //sort the widths | |
3480 widths.sort(function(a,b) {return a - b;}); | |
3481 //drop 10% of the items off each end | |
3482 tenpercent = Math.floor(widths.length / 10); | |
3483 for (i = 0; i < tenpercent; ++i) { | |
3484 widths.pop(); | |
3485 widths.shift(); | |
3486 } | |
3487 //calculate average width | |
3488 avg_width = 0; | |
3489 for (i = 0; i < widths.length; ++i) { | |
3490 avg_width += widths[i]; | |
3491 } | |
3492 avg_width /= widths.length; | |
3493 // calculate the target width | |
3494 target_width = width * this.scale * 2; | |
3495 // calculate scales | |
3496 for (i = 0; i < alphabet.get_size_core(); ++i) { | |
3497 sym = alphabet.get_symbol(i); | |
3498 size = sizes[i]; | |
3499 // calculate scale | |
3500 scale = target_width / Math.max(avg_width, size.width); | |
3501 // estimate scaled height | |
3502 target_height = size.height * scale; | |
3503 // create an appropriately sized canvas | |
3504 canvas = document.createElement("canvas"); | |
3505 canvas.width = target_width; | |
3506 canvas.height = target_height + safety_pad * 2; | |
3507 // calculate the middle | |
3508 middle = Math.round(canvas.width / 2); | |
3509 // calculate the baseline | |
3510 baseline = Math.round(canvas.height - safety_pad); | |
3511 // get the context and prepare to draw the rasterized text | |
3512 ctx = canvas.getContext('2d'); | |
3513 ctx.font = font; | |
3514 ctx.fillStyle = alphabet.get_colour(i); | |
3515 ctx.textAlign = "center"; | |
3516 ctx.translate(middle, baseline); | |
3517 ctx.save(); | |
3518 ctx.scale(scale, scale); | |
3519 // draw the text | |
3520 ctx.fillText(sym, 0, 0); | |
3521 ctx.restore(); | |
3522 this.sym_cache[sym] = {"image": canvas, "size": canvas_bounds(ctx, canvas.width, canvas.height)}; | |
3523 } | |
3524 }; | |
3525 | |
3526 RasterizedAlphabet.prototype.get_alphabet = function() { | |
3527 return this.alphabet; | |
3528 }; | |
3529 | |
3530 RasterizedAlphabet.prototype.get_scale = function() { | |
3531 return this.scale; | |
3532 }; | |
3533 | |
3534 RasterizedAlphabet.prototype.draw_stack_sym = function(ctx, letter, dx, dy, dWidth, dHeight) { | |
3535 "use strict"; | |
3536 var entry, image, size; | |
3537 entry = this.sym_cache[letter]; | |
3538 image = entry.image; | |
3539 size = entry.size; | |
3540 ctx.drawImage(image, 0, size.bound_top -1, image.width, size.height+1, dx, dy, dWidth, dHeight); | |
3541 }; | |
3542 | |
3543 RasterizedAlphabet.prototype.draw_stack_num = function(ctx, font, stack_width, index) { | |
3544 var image, image_ctx, text_length; | |
3545 if (index >= this.stack_num_cache.length) { | |
3546 image = document.createElement("canvas"); | |
3547 // measure the text | |
3548 image_ctx = image.getContext('2d'); | |
3549 image_ctx.save(); | |
3550 image_ctx.font = font; | |
3551 text_length = image_ctx.measureText("" + (index + 1)).width; | |
3552 image_ctx.restore(); | |
3553 // resize the canvas to fit | |
3554 image.width = Math.ceil(stack_width); | |
3555 image.height = Math.ceil(text_length); | |
3556 // draw the text | |
3557 image_ctx = image.getContext('2d'); | |
3558 image_ctx.translate(Math.round(stack_width / 2), 0); | |
3559 image_ctx.font = font; | |
3560 image_ctx.textBaseline = "middle"; | |
3561 image_ctx.textAlign = "right"; | |
3562 image_ctx.rotate(-(Math.PI / 2)); | |
3563 image_ctx.fillText("" + (index + 1), 0, 0); | |
3564 this.stack_num_cache[index] = image; | |
3565 } else { | |
3566 image = this.stack_num_cache[index]; | |
3567 } | |
3568 ctx.drawImage(image, 0, 0); | |
3569 } | |
3570 | |
3571 RasterizedAlphabet.prototype.draw_scale_num = function(ctx, font, num) { | |
3572 var image, image_ctx, text_size, m_length; | |
3573 if (num >= this.scale_num_cache.length) { | |
3574 image = document.createElement("canvas"); | |
3575 // measure the text | |
3576 image_ctx = image.getContext('2d'); | |
3577 image_ctx.font = font; | |
3578 text_size = image_ctx.measureText("" + num); | |
3579 if (text_size.actualBoundingBoxAscent && text_size.actualBoundingBoxDesent) { | |
3580 // resize the canvas to fit | |
3581 image.width = Math.ceil(text_size.width); | |
3582 image.height = Math.ceil(text_size.actualBoundingBoxAscent + text_size.actualBoundingBoxDesent); | |
3583 // draw the text | |
3584 image_ctx = image.getContext('2d'); | |
3585 image_ctx.font = font; | |
3586 image_ctx.textAlign = "right"; | |
3587 image_ctx.fillText("" + num, image.width, text_size.actualBoundingBoxAscent); | |
3588 } else { | |
3589 // measure width of 'm' to approximate height, we double it later anyway | |
3590 m_length = image_ctx.measureText("m").width; | |
3591 // resize the canvas to fit | |
3592 image.width = Math.ceil(text_size.width); | |
3593 image.height = Math.ceil(2 * m_length); | |
3594 // draw the text | |
3595 image_ctx = image.getContext('2d'); | |
3596 image_ctx.font = font; | |
3597 image_ctx.textAlign = "right"; | |
3598 image_ctx.textBaseline = "middle"; | |
3599 image_ctx.fillText("" + num, image.width, m_length); | |
3600 } | |
3601 this.scale_num_cache[num] = image; | |
3602 } else { | |
3603 image = this.scale_num_cache[num]; | |
3604 } | |
3605 ctx.drawImage(image, -image.width, -Math.round(image.height / 2)) | |
3606 } | |
3607 | |
3608 //====================================================================== | 115 //====================================================================== |
3609 // end RasterizedAlphabet | 116 // end RasterizedAlphabet |
3610 //====================================================================== | 117 //====================================================================== |
3611 | |
3612 //====================================================================== | 118 //====================================================================== |
3613 // start LogoMetrics object | 119 // start LogoMetrics object |
3614 //====================================================================== | 120 //====================================================================== |
3615 | |
3616 var LogoMetrics = function(ctx, logo_columns, logo_rows, has_names, has_finetext, x_axis, y_axis) { | |
3617 "use strict"; | |
3618 var i, row_height; | |
3619 //variable prototypes | |
3620 this.pad_top = (has_names ? 5 : 0); | |
3621 this.pad_left = (y_axis ? 10 : 0); | |
3622 this.pad_right = (has_finetext ? 15 : 0); | |
3623 this.pad_bottom = 0; | |
3624 this.pad_middle = 20; | |
3625 this.name_height = 14; | |
3626 this.name_font = "bold " + this.name_height + "px Times, sans-serif"; | |
3627 this.name_spacer = 0; | |
3628 this.y_axis = y_axis; | |
3629 this.y_label = "bits"; | |
3630 this.y_label_height = 12; | |
3631 this.y_label_font = "bold " + this.y_label_height + "px Helvetica, sans-serif"; | |
3632 this.y_label_spacer = 3; | |
3633 this.y_num_height = 12; | |
3634 this.y_num_width = 0; | |
3635 this.y_num_font = "bold " + this.y_num_height + "px Helvetica, sans-serif"; | |
3636 this.y_tic_width = 5; | |
3637 this.stack_pad_left = 0; | |
3638 this.stack_font = "bold 25px Helvetica, sans-serif"; | |
3639 this.stack_height = 90; | |
3640 this.stack_width = 26; | |
3641 this.stacks_pad_right = 5; | |
3642 this.x_axis = x_axis; | |
3643 this.x_num_above = 2; | |
3644 this.x_num_height = 12; | |
3645 this.x_num_width = 0; | |
3646 this.x_num_font = "bold " + this.x_num_height + "px Helvetica, sans-serif"; | |
3647 this.fine_txt_height = 6; | |
3648 this.fine_txt_above = 2; | |
3649 this.fine_txt_font = "normal " + this.fine_txt_height + "px Helvetica, sans-serif"; | |
3650 this.letter_metrics = new Array(); | |
3651 this.summed_width = 0; | |
3652 this.summed_height = 0; | |
3653 //calculate the width of the y axis numbers | |
3654 ctx.font = this.y_num_font; | |
3655 for (i = 0; i <= 2; i++) { | |
3656 this.y_num_width = Math.max(this.y_num_width, ctx.measureText("" + i).width); | |
3657 } | |
3658 //calculate the width of the x axis numbers (but they are rotated so it becomes height) | |
3659 if (x_axis == 1) { | |
3660 ctx.font = this.x_num_font; | |
3661 for (i = 1; i <= logo_columns; i++) { | |
3662 this.x_num_width = Math.max(this.x_num_width, ctx.measureText("" + i).width); | |
3663 } | |
3664 } else if (x_axis == 0) { | |
3665 this.x_num_height = 4; | |
3666 this.x_num_width = 4; | |
3667 } else { | |
3668 this.x_num_height = 0; | |
3669 this.x_num_width = 0; | |
3670 } | |
3671 | |
3672 //calculate how much vertical space we want to draw this | |
3673 //first we add the padding at the top and bottom since that's always there | |
3674 this.summed_height += this.pad_top + this.pad_bottom; | |
3675 //all except the last row have the same amount of space allocated to them | |
3676 if (logo_rows > 1) { | |
3677 row_height = this.stack_height + this.pad_middle; | |
3678 if (has_names) { | |
3679 row_height += this.name_height; | |
3680 //the label is allowed to overlap into the spacer | |
3681 row_height += Math.max(this.y_num_height/2, this.name_spacer); | |
3682 //the label is allowed to overlap the space used by the other label | |
3683 row_height += Math.max(this.y_num_height/2, this.x_num_height + this.x_num_above); | |
3684 } else { | |
3685 row_height += this.y_num_height/2; | |
3686 //the label is allowed to overlap the space used by the other label | |
3687 row_height += Math.max(this.y_num_height/2, this.x_num_height + this.x_num_above); | |
3688 } | |
3689 this.summed_height += row_height * (logo_rows - 1); | |
3690 } | |
3691 //the last row has the name and fine text below it but no padding | |
3692 this.summed_height += this.stack_height + (this.y_axis ? this.y_num_height/2 : 0); | |
3693 | |
3694 var fine_txt_total = (has_finetext ? this.fine_txt_height + this.fine_txt_above : 0); | |
3695 if (has_names) { | |
3696 this.summed_height += fine_txt_total + this.name_height; | |
3697 this.summed_height += Math.max((this.y_axis ? this.y_num_height/2 : 0), | |
3698 this.x_num_height + this.x_num_above + this.name_spacer); | |
3699 } else { | |
3700 this.summed_height += Math.max((this.y_axis ? this.y_num_height/2 : 0), | |
3701 this.x_num_height + this.x_num_above + fine_txt_total); | |
3702 } | |
3703 | |
3704 //calculate how much horizontal space we want to draw this | |
3705 //first add the padding at the left and right since that's always there | |
3706 this.summed_width += this.pad_left + this.pad_right; | |
3707 if (this.y_axis) { | |
3708 //add on the space for the y-axis label | |
3709 this.summed_width += this.y_label_height + this.y_label_spacer; | |
3710 //add on the space for the y-axis | |
3711 this.summed_width += this.y_num_width + this.y_tic_width; | |
3712 } | |
3713 //add on the space for the stacks | |
3714 this.summed_width += (this.stack_pad_left + this.stack_width) * logo_columns; | |
3715 //add on the padding after the stacks (an offset from the fine text) | |
3716 this.summed_width += this.stacks_pad_right; | |
3717 | |
3718 }; | |
3719 | |
3720 //====================================================================== | |
3721 // end LogoMetrics object | |
3722 //====================================================================== | |
3723 | |
3724 //found this trick at http://talideon.com/weblog/2005/02/detecting-broken-images-js.cfm | |
3725 function image_ok(img) { | |
3726 "use strict"; | |
3727 // During the onload event, IE correctly identifies any images that | |
3728 // weren't downloaded as not complete. Others should too. Gecko-based | |
3729 // browsers act like NS4 in that they report this incorrectly. | |
3730 if (!img.complete) { | |
3731 return false; | |
3732 } | |
3733 // However, they do have two very useful properties: naturalWidth and | |
3734 // naturalHeight. These give the true size of the image. If it failed | |
3735 // to load, either of these should be zero. | |
3736 if (typeof img.naturalWidth !== "undefined" && img.naturalWidth === 0) { | |
3737 return false; | |
3738 } | |
3739 // No other way of checking: assume it's ok. | |
3740 return true; | |
3741 } | |
3742 | |
3743 function supports_text(ctx) { | |
3744 "use strict"; | |
3745 if (!ctx.fillText) { | |
3746 return false; | |
3747 } | |
3748 if (!ctx.measureText) { | |
3749 return false; | |
3750 } | |
3751 return true; | |
3752 } | |
3753 | |
3754 //draws the scale, returns the width | |
3755 function draw_scale(ctx, metrics, alphabet_ic, raster) { | |
3756 "use strict"; | |
3757 var tic_height, i; | |
3758 tic_height = metrics.stack_height / alphabet_ic; | |
3759 ctx.save(); | |
3760 ctx.translate(metrics.y_label_height, metrics.y_num_height/2); | |
3761 //draw the axis label | |
3762 ctx.save(); | |
3763 ctx.font = metrics.y_label_font; | |
3764 ctx.translate(0, metrics.stack_height/2); | |
3765 ctx.rotate(-(Math.PI / 2)); | |
3766 ctx.textAlign = "center"; | |
3767 ctx.fillText("bits", 0, 0); | |
3768 ctx.restore(); | |
3769 | |
3770 ctx.translate(metrics.y_label_spacer + metrics.y_num_width, 0); | |
3771 | |
3772 //draw the axis tics | |
3773 ctx.save(); | |
3774 ctx.translate(0, metrics.stack_height); | |
3775 for (i = 0; i <= alphabet_ic; i++) { | |
3776 //draw the number | |
3777 ctx.save(); | |
3778 ctx.translate(-1, 0); | |
3779 raster.draw_scale_num(ctx, metrics.y_num_font, i); | |
3780 ctx.restore(); | |
3781 //draw the tic | |
3782 ctx.fillRect(0, -1, metrics.y_tic_width, 2); | |
3783 //prepare for next tic | |
3784 ctx.translate(0, -tic_height); | |
3785 } | |
3786 ctx.restore(); | |
3787 | |
3788 ctx.fillRect(metrics.y_tic_width - 2, 0, 2, metrics.stack_height) | |
3789 | |
3790 ctx.restore(); | |
3791 } | |
3792 | |
3793 function draw_stack_num(ctx, metrics, row_index, raster) { | |
3794 "use strict"; | |
3795 ctx.save(); | |
3796 ctx.translate(0, Math.round(metrics.stack_height + metrics.x_num_above)); | |
3797 if (metrics.x_axis == 1) { | |
3798 raster.draw_stack_num(ctx, metrics.x_num_font, metrics.stack_width, row_index); | |
3799 } else if (metrics.x_axis == 0) { | |
3800 // draw dots instead of the numbers (good for small logos) | |
3801 ctx.beginPath(); | |
3802 var radius = Math.round(metrics.x_num_height / 2); | |
3803 ctx.arc(Math.round(metrics.stack_width / 2), radius, radius, 0, 2 * Math.PI, false); | |
3804 ctx.fill(); | |
3805 } | |
3806 ctx.restore(); | |
3807 } | |
3808 | |
3809 function draw_stack(ctx, metrics, symbols, raster) { | |
3810 "use strict"; | |
3811 var preferred_pad, sym_min, i, sym, sym_height, pad; | |
3812 preferred_pad = 0; | |
3813 sym_min = 5; | |
3814 | |
3815 ctx.save();//1 | |
3816 ctx.translate(0, metrics.stack_height); | |
3817 for (i = 0; i < symbols.length; i++) { | |
3818 sym = symbols[i]; | |
3819 sym_height = metrics.stack_height * sym.get_scale(); | |
3820 | |
3821 pad = preferred_pad; | |
3822 if (sym_height - pad < sym_min) { | |
3823 pad = Math.min(pad, Math.max(0, sym_height - sym_min)); | |
3824 } | |
3825 sym_height -= pad; | |
3826 | |
3827 //translate to the correct position | |
3828 ctx.translate(0, -(pad/2 + sym_height)); | |
3829 | |
3830 //draw | |
3831 raster.draw_stack_sym(ctx, sym.get_symbol(), 0, 0, metrics.stack_width, sym_height); | |
3832 //translate past the padding | |
3833 ctx.translate(0, -(pad/2)); | |
3834 } | |
3835 ctx.restore();//1 | |
3836 } | |
3837 | |
3838 function draw_dashed_line(ctx, pattern, start, x1, y1, x2, y2) { | |
3839 "use strict"; | |
3840 var x, y, len, i, dx, dy, tlen, theta, mulx, muly, lx, ly; | |
3841 dx = x2 - x1; | |
3842 dy = y2 - y1; | |
3843 tlen = Math.pow(dx*dx + dy*dy, 0.5); | |
3844 theta = Math.atan2(dy,dx); | |
3845 mulx = Math.cos(theta); | |
3846 muly = Math.sin(theta); | |
3847 lx = []; | |
3848 ly = []; | |
3849 for (i = 0; i < pattern; ++i) { | |
3850 lx.push(pattern[i] * mulx); | |
3851 ly.push(pattern[i] * muly); | |
3852 } | |
3853 i = start; | |
3854 x = x1; | |
3855 y = y1; | |
3856 len = 0; | |
3857 ctx.beginPath(); | |
3858 while (len + pattern[i] < tlen) { | |
3859 ctx.moveTo(x, y); | |
3860 x += lx[i]; | |
3861 y += ly[i]; | |
3862 ctx.lineTo(x, y); | |
3863 len += pattern[i]; | |
3864 i = (i + 1) % pattern.length; | |
3865 x += lx[i]; | |
3866 y += ly[i]; | |
3867 len += pattern[i]; | |
3868 i = (i + 1) % pattern.length; | |
3869 } | |
3870 if (len < tlen) { | |
3871 ctx.moveTo(x, y); | |
3872 x += mulx * (tlen - len); | |
3873 y += muly * (tlen - len); | |
3874 ctx.lineTo(x, y); | |
3875 } | |
3876 ctx.stroke(); | |
3877 } | |
3878 | |
3879 function draw_trim_background(ctx, metrics, left_start, left_end, left_divider, right_start, right_end, right_divider) { | |
3880 "use strict"; | |
3881 var left_size = left_end - left_start; | |
3882 var right_size = right_end - right_start; | |
3883 var line_x; | |
3884 | |
3885 ctx.save();//s8 | |
3886 ctx.fillStyle = "rgb(240, 240, 240)"; | |
3887 if (left_size > 0) { | |
3888 ctx.fillRect(left_start * metrics.stack_width, 0, left_size * metrics.stack_width, metrics.stack_height); | |
3889 } | |
3890 if (right_size > 0) { | |
3891 ctx.fillRect(right_start * metrics.stack_width, 0, right_size * metrics.stack_width, metrics.stack_height); | |
3892 } | |
3893 ctx.fillStyle = "rgb(51, 51, 51)"; | |
3894 if (left_size > 0 && left_divider) { | |
3895 line_x = (left_end * metrics.stack_width) - 0.5; | |
3896 draw_dashed_line(ctx, [3], 0, line_x, 0, line_x, metrics.stack_height); | |
3897 } | |
3898 if (right_size > 0 && right_divider) { | |
3899 line_x = (right_start * metrics.stack_width) + 0.5; | |
3900 draw_dashed_line(ctx, [3], 0, line_x, 0, line_x, metrics.stack_height); | |
3901 } | |
3902 ctx.restore();//s8 | |
3903 } | |
3904 | |
3905 function size_logo_on_canvas(logo, canvas, show_names, scale) { | |
3906 "use strict"; | |
3907 var draw_name, draw_finetext, metrics; | |
3908 draw_name = (typeof show_names === "boolean" ? show_names : (logo.get_rows() > 1)); | |
3909 draw_finetext = (logo.fine_text.length > 0); | |
3910 if (canvas.width !== 0 && canvas.height !== 0) { | |
3911 return; | |
3912 } | |
3913 metrics = new LogoMetrics(canvas.getContext('2d'), | |
3914 logo.get_xlate_columns(), logo.get_rows(), draw_name, draw_finetext, logo.x_axis, logo.y_axis); | |
3915 if (typeof scale == "number") { | |
3916 //resize the canvas to fit the scaled logo | |
3917 canvas.width = metrics.summed_width * scale; | |
3918 canvas.height = metrics.summed_height * scale; | |
3919 } else { | |
3920 if (canvas.width === 0 && canvas.height === 0) { | |
3921 canvas.width = metrics.summed_width; | |
3922 canvas.height = metrics.summed_height; | |
3923 } else if (canvas.width === 0) { | |
3924 canvas.width = metrics.summed_width * (canvas.height / metrics.summed_height); | |
3925 } else if (canvas.height === 0) { | |
3926 canvas.height = metrics.summed_height * (canvas.width / metrics.summed_width); | |
3927 } | |
3928 } | |
3929 } | |
3930 | |
3931 function draw_logo_on_canvas(logo, canvas, show_names, scale) { | |
3932 "use strict"; | |
3933 var i, draw_name, draw_finetext, ctx, metrics, raster, pspm_i, pspm, | |
3934 offset, col_index, motif_position, ssc; | |
3935 ssc = false; | |
3936 draw_name = (typeof show_names === "boolean" ? show_names : (logo.get_rows() > 1)); | |
3937 draw_finetext = (logo.fine_text.length > 0); | |
3938 ctx = canvas.getContext('2d'); | |
3939 //assume that the user wants the canvas scaled equally so calculate what the best width for this image should be | |
3940 metrics = new LogoMetrics(ctx, logo.get_xlate_columns(), logo.get_rows(), draw_name, draw_finetext, logo.x_axis, logo.y_axis); | |
3941 if (typeof scale == "number") { | |
3942 //resize the canvas to fit the scaled logo | |
3943 canvas.width = metrics.summed_width * scale; | |
3944 canvas.height = metrics.summed_height * scale; | |
3945 } else { | |
3946 if (canvas.width === 0 && canvas.height === 0) { | |
3947 scale = 1; | |
3948 canvas.width = metrics.summed_width; | |
3949 canvas.height = metrics.summed_height; | |
3950 } else if (canvas.width === 0) { | |
3951 scale = canvas.height / metrics.summed_height; | |
3952 canvas.width = metrics.summed_width * scale; | |
3953 } else if (canvas.height === 0) { | |
3954 scale = canvas.width / metrics.summed_width; | |
3955 canvas.height = metrics.summed_height * scale; | |
3956 } else { | |
3957 scale = Math.min(canvas.width / metrics.summed_width, canvas.height / metrics.summed_height); | |
3958 } | |
3959 } | |
3960 // cache the raster based on the assumption that we will be drawing a lot | |
3961 // of logos the same size and alphabet | |
3962 if (typeof draw_logo_on_canvas.raster_cache === "undefined") { | |
3963 draw_logo_on_canvas.raster_cache = []; | |
3964 } | |
3965 for (i = 0; i < draw_logo_on_canvas.raster_cache.length; i++) { | |
3966 raster = draw_logo_on_canvas.raster_cache[i]; | |
3967 if (raster.get_alphabet().equals(logo.alphabet) && | |
3968 Math.abs(raster.get_scale() - scale) < 0.1) break; | |
3969 raster = null; | |
3970 } | |
3971 if (raster == null) { | |
3972 raster = new RasterizedAlphabet(logo.alphabet, scale, metrics.stack_font, metrics.stack_width); | |
3973 draw_logo_on_canvas.raster_cache.push(raster); | |
3974 } | |
3975 ctx = canvas.getContext('2d'); | |
3976 ctx.save();//s1 | |
3977 ctx.scale(scale, scale); | |
3978 ctx.save();//s2 | |
3979 ctx.save();//s7 | |
3980 //create margin | |
3981 ctx.translate(Math.round(metrics.pad_left), Math.round(metrics.pad_top)); | |
3982 for (pspm_i = 0; pspm_i < logo.get_rows(); ++pspm_i) { | |
3983 pspm = logo.get_pspm(pspm_i); | |
3984 offset = logo.get_offset(pspm_i); | |
3985 //optionally draw name if this isn't the last row or is the only row | |
3986 if (draw_name && (logo.get_rows() == 1 || pspm_i != (logo.get_rows()-1))) { | |
3987 ctx.save();//s4 | |
3988 ctx.translate(Math.round(metrics.summed_width/2), Math.round(metrics.name_height)); | |
3989 ctx.font = metrics.name_font; | |
3990 ctx.textAlign = "center"; | |
3991 ctx.fillText(pspm.name, 0, 0); | |
3992 ctx.restore();//s4 | |
3993 ctx.translate(0, Math.round(metrics.name_height + | |
3994 Math.min(0, metrics.name_spacer - metrics.y_num_height/2))); | |
3995 } | |
3996 //draw scale | |
3997 if (logo.y_axis) draw_scale(ctx, metrics, logo.alphabet.get_ic(), raster); | |
3998 ctx.save();//s5 | |
3999 //translate across past the scale | |
4000 if (logo.y_axis) { | |
4001 ctx.translate(Math.round(metrics.y_label_height + metrics.y_label_spacer + | |
4002 metrics.y_num_width + metrics.y_tic_width), Math.round(metrics.y_num_height / 2)); | |
4003 } | |
4004 //draw the trimming background | |
4005 if (pspm.get_left_trim() > 0 || pspm.get_right_trim() > 0) { | |
4006 var left_start = offset * logo.get_xlate_nsyms(); | |
4007 var left_end = (offset + pspm.get_left_trim()) * logo.get_xlate_nsyms(); | |
4008 var left_divider = true; | |
4009 if (left_end < logo.get_xlate_start() || left_start > logo.get_xlate_end()) { | |
4010 // no overlap | |
4011 left_start = 0; | |
4012 left_end = 0; | |
4013 left_divider = false; | |
4014 } else { | |
4015 if (left_start < logo.get_xlate_start()) { | |
4016 left_start = logo.get_xlate_start(); | |
4017 } | |
4018 if (left_end > logo.get_xlate_end()) { | |
4019 left_end = logo.get_xlate_end(); | |
4020 left_divider = false; | |
4021 } | |
4022 left_start -= logo.get_xlate_start(); | |
4023 left_end -= logo.get_xlate_start(); | |
4024 if (left_end < left_start) { | |
4025 left_start = 0; | |
4026 left_end = 0; | |
4027 left_divider = false; | |
4028 } | |
4029 } | |
4030 var right_end = (offset + pspm.get_motif_length()) * logo.get_xlate_nsyms(); | |
4031 //var right_start = right_end - (pspm.get_left_trim() * logo.get_xlate_nsyms()); | |
4032 var right_start = right_end - (pspm.get_right_trim() * logo.get_xlate_nsyms()); | |
4033 var right_divider = true; | |
4034 if (right_end < logo.get_xlate_start() || right_start > logo.get_xlate_end()) { | |
4035 // no overlap | |
4036 right_start = 0; | |
4037 right_end = 0; | |
4038 right_divider = false; | |
4039 } else { | |
4040 if (right_start < logo.get_xlate_start()) { | |
4041 right_start = logo.get_xlate_start(); | |
4042 right_divider = false; | |
4043 } | |
4044 if (right_end > logo.get_xlate_end()) { | |
4045 right_end = logo.get_xlate_end(); | |
4046 } | |
4047 right_start -= logo.get_xlate_start(); | |
4048 right_end -= logo.get_xlate_start(); | |
4049 if (right_end < right_start) { | |
4050 right_start = 0; | |
4051 right_end = 0; | |
4052 right_divider = false; | |
4053 } | |
4054 } | |
4055 draw_trim_background(ctx, metrics, left_start, left_end, left_divider, right_start, right_end, right_divider); | |
4056 } | |
4057 //draw letters | |
4058 var xlate_col; | |
4059 for (xlate_col = logo.get_xlate_start(); xlate_col < logo.get_xlate_end(); xlate_col++) { | |
4060 ctx.translate(metrics.stack_pad_left,0); | |
4061 col_index = Math.floor(xlate_col / logo.get_xlate_nsyms()); | |
4062 if (xlate_col % logo.get_xlate_nsyms() == 0) { | |
4063 if (col_index >= offset && col_index < (offset + pspm.get_motif_length())) { | |
4064 motif_position = col_index - offset; | |
4065 draw_stack_num(ctx, metrics, motif_position, raster); | |
4066 draw_stack(ctx, metrics, pspm.get_stack(motif_position, logo.alphabet, ssc), raster); | |
4067 } | |
4068 } else { | |
4069 if (col_index >= offset && col_index < (offset + pspm.get_motif_length())) { | |
4070 ctx.save();// s5.1 | |
4071 ctx.translate(0, Math.round(metrics.stack_height)); | |
4072 // TODO draw a dot or dash or something to indicate continuity of the motif | |
4073 ctx.restore(); //s5.1 | |
4074 } | |
4075 } | |
4076 ctx.translate(Math.round(metrics.stack_width), 0); | |
4077 } | |
4078 ctx.restore();//s5 | |
4079 ////optionally draw name if this is the last row but isn't the only row | |
4080 if (draw_name && (logo.get_rows() != 1 && pspm_i == (logo.get_rows()-1))) { | |
4081 //translate vertically past the stack and axis's | |
4082 ctx.translate(0, metrics.y_num_height/2 + metrics.stack_height + | |
4083 Math.max(metrics.y_num_height/2, metrics.x_num_above + metrics.x_num_width + metrics.name_spacer)); | |
4084 | |
4085 ctx.save();//s6 | |
4086 ctx.translate(metrics.summed_width/2, metrics.name_height); | |
4087 ctx.font = metrics.name_font; | |
4088 ctx.textAlign = "center"; | |
4089 ctx.fillText(pspm.name, 0, 0); | |
4090 ctx.restore();//s6 | |
4091 ctx.translate(0, metrics.name_height); | |
4092 } else { | |
4093 //translate vertically past the stack and axis's | |
4094 ctx.translate(0, metrics.y_num_height/2 + metrics.stack_height + | |
4095 Math.max(metrics.y_num_height/2, metrics.x_num_above + metrics.x_num_width)); | |
4096 } | |
4097 //if not the last row then add middle padding | |
4098 if (pspm_i != (logo.get_rows() -1)) { | |
4099 ctx.translate(0, metrics.pad_middle); | |
4100 } | |
4101 } | |
4102 ctx.restore();//s7 | |
4103 if (logo.fine_text.length > 0) { | |
4104 ctx.translate(metrics.summed_width - metrics.pad_right, metrics.summed_height - metrics.pad_bottom); | |
4105 ctx.font = metrics.fine_txt_font; | |
4106 ctx.textAlign = "right"; | |
4107 ctx.fillText(logo.fine_text, 0,0); | |
4108 } | |
4109 ctx.restore();//s2 | |
4110 ctx.restore();//s1 | |
4111 } | |
4112 | |
4113 function create_canvas(c_width, c_height, c_id, c_title, c_display) { | |
4114 "use strict"; | |
4115 var canvas = document.createElement("canvas"); | |
4116 //check for canvas support before attempting anything | |
4117 if (!canvas.getContext) { | |
4118 return null; | |
4119 } | |
4120 var ctx = canvas.getContext('2d'); | |
4121 //check for html5 text drawing support | |
4122 if (!supports_text(ctx)) { | |
4123 return null; | |
4124 } | |
4125 //size the canvas | |
4126 canvas.width = c_width; | |
4127 canvas.height = c_height; | |
4128 canvas.id = c_id; | |
4129 canvas.title = c_title; | |
4130 canvas.style.display = c_display; | |
4131 return canvas; | |
4132 } | |
4133 | |
4134 function logo_1(alphabet, fine_text, pspm) { | |
4135 "use strict"; | |
4136 var logo = new Logo(alphabet, fine_text); | |
4137 logo.add_pspm(pspm); | |
4138 return logo; | |
4139 } | |
4140 | |
4141 function logo_2(alphabet, fine_text, target, query, query_offset) { | |
4142 "use strict"; | |
4143 var logo = new Logo(alphabet, fine_text); | |
4144 if (query_offset < 0) { | |
4145 logo.add_pspm(target, -query_offset); | |
4146 logo.add_pspm(query); | |
4147 } else { | |
4148 logo.add_pspm(target); | |
4149 logo.add_pspm(query, query_offset); | |
4150 } | |
4151 return logo; | |
4152 } | |
4153 | |
4154 /* | |
4155 * Specifies an alternate source for an image. | |
4156 * If the image with the image_id specified has | |
4157 * not loaded then a generated logo will be used | |
4158 * to replace it. | |
4159 * | |
4160 * Note that the image must either have dimensions | |
4161 * or a scale must be set. | |
4162 */ | |
4163 function alternate_logo(logo, image_id, scale) { | |
4164 "use strict"; | |
4165 var image = document.getElementById(image_id); | |
4166 if (!image) { | |
4167 alert("Can't find specified image id (" + image_id + ")"); | |
4168 return; | |
4169 } | |
4170 //if the image has loaded then there is no reason to use the canvas | |
4171 if (image_ok(image)) { | |
4172 return; | |
4173 } | |
4174 //the image has failed to load so replace it with a canvas if we can. | |
4175 var canvas = create_canvas(image.width, image.height, image_id, image.title, image.style.display); | |
4176 if (canvas === null) { | |
4177 return; | |
4178 } | |
4179 //draw the logo on the canvas | |
4180 draw_logo_on_canvas(logo, canvas, null, scale); | |
4181 //replace the image with the canvas | |
4182 image.parentNode.replaceChild(canvas, image); | |
4183 } | |
4184 | |
4185 /* | |
4186 * Specifes that the element with the specified id | |
4187 * should be replaced with a generated logo. | |
4188 */ | |
4189 function replace_logo(logo, replace_id, scale, title_txt, display_style) { | |
4190 "use strict"; | |
4191 var element = document.getElementById(replace_id); | |
4192 if (!replace_id) { | |
4193 alert("Can't find specified id (" + replace_id + ")"); | |
4194 return; | |
4195 } | |
4196 //found the element! | |
4197 var canvas = create_canvas(50, 120, replace_id, title_txt, display_style); | |
4198 if (canvas === null) { | |
4199 return; | |
4200 } | |
4201 //draw the logo on the canvas | |
4202 draw_logo_on_canvas(logo, canvas, null, scale); | |
4203 //replace the element with the canvas | |
4204 element.parentNode.replaceChild(canvas, element); | |
4205 } | |
4206 | |
4207 /* | |
4208 * Fast string trimming implementation found at | |
4209 * http://blog.stevenlevithan.com/archives/faster-trim-javascript | |
4210 * | |
4211 * Note that regex is good at removing leading space but | |
4212 * bad at removing trailing space as it has to first go through | |
4213 * the whole string. | |
4214 */ | |
4215 function trim (str) { | |
4216 "use strict"; | |
4217 var ws, i; | |
4218 str = str.replace(/^\s\s*/, ''); | |
4219 ws = /\s/; i = str.length; | |
4220 while (ws.test(str.charAt(--i))); | |
4221 return str.slice(0, i + 1); | |
4222 } | |
4223 </script> | |
4224 <script> | |
4225 var current_motif = 0; | |
4226 var dreme_alphabet = new Alphabet(data.alphabet, data.control_db.freqs); | |
4227 | |
4228 /* | |
4229 * Create a pspm for the given motif data | |
4230 */ | |
4231 function motif_pspm(m) { | |
4232 return new Pspm(m.pwm, m.id, 0, 0, m.nsites, m.evalue); | |
4233 } | |
4234 | |
4235 /* | |
4236 * Create a count matrix from the given motif data | |
4237 */ | |
4238 function motif_count_matrix(motif) { | |
4239 return motif_pspm(motif).as_count_matrix(); | |
4240 } | |
4241 | |
4242 /* | |
4243 * Create a probablity matrix from the given motif data | |
4244 */ | |
4245 function motif_prob_matrix(motif) { | |
4246 return motif_pspm(motif).as_probability_matrix(); | |
4247 } | |
4248 | |
4249 /* | |
4250 * Create a minimal meme format motif from the given motif data | |
4251 */ | |
4252 function motif_minimal_meme(motif) { | |
4253 return motif_pspm(motif).as_meme({ | |
4254 "with_header": true, | |
4255 "with_pspm": true, | |
4256 "with_pssm": false, | |
4257 "version": data["version"], | |
4258 "alphabet": dreme_alphabet, | |
4259 "strands": (data.options.revcomp ? 2 : 1) | |
4260 }); | |
4261 } | |
4262 | |
4263 /* | |
4264 * Fill in a template variable | |
4265 */ | |
4266 function set_tvar(template, tvar, value) { | |
4267 var node; | |
4268 node = find_child(template, tvar); | |
4269 if (node === null) { | |
4270 throw new Error("Template does not contain variable " + tvar); | |
4271 } | |
4272 node.innerHTML = ""; | |
4273 if (typeof value !== "object") { | |
4274 node.appendChild(document.createTextNode(value)); | |
4275 } else { | |
4276 node.appendChild(value); | |
4277 } | |
4278 } | |
4279 | |
4280 /* | |
4281 * Make a canvas with the motif logo drawn on it. | |
4282 */ | |
4283 function make_logo(motif, height, rc) { | |
4284 var pspm = new Pspm(motif["pwm"]); | |
4285 if (rc) pspm = pspm.copy().reverse_complement(dreme_alphabet); | |
4286 var logo = new Logo(dreme_alphabet); | |
4287 logo.add_pspm(pspm, 0); | |
4288 var canvas = document.createElement('canvas'); | |
4289 canvas.height = height; | |
4290 canvas.width = 0; | |
4291 draw_logo_on_canvas(logo, canvas, false); | |
4292 return canvas; | |
4293 } | |
4294 | |
4295 /* | |
4296 * Create a button designed to contain a single symbol | |
4297 */ | |
4298 function make_sym_btn(symbol, title, action) { | |
4299 var box, sbox; | |
4300 box = document.createElement("div"); | |
4301 box.tabIndex = 0; | |
4302 box.className = "sym_btn"; | |
4303 sbox = document.createElement("span"); | |
4304 if (typeof symbol == "string") { | |
4305 sbox.appendChild(document.createTextNode(symbol)); | |
4306 } else { | |
4307 sbox.appendChild(symbol); | |
4308 } | |
4309 box.appendChild(sbox); | |
4310 box.title = title; | |
4311 box.addEventListener('click', action, false); | |
4312 box.addEventListener('keydown', action, false); | |
4313 return box; | |
4314 } | |
4315 | |
4316 /* | |
4317 * Create a pair of text spans with different classes. | |
4318 * This is useful when using CSS to only display one of them. | |
4319 */ | |
4320 function text_pair(txt1, cls1, txt2, cls2) { | |
4321 var container, part1, part2; | |
4322 container = document.createElement("span"); | |
4323 part1 = document.createElement("span"); | |
4324 part1.appendChild(document.createTextNode(txt1)); | |
4325 part1.className = cls1; | |
4326 container.appendChild(part1); | |
4327 part2 = document.createElement("span"); | |
4328 part2.appendChild(document.createTextNode(txt2)); | |
4329 part2.className = cls2; | |
4330 container.appendChild(part2); | |
4331 return container; | |
4332 } | |
4333 | |
4334 /* | |
4335 * Make a colourised sequence. | |
4336 */ | |
4337 function make_seq(seq) { | |
4338 var i, j, letter, lbox, sbox; | |
4339 sbox = document.createElement("span"); | |
4340 for (i = 0; i < seq.length; i = j) { | |
4341 letter = seq.charAt(i); | |
4342 for (j = i+1; j < seq.length; j++) { | |
4343 if (seq.charAt(j) !== letter) { | |
4344 break; | |
4345 } | |
4346 } | |
4347 lbox = document.createElement("span"); | |
4348 lbox.style.color = dreme_alphabet.get_colour(dreme_alphabet.get_index(letter)); | |
4349 lbox.appendChild(document.createTextNode(seq.substring(i, j))); | |
4350 sbox.appendChild(lbox); | |
4351 } | |
4352 return sbox; | |
4353 } | |
4354 | |
4355 /* | |
4356 * Create a description element taking into account the newlines in the source text. | |
4357 */ | |
4358 function make_description(text) { | |
4359 var i, j, lines, p; | |
4360 var container = document.createElement("div"); | |
4361 var paragraphs = text.split(/\n\n+/); | |
4362 for (i = 0; i < paragraphs.length; i++) { | |
4363 lines = paragraphs[i].split(/\n/); | |
4364 p = document.createElement("p"); | |
4365 p.appendChild(document.createTextNode(lines[0])); | |
4366 for (j = 1; j < lines.length; j++) { | |
4367 p.appendChild(document.createElement("br")); | |
4368 p.appendChild(document.createTextNode(lines[j])); | |
4369 } | |
4370 container.appendChild(p); | |
4371 } | |
4372 return container; | |
4373 } | |
4374 | |
4375 /* | |
4376 * Make the table header for the discovered motifs. | |
4377 */ | |
4378 function make_motif_header() { | |
4379 var row = document.createElement("tr"); | |
4380 add_text_header_cell(row, "", "", "motif_ordinal"); | |
4381 add_text_header_cell(row, "Motif", "pop_motifs_word", "motif_word"); | |
4382 add_text_header_cell(row, "Logo", "pop_motifs_logo", "motif_logo"); | |
4383 if (data.options.revcomp) { | |
4384 add_text_header_cell(row, "RC Logo", "pop_motifs_rc_logo", "motif_logo"); | |
4385 } | |
4386 add_text_header_cell(row, "E-value", "pop_motifs_evalue", "motif_evalue"); | |
4387 add_text_header_cell(row, "Unerased E-value", "pop_motifs_uevalue", "motif_evalue"); | |
4388 add_text_header_cell(row, "More", "pop_more", "motif_more"); | |
4389 add_text_header_cell(row, "Submit/Download", "pop_submit_dl", "motif_submit"); | |
4390 row.className = "more"; | |
4391 return row; | |
4392 } | |
4393 | |
4394 /* | |
4395 * Make a compact motif summary row for the discovered motifs. | |
4396 */ | |
4397 function make_motif_row(tbody, ordinal, motif) { | |
4398 var row = document.createElement("tr"); | |
4399 add_text_cell(row, "" + ordinal + ".", "motif_ordinal"); | |
4400 add_text_cell(row, motif["id"], "motif_word"); | |
4401 add_cell(row, make_logo(motif, 50, false), "motif_logo"); | |
4402 if (data.options.revcomp) { | |
4403 add_cell(row, make_logo(motif, 50, true), "motif_logo"); | |
4404 } | |
4405 add_text_cell(row, motif["evalue"], "motif_evalue"); | |
4406 add_text_cell(row, motif["unerased_evalue"], "motif_evalue"); | |
4407 add_cell(row, make_sym_btn(text_pair("\u21A7", "less", "\u21A5", "more"), "Show more information.", function(e) { toggle_class(tbody, "collapsed"); }, "\u21A5", ""), "motif_more"); | |
4408 add_cell(row, make_sym_btn("\u21E2", "Submit the motif to another MEME Suite program or download it.", function(e) { action_show_outpop(e, ordinal); }), "motif_submit"); | |
4409 return row; | |
4410 } | |
4411 | |
4412 /* | |
4413 * Make a sortable table of enriched matching rows. | |
4414 */ | |
4415 function make_motif_words(motif) { | |
4416 var row, i, match; | |
4417 var table = document.createElement("table"); | |
4418 var thead = document.createElement("thead"); | |
4419 row = document.createElement("tr"); | |
4420 add_text_header_cell(row, "Word", "pop_match_word", "match_word", function(e) {sort_table(this, compare_words);}); | |
4421 add_text_header_cell(row, "Positives", "pop_match_pos", "match_count", function(e) {sort_table(this, compare_counts);}); | |
4422 add_text_header_cell(row, "Negatives", "pop_match_neg", "match_count", function(e) {sort_table(this, compare_counts);}); | |
4423 add_text_header_cell(row, "P-value", "pop_match_pval", "match_evalue", function(e) {sort_table(this, compare_evalues);}); | |
4424 add_text_header_cell(row, "E-value", "pop_match_eval", "match_evalue", function(e) {sort_table(this, compare_evalues);}); | |
4425 thead.appendChild(row); | |
4426 table.appendChild(thead); | |
4427 var tbody = document.createElement("tbody"); | |
4428 for (i = 0; i < motif.matches.length; i++) { | |
4429 match = motif.matches[i]; | |
4430 row = document.createElement("tr"); | |
4431 add_cell(row, make_seq(match.seq), "match_word"); | |
4432 add_text_cell(row, match.p + " / " + data.sequence_db.count, "match_count"); | |
4433 add_text_cell(row, match.n + " / " + data.control_db.count, "match_count"); | |
4434 add_text_cell(row, match.pvalue, "match_evalue"); | |
4435 add_text_cell(row, match.evalue, "match_evalue"); | |
4436 tbody.appendChild(row); | |
4437 } | |
4438 table.appendChild(tbody); | |
4439 return table; | |
4440 } | |
4441 | |
4442 /* | |
4443 * Make an expanded view of a discovered motif. | |
4444 */ | |
4445 function make_motif_exp(tbody, ordinal, motif) { | |
4446 "use strict"; | |
4447 var box, pspm, logo_box; | |
4448 box = $("tmpl_motif_expanded").cloneNode(true); | |
4449 toggle_class(box, "template", false); | |
4450 box.id = ""; | |
4451 find_child(box, "tvar_logo").appendChild(make_logo(motif, 150, false)); | |
4452 if (data.options.revcomp) { | |
4453 find_child(box, "tvar_rclogo").appendChild(make_logo(motif, 150, true)); | |
4454 } | |
4455 set_tvar(box, "tvar_p", motif["p"]); | |
4456 set_tvar(box, "tvar_p_total", data.sequence_db.count); | |
4457 set_tvar(box, "tvar_n", motif["n"]); | |
4458 set_tvar(box, "tvar_n_total", data.control_db.count); | |
4459 set_tvar(box, "tvar_pvalue", motif["pvalue"]); | |
4460 set_tvar(box, "tvar_evalue", motif["evalue"]); | |
4461 set_tvar(box, "tvar_uevalue", motif["unerased_evalue"]); | |
4462 set_tvar(box, "tvar_words", make_motif_words(motif)); | |
4463 var cell = document.createElement("td"); | |
4464 cell.colSpan = 8; | |
4465 cell.appendChild(box); | |
4466 var row = document.createElement("tr"); | |
4467 row.className = "more"; | |
4468 row.appendChild(cell); | |
4469 return row; | |
4470 } | |
4471 | |
4472 /* | |
4473 * Convert a string containing a scientific number into the log of that number | |
4474 * without having an intermediate representation of the number. | |
4475 * This is intended to avoid underflow problems with the tiny evalues that | |
4476 * MEME and DREME can create. | |
4477 */ | |
4478 function sci2log(scinum) { | |
4479 "use strict"; | |
4480 var ev_re, match, sig, exp; | |
4481 ev_re = /^(.*)e(.*)$/; | |
4482 if (match = ev_re.exec(scinum)) { | |
4483 sig = parseFloat(match[1]); | |
4484 exp = parseInt(match[2]); | |
4485 return Math.log(sig) + (exp * Math.log(10)); | |
4486 } | |
4487 return 0; | |
4488 } | |
4489 | |
4490 /* | |
4491 * Create a table of discovered motifs. A fresh table body is used for each | |
4492 * motif to make hiding/showing rows with css easier. | |
4493 */ | |
4494 function make_motifs() { | |
4495 "use strict"; | |
4496 var i, row, tbody, motif, ordinal; | |
4497 // make the motifs table | |
4498 var container = $("motifs"); | |
4499 container.innerHTML = ""; // clear content | |
4500 var table = document.createElement("table"); | |
4501 // add a header that is always shown | |
4502 var thead = document.createElement("thead"); | |
4503 thead.appendChild(make_motif_header()); | |
4504 table.appendChild(thead); | |
4505 for (i = 0; i < data.motifs.length; i++) { | |
4506 ordinal = i + 1; | |
4507 motif = data.motifs[i]; | |
4508 tbody = document.createElement("tbody"); | |
4509 tbody.className = "collapsed"; | |
4510 tbody.appendChild(make_motif_row(tbody, ordinal, motif)); | |
4511 tbody.appendChild(make_motif_exp(tbody, ordinal, motif)); | |
4512 // create a following header for every row except the last one | |
4513 if ((i + 1) < data.motifs.length) tbody.appendChild(make_motif_header()); | |
4514 table.appendChild(tbody); | |
4515 } | |
4516 container.appendChild(table); | |
4517 } | |
4518 | |
4519 /* | |
4520 * Create a table showing all the alphabet symbols, their names and frequencies. | |
4521 */ | |
4522 function make_alpha_bg(alph, freqs) { | |
4523 function colour_symbol(index) { | |
4524 var span = document.createElement("span"); | |
4525 span.appendChild(document.createTextNode(alph.get_symbol(index))); | |
4526 span.style.color = alph.get_colour(index); | |
4527 span.className = "alpha_symbol"; | |
4528 return span; | |
4529 } | |
4530 var table, thead, tbody, row, th, span, i; | |
4531 // create table | |
4532 table = document.createElement("table"); | |
4533 table.className = "inputs"; | |
4534 // create header | |
4535 thead = document.createElement("thead"); | |
4536 table.appendChild(thead); | |
4537 row = thead.insertRow(thead.rows.length); | |
4538 if (alph.has_complement()) { | |
4539 add_text_header_cell(row, "Name", "pop_alph_name"); | |
4540 add_text_header_cell(row, "Bg.", "pop_alph_control"); | |
4541 add_text_header_cell(row, ""); | |
4542 add_text_header_cell(row, ""); | |
4543 add_text_header_cell(row, ""); | |
4544 add_text_header_cell(row, "Bg.", "pop_alph_control"); | |
4545 add_text_header_cell(row, "Name", "pop_alph_name"); | |
4546 } else { | |
4547 add_text_header_cell(row, ""); | |
4548 add_text_header_cell(row, "Name", "pop_alph_name"); | |
4549 add_text_header_cell(row, "Bg.", "pop_alph_control"); | |
4550 } | |
4551 // add alphabet entries | |
4552 tbody = document.createElement("tbody"); | |
4553 table.appendChild(tbody); | |
4554 if (alph.has_complement()) { | |
4555 for (i = 0; i < alph.get_size_core(); i++) { | |
4556 var c = alph.get_complement(i); | |
4557 if (i > c) continue; | |
4558 row = tbody.insertRow(tbody.rows.length); | |
4559 add_text_cell(row, alph.get_name(i)); | |
4560 add_text_cell(row, "" + freqs[i].toFixed(3)); | |
4561 add_cell(row, colour_symbol(i)); | |
4562 add_text_cell(row, "~"); | |
4563 add_cell(row, colour_symbol(c)); | |
4564 add_text_cell(row, "" + freqs[c].toFixed(3)); | |
4565 add_text_cell(row, alph.get_name(c)); | |
4566 } | |
4567 } else { | |
4568 for (i = 0; i < alph.get_size_core(); i++) { | |
4569 row = tbody.insertRow(tbody.rows.length); | |
4570 add_cell(row, colour_symbol(i)); | |
4571 add_text_cell(row, alph.get_name(i)); | |
4572 add_text_cell(row, "" + freqs[i].toFixed(3)); | |
4573 } | |
4574 } | |
4575 return table; | |
4576 } | |
4577 | |
4578 /* | |
4579 * Updates the format download text in the popup. | |
4580 * This is called when either the format or current motif changes. | |
4581 */ | |
4582 function update_outpop_format(index) { | |
4583 var motif = data.motifs[index]; | |
4584 var fn = [motif_count_matrix, motif_prob_matrix, motif_minimal_meme]; | |
4585 var suffix = ["_counts.txt", "_freqs.txt", ".meme"]; | |
4586 var format = parseInt($("text_format").value); | |
4587 var text = fn[format](motif); | |
4588 prepare_download(text, "text/plain", motif.id + suffix[format], $("outpop_text_dl")); | |
4589 $("outpop_text").value = text; | |
4590 } | |
4591 | |
4592 /* | |
4593 * Updates the motif logos and format download text in the popup. | |
4594 * This is called whenever the current motif changes. | |
4595 */ | |
4596 function update_outpop_motif(index) { | |
4597 "use strict"; | |
4598 var motifs, motif, pspm, logo, canvas, num; | |
4599 motifs = data["motifs"]; | |
4600 if (index < 0 || index >= motifs.length) {return;} | |
4601 current_motif = index; | |
4602 motif = motifs[index]; | |
4603 pspm = new Pspm(motif["pwm"]); | |
4604 logo = new Logo(dreme_alphabet, ""); | |
4605 logo.add_pspm(pspm, 0); | |
4606 canvas = $("outpop_logo"); | |
4607 canvas.width = canvas.width; // clear canvas | |
4608 draw_logo_on_canvas(logo, canvas, false); | |
4609 canvas = $("outpop_logo_rc"); | |
4610 canvas.width = canvas.width; // clear rc canvas | |
4611 if (data.options.revcomp) { | |
4612 pspm.reverse_complement(dreme_alphabet); | |
4613 logo = new Logo(dreme_alphabet, ""); | |
4614 logo.add_pspm(pspm, 0); | |
4615 draw_logo_on_canvas(logo, canvas, false); | |
4616 } | |
4617 num = $("outpop_num"); | |
4618 num.innerHTML = ""; | |
4619 num.appendChild(document.createTextNode("" + (index + 1))); | |
4620 update_outpop_format(index); | |
4621 } | |
4622 | |
4623 | |
4624 /* | |
4625 * Initialise and display the download popup. | 121 * Initialise and display the download popup. |
4626 */ | |
4627 function action_show_outpop(e, ordinal) { | |
4628 "use strict"; | |
4629 function init() { | |
4630 "use strict"; | |
4631 var close_btn, next_btn, prev_btn, cancel_btn, do_btn; | |
4632 var tab1, tab2, tab3; | |
4633 var pnl1, pnl2, pnl3; | |
4634 var format_list; | |
4635 var tbl_submit, inputs, i, default_prog; | |
4636 close_btn = $("outpop_close"); | |
4637 close_btn.addEventListener("click", action_hide_outpop, false); | |
4638 close_btn.addEventListener("keydown", action_hide_outpop, false); | |
4639 next_btn = $("outpop_next"); | |
4640 next_btn.addEventListener("click", action_outpop_next, false); | |
4641 next_btn.addEventListener("keydown", action_outpop_next, false); | |
4642 prev_btn = $("outpop_prev"); | |
4643 prev_btn.addEventListener("click", action_outpop_prev, false); | |
4644 prev_btn.addEventListener("keydown", action_outpop_prev, false); | |
4645 cancel_btn = $("outpop_cancel"); | |
4646 cancel_btn.addEventListener("click", action_hide_outpop, false); | |
4647 do_btn = $("outpop_do"); | |
4648 do_btn.addEventListener("click", action_outpop_submit, false); | |
4649 tab1 = $("outpop_tab_1"); | |
4650 tab1.tabIndex = 0; | |
4651 tab1.addEventListener("click", action_outpop_tab, false); | |
4652 tab1.addEventListener("keydown", action_outpop_tab, false); | |
4653 tab2 = $("outpop_tab_2"); | |
4654 tab2.tabIndex = 0; | |
4655 tab2.addEventListener("click", action_outpop_tab, false); | |
4656 tab2.addEventListener("keydown", action_outpop_tab, false); | |
4657 tab3 = $("outpop_tab_3"); | |
4658 tab3.tabIndex = 0; | |
4659 tab3.addEventListener("click", action_outpop_tab, false); | |
4660 tab3.addEventListener("keydown", action_outpop_tab, false); | |
4661 pnl1 = $("outpop_pnl_1"); | |
4662 pnl2 = $("outpop_pnl_2"); | |
4663 pnl3 = $("outpop_pnl_3"); | |
4664 toggle_class(tab1, "activeTab", true); | |
4665 toggle_class(tab2, "activeTab", false); | |
4666 toggle_class(tab3, "activeTab", false); | |
4667 pnl1.style.display = "block"; | |
4668 pnl2.style.display = "none"; | |
4669 pnl3.style.display = "none"; | |
4670 format_list = $("text_format"); | |
4671 format_list.addEventListener("change", action_outpop_format, false); | |
4672 // setup program selection | |
4673 tbl_submit = $("programs"); | |
4674 // when not dna, hide the inputs for programs that require dna motifs | |
4675 toggle_class(tbl_submit, "alphabet_dna", dreme_alphabet.has_complement());//TODO FIXME alphabet_dna is a bad name for a field when allowing custom alphabets | |
4676 // add a click listener for the radio buttons | |
4677 inputs = tbl_submit.querySelectorAll("input[type='radio']"); | |
4678 for (i = 0; i < inputs.length; i++) { | |
4679 inputs[i].addEventListener("click", action_outpop_program, false); | |
4680 } | |
4681 // ensure that a default program option is selected for DNA and Protein | |
4682 default_prog = document.getElementById(dreme_alphabet.has_complement() ? "submit_tomtom" : "submit_fimo"); | |
4683 default_prog.checked = true; | |
4684 action_outpop_program.call(default_prog); | |
4685 // disable reverse-complement when not DNA | |
4686 $("logo_rc_option").disabled = !dreme_alphabet.has_complement(); | |
4687 // set errorbars on when ssc is on | |
4688 $("logo_ssc").addEventListener("change", action_outpop_ssc, false); | |
4689 } | |
4690 // store the focused element | |
4691 action_hide_outpop.last_active = document.activeElement; | |
4692 if (!e) e = window.event; | |
4693 if (e.type === "keydown") { | |
4694 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
4695 return; | |
4696 } | |
4697 // stop a submit or something like that | |
4698 e.preventDefault(); | |
4699 } | |
4700 // hide the help popup | |
4701 help_popup(); | |
4702 // on first load initilize the popup | |
4703 if (!action_show_outpop.ready) { | |
4704 init(); | |
4705 action_show_outpop.ready = true; | |
4706 } | |
4707 update_outpop_motif(ordinal - 1); | |
4708 // display the download popup | |
4709 $("grey_out_page").style.display = "block"; | |
4710 $("download").style.display = "block"; | |
4711 $("outpop_close").focus(); | |
4712 } | |
4713 | |
4714 /* | |
4715 * Hide the download popup. | |
4716 */ | |
4717 function action_hide_outpop(e) { | |
4718 if (!e) e = window.event; | |
4719 if (e.type === "keydown") { | |
4720 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
4721 return; | |
4722 } | |
4723 // stop a submit or something like that | |
4724 e.preventDefault(); | |
4725 } | |
4726 $("download").style.display = "none"; | |
4727 $("grey_out_page").style.display = "none"; | |
4728 if (typeof action_hide_outpop.last_active !== "undefined") { | |
4729 action_hide_outpop.last_active.focus(); | |
4730 } | |
4731 } | |
4732 | |
4733 /* | |
4734 * Show the next motif in the download popup. | |
4735 */ | |
4736 function action_outpop_next(e) { | |
4737 if (!e) e = window.event; | |
4738 if (e.type === "keydown") { | |
4739 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
4740 return; | |
4741 } | |
4742 // stop a submit or something like that | |
4743 e.preventDefault(); | |
4744 } | |
4745 update_outpop_motif(current_motif + 1); | |
4746 } | |
4747 | |
4748 /* | |
4749 * Show the previous motif in the download popup. | |
4750 */ | |
4751 function action_outpop_prev(e) { | |
4752 if (!e) e = window.event; | |
4753 if (e.type === "keydown") { | |
4754 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
4755 return; | |
4756 } | |
4757 // stop a submit or something like that | |
4758 e.preventDefault(); | |
4759 } | |
4760 update_outpop_motif(current_motif - 1); | |
4761 } | |
4762 | |
4763 /* | |
4764 * Highlight the selected row in the program list. | |
4765 */ | |
4766 function action_outpop_program() { | |
4767 "use strict"; | |
4768 var table, tr, rows, i; | |
4769 tr = find_parent_tag(this, "TR"); | |
4770 table = find_parent_tag(tr, "TABLE"); | |
4771 rows = table.querySelectorAll("tr"); | |
4772 for (i = 0; i < rows.length; i++) { | |
4773 toggle_class(rows[i], "selected", rows[i] === tr); | |
4774 } | |
4775 } | |
4776 | |
4777 /* | |
4778 * Enable error bars when small sample correction is enabled. | |
4779 */ | |
4780 function action_outpop_ssc() { | |
4781 "use strict"; | |
4782 $("logo_err").value = $("logo_ssc").value; | |
4783 } | |
4784 | |
4785 /* | |
4786 * Submit the motif to the selected program. | |
4787 */ | |
4788 function action_outpop_submit(e) { | |
4789 "use strict"; | |
4790 var form, input, program, motifs; | |
4791 // find out which program is selected | |
4792 var radios, i; | |
4793 radios = document.getElementsByName("program"); | |
4794 program = "fimo"; // default to fimo, since it works with all alphabet types | |
4795 for (i = 0; i < radios.length; i++) { | |
4796 if (radios[i].checked) program = radios[i].value; | |
4797 } | |
4798 | |
4799 motifs = motif_minimal_meme(data.motifs[current_motif]); | |
4800 form = document.createElement("form"); | |
4801 form.setAttribute("method", "post"); | |
4802 form.setAttribute("action", site_url + "/tools/" + program); | |
4803 | |
4804 input = document.createElement("input"); | |
4805 input.setAttribute("type", "hidden"); | |
4806 input.setAttribute("name", "motifs_embed"); | |
4807 input.setAttribute("value", motifs); | |
4808 form.appendChild(input); | |
4809 | |
4810 document.body.appendChild(form); | |
4811 form.submit(); | |
4812 document.body.removeChild(form); | |
4813 } | |
4814 | |
4815 /* | |
4816 * Download the format text. | |
4817 * Wire the link containing the data URI text to a download button so it looks | |
4818 * the same as the server submit stuff. | |
4819 */ | |
4820 function action_outpop_download_motif(e) { | |
4821 $("outpop_text_dl").click(); | |
4822 } | |
4823 | |
4824 /* | |
4825 * Download the motif logo. | |
4826 * The EPS format can be calculated locally in Javascript | |
4827 */ | |
4828 function action_outpop_download_logo(e) { | |
4829 "use strict"; | |
4830 var pspm, logo, eps; | |
4831 var logo_rc, logo_ssc, logo_width, logo_height; | |
4832 var motif = data.motifs[current_motif]; | |
4833 if ($("logo_format").value == "0") { // EPS | |
4834 logo_rc = ($("logo_rc").value == "1"); | |
4835 logo_ssc = ($("logo_ssc").value == "1"); | |
4836 logo_width = parseFloat($("logo_width").value); | |
4837 if (isNaN(logo_width) || !isFinite(logo_width) || logo_width <= 0) logo_width = null; | |
4838 logo_height = parseFloat($("logo_height").value); | |
4839 if (isNaN(logo_height) || !isFinite(logo_height) || logo_height <= 0) logo_height = null; | |
4840 // create a PSPM from the motif | |
4841 pspm = motif_pspm(motif); | |
4842 if (logo_rc) pspm.reverse_complement(dreme_alphabet); | |
4843 logo = new Logo(dreme_alphabet); | |
4844 logo.add_pspm(pspm, 0); | |
4845 eps = logo.as_eps({"ssc": logo_ssc, "logo_width": logo_width, "logo_height": logo_height}); | |
4846 prepare_download(eps, "application/postscript", motif.id + ".eps"); | |
4847 } else { | |
4848 $("logo_motifs").value = motif_minimal_meme(motif); | |
4849 $("logo_form").submit(); | |
4850 } | |
4851 } | |
4852 | |
4853 /* | |
4854 * Change the selected tab in the download popup. | |
4855 */ | |
4856 function action_outpop_tab(e) { | |
4857 "use strict"; | |
4858 var tab1, tab2, tab3, pnl1, pnl2, pnl3, do_btn; | |
4859 if (!e) e = window.event; | |
4860 if (e.type === "keydown") { | |
4861 if (e.keyCode !== 13 && e.keyCode !== 32) { | |
4862 return; | |
4863 } | |
4864 // stop a submit or something like that | |
4865 e.preventDefault(); | |
4866 } | |
4867 tab1 = $("outpop_tab_1"); | |
4868 tab2 = $("outpop_tab_2"); | |
4869 tab3 = $("outpop_tab_3"); | |
4870 pnl1 = $("outpop_pnl_1"); | |
4871 pnl2 = $("outpop_pnl_2"); | |
4872 pnl3 = $("outpop_pnl_3"); | |
4873 do_btn = $("outpop_do"); | |
4874 | |
4875 toggle_class(tab1, "activeTab", (this === tab1)); | |
4876 toggle_class(tab2, "activeTab", (this === tab2)); | |
4877 toggle_class(tab3, "activeTab", (this === tab3)); | |
4878 pnl1.style.display = ((this === tab1) ? "block" : "none"); | |
4879 pnl2.style.display = ((this === tab2) ? "block" : "none"); | |
4880 pnl3.style.display = ((this === tab3) ? "block" : "none"); | |
4881 do_btn.value = ((this === tab1) ? "Submit" : "Download"); | |
4882 do_btn.removeEventListener("click", action_outpop_submit, false); | |
4883 do_btn.removeEventListener("click", action_outpop_download_logo, false); | |
4884 do_btn.removeEventListener("click", action_outpop_download_motif, false); | |
4885 if (this === tab1) { | |
4886 do_btn.addEventListener("click", action_outpop_submit, false); | |
4887 } else if (this === tab2) { | |
4888 do_btn.addEventListener("click", action_outpop_download_motif, false); | |
4889 } else { | |
4890 do_btn.addEventListener("click", action_outpop_download_logo, false); | |
4891 } | |
4892 } | |
4893 | |
4894 /* | |
4895 * Update the text in the download format popup. | |
4896 */ | |
4897 function action_outpop_format() { | |
4898 update_outpop_format(current_motif); | |
4899 } | |
4900 | |
4901 /* | |
4902 * Find all text nodes in the given container. | |
4903 */ | |
4904 function text_nodes(container) { | |
4905 var textNodes = []; | |
4906 var stack = [container]; | |
4907 // depth first search to maintain ordering when flattened | |
4908 while (stack.length > 0) { | |
4909 var node = stack.pop(); | |
4910 if (node.nodeType == Node.TEXT_NODE) { | |
4911 textNodes.push(node); | |
4912 } else { | |
4913 for (var i = node.childNodes.length-1; i >= 0; i--) { | |
4914 stack.push(node.childNodes[i]); | |
4915 } | |
4916 } | |
4917 } | |
4918 return textNodes; | |
4919 } | |
4920 | |
4921 /* | |
4922 * Get the text out of a specific text node. | |
4923 */ | |
4924 function node_text(node) { | |
4925 if (node === undefined) { | |
4926 return ''; | |
4927 } else if (node.textContent) { | |
4928 return node.textContent; | |
4929 } else if (node.innerText) { | |
4930 return node.innerText; | |
4931 } else { | |
4932 return ''; | |
4933 } | |
4934 } | |
4935 | |
4936 /* | |
4937 * Get the text contained within the element. | |
4938 */ | |
4939 function elem_text(elem, separator) { | |
4940 if (separator === undefined) separator = ''; | |
4941 return text_nodes(elem).map(node_text).join(separator); | |
4942 } | |
4943 | |
4944 /* | |
4945 * Sort all rows in the first table body based on the column of the given element and the comparison function. | |
4946 * The sort is not very fast and is intended for small tables only. | |
4947 */ | |
4948 function sort_table(colEle, compare_function) { | |
4949 //find the parent of colEle that is either a td or th | |
4950 var i, j; | |
4951 var cell = colEle; | |
4952 while (true) { | |
4953 if (cell == null) return; | |
4954 if (cell.nodeType == Node.ELEMENT_NODE && | |
4955 (cell.tagName.toLowerCase() == "td" || cell.tagName.toLowerCase() == "th")) { | |
4956 break; | |
4957 } | |
4958 cell = cell.parentNode; | |
4959 } | |
4960 //find the parent of cell that is a tr | |
4961 var row = cell; | |
4962 while (true) { | |
4963 if (row == null) return; | |
4964 if (row.nodeType == Node.ELEMENT_NODE && row.tagName.toLowerCase() == "tr") { | |
4965 break; | |
4966 } | |
4967 row = row.parentNode; | |
4968 } | |
4969 //find the parent of row that is a table | |
4970 var table = row; | |
4971 while (true) { | |
4972 if (table == null) return; | |
4973 if (table.nodeType == Node.ELEMENT_NODE && table.tagName.toLowerCase() == "table") { | |
4974 break; | |
4975 } | |
4976 table = table.parentNode; | |
4977 } | |
4978 var column_index = cell.cellIndex; | |
4979 // do a bubble sort, because the tables are so small it doesn't matter | |
4980 var change; | |
4981 var trs = table.tBodies[0].getElementsByTagName('tr'); | |
4982 var already_sorted = true; | |
4983 var reverse = false; | |
4984 while (true) { | |
4985 do { | |
4986 change = false; | |
4987 for (i = 0; i < trs.length -1; i++) { | |
4988 var v1 = elem_text(trs[i].cells[column_index]); | |
4989 var v2 = elem_text(trs[i+1].cells[column_index]); | |
4990 if (reverse) { | |
4991 var tmp = v1; | |
4992 v1 = v2; | |
4993 v2 = tmp; | |
4994 } | |
4995 if (compare_function(v1, v2) > 0) { | |
4996 exchange(trs[i], trs[i+1], table); | |
4997 change = true; | |
4998 already_sorted = false; | |
4999 trs = table.tBodies[0].getElementsByTagName('tr'); | |
5000 } | |
5001 } | |
5002 } while (change); | |
5003 if (reverse) break;// we've sorted twice so exit | |
5004 if (!already_sorted) break;// sort did something so exit | |
5005 // when it's sorted one way already then sort the opposite way | |
5006 reverse = true; | |
5007 } | |
5008 // put arrows on the headers | |
5009 var headers = table.tHead.getElementsByTagName('tr'); | |
5010 for (i = 0; i < headers.length; i++) { | |
5011 for (j = 0; j < headers[i].cells.length; j++) { | |
5012 var cell = headers[i].cells[j]; | |
5013 var arrows = cell.getElementsByClassName("sort_arrow"); | |
5014 var arrow; | |
5015 if (arrows.length == 0) { | |
5016 arrow = document.createElement("span"); | |
5017 arrow.className = "sort_arrow"; | |
5018 cell.insertBefore(arrow, cell.firstChild); | |
5019 } else { | |
5020 arrow = arrows[0]; | |
5021 } | |
5022 arrow.innerHTML = ""; | |
5023 if (j == column_index) { | |
5024 arrow.appendChild(document.createTextNode(reverse ? "\u25B2" : "\u25BC")); | |
5025 } | |
5026 } | |
5027 } | |
5028 } | |
5029 | |
5030 /* | |
5031 * Swap two rows in a table. | |
5032 */ | |
5033 function exchange(oRowI, oRowJ, oTable) { | |
5034 var i = oRowI.rowIndex; | |
5035 var j = oRowJ.rowIndex; | |
5036 if (i == j+1) { | |
5037 oTable.tBodies[0].insertBefore(oRowI, oRowJ); | |
5038 } if (j == i+1) { | |
5039 oTable.tBodies[0].insertBefore(oRowJ, oRowI); | |
5040 } else { | |
5041 var tmpNode = oTable.tBodies[0].replaceChild(oRowI, oRowJ); | |
5042 if(typeof(oRowI) != "undefined") { | |
5043 oTable.tBodies[0].insertBefore(tmpNode, oRowI); | |
5044 } else { | |
5045 oTable.appendChild(tmpNode); | |
5046 } | |
5047 } | |
5048 } | |
5049 | |
5050 /* | |
5051 * Compare two E-values which may be very small. | |
5052 */ | |
5053 function compare_evalues(v1, v2) { | |
5054 var e1 = sci2log(v1); | |
5055 var e2 = sci2log(v2); | |
5056 if (e1 < e2) return -1; | |
5057 else if (e1 > e2) return 1; | |
5058 return 0; | |
5059 } | |
5060 | |
5061 /* | |
5062 * Compare two counts. | |
5063 */ | |
5064 function compare_counts(v1, v2) { | |
5065 var re = /(\d+)\s*\/\s*\d+/; | |
5066 var m1 = re.exec(v1); | |
5067 var m2 = re.exec(v2); | |
5068 if (m1 == null && m2 == null) return 0; | |
5069 if (m1 == null) return -1; | |
5070 if (m2 == null) return 1; | |
5071 return parseInt(m2[1]) - parseInt(m1[1]); | |
5072 } | |
5073 | |
5074 /* | |
5075 * Compare two sequence words. | |
5076 */ | |
5077 function compare_words(v1, v2) { | |
5078 return v1.localeCompare(v2); | |
5079 } | |
5080 | |
5081 | |
5082 </script> | |
5083 <style> | |
5084 /* The following is the content of meme.css */ | |
5085 body { background-color:white; font-size: 12px; font-family: Verdana, Arial, Helvetica, sans-serif;} | |
5086 | |
5087 div.help { | |
5088 display: inline-block; | |
5089 margin: 0px; | |
5090 padding: 0px; | |
5091 width: 12px; | |
5092 height: 13px; | |
5093 cursor: pointer; | |
5094 background-image: url(data:image/gif;base64,R0lGODlhDAANAIABANR0AP///yH5BAEAAAEALAAAAAAMAA0AAAIdhI8Xy22MIFgv1DttrrJ7mlGNNo4c+aFg6SQuUAAAOw==); | |
5095 } | |
5096 | |
5097 div.help:hover { | |
5098 background-image: url(data:image/gif;base64,R0lGODlhDAANAKEAANR0AP///9R0ANR0ACH+EUNyZWF0ZWQgd2l0aCBHSU1QACH5BAEAAAIALAAAAAAMAA0AAAIdDGynCe3PgoxONntvwqz2/z2K2ImjR0KhmSIZUgAAOw==); | |
5099 } | |
5100 | |
5101 p.spaced { line-height: 1.8em;} | |
5102 | |
5103 span.citation { font-family: "Book Antiqua", "Palatino Linotype", serif; color: #004a4d;} | |
5104 | |
5105 p.pad { padding-left: 30px; padding-top: 5px; padding-bottom: 10px;} | |
5106 | |
5107 td.jump { font-size: 13px; color: #ffffff; background-color: #00666a; | |
5108 font-family: Georgia, "Times New Roman", Times, serif;} | |
5109 | |
5110 a.jump { margin: 15px 0 0; font-style: normal; font-variant: small-caps; | |
5111 font-weight: bolder; font-family: Georgia, "Times New Roman", Times, serif;} | |
5112 | |
5113 h2.mainh {font-size: 1.5em; font-style: normal; margin: 15px 0 0; | |
5114 font-variant: small-caps; font-family: Georgia, "Times New Roman", Times, serif;} | |
5115 | |
5116 h2.line {border-bottom: 1px solid #CCCCCC; font-size: 1.5em; font-style: normal; | |
5117 margin: 15px 0 0; padding-bottom: 3px; font-variant: small-caps; | |
5118 font-family: Georgia, "Times New Roman", Times, serif;} | |
5119 | |
5120 h4 {border-bottom: 1px solid #CCCCCC; font-size: 1.2em; font-style: normal; | |
5121 margin: 10px 0 0; padding-bottom: 3px; font-family: Georgia, "Times New Roman", Times, serif;} | |
5122 | |
5123 h5 {margin: 0px} | |
5124 | |
5125 a.help { font-size: 9px; font-style: normal; text-transform: uppercase; | |
5126 font-family: Georgia, "Times New Roman", Times, serif;} | |
5127 | |
5128 div.pad { padding-left: 30px; padding-top: 5px; padding-bottom: 10px;} | |
5129 | |
5130 div.pad1 { margin: 10px 5px;} | |
5131 | |
5132 div.pad2 { margin: 25px 5px 5px;} | |
5133 h2.pad2 { padding: 25px 5px 5px;} | |
5134 | |
5135 div.pad3 { padding: 5px 0px 10px 30px;} | |
5136 | |
5137 div.box { border: 2px solid #CCCCCC; padding:10px; overflow: hidden;} | |
5138 | |
5139 div.bar { border-left: 7px solid #00666a; padding:5px; margin-top:25px; } | |
5140 | |
5141 div.subsection {margin:25px 0px;} | |
5142 | |
5143 img {border:0px none;} | |
5144 | |
5145 th.majorth {text-align:left;} | |
5146 th.minorth {font-weight:normal; text-align:left; width:8em; padding: 3px 0px;} | |
5147 th.actionth {font-weight:normal; text-align:left;} | |
5148 | |
5149 .explain h5 {font-size:1em; margin-left: 1em;} | |
5150 | |
5151 div.doc {margin-left: 2em; margin-bottom: 3em;} | |
5152 | |
5153 th.trainingset { | |
5154 border-bottom: thin dashed black; | |
5155 font-weight:normal; | |
5156 padding:0px 10px; | |
5157 } | |
5158 div.pop_content { | |
5159 position:absolute; | |
5160 z-index:50; | |
5161 width:300px; | |
5162 padding: 5px; | |
5163 background: #E4ECEC; | |
5164 font-size: 12px; | |
5165 font-family: Arial; | |
5166 border-style: double; | |
5167 border-width: 3px; | |
5168 border-color: #AA2244; | |
5169 display:none; | |
5170 } | |
5171 | |
5172 div.pop_content > *:first-child { | |
5173 margin-top: 0px; | |
5174 } | |
5175 | |
5176 div.pop_content h1, div.pop_content h2, div.pop_content h3, div.pop_content h4, | |
5177 div.pop_content h5, div.pop_content h6, div.pop_content p { | |
5178 margin: 0px; | |
5179 } | |
5180 | |
5181 div.pop_content p + h1, div.pop_content p + h2, div.pop_content p + h3, | |
5182 div.pop_content p + h4, div.pop_content p + h5, div.pop_content p + h6 { | |
5183 margin-top: 5px; | |
5184 } | |
5185 | |
5186 div.pop_content p + p { | |
5187 margin-top: 5px; | |
5188 } | |
5189 | |
5190 div.pop_content > *:last-child { | |
5191 margin-bottom: 0px; | |
5192 } | |
5193 | |
5194 div.pop_content div.pop_close { | |
5195 /* old definition */ | |
5196 float:right; | |
5197 bottom: 0; | |
5198 } | |
5199 | |
5200 div.pop_content span.pop_close, div.pop_content span.pop_back { | |
5201 display: inline-block; | |
5202 border: 2px outset #661429; | |
5203 background-color: #CCC; | |
5204 padding-left: 1px; | |
5205 padding-right: 1px; | |
5206 padding-top: 0px; | |
5207 padding-bottom: 0px; | |
5208 cursor: pointer; | |
5209 color: #AA2244; /*#661429;*/ | |
5210 font-weight: bold; | |
5211 } | |
5212 | |
5213 div.pop_content span.pop_close:active, div.pop_content span.pop_back:active { | |
5214 border-style: inset; | |
5215 } | |
5216 | |
5217 div.pop_content span.pop_close { | |
5218 float:right; | |
5219 /*border: 2px outset #AA002B;*/ | |
5220 /*color: #AA2244;*/ | |
5221 } | |
5222 | |
5223 div.pop_content:not(.nested) .nested_only { | |
5224 display: none; | |
5225 } | |
5226 | |
5227 div.pop_back_sec { | |
5228 margin-bottom: 5px; | |
5229 } | |
5230 | |
5231 div.pop_close_sec { | |
5232 margin-top: 5px; | |
5233 } | |
5234 | |
5235 table.hide_advanced tr.advanced { | |
5236 display: none; | |
5237 } | |
5238 span.show_more { | |
5239 display: none; | |
5240 } | |
5241 table.hide_advanced span.show_more { | |
5242 display: inline; | |
5243 } | |
5244 table.hide_advanced span.show_less { | |
5245 display: none; | |
5246 } | |
5247 | |
5248 | |
5249 /***************************************************************************** | |
5250 * Program logo styling | |
5251 ****************************************************************************/ | |
5252 div.prog_logo { | |
5253 border-bottom: 0.25em solid #0f5f60; | |
5254 height: 4.5em; | |
5255 width: 24em; | |
5256 display:inline-block; | |
5257 } | |
5258 div.prog_logo img { | |
5259 float:left; | |
5260 width: 4em; | |
5261 border-style: none; | |
5262 margin-right: 0.2em; | |
5263 } | |
5264 div.prog_logo h1, div.prog_logo h1:hover, div.prog_logo h1:active, div.prog_logo h1:visited { | |
5265 margin:0; | |
5266 padding:0; | |
5267 font-family: Arial, Helvetica, sans-serif; | |
5268 font-size: 3.2em; | |
5269 line-height: 1em; | |
5270 vertical-align: top; | |
5271 display: block; | |
5272 color: #026666; | |
5273 letter-spacing: -0.06em; | |
5274 text-shadow: 0.04em 0.06em 0.05em #666; | |
5275 } | |
5276 div.prog_logo h2, div.prog_logo h2:hover, div.prog_logo h2:active, div.prog_logo h2:visited { | |
5277 display: block; | |
5278 margin:0; | |
5279 padding:0; | |
5280 font-family: Helvetica, sans-serif; | |
5281 font-size: 0.9em; | |
5282 line-height: 1em; | |
5283 letter-spacing: -0.06em; | |
5284 color: black; | |
5285 } | |
5286 | |
5287 div.big.prog_logo { | |
5288 font-size: 18px; | |
5289 } | |
5290 | |
5291 </style> | |
5292 <style> | |
5293 /* dreme output specific css */ | |
5294 div.header { | |
5295 position: relative; | |
5296 overflow: hidden; | |
5297 margin-top: 15px; | |
5298 margin-bottom: 5px; | |
5299 margin-right: 3px; | |
5300 margin-left: 3px; | |
5301 } | |
5302 div.header > h2 { | |
5303 font-size: 1.5em; | |
5304 font-style: normal; | |
5305 margin: 0; | |
5306 font-variant: small-caps; | |
5307 font-family: Georgia, "Times New Roman", Times, serif; | |
5308 } | |
5309 div.header > span { | |
5310 position: absolute; | |
5311 right: 0; | |
5312 bottom: 0; | |
5313 } | |
5314 | |
5315 div.template { | |
5316 position: absolute; | |
5317 z-index: 1; | |
5318 left: 0; | |
5319 top: 0; | |
5320 visibility: hidden; | |
5321 } | |
5322 | |
5323 div.sym_btn { | |
5324 display:inline-block; | |
5325 text-decoration: underline; | |
5326 cursor: pointer; | |
5327 font-size: 20px; | |
5328 line-height:20px; | |
5329 text-align: center; | |
5330 width: 20px; | |
5331 height: 20px; | |
5332 color: blue; | |
5333 } | |
5334 div.sym_btn:hover { | |
5335 color: white; | |
5336 background-color: blue; | |
5337 } | |
5338 | |
5339 div.sym_btn.positioned { | |
5340 position: absolute; | |
5341 top: 0px; | |
5342 } | |
5343 | |
5344 div.box + div.box { | |
5345 margin-top: 5px; | |
5346 } | |
5347 | |
5348 th.motif_ordinal { | |
5349 | |
5350 } | |
5351 td.motif_ordinal { | |
5352 text-align: right; | |
5353 padding-right: 10px; | |
5354 font-weight: bold; | |
5355 font-size: large; | |
5356 } | |
5357 th.motif_word { | |
5358 padding-right: 10px; | |
5359 } | |
5360 td.motif_word { | |
5361 font-size:15px; | |
5362 font-family: 'Courier New', Courier, monospace; | |
5363 padding-right: 10px; | |
5364 } | |
5365 th.motif_logo { | |
5366 padding-right: 10px; | |
5367 } | |
5368 td.motif_logo { | |
5369 padding-right: 10px; | |
5370 } | |
5371 th.motif_evalue { | |
5372 text-align:right; | |
5373 padding-right: 10px; | |
5374 } | |
5375 td.motif_evalue { | |
5376 text-align: right; | |
5377 white-space: nowrap; | |
5378 padding-right: 20px; | |
5379 } | |
5380 th.motif_more { | |
5381 padding: 0 5px; | |
5382 } | |
5383 td.motif_more { | |
5384 text-align: center; | |
5385 padding: 0 5px; | |
5386 } | |
5387 th.motif_submit { | |
5388 padding: 0 5px; | |
5389 } | |
5390 td.motif_submit { | |
5391 text-align: center; | |
5392 padding: 0 5px; | |
5393 } | |
5394 th.match_word { | |
5395 padding-right: 10px; | |
5396 } | |
5397 td.match_word { | |
5398 padding-right: 10px; | |
5399 font-weight: bold; | |
5400 font-size: large; | |
5401 font-family: 'Courier New', Courier, monospace; | |
5402 } | |
5403 th.match_evalue, th.match_count { | |
5404 text-align: right; | |
5405 padding-right: 10px; | |
5406 } | |
5407 td.match_evalue, td.match_count { | |
5408 text-align: right; | |
5409 white-space: nowrap; | |
5410 padding-right: 20px; | |
5411 } | |
5412 | |
5413 div.tabArea { | |
5414 font-size: 80%; | |
5415 font-weight: bold; | |
5416 } | |
5417 | |
5418 .norc div.tabArea { | |
5419 display: none; | |
5420 } | |
5421 | |
5422 span.tab, span.tab:visited { | |
5423 cursor: pointer; | |
5424 color: #888; | |
5425 background-color: #ddd; | |
5426 border: 2px solid #ccc; | |
5427 padding: 2px 1em; | |
5428 text-decoration: none; | |
5429 } | |
5430 span.tab.middle { | |
5431 border-left-width: 0px; | |
5432 } | |
5433 div.tabArea.base span.tab { | |
5434 border-top-width: 0px; | |
5435 } | |
5436 div.tabArea.top span.tab { | |
5437 border-bottom-width: 0px; | |
5438 } | |
5439 | |
5440 span.tab:hover { | |
5441 background-color: #bbb; | |
5442 border-color: #bbb; | |
5443 color: #666; | |
5444 } | |
5445 span.tab.activeTab, span.tab.activeTab:hover, span.tab.activeTab:visited { | |
5446 background-color: white; | |
5447 color: black; | |
5448 cursor: default; | |
5449 } | |
5450 div.tabMain { | |
5451 border: 2px solid #ccc; | |
5452 background-color: white; | |
5453 padding: 10px; | |
5454 } | |
5455 div.tabMain.base { | |
5456 margin-top: 5px; | |
5457 display: inline-block; | |
5458 max-width: 98%; | |
5459 } | |
5460 | |
5461 div.tabMain.top { | |
5462 margin-bottom: 5px; | |
5463 } | |
5464 | |
5465 div.grey_background { | |
5466 position:fixed; | |
5467 z-index: 8; | |
5468 background-color: #000; | |
5469 -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; | |
5470 opacity: 0.5; | |
5471 left: 0; | |
5472 top: 0; | |
5473 width: 100%; | |
5474 height: 100%; | |
5475 } | |
5476 | |
5477 div.popup_wrapper { | |
5478 position:fixed; | |
5479 z-index:9; | |
5480 width:100%; | |
5481 height:0; | |
5482 top:50%; | |
5483 left:0; | |
5484 } | |
5485 | |
5486 div.popup { | |
5487 width: 600px; | |
5488 z-index:9; | |
5489 margin-left: auto; | |
5490 margin-right: auto; | |
5491 padding: 5px; | |
5492 background-color: #FFF; | |
5493 border-style: double; | |
5494 border-width: 5px; | |
5495 border-color: #00666a; | |
5496 position:relative; | |
5497 } | |
5498 div.close { | |
5499 cursor: pointer; | |
5500 border: 1px solid black; | |
5501 width:15px; | |
5502 height:15px; | |
5503 line-height:15px; /* this causes vertical centering */ | |
5504 text-align:center; | |
5505 background-color:#FFF; | |
5506 color:#000; | |
5507 font-size:15px; | |
5508 font-family:monospace; | |
5509 } | |
5510 | |
5511 div.close:hover { | |
5512 color:#FFF; | |
5513 background-color:#000; | |
5514 } | |
5515 | |
5516 div.navnum { | |
5517 width:100%; | |
5518 height:20px; | |
5519 line-height:20px; | |
5520 text-align:center; | |
5521 font-size:medium; | |
5522 } | |
5523 | |
5524 div.navarrow { | |
5525 font-size: 30px; | |
5526 text-decoration:none; | |
5527 cursor: pointer; | |
5528 -moz-user-select: none; | |
5529 -webkit-user-select: none; | |
5530 -ms-user-select: none; | |
5531 } | |
5532 | |
5533 div.navarrow > span.inactive { | |
5534 display: inline; | |
5535 } | |
5536 div.navarrow > span.active { | |
5537 display: none; | |
5538 } | |
5539 | |
5540 div.navarrow:hover > span.active { | |
5541 display: inline; | |
5542 } | |
5543 div.navarrow:hover > span.inactive { | |
5544 display: none; | |
5545 } | |
5546 | |
5547 table.programs { | |
5548 width: 100%; | |
5549 } | |
5550 | |
5551 table.programs tr { | |
5552 background-color: #EFE; | |
5553 } | |
5554 | |
5555 table.programs tr.selected { | |
5556 background-color: #262; | |
5557 color: #FFF; | |
5558 } | |
5559 | |
5560 table.programs tr.dna_only { | |
5561 display: none; | |
5562 } | |
5563 | |
5564 table.programs.alphabet_dna tr.dna_only { | |
5565 display: table-row; | |
5566 } | |
5567 | |
5568 table.inputs { | |
5569 margin-top: 20px; | |
5570 border-collapse:collapse; | |
5571 } | |
5572 table.inputs * td, table.inputs * th { | |
5573 padding-left: 15px; | |
5574 padding-right: 15px; | |
5575 padding-top: 1px; | |
5576 padding-bottom: 1px; | |
5577 } | |
5578 | |
5579 /* program settings */ | |
5580 span.strand_none, span.strand_given, span.strand_both { | |
5581 display: none; | |
5582 } | |
5583 td.none span.strand_none, td.given span.strand_given, td.both span.strand_both { | |
5584 display: inline; | |
5585 } | |
5586 | |
5587 /* show the expanded motif only when the collapsed one is hidden */ | |
5588 tbody *.less, tbody.collapsed *.more { | |
5589 display: none; | |
5590 } | |
5591 | |
5592 tbody.collapsed *.less { | |
5593 display: inline; | |
5594 } | |
5595 | |
5596 </style> | 122 </style> |
5597 </head> | 123 </head> |
5598 <body data-scrollpad="true"> | 124 <body data-scrollpad="true"> |
5599 <!-- --> | 125 <!-- --> |
5600 <div id="grey_out_page" class="grey_background" style="display:none;"> | 126 <div id="grey_out_page" class="grey_background" style="display:none;"> |
5601 </div> | |
5602 | |
5603 <!-- Help popups --> | |
5604 <div class="pop_content" id="pop_"> | |
5605 <p>Help poup.</p> | |
5606 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5607 </div> | |
5608 | |
5609 <div class="pop_content" id="pop_motifs_word"> | |
5610 <p> | |
5611 The name of the motif uses the IUPAC codes for nucleotides which has | 127 The name of the motif uses the IUPAC codes for nucleotides which has |
5612 a different letter to represent each of the 15 possible combinations. | 128 a different letter to represent each of the 15 possible combinations. |
5613 </p> | |
5614 <p> | |
5615 The name is itself a representation of the motif though the position | 129 The name is itself a representation of the motif though the position |
5616 weight matrix is not directly equalivant as it is generated from the | 130 weight matrix is not directly equivalent as it is generated from the |
5617 sites found that matched the letters given in the name. | 131 sites found that matched the letters given in the name. |
5618 </p> | |
5619 <p> | |
5620 <a id="doc_alphabets_url" href="#"> | |
5621 Read more about the MEME suite's use of the IUPAC alphabets. | |
5622 </a> | |
5623 <script>$("doc_alphabets_url").href = site_url + "/doc/alphabets.html";</script> | |
5624 </p> | |
5625 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5626 </div> | |
5627 <div class="pop_content" id="pop_motifs_logo"> | |
5628 <p>The logo of the motif.</p> | 132 <p>The logo of the motif.</p> |
5629 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5630 </div> | |
5631 <div class="pop_content" id="pop_motifs_rc_logo"> | |
5632 <p>The logo of the reverse complement motif.</p> | 133 <p>The logo of the reverse complement motif.</p> |
5633 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5634 </div> | |
5635 <div class="pop_content" id="pop_motifs_evalue"> | |
5636 <p>The E-value is the enrichment p-value times the number of candidate | 134 <p>The E-value is the enrichment p-value times the number of candidate |
5637 motifs tested.</p> | 135 motifs tested.</p> |
5638 <p>The enrichment p-value is calculated using Fisher's Exact Test for | 136 <p>The enrichment p-value is calculated using Fisher's Exact Test for |
5639 enrichment of the motif in the positive sequences.</p> | 137 enrichment of the motif in the positive sequences.</p> |
5640 <p>Note that the counts used in Fisher's Exact Test are made after | 138 <p>Note that the counts used in Fisher's Exact Test are made after |
5641 erasing sites that match previously found motifs.</p> | 139 erasing sites that match previously found motifs.</p> |
5642 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5643 </div> | |
5644 <div class="pop_content" id="pop_motifs_uevalue"> | |
5645 <p>The E-value of the motif calculated without erasing the sites of | 140 <p>The E-value of the motif calculated without erasing the sites of |
5646 previously found motifs.</p> | 141 previously found motifs.</p> |
5647 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5648 </div> | |
5649 <div class="pop_content" id="pop_more"> | |
5650 <p>Show more information on the motif.</p> | 142 <p>Show more information on the motif.</p> |
5651 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5652 </div> | |
5653 <div class="pop_content" id="pop_submit_dl"> | |
5654 <p>Submit your motif to another MEME Suite program or download your motif.</p> | 143 <p>Submit your motif to another MEME Suite program or download your motif.</p> |
5655 <h5>Supported Programs</h5> | 144 <h5>Supported Programs</h5> |
5656 <dl> | |
5657 <dt>Tomtom</dt> | 145 <dt>Tomtom</dt> |
5658 <dd>Tomtom is a tool for searching for similar known motifs. | 146 <dd>Tomtom is a tool for searching for similar known motifs. |
5659 </dd> | |
5660 <dt>MAST</dt> | 147 <dt>MAST</dt> |
5661 <dd>MAST is a tool for searching biological sequence databases for | 148 <dd>MAST is a tool for searching biological sequence databases for |
5662 sequences that contain one or more of a group of known motifs. | 149 sequences that contain one or more of a group of known motifs. |
5663 </dd> | |
5664 <dt>FIMO</dt> | 150 <dt>FIMO</dt> |
5665 <dd>FIMO is a tool for searching biological sequence databases for | 151 <dd>FIMO is a tool for searching biological sequence databases for |
5666 sequences that contain one or more known motifs. | 152 sequences that contain one or more known motifs. |
5667 </dd> | 153 <dt>GOMo</dt> |
5668 <dt>GOMO</dt> | 154 <dd>GOMo is a tool for identifying possible roles (Gene Ontology |
5669 <dd>GOMO is a tool for identifying possible roles (Gene Ontology | |
5670 terms) for DNA binding motifs. | 155 terms) for DNA binding motifs. |
5671 </dd> | |
5672 <dt>SpaMo</dt> | 156 <dt>SpaMo</dt> |
5673 <dd>SpaMo is a tool for inferring possible transcription factor | 157 <dd>SpaMo is a tool for inferring possible transcription factor |
5674 complexes by finding motifs with enriched spacings. | 158 complexes by finding motifs with enriched spacings. |
5675 </dd> | |
5676 </dl> | |
5677 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5678 </div> | |
5679 <div class="pop_content" id="pop_motif_positives"> | |
5680 <p># positive sequences matching the motif / # positive sequences.</p> | |
5681 <p>Note these counts are made after erasing sites that match previously | |
5682 found motifs.</p> | |
5683 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5684 </div> | |
5685 <div class="pop_content" id="pop_motif_negatives"> | |
5686 <p># negative sequences matching the motif / # negative sequences.</p> | |
5687 <p>Note these counts are made after erasing sites that match previously | |
5688 found motifs.</p> | |
5689 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5690 </div> | |
5691 <div class="pop_content" id="pop_motif_pvalue"> | |
5692 <p>The p-value of Fisher's Exact Test for enrichment of the motif in | |
5693 the positive sequences.</p> | |
5694 <p>Note that the counts used in Fisher's Exact Test are made after | |
5695 erasing sites that match previously found motifs.</p> | |
5696 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5697 </div> | |
5698 <div class="pop_content" id="pop_motif_evalue"> | |
5699 <p>The E-value is the motif p-value times the number of candidate motifs | |
5700 tested.</p> | |
5701 <p>Note that the p-value was calculated with counts made after | |
5702 erasing sites that match previously found motifs.</p> | |
5703 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5704 </div> | |
5705 <div class="pop_content" id="pop_motif_uevalue"> | |
5706 <p>The E-value of the motif calculated without erasing the sites of | |
5707 previously found motifs.</p> | |
5708 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5709 </div> | |
5710 <div class="pop_content" id="pop_match_word"> | |
5711 <p>All words matching the motif whose uncorrected p-value is less than | |
5712 <span id="help_add_pv_thresh"></span>.</p> | |
5713 <script>$("help_add_pv_thresh").innerHTML = data.options.add_pv_thresh;</script> | |
5714 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5715 </div> | |
5716 <div class="pop_content" id="pop_match_pos"> | |
5717 <p># positive sequences with matches to the word / # positive sequences.</p> | |
5718 <p>Note these counts are made after erasing sites that match previously | |
5719 found motifs.</p> | |
5720 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5721 </div> | |
5722 <div class="pop_content" id="pop_match_neg"> | |
5723 <p># negative sequences with matches to the word / # negative sequences.</p> | |
5724 <p>Note these counts are made after erasing sites that match previously | |
5725 found motifs.</p> | |
5726 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5727 </div> | |
5728 <div class="pop_content" id="pop_match_pval"> | |
5729 <p>The p-value of Fisher's Exact Test for enrichment of the word in | |
5730 the positive sequences.</p> | |
5731 <p>Note that the counts used in Fisher's Exact Test are made after | |
5732 erasing sites that match previously found motifs.</p> | |
5733 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5734 </div> | |
5735 <div class="pop_content" id="pop_match_eval"> | |
5736 <p>The word p-value times the number of candidates tested.</p> | |
5737 <p>Note that the p-value was calculated with counts made after | |
5738 erasing sites that match previously found motifs.</p> | |
5739 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5740 </div> | |
5741 | |
5742 <div class="pop_content" id="pop_seq_source"> | |
5743 <p>The sequence file used by DREME to find the motifs.</p> | |
5744 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5745 </div> | |
5746 <div class="pop_content" id="pop_seq_alph"> | |
5747 <p>The alphabet of the sequences.</p> | |
5748 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5749 </div> | |
5750 <div class="pop_content" id="pop_seq_count"> | |
5751 <p>The count of the sequences.</p> | |
5752 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5753 </div> | |
5754 | |
5755 <div class="pop_content" id="pop_alph_name"> | |
5756 <p>The name of the alphabet symbol.</p> | |
5757 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5758 </div> | |
5759 | |
5760 <div class="pop_content" id="pop_alph_control"> | |
5761 <p>The frequency of the alphabet symbol in the control dataset.</p> | |
5762 <div class="pop_close">[<a href="javascript:help_popup()">close</a> ]</div> | |
5763 </div> | |
5764 | |
5765 <!-- templates --> | |
5766 | |
5767 <div class="template box expanded_motif" id="tmpl_motif_expanded"> | |
5768 <div> | |
5769 <span class="tvar_logo"></span> | |
5770 <span class="tvar_rclogo"></span> | |
5771 </div> | |
5772 <h4>Details</h4> | |
5773 <table class="details"> | |
5774 <thead> | |
5775 <tr> | |
5776 <th class="match_count">Positives <div class="help" data-topic="pop_motif_positives"></div></th> | |
5777 <th class="match_count">Negatives <div class="help" data-topic="pop_motif_negatives"></div></th> | |
5778 <th class="match_evalue">P-value <div class="help" data-topic="pop_motif_pvalue"></div></th> | |
5779 <th class="match_evalue">E-value <div class="help" data-topic="pop_motif_evalue"></div></th> | |
5780 <th class="match_evalue">Unerased E-value <div class="help" data-topic="pop_motif_uevalue"></div></th> | |
5781 </tr> | |
5782 </thead> | |
5783 <tbody> | |
5784 <tr> | |
5785 <td class="match_count"> | |
5786 <span class="tvar_p"></span> / <span class="tvar_p_total"></span> | |
5787 </td> | |
5788 <td class="match_count"> | |
5789 <span class="tvar_n"></span> / <span class="tvar_n_total"></span> | |
5790 </td> | |
5791 <td class="tvar_pvalue match_evalue"></td> | |
5792 <td class="tvar_evalue match_evalue"></td> | |
5793 <td class="tvar_uevalue match_evalue"></td> | |
5794 </tr> | |
5795 </tbody> | |
5796 </table> | |
5797 <h4>Enriched Matching Words</h4> | |
5798 <div class="tvar_words"></div> | |
5799 </div> | |
5800 | |
5801 | |
5802 <div class="popup_wrapper"> | |
5803 <div class="popup" style="display:none; top: -150px;" id="download"> | |
5804 <div> | |
5805 <div style="float:right; "> | |
5806 <div id="outpop_close" class="close" tabindex="0">x</div> | |
5807 </div> | |
5808 <h2 class="mainh" style="margin:0; padding:0;">Submit or Download</h2> | |
5809 <div style="clear:both"></div> | |
5810 </div> | |
5811 <div style="height:100px"> | |
5812 <div style="float:right; width: 30px;"> | |
5813 <div id="outpop_prev" class="navarrow" tabindex="0"> | |
5814 <span class="inactive">⇧</span><span class="active">⬆</span> | |
5815 </div> | |
5816 <div id="outpop_num" class="navnum"></div> | |
5817 <div id="outpop_next" class="navarrow" tabindex="0"> | |
5818 <span class="inactive">⇩</span><span class="active">⬇</span> | |
5819 </div> | |
5820 </div> | |
5821 <div id="logo_box" style="height: 100px; margin-right: 40px;"> | |
5822 <canvas id="outpop_logo" height="100" width="250"></canvas> | |
5823 <canvas id="outpop_logo_rc" height="100" width="250"></canvas> | |
5824 </div> | |
5825 </div> | |
5826 <div> | |
5827 <!-- tabs start --> | |
5828 <div class="tabArea top"> | |
5829 <span id="outpop_tab_1" class="tab">Submit Motif</span><span | |
5830 id="outpop_tab_2" class="tab middle">Download Motif</span><span | |
5831 id="outpop_tab_3" class="tab middle">Download Logo</span> | |
5832 </div> | |
5833 <div class="tabMain top"> | |
5834 <!-- Submit to another program --> | |
5835 <div id="outpop_pnl_1"> | |
5836 <h4 class="compact">Submit to program</h4> | |
5837 <table id="programs" class="programs"> | |
5838 <tr class="dna_only"> | |
5839 <td><input type="radio" name="program" value="tomtom" id="submit_tomtom"></td> | |
5840 <td><label for="submit_tomtom">Tomtom</label></td> | |
5841 <td><label for="submit_tomtom">Find similar motifs in | |
5842 published libraries or a library you supply.</label></td> | |
5843 </tr> | |
5844 <tr> | |
5845 <td><input type="radio" name="program" value="fimo" id="submit_fimo"></td> | |
5846 <td><label for="submit_fimo">FIMO</label></td> | |
5847 <td><label for="submit_fimo">Find motif occurrences in | |
5848 sequence data.</label></td> | |
5849 </tr> | |
5850 <tr> | |
5851 <td><input type="radio" name="program" value="mast" id="submit_mast"></td> | |
5852 <td><label for="submit_mast">MAST</label></td> | |
5853 <td><label for="submit_mast">Rank sequences by affinity to | |
5854 groups of motifs.</label></td> | |
5855 </tr> | |
5856 <tr class="dna_only"> | |
5857 <td><input type="radio" name="program" value="gomo" id="submit_gomo"></td> | |
5858 <td><label for="submit_gomo">GOMo</label></td> | |
5859 <td><label for="submit_gomo">Identify possible roles (Gene | |
5860 Ontology terms) for motifs.</label></td> | |
5861 </tr> | |
5862 <tr class="dna_only"> | |
5863 <td><input type="radio" name="program" value="spamo" id="submit_spamo"></td> | |
5864 <td><label for="submit_spamo">SpaMo</label></td> | |
5865 <td><label for="submit_spamo">Find other motifs that are | |
5866 enriched at specific close spacings which might imply the existance of a complex.</label></td> | |
5867 </tr> | |
5868 </table> | |
5869 </div> | |
5870 <!-- download text format --> | |
5871 <div id="outpop_pnl_2"> | |
5872 <div> | |
5873 <label for="text_format">Format:</label> | |
5874 <select id="text_format"> | |
5875 <option value="0">Count Matrix</option> | |
5876 <option value="1">Probability Matrix</option> | |
5877 <option value="2">Minimal MEME</option> | |
5878 </select> | |
5879 </div> | |
5880 <textarea id="outpop_text" name="content" | |
5881 style="width:99%; white-space: pre; word-wrap: normal; overflow-x: scroll;" | |
5882 rows="8" readonly="readonly" wrap="off"></textarea> | |
5883 <a id="outpop_text_dl" download="meme.txt" href=""></a> | |
5884 </div> | |
5885 <!-- download logo format --> | |
5886 <div id="outpop_pnl_3"> | |
5887 <form id="logo_form" method="post" action=""> | |
5888 <script>$("logo_form").action = site_url + "/utilities/generate_logo";</script> | |
5889 <input type="hidden" name="program" value="DREME"/> | |
5890 <input type="hidden" id="logo_motifs" name="motifs" value=""/> | |
5891 <table> | |
5892 <tr> | |
5893 <td><label for="logo_format">Format:</label></td> | |
5894 <td> | |
5895 <select id="logo_format" name="png"> | |
5896 <option value="1">PNG (for web)</option> | |
5897 <option value="0">EPS (for publication)</option> | |
5898 </select> | |
5899 </td> | |
5900 </tr> | |
5901 <tr> | |
5902 <td><label for="logo_rc">Orientation:</label></td> | |
5903 <td> | |
5904 <select id="logo_rc" name="rc1"> | |
5905 <option value="0">Normal</option> | |
5906 <option value="1" id="logo_rc_option">Reverse Complement</option> | |
5907 </select> | |
5908 </td> | |
5909 </tr> | |
5910 <tr> | |
5911 <td><label for="logo_ssc">Small Sample Correction:</label></td> | |
5912 <td> | |
5913 <input type="hidden" id="logo_err" name="errbars" value="0"/> | |
5914 <select id="logo_ssc" name="ssc"> | |
5915 <option value="0">Off</option> | |
5916 <option value="1">On</option> | |
5917 </select> | |
5918 </td> | |
5919 </tr> | |
5920 <tr> | |
5921 <td><label for="logo_width">Width:</label></td> | |
5922 <td> | |
5923 <input type="text" id="logo_width" size="4" placeholder="default" name="width"/> cm | |
5924 </td> | |
5925 </tr> | |
5926 <tr> | |
5927 <td><label for="logo_height">Height:</label></td> | |
5928 <td> | |
5929 <input type="text" id="logo_height" size="4" placeholder="default" name="height"/> cm | |
5930 </td> | |
5931 </tr> | |
5932 </table> | |
5933 </form> | |
5934 </div> | |
5935 <!-- Buttons --> | |
5936 <div> | |
5937 <div style="float:left;"> | |
5938 <input type="button" id="outpop_do" value="Submit" /> | |
5939 </div> | |
5940 <div style="float:right;"> | |
5941 <input id="outpop_cancel" type="button" value="Cancel" /> | |
5942 </div> | |
5943 <div style="clear:both;"></div> | |
5944 </div> | |
5945 </div> | |
5946 </div> | |
5947 </div> | |
5948 </div> | |
5949 | |
5950 | |
5951 | |
5952 <!-- Page starts here --> | 159 <!-- Page starts here --> |
5953 <div id="top" class="pad1"> | |
5954 <div class="prog_logo big"> | |
5955 <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAAAd0SU1FB9wLGwYBMoyhiwgAACAASURBVHja7Z13fFRV9sC/503JpIeaKFVREBQVEEFFQVcUuyDouuraFoVV17IrayPjDP4U3XXXhrpiXxuKKF1dV8WCiEgXRUAQlJIYAilMf+f3x3szmVSSkICyOR/eh8wr9717z7mn3XPOhRZogRbYLQiQJiJGy1D8D0LHjh1/XrZsmQK9gU3Ap0mE0QL7KTx411136Z///GdV1ZhpmvrYY4+pqsZUVceNG6dAz5Zh2j/hnpKSEtM0TVVVrfq/qmosFtMxY8Yo8CPQHejucDjyfm0dbZFp1WGaxmJ3ppSWSiQUIhQKIWJxehGhpKSE1LQ0uvfowQHZ2exYt65D8dq1qzUaXR2LxTYCP7UM4a8MVFWA9mY0+nLhmjV63LHH6vMvvKATJkzQfn366CtPPKEDjjtOV61apeeedZZqMKgaCKju2KFmYaFqJKJaWqqqqoACR/9a+u5sQT+IiD77zDPbKCnR00aOZPHSpcydO5fLf/972rdvTzgcZsEnnzDmqquYPmsW/PxzxbMOB4NPOokRZ5+tB3XrJsAUYKmqOkQkVuVVKUBGDRbGz/vSvPlfBHcVDpB7XP/+Gz9/5x1mzJ/P1i1buPbaa1m/fj1du3alW5curPvqqxobCoVCpKSkIO3a1Wc89bCuBy51OZwu1RgiBqGYGf1uw09/Bd5t4QDNT+wKfPXoxIl9jzm6gkt/OmsWn3/4oRIMyrfLlzPuzjvp27cvHTt2ZNGXX+K/7baaW8zJITM1lflz5wL8ZtWqVWm9evXaVcv7Q2cN6hM68tAuR2MIhirFpUEef+MdBFK1RQQ0v6g/MC9PLxk5Uq+/7joIBECSJmwwKKqKR4RTTjyR0885h2effZaNa9Ywa9YstKQkoQzGwSwuJhwIwK5ddO3a9b+9evWqjQPoSf16qUOQles2gYLL5WDaBwu5dtSw8L/eeKdFB2huaJWTo0NOOMF8wOs1qiG/QhfghtGj+dMNN4DblhJlZVBaWg35AIYI7NgBbjcbNmwwjz766I+WLl16OfCDfUs3YB3AAdke6d7tYNR2Jv6wuZA2rbIwxbFPxfD/jBn4t7vv5uUnnzRsTNelEEIoBKWl1qH1YM7hMAXffWcsWbBgMLAB+AgoAtYC5SJCp86dUTPG89M/ACASi1mDH4u2+AH2Bvzhpps+p3Xrmi+mp2Oa5h613y4nh1WLFuHxeHTq1KmDVbWVqjJjxozU4cOH88rsj1FXKteMOh3D4eQ/ny/hjnwfn32zyWUYxtAWK6D54YW1X355WbcePSr32elk1EUX8cYbb1gzvvG+BEzA2b49msQ1RARVZe7cuTzzjwl079qB6R8s4PhThjF58mQADjroIDZs2CAtBLAXFMEaTwaDe4R8ALKzEbcbVUVVE4hP/t/lctGnTx9+c8op3HvffQm9YubMmZx77rlic2Rzbw7I/5oreNzJgwapqqI7dqA7d1pHSUnyVG5Uw0MGD6agoCCB7IQ+kcQFIpEIkydPZszYsQmuoaqEw2H95JNPdOHChTHgervJT4DTWzhA08OjwGWT7r8/u3xXhckei8W47c47obwcMjMtsy9OEHUojdhI/M2oUXwwb16dIqKqJVFaWkJmZlal60OGDJk7b968M1SVsWPH8uSTTxYC7WmBvQIbV6xcGQK0aM0a3bRsmW5fu1b1559NLSzUakdBgXXYawC7A0CXLVtW7fzMmTOrnTNNU03T1OXLl5u1ia4WaA4lQbXVmDFjcoFcIHfJ0qUXqKqaBQXVCMC0kZ+MtNogEAjoli1bar0ef/bee++tiXDMFszsQ+jbt+/r1111leqOHZWJIBBQ7913azgc1qaAeABCVUKqQgBvAO8BL7RgZu9xhd6nnnpqNLpzp8559VXVkhL9ZOZM/X79+j1Bdm1sP2KaZix+rqioSBctWqSqejPw2KhRo+IPxjp06KC0rOg2p82AkE8O4zkbiAJ6ySWXVJLRjUW2qmqrVq30+eefr3RrFWYQf4+pqjpq1Khq7zYMY4/0g5agxppgPCnARcC1GPRFSUF5gAn0VNVz69vMkiVLWLNmDRdeeGGt93Tu3JmNGzfu1slkGAZbt24lNze30jWHw4Fpmo3GYwv7SIY7GYKD8UA/INteQFaKEL7iqBNPPPHYhjTXp08f+vTpU+O1rVu38sc//pE5c+ZgmiaGUbtLJu5HqMNdfYXT6VwdjUY/b+EAdXm9DMNlmmYrAA/I6SKe6ao/JM38v2Pw52oPrgV9Seu06RugTzT62ZogNzeXbdu2ISIYhpFmmmagxRNYC7Gbptlnw5Il29Z/9dW2H1ev3jpddQPwQ79+/f5k32NNsSpStdeSXonZ99JLL+0RAkWEG2+8samUU7Zt2xb3KKppmptbREAd43Vwly5fdDniCLS42DoRDILb3fnRiRPzLzzrrIdVlX9MeoKCw36GSMUIpaWlJVh0UVHRHiPt4YcfbhqKruxyFiDUIsfrhpWAfjprlpplZVZUb1UHTyBgRfnGD1Xt0KlTvR0++xIcDodecMEFCvxDVXv/79nrM2lnzuAanUWdnV8InzocDu104IGqO3aY+vPPWqOrt7BQdds2jcRijTLzmsg5VO93RCyCNa+88kotKCgosTnC6f8rIkD/PGcsQ044AYAZL9XOgkdfdlnxibEYuzZvjkpOjrN1q1Yc1Llz4p4Lzz+fcePH21qBidMwGDhwIA8++CAn2O1XZcWjR4/mmmuuoX///k2ruIjw9ttvc/755+9enjudAGIYBu3atctUVXr16vXOqlWrOovIpv169pszeX/yP/9Z+0xOPqLRymy+yvHp7Nl61tChetbQoTpt6tRaF2x+qSLh6quvTvw9adIkE3hrw4YNjv3WDNSZDEZ5/6Hl9zhvGjsWzKZbOykH0tu2Tdjie9H9XCmYpCHPjR49mqeffjqZi5iq+jQwVkTM/c4MVCUfcM564y6u+8ufmxRRV1xxxb6xWUUSR0Ofa2clqdgSzOTVV181otHoNTNnzjxjv9MBzBl0EeUUNeD9e2Dkfc/z53xPnUGeN4wezcFHHlmvwX1j1iwAJk6cuNf7tmLFit16CavCww8/jMPhSHZ+sWDBAi688EL97W9/O0tVPSIS2m9EgDmda8XgSRRFKvqjigooQh85h+Xx819BaxO2nQ/ODGAjrA5A9WyeTA4kg1y2UCnO75cOJ510Eh9//HGN10aOHKmLFi2a98MPP5y834gAEYaikIx8+7wAhpr8oabnpgMvA5/Av4C+1Y5bWMRoIJUXBg0apL8G5KsqH3/8cY0iUFWZOnWqnHfeeUNUtcPeJoBUwFPHkdoYbqQ+BBiitYVMWYs57iqnYskv0trEoGlzjXGMX7du3R3RaNMlcDz55JN1c7VGKrFVg1DjiE++duutt6phGAtV1dhbBHBW6+zM0iM6tSkccFTPwv6HH1o44KiehUd0ap34PahPr0Kga4MJ4CjOUWgjUgvxKIiDc8yZpMVPReEkrZ/uk2hz69atr7344ot2i3sOY8aMqRXJt99+e40yf9q0aU2iRHbs2FEGDhx4wPr1689sbiXwcDEck3My044ePWKo4dRwRjQUILPtgZRt34rDeTCxWJSs1rk8PmUOKSkeCYWCDSR5OiXQpLYggLCCS8TCv0CmmBWELtDNThPeKZBdzzdtuPrqqyU1NVXPPPNMsrKyqs26hkJtil12dnaN7HvEiBEN1kPatGnD9OnTOe+88yqdnzRpkoiIpzk5wMnZGWkrhxxz+HG3XHRqqsPplPKyMlxZ7dkVCODMaEtZeRlpOe3419S5dOt8ANFYrDHy/5i4wocgKH9Xg0MFvkiewVolqNKexu+p5TFIq+/7fve733Wcm5PzStzbJyK43e4mHbjbbrutmgyvmmdQX+jatSu1ia4tW7asbS4O0Mntcn5wxx9GaUlJiQQiiivFQKNhHAaEy8pwuj1kt8njqTfepXvXA+nToyvzvlzZCK2HYxGsf8oKPNxunE5U3+Kv6uBdBI8qHlEOB76sLiBQA04GfPV840/dIPDEV1/dd5lhXP0TLAmrzveIyKlnnXV3z549uSknp/Qf27dnDh06lGHDhu2RLG+M4ygOmzZt4swzz+SCCy6o8f7PPvtsS3MRwPI22Zn61LT3JRwoQw0noIg4UP0Wp8uNwzAQEbp3PZDB/Xqxs2xXwzs8B6dGcNvs3BST2+V0ogAynI/N6SwTYYCCC8FTnQEkwNEwqYNDwbgZCoAPROSBL6CLMXv2rctmz85eC5suX7Yss3fv5lmMi0QiqGo1ziMiZGdns2LFCjp37kynTp3w+/01EoqtDJY0FwEEI6FAzgF5OQw6vi/ZuZ2qsDTFYRj4n5pKSXmAwcc0cqBCHC0ODrFlf1TdLK6CqWUKA5IxvhwkDCfsoSY3AHhCLQKIZ+x4gNgy0KOgpP9RR5kh1QNVVZrafEx28lRF7M6dO+vFXbZt2zYzLiKbRQfof2QvBg/sh5HZlrJdAcp2BSgPBO0jRFnAUvaGDuhNWKVR+XhacQiwQkJsq9RRk9fFVgvjKCgHUTjY/j0TCNOApItnraacWAUgNgI9kr4n8Hu4HejxCVwhIufbkT9ah07RoD5PmTIFwzBqVCDrS2hjxozh0ksvzW1WP4DGPTGqlO/4GTEMMGMJRBticGiXA4hEYxAO1J17V7sCmJpkq70vw6shsrwWV6fadPGFQkSg+6dW9a7dQtuK7glgSAXxuA3IFbgnaQhmPvroo2kvvviiTJkyhfnz51ebsa+++mqD+nzRRRdVUxAbsu6hqvztb3/ToqKi9LfeessNuJqFAFJcTsx4UqXhsIY8o03i7x+3FfH1uk10ymvbKOTbLP4uxEaFWb3CVtw5ZP9f0yw37OttU6mkI9QK7SALONh+7geFTp9bhHBI8jsdkGkTQeDyyy+XRy6++MCJQ4aQ4jJwuxy4nQYOw2DqM4/w6KOPNsi/EJ/p4XCYYDCIiDBz5sz6IF9FhKysLFHVZ4YPH74DiDQLAfxnwTI8bhcA6QceCmrCrh1JyFGcDgebC7ZbMe+N0XohB0BNtkuUFdUNapYqrBTlWnawIPnRKm8z+sHO+rzTsPP3HfCdAcUKrRwWAcTZglj0WNmpNV+15OgLYMLYU/nsoYP4+w0D6NPzIFYu/YqbLDEhDZ3NW7duZf369agq55xzTr0IZ+TIkfTs2fMqEflns4qAXcEQD7000+pQeBehcMTqY0paJXZkGILDYbDw63UYTkcjGQGFMrJ6oUXjZEKEOEHOZbJcTsy+1wW018revtKvbGKqr3SzxYgA6tiNFxFg1nhy+x4coaA0g+8Lc1mxqSsOw8CMmYrAIYcc8pWIMGHCBD744IN4mjGzZ8+u9UM6d+5Mz5496yP7TWDh+PHj5c0337z5m2++eS75YnNYAUuBYcUlZRTtLKNtNjhTMyAWhkAJVCm57zAMPl60ktRUD4FG+Nu1jjUEYxQlVe51A22A4hDsTIEihdxwPXUAJ7QywWXP9gKgvQluhSyjMmEkOqmzOBqTawpLiKalf+WcumQkaZ7VRCMRwsEyycnKCq9bt+4e4G2v1wswbs2aNfcfcsghnH322XsU26CqFBQUGHl5eZMsCca/apSDTQxnAEsNw+Bfb7yDw+nAiIYs5ljDfguC4HQ5oQEeOX2LTsDRtgqwraHjIrDhBNgKfA+kOmFIPblNOhADchXuAGKm1auOSRbJarGqh6PT6YPJuTj4dvgDrPv7zJNp3+pHwsEQS77bqNlpLkrLylxVfBEP9OvXL7t169bFzzzzTKMJIO4YysvLU9216wWgNRDYGwQAdrFkj8dj6WmGUaupVx4MIohCDXK8ts45SMewuJdq40usaoUdbzRABDgU3hZrrwBxWs/GrdJLgamAQ+dyACnI1PlDNslZW5Z/tvxC98at82MSLg+nOwkhEvnHy+8QicYEeDP5JSUlJSXFxcVnXH311Yfcd999DepTfJFpxIgRiAgbFi+Oh8gJtWnCzQBvJ3ynBdvrObkaKPs18aDRAIRrfcVHLc9nWRYoOTbCU0LWgpJD4Zn+VoiBiIGDCA+tWt/5xElPnb/11uzhOfD6tNUb1sy654XZcx+c8v5MPPpM4Q0l5yb67k36tnyuwcsaYN2bb7652lbidzvjwUpIFZFvZ8yY8U9AFi1bJpgmpw4e7Hn8iSfa7y0C2BFXmMLRmGUFZOVZXKBKR9I8KbYzr/khDVphKYJxKGggAXSsgWhjapmBAfueoKTQ86ftbd4cNvZv342KPtj3b/4FC4C/AOfbxygC/BGYjJerKymY+dyNn6fwsR1g8eLFh4kFNdr+yev9IsLAgQMBnjFN8xawah8RCjH12Wfd2dnZf9pbBHCl9U1JXj61TfHUrEZX4tpTiMJRgEcrMPiuzbu7NcDqSDYHDCzF0p0w5WBtaSDd3fGKDwdc1/rO464bt+kZbmRrjY35yQMW4+VBNNH0kYnr3sS5/r17917bt29fzjzzzER0kmmaiAiHdu/OgL59WfzBB0QikUqv+L/HHoNWrcjOztY/33jjnap6dnNbAYmPq8S2DAcYTUdvWuGTq7PAnzmdozEpM4azNsmMe99G5nr791H1fGerJCL4RKCvEzpbcSd8Y99jOohFx7cfNf/JS9Yu5C4b+V5yUcZWoiA/d+NjCV4mIVxGPjvwVzJJ7yGfUvxMXLly5aH2uRduvfXW30+bNo1Vq1bN9Hg8Zxd8842069TJKoAdDlf65uVLl743b/bsUwcff7zx1xtvVBE5CpjV7ARAJbepQGlBHaK+gSqA4BZLoIeBGnOAzHcRCeJVAy9wElbd3jgU1TyhdwuH2Q+8HYMrndaagClgKBTbDao4gtnzTlr93YZ/sslm688AF+InM2l2tyKfAEIaPo7HyybCDALgrwzEzTx8pPR6rJesYlXyN1z+97///fKMjAyXx+OJANF2HTo4CNSaFX76I5Mn6+Djj+emcePkhWnTzli6ePH/NbcIALghMcBij7DhqHGkXbYTyOPxXFw/9sJVGveKhqkWSWK+TT8JsdJGfk1UmdKYDomle84Pw4VRCKj1uyPQmQo38I8pMe75eGqSVePnanxJyAfwUYyfVOAa+3enj679aMW9H99bFLwn+Ln6NWKapq66flWNxFlWVhZpqNiipIQ+3bufoKr/anYCcDqdyypZ+6oEgoGqwbuEI1Fuu/J8cjLTFOhUrw4ZpCS5X6uy/KEYzEfoJdVV/Y72gMQX0x0NHIc0gc3HQ2SQlXi5zrAcLO74esMxML8//LsBQ/U1+TyOFx3yxJCH7njjjpM9Lo+ISIZhGPVhjY7vvv66Tg722cKF4HKBKrfecIOKyEnNTgAulyvJTWmha9eW7xEUl9NRKcwpHImi2mCTrDboIVW2hEl65veSNCkUFoiFyH71fGc/S5dMNBKxiSgSg28bOVSfIUxC+fCREY+s59GKHIZ6Qu+1dRealq0FBectWLBAAXr26SM9DjnksCVLlvTdGzoAYhgEy3bgycgmvU0eGijhgHatq/mw60fsu9ccpGK5tyZiidp/L7bPXwakaP11ALcJHye1G8WS/+4YBBs1QD4U+Bo45U/+PzXK9e5yuSq6XD3aWIEZp40cKSUbNyrl5fLa5Mn06dMnrVk5gL0Vm+XtKy4EEVLSMkGEDZsLKsKi7aybmy85m2CwYWOoDSeUDvbfZQthmgF3S8NCvdVIcqfO5oznT2POfxww5Xj4jn0Dg6KRSJyd0uGgg2q8qbSs7IzNP/0ksViMjPR0Jk+e3LwcQERmqypqmrTueDCoJpb+K0W0CIgKu4JNU91E6yYE077+qgGt1FoXkPrQ0nJwhaBrhUqxsN/duLIBTz+YyL6DBWaSuR0Oh2vMZDZEPuzQu3eJqmYempe3CXikua2ADBGxAkOo0AUioSrmSkome6sWssI2+0vi9nz8w7IWWG7eWmGXpVekAz/Cyr5geCD2M5hL+IWAmiaFRUU1LiCZqiERWSYiAaDL3jADUVW6dcy1RlmsSOBoMgGoWg6iRkYFST3PKbDQ0tYHUF0/UIEsl4XcOtcRBCKTuMYNWg6aB/IK9Iv9UghARKo5gqrg46SsrKz2e8MVXAnHoJDexqbS5HAMgWBZk7J7Tfo/mfdErb46arLt6/MuAzmojPSVU7i4OwT7gsyDfr+oqlyqWrHbWS1QUlJSvldcwTWlMZmmSVp2a3YW7UiSylEUSE9NaeIPoFwAFdJFCRh7mAp/LLo5j9e+DZAxEPgDHFP6S0G8y+UCtxsxDFrl5OAwjB9i9UwwbRYCSE1NTVFV0jzuauxZq85XO7Pz+Rkf4XanEA7Xf1JJ7d66dUQZpk7mohyCtQCkdbUT3s1YCItag7YB9wg4KvhLmvnDkmoR2/L/jfo+21wi4OGYaeqNvzuLmGnaYeL2QDprZlE/7yhtsC9Aa+7RQkyOM0awNp4cqyYxo3osQPJvtxN+U/fbytdD/zN/aciPm9zxgwZyuuYiAAUkGAoTLN2BiAFlRVbwlMOdUFhcTme1Ldb2WBE6h4XG+RRW0gOcGAYcYfdXbeSLwncmPG/fuxsZNHj3PNXLoc2K6b9wYigSupwbGNug527nZrxMx8uBe4sAKsynnUVWSFgSbYgITofBh4u+xu1qvBTanQEvlf9ok6R6isKiGBzeiJjCuuD4ZhvIfN7DyTtRjT6vj+gk7iJGvrUEXQdBtsPL53qvrsbHefjYvNcJoE3nQzFj8awgC11tsjO57OzB/PDTNis82jSJRmON4TMOcXNCXaZbTQShMMMJJw5M8utT+e+KJmJEGvBF/ya/0rJz04Gf05hIevq49C4dHuwgOkEnTr94+pXG3UYxXsprQH4PrEDQITEz1mGvKoGVNP9YLCGI4/hwuxw899qHiQCRcCRK6a5Ag6phYWn4DkyOISnAIXFtFjlqkpcsEW1q+MgDI3tXlIOOV1o+A3im0uRQbuGeKtE8Xt6ASkEoG/BhpeH6MPEyGS95+GqJAtpTeIiNm9kssk3ye3ftfT3wb5SX8PIZJrOZwL3cxFCUTvh5FqB/Xv8ruZNcHDyBn6K9SgAJuW6HhyngcDj3SN5rZQO+ZtYRpZc4yNDqzy5OQj6KpS9QOd4PDCbhY2MVNjwNHyOqEMQBeNmAcjF+PsdysZ6FFR3cfJDKSyu2rTDwsxYvxUC7Nultvp7749z/e+ubt7hv6H3xYNO3T+t2WtclY5fcszeVwKpmScULDQc7Nm+o17NL4IAaiUoxRHfLHQ6KZ2mQTlShVy3WY827bFRFvsWGR9Rw3xZ8dMXP53h5Dx8BlEnNrvr7+R5hjv0NrVCuL7qtqMexHY+VNFcaqvoR+UTxcf7EoRPz97YVUAnh5TsqZ25Z6wG1Y3ARdPsSvozC+Fq8iy8ma/k1EolB3wSq04hRS9yfNOVChDKKfD7GT64d5CE1yOamLBjQm3y+sAniIfJR8vnD+Nnjh8pd0gc/zqlfTxXqSH5tdgIQh5NYknPHNGO07nCQlRHkMFizcUul+7+EEQrLgWMEMudXDuOOY62Iiiz002tBRq5URnQ8O2JHLZ/q+XRPRaKfnfg5iXyOAYIog2v4rqvx8h7eSvpG48BHCD8D8NolboQr8PM0afyH/yP7uSXP6cjDR6qIPLbPCKDs581ktM2rpIkbTjfZGWm4nE4Wrfo+ce30aPQ0gdelIk1suLPCfKvNDGxXTfGciaHKwBruj5kwt5bmfmoSVuAlHcGNn1uQJI+cl1S8BPDzND5Ow5fIB2gKOAkvgo8X8LIVH4KXdVf2uVLI54p94QhKhP6nt26PiGHrAla9gFgsyvUXDSMQDNOv18EJVt5F9US1F2xs5LqB3+7GE5ii06tk95aSJkJ2LSKiqkNnrcIcAy48sWZTsKGzshyYZn/gNXjJJp8XUVrjs4paNDn4OBl4CC9ulP72uW54KUCYXdejzWYFHNyhPSJimYGGEzSGQ6Ns27KJtq1y2BUKgcbo0eVAAqEwrbMyMIp3uqtYbS7g8M/BcVwVbV8FEUUx6KIx+gIfJC5mcCbQVuIpRy5q3WHTgMIoXNKPKra0lwwUL5L0XqsYXVHco4iSDfyIn8cT8t0K8boLL0OB2cBDwN0Im5uV1fqI70S1KencbncdbzYC2Fq0w1oRVAVPBlq+nSlzP+LIzq1p1+aYRKZQLFEfUCv7kSvs80tdcC8VSRyIEFOTXRikq5WVcYr5Jh8aF6Dm2xgoN2Hlo/5HlFOD82kLdsx9FehnvW5HFXOvLVao9yuAQRTFQRQoRVmAUGqXoC0CnHgZh9IJpTtesvDxNF6eQ/Hhr+Id9HIYVuyBAEvwsU/jCZptLSAQClcIanEQDEdZvbmYEjMlYRq63W6m/OdzAqEwRTvLksmgIMlec8bssiwJ2Mk6YGVivV8ZKS477l7JQ+hjhyGEENAwTir0it33WSgGvkH4FlhVMr4kHViG8D1CayAHP53x0wc/vfHRFZgBPAisw0suEKiG/HwUK5l0FfD1HiE/n3FNgahm4QDJAaEgEA3iSXFxxx9GgghqmoghqEKq221XXDLi4rnIztm7LE4ADhgO/DfR4qWY5vREHJGqcChwhU5nKvCICh5bh1ibrAQohBywsR7sNEZS8GeWL+sTVT1fRKYnIWAW8Cx+W977GW4j/jzgRdTOcfAyFzgN2IKvCU1AaRrcNRcHGNOtY57l7bM9gPEUIVEzyTsIwXAET4qb1tnpcc3RNGGjWCHcaiPy5K9qswbssvCi/E2Fb9XgAtFEINKcKq7gsEKjQpAqId9C+NkIHrxMJZ8rbcLZBrwOvIcQxcst+DgDHw58VTyNzQVeO9MI4E+cz1+rW0PNzgFEhB8LiiqigtQEhxuJhSElA8JWZdBIOMzFw04gEAqT17YV5s/FCEwOw/1uuFEgw9L36Bi1qm58DqBvISqsUOhvl4lVxMrSTSDf0hVMtf6PU44a0wAADOxJREFUn3I2qcD18YqtJ1js3Y/gs7dq86JNMuO9OFBS8e+GcL38AZiAz/ae3sIgfVD/DVwo98veNwMPaJNTMdMjAUhrXaurWFUZecoATNMkBl96rLy7Fyo8v2Q54KoEgQ1HxeRf8ZTqeLl4iXMEQTD5UiN8KyDFy+gOuAVGD6SGlbOmYcmLbUSk2cUeXE3UsgehG16OrOJFrIrVc/FxAF4EL7dKpqwTkUzJl6v3jR+g0lqAQLicJKldjWNErcgh0wFb+kFU4Lvk5VyF3E+pZEerUPtSrQg7RXAA6wrnkgrc1h9ea0YzrJ9tBu6yZ36sidotx8cylP/aJibkEyJ5BTSfyfiIb2t/JcpoRXfh5SGEW+pyPze7J9CMRSzEh8vt11X+lnhmcHFJGbtgmwkLbYTPxpbX9hMnpVvr2xb2f2IpymqtzUOovI6DHRRzZHgbT/SHvze7/PUlqZy+Jk528NMOr+3UEYL4WJbk2ppuE8Ig4EH8dMfaMfRW/Gys61uanQBKCjYj8WqhalZUDgXEcLD42/VEojEem/IuLpcz8aUGbLft7PipzCgk0seN64iqEJUaOI/d/Eo5n1K5nED/2v3/vzaovnI5nmKdoGWMYxTwHj5a4eVRfLyOb/fBLM1OAK7UNKLhoB2IZev0CfvOwdpNW4nFYjgEypTCATbCTYvdB5NYhgGc+kVyAoda8Xw1sIAdmKxkfwMf99j6Raatb/TAYCxw76Aeg/6CnzTyeRz4x742AxOQ2ToPrSNGPVEvwDA40TTzAeZDZwP+A/TQCnkPMESS6wka/KhKTLWyG1FMJhvDKWV/BCUdtXZCE6QNPl57/MvHp8/fNH+aPaAd8FV4TfeZK7hCyzdxuOrOVkn1uGmdlU7fguDOhTBArLp58Uze5AQfF3AmtoUg5bxDGisQjrb3CUSEsLIXAjL2FfjZBTyLl+s0omNV9XC5XT5iIifjJYqvYTjdK9vGWYtCUWKRcC1EAqaVMpYr8K5ABxvzPyu8lMQFDIE+iXZ/S7kq/1TTrk5v7Rs03TiXH9jfQXno5d++/FdLg+YW8nkJu0DnL44A4gpf8eYNSKisxpKx9se8FN/JS+FHE07TuPPHeqrs2zwrGijJ3HsT4d9qEkL5gihj+F8AofCSIy+ZLSJiHGCkImTga7jes3cIQBwI0LZL90T5uNQUN0n1+uJOI6ddSvI7gcEDYEnMEgfrDNh16ZVMuOyaymvqci7l4uCPAscS5UQZYVXr+kWCFw/5XNpErT2Bl3mQKA/bqHLre4cAnCnWHLZdww7D4PE33sPpMDBVCYYjyRtHLVc49RirkDMu+DkTplx/Mb5VnVmFn8+q0ddZlMl5LDdGNCiGf19o8UHgVbw82ARtTQBOAejVvtdCfAxvHAHk4yOfmXjrXTO/8X7BlIxEQfUtQ4sItg9y5KGdMU0zTsWLIzDk2KSghmPAPOxe5n9+KFvxVY///xUqcTEaUBl9N0QQA/j6uq+vNk1zTuMkSYU78SWEQpQZ+PlwT74rNTVVO7RvzUWnHR93BliewMxcKN2G4XDwwLzpDOvQhz7du7Jm4xZ6z/n46xNUj64WljWeS1Bi3NOMbtx9AfHFoyYA0zQLDcNot2ciwM+l+LgZYSNeFC+X4G1qEWFxggm5r+PY6CAtxY0gvDx7Hk+53RdUQ76X36HsqIz8+Zn7iTkn5DeByPJiNhb5NesAPtbZixkLgbHkN5VHTatSLa/O+RhPiguny8VmkUiVGTKSGC7uSQ5qXDQK3K79hgsIfcnn/AZwjYer/P4C355NUqfdkA+pISJWWY6fI8hnIjALP5820htklYhRZYO7gKtWnMorfMp5pwwkGI4kfAVJbP8iYDJBKuLJ+eIWYCZEOmCtE/z6wccK8rmMpP0V6pjpU/BxUdLvl/FZNY/2nAD8NdfUxcsg8ilE6IFyCV5W4KvfDluVIFwOqZmwcyuSYmA4LKI9uEP76hWtLLb/AlBi+Ym/NICnQB8FvRKMf+5nFv3h9UD+DnxJSnp+EwWb7NYM9PEpftqhBBC6AB3twMbdTPgqhWAMB5QUgOEg3eXh6TffQ0Qo2F6FlsZzMcrLdrybsOVQB+gLIK+CXAuOt2BA4X6Ffj9nkV9rsgp4ObkS8r1kIPRtqtcb9fzIAPA8ynm28vJinaJNhEAwnCAGQwwrLkuVVzM/AXFgqtIxtyLML3RuaBjCYwl1IepciCttEX89+Wt6z/kdyELo+2UT59b9UnSBTnWoTq/ZiBe8DEK5CB9L9i4BWNxgJX7uxctHwH/JR/FyYhJbOiX+ZyQa5dQB1qbQhsNBSeFPliQIlFVgT8HjdlG0s8yOCMZEWWR/1TpWn5rJc08fyf0fLGHFsC/gmOftJw8mn6f2G+RbBH2pHcxRVel7Fz/xfX4PQTkFfxPkFO6RJ9DHEPy8gHAqMA8vx9lX1sRr0LicTuZ9ZW1yoKaJM8WDqsmM7C+4qei8Srs0/LClEIfDIGVGynsIp+HmFe5eOJVpvkugJAbGRXDsU/ZglaL0wZ8U+frrVwQVH0uRiiLUSVCSxAmm4cff1K937sGH/9f2JBaTz834eZ58lgNHqipOO9RLVUnNbGU5AtOzeOfdJXhS3PHVPwxDCF4TRLbLQGLi5a4VCrumoOEfIethkDH2bFhUbdOFX+aMdgC/QdmF8AJWfkFOPcLCT8FLb3yVts9bYre5EpLkfjwFLZ9DgG74G7913p7HA/hpRT7r8PIwPrLx8qg8IDbRWmG7ajggFkVRHA4rUbRjbmvmZixma7diPE95UId+ESyb1xVCr8Gx38PCiRC8CU6M4OXf+DjmVzKjY8B7NpK6JbHzOVh7FK/Cz4QanvwUrVjqTngKrXE9osq9t5DPQIQVifI0+4wALCLoRj4R8jkaMEzsDaPsAD1JyYRdxYm9/qKeGMHLggzddjzlgRCPx94lFnr8jyAvQb/vYeEEcE7Ce2w7YAE+OjdgBrpQ2iEU1icmrllZe+UxOrMScoUsfElRSz6ieHkT7L4KP9Rg++cA3yRi/5tEBHiTskkra58K5FCRVJFVp+3px0U+EZQRsQGxQ9Z/UnCa3RErGFSE7Fg6ZZlBUtJcXLJtCAK0ykq3zcZYAI5dAgvvwBF7lbuOPQblM6TyTty78ZTFgHeAMfALthYsS6ojXlYB1wMz8KGChBQFL2vw0ZX8JP+Ml09QzsHfdMiPc4D7azFNFDgQiODjbnuAb0bwxNGKcritqMzBzyybCFbHesZSnfMrPLYhwrgEWpmZzB+0mq7rc3FJ5eVrd9qNzwWdb9/K1cMGk7F9O7ABfz02dsznLWA7wiZ8jVsT30dE8CPQCS9DUd4ARuoOvfH292/X+96574JEZpFVfHKUbYI3eXSz7IadrrQR3BqlE0IaVkxeCJ+dj5/PowijgFyUE4AhrlmuAZGzI+fesXkkgrI6awcHl+XwwAFvcvyMXmwpLOZ3ZwyC9Na4gsXc88xbRA+68TPOes4gs+hKrMIQB9jf9yjCw/iS4vy8ZAPHAq/gox2/ZogrdF6+xcdhtnjItJXHTsBIfM2X0+DcjRw7ogbNcztwpR2eHEM5AR834KUz8APCBZEhkb84J7pY/4dtFLp2MiDSn+ScTDNeNNL2GEZvimA8mPeymVW0EFiDErMVIAOftRN3Jflp+c4v+NUjP1lX8HEYXhaggM8eLC/rsfYoajYCkEZSbW+U9ggG8B5KCTADP5fhZYkUSh5l5LXrlMXBgVx+Ez0JgqXMT/+G0vcCDO53OKkeD2ZaNs+lvc62B0rR0bHbtJ1+AzjxMy1pZjyFtd9uJjAK+BTffpPoUZNIU1tHUGAw/hr9A/vYD1DZVhW8HIdyDfkoyunaVt+lLRRHykihE0TDoCZhiWCq0io7g03fLufLo8ro9+khvNd9GeHW0ZsQXsbHX2wl9HK7ysZMhEvw8R/2d7C8gr9JKIp7xQvd9J24C2UGWLlrKeoiKjFu2zycmGny3oIVnH1aX/xZr3PnllGoKve2mUrqI6ndAuMCG7Ayf0pQyvGTQQs0KzRHUOgw/CyPk9ag8p5cuv0UAhKh+KfvibYx+XDV19y5ZRTRcIiH8mbieMZJcHRwNMpbKCUoHVqQ/2slAB+D8CKe+z0XuF9wU2IGOCCYzbK0DSw8ooRdrYJsWlWEAu+3W4G37CLMo00Q0hA+x4/gb+aKWi3QrBwAfKiIFOKEHms74ED4MHsF3QMHkvdjNtt3lmkwFObbnVujTy59d6drvnOjpmm0Vp9EC/zKCMAGx2ZHzAyHYmXbC2JXLTgilrOmNHZc58xYaSDMP16cPsN4zXHk+sHbMsO7wl1wM67Jc+pboJmsgPrBAlXNKy8uIJN0XA7LMxiOmfGNnQpLS8tIeO/2cb28FmiBFmiBFmiBFmiBFmiBFmiBFmiB/wH4f+jryq6sRdUDAAAAAElFTkSuQmCC" alt="DREME Logo"/> | |
5956 <h1>DREME</h1> | 160 <h1>DREME</h1> |
5957 <h2>Discriminative Regular Expression Motif Elicitation</h2> | 161 <h2>Discriminative Regular Expression Motif Elicitation</h2> |
5958 </div> | 162 For further information on how to interpret these results please access |
5959 <p class="spaced"> | 163 To get a copy of the MEME software please access |
5960 For further information on how to interpret these results or to get a | |
5961 copy of the MEME software please access | |
5962 <a href="http://meme.nbcr.net/">http://meme.nbcr.net</a>. | |
5963 </p> | |
5964 <p> | |
5965 If you use DREME in your research please cite the following paper:<br /> | 164 If you use DREME in your research please cite the following paper:<br /> |
5966 <span class="citation"> | |
5967 Timothy L. Bailey, "DREME: Motif discovery in transcription factor ChIP-seq data", <i>Bioinformatics</i>, <b>27</b>(12):1653-1659, 2011. | 165 Timothy L. Bailey, "DREME: Motif discovery in transcription factor ChIP-seq data", <i>Bioinformatics</i>, <b>27</b>(12):1653-1659, 2011. |
5968 <a href="http://bioinformatics.oxfordjournals.org/content/27/12/1653">[full text]</a> | |
5969 </span> | |
5970 </p> | |
5971 </div> | |
5972 <!-- navigation --> | |
5973 <div class="pad2"> | |
5974 <a class="jump" href="#motifs_sec">Discovered Motifs</a> | |
5975 | | |
5976 <a class="jump" href="#inputs_sec">Inputs & Settings</a> | |
5977 | | |
5978 <a class="jump" href="#info_sec">Program information</a> | |
5979 </div> | |
5980 <!-- alert the user when their browser is not up to the task --> | |
5981 <noscript><h1 style="color:red">Javascript is required to view these results!</h1></noscript> | |
5982 <h1 id="html5_warning" style="color:red; display:none;">Your browser does not support canvas!</h1> | |
5983 <script> | |
5984 if (!window.HTMLCanvasElement) $("html5_warning").style.display = "block"; | |
5985 </script> | |
5986 <!-- description --> | |
5987 <div id="description_section" style="display:none"> | |
5988 <div id="description" class="header"> | |
5989 <h2>Description</h2> | |
5990 </div> | |
5991 <div id="description_text" class="box"> | |
5992 </div> | |
5993 </div> | |
5994 <script> | |
5995 if (data.description) { | |
5996 $("description_text").innerHTML = ""; | |
5997 $("description_text").appendChild(make_description(data.description)); | |
5998 $("description_section").style.display = "block"; | |
5999 } | |
6000 </script> | |
6001 <!-- motifs --> | |
6002 <div id="motifs_sec" class="header"> | |
6003 <h2>Discovered Motifs</h2> | |
6004 <span><a href="#inputs_sec">Next</a> <a href="#">Top</a></span> | |
6005 </div> | |
6006 <div id="motifs" class="box"> | |
6007 <p>No motifs were discovered!</p> | |
6008 </div> | |
6009 <script>make_motifs();</script> | |
6010 <!-- inputs and settings --> | |
6011 <div id="inputs_sec" class="header"> | |
6012 <h2>Inputs & Settings</h2> | |
6013 <span><a href="#motifs_sec">Previous</a> <a href="#info_sec">Next</a> <a href="#">Top</a></span> | |
6014 </div> | |
6015 <div class="box"> | |
6016 <h4>Sequences</h4> | |
6017 <table id="seq_info" class="inputs"> | |
6018 <tr><th>Source <div class="help" data-topic="pop_seq_source"></div></th> | |
6019 <th>Alphabet <div class="help" data-topic="pop_seq_alph"></div></th> | |
6020 <th>Sequence Count <div class="help" data-topic="pop_seq_count"></div></th> | |
6021 </tr> | |
6022 <tr> | |
6023 <td id="ins_seq_source"></td> | |
6024 <td id="ins_seq_alphabet"></td> | |
6025 <td id="ins_seq_count"></td> | |
6026 </tr> | |
6027 </table> | |
6028 <script> | |
6029 { | |
6030 var db = data.sequence_db; | |
6031 $("ins_seq_source").innerHTML = db.file; | |
6032 $("ins_seq_alphabet").innerHTML = dreme_alphabet.get_alphabet_name(); | |
6033 $("ins_seq_count").innerHTML = db.count; | |
6034 } | |
6035 </script> | |
6036 <h4>Control Sequences</h4> | |
6037 <table id="seq_info" class="inputs"> | |
6038 <tr><th>Source <div class="help" data-topic="pop_seq_source"></div></th> | |
6039 <th>Sequence Count <div class="help" data-topic="pop_seq_count"></div></th> | |
6040 </tr> | |
6041 <tr> | |
6042 <td id="ins_cseq_source"></td> | |
6043 <td id="ins_cseq_count"></td> | |
6044 </tr> | |
6045 </table> | |
6046 <script> | |
6047 { | |
6048 var db = data.control_db; | |
6049 if (db.from == "shuffled") { | |
6050 $("ins_cseq_source").innerHTML = "Shuffled Sequences"; | |
6051 } else { | |
6052 $("ins_cseq_source").innerHTML = db.file; | |
6053 } | |
6054 $("ins_cseq_count").innerHTML = db.count; | |
6055 } | |
6056 </script> | |
6057 <h4>Background</h4> | |
6058 <span id="alpha_bg"></span> | |
6059 <script> | |
6060 { | |
6061 $("alpha_bg").appendChild(make_alpha_bg(dreme_alphabet, data.control_db.freqs)); | |
6062 } | |
6063 </script> | |
6064 <h4>Other Settings</h4> | |
6065 <table id="tbl_settings" class="inputs hide_advanced"> | |
6066 <tr> | |
6067 <th>Strand Handling</th> | |
6068 <td id="opt_strand"> | |
6069 <span class="strand_none">This alphabet only has one strand</span> | |
6070 <span class="strand_given">Only the given strand is processed</span> | |
6071 <span class="strand_both">Both the given and reverse complement strands are processed</span> | |
6072 </td> | |
6073 </tr> | |
6074 <tr><th># REs to Generalize</th><td id="opt_ngen"></td></tr> | |
6075 <tr><th>Shuffle Seed</th><td id="opt_seed"></td></tr> | |
6076 <tr><th>E-value Threshold</th><td id="opt_stop_evalue"></td></tr> | |
6077 <tr><th>Max Motif Count</th><td id="opt_stop_count"></td></tr> | |
6078 <tr><th>Max Run Time</th><td id="opt_stop_time"></td></tr> | |
6079 </table> | |
6080 <script> | |
6081 { | |
6082 $("opt_strand").className = (dreme_alphabet.has_complement() ? (data.options.revcomp ? "both" : "given") : "none"); | |
6083 $("opt_ngen").innerHTML = data.options.ngen; | |
6084 $("opt_seed").innerHTML = data.options.seed; | |
6085 $("opt_stop_evalue").innerHTML = data.options.stop.evalue; | |
6086 $("opt_stop_count").innerHTML = (typeof data.options.stop.count == "number" ? data.options.stop.count : "No maximum motif count."); | |
6087 $("opt_stop_time").innerHTML = (typeof data.options.stop.time == "number" ? data.options.stop.time + " seconds." : "No maximum running time."); | |
6088 } | |
6089 </script> | |
6090 </div> | |
6091 <!-- list information on this program --> | |
6092 <div id="info_sec" class="bar" style="position:relative"> | |
6093 <div style="position: absolute; right: 0;"><a href="#inputs_sec">Previous</a> <a href="#">Top</a></div> | |
6094 <div class="subsection"> | |
6095 <h5 id="version">DREME version</h5> | |
6096 <span id="ins_version"></span> | |
6097 (Release date: <span id="ins_release"></span>)<br> | |
6098 </div> | |
6099 <script> | |
6100 $("ins_version").innerHTML = data["version"]; | |
6101 $("ins_release").innerHTML = data["release"]; | |
6102 </script> | |
6103 <div class="subsection"> | |
6104 <h5 id="reference">Reference</h5> | |
6105 <span class="citation"> | |
6106 Timothy L. Bailey, "DREME: Motif discovery in transcription factor ChIP-seq data", <i>Bioinformatics</i>, <b>27</b>(12):1653-1659, 2011. | |
6107 <a href="http://bioinformatics.oxfordjournals.org/content/27/12/1653">[full text]</a> | |
6108 </span> | |
6109 </div> | |
6110 <div class="subsection"> | |
6111 <h5 id="command">Command line</h5> | |
6112 <textarea id="cmd" rows="3" style="width:100%;" readonly="readonly"> | |
6113 </textarea> | |
6114 <script>$("cmd").value = data["cmd"].join(" ");</script> | |
6115 </div> | |
6116 </div> | |
6117 | |
6118 </body> | 166 </body> |
6119 </html> | 167 </html> |