sequenceserver 3.1.0 → 3.1.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sequenceserver might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 274ace0d8b4683a9bcbf6b54c83e7c28e5bc1f89b0211676f91c4d3241699148
4
- data.tar.gz: d61d26277f381827f764e5d7bfd1cb61476e1e5da9dbeec9f4d9ccd4a383385c
3
+ metadata.gz: b058e493103527b8d0120243180b748b256997416d9886c3d349539d0afe9420
4
+ data.tar.gz: 836645ba201b125b1f379b934b86186128b76717c5bd456c85caa71e5cdc9269
5
5
  SHA512:
6
- metadata.gz: e83fe0932a797dcb8cb6f353b1256dd7e387031e078b3d3a3740662c4bd805a4f19554bf84c9bd79f4252df2decc2c6b58e911864e716944f7177014ceb4b1b4
7
- data.tar.gz: fb38408e8e8da2f4f7574de8e2a0c0547072fa53b6124a68a89b2771fec533c98c01986c4928ecc2007c3f32308ba6c4bd909703d97c216cdd6d38a11fff6c97
6
+ metadata.gz: 16e202385e0704918489da25ade9f938e3afe92a3298e338a2462188da049d9c780e80ba3cfe6de1c156196901d50570c845e030de71980151b2580ffd15a65f
7
+ data.tar.gz: a1033d77cb3feb609d0cf0b2f6af7dbfd558cca05027c4a6308d176f39c39b281d9dbf16b5de4d4d58620d00e372078a14103b35b456dc79b153d2d76c3c24e0
@@ -1,9 +1,10 @@
1
1
  module SequenceServer
2
2
  Error = Class.new(Sinatra::Error)
3
3
 
4
+ ValidationError = Class.new(Error)
4
5
  # DatabaseUnreachableError is raised when the serialised Job object is
5
6
  # refering to a database that is not present in the current filesystem.
6
- class DatabaseUnreachableError < Error
7
+ class DatabaseUnreachableError < ValidationError
7
8
  attr_reader :more_info
8
9
 
9
10
  def initialize(more_info)
@@ -11,6 +12,10 @@ module SequenceServer
11
12
  @more_info = more_info
12
13
  end
13
14
 
15
+ def http_status
16
+ 422
17
+ end
18
+
14
19
  def title
15
20
  'Sequence database unreachable'
16
21
  end
@@ -22,6 +27,31 @@ module SequenceServer
22
27
  end
23
28
  end
24
29
 
30
+ # InvalidSequenceIdError is raised when the FASTA sequence ID provided by the
31
+ # frontend appears to be invalid. It is important to validate the sequence ID
32
+ # format for security reasons.
33
+ class InvalidSequenceIdError < ValidationError
34
+ attr_reader :more_info
35
+
36
+ def initialize(more_info)
37
+ super
38
+ @more_info = more_info
39
+ end
40
+
41
+ def http_status
42
+ 422
43
+ end
44
+
45
+ def title
46
+ 'Sequence ID invalid'
47
+ end
48
+
49
+ def message
50
+ "The action you're trying to perform is not possible because \
51
+ one of the FASTA ids seems to be invalid."
52
+ end
53
+ end
54
+
25
55
  # API errors have an http status, title, message, and additional information
26
56
  # like stacktrace or information from program output.
27
57
  APIError = Class.new(Error)
@@ -9,6 +9,7 @@ require 'sequenceserver/blast/tasks'
9
9
  require 'sequenceserver/report'
10
10
  require 'sequenceserver/database'
11
11
  require 'sequenceserver/sequence'
12
+ require 'rack/csrf'
12
13
 
13
14
  module SequenceServer
14
15
  # Controller.
@@ -58,6 +59,14 @@ module SequenceServer
58
59
  frame_options = SequenceServer.config[:frame_options]
59
60
  frame_options && { frame_options: frame_options }
60
61
  }
62
+
63
+ use(
64
+ Rack::Session::Cookie,
65
+ key: 'rack.session.sequenceserver',
66
+ secret: ENV.fetch('SESSION_SECRET') { SecureRandom.alphanumeric(64) }
67
+ )
68
+
69
+ use Rack::Csrf, raise: true, skip: ['POST:/cloud_share']
61
70
  end
62
71
 
63
72
  unless ENV['SEQUENCE_SERVER_COMPRESS_RESPONSES'] == 'false'
@@ -147,15 +156,35 @@ module SequenceServer
147
156
  # in identifiers) and retreival_databases (we don't allow whitespace in a
148
157
  # database's name, so it's safe).
149
158
  get '/get_sequence/' do
150
- sequence_ids = params[:sequence_ids].split(',')
151
- database_ids = params[:database_ids].split(',')
159
+ sequence_ids = params[:sequence_ids].to_s.split(',')
160
+ database_ids = params[:database_ids].to_s.split(',')
161
+ if sequence_ids.empty?
162
+ status 422
163
+ return { error: 'No sequence ids provided' }.to_json
164
+ end
165
+
166
+ if database_ids.empty?
167
+ status 422
168
+ return { error: 'No database ids provided' }.to_json
169
+ end
152
170
  sequences = Sequence::Retriever.new(sequence_ids, database_ids)
153
171
  sequences.to_json
154
172
  end
155
173
 
156
174
  post '/get_sequence' do
157
- sequence_ids = params['sequence_ids'].split(',')
158
- database_ids = params['database_ids'].split(',')
175
+ sequence_ids = params['sequence_ids'].to_s.split(',')
176
+ database_ids = params['database_ids'].to_s.split(',')
177
+
178
+ if sequence_ids.empty?
179
+ status 422
180
+ return 'No sequence ids provided'
181
+ end
182
+
183
+ if database_ids.empty?
184
+ status 422
185
+ return 'No database ids provided'
186
+ end
187
+
159
188
  sequences = Sequence::Retriever.new(sequence_ids, database_ids, true)
160
189
  send_file(sequences.file.path,
161
190
  type: sequences.mime,
@@ -319,7 +348,8 @@ module SequenceServer
319
348
  download_links: [
320
349
  { name: 'Standard Tabular Report', url: "download/#{jid}.std_tsv" },
321
350
  { name: 'Full Tabular Report', url: "/download/#{jid}.full_tsv" },
322
- { name: 'Results in XML', url: "/download/#{jid}.xml" }
351
+ { name: 'Results in XML', url: "/download/#{jid}.xml" },
352
+ { name: 'Pairwise', url: "/download/#{jid}.pairwise" },
323
353
  ]
324
354
  }
325
355
  end
@@ -180,7 +180,7 @@ module SequenceServer
180
180
  @database_ids = Array database_ids
181
181
  @in_file = in_file
182
182
 
183
- validate && run
183
+ validate && create_entry_batch_file && run
184
184
  end
185
185
 
186
186
  attr_reader :sequence_ids, :database_ids, :in_file, :sequences
@@ -197,10 +197,9 @@ module SequenceServer
197
197
  def run
198
198
  command = "blastdbcmd -outfmt '%g %i %a %t %s'" \
199
199
  " -db '#{database_names.join(' ')}'" \
200
- " -entry '#{sequence_ids.join(',')}'"
200
+ " -entry_batch '#{@batch_file.path}'"
201
201
 
202
202
  out, = sys(command, path: config[:bin])
203
-
204
203
  @sequences = out.each_line.map do |line|
205
204
  # Stop codons in amino acid sequence databases show up as invalid
206
205
  # UTF-8 characters in the output and cause the subsequent call to
@@ -208,6 +207,8 @@ module SequenceServer
208
207
  line = line.encode('UTF-8', invalid: :replace, replace: 'X')
209
208
  Sequence.new(*line.chomp.split(' '))
210
209
  end
210
+
211
+ @batch_file.unlink
211
212
  extend(IO) && write if in_file
212
213
  end
213
214
 
@@ -221,11 +222,38 @@ module SequenceServer
221
222
 
222
223
  def validate
223
224
  ids = Database.ids
