sequenceserver 0.8.9 → 1.0.0.pre.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. checksums.yaml +4 -4
  2. data/{README.txt → README.md} +2 -0
  3. data/bin/sequenceserver +255 -55
  4. data/config.ru +2 -4
  5. data/lib/sequenceserver.rb +293 -447
  6. data/lib/sequenceserver/blast.rb +464 -64
  7. data/lib/sequenceserver/database.rb +185 -19
  8. data/lib/sequenceserver/links.rb +114 -0
  9. data/lib/sequenceserver/logger.rb +27 -0
  10. data/lib/sequenceserver/sequence.rb +141 -0
  11. data/public/css/bootstrap.min.css +8 -413
  12. data/public/css/custom.css +363 -122
  13. data/public/css/font-awesome.min.css +4 -0
  14. data/public/fonts/FontAwesome.otf +0 -0
  15. data/public/fonts/fontawesome-webfont.eot +0 -0
  16. data/public/fonts/fontawesome-webfont.svg +565 -0
  17. data/public/fonts/fontawesome-webfont.ttf +0 -0
  18. data/public/fonts/fontawesome-webfont.woff +0 -0
  19. data/public/fonts/fontawesome-webfont.woff2 +0 -0
  20. data/public/js/bootstrap.min.js +11 -0
  21. data/public/js/d3.v3.min.js +5 -0
  22. data/public/js/html5shiv.min.js +4 -0
  23. data/public/js/jquery.scrollspy.js +74 -0
  24. data/public/js/jquery.t.js +353 -0
  25. data/public/js/sequence.js +2419 -0
  26. data/public/js/sequenceserver.blast.js +29 -30
  27. data/public/js/sequenceserver.js +544 -120
  28. data/public/js/underscore.min.js +6 -0
  29. data/public/js/webshims/polyfiller.js +1 -0
  30. data/public/js/webshims/shims/FlashCanvas/canvas2png.js +1 -0
  31. data/public/js/webshims/shims/FlashCanvas/flashcanvas.js +1 -0
  32. data/public/js/webshims/shims/FlashCanvas/flashcanvas.swf +0 -0
  33. data/public/js/webshims/shims/FlashCanvasPro/canvas2png.js +1 -0
  34. data/public/js/webshims/shims/FlashCanvasPro/flash10canvas.swf +0 -0
  35. data/public/js/webshims/shims/FlashCanvasPro/flash9canvas.swf +0 -0
  36. data/public/js/webshims/shims/FlashCanvasPro/flashcanvas.js +1 -0
  37. data/public/js/webshims/shims/canvas-blob.js +1 -0
  38. data/public/js/webshims/shims/color-picker.js +2 -0
  39. data/public/js/webshims/shims/combos/1.js +6 -0
  40. data/public/js/webshims/shims/combos/10.js +2 -0
  41. data/public/js/webshims/shims/combos/11.js +2 -0
  42. data/public/js/webshims/shims/combos/12.js +6 -0
  43. data/public/js/webshims/shims/combos/13.js +1 -0
  44. data/public/js/webshims/shims/combos/14.js +1 -0
  45. data/public/js/webshims/shims/combos/15.js +2 -0
  46. data/public/js/webshims/shims/combos/16.js +7 -0
  47. data/public/js/webshims/shims/combos/17.js +2 -0
  48. data/public/js/webshims/shims/combos/18.js +3 -0
  49. data/public/js/webshims/shims/combos/2.js +7 -0
  50. data/public/js/webshims/shims/combos/21.js +2 -0
  51. data/public/js/webshims/shims/combos/22.js +1 -0
  52. data/public/js/webshims/shims/combos/23.js +6 -0
  53. data/public/js/webshims/shims/combos/25.js +2 -0
  54. data/public/js/webshims/shims/combos/27.js +1 -0
  55. data/public/js/webshims/shims/combos/28.js +1 -0
  56. data/public/js/webshims/shims/combos/29.js +1 -0
  57. data/public/js/webshims/shims/combos/3.js +1 -0
  58. data/public/js/webshims/shims/combos/30.js +2 -0
  59. data/public/js/webshims/shims/combos/31.js +1 -0
  60. data/public/js/webshims/shims/combos/33.js +1 -0
  61. data/public/js/webshims/shims/combos/34.js +1 -0
  62. data/public/js/webshims/shims/combos/4.js +1 -0
  63. data/public/js/webshims/shims/combos/5.js +2 -0
  64. data/public/js/webshims/shims/combos/6.js +2 -0
  65. data/public/js/webshims/shims/combos/7.js +7 -0
  66. data/public/js/webshims/shims/combos/8.js +7 -0
  67. data/public/js/webshims/shims/combos/9.js +2 -0
  68. data/public/js/webshims/shims/combos/97.js +1 -0
  69. data/public/js/webshims/shims/combos/98.js +1 -0
  70. data/public/js/webshims/shims/combos/99.js +1 -0
  71. data/public/js/webshims/shims/details.js +1 -0
  72. data/public/js/webshims/shims/dom-extend.js +1 -0
  73. data/public/js/webshims/shims/es5.js +1 -0
  74. data/public/js/webshims/shims/es6.js +1 -0
  75. data/public/js/webshims/shims/excanvas.js +1 -0
  76. data/public/js/webshims/shims/filereader-xhr.js +1 -0
  77. data/public/js/webshims/shims/form-combat.js +1 -0
  78. data/public/js/webshims/shims/form-core.js +1 -0
  79. data/public/js/webshims/shims/form-datalist-lazy.js +1 -0
  80. data/public/js/webshims/shims/form-datalist.js +1 -0
  81. data/public/js/webshims/shims/form-fixrangechange.js +1 -0
  82. data/public/js/webshims/shims/form-inputmode.js +1 -0
  83. data/public/js/webshims/shims/form-message.js +1 -0
  84. data/public/js/webshims/shims/form-native-extend.js +1 -0
  85. data/public/js/webshims/shims/form-number-date-api.js +1 -0
  86. data/public/js/webshims/shims/form-number-date-ui.js +1 -0
  87. data/public/js/webshims/shims/form-shim-extend.js +1 -0
  88. data/public/js/webshims/shims/form-shim-extend2.js +1 -0
  89. data/public/js/webshims/shims/form-validation.js +1 -0
  90. data/public/js/webshims/shims/form-validators.js +1 -0
  91. data/public/js/webshims/shims/forms-picker.js +1 -0
  92. data/public/js/webshims/shims/geolocation.js +1 -0
  93. data/public/js/webshims/shims/i18n/formcfg-ar.js +1 -0
  94. data/public/js/webshims/shims/i18n/formcfg-ch-CN.js +1 -0
  95. data/public/js/webshims/shims/i18n/formcfg-cs.js +1 -0
  96. data/public/js/webshims/shims/i18n/formcfg-de.js +1 -0
  97. data/public/js/webshims/shims/i18n/formcfg-el.js +1 -0
  98. data/public/js/webshims/shims/i18n/formcfg-en.js +1 -0
  99. data/public/js/webshims/shims/i18n/formcfg-es.js +1 -0
  100. data/public/js/webshims/shims/i18n/formcfg-fa.js +1 -0
  101. data/public/js/webshims/shims/i18n/formcfg-fr.js +1 -0
  102. data/public/js/webshims/shims/i18n/formcfg-he.js +1 -0
  103. data/public/js/webshims/shims/i18n/formcfg-hi.js +1 -0
  104. data/public/js/webshims/shims/i18n/formcfg-hu.js +1 -0
  105. data/public/js/webshims/shims/i18n/formcfg-it.js +1 -0
  106. data/public/js/webshims/shims/i18n/formcfg-ja.js +1 -0
  107. data/public/js/webshims/shims/i18n/formcfg-lt.js +1 -0
  108. data/public/js/webshims/shims/i18n/formcfg-nl.js +1 -0
  109. data/public/js/webshims/shims/i18n/formcfg-pl.js +1 -0
  110. data/public/js/webshims/shims/i18n/formcfg-pt-BR.js +1 -0
  111. data/public/js/webshims/shims/i18n/formcfg-pt-PT.js +1 -0
  112. data/public/js/webshims/shims/i18n/formcfg-pt.js +1 -0
  113. data/public/js/webshims/shims/i18n/formcfg-ru.js +1 -0
  114. data/public/js/webshims/shims/i18n/formcfg-sv.js +1 -0
  115. data/public/js/webshims/shims/i18n/formcfg-zh-CN.js +1 -0
  116. data/public/js/webshims/shims/i18n/formcfg-zh-TW.js +1 -0
  117. data/public/js/webshims/shims/jme/alternate-media.js +1 -0
  118. data/public/js/webshims/shims/jme/base.js +1 -0
  119. data/public/js/webshims/shims/jme/controls.css +1 -0
  120. data/public/js/webshims/shims/jme/jme.eot +0 -0
  121. data/public/js/webshims/shims/jme/jme.svg +36 -0
  122. data/public/js/webshims/shims/jme/jme.ttf +0 -0
  123. data/public/js/webshims/shims/jme/jme.woff +0 -0
  124. data/public/js/webshims/shims/jme/mediacontrols-lazy.js +1 -0
  125. data/public/js/webshims/shims/jme/mediacontrols.js +1 -0
  126. data/public/js/webshims/shims/jme/playlist.js +1 -0
  127. data/public/js/webshims/shims/jpicker/images/AlphaBar.png +0 -0
  128. data/public/js/webshims/shims/jpicker/images/Bars.png +0 -0
  129. data/public/js/webshims/shims/jpicker/images/Maps.png +0 -0
  130. data/public/js/webshims/shims/jpicker/images/NoColor.png +0 -0
  131. data/public/js/webshims/shims/jpicker/images/bar-opacity.png +0 -0
  132. data/public/js/webshims/shims/jpicker/images/map-opacity.png +0 -0
  133. data/public/js/webshims/shims/jpicker/images/mappoint.gif +0 -0
  134. data/public/js/webshims/shims/jpicker/images/picker.gif +0 -0
  135. data/public/js/webshims/shims/jpicker/images/preview-opacity.png +0 -0
  136. data/public/js/webshims/shims/jpicker/images/rangearrows.gif +0 -0
  137. data/public/js/webshims/shims/jpicker/jpicker.css +1 -0
  138. data/public/js/webshims/shims/matchMedia.js +3 -0
  139. data/public/js/webshims/shims/mediacapture-picker.js +1 -0
  140. data/public/js/webshims/shims/mediacapture.js +1 -0
  141. data/public/js/webshims/shims/mediaelement-core.js +1 -0
  142. data/public/js/webshims/shims/mediaelement-debug.js +1 -0
  143. data/public/js/webshims/shims/mediaelement-jaris.js +1 -0
  144. data/public/js/webshims/shims/mediaelement-native-fix.js +1 -0
  145. data/public/js/webshims/shims/mediaelement-yt.js +1 -0
  146. data/public/js/webshims/shims/moxie/flash/Moxie.cdn.swf +0 -0
  147. data/public/js/webshims/shims/moxie/flash/Moxie.min.swf +0 -0
  148. data/public/js/webshims/shims/moxie/js/moxie-html4.js +3 -0
  149. data/public/js/webshims/shims/moxie/js/moxie-swf.js +2 -0
  150. data/public/js/webshims/shims/picture.js +1 -0
  151. data/public/js/webshims/shims/plugins/jquery.ui.position.js +11 -0
  152. data/public/js/webshims/shims/range-ui.js +1 -0
  153. data/public/js/webshims/shims/sizzle.js +11 -0
  154. data/public/js/webshims/shims/sticky.js +1 -0
  155. data/public/js/webshims/shims/styles/color-picker.png +0 -0
  156. data/public/js/webshims/shims/styles/forms-ext.css +1 -0
  157. data/public/js/webshims/shims/styles/forms-picker.css +1 -0
  158. data/public/js/webshims/shims/styles/progress.gif +0 -0
  159. data/public/js/webshims/shims/styles/progress.png +0 -0
  160. data/public/js/webshims/shims/styles/shim-ext.css +1 -0
  161. data/public/js/webshims/shims/styles/shim.css +1 -0
  162. data/public/js/webshims/shims/styles/transparent.png +0 -0
  163. data/public/js/webshims/shims/styles/widget.eot +0 -0
  164. data/public/js/webshims/shims/styles/widget.svg +12 -0
  165. data/public/js/webshims/shims/styles/widget.ttf +0 -0
  166. data/public/js/webshims/shims/styles/widget.woff +0 -0
  167. data/public/js/webshims/shims/swf/JarisFLVPlayer.swf +0 -0
  168. data/public/js/webshims/shims/swfmini-embed.js +1 -0
  169. data/public/js/webshims/shims/swfmini.js +6 -0
  170. data/public/js/webshims/shims/track-ui.js +1 -0
  171. data/public/js/webshims/shims/track.js +1 -0
  172. data/public/js/webshims/shims/url.js +1 -0
  173. data/public/js/webshims/shims/usermedia-core.js +1 -0
  174. data/public/js/webshims/shims/usermedia-shim.js +1 -0
  175. data/sequenceserver.gemspec +16 -13
  176. data/views/400.erb +28 -0
  177. data/views/500.erb +35 -19
  178. data/views/_options.erb +6 -15
  179. data/views/result.erb +218 -0
  180. data/views/search.erb +354 -151
  181. metadata +254 -62
  182. data/example.config.yml +0 -39
  183. data/lib/sequenceserver/customisation.rb +0 -60
  184. data/lib/sequenceserver/database_formatter.rb +0 -190
  185. data/lib/sequenceserver/helpers.rb +0 -136
  186. data/lib/sequenceserver/sequencehelpers.rb +0 -93
  187. data/lib/sequenceserver/sinatralikeloggerformatter.rb +0 -12
  188. data/lib/sequenceserver/version.rb +0 -9
  189. data/public/css/beige.css.css +0 -254
  190. data/public/css/bootstrap.dropdown.css +0 -29
  191. data/public/css/bootstrap.icons.css +0 -155
  192. data/public/css/bootstrap.modal.css +0 -28
  193. data/public/js/bootstrap.dropdown.js +0 -92
  194. data/public/js/bootstrap.modal.js +0 -7
  195. data/public/js/bootstrap.transition.js +0 -7
  196. data/public/js/jquery-scrollspy.js +0 -98
  197. data/public/js/jquery.activity.js +0 -10
  198. data/public/js/jquery.enablePlaceholder.min.js +0 -10
  199. data/public/js/store.min.js +0 -2
  200. data/public/sequence.html +0 -28
  201. data/tests/database/nucleotide/Sinvicta2-2-3.cdna.subset.fasta +0 -5486
  202. data/tests/database/nucleotide/Sinvicta2-2-3.cdna.subset.fasta.nhr +0 -0
  203. data/tests/database/nucleotide/Sinvicta2-2-3.cdna.subset.fasta.nin +0 -0
  204. data/tests/database/nucleotide/Sinvicta2-2-3.cdna.subset.fasta.nsq +0 -0
  205. data/tests/database/protein/Sinvicta2-2-3.prot.subset.fasta +0 -6449
  206. data/tests/database/protein/Sinvicta2-2-3.prot.subset.fasta.phr +0 -0
  207. data/tests/database/protein/Sinvicta2-2-3.prot.subset.fasta.pin +0 -0
  208. data/tests/database/protein/Sinvicta2-2-3.prot.subset.fasta.psq +0 -0
  209. data/tests/run +0 -26
  210. data/tests/test_sequencehelpers.rb +0 -77
  211. data/tests/test_sequenceserver_blast.rb +0 -60
  212. data/tests/test_ui.rb +0 -104
  213. data/tests/ui.specs.todo +0 -10
