quorum 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +10 -0
- data/.rspec +1 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +170 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +318 -0
- data/Rakefile +31 -0
- data/app/assets/images/quorum/.gitkeep +0 -0
- data/app/assets/images/quorum/bg.png +0 -0
- data/app/assets/images/quorum/knight_rider.gif +0 -0
- data/app/assets/images/quorum/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/app/assets/images/quorum/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/app/assets/images/quorum/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/app/assets/images/quorum/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/app/assets/images/quorum/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/app/assets/images/quorum/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/app/assets/images/quorum/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/app/assets/images/quorum/ui-bg_inset-soft_95_fef1ec_1x100.png +0 -0
- data/app/assets/images/quorum/ui-icons_222222_256x240.png +0 -0
- data/app/assets/images/quorum/ui-icons_2e83ff_256x240.png +0 -0
- data/app/assets/images/quorum/ui-icons_454545_256x240.png +0 -0
- data/app/assets/images/quorum/ui-icons_888888_256x240.png +0 -0
- data/app/assets/images/quorum/ui-icons_cd0a0a_256x240.png +0 -0
- data/app/assets/javascripts/quorum/application.js +13 -0
- data/app/assets/javascripts/quorum/jobs.js +386 -0
- data/app/assets/stylesheets/quorum/application.css +224 -0
- data/app/assets/stylesheets/quorum/jobs.css +72 -0
- data/app/assets/stylesheets/quorum/jquery-ui-1.8.16.custom.css +568 -0
- data/app/assets/stylesheets/quorum/tipsy.css +25 -0
- data/app/controllers/quorum/application_controller.rb +5 -0
- data/app/controllers/quorum/jobs_controller.rb +102 -0
- data/app/helpers/quorum/application_helper.rb +4 -0
- data/app/models/quorum/blastn_job.rb +111 -0
- data/app/models/quorum/blastn_job_report.rb +7 -0
- data/app/models/quorum/blastp_job.rb +111 -0
- data/app/models/quorum/blastp_job_report.rb +7 -0
- data/app/models/quorum/blastx_job.rb +111 -0
- data/app/models/quorum/blastx_job_report.rb +7 -0
- data/app/models/quorum/job.rb +164 -0
- data/app/models/quorum/tblastn_job.rb +111 -0
- data/app/models/quorum/tblastn_job_report.rb +7 -0
- data/app/views/layouts/quorum/application.html.erb +15 -0
- data/app/views/quorum/jobs/_blastn_form.html.erb +71 -0
- data/app/views/quorum/jobs/_blastp_form.html.erb +71 -0
- data/app/views/quorum/jobs/_blastx_form.html.erb +71 -0
- data/app/views/quorum/jobs/_tblastn_form.html.erb +71 -0
- data/app/views/quorum/jobs/new.html.erb +45 -0
- data/app/views/quorum/jobs/show.html.erb +183 -0
- data/app/views/shared/_error_messages.html.erb +10 -0
- data/config/locales/en.yml +8 -0
- data/config/routes.rb +9 -0
- data/db/migrate/20111031204518_create_jobs.rb +12 -0
- data/db/migrate/20111031204701_create_blastn_jobs.rb +19 -0
- data/db/migrate/20111031204719_create_blastx_jobs.rb +19 -0
- data/db/migrate/20111031204733_create_blastp_jobs.rb +19 -0
- data/db/migrate/20111031204754_create_tblastn_jobs.rb +19 -0
- data/db/migrate/20111031204846_create_blastn_job_reports.rb +34 -0
- data/db/migrate/20111031204903_create_blastx_job_reports.rb +34 -0
- data/db/migrate/20111031204922_create_blastp_job_reports.rb +34 -0
- data/db/migrate/20111031204941_create_tblastn_job_reports.rb +34 -0
- data/lib/generators/quorum/install_generator.rb +68 -0
- data/lib/generators/quorum/styles_generator.rb +18 -0
- data/lib/generators/quorum/views_generator.rb +18 -0
- data/lib/generators/templates/README +25 -0
- data/lib/generators/templates/blast.rb +412 -0
- data/lib/generators/templates/logger.rb +43 -0
- data/lib/generators/templates/quorum_initializer.rb +36 -0
- data/lib/generators/templates/quorum_settings.yml +157 -0
- data/lib/generators/templates/search +141 -0
- data/lib/generators/templates/trollop.rb +781 -0
- data/lib/quorum/engine.rb +5 -0
- data/lib/quorum/helpers.rb +17 -0
- data/lib/quorum/sequence.rb +89 -0
- data/lib/quorum/version.rb +3 -0
- data/lib/quorum.rb +89 -0
- data/lib/tasks/blastdb/README +17 -0
- data/lib/tasks/blastdb/build_blast_db.rb +222 -0
- data/lib/tasks/quorum_resque.rake +3 -0
- data/lib/tasks/quorum_tasks.rake +50 -0
- data/lib/workers/quorum.rb +45 -0
- data/quorum.gemspec +29 -0
- data/script/rails +6 -0
- data/spec/data/nucl_prot_seqs.txt +36 -0
- data/spec/data/nucl_seqs.txt +32 -0
- data/spec/data/prot_seqs.txt +4 -0
- data/spec/data/seqs.docx +0 -0
- data/spec/data/seqs_not_fa.txt +16 -0
- data/spec/data/tmp/test.tgz +0 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +9 -0
- data/spec/dummy/app/assets/stylesheets/application.css +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/blast.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config/application.rb +46 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/environment.rb +20 -0
- data/spec/dummy/config/environments/development.rb +30 -0
- data/spec/dummy/config/environments/production.rb +60 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/quorum_initializer.rb +36 -0
- data/spec/dummy/config/initializers/resque.rb +1 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/locales/quorum.en.yml +8 -0
- data/spec/dummy/config/quorum_settings.yml +145 -0
- data/spec/dummy/config/routes.rb +9 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/schema.rb +214 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/quorum/bin/search +141 -0
- data/spec/dummy/quorum/blastdb/test/contigs.fa +2 -0
- data/spec/dummy/quorum/blastdb/test/peptides.fa +2 -0
- data/spec/dummy/quorum/blastdb/test.nhd +1 -0
- data/spec/dummy/quorum/blastdb/test.nhi +0 -0
- data/spec/dummy/quorum/blastdb/test.nhr +0 -0
- data/spec/dummy/quorum/blastdb/test.nin +0 -0
- data/spec/dummy/quorum/blastdb/test.nog +0 -0
- data/spec/dummy/quorum/blastdb/test.nsd +1 -0
- data/spec/dummy/quorum/blastdb/test.nsi +0 -0
- data/spec/dummy/quorum/blastdb/test.nsq +0 -0
- data/spec/dummy/quorum/blastdb/test.phd +1 -0
- data/spec/dummy/quorum/blastdb/test.phi +0 -0
- data/spec/dummy/quorum/blastdb/test.phr +0 -0
- data/spec/dummy/quorum/blastdb/test.pin +0 -0
- data/spec/dummy/quorum/blastdb/test.pog +0 -0
- data/spec/dummy/quorum/blastdb/test.psd +1 -0
- data/spec/dummy/quorum/blastdb/test.psi +0 -0
- data/spec/dummy/quorum/blastdb/test.psq +0 -0
- data/spec/dummy/quorum/lib/logger.rb +43 -0
- data/spec/dummy/quorum/lib/search_tools/blast.rb +412 -0
- data/spec/dummy/quorum/lib/trollop.rb +781 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/models/blastn_job_report_spec.rb +13 -0
- data/spec/models/blastn_job_spec.rb +103 -0
- data/spec/models/blastp_job_report_spec.rb +13 -0
- data/spec/models/blastp_job_spec.rb +103 -0
- data/spec/models/blastx_job_report_spec.rb +13 -0
- data/spec/models/blastx_job_spec.rb +103 -0
- data/spec/models/job_spec.rb +40 -0
- data/spec/models/tblastn_job_report_spec.rb +13 -0
- data/spec/models/tblastn_job_spec.rb +103 -0
- data/spec/quorum/quorum_sequence_spec.rb +64 -0
- data/spec/quorum_installed_spec.rb +64 -0
- data/spec/requests/jobs_spec.rb +138 -0
- data/spec/spec_helper.rb +45 -0
- data/spec/tasks/blastdb_rake_spec.rb +119 -0
- data/spec/templates/blast_spec.rb +8 -0
- data/spec/templates/logger_spec.rb +35 -0
- data/vendor/assets/javascripts/jquery.tipsy.js +241 -0
- data/vendor/assets/javascripts/underscore-min.js +30 -0
- metadata +325 -0
@@ -0,0 +1,138 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Jobs" do
|
4
|
+
describe "GET /" do
|
5
|
+
it "redirects to new" do
|
6
|
+
visit jobs_path
|
7
|
+
current_path.should eq(new_job_path)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "submit empty form" do
|
12
|
+
it "displays error and renders form" do
|
13
|
+
visit new_job_path
|
14
|
+
current_path.should eq(new_job_path)
|
15
|
+
|
16
|
+
fill_in "job_sequence", :with => ""
|
17
|
+
click_button "Submit"
|
18
|
+
|
19
|
+
page.should have_content("Please enter your sequence(s) in Plain Text " <<
|
20
|
+
"as FASTA.")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "submit sequences in Word file" do
|
25
|
+
it "displays error and renders form" do
|
26
|
+
visit new_job_path
|
27
|
+
current_path.should eq(new_job_path)
|
28
|
+
|
29
|
+
word_file = File.expand_path("../../data/seqs.docx", __FILE__)
|
30
|
+
attach_file "job_sequence_file", word_file
|
31
|
+
click_button "Submit"
|
32
|
+
page.should have_content("Please enter your sequence(s) in Plain Text " <<
|
33
|
+
"as FASTA.")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "submit sequences not in FASTA format" do
|
38
|
+
it "displays error and renders form" do
|
39
|
+
visit new_job_path
|
40
|
+
current_path.should eq(new_job_path)
|
41
|
+
|
42
|
+
file = File.expand_path("../../data/seqs_not_fa.txt", __FILE__)
|
43
|
+
attach_file "job_sequence_file", file
|
44
|
+
click_button "Submit"
|
45
|
+
page.should have_content("Please enter your sequence(s) in Plain Text " <<
|
46
|
+
"as FASTA.")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "javascript", @javascript do
|
51
|
+
before(:all) do
|
52
|
+
Capybara.current_driver = :selenium
|
53
|
+
end
|
54
|
+
describe "submit sequences in attached file" do
|
55
|
+
it "check Blast algorithms, fill in default values and view query results" do
|
56
|
+
visit new_job_path
|
57
|
+
current_path.should eq(new_job_path)
|
58
|
+
|
59
|
+
nucl_seqs = File.expand_path("../../data/nucl_prot_seqs.txt", __FILE__)
|
60
|
+
attach_file "job_sequence_file", nucl_seqs
|
61
|
+
|
62
|
+
# Blastn
|
63
|
+
check "job_blastn_job_attributes_queue"
|
64
|
+
select "tmp", :from => "job_blastn_job_attributes_blast_dbs"
|
65
|
+
check "job_blastn_job_attributes_filter"
|
66
|
+
fill_in "job_blastn_job_attributes_expectation", :with => "5e-20"
|
67
|
+
fill_in "job_blastn_job_attributes_min_bit_score", :with => "0"
|
68
|
+
fill_in "job_blastn_job_attributes_max_score", :with => "25"
|
69
|
+
select "Yes", :from => "job_blastn_job_attributes_gapped_alignments"
|
70
|
+
select "11, 2", :from => "job_blastn_job_attributes_gap_opening_extension"
|
71
|
+
|
72
|
+
# Blastx
|
73
|
+
check "job_blastx_job_attributes_queue"
|
74
|
+
select "tmp", :from => "job_blastx_job_attributes_blast_dbs"
|
75
|
+
check "job_blastx_job_attributes_filter"
|
76
|
+
fill_in "job_blastx_job_attributes_expectation", :with => "5e-20"
|
77
|
+
fill_in "job_blastx_job_attributes_min_bit_score", :with => "0"
|
78
|
+
fill_in "job_blastx_job_attributes_max_score", :with => "25"
|
79
|
+
select "Yes", :from => "job_blastx_job_attributes_gapped_alignments"
|
80
|
+
select "10, 2", :from => "job_blastx_job_attributes_gap_opening_extension"
|
81
|
+
|
82
|
+
# Tblastn
|
83
|
+
check "job_tblastn_job_attributes_queue"
|
84
|
+
select "tmp", :from => "job_tblastn_job_attributes_blast_dbs"
|
85
|
+
check "job_tblastn_job_attributes_filter"
|
86
|
+
fill_in "job_tblastn_job_attributes_expectation", :with => "5e-20"
|
87
|
+
fill_in "job_tblastn_job_attributes_min_bit_score", :with => "0"
|
88
|
+
fill_in "job_tblastn_job_attributes_max_score", :with => "25"
|
89
|
+
select "Yes", :from => "job_tblastn_job_attributes_gapped_alignments"
|
90
|
+
select "9, 2", :from => "job_tblastn_job_attributes_gap_opening_extension"
|
91
|
+
|
92
|
+
# Blastp
|
93
|
+
check "job_blastp_job_attributes_queue"
|
94
|
+
select "tmp", :from => "job_blastp_job_attributes_blast_dbs"
|
95
|
+
check "job_blastp_job_attributes_filter"
|
96
|
+
fill_in "job_blastp_job_attributes_expectation", :with => "5e-20"
|
97
|
+
fill_in "job_blastp_job_attributes_min_bit_score", :with => "0"
|
98
|
+
fill_in "job_blastp_job_attributes_max_score", :with => "25"
|
99
|
+
select "Yes", :from => "job_blastp_job_attributes_gapped_alignments"
|
100
|
+
select "13, 1", :from => "job_blastp_job_attributes_gap_opening_extension"
|
101
|
+
|
102
|
+
click_button "Submit"
|
103
|
+
|
104
|
+
page.should have_content("Search Results")
|
105
|
+
|
106
|
+
click_link "Blastx"
|
107
|
+
page.should have_content("Your search returned 0 hits.")
|
108
|
+
|
109
|
+
click_link "Tblastn"
|
110
|
+
page.should have_content("Your search returned 0 hits.")
|
111
|
+
|
112
|
+
click_link "Blastn"
|
113
|
+
find("#blastn-results").find("td a").click
|
114
|
+
page.should have_content("Quorum Report Details")
|
115
|
+
page.should have_content("qseq")
|
116
|
+
page.should have_content("hseq")
|
117
|
+
|
118
|
+
click_link "Blastp"
|
119
|
+
find("#blastp-results").find("td a").click
|
120
|
+
page.should have_content("Quorum Report Details")
|
121
|
+
page.should have_content("qseq")
|
122
|
+
page.should have_content("hseq")
|
123
|
+
end
|
124
|
+
end
|
125
|
+
after(:all) do
|
126
|
+
Capybara.use_default_driver
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe "GET /quorum/jobs/unknown_id" do
|
131
|
+
it "displays notice and renders form" do
|
132
|
+
visit job_path('12893479812347912')
|
133
|
+
page.should have_content("The data you requested is unavailable. Please check your URL and try again.")
|
134
|
+
current_path.should eq(new_job_path)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# This file is copied to spec/ when you run 'rails generate rspec:install'
|
2
|
+
ENV["RAILS_ENV"] ||= 'test'
|
3
|
+
require File.expand_path("../dummy/config/environment.rb", __FILE__)
|
4
|
+
require 'rspec/rails'
|
5
|
+
require 'capybara/rspec'
|
6
|
+
require 'factory_girl'
|
7
|
+
require 'database_cleaner'
|
8
|
+
|
9
|
+
FactoryGirl.definition_file_paths << File.join(File.dirname(__FILE__), 'factories')
|
10
|
+
FactoryGirl.find_definitions
|
11
|
+
|
12
|
+
ENGINE_RAILS_ROOT = File.expand_path("../../", __FILE__)
|
13
|
+
|
14
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
15
|
+
# in spec/support/ and its subdirectories.
|
16
|
+
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
|
17
|
+
|
18
|
+
RSpec.configure do |config|
|
19
|
+
config.mock_with :rspec
|
20
|
+
config.use_transactional_fixtures = false
|
21
|
+
config.include Capybara::DSL, :example_group => { :file_path => /\bspec\/requests\// }
|
22
|
+
|
23
|
+
# This will include the routing helpers in the specs so that we can use
|
24
|
+
# quorum_path, etc., to get to the routes.
|
25
|
+
config.include Quorum::Engine.routes.url_helpers
|
26
|
+
|
27
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
28
|
+
config.filter_run :focus => true
|
29
|
+
config.run_all_when_everything_filtered = true
|
30
|
+
|
31
|
+
## DatabaseCleaner ##
|
32
|
+
DatabaseCleaner.logger = Rails.logger
|
33
|
+
config.before(:suite) do
|
34
|
+
DatabaseCleaner.strategy = :truncation
|
35
|
+
DatabaseCleaner.clean_with(:truncation)
|
36
|
+
end
|
37
|
+
|
38
|
+
config.before(:each) do
|
39
|
+
DatabaseCleaner.start
|
40
|
+
end
|
41
|
+
|
42
|
+
config.after(:each) do
|
43
|
+
DatabaseCleaner.clean
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'tasks/blastdb/build_blast_db'
|
3
|
+
|
4
|
+
describe "blastdb rake tasks" do
|
5
|
+
describe "rake quorum:blastdb:build" do
|
6
|
+
before(:each) do
|
7
|
+
# Set args as though we executed the rake task.
|
8
|
+
@args = {
|
9
|
+
:dir => File.expand_path("../../data/tmp", __FILE__),
|
10
|
+
:empty => false,
|
11
|
+
:type => "both",
|
12
|
+
:prot_file => "peptides.fa",
|
13
|
+
:nucl_file => "contigs.fa",
|
14
|
+
:rebuild_db => true,
|
15
|
+
:blastdb_dir => File.expand_path(
|
16
|
+
"../../dummy/quorum/blastdb", __FILE__
|
17
|
+
),
|
18
|
+
:gff_dir => File.expand_path(
|
19
|
+
"../../dummy/quorum/gff3", __FILE__
|
20
|
+
),
|
21
|
+
:log_dir => File.expand_path(
|
22
|
+
"../../dummy/quorum/log", __FILE__
|
23
|
+
)
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
it "raises exception without DIR argument" do
|
28
|
+
expect {
|
29
|
+
Quorum::BuildBlastDB.new({}).build_blast_db_data
|
30
|
+
}.to raise_error(
|
31
|
+
RuntimeError,
|
32
|
+
'DIR must be set to continue. Execute `rake -D` for more information.'
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "raises exception with unknown TYPE" do
|
37
|
+
@args[:type] = "unknown"
|
38
|
+
expect {
|
39
|
+
Quorum::BuildBlastDB.new(@args).build_blast_db_data
|
40
|
+
}.to raise_error(
|
41
|
+
RuntimeError,
|
42
|
+
"Unknown type: #{@args[:type]}. Please provide one: both, nucl or prot."
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "raise exception with unknown directory" do
|
47
|
+
@args[:dir] = "foo_bar_baz"
|
48
|
+
@build = Quorum::BuildBlastDB.new(@args)
|
49
|
+
expect {
|
50
|
+
@build.build_blast_db_data
|
51
|
+
@build.stub(:check_dependencies)
|
52
|
+
@build.stub(:make_directories)
|
53
|
+
}.to raise_error(
|
54
|
+
RuntimeError,
|
55
|
+
"Directory not found: foo_bar_baz"
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "raise exception without correct data directory" do
|
60
|
+
@args[:dir] = File.dirname( __FILE__)
|
61
|
+
@build = Quorum::BuildBlastDB.new(@args)
|
62
|
+
expect {
|
63
|
+
@build.build_blast_db_data
|
64
|
+
@build.stub(:check_dependencies)
|
65
|
+
@build.stub(:make_directories)
|
66
|
+
}.to raise_error(
|
67
|
+
RuntimeError,
|
68
|
+
"Data not found. Please check your directory and try again.\n" <<
|
69
|
+
"Directory Entered: #{File.dirname(__FILE__)}"
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "creates directories and ignores makeblastdb if empty is true" do
|
74
|
+
# Suppress printing to STDOUT.
|
75
|
+
output = double("IO")
|
76
|
+
output.stub(:puts)
|
77
|
+
output.stub(:print)
|
78
|
+
|
79
|
+
@args[:empty] = true
|
80
|
+
@build = Quorum::BuildBlastDB.new(@args, output)
|
81
|
+
|
82
|
+
expect {
|
83
|
+
@build.build_blast_db_data
|
84
|
+
}.to_not raise_error
|
85
|
+
|
86
|
+
File.directory?(@args[:blastdb_dir]).should be_true
|
87
|
+
File.directory?(@args[:gff_dir]).should be_true
|
88
|
+
end
|
89
|
+
|
90
|
+
it "builds Blast database with valid arguments" do
|
91
|
+
# Suppress printing to STDOUT.
|
92
|
+
output = double("IO")
|
93
|
+
output.stub(:puts)
|
94
|
+
output.stub(:print)
|
95
|
+
|
96
|
+
@args[:empty] = false
|
97
|
+
@build = Quorum::BuildBlastDB.new(@args, output)
|
98
|
+
|
99
|
+
expect {
|
100
|
+
@build.build_blast_db_data
|
101
|
+
}.to_not raise_error
|
102
|
+
|
103
|
+
## Make sure build_blast_db_data created files and directories ##
|
104
|
+
|
105
|
+
Dir.glob(@args[:blastdb_dir]).length.should be > 0
|
106
|
+
|
107
|
+
File.exists?(
|
108
|
+
File.join(@args[:blastdb_dir], "test", "contigs.fa")
|
109
|
+
).should be_true
|
110
|
+
|
111
|
+
File.exists?(
|
112
|
+
File.join(@args[:blastdb_dir], "test", "peptides.fa")
|
113
|
+
).should be_true
|
114
|
+
|
115
|
+
File.directory?(@args[:gff_dir]).should be_true
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'generators/templates/logger'
|
3
|
+
|
4
|
+
describe "Quorum::Logger" do
|
5
|
+
describe "#log" do
|
6
|
+
before(:all) do
|
7
|
+
@args = File.join(::Rails.root.to_s, "log")
|
8
|
+
@logger = Quorum::Logger.new(@args)
|
9
|
+
end
|
10
|
+
|
11
|
+
it "records program and message in a log file" do
|
12
|
+
@logger.log("RSpec", "This is a test.")
|
13
|
+
|
14
|
+
File.size(
|
15
|
+
File.join(@args, "quorum.log")
|
16
|
+
).should be > 0
|
17
|
+
|
18
|
+
`rm #{File.join(@args, "quorum.log")}`
|
19
|
+
end
|
20
|
+
|
21
|
+
it "records program, message, exits and removes files" do
|
22
|
+
lambda {
|
23
|
+
@logger.log(
|
24
|
+
"RSpec", "This is a test.", 1,
|
25
|
+
File.join(@args, "quorum.log")
|
26
|
+
)
|
27
|
+
}.should raise_error(SystemExit)
|
28
|
+
|
29
|
+
File.exists?(
|
30
|
+
File.join(@args, "quorum.log")
|
31
|
+
).should be_false
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
// tipsy, facebook style tooltips for jquery
|
2
|
+
// version 1.0.0a
|
3
|
+
// (c) 2008-2010 jason frame [jason@onehackoranother.com]
|
4
|
+
// released under the MIT license
|
5
|
+
|
6
|
+
(function($) {
|
7
|
+
|
8
|
+
function maybeCall(thing, ctx) {
|
9
|
+
return (typeof thing == 'function') ? (thing.call(ctx)) : thing;
|
10
|
+
};
|
11
|
+
|
12
|
+
function Tipsy(element, options) {
|
13
|
+
this.$element = $(element);
|
14
|
+
this.options = options;
|
15
|
+
this.enabled = true;
|
16
|
+
this.fixTitle();
|
17
|
+
};
|
18
|
+
|
19
|
+
Tipsy.prototype = {
|
20
|
+
show: function() {
|
21
|
+
var title = this.getTitle();
|
22
|
+
if (title && this.enabled) {
|
23
|
+
var $tip = this.tip();
|
24
|
+
|
25
|
+
$tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title);
|
26
|
+
$tip[0].className = 'tipsy'; // reset classname in case of dynamic gravity
|
27
|
+
$tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).prependTo(document.body);
|
28
|
+
|
29
|
+
var pos = $.extend({}, this.$element.offset(), {
|
30
|
+
width: this.$element[0].offsetWidth,
|
31
|
+
height: this.$element[0].offsetHeight
|
32
|
+
});
|
33
|
+
|
34
|
+
var actualWidth = $tip[0].offsetWidth,
|
35
|
+
actualHeight = $tip[0].offsetHeight,
|
36
|
+
gravity = maybeCall(this.options.gravity, this.$element[0]);
|
37
|
+
|
38
|
+
var tp;
|
39
|
+
switch (gravity.charAt(0)) {
|
40
|
+
case 'n':
|
41
|
+
tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
|
42
|
+
break;
|
43
|
+
case 's':
|
44
|
+
tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
|
45
|
+
break;
|
46
|
+
case 'e':
|
47
|
+
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset};
|
48
|
+
break;
|
49
|
+
case 'w':
|
50
|
+
tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset};
|
51
|
+
break;
|
52
|
+
}
|
53
|
+
|
54
|
+
if (gravity.length == 2) {
|
55
|
+
if (gravity.charAt(1) == 'w') {
|
56
|
+
tp.left = pos.left + pos.width / 2 - 15;
|
57
|
+
} else {
|
58
|
+
tp.left = pos.left + pos.width / 2 - actualWidth + 15;
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
$tip.css(tp).addClass('tipsy-' + gravity);
|
63
|
+
$tip.find('.tipsy-arrow')[0].className = 'tipsy-arrow tipsy-arrow-' + gravity.charAt(0);
|
64
|
+
if (this.options.className) {
|
65
|
+
$tip.addClass(maybeCall(this.options.className, this.$element[0]));
|
66
|
+
}
|
67
|
+
|
68
|
+
if (this.options.fade) {
|
69
|
+
$tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity});
|
70
|
+
} else {
|
71
|
+
$tip.css({visibility: 'visible', opacity: this.options.opacity});
|
72
|
+
}
|
73
|
+
}
|
74
|
+
},
|
75
|
+
|
76
|
+
hide: function() {
|
77
|
+
if (this.options.fade) {
|
78
|
+
this.tip().stop().fadeOut(function() { $(this).remove(); });
|
79
|
+
} else {
|
80
|
+
this.tip().remove();
|
81
|
+
}
|
82
|
+
},
|
83
|
+
|
84
|
+
fixTitle: function() {
|
85
|
+
var $e = this.$element;
|
86
|
+
if ($e.attr('title') || typeof($e.attr('original-title')) != 'string') {
|
87
|
+
$e.attr('original-title', $e.attr('title') || '').removeAttr('title');
|
88
|
+
}
|
89
|
+
},
|
90
|
+
|
91
|
+
getTitle: function() {
|
92
|
+
var title, $e = this.$element, o = this.options;
|
93
|
+
this.fixTitle();
|
94
|
+
var title, o = this.options;
|
95
|
+
if (typeof o.title == 'string') {
|
96
|
+
title = $e.attr(o.title == 'title' ? 'original-title' : o.title);
|
97
|
+
} else if (typeof o.title == 'function') {
|
98
|
+
title = o.title.call($e[0]);
|
99
|
+
}
|
100
|
+
title = ('' + title).replace(/(^\s*|\s*$)/, "");
|
101
|
+
return title || o.fallback;
|
102
|
+
},
|
103
|
+
|
104
|
+
tip: function() {
|
105
|
+
if (!this.$tip) {
|
106
|
+
this.$tip = $('<div class="tipsy"></div>').html('<div class="tipsy-arrow"></div><div class="tipsy-inner"></div>');
|
107
|
+
}
|
108
|
+
return this.$tip;
|
109
|
+
},
|
110
|
+
|
111
|
+
validate: function() {
|
112
|
+
if (!this.$element[0].parentNode) {
|
113
|
+
this.hide();
|
114
|
+
this.$element = null;
|
115
|
+
this.options = null;
|
116
|
+
}
|
117
|
+
},
|
118
|
+
|
119
|
+
enable: function() { this.enabled = true; },
|
120
|
+
disable: function() { this.enabled = false; },
|
121
|
+
toggleEnabled: function() { this.enabled = !this.enabled; }
|
122
|
+
};
|
123
|
+
|
124
|
+
$.fn.tipsy = function(options) {
|
125
|
+
|
126
|
+
if (options === true) {
|
127
|
+
return this.data('tipsy');
|
128
|
+
} else if (typeof options == 'string') {
|
129
|
+
var tipsy = this.data('tipsy');
|
130
|
+
if (tipsy) tipsy[options]();
|
131
|
+
return this;
|
132
|
+
}
|
133
|
+
|
134
|
+
options = $.extend({}, $.fn.tipsy.defaults, options);
|
135
|
+
|
136
|
+
function get(ele) {
|
137
|
+
var tipsy = $.data(ele, 'tipsy');
|
138
|
+
if (!tipsy) {
|
139
|
+
tipsy = new Tipsy(ele, $.fn.tipsy.elementOptions(ele, options));
|
140
|
+
$.data(ele, 'tipsy', tipsy);
|
141
|
+
}
|
142
|
+
return tipsy;
|
143
|
+
}
|
144
|
+
|
145
|
+
function enter() {
|
146
|
+
var tipsy = get(this);
|
147
|
+
tipsy.hoverState = 'in';
|
148
|
+
if (options.delayIn == 0) {
|
149
|
+
tipsy.show();
|
150
|
+
} else {
|
151
|
+
tipsy.fixTitle();
|
152
|
+
setTimeout(function() { if (tipsy.hoverState == 'in') tipsy.show(); }, options.delayIn);
|
153
|
+
}
|
154
|
+
};
|
155
|
+
|
156
|
+
function leave() {
|
157
|
+
var tipsy = get(this);
|
158
|
+
tipsy.hoverState = 'out';
|
159
|
+
if (options.delayOut == 0) {
|
160
|
+
tipsy.hide();
|
161
|
+
} else {
|
162
|
+
setTimeout(function() { if (tipsy.hoverState == 'out') tipsy.hide(); }, options.delayOut);
|
163
|
+
}
|
164
|
+
};
|
165
|
+
|
166
|
+
if (!options.live) this.each(function() { get(this); });
|
167
|
+
|
168
|
+
if (options.trigger != 'manual') {
|
169
|
+
var binder = options.live ? 'live' : 'bind',
|
170
|
+
eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus',
|
171
|
+
eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur';
|
172
|
+
this[binder](eventIn, enter)[binder](eventOut, leave);
|
173
|
+
}
|
174
|
+
|
175
|
+
return this;
|
176
|
+
|
177
|
+
};
|
178
|
+
|
179
|
+
$.fn.tipsy.defaults = {
|
180
|
+
className: null,
|
181
|
+
delayIn: 0,
|
182
|
+
delayOut: 0,
|
183
|
+
fade: false,
|
184
|
+
fallback: '',
|
185
|
+
gravity: 'n',
|
186
|
+
html: false,
|
187
|
+
live: false,
|
188
|
+
offset: 0,
|
189
|
+
opacity: 0.8,
|
190
|
+
title: 'title',
|
191
|
+
trigger: 'hover'
|
192
|
+
};
|
193
|
+
|
194
|
+
// Overwrite this method to provide options on a per-element basis.
|
195
|
+
// For example, you could store the gravity in a 'tipsy-gravity' attribute:
|
196
|
+
// return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' });
|
197
|
+
// (remember - do not modify 'options' in place!)
|
198
|
+
$.fn.tipsy.elementOptions = function(ele, options) {
|
199
|
+
return $.metadata ? $.extend({}, options, $(ele).metadata()) : options;
|
200
|
+
};
|
201
|
+
|
202
|
+
$.fn.tipsy.autoNS = function() {
|
203
|
+
return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n';
|
204
|
+
};
|
205
|
+
|
206
|
+
$.fn.tipsy.autoWE = function() {
|
207
|
+
return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w';
|
208
|
+
};
|
209
|
+
|
210
|
+
/**
|
211
|
+
* yields a closure of the supplied parameters, producing a function that takes
|
212
|
+
* no arguments and is suitable for use as an autogravity function like so:
|
213
|
+
*
|
214
|
+
* @param margin (int) - distance from the viewable region edge that an
|
215
|
+
* element should be before setting its tooltip's gravity to be away
|
216
|
+
* from that edge.
|
217
|
+
* @param prefer (string, e.g. 'n', 'sw', 'w') - the direction to prefer
|
218
|
+
* if there are no viewable region edges effecting the tooltip's
|
219
|
+
* gravity. It will try to vary from this minimally, for example,
|
220
|
+
* if 'sw' is preferred and an element is near the right viewable
|
221
|
+
* region edge, but not the top edge, it will set the gravity for
|
222
|
+
* that element's tooltip to be 'se', preserving the southern
|
223
|
+
* component.
|
224
|
+
*/
|
225
|
+
$.fn.tipsy.autoBounds = function(margin, prefer) {
|
226
|
+
return function() {
|
227
|
+
var dir = {ns: prefer[0], ew: (prefer.length > 1 ? prefer[1] : false)},
|
228
|
+
boundTop = $(document).scrollTop() + margin,
|
229
|
+
boundLeft = $(document).scrollLeft() + margin,
|
230
|
+
$this = $(this);
|
231
|
+
|
232
|
+
if ($this.offset().top < boundTop) dir.ns = 'n';
|
233
|
+
if ($this.offset().left < boundLeft) dir.ew = 'w';
|
234
|
+
if ($(window).width() + $(document).scrollLeft() - $this.offset().left < margin) dir.ew = 'e';
|
235
|
+
if ($(window).height() + $(document).scrollTop() - $this.offset().top < margin) dir.ns = 's';
|
236
|
+
|
237
|
+
return dir.ns + (dir.ew ? dir.ew : '');
|
238
|
+
}
|
239
|
+
};
|
240
|
+
|
241
|
+
})(jQuery);
|
@@ -0,0 +1,30 @@
|
|
1
|
+
// Underscore.js 1.2.2
|
2
|
+
// (c) 2011 Jeremy Ashkenas, DocumentCloud Inc.
|
3
|
+
// Underscore is freely distributable under the MIT license.
|
4
|
+
// Portions of Underscore are inspired or borrowed from Prototype,
|
5
|
+
// Oliver Steele's Functional, and John Resig's Micro-Templating.
|
6
|
+
// For all details and documentation:
|
7
|
+
// http://documentcloud.github.com/underscore
|
8
|
+
(function(){function r(a,c,d){if(a===c)return a!==0||1/a==1/c;if(a==null||c==null)return a===c;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(b.isFunction(a.isEqual))return a.isEqual(c);if(b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return false;switch(e){case "[object String]":return String(a)==String(c);case "[object Number]":return a=+a,c=+c,a!=a?c!=c:a==0?1/a==1/c:a==c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
|
9
|
+
c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if(typeof a!="object"||typeof c!="object")return false;for(var f=d.length;f--;)if(d[f]==a)return true;d.push(a);var f=0,g=true;if(e=="[object Array]"){if(f=a.length,g=f==c.length)for(;f--;)if(!(g=f in a==f in c&&r(a[f],c[f],d)))break}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return false;for(var h in a)if(m.call(a,h)&&(f++,!(g=m.call(c,h)&&r(a[h],c[h],d))))break;if(g){for(h in c)if(m.call(c,
|
10
|
+
h)&&!f--)break;g=!f}}d.pop();return g}var s=this,F=s._,o={},k=Array.prototype,p=Object.prototype,i=k.slice,G=k.unshift,l=p.toString,m=p.hasOwnProperty,v=k.forEach,w=k.map,x=k.reduce,y=k.reduceRight,z=k.filter,A=k.every,B=k.some,q=k.indexOf,C=k.lastIndexOf,p=Array.isArray,H=Object.keys,t=Function.prototype.bind,b=function(a){return new n(a)};if(typeof exports!=="undefined"){if(typeof module!=="undefined"&&module.exports)exports=module.exports=b;exports._=b}else typeof define==="function"&&define.amd?
|
11
|
+
define("underscore",function(){return b}):s._=b;b.VERSION="1.2.2";var j=b.each=b.forEach=function(a,c,b){if(a!=null)if(v&&a.forEach===v)a.forEach(c,b);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(b,a[e],e,a)===o)break}else for(e in a)if(m.call(a,e)&&c.call(b,a[e],e,a)===o)break};b.map=function(a,c,b){var e=[];if(a==null)return e;if(w&&a.map===w)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=
|
12
|
+
d!==void 0;a==null&&(a=[]);if(x&&a.reduce===x)return e&&(c=b.bind(c,e)),f?a.reduce(c,d):a.reduce(c);j(a,function(a,b,i){f?d=c.call(e,d,a,b,i):(d=a,f=true)});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){a==null&&(a=[]);if(y&&a.reduceRight===y)return e&&(c=b.bind(c,e)),d!==void 0?a.reduceRight(c,d):a.reduceRight(c);a=(b.isArray(a)?a.slice():b.toArray(a)).reverse();return b.reduce(a,c,d,e)};b.find=b.detect=function(a,c,b){var e;
|
13
|
+
D(a,function(a,g,h){if(c.call(b,a,g,h))return e=a,true});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(z&&a.filter===z)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(A&&a.every===A)return a.every(c,b);j(a,function(a,g,h){if(!(e=e&&c.call(b,a,g,h)))return o});
|
14
|
+
return e};var D=b.some=b.any=function(a,c,d){var c=c||b.identity,e=false;if(a==null)return e;if(B&&a.some===B)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return o});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;return q&&a.indexOf===q?a.indexOf(c)!=-1:b=D(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(c.call?c||a:a[c]).apply(a,d)})};b.pluck=function(a,c){return b.map(a,function(a){return a[c]})};
|
15
|
+
b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&(e={value:a,computed:b})});return e.value};b.shuffle=function(a){var c=[],b;
|
16
|
+
j(a,function(a,f){f==0?c[0]=a:(b=Math.floor(Math.random()*(f+1)),c[f]=c[b],c[b]=a)});return c};b.sortBy=function(a,c,d){return b.pluck(b.map(a,function(a,b,g){return{value:a,criteria:c.call(d,a,b,g)}}).sort(function(a,c){var b=a.criteria,d=c.criteria;return b<d?-1:b>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};j(a,function(a,c){var b=e(a,c);(d[b]||(d[b]=[])).push(a)});return d};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e<
|
17
|
+
f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:a.toArray?a.toArray():b.isArray(a)?i.call(a):b.isArguments(a)?i.call(a):b.values(a)};b.size=function(a){return b.toArray(a).length};b.first=b.head=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=b.tail=function(a,b,d){return i.call(a,b==null||
|
18
|
+
d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,e=[];b.reduce(d,function(d,g,h){if(0==h||(c===true?b.last(d)!=g:!b.include(d,g)))d[d.length]=g,e[e.length]=a[h];return d},[]);return e};b.union=function(){return b.uniq(b.flatten(arguments,
|
19
|
+
true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a,c){return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,d){if(a==null)return-1;var e;if(d)return d=b.sortedIndex(a,c),a[d]===c?d:-1;if(q&&a.indexOf===q)return a.indexOf(c);
|
20
|
+
for(d=0,e=a.length;d<e;d++)if(a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(C&&a.lastIndexOf===C)return a.lastIndexOf(b);for(var d=a.length;d--;)if(a[d]===b)return d;return-1};b.range=function(a,b,d){arguments.length<=1&&(b=a||0,a=0);for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;)g[f++]=a,a+=d;return g};var E=function(){};b.bind=function(a,c){var d,e;if(a.bind===t&&t)return t.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;
|
21
|
+
e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));E.prototype=a.prototype;var b=new E,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,c){var d={};c||(c=b.identity);return function(){var b=c.apply(this,arguments);return m.call(d,b)?d[b]:d[b]=a.apply(this,arguments)}};b.delay=
|
22
|
+
function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(a,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i=b.debounce(function(){h=g=false},c);return function(){d=this;e=arguments;var b;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);i()},c));g?h=true:a.apply(d,e);i();g=true}};b.debounce=function(a,b){var d;return function(){var e=this,f=arguments;clearTimeout(d);d=setTimeout(function(){d=
|
23
|
+
null;a.apply(e,f)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments));return b.apply(this,d)}};b.compose=function(){var a=i.call(arguments);return function(){for(var b=i.call(arguments),d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=H||function(a){if(a!==
|
24
|
+
Object(a))throw new TypeError("Invalid object");var b=[],d;for(d in a)m.call(a,d)&&(b[b.length]=d);return b};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)b[d]!==void 0&&(a[d]=b[d])});return a};b.defaults=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?
|
25
|
+
a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return r(a,b,[])};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(m.call(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};b.isArguments=l.call(arguments)=="[object Arguments]"?function(a){return l.call(a)=="[object Arguments]"}:
|
26
|
+
function(a){return!(!a||!m.call(a,"callee"))};b.isFunction=function(a){return l.call(a)=="[object Function]"};b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};
|
27
|
+
b.isUndefined=function(a){return a===void 0};b.noConflict=function(){s._=F;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.mixin=function(a){j(b.functions(a),function(c){I(c,b[c]=a[c])})};var J=0;b.uniqueId=function(a){var b=J++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,
|
28
|
+
interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};b.template=function(a,c){var d=b.templateSettings,d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.escape,function(a,b){return"',_.escape("+b.replace(/\\'/g,"'")+"),'"}).replace(d.interpolate,function(a,b){return"',"+b.replace(/\\'/g,"'")+",'"}).replace(d.evaluate||null,function(a,b){return"');"+b.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+";__p.push('"}).replace(/\r/g,
|
29
|
+
"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');",e=new Function("obj","_",d);return c?e(c,b):function(a){return e(a,b)}};var n=function(a){this._wrapped=a};b.prototype=n.prototype;var u=function(a,c){return c?b(a).chain():a},I=function(a,c){n.prototype[a]=function(){var a=i.call(arguments);G.call(a,this._wrapped);return u(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];n.prototype[a]=function(){b.apply(this._wrapped,
|
30
|
+
arguments);return u(this._wrapped,this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];n.prototype[a]=function(){return u(b.apply(this._wrapped,arguments),this._chain)}});n.prototype.chain=function(){this._chain=true;return this};n.prototype.value=function(){return this._wrapped}}).call(this);
|