224
- return true if database_ids.is_a?(Array) && !database_ids.empty? &&
225
- (ids & database_ids).length == database_ids.length
225
+ unless database_ids.is_a?(Array) &&
226
+ !database_ids.empty? &&
227
+ (ids & database_ids).length == database_ids.length
228
+
229
+ fail(
230
+ DatabaseUnreachableError,
231
+ "Database id should be one of: #{ids.join("\n")}"
232
+ )
233
+ end
234
+
235
+ valid_id_regex = /\A[a-zA-Z0-9-_.:*#|\[\]]+\z/
236
+ invalid_sequence_ids = sequence_ids.reject do |id|
237
+ id =~ valid_id_regex
238
+ end
226
239
 
227
- fail DatabaseUnreachableError, 'Database id should be one of:' \
228
- " #{ids.join("\n")}"
240
+ unless invalid_sequence_ids.empty?
241
+ fail(
242
+ InvalidSequenceIdError,
243
+ "Invalid sequence id(s): #{invalid_sequence_ids.join(', ')}"
244
+ )
245
+ end
246
+
247
+ true
248
+ end
249
+
250
+ ##
251
+ # Create a temporary file containing sequence ids to fetch.
252
+ def create_entry_batch_file
253
+ @batch_file = Tempfile.new("#{Time.now}_batch").tap do |f|
254
+ f.write(sequence_ids.join("\n"))
255
+ f.flush
256
+ end
229
257
  end
230
258
 
231
259
  # rubocop:disable Metrics/MethodLength
@@ -22,7 +22,7 @@ module SequenceServer
22
22
  def start
23
23
  setup_signal_handlers
24
24
  @server = WEBrick::HTTPServer.new(options)
25
- @server.mount '/', Rack::Handler::WEBrick, app
25
+ @server.mount '/', Rackup::Handler::WEBrick, app
26
26
  @server.start
27
27
  end
28
28
 
@@ -1,4 +1,4 @@
1
1
  # Define version number.
2
2
  module SequenceServer
3
- VERSION = '3.1.0'.freeze
3
+ VERSION = '3.1.1'.freeze
4
4
  end
data/public/404.html CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  <body class="bg-light text-dark text-center">
16
16
 
17
- <img src="SequenceServer_logo.png" alt="SequenceServer Logo" class="mt-5 mb-3" style="max-width: 200px;">
17
+ <img src="/sequenceserver_logo.webp" alt="SequenceServer Logo" class="mt-5 mb-3" style="max-width: 200px;">
18
18
 
19
19
  <div class="container" style="margin-top: 10%;">
20
20
  <h1 class="display-4">404 Not Found</h1>
@@ -1 +1 @@
1
- /*! tailwindcss v3.4.3 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.\!container{width:100%!important}.container{width:100%}@media (min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.visible{visibility:visible}.invisible{visibility:hidden}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.bottom-0{bottom:0}.bottom-12{bottom:3rem}.end-1{inset-inline-end:.25rem}.left-0{left:0}.left-1{left:.25rem}.right-0{right:0}.right-2{right:.5rem}.start-1{inset-inline-start:.25rem}.top-0{top:0}.top-2{top:.5rem}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.col-span-2{grid-column:span 2/span 2}.mx-1{margin-left:.25rem;margin-right:.25rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-4{margin-top:1rem;margin-bottom:1rem}.my-6{margin-top:1.5rem;margin-bottom:1.5rem}.-ml-8{margin-left:-2rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-5{margin-top:1.25rem}.\!block{display:block!important}.block{display:block}.inline-block{display:inline-block}.\!inline{display:inline!important}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.table-caption{display:table-caption}.table-cell{display:table-cell}.grid{display:grid}.contents{display:contents}.list-item{display:list-item}.\!hidden{display:none!important}.hidden{display:none}.h-2{height:.5rem}.h-4{height:1rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-full{height:100%}.max-h-8{max-height:2rem}.max-h-96{max-height:24rem}.min-h-screen{min-height:100vh}.w-3{width:.75rem}.w-4{width:1rem}.w-48{width:12rem}.w-6{width:1.5rem}.w-auto{width:auto}.w-full{width:100%}.max-w-7xl{max-width:80rem}.max-w-screen-xl{max-width:1280px}.flex-auto{flex:1 1 auto}.flex-none{flex:none}.shrink{flex-shrink:1}.flex-grow,.grow{flex-grow:1}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-pointer{cursor:pointer}.resize{resize:both}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.gap-4{gap:1rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-gray-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(243 244 246/var(--tw-divide-opacity))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-scroll{overflow-y:scroll}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rounded{border-radius:.25rem}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-l-lg{border-top-left-radius:.5rem;border-bottom-left-radius:.5rem}.rounded-r-md{border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}.rounded-tr-lg{border-top-right-radius:.5rem}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-blue-800{--tw-border-opacity:1;border-color:rgb(30 64 175/var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity))}.border-seqblue{--tw-border-opacity:1;border-color:rgb(27 85 122/var(--tw-border-opacity))}.border-seqorange{--tw-border-opacity:1;border-color:rgb(199 79 19/var(--tw-border-opacity))}.border-transparent{border-color:#0000}.bg-blue-100{--tw-bg-opacity:1;background-color:rgb(219 234 254/var(--tw-bg-opacity))}.bg-blue-300{--tw-bg-opacity:1;background-color:rgb(147 197 253/var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-gray-500{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity))}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity))}.bg-seqblue{--tw-bg-opacity:1;background-color:rgb(27 85 122/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-yellow-100{--tw-bg-opacity:1;background-color:rgb(254 249 195/var(--tw-bg-opacity))}.bg-opacity-75{--tw-bg-opacity:0.75}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-white\/90{--tw-gradient-from:#ffffffe6 var(--tw-gradient-from-position);--tw-gradient-to:#fff0 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.to-gray-100\/90{--tw-gradient-to:#f3f4f6e6 var(--tw-gradient-to-position)}.fill-current{fill:currentColor}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-4{padding:1rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-16{padding-left:4rem;padding-right:4rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-8{padding-left:2rem;padding-right:2rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pb-20{padding-bottom:5rem}.pb-4{padding-bottom:1rem}.pr-2{padding-right:.5rem}.pt-2{padding-top:.5rem}.pt-4{padding-top:1rem}.pt-5{padding-top:1.25rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-bottom{vertical-align:bottom}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.italic{font-style:italic}.ordinal{--tw-ordinal:ordinal;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-6{line-height:1.5rem}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.text-red-800{--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity))}.text-seqblue{--tw-text-opacity:1;color:rgb(27 85 122/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.underline{-webkit-text-decoration-line:underline;text-decoration-line:underline}.\!shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a!important;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)!important;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)!important}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-2xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px #00000040;--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-xl{--tw-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a;--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid #0000;outline-offset:2px}.outline{outline-style:solid}.ring{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.blur{--tw-blur:blur(8px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.\!invert{--tw-invert:invert(100%)!important;filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.invert{--tw-invert:invert(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.\!filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.\!transition{transition-property:color,background-color,border-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-text-decoration-color,-webkit-backdrop-filter!important;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter!important;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-text-decoration-color,-webkit-backdrop-filter!important;transition-timing-function:cubic-bezier(.4,0,.2,1)!important;transition-duration:.15s!important}.transition{transition-property:color,background-color,border-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.tooltip{position:absolute;z-index:1070;display:block;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px}.tooltip.top-left .tooltip-arrow,.tooltip.top-right .tooltip-arrow{bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{left:5px}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:#0000;border-style:solid}.hover\:bg-blue-400:hover{--tw-bg-opacity:1;background-color:rgb(96 165 250/var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.hover\:bg-gray-300:hover{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity))}.hover\:bg-red-600:hover{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity))}.hover\:bg-seqorange:hover{--tw-bg-opacity:1;background-color:rgb(199 79 19/var(--tw-bg-opacity))}.hover\:bg-sky-400:hover{--tw-bg-opacity:1;background-color:rgb(56 189 248/var(--tw-bg-opacity))}.hover\:bg-sky-500:hover{--tw-bg-opacity:1;background-color:rgb(14 165 233/var(--tw-bg-opacity))}.hover\:bg-yellow-100:hover{--tw-bg-opacity:1;background-color:rgb(254 249 195/var(--tw-bg-opacity))}.hover\:font-bold:hover{font-weight:700}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-1:focus,.focus\:ring-2:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-seqorange:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(199 79 19/var(--tw-ring-opacity))}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.group:hover .group-hover\:visible{visibility:visible}@media (min-width:640px){.sm\:my-8{margin-top:2rem;margin-bottom:2rem}.sm\:ml-3{margin-left:.75rem}.sm\:block{display:block}.sm\:inline-block{display:inline-block}.sm\:flex{display:flex}.sm\:h-screen{height:100vh}.sm\:w-auto{width:auto}.sm\:w-full{width:100%}.sm\:max-w-screen-md{max-width:768px}.sm\:flex-row-reverse{flex-direction:row-reverse}.sm\:items-center{align-items:center}.sm\:p-0{padding:0}.sm\:p-6{padding:1.5rem}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pb-4{padding-bottom:1rem}.sm\:align-middle{vertical-align:middle}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:768px){.md\:my-2{margin-top:.5rem;margin-bottom:.5rem}.md\:flex{display:flex}.md\:w-auto{width:auto}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}}@media (min-width:1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}}
1
+ /*! tailwindcss v3.4.3 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.\!container{width:100%!important}.container{width:100%}@media (min-width:640px){.\!container{max-width:640px!important}.container{max-width:640px}}@media (min-width:768px){.\!container{max-width:768px!important}.container{max-width:768px}}@media (min-width:1024px){.\!container{max-width:1024px!important}.container{max-width:1024px}}@media (min-width:1280px){.\!container{max-width:1280px!important}.container{max-width:1280px}}@media (min-width:1536px){.\!container{max-width:1536px!important}.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.visible{visibility:visible}.invisible{visibility:hidden}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.bottom-0{bottom:0}.bottom-12{bottom:3rem}.end-1{inset-inline-end:.25rem}.left-0{left:0}.left-1{left:.25rem}.right-0{right:0}.right-2{right:.5rem}.start-1{inset-inline-start:.25rem}.top-0{top:0}.top-2{top:.5rem}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.col-span-2{grid-column:span 2/span 2}.mx-1{margin-left:.25rem;margin-right:.25rem}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-4{margin-top:1rem;margin-bottom:1rem}.my-6{margin-top:1.5rem;margin-bottom:1.5rem}.-mb-px{margin-bottom:-1px}.-ml-8{margin-left:-2rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.mr-1{margin-right:.25rem}.mr-2{margin-right:.5rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-5{margin-top:1.25rem}.\!block{display:block!important}.block{display:block}.inline-block{display:inline-block}.\!inline{display:inline!important}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.table-caption{display:table-caption}.table-cell{display:table-cell}.grid{display:grid}.contents{display:contents}.list-item{display:list-item}.\!hidden{display:none!important}.hidden{display:none}.h-2{height:.5rem}.h-4{height:1rem}.h-6{height:1.5rem}.h-8{height:2rem}.h-full{height:100%}.max-h-8{max-height:2rem}.max-h-96{max-height:24rem}.min-h-screen{min-height:100vh}.w-3{width:.75rem}.w-4{width:1rem}.w-48{width:12rem}.w-6{width:1.5rem}.w-auto{width:auto}.w-full{width:100%}.max-w-7xl{max-width:80rem}.max-w-screen-xl{max-width:1280px}.flex-auto{flex:1 1 auto}.flex-none{flex:none}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.flex-grow,.grow{flex-grow:1}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.cursor-help{cursor:help}.cursor-pointer{cursor:pointer}.resize{resize:both}.list-inside{list-style-position:inside}.list-disc{list-style-type:disc}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.gap-4{gap:1rem}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-gray-100>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(243 244 246/var(--tw-divide-opacity))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.overflow-y-scroll{overflow-y:scroll}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rounded{border-radius:.25rem}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-l-lg{border-top-left-radius:.5rem;border-bottom-left-radius:.5rem}.rounded-r-md{border-top-right-radius:.375rem;border-bottom-right-radius:.375rem}.rounded-t{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.rounded-tr-lg{border-top-right-radius:.5rem}.border{border-width:1px}.border-x{border-left-width:1px;border-right-width:1px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-blue-800{--tw-border-opacity:1;border-color:rgb(30 64 175/var(--tw-border-opacity))}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity))}.border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity))}.border-seqblue{--tw-border-opacity:1;border-color:rgb(27 85 122/var(--tw-border-opacity))}.border-seqorange{--tw-border-opacity:1;border-color:rgb(199 79 19/var(--tw-border-opacity))}.border-transparent{border-color:#0000}.bg-blue-100{--tw-bg-opacity:1;background-color:rgb(219 234 254/var(--tw-bg-opacity))}.bg-blue-300{--tw-bg-opacity:1;background-color:rgb(147 197 253/var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity))}.bg-gray-200{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity))}.bg-gray-500{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity))}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity))}.bg-seqblue{--tw-bg-opacity:1;background-color:rgb(27 85 122/var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.bg-yellow-100{--tw-bg-opacity:1;background-color:rgb(254 249 195/var(--tw-bg-opacity))}.bg-opacity-75{--tw-bg-opacity:0.75}.bg-gradient-to-t{background-image:linear-gradient(to top,var(--tw-gradient-stops))}.from-white\/90{--tw-gradient-from:#ffffffe6 var(--tw-gradient-from-position);--tw-gradient-to:#fff0 var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.to-gray-100\/90{--tw-gradient-to:#f3f4f6e6 var(--tw-gradient-to-position)}.fill-current{fill:currentColor}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-4{padding:1rem}.px-1{padding-left:.25rem;padding-right:.25rem}.px-16{padding-left:4rem;padding-right:4rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pb-20{padding-bottom:5rem}.pb-4{padding-bottom:1rem}.pr-2{padding-right:.5rem}.pt-2{padding-top:.5rem}.pt-4{padding-top:1rem}.pt-5{padding-top:1.25rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.align-bottom{vertical-align:bottom}.font-mono{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-2xl{font-size:1.5rem;line-height:2rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-thin{font-weight:100}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.capitalize{text-transform:capitalize}.italic{font-style:italic}.ordinal{--tw-ordinal:ordinal;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-6{line-height:1.5rem}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity))}.text-red-800{--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity))}.text-seqblue{--tw-text-opacity:1;color:rgb(27 85 122/var(--tw-text-opacity))}.text-seqorange{--tw-text-opacity:1;color:rgb(199 79 19/var(--tw-text-opacity))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.underline{-webkit-text-decoration-line:underline;text-decoration-line:underline}.\!shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a!important;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)!important;box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)!important}.shadow{--tw-shadow:0 1px 3px 0 #0000001a,0 1px 2px -1px #0000001a;--tw-shadow-colored:0 1px 3px 0 var(--tw-shadow-color),0 1px 2px -1px var(--tw-shadow-color)}.shadow,.shadow-2xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px #00000040;--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.shadow-lg{--tw-shadow:0 10px 15px -3px #0000001a,0 4px 6px -4px #0000001a;--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-lg,.shadow-sm{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow:0 1px 2px 0 #0000000d;--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-xl{--tw-shadow:0 20px 25px -5px #0000001a,0 8px 10px -6px #0000001a;--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid #0000;outline-offset:2px}.outline{outline-style:solid}.ring{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.blur{--tw-blur:blur(8px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.\!invert{--tw-invert:invert(100%)!important;filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.invert{--tw-invert:invert(100%);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.\!filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)!important}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.\!transition{transition-property:color,background-color,border-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-text-decoration-color,-webkit-backdrop-filter!important;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter!important;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-text-decoration-color,-webkit-backdrop-filter!important;transition-timing-function:cubic-bezier(.4,0,.2,1)!important;transition-duration:.15s!important}.transition{transition-property:color,background-color,border-color,fill,stroke,opacity,box-shadow,transform,filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter,-webkit-text-decoration-color,-webkit-backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.tooltip{position:absolute;z-index:1070;display:block;font-family:Helvetica Neue,Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px}.tooltip.top-left .tooltip-arrow,.tooltip.top-right .tooltip-arrow{bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{left:5px}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:#0000;border-style:solid}.hover\:bg-blue-400:hover{--tw-bg-opacity:1;background-color:rgb(96 165 250/var(--tw-bg-opacity))}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity))}.hover\:bg-gray-300:hover{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity))}.hover\:bg-red-600:hover{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity))}.hover\:bg-seqorange:hover{--tw-bg-opacity:1;background-color:rgb(199 79 19/var(--tw-bg-opacity))}.hover\:bg-sky-400:hover{--tw-bg-opacity:1;background-color:rgb(56 189 248/var(--tw-bg-opacity))}.hover\:bg-sky-500:hover{--tw-bg-opacity:1;background-color:rgb(14 165 233/var(--tw-bg-opacity))}.hover\:bg-yellow-100:hover{--tw-bg-opacity:1;background-color:rgb(254 249 195/var(--tw-bg-opacity))}.hover\:font-bold:hover{font-weight:700}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.focus\:outline-none:focus{outline:2px solid #0000;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-1:focus,.focus\:ring-2:focus{box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color)}.focus\:ring-seqorange:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(199 79 19/var(--tw-ring-opacity))}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.group:hover .group-hover\:visible{visibility:visible}@media (min-width:640px){.sm\:my-8{margin-top:2rem;margin-bottom:2rem}.sm\:ml-3{margin-left:.75rem}.sm\:mt-0{margin-top:0}.sm\:block{display:block}.sm\:inline-block{display:inline-block}.sm\:flex{display:flex}.sm\:h-screen{height:100vh}.sm\:w-auto{width:auto}.sm\:w-full{width:100%}.sm\:max-w-screen-md{max-width:768px}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:flex-row-reverse{flex-direction:row-reverse}.sm\:items-center{align-items:center}.sm\:p-0{padding:0}.sm\:p-6{padding:1.5rem}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pb-4{padding-bottom:1rem}.sm\:align-middle{vertical-align:middle}.sm\:text-sm{font-size:.875rem;line-height:1.25rem}}@media (min-width:768px){.md\:my-2{margin-top:.5rem;margin-bottom:.5rem}.md\:flex{display:flex}.md\:w-auto{width:auto}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.md\:divide-x>:not([hidden])~:not([hidden]){--tw-divide-x-reverse:0;border-right-width:calc(1px*var(--tw-divide-x-reverse));border-left-width:calc(1px*(1 - var(--tw-divide-x-reverse)))}.md\:divide-y-0>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(0px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(0px*var(--tw-divide-y-reverse))}.md\:pl-2{padding-left:.5rem}.md\:pt-0{padding-top:0}}@media (min-width:1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}}
@@ -7,6 +7,7 @@ export default function downloadFASTA(sequence_ids, database_ids) {
7
7
  var form = $('<form/>').attr('method', 'post').attr('action', 'get_sequence');
8
8
  addField('sequence_ids', sequence_ids);
9
9
  addField('database_ids', database_ids);
10
+ addField('_csrf', document.querySelector('meta[name="_csrf"]').content);
10
11
  form.appendTo('body').submit().remove();
11
12
 
12
13
  function addField(name, val) {
data/public/js/form.js CHANGED
@@ -203,6 +203,7 @@ export class Form extends Component {
203
203
  </div>
204
204
 
205
205
  <form id="blast" ref={this.formRef} onSubmit={this.handleFormSubmission}>
206
+ <input type="hidden" name="_csrf" value={document.querySelector('meta[name="_csrf"]').content} />
206
207
  <div className="px-4">
207
208
  <SearchQueryWidget ref="query" onSequenceTypeChanged={this.handleSequenceTypeChanged} onSequenceChanged={this.handleSequenceChanged}/>
208
209
 
data/public/js/sidebar.js CHANGED
@@ -350,6 +350,15 @@ export default class extends Component {
350
350
  </a>
351
351
  </li>
352
352
  }
353
+ {
354
+ !this.props.data.imported_xml && <li>
355
+ <a className="btn-link download" data-toggle="tooltip"
356
+ title="Results in pairwise format."
357
+ href={'download/' + this.props.data.search_id + '.pairwise'}>
358
+ Full Pairwise report
359
+ </a>
360
+ </li>
361
+ }
353
362
  <DownloadLinks imported_xml={this.props.data.imported_xml} search_id={this.props.data.search_id} />
354
363
  </ul>
355
364
  </div>
@@ -13,6 +13,20 @@ export const setMockJSONResult = (result) => {
13
13
  };
14
14
 
15
15
  describe('ADVANCED PARAMETERS', () => {
16
+ let csrfMetaTag;
17
+
18
+ beforeEach(() => {
19
+ csrfMetaTag = document.createElement('meta');
20
+ csrfMetaTag.setAttribute('name', '_csrf');
21
+ csrfMetaTag.setAttribute('content', 'test-token');
22
+ document.head.appendChild(csrfMetaTag);
23
+ });
24
+
25
+ afterEach(() => {
26
+ // Remove the CSRF meta tag after each test to clean up
27
+ document.head.removeChild(csrfMetaTag);
28
+ });
29
+
16
30
  const getInputElement = () => screen.getByRole('textbox', { name: '' });
17
31
  test('should not render the link to advanced parameters modal if blast algorithm is unknown', () => {
18
32
  setMockJSONResult(data);
@@ -37,6 +51,20 @@ describe('ADVANCED PARAMETERS', () => {
37
51
  });
38
52
 
39
53
  describe('query stats', () => {
54
+ let csrfMetaTag;
55
+
56
+ beforeEach(() => {
57
+ csrfMetaTag = document.createElement('meta');
58
+ csrfMetaTag.setAttribute('name', '_csrf');
59
+ csrfMetaTag.setAttribute('content', 'test-token');
60
+ document.head.appendChild(csrfMetaTag);
61
+ });
62
+
63
+ afterEach(() => {
64
+ // Remove the CSRF meta tag after each test to clean up
65
+ document.head.removeChild(csrfMetaTag);
66
+ });
67
+
40
68
  const getInputElement = () => screen.getByRole('textbox', { name: '' });
41
69
 
42
70
  test('should render the query stats modal when clicked', () => {
@@ -10,12 +10,23 @@ let container;
10
10
  let inputEl;
11
11
 
12
12
  describe('SEARCH COMPONENT', () => {
13
+ let csrfMetaTag;
14
+
13
15
  beforeEach(() => {
16
+ csrfMetaTag = document.createElement('meta');
17
+ csrfMetaTag.setAttribute('name', '_csrf');
18
+ csrfMetaTag.setAttribute('content', 'test-token');
19
+ document.head.appendChild(csrfMetaTag);
14
20
  container = render(<Form onSequenceTypeChanged={() => { }
15
21
  } />).container;
16
22
  inputEl = screen.getByRole('textbox', { name: '' });
17
23
  });
18
24
 
25
+ afterEach(() => {
26
+ // Remove the CSRF meta tag after each test to clean up
27
+ document.head.removeChild(csrfMetaTag);
28
+ });
29
+
19
30
  test('should render the search component textarea', () => {
20
31
  expect(inputEl).toBeInTheDocument();
21
32
  });
@@ -60,7 +60,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
60
60
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
61
61
 
62
62
  "use strict";
63
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ downloadFASTA)\n/* harmony export */ });\n/* provided dependency */ var $ = __webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\");\n/**\n * Dynamically create form and submit.\n *\n * Author: Filip Ter\n */\nfunction downloadFASTA(sequence_ids, database_ids) {\n var form = $('<form/>').attr('method', 'post').attr('action', 'get_sequence');\n addField('sequence_ids', sequence_ids);\n addField('database_ids', database_ids);\n form.appendTo('body').submit().remove();\n function addField(name, val) {\n form.append($('<input>').attr('type', 'hidden').attr('name', name).val(val));\n }\n}\n\n//# sourceURL=webpack://SequenceServer/./public/js/download_fasta.js?");
63
+ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ downloadFASTA)\n/* harmony export */ });\n/* provided dependency */ var $ = __webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\");\n/**\n * Dynamically create form and submit.\n *\n * Author: Filip Ter\n */\nfunction downloadFASTA(sequence_ids, database_ids) {\n var form = $('<form/>').attr('method', 'post').attr('action', 'get_sequence');\n addField('sequence_ids', sequence_ids);\n addField('database_ids', database_ids);\n addField('_csrf', document.querySelector('meta[name=\"_csrf\"]').content);\n form.appendTo('body').submit().remove();\n function addField(name, val) {\n form.append($('<input>').attr('type', 'hidden').attr('name', name).val(val));\n }\n}\n\n//# sourceURL=webpack://SequenceServer/./public/js/download_fasta.js?");
64
64
 