@@ -96,21 +96,21 @@ SS.blast = (function () {
96
96
  return true;
97
97
  }
98
98
 
99
- /*
100
- determine input sequence type, and trigger 'sequence_type_changed'
101
- event if the input sequence type has changed
102
- */
99
+ /**
100
+ * Determine input sequence type, and trigger 'sequence_type_changed' event
101
+ * if the input sequence type has changed, or cannot be determined.
102
+ */
103
103
  var signal_sequence_type_changed = function () {
104
- var type, tmp;
104
+ var type, _type;
105
105
 
106
106
  $('#sequence').change(function () {
107
- tmp = type_of_sequences();
107
+ _type = type_of_sequences();
108
108
 
109
- if (tmp != type){
110
- type = tmp;
109
+ if (!_type || _type !== type) {
110
+ type = _type;
111
111
 
112
- //notify listeners
113
- $(this).trigger('sequence_type_changed', type);
112
+ //notify listeners
113
+ $(this).trigger('sequence_type_changed', type);
114
114
  }
115
115
  });
116
116
  };
@@ -131,39 +131,36 @@ SS.blast = (function () {
131
131
  });
132
132
  }
133
133
 
134
- /* */
134
+ /**
135
+ * Triggers 'blast_method_changed' event if BLAST algorithms that can be
136
+ * used for the user input have changed.
137
+ */
135
138
  var signal_blast_method_changed = function () {
136
- var method, tmp;
139
+ var method, _method;
137
140
 
138
141
  $('#blast').on('sequence_type_changed database_type_changed',
139
142
  function (event) {
140
- tmp = determine_blast_method();
143
+ _method = determine_blast_method();
141
144
 
142
- if (tmp != method) {
143
- method = tmp;
145
+ if (!_.isEqual(_method, method)) {
146
+ method = _method;
144
147
 
145
148
  //notify listeners
146
- $(this).trigger('blast_method_changed', [method]);
149
+ $(this).trigger('blast_method_changed', [method.slice()]);
147
150
  }
148
151
  });
149
152
  }
150
153
 
151
- /*
152
- Return a BLAST method to use for the selected database and input
153
- sequence type.
154
-
155
- database method
156
- ------------------------
157
- protein blastx
158
- protein blastp
159
- ------------------------
160
- nucleotide tblastx
161
- nucleotide tblastn
162
- nucleotide blastn
163
- */
154
+ /**
155
+ * Returns name of BLAST algorithms that can be used for the input query
156
+ * and selected database combination.
157
+ *
158
+ * Returns empty array if no BLAST algorithm is appropriate for the user
159
+ * input.
160
+ */
164
161
  var determine_blast_method = function () {
165
162
  if (!required_params_present()) {
166
- return
163
+ return [];
167
164
  }
168
165
 
169
166
  var database_type = type_of_databases();
@@ -190,6 +187,8 @@ SS.blast = (function () {
190
187
  return ['blastn', 'tblastx'];
191
188
  }
192
189
  }
190
+
191
+ return [];
193
192
  }
194
193
 
195
194
  /* public interface */
@@ -2,31 +2,46 @@
2
2
  (function( $ ){
3
3
  //disable an element
4
4
  $.fn.disable = function() {
5
- return this.attr('disabled', 'disabled');
5
+ return this.prop('disabled', true).addClass('disabled');
6
6
  };
7
7
  })( jQuery );
8
8
 
9
9
  (function( $ ){
10
10
  //enable an element
11
11
  $.fn.enable = function() {
12
- return this.removeAttr('disabled');
12
+ return this.prop('disabled', false).removeClass('disabled');
13
13
  };
14
14
  })( jQuery );
15
15
 
16
16
  (function( $ ){
17
17
  //uncheck an element
18
18
  $.fn.uncheck = function() {
19
- return this.removeAttr('checked');
19
+ return this.prop('checked', false);
20
20
  };
21
21
  })( jQuery );
22
22
 
23
23
  (function( $ ){
24
24
  //check an element
25
25
  $.fn.check = function() {
26
- return this.attr('checked', 'checked');
26
+ return this.prop('checked', true);
27
27
  };
28
28
  })( jQuery );
29
29
 
30
+ /**
31
+ * Wiggle an element.
32
+ *
33
+ * Used for wiggling BLAST button.
34
+ */
35
+ (function ($) {
36
+ $.fn.wiggle = function() {
37
+ this.finish().effect("bounce", {
38
+ direction: 'left',
39
+ distance: 24,
40
+ times: 4,
41
+ }, 250);
42
+ };
43
+ }(jQuery));
44
+
30
45
  (function( $ ){
31
46
  //(pre-)check the only active database checkbox
32
47
  $.onedb = function(selector) {
@@ -38,25 +53,12 @@
38
53
  };
39
54
  })( jQuery );
40
55
 
41
- (function( $ ){
42
- //highlight an element
43
- $.fn.highlight = function() {
44
- return this.addClass('focussed');
45
- };
46
- })( jQuery );
47
-
48
- (function( $ ){
49
- //unhighlight an element
50
- $.fn.unhighlight = function() {
51
- return this.removeClass('focussed');
52
- };
53
- })( jQuery );
54
-
55
56
  (function ($) {
56
57
  $.fn.poll = function () {
57
58
  var that, val, tmp;
58
59
 
59
60
  that = this;
61
+ val = that.val();
60
62
 
61
63
  (function ping () {
62
64
  tmp = that.val();
@@ -92,6 +94,9 @@ if (!SS) {
92
94
  //SS module
93
95
  (function () {
94
96
 
97
+ // Starts with >.
98
+ SS.FASTA_FORMAT = /^>/;
99
+
95
100
  SS.decorate = function (name) {
96
101
  return name.match(/(.?)(blast)(.?)/).slice(1).map(function (token, _) {
97
102
  if (token) {
@@ -103,28 +108,437 @@ if (!SS) {
103
108
  }
104
109
  }
105
110
  }).join('');
106
- }
111
+ };
112
+
113
+ SS.generateGraphicalOverview = function () {
114
+ $("[data-graphit='overview']").each(function () {
115
+ var $this = $(this);
116
+ var $graphDiv = $('<div/>').addClass('graphical-overview');
117
+ $this.children().eq(1).children().eq(0).before($graphDiv);
118
+
119
+ $.graphIt($this, $graphDiv, 0, 20);
120
+ });
121
+ };
122
+
123
+ SS.updateDownloadFastaOfAllLink = function () {
124
+ var num_hits = $('.hitn').length;
125
+
126
+ var $a = $('.download-fasta-of-all');
127
+ if (num_hits >= 1 && num_hits <= 30) {
128
+ var sequence_ids = $('.hitn :checkbox').map(function() {
129
+ return this.value;
130
+ }).get();
131
+ $a
132
+ .enable()
133
+ .attr('href', SS.generateURI(sequence_ids, $a.data().databases))
134
+ .tooltip('destroy')
135
+ .tooltip({
136
+ placement: 'left',
137
+ title: num_hits + " hit(s)."
138
+ });
139
+ return;
140
+ }
141
+
142
+ if (num_hits == 0) {
143
+ $a
144
+ .tooltip('destroy')
145
+ .tooltip({
146
+ placement: 'left',
147
+ title: "No hit to download."
148
+ });
149
+ }
150
+
151
+ if (num_hits > 30) {
152
+ $a
153
+ .tooltip('destroy')
154
+ .tooltip({
155
+ placement: 'left',
156
+ title: "Can't download more than 30 hits."
157
+ });
158
+ }
159
+ $a.disable();
160
+ };
161
+
162
+ /* Update the FASTA downloader button's state appropriately.
163
+ *
164
+ * When more than 30 hits are obtained, the link is disabled.
165
+ * When no hits are obtained, the link is not present at all.
166
+ */
167
+ SS.updateDownloadFastaOfSelectedLink = function () {
168
+ var num_checked = $('.hitn :checkbox:checked').length;
169
+
170
+ var $a = $('.download-fasta-of-selected');
171
+ var $n = $a.find('span');
172
+
173
+ if (num_checked >= 1 && num_checked <= 30) {
174
+ var sequence_ids = $('.hitn :checkbox:checked').map(function () {
175
+ return this.value;
176
+ }).get();
177
+
178
+ $a
179
+ .enable()
180
+ .attr('href', SS.generateURI(sequence_ids, $a.data().databases))
181
+ .tooltip('destroy')
182
+ .tooltip({
183
+ placement: 'left',
184
+ title: num_checked + " hit(s) selected."
185
+ })
186
+ .find('span').html(num_checked);
187
+ return;
188
+ }
189
+
190
+ if (num_checked == 0) {
191
+ $n.empty();
192
+ $a
193
+ .tooltip('destroy')
194
+ .tooltip({
195
+ placement: 'left',
196
+ title: "No hit selected."
197
+ });
198
+ }
199
+
200
+ if (num_checked > 30) {
201
+ $a
202
+ .tooltip('destroy')
203
+ .tooltip({
204
+ placement: 'left',
205
+ title: "Can't download more than 30 hits."
206
+ });
207
+ }
208
+
209
+ $a
210
+ .disable()
211
+ .removeAttr('href');
212
+ };
213
+
214
+ SS.updateSequenceViewerLinks = function () {
215
+ var MAX_LENGTH = 10000;
216
+
217
+ $('.view-sequence').each(function () {
218
+ var $this = $(this);
219
+ var $hitn = $this.closest('.hitn');
220
+ if ($hitn.data().hitLen > MAX_LENGTH) {
221
+ $this
222
+ .disable()
223
+ .removeAttr('href')
224
+ .tooltip({
225
+ title: 'Sequence too long to show. Please view it ' +
226
+ 'locally after download.'
227
+ });
228
+ }
229
+ });
230
+ };
231
+
232
+ SS.setupTooltipsForPosLabels = function () {
233
+ $('.pos-label').each(function () {
234
+ $(this).tooltip({
235
+ container: 'body',
236
+ placement: 'right',
237
+ });
238
+ });
239
+ };
240
+
241
+ SS.generateURI = function (sequence_ids, database_ids) {
242
+ // Encode URIs against strange characters in sequence ids.
243
+ sequence_ids = encodeURIComponent(sequence_ids.join(' '));
244
+ database_ids = encodeURIComponent(database_ids);
245
+
246
+ var url = "get_sequence/?sequence_ids=" + sequence_ids +
247
+ "&database_ids=" + database_ids + '&download=fasta';
248
+
249
+ return url;
250
+ },
251
+
252
+ SS.showErrorModal = function (jqXHR, beforeShow) {
253
+ setTimeout(function () {
254
+ beforeShow();
255
+ if (jqXHR.responseText) {
256
+ $("#error").html(jqXHR.responseText).modal();
257
+ }
258
+ else {
259
+ $("#error-no-response").modal();
260
+ }
261
+ }, 500);
262
+ },
263
+
264
+ SS.init = function () {
265
+ this.$sequence = $('#sequence');
266
+ this.$sequenceFile = $('#sequence-file');
267
+ this.$sequenceControls = $('.sequence-controls');
268
+
269
+ this.$sequence.poll();
107
270
 
108
- /*
109
- ask each module to initialize itself
110
- */
111
- SS.main = function () {
112
271
  SS.blast.init();
113
- }
272
+ };
273
+
114
274
  }()); //end SS module
115
275
 
116
- $(document).ready(function(){
117
- // poll the sequence textbox for a change in user input
118
- $('#sequence').poll();
276
+ /**
277
+ * Highlight hit div corresponding to given checkbox and update bulk download
278
+ * link.
279
+ */
280
+ SS.selectHit = function (checkbox) {
281
+ if (!checkbox || !checkbox.value) return;
282
+
283
+ var $hitn = $('.hitn[data-hit-def="' + checkbox.value + '"]');
284
+
285
+ // Highlight selected hit and sync checkboxes if sequence viewer is open.
286
+ if(checkbox.checked) {
287
+ $hitn
288
+ .addClass('glow')
289
+ .find(":checkbox").not(checkbox).check();
290
+ } else {
291
+ $hitn
292
+ .removeClass('glow')
293
+ .find(":checkbox").not(checkbox).uncheck();
294
+ }
295
+
296
+ this.updateDownloadFastaOfSelectedLink();
297
+ };
119
298
 
120
- // start SequenceServer's event loop
121
- SS.main();
299
+ SS.showSequenceViewer = (function () {
300
+
301
+ var $viewer = $('#sequence-viewer');
302
+ var $spinner = $('.spinner', $viewer);
303
+ var $viewerBody = $('.modal-body', $viewer);
304
+ var $viewerFooter = $('.modal-footer', $viewer);
305
+
306
+ var widgetClass = 'biojs-vis-sequence';
307
+
308
+ var initViewer = function ($clicked) {
309
+ $viewerBody.empty();
310
+ $viewerFooter.empty();
311
+
312
+ initFooter($clicked);
313
+ $spinner.show();
314
+ $viewer.modal('show');
315
+ };
316
+
317
+ var initFooter = function ($clicked) {
318
+ // Generate links in the footer.
319
+ var links = $clicked.parent().clone();
320
+ var viewSequenceLink = links.find('.view-sequence');
321
+ var nextSeparator = $(viewSequenceLink[0].nextSibling);
322
+ viewSequenceLink.remove();
323
+ nextSeparator.remove();
324
+ $viewerFooter.empty().append(links);
325
+ };
326
+
327
+ var showSequences = function (sequence_ids, sequences, databases) {
328
+ // Inform user if number of sequences retrieved is more or less than requested for.
329
+ if (sequence_ids.length != sequences.length) {
330
+ showLessOrMoreSequencesRetrievedMessage(sequence_ids, sequences, databases);
331
+ }
332
+
333
+ // Render sequence.
334
+ sequences.forEach(showSequence);
335
+ };
336
+
337
+ var showSequence = function (sequence) {
338
+ // generate html template
339
+ var header = sequence.id + "<small>&nbsp;" + sequence.title + "</small>";
340
+ var widgetId = widgetClass + (new Date().getUTCMilliseconds());
341
+
342
+ $viewerBody
343
+ .append(
344
+ $('<div/>')
345
+ .addClass('fastan')
346
+ .append(
347
+ $('<div/>')
348
+ .addClass('page-header')
349
+ .append(
350
+ $('<h4>')
351
+ .html(header)
352
+ ),
353
+ $('<div>')
354
+ .attr('id', widgetId)
355
+ .addClass(widgetClass)
356
+ .addClass('page-content')
357
+ )
358
+ );
359
+
360
+ // attach BioJS sequence viewer
361
+ var widget = new Sequence({
362
+ sequence: sequence.value,
363
+ target: widgetId,
364
+ format: 'CODATA',
365
+ columns: {
366
+ size: 35
367
+ },
368
+ formatOptions: {
369
+ title: false,
370
+ footer: false
371
+ }
372
+ });
373
+ widget.hideFormatSelector();
374
+ };
375
+
376
+ var showLessOrMoreSequencesRetrievedMessage = function (sequence_ids, sequences, databases) {
377
+ $viewerBody
378
+ .append(
379
+ $('<h4/>')
380
+ .html("ERROR: incorrect number of sequences found."),
381
+ $('<p/>')
382
+ .html('You requested ' + sequence_ids.length +
383
+ ' sequence(s) with the following identifiers: <br>' +
384
+ '<code>' + sequence_ids.join(', ') + '</code> <br>' +
385
+ 'from the following databases: <br>' +
386
+ '<code>' + databases.join(', ') + '</code> <br>' +
387
+ 'but we found ' + sequences.length + ' sequence(s).'),
388
+ $('<p/>')
389
+ .html('This is likley due to a problem with how databases ' +
390
+ 'are formatted.<strong> Please share this text with ' +
391
+ 'the person managing this website so that the issue ' +
392
+ 'can be resolved.'),
393
+ $('<p/>')
394
+ .html('If any sequences were retrieved, you ' +
395
+ 'can find them below (but some may be incorrect, so ' +
396
+ 'be careful!)')
397
+ );
398
+ };
399
+
400
+ return function (clicked) {
401
+ var $clicked = $(clicked);
402
+ initViewer($clicked);
403
+
404
+ var url = $clicked.attr('href');
405
+ $.getJSON(url)
406
+ .done(function (response) {
407
+ showSequences(response.sequence_ids, response.sequences, response.databases);
408
+ $spinner.hide();
409
+ })
410
+ .fail(function (jqXHR, status, error) {
411
+ SS.showErrorModal(jqXHR, function () {
412
+ $viewer.modal('hide');
413
+ });
414
+ });
415
+ };
416
+ }());
417
+
418
+ $(document).ready(function(){
419
+ SS.init();
122
420
 
123
421
  var notification_timeout;
124
422
 
423
+ // drag-and-drop code
424
+ var tgtMarker = $('.dnd-overlay');
425
+ var $sequence = $('#sequence');
426
+
427
+ var dndError = function (id) {
428
+ $('.dnd-error').hide();
429
+ $('#' + id + '-notification').show();
430
+ tgtMarker.effect('fade', 2500);
431
+ };
432
+
433
+ $(document)
434
+ .on('dragenter', function (evt) {
435
+ // Based on http://stackoverflow.com/a/8494918/1205465.
436
+ // Contrary to what the above link says, the snippet below can't
437
+ // distinguish directories from files. We handle that on drop.
438
+ var dt = evt.originalEvent.dataTransfer;
439
+ var isFile = dt.types && ((dt.types.indexOf && // Chrome and Safari
440
+ dt.types.indexOf('Files') != -1) ||
441
+ (dt.types.contains && // Firefox
442
+ dt.types.contains('application/x-moz-file')));
443
+
444
+ if (!isFile) { return; }
445
+
446
+ $('.dnd-error').hide();
447
+ tgtMarker.stop(true, true);
448
+ tgtMarker.show();
449
+ dt.effectAllowed = 'copy';
450
+ if ($sequence.val() === '') {
451
+ $('.dnd-overlay-overwrite').hide();
452
+ $('.dnd-overlay-drop').show('drop', {direction: 'down'}, 'fast');
453
+ }
454
+ else {
455
+ $('.dnd-overlay-drop').hide();
456
+ $('.dnd-overlay-overwrite').show('drop', {direction: 'down'}, 'fast');
457
+ }
458
+ })
459
+ .on('dragleave', '.dnd-overlay', function (evt) {
460
+ tgtMarker.hide();
461
+ $('.dnd-overlay-drop').hide();
462
+ $('.dnd-overlay-overwrite').hide();
463
+ })
464
+ .on('dragover', '.dnd-overlay', function (evt) {
465
+ evt.originalEvent.dataTransfer.dropEffect = 'copy';
466
+ evt.preventDefault();
467
+ })
468
+ .on('drop', '.dnd-overlay', function (evt) {
469
+ evt.preventDefault();
470
+ evt.stopPropagation();
471
+
472
+ var textarea = $('#sequence');
473
+ var indicator = $('#sequence-file');
474
+ textarea.focus();
475
+
476
+ var files = evt.originalEvent.dataTransfer.files;
477
+ if (files.length > 1) {
478
+ dndError('dnd-multi');
479
+ return;
480
+ }
481
+
482
+ var file = files[0];
483
+ if (file.size > 10 * 1048576) {
484
+ dndError('dnd-large-file');
485
+ return;
486
+ }
487
+
488
+ var reader = new FileReader();
489
+ reader.onload = function (e) {
490
+ var content = e.target.result;
491
+ if (SS.FASTA_FORMAT.test(content)) {
492
+ textarea.val(content);
493
+ indicator.text(file.name);
494
+ tgtMarker.hide();
495
+ } else {
496
+ // apparently not FASTA
497
+ dndError('dnd-format');
498
+ }
499
+ };
500
+ reader.onerror = function (e) {
501
+ // Couldn't read. Means dropped stuff wasn't FASTA file.
502
+ dndError('dnd-format');
503
+ }
504
+ reader.readAsText(file);
505
+ });
506
+ // end drag-and-drop
507
+
508
+ SS.$sequence.change(function () {
509
+ if (SS.$sequence.val()) {
510
+ // Calculation below is based on -
511
+ // http://chris-spittles.co.uk/jquery-calculate-scrollbar-width/
512
+ var sequenceControlsRight = SS.$sequence[0].offsetWidth -
513
+ SS.$sequence[0].clientWidth;
514
+ SS.$sequenceControls.css('right', sequenceControlsRight + 17);
515
+ SS.$sequenceControls.removeClass('hidden');
516
+ }
517
+ else {
518
+ SS.$sequenceFile.empty();
519
+ SS.$sequenceControls.addClass('hidden');
520
+ SS.$sequence.parent().removeClass('has-error');
521
+ }
522
+ });
523
+
524
+ // Handle clearing query sequences(s) when x button is pressed.
525
+ $('#btn-sequence-clear').click(function (e) {
526
+ $('#sequence').val("").focus();
527
+ })
528
+
529
+ // pre-select if only on db
530
+ $.onedb();
531
+
532
+ // Handles the form submission when Ctrl+Enter is pressed anywhere on page
533
+ $(document).bind("keydown", function (e) {
534
+ if (e.ctrlKey && e.keyCode === 13 && !$('#method').is(':disabled')) {
535
+ $('#method').trigger('submit');
536
+ }
537
+ });
538
+
125
539
  $('#sequence').on('sequence_type_changed', function (event, type) {
126
540
  clearTimeout(notification_timeout);
127
- $(this).parent('.control-group').removeClass('error');
541
+ $(this).parent().removeClass('has-error');
128
542
  $('.notifications .active').hide().removeClass('active');
129
543
 
130
544
  if (type) {
@@ -135,7 +549,7 @@ $(document).ready(function(){
135
549
  }, 5000);
136
550
 
137
551
  if (type === 'mixed') {
138
- $(this).parent('.control-group').addClass('error');
552
+ $(this).parent().addClass('has-error');
139
553
  }
140
554
  }
141
555
  });
@@ -148,70 +562,113 @@ $(document).ready(function(){
148
562
  switch (type) {
149
563
  case 'protein':
150
564
  $('.databases.nucleotide input:checkbox').uncheck().disable();
565
+ $('.databases.nucleotide .checkbox').addClass('disabled');
151
566
  break;
152
567
  case 'nucleotide':
153
568
  $('.databases.protein input:checkbox').uncheck().disable();
569
+ $('.databases.protein .checkbox').addClass('disabled');
154
570
  break;
155
571
  default:
156
572
  $('.databases input:checkbox').enable();
573
+ $('.databases .checkbox').removeClass('disabled');
157
574
  break;
158
575
  }
159
576
  });
160
577
 
161
- $('form').on('blast_method_changed', function (event, methods){
578
+ $('form').on('blast_method_changed', function (event, methods) {
162
579
  // reset
163
- $('#methods .dropdown-menu').html('');
164
- $('#method').disable().val('').html('blast');
165
- $('#methods').removeClass('btn-group').children('.dropdown-toggle').hide();
580
+ $('#method')
581
+ .disable().val('').html('blast')
582
+ .removeClass('col-md-11').addClass('col-md-12');
166
583
 
167
- if (methods) {
584
+ $('#methods')
585
+ .children().not('#method').remove();
586
+
587
+ // set
588
+ if (methods.length > 0) {
168
589
  var method = methods.shift();
169
590
 
170
- $('#method').enable().val(method).html(SS.decorate(method));
591
+ $('#method')
592
+ .enable().val(method).html(SS.decorate(method));
171
593
 
172
594
  if (methods.length >=1) {
173
- $('#methods').addClass('btn-group').
174
- children('.dropdown-toggle').show();
175
-
176
- var methods_list = $.map(methods, function (method, _) {
177
- return "<li>" + SS.decorate(method) + "</li>";
178
- }).join('');
179
-
180
- $('#methods .dropdown-menu').html(methods_list);
595
+ $('#methods')
596
+ .append
597
+ (
598
+ $('<button/>')
599
+ .attr('type', 'button')
600
+ .addClass("btn btn-primary dropdown-toggle col-md-1")
601
+ .attr('data-toggle', 'dropdown')
602
+ .append
603
+ (
604
+ $('<span/>')
605
+ .addClass('caret')
606
+ ),
607
+ $('<ul/>')
608
+ .addClass('dropdown-menu')
609
+ .append
610
+ (
611
+ $.map(methods, function (method) {
612
+ return $('<li/>').html(SS.decorate(method));
613
+ })
614
+ )
615
+ );
616
+
617
+ $('#method')
618
+ .removeClass('col-md-12').addClass('col-md-11');
181
619
  }
182
620
 
183
621
  // jiggle
184
- $("#methods").effect("bounce", { times:5, direction: 'left', distance: 12 }, 120);
622
+ $("#methods").wiggle();
185
623
  }
186
624
  });
187
625
 
188
- $('#methods .dropdown-menu').click(function (event) {
189
- // The list of possible blast methods is dynamically generated. So we
190
- // leverage event bubbling to trap 'click' event on the list items.
191
- var clicked = $(event.target);
626
+ // The list of possible blast methods is dynamically generated. So we
627
+ // leverage event bubbling and delegation to trap 'click' event on the list items.
628
+ // Please see : http://api.jquery.com/on/#direct-and-delegated-events
629
+ $(document).on("click", "#methods .dropdown-menu li", function(event) {
630
+ var clicked = $(this);
192
631
  var mbutton = $('#method');
193
-
194
632
  var old_method = mbutton.text();
195
633
  var new_method = clicked.text();
196
634
 
197
- //swap
635
+ // swap
198
636
  clicked.html(SS.decorate(old_method));
199
637
  mbutton.val(new_method).html(SS.decorate(new_method));
200
638
 
201
639
  // jiggle
202
- $("#methods").effect("bounce", { times:5, direction: 'left', distance: 12 }, 120);
640
+ $("#methods").wiggle();
203
641
  });
204
642
 
205
- $("input#advanced").enablePlaceholder({"withPlaceholderClass": "greytext"});
206
- $("textarea#sequence").enablePlaceholder({"withPlaceholderClass": "greytext"});
643
+ // HACK to allow users to select names from hit headers
644
+ $('.result').on('mousedown', ".hitn > .page-header > h4", function (event) {
645
+ var $this = $(this);
646
+ $this.on('mouseup mousemove', function handler(event) {
647
+ if (event.type === 'mouseup') {
648
+ // user wants to toggle
649
+ $this.attr('data-toggle', 'collapse');
650
+ $this.find('.fa-chevron-down').toggleClass('fa-rotate-270');
651
+ } else {
652
+ // user wants to select
653
+ $this.attr('data-toggle', '');
654
+ }
655
+ $this.off('mouseup mousemove', handler);
656
+ });
657
+ });
207
658
 
208
- $('.advanced pre').hover(function () {
209
- $(this).addClass('hover-focus');
210
- },
211
- function () {
212
- $(this).removeClass('hover-focus');
659
+ $('.result').on('click', '.view-sequence', function (event) {
660
+ event.preventDefault();
661
+ event.stopPropagation();
662
+ if (!event.target.disabled)
663
+ SS.showSequenceViewer(event.target);
664
+ });
665
+
666
+ $(document).on('change', '.hit-links :checkbox', function (event) {
667
+ event.stopPropagation();
668
+ SS.selectHit(this);
213
669
  });
214
670
 
671
+
215
672
  $('#blast').submit(function(){
216
673
  //parse AJAX URL
217
674
  var action = $(this).attr('action');
@@ -222,14 +679,8 @@ $(document).ready(function(){
222
679
  // reset hash so we can always _jump_ back to result
223
680
  location.hash = '';
224
681
 
225
- // display a modal window and attach an activity spinner to it
682
+ // show activity spinner
226
683
  $('#spinner').modal();
227
- $('#spinner > div').activity({
228
- segments: 8,
229
- length: 40,
230
- width: 16,
231
- speed: 1.8
232
- });
233
684
 
234
685
  // BLAST now
235
686
  var data = ($(this).serialize() + '&method=' + $('#method').val());
@@ -238,67 +689,40 @@ $(document).ready(function(){
238
689
  // BLASTed successfully
239
690
 
240
691
  // display the result
241
- $('.results').show();
242
- $('#result').html(data);
243
-
244
- $('#blast').addClass('detached-bottom');
245
- $('#underbar').addClass('detached-top');
692
+ $('.result').html(data).show();
693
+
694
+ // affix sidebar
695
+ var $sidebar = $('.sidebar');
696
+ if ($sidebar.length !== 0) {
697
+ $sidebar.affix({
698
+ offset: {
699
+ top: $sidebar.offset().top
700
+ }
701
+ })
702
+ .width($sidebar.width());
703
+ }
246
704
 
247
705
  //jump to the results
248
706
  location.hash = hash;
249
707
 
250
- $('.resultn').
251
- scrollspy({
252
- approach: screen.height / 4
253
- }).
254
- on('enter.scrollspy', function () {
255
- var id = $(this).attr('id');
256
- $(this).highlight();
257
- $('.index').find('a[href="#' + id + '"]').parent().highlight();
258
-
259
- return false;
260
- }).
261
- on('leave.scrollspy', function () {
262
- var id = $(this).attr('id');
263
- $(this).unhighlight();
264
- $('.index').find('a[href="#' + id + '"]').parent().unhighlight();
265
-
266
- return false;
267
- });
268
- }).
269
- fail(function (jqXHR, status, error) {
270
- //alert user
271
- $("#error-type").text(error);
272
- if(jqXHR.responseText == ''){
273
- $("#error-message").text('Is the SequenceServer running? The JQuery status was \''+status+'\', and the JQuery error was \''+error+'\'.');
274
- } else {
275
- $("#error-message").text(jqXHR.responseText);
276
- }
277
- $("#error").modal();
278
- }).
279
- always(function () {
280
- // BLAST complete (succefully or otherwise)
708
+ SS.generateGraphicalOverview();
709
+
710
+ SS.updateDownloadFastaOfAllLink();
711
+ SS.updateDownloadFastaOfSelectedLink();
712
+ SS.updateSequenceViewerLinks();
713
+ SS.setupTooltipsForPosLabels();
714
+
715
+ $('body').scrollspy({target: '.sidebar'});
281
716
 
282
- // remove progress notification
283
- $('#spinner > div').activity(false);
284
717
  $('#spinner').modal('hide');
718
+
719
+ }).
720
+ fail(function (jqXHR, status, error) {
721
+ SS.showErrorModal(jqXHR, function () {
722
+ $('#spinner').modal('hide');
723
+ });
285
724
  });
286
725
 
287
726
  return false;
288
727
  });
289
-
290
- (function (store) {
291
- try {
292
- var visits = store.get('visits') || 0;
293
- visits = visits + 1;
294
- if (visits <= 10) {
295
- store.set('visits', visits);
296
-
297
- if (visits === 10) {
298
- $('#social').modal();
299
- }
300
- }
301
- }
302
- catch (e) {};
303
- }(store));
304
728
  });