65
65
  /***/ }),
66
66
 
@@ -302,7 +302,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
302
302
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
303
303
 
304
304
  "use strict";
305
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ _default)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var underscore__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! underscore */ \"./node_modules/underscore/modules/index-all.js\");\n/* harmony import */ var _download_fasta__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./download_fasta */ \"./public/js/download_fasta.js\");\n/* harmony import */ var _mailto__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./mailto */ \"./public/js/mailto.js\");\n/* harmony import */ var _cloud_share_modal__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./cloud_share_modal */ \"./public/js/cloud_share_modal.js\");\n/* harmony import */ var download_links__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! download_links */ \"./public/js/null_plugins/download_links.js\");\n/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! react/jsx-runtime */ \"./node_modules/react/jsx-runtime.js\");\n/* provided dependency */ var $ = __webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\");\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; return _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && \"function\" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }, _typeof(obj); }\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, \"prototype\", { writable: false }); return Constructor; }\nfunction _callSuper(_this, derived, args) {\n function isNativeReflectConstruct() {\n if (typeof Reflect === \"undefined\" || !Reflect.construct) return false;\n if (Reflect.construct.sham) return false;\n if (typeof Proxy === \"function\") return true;\n try {\n return !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));\n } catch (e) {\n return false;\n }\n }\n derived = _getPrototypeOf(derived);\n return _possibleConstructorReturn(_this, isNativeReflectConstruct() ? Reflect.construct(derived, args || [], _getPrototypeOf(_this).constructor) : derived.apply(_this, args));\n}\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } else if (call !== void 0) { throw new TypeError(\"Derived constructors may only return object or undefined\"); } return _assertThisInitialized(self); }\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, \"prototype\", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\n\n\n\n\n\n/**\n * checks whether code is being run by jest\n */\n// eslint-disable-next-line no-undef\n\nvar isTestMode = function isTestMode() {\n return ({}).JEST_WORKER_ID !== undefined || \"development\" === 'test';\n};\n/**\n * Renders links for downloading hit information in different formats.\n * Renders links for navigating to each query.\n */\nvar _default = /*#__PURE__*/function (_Component) {\n function _default(props) {\n var _this2;\n _classCallCheck(this, _default);\n _this2 = _callSuper(this, _default, [props]);\n _this2.downloadFastaOfAll = _this2.downloadFastaOfAll.bind(_this2);\n _this2.downloadFastaOfSelected = _this2.downloadFastaOfSelected.bind(_this2);\n _this2.topPanelJSX = _this2.topPanelJSX.bind(_this2);\n _this2.summaryString = _this2.summaryString.bind(_this2);\n _this2.indexJSX = _this2.indexJSX.bind(_this2);\n _this2.downloadsPanelJSX = _this2.downloadsPanelJSX.bind(_this2);\n _this2.handleQueryIndexChange = _this2.handleQueryIndexChange.bind(_this2);\n _this2.isElementInViewPort = _this2.isElementInViewPort.bind(_this2);\n _this2.setVisibleQueryIndex = _this2.setVisibleQueryIndex.bind(_this2);\n _this2.debounceScrolling = _this2.debounceScrolling.bind(_this2);\n _this2.scrollListener = _this2.scrollListener.bind(_this2);\n _this2.copyURL = _this2.copyURL.bind(_this2);\n _this2.shareCloudInit = _this2.shareCloudInit.bind(_this2);\n _this2.sharingPanelJSX = _this2.sharingPanelJSX.bind(_this2);\n _this2.timeout = null;\n _this2.queryElems = [];\n _this2.state = {\n queryIndex: 1\n };\n return _this2;\n }\n _inherits(_default, _Component);\n return _createClass(_default, [{\n key: \"componentDidMount\",\n value: function componentDidMount() {\n /**\n * Fixes tooltips in the sidebar, allows tooltip display on click\n */\n $(function () {\n $('.sidebar [data-toggle=\"tooltip\"]').tooltip({\n placement: 'right'\n });\n $('#copyURL').tooltip({\n title: 'Copied!',\n trigger: 'click',\n placement: 'right',\n delay: 0\n });\n });\n\n //keep track of the current queryIndex so it doesn't get lost on page reload\n var urlMatch = window.location.href.match(/#Query_(\\d+)/);\n if (urlMatch && urlMatch.length > 1) {\n var queryNumber = +urlMatch[1];\n var index = this.props.data.queries.findIndex(function (query) {\n return query.number === queryNumber;\n });\n this.setState({\n queryIndex: index + 1\n });\n }\n window.addEventListener('scroll', this.scrollListener);\n $('a[href^=\"#Query_\"]').on('click', this.animateAnchorElements);\n }\n }, {\n key: \"componentWillUnmount\",\n value: function componentWillUnmount() {\n window.removeEventListener('scroll', this.scrollListener);\n }\n }, {\n key: \"componentDidUpdate\",\n value: function componentDidUpdate(prevProps) {\n if (this.props.allQueriesLoaded && !prevProps.allQueriesLoaded) {\n /**\n * storing all query elements in this variable once they all become available so we don't have to fetch them all over again\n */\n this.queryElems = Array.from(document.querySelectorAll('.resultn'));\n }\n }\n\n /**\n * to avoid unnecessary computations, we debounce the scroll listener so it only fires after user has stopped scrolling for some milliseconds\n */\n }, {\n key: \"scrollListener\",\n value: function scrollListener() {\n this.debounceScrolling(this.setVisibleQueryIndex, 500);\n }\n }, {\n key: \"debounceScrolling\",\n value: function debounceScrolling(callback, timer) {\n if (this.timeout) {\n clearTimeout(this.timeout);\n }\n this.timeout = setTimeout(callback, timer);\n }\n\n /**\n * This method makes the page aware of what query is visible so that clicking previous / next button at any point\n * navigates to the proper query\n */\n }, {\n key: \"setVisibleQueryIndex\",\n value: function setVisibleQueryIndex() {\n var queryElems = this.queryElems.length ? this.queryElems : Array.from(document.querySelectorAll('.resultn'));\n var hits = Array.from(document.querySelectorAll('.hit[id^=Query_]'));\n // get the first visible element and marks it as the current query\n var topmostEl = queryElems.find(this.isElementInViewPort) || hits.find(this.isElementInViewPort);\n if (topmostEl) {\n var queryIndex = Number(topmostEl.id.match(/Query_(\\d+)/)[1]);\n var hash = \"#Query_\".concat(queryIndex);\n // if we can guarantee that the browser can handle change in url hash without the page jumping,\n // then we update the url hash after scroll. else, hash is only updated on click of next or prev button\n if (window.history.pushState) {\n window.history.pushState(null, null, hash);\n }\n this.setState({\n queryIndex: queryIndex\n });\n }\n }\n }, {\n key: \"animateAnchorElements\",\n value: function animateAnchorElements(e) {\n // allow normal behavior in test mode to prevent warnings or errors from jquery\n if (isTestMode()) return;\n e.preventDefault();\n $('html, body').animate({\n scrollTop: $(this.hash).offset().top\n }, 300);\n if (window.history.pushState) {\n window.history.pushState(null, null, this.hash);\n } else {\n window.location.hash = this.hash;\n }\n }\n }, {\n key: \"isElementInViewPort\",\n value: function isElementInViewPort(elem) {\n var _elem$getBoundingClie = elem.getBoundingClientRect(),\n top = _elem$getBoundingClie.top,\n left = _elem$getBoundingClie.left,\n right = _elem$getBoundingClie.right,\n bottom = _elem$getBoundingClie.bottom;\n return top >= 0 && left >= 0 && bottom <= (window.innerHeight || document.documentElement.clientHeight) && right <= (window.innerWidth || document.documentElement.clientWidth);\n }\n /**\n * Clear sessionStorage - useful to initiate a new search in the same tab.\n * Passing sessionStorage.clear directly as onclick callback didn't work\n * (on macOS Chrome).\n */\n }, {\n key: \"clearSession\",\n value: function clearSession() {\n sessionStorage.clear();\n }\n /**\n *\n * handle next and previous query button clicks\n */\n }, {\n key: \"handleQueryIndexChange\",\n value: function handleQueryIndexChange(nextQuery) {\n if (nextQuery < 1 || nextQuery > this.props.data.queries.length) return;\n var anchorEl = document.createElement('a');\n //indexing at [nextQuery - 1] because array is 0-indexed\n anchorEl.setAttribute('href', '#Query_' + this.props.data.queries[nextQuery - 1].number);\n anchorEl.setAttribute('hidden', true);\n document.body.appendChild(anchorEl);\n // add smooth scrolling animation with jquery\n $(anchorEl).on('click', this.animateAnchorElements);\n anchorEl.click();\n document.body.removeChild(anchorEl);\n this.setState({\n queryIndex: nextQuery\n });\n }\n /**\n * Event-handler for downloading fasta of all hits.\n */\n }, {\n key: \"downloadFastaOfAll\",\n value: function downloadFastaOfAll() {\n var sequence_ids = [];\n this.props.data.queries.forEach(function (query) {\n return query.hits.forEach(function (hit) {\n return sequence_ids.push(hit.id);\n });\n });\n var database_ids = this.props.data.querydb.map(function (querydb) {\n return querydb.id;\n });\n (0,_download_fasta__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(sequence_ids, database_ids);\n return false;\n }\n\n /**\n * Handles downloading fasta of selected hits.\n */\n }, {\n key: \"downloadFastaOfSelected\",\n value: function downloadFastaOfSelected() {\n var sequence_ids = $('.hit-links :checkbox:checked').map(function () {\n return this.value;\n }).get();\n if (sequence_ids.length === 0) {\n return false;\n }\n var database_ids = underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].map(this.props.data.querydb, underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].iteratee('id'));\n (0,_download_fasta__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(sequence_ids, database_ids);\n return false;\n }\n\n /**\n * Handles copying the URL into the user's clipboard. Modified from: https://stackoverflow.com/a/49618964/18117380\n * Hides the 'Copied!' tooltip after 3 seconds\n */\n }, {\n key: \"copyURL\",\n value: function copyURL() {\n var element = document.createElement('input');\n var url = window.location.href;\n document.body.appendChild(element);\n element.value = url;\n element.select();\n document.execCommand('copy');\n document.body.removeChild(element);\n setTimeout(function () {\n $('#copyURL')._tooltip('hide');\n }, 3000);\n }\n }, {\n key: \"shareCloudInit\",\n value: function shareCloudInit() {\n this.refs.cloudShareModal.show();\n }\n }, {\n key: \"topPanelJSX\",\n value: function topPanelJSX() {\n var path = location.pathname.split('/');\n // Get job id.\n var job_id = path.pop();\n // Deriving rootURL this way is required for subURI deployments\n // - we cannot just send to '/'.\n var rootURL = path.join('/');\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"div\", {\n className: \"sidebar-top-panel\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"div\", {\n className: \"section-header-sidebar\",\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"h4\", {\n children: this.summaryString()\n })\n }), this.props.data.queries.length > 12 && this.queryIndexButtons(), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"div\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"a\", {\n href: \"\".concat(rootURL, \"/?job_id=\").concat(job_id),\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"i\", {\n className: \"fa fa-pencil\"\n }), \" Edit search\"]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"span\", {\n className: \"line\",\n children: \"|\"\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"a\", {\n href: \"\".concat(rootURL, \"/\"),\n onClick: this.clearSession,\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"i\", {\n className: \"fa fa-file-o\"\n }), \" New search\"]\n })]\n }), this.props.shouldShowIndex && this.indexJSX()]\n });\n }\n }, {\n key: \"summaryString\",\n value: function summaryString() {\n var program = this.props.data.program;\n var numqueries = this.props.data.queries.length;\n var numquerydb = this.props.data.querydb.length;\n return program.toUpperCase() + ': ' + numqueries + ' ' + (numqueries > 1 ? 'queries' : 'query') + ', ' + numquerydb + ' ' + (numquerydb > 1 ? 'databases' : 'database');\n }\n }, {\n key: \"queryIndexButtons\",\n value: function queryIndexButtons() {\n var _this3 = this;\n var buttonStyle = {\n outline: 'none',\n border: 'none',\n background: 'none'\n };\n var buttonClasses = 'btn-link nowrap-ellipsis hover-bold';\n var handlePreviousBtnClick = function handlePreviousBtnClick() {\n return _this3.handleQueryIndexChange(_this3.state.queryIndex - 1);\n };\n var handleNextBtnClick = function handleNextBtnClick() {\n return _this3.handleQueryIndexChange(_this3.state.queryIndex + 1);\n };\n\n // eslint-disable-next-line no-unused-vars\n var NavButton = function NavButton(_ref) {\n var text = _ref.text,\n onClick = _ref.onClick;\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"button\", {\n className: buttonClasses,\n onClick: onClick,\n style: buttonStyle,\n children: text\n });\n };\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"div\", {\n style: {\n display: 'flex',\n width: '100%',\n margin: '7px 0'\n },\n children: [this.state.queryIndex > 1 && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(NavButton, {\n text: \"Previous Query\",\n onClick: handlePreviousBtnClick\n }), this.state.queryIndex > 1 && this.state.queryIndex < this.props.data.queries.length && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"span\", {\n className: \"line\",\n children: \"|\"\n }), this.state.queryIndex < this.props.data.queries.length && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(NavButton, {\n onClick: handleNextBtnClick,\n text: \"Next Query\"\n })]\n });\n }\n }, {\n key: \"indexJSX\",\n value: function indexJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"ul\", {\n className: \"nav hover-reset active-bold\",\n children: [\" \", underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].map(this.props.data.queries, function (query) {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"a\", {\n className: \"btn-link nowrap-ellipsis hover-bold\",\n title: 'Query= ' + query.id + ' ' + query.title,\n href: '#Query_' + query.number,\n children: 'Query= ' + query.id\n })\n }, 'Side_bar_' + query.id);\n })]\n });\n }\n }, {\n key: \"downloadsPanelJSX\",\n value: function downloadsPanelJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"div\", {\n className: \"downloads\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"div\", {\n className: \"section-header-sidebar\",\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"h4\", {\n children: \"Download FASTA, XML, TSV\"\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"ul\", {\n className: \"nav\",\n children: [!(this.props.data.imported_xml || this.props.data.non_parse_seqids) && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"a\", {\n href: \"#\",\n className: \"btn-link download-fasta-of-all \".concat(!this.props.atLeastOneHit && 'disabled'),\n onClick: this.downloadFastaOfAll,\n children: \"FASTA of all hits\"\n })\n }), !(this.props.data.imported_xml || this.props.data.non_parse_seqids) && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"a\", {\n href: \"#\",\n className: \"btn-link download-fasta-of-selected disabled\",\n onClick: this.downloadFastaOfSelected,\n children: [\"FASTA of \", /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"span\", {\n className: \"text-bold\"\n }), \" selected hit(s)\"]\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"a\", {\n href: \"#\",\n className: \"btn-link download-alignment-of-all \".concat(!this.props.atLeastOneHit && 'disabled'),\n children: \"Alignment of all hits\"\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"a\", {\n href: \"#\",\n className: \"btn-link download-alignment-of-selected disabled\",\n children: [\"Alignment of \", /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"span\", {\n className: \"text-bold\"\n }), \" selected hit(s)\"]\n })\n }), !this.props.data.imported_xml && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"a\", {\n className: \"btn-link download\",\n \"data-toggle\": \"tooltip\",\n title: \"15 columns: query and subject ID; scientific name, alignment length, mismatches, gaps, identity, start and end coordinates, e value, bitscore, query coverage per subject and per HSP.\",\n href: 'download/' + this.props.data.search_id + '.std_tsv',\n children: \"Standard tabular report\"\n })\n }), !this.props.data.imported_xml && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"a\", {\n className: \"btn-link download\",\n \"data-toggle\": \"tooltip\",\n title: \"44 columns: query and subject ID, GI, accessions, and length; alignment details; taxonomy details of subject sequence(s) and query coverage per subject and per HSP.\",\n href: 'download/' + this.props.data.search_id + '.full_tsv',\n children: \"Full tabular report\"\n })\n }), !this.props.data.imported_xml && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"a\", {\n className: \"btn-link download\",\n \"data-toggle\": \"tooltip\",\n title: \"Results in XML format.\",\n href: 'download/' + this.props.data.search_id + '.xml',\n children: \"Full XML report\"\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(download_links__WEBPACK_IMPORTED_MODULE_5__[\"default\"], {\n imported_xml: this.props.data.imported_xml,\n search_id: this.props.data.search_id\n })]\n })]\n });\n }\n }, {\n key: \"sharingPanelJSX\",\n value: function sharingPanelJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"div\", {\n className: \"sharing-panel\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"div\", {\n className: \"section-header-sidebar\",\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"h4\", {\n children: \"Share results\"\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"ul\", {\n className: \"nav\",\n children: [!this.props.cloudSharingEnabled && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"a\", {\n id: \"copyURL\",\n className: \"btn-link copy-URL cursor-pointer\",\n \"data-toggle\": \"tooltip\",\n onClick: this.copyURL,\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"i\", {\n className: \"fa fa-copy\"\n }), \" Copy URL to clipboard\"]\n })\n }), !this.props.cloudSharingEnabled && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"a\", {\n id: \"sendEmail\",\n className: \"btn-link email-URL cursor-pointer\",\n \"data-toggle\": \"tooltip\",\n title: \"Send by email\",\n href: (0,_mailto__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(this.props.data.querydb, this.props.data.program, this.props.data.queries.length, window.location.href),\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"i\", {\n className: \"fa fa-envelope\"\n }), \" Send by email\"]\n })\n }), this.props.cloudSharingEnabled && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"button\", {\n className: \"btn-link cloud-Post cursor-pointer\",\n \"data-toggle\": \"tooltip\",\n title: \"Upload results to SequenceServer Cloud where it will become accessable to everyone who has a link.\",\n onClick: this.shareCloudInit,\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"i\", {\n className: \"fa fa-cloud\"\n }), \" Share to cloud\"]\n })\n })]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(_cloud_share_modal__WEBPACK_IMPORTED_MODULE_4__[\"default\"], {\n ref: \"cloudShareModal\",\n querydb: this.props.data.querydb,\n program: this.props.data.program,\n queryLength: this.props.data.queries.length\n })]\n });\n }\n }, {\n key: \"render\",\n value: function render() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"div\", {\n className: \"sidebar\",\n children: [this.topPanelJSX(), this.downloadsPanelJSX(), this.sharingPanelJSX(), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"div\", {\n className: \"referral-panel\",\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"div\", {\n className: \"section-header-sidebar\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"h4\", {\n children: \"Recommend SequenceServer\"\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"p\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"a\", {\n href: \"https://sequenceserver.com/referral-program\",\n target: \"_blank\",\n children: \"Earn up to $100 per signup\"\n })\n })]\n })\n })]\n });\n }\n }]);\n}(react__WEBPACK_IMPORTED_MODULE_0__.Component);\n\n\n//# sourceURL=webpack://SequenceServer/./public/js/sidebar.js?");
305
+ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ _default)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var underscore__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! underscore */ \"./node_modules/underscore/modules/index-all.js\");\n/* harmony import */ var _download_fasta__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./download_fasta */ \"./public/js/download_fasta.js\");\n/* harmony import */ var _mailto__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./mailto */ \"./public/js/mailto.js\");\n/* harmony import */ var _cloud_share_modal__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./cloud_share_modal */ \"./public/js/cloud_share_modal.js\");\n/* harmony import */ var download_links__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! download_links */ \"./public/js/null_plugins/download_links.js\");\n/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! react/jsx-runtime */ \"./node_modules/react/jsx-runtime.js\");\n/* provided dependency */ var $ = __webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\");\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; return _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && \"function\" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }, _typeof(obj); }\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, \"prototype\", { writable: false }); return Constructor; }\nfunction _callSuper(_this, derived, args) {\n function isNativeReflectConstruct() {\n if (typeof Reflect === \"undefined\" || !Reflect.construct) return false;\n if (Reflect.construct.sham) return false;\n if (typeof Proxy === \"function\") return true;\n try {\n return !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));\n } catch (e) {\n return false;\n }\n }\n derived = _getPrototypeOf(derived);\n return _possibleConstructorReturn(_this, isNativeReflectConstruct() ? Reflect.construct(derived, args || [], _getPrototypeOf(_this).constructor) : derived.apply(_this, args));\n}\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } else if (call !== void 0) { throw new TypeError(\"Derived constructors may only return object or undefined\"); } return _assertThisInitialized(self); }\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, \"prototype\", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\n\n\n\n\n\n/**\n * checks whether code is being run by jest\n */\n// eslint-disable-next-line no-undef\n\nvar isTestMode = function isTestMode() {\n return ({}).JEST_WORKER_ID !== undefined || \"development\" === 'test';\n};\n/**\n * Renders links for downloading hit information in different formats.\n * Renders links for navigating to each query.\n */\nvar _default = /*#__PURE__*/function (_Component) {\n function _default(props) {\n var _this2;\n _classCallCheck(this, _default);\n _this2 = _callSuper(this, _default, [props]);\n _this2.downloadFastaOfAll = _this2.downloadFastaOfAll.bind(_this2);\n _this2.downloadFastaOfSelected = _this2.downloadFastaOfSelected.bind(_this2);\n _this2.topPanelJSX = _this2.topPanelJSX.bind(_this2);\n _this2.summaryString = _this2.summaryString.bind(_this2);\n _this2.indexJSX = _this2.indexJSX.bind(_this2);\n _this2.downloadsPanelJSX = _this2.downloadsPanelJSX.bind(_this2);\n _this2.handleQueryIndexChange = _this2.handleQueryIndexChange.bind(_this2);\n _this2.isElementInViewPort = _this2.isElementInViewPort.bind(_this2);\n _this2.setVisibleQueryIndex = _this2.setVisibleQueryIndex.bind(_this2);\n _this2.debounceScrolling = _this2.debounceScrolling.bind(_this2);\n _this2.scrollListener = _this2.scrollListener.bind(_this2);\n _this2.copyURL = _this2.copyURL.bind(_this2);\n _this2.shareCloudInit = _this2.shareCloudInit.bind(_this2);\n _this2.sharingPanelJSX = _this2.sharingPanelJSX.bind(_this2);\n _this2.timeout = null;\n _this2.queryElems = [];\n _this2.state = {\n queryIndex: 1\n };\n return _this2;\n }\n _inherits(_default, _Component);\n return _createClass(_default, [{\n key: \"componentDidMount\",\n value: function componentDidMount() {\n /**\n * Fixes tooltips in the sidebar, allows tooltip display on click\n */\n $(function () {\n $('.sidebar [data-toggle=\"tooltip\"]').tooltip({\n placement: 'right'\n });\n $('#copyURL').tooltip({\n title: 'Copied!',\n trigger: 'click',\n placement: 'right',\n delay: 0\n });\n });\n\n //keep track of the current queryIndex so it doesn't get lost on page reload\n var urlMatch = window.location.href.match(/#Query_(\\d+)/);\n if (urlMatch && urlMatch.length > 1) {\n var queryNumber = +urlMatch[1];\n var index = this.props.data.queries.findIndex(function (query) {\n return query.number === queryNumber;\n });\n this.setState({\n queryIndex: index + 1\n });\n }\n window.addEventListener('scroll', this.scrollListener);\n $('a[href^=\"#Query_\"]').on('click', this.animateAnchorElements);\n }\n }, {\n key: \"componentWillUnmount\",\n value: function componentWillUnmount() {\n window.removeEventListener('scroll', this.scrollListener);\n }\n }, {\n key: \"componentDidUpdate\",\n value: function componentDidUpdate(prevProps) {\n if (this.props.allQueriesLoaded && !prevProps.allQueriesLoaded) {\n /**\n * storing all query elements in this variable once they all become available so we don't have to fetch them all over again\n */\n this.queryElems = Array.from(document.querySelectorAll('.resultn'));\n }\n }\n\n /**\n * to avoid unnecessary computations, we debounce the scroll listener so it only fires after user has stopped scrolling for some milliseconds\n */\n }, {\n key: \"scrollListener\",\n value: function scrollListener() {\n this.debounceScrolling(this.setVisibleQueryIndex, 500);\n }\n }, {\n key: \"debounceScrolling\",\n value: function debounceScrolling(callback, timer) {\n if (this.timeout) {\n clearTimeout(this.timeout);\n }\n this.timeout = setTimeout(callback, timer);\n }\n\n /**\n * This method makes the page aware of what query is visible so that clicking previous / next button at any point\n * navigates to the proper query\n */\n }, {\n key: \"setVisibleQueryIndex\",\n value: function setVisibleQueryIndex() {\n var queryElems = this.queryElems.length ? this.queryElems : Array.from(document.querySelectorAll('.resultn'));\n var hits = Array.from(document.querySelectorAll('.hit[id^=Query_]'));\n // get the first visible element and marks it as the current query\n var topmostEl = queryElems.find(this.isElementInViewPort) || hits.find(this.isElementInViewPort);\n if (topmostEl) {\n var queryIndex = Number(topmostEl.id.match(/Query_(\\d+)/)[1]);\n var hash = \"#Query_\".concat(queryIndex);\n // if we can guarantee that the browser can handle change in url hash without the page jumping,\n // then we update the url hash after scroll. else, hash is only updated on click of next or prev button\n if (window.history.pushState) {\n window.history.pushState(null, null, hash);\n }\n this.setState({\n queryIndex: queryIndex\n });\n }\n }\n }, {\n key: \"animateAnchorElements\",\n value: function animateAnchorElements(e) {\n // allow normal behavior in test mode to prevent warnings or errors from jquery\n if (isTestMode()) return;\n e.preventDefault();\n $('html, body').animate({\n scrollTop: $(this.hash).offset().top\n }, 300);\n if (window.history.pushState) {\n window.history.pushState(null, null, this.hash);\n } else {\n window.location.hash = this.hash;\n }\n }\n }, {\n key: \"isElementInViewPort\",\n value: function isElementInViewPort(elem) {\n var _elem$getBoundingClie = elem.getBoundingClientRect(),\n top = _elem$getBoundingClie.top,\n left = _elem$getBoundingClie.left,\n right = _elem$getBoundingClie.right,\n bottom = _elem$getBoundingClie.bottom;\n return top >= 0 && left >= 0 && bottom <= (window.innerHeight || document.documentElement.clientHeight) && right <= (window.innerWidth || document.documentElement.clientWidth);\n }\n /**\n * Clear sessionStorage - useful to initiate a new search in the same tab.\n * Passing sessionStorage.clear directly as onclick callback didn't work\n * (on macOS Chrome).\n */\n }, {\n key: \"clearSession\",\n value: function clearSession() {\n sessionStorage.clear();\n }\n /**\n *\n * handle next and previous query button clicks\n */\n }, {\n key: \"handleQueryIndexChange\",\n value: function handleQueryIndexChange(nextQuery) {\n if (nextQuery < 1 || nextQuery > this.props.data.queries.length) return;\n var anchorEl = document.createElement('a');\n //indexing at [nextQuery - 1] because array is 0-indexed\n anchorEl.setAttribute('href', '#Query_' + this.props.data.queries[nextQuery - 1].number);\n anchorEl.setAttribute('hidden', true);\n document.body.appendChild(anchorEl);\n // add smooth scrolling animation with jquery\n $(anchorEl).on('click', this.animateAnchorElements);\n anchorEl.click();\n document.body.removeChild(anchorEl);\n this.setState({\n queryIndex: nextQuery\n });\n }\n /**\n * Event-handler for downloading fasta of all hits.\n */\n }, {\n key: \"downloadFastaOfAll\",\n value: function downloadFastaOfAll() {\n var sequence_ids = [];\n this.props.data.queries.forEach(function (query) {\n return query.hits.forEach(function (hit) {\n return sequence_ids.push(hit.id);\n });\n });\n var database_ids = this.props.data.querydb.map(function (querydb) {\n return querydb.id;\n });\n (0,_download_fasta__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(sequence_ids, database_ids);\n return false;\n }\n\n /**\n * Handles downloading fasta of selected hits.\n */\n }, {\n key: \"downloadFastaOfSelected\",\n value: function downloadFastaOfSelected() {\n var sequence_ids = $('.hit-links :checkbox:checked').map(function () {\n return this.value;\n }).get();\n if (sequence_ids.length === 0) {\n return false;\n }\n var database_ids = underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].map(this.props.data.querydb, underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].iteratee('id'));\n (0,_download_fasta__WEBPACK_IMPORTED_MODULE_2__[\"default\"])(sequence_ids, database_ids);\n return false;\n }\n\n /**\n * Handles copying the URL into the user's clipboard. Modified from: https://stackoverflow.com/a/49618964/18117380\n * Hides the 'Copied!' tooltip after 3 seconds\n */\n }, {\n key: \"copyURL\",\n value: function copyURL() {\n var element = document.createElement('input');\n var url = window.location.href;\n document.body.appendChild(element);\n element.value = url;\n element.select();\n document.execCommand('copy');\n document.body.removeChild(element);\n setTimeout(function () {\n $('#copyURL')._tooltip('hide');\n }, 3000);\n }\n }, {\n key: \"shareCloudInit\",\n value: function shareCloudInit() {\n this.refs.cloudShareModal.show();\n }\n }, {\n key: \"topPanelJSX\",\n value: function topPanelJSX() {\n var path = location.pathname.split('/');\n // Get job id.\n var job_id = path.pop();\n // Deriving rootURL this way is required for subURI deployments\n // - we cannot just send to '/'.\n var rootURL = path.join('/');\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"div\", {\n className: \"sidebar-top-panel\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"div\", {\n className: \"section-header-sidebar\",\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"h4\", {\n children: this.summaryString()\n })\n }), this.props.data.queries.length > 12 && this.queryIndexButtons(), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"div\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"a\", {\n href: \"\".concat(rootURL, \"/?job_id=\").concat(job_id),\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"i\", {\n className: \"fa fa-pencil\"\n }), \" Edit search\"]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"span\", {\n className: \"line\",\n children: \"|\"\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"a\", {\n href: \"\".concat(rootURL, \"/\"),\n onClick: this.clearSession,\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"i\", {\n className: \"fa fa-file-o\"\n }), \" New search\"]\n })]\n }), this.props.shouldShowIndex && this.indexJSX()]\n });\n }\n }, {\n key: \"summaryString\",\n value: function summaryString() {\n var program = this.props.data.program;\n var numqueries = this.props.data.queries.length;\n var numquerydb = this.props.data.querydb.length;\n return program.toUpperCase() + ': ' + numqueries + ' ' + (numqueries > 1 ? 'queries' : 'query') + ', ' + numquerydb + ' ' + (numquerydb > 1 ? 'databases' : 'database');\n }\n }, {\n key: \"queryIndexButtons\",\n value: function queryIndexButtons() {\n var _this3 = this;\n var buttonStyle = {\n outline: 'none',\n border: 'none',\n background: 'none'\n };\n var buttonClasses = 'btn-link nowrap-ellipsis hover-bold';\n var handlePreviousBtnClick = function handlePreviousBtnClick() {\n return _this3.handleQueryIndexChange(_this3.state.queryIndex - 1);\n };\n var handleNextBtnClick = function handleNextBtnClick() {\n return _this3.handleQueryIndexChange(_this3.state.queryIndex + 1);\n };\n\n // eslint-disable-next-line no-unused-vars\n var NavButton = function NavButton(_ref) {\n var text = _ref.text,\n onClick = _ref.onClick;\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"button\", {\n className: buttonClasses,\n onClick: onClick,\n style: buttonStyle,\n children: text\n });\n };\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"div\", {\n style: {\n display: 'flex',\n width: '100%',\n margin: '7px 0'\n },\n children: [this.state.queryIndex > 1 && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(NavButton, {\n text: \"Previous Query\",\n onClick: handlePreviousBtnClick\n }), this.state.queryIndex > 1 && this.state.queryIndex < this.props.data.queries.length && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"span\", {\n className: \"line\",\n children: \"|\"\n }), this.state.queryIndex < this.props.data.queries.length && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(NavButton, {\n onClick: handleNextBtnClick,\n text: \"Next Query\"\n })]\n });\n }\n }, {\n key: \"indexJSX\",\n value: function indexJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"ul\", {\n className: \"nav hover-reset active-bold\",\n children: [\" \", underscore__WEBPACK_IMPORTED_MODULE_1__[\"default\"].map(this.props.data.queries, function (query) {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"a\", {\n className: \"btn-link nowrap-ellipsis hover-bold\",\n title: 'Query= ' + query.id + ' ' + query.title,\n href: '#Query_' + query.number,\n children: 'Query= ' + query.id\n })\n }, 'Side_bar_' + query.id);\n })]\n });\n }\n }, {\n key: \"downloadsPanelJSX\",\n value: function downloadsPanelJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"div\", {\n className: \"downloads\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"div\", {\n className: \"section-header-sidebar\",\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"h4\", {\n children: \"Download FASTA, XML, TSV\"\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"ul\", {\n className: \"nav\",\n children: [!(this.props.data.imported_xml || this.props.data.non_parse_seqids) && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"a\", {\n href: \"#\",\n className: \"btn-link download-fasta-of-all \".concat(!this.props.atLeastOneHit && 'disabled'),\n onClick: this.downloadFastaOfAll,\n children: \"FASTA of all hits\"\n })\n }), !(this.props.data.imported_xml || this.props.data.non_parse_seqids) && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"a\", {\n href: \"#\",\n className: \"btn-link download-fasta-of-selected disabled\",\n onClick: this.downloadFastaOfSelected,\n children: [\"FASTA of \", /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"span\", {\n className: \"text-bold\"\n }), \" selected hit(s)\"]\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"a\", {\n href: \"#\",\n className: \"btn-link download-alignment-of-all \".concat(!this.props.atLeastOneHit && 'disabled'),\n children: \"Alignment of all hits\"\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"a\", {\n href: \"#\",\n className: \"btn-link download-alignment-of-selected disabled\",\n children: [\"Alignment of \", /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"span\", {\n className: \"text-bold\"\n }), \" selected hit(s)\"]\n })\n }), !this.props.data.imported_xml && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"a\", {\n className: \"btn-link download\",\n \"data-toggle\": \"tooltip\",\n title: \"15 columns: query and subject ID; scientific name, alignment length, mismatches, gaps, identity, start and end coordinates, e value, bitscore, query coverage per subject and per HSP.\",\n href: 'download/' + this.props.data.search_id + '.std_tsv',\n children: \"Standard tabular report\"\n })\n }), !this.props.data.imported_xml && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"a\", {\n className: \"btn-link download\",\n \"data-toggle\": \"tooltip\",\n title: \"44 columns: query and subject ID, GI, accessions, and length; alignment details; taxonomy details of subject sequence(s) and query coverage per subject and per HSP.\",\n href: 'download/' + this.props.data.search_id + '.full_tsv',\n children: \"Full tabular report\"\n })\n }), !this.props.data.imported_xml && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"a\", {\n className: \"btn-link download\",\n \"data-toggle\": \"tooltip\",\n title: \"Results in XML format.\",\n href: 'download/' + this.props.data.search_id + '.xml',\n children: \"Full XML report\"\n })\n }), !this.props.data.imported_xml && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"a\", {\n className: \"btn-link download\",\n \"data-toggle\": \"tooltip\",\n title: \"Results in pairwise format.\",\n href: 'download/' + this.props.data.search_id + '.pairwise',\n children: \"Full Pairwise report\"\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(download_links__WEBPACK_IMPORTED_MODULE_5__[\"default\"], {\n imported_xml: this.props.data.imported_xml,\n search_id: this.props.data.search_id\n })]\n })]\n });\n }\n }, {\n key: \"sharingPanelJSX\",\n value: function sharingPanelJSX() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"div\", {\n className: \"sharing-panel\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"div\", {\n className: \"section-header-sidebar\",\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"h4\", {\n children: \"Share results\"\n })\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"ul\", {\n className: \"nav\",\n children: [!this.props.cloudSharingEnabled && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"a\", {\n id: \"copyURL\",\n className: \"btn-link copy-URL cursor-pointer\",\n \"data-toggle\": \"tooltip\",\n onClick: this.copyURL,\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"i\", {\n className: \"fa fa-copy\"\n }), \" Copy URL to clipboard\"]\n })\n }), !this.props.cloudSharingEnabled && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"a\", {\n id: \"sendEmail\",\n className: \"btn-link email-URL cursor-pointer\",\n \"data-toggle\": \"tooltip\",\n title: \"Send by email\",\n href: (0,_mailto__WEBPACK_IMPORTED_MODULE_3__[\"default\"])(this.props.data.querydb, this.props.data.program, this.props.data.queries.length, window.location.href),\n target: \"_blank\",\n rel: \"noopener noreferrer\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"i\", {\n className: \"fa fa-envelope\"\n }), \" Send by email\"]\n })\n }), this.props.cloudSharingEnabled && /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"li\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"button\", {\n className: \"btn-link cloud-Post cursor-pointer\",\n \"data-toggle\": \"tooltip\",\n title: \"Upload results to SequenceServer Cloud where it will become accessable to everyone who has a link.\",\n onClick: this.shareCloudInit,\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"i\", {\n className: \"fa fa-cloud\"\n }), \" Share to cloud\"]\n })\n })]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(_cloud_share_modal__WEBPACK_IMPORTED_MODULE_4__[\"default\"], {\n ref: \"cloudShareModal\",\n querydb: this.props.data.querydb,\n program: this.props.data.program,\n queryLength: this.props.data.queries.length\n })]\n });\n }\n }, {\n key: \"render\",\n value: function render() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"div\", {\n className: \"sidebar\",\n children: [this.topPanelJSX(), this.downloadsPanelJSX(), this.sharingPanelJSX(), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"div\", {\n className: \"referral-panel\",\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsxs)(\"div\", {\n className: \"section-header-sidebar\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"h4\", {\n children: \"Recommend SequenceServer\"\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"p\", {\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_6__.jsx)(\"a\", {\n href: \"https://sequenceserver.com/referral-program\",\n target: \"_blank\",\n children: \"Earn up to $100 per signup\"\n })\n })]\n })\n })]\n });\n }\n }]);\n}(react__WEBPACK_IMPORTED_MODULE_0__.Component);\n\n\n//# sourceURL=webpack://SequenceServer/./public/js/sidebar.js?");
306
306
 
307
307
  /***/ }),
308
308
 
@@ -82,7 +82,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpac
82
82
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
83
83
 
84
84
  "use strict";
85
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Form: () => (/* binding */ Form)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _search_button__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./search_button */ \"./public/js/search_button.js\");\n/* harmony import */ var _query__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./query */ \"./public/js/query.js\");\n/* harmony import */ var _databases_tree__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./databases_tree */ \"./public/js/databases_tree.js\");\n/* harmony import */ var _databases__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./databases */ \"./public/js/databases.js\");\n/* harmony import */ var underscore__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! underscore */ \"./node_modules/underscore/modules/index-all.js\");\n/* harmony import */ var options__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! options */ \"./public/js/null_plugins/options.js\");\n/* harmony import */ var query_stats__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! query_stats */ \"./public/js/null_plugins/query_stats.js\");\n/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! react/jsx-runtime */ \"./node_modules/react/jsx-runtime.js\");\n/* provided dependency */ var $ = __webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\");\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; return _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && \"function\" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }, _typeof(obj); }\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, \"prototype\", { writable: false }); return Constructor; }\nfunction _callSuper(_this, derived, args) {\n function isNativeReflectConstruct() {\n if (typeof Reflect === \"undefined\" || !Reflect.construct) return false;\n if (Reflect.construct.sham) return false;\n if (typeof Proxy === \"function\") return true;\n try {\n return !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));\n } catch (e) {\n return false;\n }\n }\n derived = _getPrototypeOf(derived);\n return _possibleConstructorReturn(_this, isNativeReflectConstruct() ? Reflect.construct(derived, args || [], _getPrototypeOf(_this).constructor) : derived.apply(_this, args));\n}\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } else if (call !== void 0) { throw new TypeError(\"Derived constructors may only return object or undefined\"); } return _assertThisInitialized(self); }\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, \"prototype\", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\n\n\n\n\n\n\n\n\n/**\n * Search form.\n *\n * Top level component that initialises and holds all other components, and\n * facilitates communication between them.\n */\n\nvar Form = /*#__PURE__*/function (_Component) {\n function Form(props) {\n var _this2;\n _classCallCheck(this, Form);\n _this2 = _callSuper(this, Form, [props]);\n _this2.state = {\n databases: [],\n preSelectedDbs: [],\n currentlySelectedDbs: [],\n preDefinedOpts: {},\n tree: {},\n residuesInQuerySequence: 0,\n blastMethod: ''\n };\n _this2.useTreeWidget = _this2.useTreeWidget.bind(_this2);\n _this2.determineBlastMethods = _this2.determineBlastMethods.bind(_this2);\n _this2.handleSequenceTypeChanged = _this2.handleSequenceTypeChanged.bind(_this2);\n _this2.handleSequenceChanged = _this2.handleSequenceChanged.bind(_this2);\n _this2.handleDatabaseTypeChanged = _this2.handleDatabaseTypeChanged.bind(_this2);\n _this2.handleDatabaseSelectionChanged = _this2.handleDatabaseSelectionChanged.bind(_this2);\n _this2.handleAlgoChanged = _this2.handleAlgoChanged.bind(_this2);\n _this2.handleFormSubmission = _this2.handleFormSubmission.bind(_this2);\n _this2.formRef = /*#__PURE__*/(0,react__WEBPACK_IMPORTED_MODULE_0__.createRef)();\n _this2.setButtonState = _this2.setButtonState.bind(_this2);\n return _this2;\n }\n _inherits(Form, _Component);\n return _createClass(Form, [{\n key: \"componentDidMount\",\n value: function componentDidMount() {\n /**\n * Fetch data to initialise the search interface from the server. These\n * include list of databases to search against, advanced options to\n * apply when an algorithm is selected, and a query sequence that\n * the user may want to search in the databases.\n */\n var search = location.search.split(/\\?|&/).filter(Boolean);\n var job_id = sessionStorage.getItem('job_id');\n if (job_id) {\n search.unshift(\"job_id=\".concat(job_id));\n }\n $.getJSON(\"searchdata.json?\".concat(search.join('&')), function (data) {\n /* Update form state (i.e., list of databases and predefined\n * advanced options.\n */\n this.setState({\n tree: data['tree'],\n databases: data['database'],\n preSelectedDbs: data['preSelectedDbs'],\n preDefinedOpts: data['options'],\n blastTaskMap: data['blastTaskMap']\n });\n\n /* Pre-populate the form with server sent query sequences\n * (if any).\n */\n if (data['query']) {\n this.refs.query.value(data['query']);\n }\n setTimeout(function () {\n $('.jstree_div').click();\n }, 1000);\n }.bind(this));\n\n /* Enable submitting form on Cmd+Enter */\n $(document).on('keydown', function (e) {\n var $button = $('#method');\n if (!$button.is(':disabled') && e.ctrlKey && e.key === 'Enter') {\n $button.trigger('click');\n }\n });\n\n // show overlay to create visual feedback on button click\n $('#method').on('click', function () {\n $('#overlay').css('display', 'block');\n });\n }\n }, {\n key: \"useTreeWidget\",\n value: function useTreeWidget() {\n return !underscore__WEBPACK_IMPORTED_MODULE_5__[\"default\"].isEmpty(this.state.tree);\n }\n }, {\n key: \"handleFormSubmission\",\n value: function handleFormSubmission(evt) {\n evt.preventDefault();\n var form = this.formRef.current;\n var formData = new FormData(form);\n formData.append('method', this.refs.button.state.methods[0]);\n fetch(window.location.href, {\n method: 'POST',\n body: formData\n }).then(function (res) {\n //remove overlay when form is submitted\n $('#overlay').css('display', 'none');\n // redirect\n if (res.redirected && res.url) {\n // setTimeout is needed here as a workaround because safari doesnt allow async calling of window.open\n // so setTimeout makes the method get called on the main thread.\n setTimeout(function () {\n window.open(res.url, $('#toggleNewTab').is(':checked') ? '_blank' : '_self');\n }, 0);\n }\n });\n }\n }, {\n key: \"determineBlastMethods\",\n value: function determineBlastMethods() {\n var database_type = this.databaseType;\n var sequence_type = this.sequenceType;\n if (this.refs.query.isEmpty()) {\n return [];\n }\n\n //database type is always known\n switch (database_type) {\n case 'protein':\n switch (sequence_type) {\n case undefined:\n return ['blastp', 'blastx'];\n case 'protein':\n return ['blastp'];\n case 'nucleotide':\n return ['blastx'];\n }\n break;\n case 'nucleotide':\n switch (sequence_type) {\n case undefined:\n return ['tblastn', 'blastn', 'tblastx'];\n case 'protein':\n return ['tblastn'];\n case 'nucleotide':\n return ['blastn', 'tblastx'];\n }\n break;\n }\n return [];\n }\n }, {\n key: \"handleSequenceChanged\",\n value: function handleSequenceChanged(residuesInQuerySequence) {\n if (residuesInQuerySequence !== this.state.residuesInQuerySequence) this.setState({\n residuesInQuerySequence: residuesInQuerySequence\n });\n }\n }, {\n key: \"handleSequenceTypeChanged\",\n value: function handleSequenceTypeChanged(type) {\n this.sequenceType = type;\n this.setButtonState();\n }\n }, {\n key: \"handleDatabaseTypeChanged\",\n value: function handleDatabaseTypeChanged(type) {\n this.databaseType = type;\n this.setButtonState();\n }\n }, {\n key: \"setButtonState\",\n value: function setButtonState() {\n this.refs.button.setState({\n hasQuery: !this.refs.query.isEmpty(),\n hasDatabases: !!this.databaseType,\n methods: this.determineBlastMethods()\n });\n }\n }, {\n key: \"handleDatabaseSelectionChanged\",\n value: function handleDatabaseSelectionChanged(selectedDbs) {\n if (!underscore__WEBPACK_IMPORTED_MODULE_5__[\"default\"].isEqual(selectedDbs, this.state.currentlySelectedDbs)) this.setState({\n currentlySelectedDbs: selectedDbs\n });\n }\n }, {\n key: \"handleAlgoChanged\",\n value: function handleAlgoChanged(algo) {\n if (algo in this.state.preDefinedOpts) {\n this.setState({\n blastMethod: algo\n });\n } else {\n this.setState({\n blastMethod: ''\n });\n }\n }\n }, {\n key: \"residuesInSelectedDbs\",\n value: function residuesInSelectedDbs() {\n return this.state.currentlySelectedDbs.reduce(function (sum, db) {\n return sum + parseInt(db.ncharacters, 10);\n }, 0);\n }\n }, {\n key: \"render\",\n value: function render() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"div\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n id: \"overlay\",\n style: {\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100vw',\n height: '100vw',\n background: 'rgba(0, 0, 0, 0.2)',\n display: 'none',\n zIndex: 99\n }\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"div\", {\n className: \"fixed top-0 left-0 w-full max-h-8 px-8\",\n \"data-notifications\": true,\n id: \"notifications\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(FastqNotification, {}), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(NucleotideNotification, {}), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(ProteinNotification, {}), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(MixedNotification, {})]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"form\", {\n id: \"blast\",\n ref: this.formRef,\n onSubmit: this.handleFormSubmission,\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"div\", {\n className: \"px-4\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(_query__WEBPACK_IMPORTED_MODULE_2__.SearchQueryWidget, {\n ref: \"query\",\n onSequenceTypeChanged: this.handleSequenceTypeChanged,\n onSequenceChanged: this.handleSequenceChanged\n }), this.useTreeWidget() ? /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(_databases_tree__WEBPACK_IMPORTED_MODULE_3__[\"default\"], {\n ref: \"databases\",\n databases: this.state.databases,\n tree: this.state.tree,\n preSelectedDbs: this.state.preSelectedDbs,\n onDatabaseTypeChanged: this.handleDatabaseTypeChanged,\n onDatabaseSelectionChanged: this.handleDatabaseSelectionChanged\n }) : /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(_databases__WEBPACK_IMPORTED_MODULE_4__.Databases, {\n ref: \"databases\",\n databases: this.state.databases,\n preSelectedDbs: this.state.preSelectedDbs,\n onDatabaseTypeChanged: this.handleDatabaseTypeChanged,\n onDatabaseSelectionChanged: this.handleDatabaseSelectionChanged\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(options__WEBPACK_IMPORTED_MODULE_6__.Options, {\n blastMethod: this.state.blastMethod,\n predefinedOptions: this.state.preDefinedOpts[this.state.blastMethod] || {},\n blastTasks: (this.state.blastTaskMap || {})[this.state.blastMethod]\n })]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n className: \"py-6\"\n }), \" \", /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"div\", {\n className: \"pb-4 pt-2 px-4 sticky bottom-0 md:flex flex-row md:space-x-4 items-center justify-end bg-gradient-to-t to-gray-100/90 from-white/90\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(query_stats__WEBPACK_IMPORTED_MODULE_7__[\"default\"], {\n residuesInQuerySequence: this.state.residuesInQuerySequence,\n numberOfDatabasesSelected: this.state.currentlySelectedDbs.length,\n residuesInSelectedDbs: this.residuesInSelectedDbs(),\n currentBlastMethod: this.state.blastMethod\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"label\", {\n className: \"block my-4 md:my-2\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"input\", {\n type: \"checkbox\",\n id: \"toggleNewTab\"\n }), \" Open results in new tab\"]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(_search_button__WEBPACK_IMPORTED_MODULE_1__.SearchButton, {\n ref: \"button\",\n onAlgoChanged: this.handleAlgoChanged\n })]\n })]\n })]\n });\n }\n }]);\n}(react__WEBPACK_IMPORTED_MODULE_0__.Component);\nvar ProteinNotification = /*#__PURE__*/function (_Component2) {\n function ProteinNotification() {\n _classCallCheck(this, ProteinNotification);\n return _callSuper(this, ProteinNotification, arguments);\n }\n _inherits(ProteinNotification, _Component2);\n return _createClass(ProteinNotification, [{\n key: \"render\",\n value: function render() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n \"data-role\": \"notification\",\n id: \"protein-sequence-notification\",\n style: {\n display: 'none'\n },\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n className: \"bg-blue-100 border rounded border-blue-800 px-4 py-2 my-2\",\n children: \"Detected: amino-acid sequence(s).\"\n })\n });\n }\n }]);\n}(react__WEBPACK_IMPORTED_MODULE_0__.Component);\nvar NucleotideNotification = /*#__PURE__*/function (_Component3) {\n function NucleotideNotification() {\n _classCallCheck(this, NucleotideNotification);\n return _callSuper(this, NucleotideNotification, arguments);\n }\n _inherits(NucleotideNotification, _Component3);\n return _createClass(NucleotideNotification, [{\n key: \"render\",\n value: function render() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n \"data-role\": \"notification\",\n id: \"nucleotide-sequence-notification\",\n style: {\n display: 'none'\n },\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n className: \"bg-blue-100 border rounded border-blue-800 px-4 py-2 my-2\",\n children: \"Detected: nucleotide sequence(s).\"\n })\n });\n }\n }]);\n}(react__WEBPACK_IMPORTED_MODULE_0__.Component);\nvar FastqNotification = /*#__PURE__*/function (_Component4) {\n function FastqNotification() {\n _classCallCheck(this, FastqNotification);\n return _callSuper(this, FastqNotification, arguments);\n }\n _inherits(FastqNotification, _Component4);\n return _createClass(FastqNotification, [{\n key: \"render\",\n value: function render() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n \"data-role\": \"notification\",\n id: \"fastq-sequence-notification\",\n style: {\n display: 'none'\n },\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n className: \"bg-blue-100 border rounded border-blue-800 px-4 py-2 my-2\",\n children: \"Detected FASTQ and automatically converted to FASTA.\"\n })\n });\n }\n }]);\n}(react__WEBPACK_IMPORTED_MODULE_0__.Component);\nvar MixedNotification = /*#__PURE__*/function (_Component5) {\n function MixedNotification() {\n _classCallCheck(this, MixedNotification);\n return _callSuper(this, MixedNotification, arguments);\n }\n _inherits(MixedNotification, _Component5);\n return _createClass(MixedNotification, [{\n key: \"render\",\n value: function render() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n \"data-role\": \"notification\",\n id: \"mixed-sequence-notification\",\n style: {\n display: 'none'\n },\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n className: \"alert-danger col-md-10 col-md-offset-1\",\n children: \"Error: mixed nucleotide and amino-acid sequences detected.\"\n })\n });\n }\n }]);\n}(react__WEBPACK_IMPORTED_MODULE_0__.Component);\n\n//# sourceURL=webpack://SequenceServer/./public/js/form.js?");
85
+ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ Form: () => (/* binding */ Form)\n/* harmony export */ });\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! react */ \"./node_modules/react/index.js\");\n/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _search_button__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./search_button */ \"./public/js/search_button.js\");\n/* harmony import */ var _query__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./query */ \"./public/js/query.js\");\n/* harmony import */ var _databases_tree__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./databases_tree */ \"./public/js/databases_tree.js\");\n/* harmony import */ var _databases__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./databases */ \"./public/js/databases.js\");\n/* harmony import */ var underscore__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! underscore */ \"./node_modules/underscore/modules/index-all.js\");\n/* harmony import */ var options__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! options */ \"./public/js/null_plugins/options.js\");\n/* harmony import */ var query_stats__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! query_stats */ \"./public/js/null_plugins/query_stats.js\");\n/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! react/jsx-runtime */ \"./node_modules/react/jsx-runtime.js\");\n/* provided dependency */ var $ = __webpack_require__(/*! jquery */ \"./node_modules/jquery/dist/jquery.js\");\nfunction _typeof(obj) { \"@babel/helpers - typeof\"; return _typeof = \"function\" == typeof Symbol && \"symbol\" == typeof Symbol.iterator ? function (obj) { return typeof obj; } : function (obj) { return obj && \"function\" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }, _typeof(obj); }\nfunction _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError(\"Cannot call a class as a function\"); } }\nfunction _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if (\"value\" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }\nfunction _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, \"prototype\", { writable: false }); return Constructor; }\nfunction _callSuper(_this, derived, args) {\n function isNativeReflectConstruct() {\n if (typeof Reflect === \"undefined\" || !Reflect.construct) return false;\n if (Reflect.construct.sham) return false;\n if (typeof Proxy === \"function\") return true;\n try {\n return !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));\n } catch (e) {\n return false;\n }\n }\n derived = _getPrototypeOf(derived);\n return _possibleConstructorReturn(_this, isNativeReflectConstruct() ? Reflect.construct(derived, args || [], _getPrototypeOf(_this).constructor) : derived.apply(_this, args));\n}\nfunction _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === \"object\" || typeof call === \"function\")) { return call; } else if (call !== void 0) { throw new TypeError(\"Derived constructors may only return object or undefined\"); } return _assertThisInitialized(self); }\nfunction _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\"); } return self; }\nfunction _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }\nfunction _inherits(subClass, superClass) { if (typeof superClass !== \"function\" && superClass !== null) { throw new TypeError(\"Super expression must either be null or a function\"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); Object.defineProperty(subClass, \"prototype\", { writable: false }); if (superClass) _setPrototypeOf(subClass, superClass); }\nfunction _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }\n\n\n\n\n\n\n\n\n\n/**\n * Search form.\n *\n * Top level component that initialises and holds all other components, and\n * facilitates communication between them.\n */\n\nvar Form = /*#__PURE__*/function (_Component) {\n function Form(props) {\n var _this2;\n _classCallCheck(this, Form);\n _this2 = _callSuper(this, Form, [props]);\n _this2.state = {\n databases: [],\n preSelectedDbs: [],\n currentlySelectedDbs: [],\n preDefinedOpts: {},\n tree: {},\n residuesInQuerySequence: 0,\n blastMethod: ''\n };\n _this2.useTreeWidget = _this2.useTreeWidget.bind(_this2);\n _this2.determineBlastMethods = _this2.determineBlastMethods.bind(_this2);\n _this2.handleSequenceTypeChanged = _this2.handleSequenceTypeChanged.bind(_this2);\n _this2.handleSequenceChanged = _this2.handleSequenceChanged.bind(_this2);\n _this2.handleDatabaseTypeChanged = _this2.handleDatabaseTypeChanged.bind(_this2);\n _this2.handleDatabaseSelectionChanged = _this2.handleDatabaseSelectionChanged.bind(_this2);\n _this2.handleAlgoChanged = _this2.handleAlgoChanged.bind(_this2);\n _this2.handleFormSubmission = _this2.handleFormSubmission.bind(_this2);\n _this2.formRef = /*#__PURE__*/(0,react__WEBPACK_IMPORTED_MODULE_0__.createRef)();\n _this2.setButtonState = _this2.setButtonState.bind(_this2);\n return _this2;\n }\n _inherits(Form, _Component);\n return _createClass(Form, [{\n key: \"componentDidMount\",\n value: function componentDidMount() {\n /**\n * Fetch data to initialise the search interface from the server. These\n * include list of databases to search against, advanced options to\n * apply when an algorithm is selected, and a query sequence that\n * the user may want to search in the databases.\n */\n var search = location.search.split(/\\?|&/).filter(Boolean);\n var job_id = sessionStorage.getItem('job_id');\n if (job_id) {\n search.unshift(\"job_id=\".concat(job_id));\n }\n $.getJSON(\"searchdata.json?\".concat(search.join('&')), function (data) {\n /* Update form state (i.e., list of databases and predefined\n * advanced options.\n */\n this.setState({\n tree: data['tree'],\n databases: data['database'],\n preSelectedDbs: data['preSelectedDbs'],\n preDefinedOpts: data['options'],\n blastTaskMap: data['blastTaskMap']\n });\n\n /* Pre-populate the form with server sent query sequences\n * (if any).\n */\n if (data['query']) {\n this.refs.query.value(data['query']);\n }\n setTimeout(function () {\n $('.jstree_div').click();\n }, 1000);\n }.bind(this));\n\n /* Enable submitting form on Cmd+Enter */\n $(document).on('keydown', function (e) {\n var $button = $('#method');\n if (!$button.is(':disabled') && e.ctrlKey && e.key === 'Enter') {\n $button.trigger('click');\n }\n });\n\n // show overlay to create visual feedback on button click\n $('#method').on('click', function () {\n $('#overlay').css('display', 'block');\n });\n }\n }, {\n key: \"useTreeWidget\",\n value: function useTreeWidget() {\n return !underscore__WEBPACK_IMPORTED_MODULE_5__[\"default\"].isEmpty(this.state.tree);\n }\n }, {\n key: \"handleFormSubmission\",\n value: function handleFormSubmission(evt) {\n evt.preventDefault();\n var form = this.formRef.current;\n var formData = new FormData(form);\n formData.append('method', this.refs.button.state.methods[0]);\n fetch(window.location.href, {\n method: 'POST',\n body: formData\n }).then(function (res) {\n //remove overlay when form is submitted\n $('#overlay').css('display', 'none');\n // redirect\n if (res.redirected && res.url) {\n // setTimeout is needed here as a workaround because safari doesnt allow async calling of window.open\n // so setTimeout makes the method get called on the main thread.\n setTimeout(function () {\n window.open(res.url, $('#toggleNewTab').is(':checked') ? '_blank' : '_self');\n }, 0);\n }\n });\n }\n }, {\n key: \"determineBlastMethods\",\n value: function determineBlastMethods() {\n var database_type = this.databaseType;\n var sequence_type = this.sequenceType;\n if (this.refs.query.isEmpty()) {\n return [];\n }\n\n //database type is always known\n switch (database_type) {\n case 'protein':\n switch (sequence_type) {\n case undefined:\n return ['blastp', 'blastx'];\n case 'protein':\n return ['blastp'];\n case 'nucleotide':\n return ['blastx'];\n }\n break;\n case 'nucleotide':\n switch (sequence_type) {\n case undefined:\n return ['tblastn', 'blastn', 'tblastx'];\n case 'protein':\n return ['tblastn'];\n case 'nucleotide':\n return ['blastn', 'tblastx'];\n }\n break;\n }\n return [];\n }\n }, {\n key: \"handleSequenceChanged\",\n value: function handleSequenceChanged(residuesInQuerySequence) {\n if (residuesInQuerySequence !== this.state.residuesInQuerySequence) this.setState({\n residuesInQuerySequence: residuesInQuerySequence\n });\n }\n }, {\n key: \"handleSequenceTypeChanged\",\n value: function handleSequenceTypeChanged(type) {\n this.sequenceType = type;\n this.setButtonState();\n }\n }, {\n key: \"handleDatabaseTypeChanged\",\n value: function handleDatabaseTypeChanged(type) {\n this.databaseType = type;\n this.setButtonState();\n }\n }, {\n key: \"setButtonState\",\n value: function setButtonState() {\n this.refs.button.setState({\n hasQuery: !this.refs.query.isEmpty(),\n hasDatabases: !!this.databaseType,\n methods: this.determineBlastMethods()\n });\n }\n }, {\n key: \"handleDatabaseSelectionChanged\",\n value: function handleDatabaseSelectionChanged(selectedDbs) {\n if (!underscore__WEBPACK_IMPORTED_MODULE_5__[\"default\"].isEqual(selectedDbs, this.state.currentlySelectedDbs)) this.setState({\n currentlySelectedDbs: selectedDbs\n });\n }\n }, {\n key: \"handleAlgoChanged\",\n value: function handleAlgoChanged(algo) {\n if (algo in this.state.preDefinedOpts) {\n this.setState({\n blastMethod: algo\n });\n } else {\n this.setState({\n blastMethod: ''\n });\n }\n }\n }, {\n key: \"residuesInSelectedDbs\",\n value: function residuesInSelectedDbs() {\n return this.state.currentlySelectedDbs.reduce(function (sum, db) {\n return sum + parseInt(db.ncharacters, 10);\n }, 0);\n }\n }, {\n key: \"render\",\n value: function render() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"div\", {\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n id: \"overlay\",\n style: {\n position: 'absolute',\n top: 0,\n left: 0,\n width: '100vw',\n height: '100vw',\n background: 'rgba(0, 0, 0, 0.2)',\n display: 'none',\n zIndex: 99\n }\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"div\", {\n className: \"fixed top-0 left-0 w-full max-h-8 px-8\",\n \"data-notifications\": true,\n id: \"notifications\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(FastqNotification, {}), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(NucleotideNotification, {}), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(ProteinNotification, {}), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(MixedNotification, {})]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"form\", {\n id: \"blast\",\n ref: this.formRef,\n onSubmit: this.handleFormSubmission,\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"input\", {\n type: \"hidden\",\n name: \"_csrf\",\n value: document.querySelector('meta[name=\"_csrf\"]').content\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"div\", {\n className: \"px-4\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(_query__WEBPACK_IMPORTED_MODULE_2__.SearchQueryWidget, {\n ref: \"query\",\n onSequenceTypeChanged: this.handleSequenceTypeChanged,\n onSequenceChanged: this.handleSequenceChanged\n }), this.useTreeWidget() ? /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(_databases_tree__WEBPACK_IMPORTED_MODULE_3__[\"default\"], {\n ref: \"databases\",\n databases: this.state.databases,\n tree: this.state.tree,\n preSelectedDbs: this.state.preSelectedDbs,\n onDatabaseTypeChanged: this.handleDatabaseTypeChanged,\n onDatabaseSelectionChanged: this.handleDatabaseSelectionChanged\n }) : /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(_databases__WEBPACK_IMPORTED_MODULE_4__.Databases, {\n ref: \"databases\",\n databases: this.state.databases,\n preSelectedDbs: this.state.preSelectedDbs,\n onDatabaseTypeChanged: this.handleDatabaseTypeChanged,\n onDatabaseSelectionChanged: this.handleDatabaseSelectionChanged\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(options__WEBPACK_IMPORTED_MODULE_6__.Options, {\n blastMethod: this.state.blastMethod,\n predefinedOptions: this.state.preDefinedOpts[this.state.blastMethod] || {},\n blastTasks: (this.state.blastTaskMap || {})[this.state.blastMethod]\n })]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n className: \"py-6\"\n }), \" \", /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"div\", {\n className: \"pb-4 pt-2 px-4 sticky bottom-0 md:flex flex-row md:space-x-4 items-center justify-end bg-gradient-to-t to-gray-100/90 from-white/90\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(query_stats__WEBPACK_IMPORTED_MODULE_7__[\"default\"], {\n residuesInQuerySequence: this.state.residuesInQuerySequence,\n numberOfDatabasesSelected: this.state.currentlySelectedDbs.length,\n residuesInSelectedDbs: this.residuesInSelectedDbs(),\n currentBlastMethod: this.state.blastMethod\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsxs)(\"label\", {\n className: \"block my-4 md:my-2\",\n children: [/*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"input\", {\n type: \"checkbox\",\n id: \"toggleNewTab\"\n }), \" Open results in new tab\"]\n }), /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(_search_button__WEBPACK_IMPORTED_MODULE_1__.SearchButton, {\n ref: \"button\",\n onAlgoChanged: this.handleAlgoChanged\n })]\n })]\n })]\n });\n }\n }]);\n}(react__WEBPACK_IMPORTED_MODULE_0__.Component);\nvar ProteinNotification = /*#__PURE__*/function (_Component2) {\n function ProteinNotification() {\n _classCallCheck(this, ProteinNotification);\n return _callSuper(this, ProteinNotification, arguments);\n }\n _inherits(ProteinNotification, _Component2);\n return _createClass(ProteinNotification, [{\n key: \"render\",\n value: function render() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n \"data-role\": \"notification\",\n id: \"protein-sequence-notification\",\n style: {\n display: 'none'\n },\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n className: \"bg-blue-100 border rounded border-blue-800 px-4 py-2 my-2\",\n children: \"Detected: amino-acid sequence(s).\"\n })\n });\n }\n }]);\n}(react__WEBPACK_IMPORTED_MODULE_0__.Component);\nvar NucleotideNotification = /*#__PURE__*/function (_Component3) {\n function NucleotideNotification() {\n _classCallCheck(this, NucleotideNotification);\n return _callSuper(this, NucleotideNotification, arguments);\n }\n _inherits(NucleotideNotification, _Component3);\n return _createClass(NucleotideNotification, [{\n key: \"render\",\n value: function render() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n \"data-role\": \"notification\",\n id: \"nucleotide-sequence-notification\",\n style: {\n display: 'none'\n },\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n className: \"bg-blue-100 border rounded border-blue-800 px-4 py-2 my-2\",\n children: \"Detected: nucleotide sequence(s).\"\n })\n });\n }\n }]);\n}(react__WEBPACK_IMPORTED_MODULE_0__.Component);\nvar FastqNotification = /*#__PURE__*/function (_Component4) {\n function FastqNotification() {\n _classCallCheck(this, FastqNotification);\n return _callSuper(this, FastqNotification, arguments);\n }\n _inherits(FastqNotification, _Component4);\n return _createClass(FastqNotification, [{\n key: \"render\",\n value: function render() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n \"data-role\": \"notification\",\n id: \"fastq-sequence-notification\",\n style: {\n display: 'none'\n },\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n className: \"bg-blue-100 border rounded border-blue-800 px-4 py-2 my-2\",\n children: \"Detected FASTQ and automatically converted to FASTA.\"\n })\n });\n }\n }]);\n}(react__WEBPACK_IMPORTED_MODULE_0__.Component);\nvar MixedNotification = /*#__PURE__*/function (_Component5) {\n function MixedNotification() {\n _classCallCheck(this, MixedNotification);\n return _callSuper(this, MixedNotification, arguments);\n }\n _inherits(MixedNotification, _Component5);\n return _createClass(MixedNotification, [{\n key: \"render\",\n value: function render() {\n return /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n \"data-role\": \"notification\",\n id: \"mixed-sequence-notification\",\n style: {\n display: 'none'\n },\n children: /*#__PURE__*/(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_8__.jsx)(\"div\", {\n className: \"alert-danger col-md-10 col-md-offset-1\",\n children: \"Error: mixed nucleotide and amino-acid sequences detected.\"\n })\n });\n }\n }]);\n}(react__WEBPACK_IMPORTED_MODULE_0__.Component);\n\n//# sourceURL=webpack://SequenceServer/./public/js/form.js?");
86
86
 
87
87
  /***/ }),
88
88
 
data/views/layout.erb CHANGED
@@ -9,6 +9,7 @@
9
9
  <meta name="author" content="Pragmatic Genomics Ltd">
10
10
  <meta name="description" content="Custom BLAST server provided by SequenceServer (https://sequenceserver.com)"/>
11
11
 
12
+ <%= Rack::Csrf.metatag(env) %>
12
13
  <!-- CSS -->
13
14
  <% if settings.production? || settings.test? %>
14
15
  <link rel="stylesheet" media="screen,print" type="text/css" href="css/sequenceserver.min.css?ver=<%= SequenceServer::VERSION %>"/>
@@ -6,6 +6,7 @@
6
6
  <meta name="author" content="Pragmatic Genomics Limited"/>
7
7
  <meta charset="UTF-8">
8
8
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
9
+ <%= Rack::Csrf.metatag(env) %>
9
10
  <link rel="stylesheet" media="screen,print" type="text/css" href="css/app.min.css"/>
10
11
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" integrity="sha512-SnH5WK+bZxgPHs44uWIX+LLJAJ9/2PkPKZ5QiAj6Ta86w+fsb2TkcmfRyVX3pBnMFcV7oQPJkl9QevSCWr3W6A==" crossorigin="anonymous" referrerpolicy="no-referrer" />
11
12
  </head>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sequenceserver
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 3.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Queen Mary University of London
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-06-17 00:00:00.000000000 Z
12
+ date: 2024-07-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json_pure
@@ -51,6 +51,26 @@ dependencies:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
53
  version: 2.1.1
54
+ - !ruby/object:Gem::Dependency
55
+ name: rack_csrf
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '2.7'
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 2.7.0
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: '2.7'
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 2.7.0
54
74
  - !ruby/object:Gem::Dependency
55
75
  name: rackup
56
76
  requirement: !ruby/object:Gem::Requirement
@@ -3539,7 +3559,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
3539
3559
  - !ruby/object:Gem::Version
3540
3560
  version: '0'
3541
3561
  requirements: []
3542
- rubygems_version: 3.5.6
3562
+ rubygems_version: 3.5.14
3543
3563
  signing_key:
3544
3564
  specification_version: 4
3545
3565
  summary: BLAST search made easy!