quorum 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. data/.gitignore +10 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +15 -0
  4. data/Gemfile.lock +170 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.rdoc +318 -0
  7. data/Rakefile +31 -0
  8. data/app/assets/images/quorum/.gitkeep +0 -0
  9. data/app/assets/images/quorum/bg.png +0 -0
  10. data/app/assets/images/quorum/knight_rider.gif +0 -0
  11. data/app/assets/images/quorum/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  12. data/app/assets/images/quorum/ui-bg_flat_75_ffffff_40x100.png +0 -0
  13. data/app/assets/images/quorum/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  14. data/app/assets/images/quorum/ui-bg_glass_65_ffffff_1x400.png +0 -0
  15. data/app/assets/images/quorum/ui-bg_glass_75_dadada_1x400.png +0 -0
  16. data/app/assets/images/quorum/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  17. data/app/assets/images/quorum/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  18. data/app/assets/images/quorum/ui-bg_inset-soft_95_fef1ec_1x100.png +0 -0
  19. data/app/assets/images/quorum/ui-icons_222222_256x240.png +0 -0
  20. data/app/assets/images/quorum/ui-icons_2e83ff_256x240.png +0 -0
  21. data/app/assets/images/quorum/ui-icons_454545_256x240.png +0 -0
  22. data/app/assets/images/quorum/ui-icons_888888_256x240.png +0 -0
  23. data/app/assets/images/quorum/ui-icons_cd0a0a_256x240.png +0 -0
  24. data/app/assets/javascripts/quorum/application.js +13 -0
  25. data/app/assets/javascripts/quorum/jobs.js +386 -0
  26. data/app/assets/stylesheets/quorum/application.css +224 -0
  27. data/app/assets/stylesheets/quorum/jobs.css +72 -0
  28. data/app/assets/stylesheets/quorum/jquery-ui-1.8.16.custom.css +568 -0
  29. data/app/assets/stylesheets/quorum/tipsy.css +25 -0
  30. data/app/controllers/quorum/application_controller.rb +5 -0
  31. data/app/controllers/quorum/jobs_controller.rb +102 -0
  32. data/app/helpers/quorum/application_helper.rb +4 -0
  33. data/app/models/quorum/blastn_job.rb +111 -0
  34. data/app/models/quorum/blastn_job_report.rb +7 -0
  35. data/app/models/quorum/blastp_job.rb +111 -0
  36. data/app/models/quorum/blastp_job_report.rb +7 -0
  37. data/app/models/quorum/blastx_job.rb +111 -0
  38. data/app/models/quorum/blastx_job_report.rb +7 -0
  39. data/app/models/quorum/job.rb +164 -0
  40. data/app/models/quorum/tblastn_job.rb +111 -0
  41. data/app/models/quorum/tblastn_job_report.rb +7 -0
  42. data/app/views/layouts/quorum/application.html.erb +15 -0
  43. data/app/views/quorum/jobs/_blastn_form.html.erb +71 -0
  44. data/app/views/quorum/jobs/_blastp_form.html.erb +71 -0
  45. data/app/views/quorum/jobs/_blastx_form.html.erb +71 -0
  46. data/app/views/quorum/jobs/_tblastn_form.html.erb +71 -0
  47. data/app/views/quorum/jobs/new.html.erb +45 -0
  48. data/app/views/quorum/jobs/show.html.erb +183 -0
  49. data/app/views/shared/_error_messages.html.erb +10 -0
  50. data/config/locales/en.yml +8 -0
  51. data/config/routes.rb +9 -0
  52. data/db/migrate/20111031204518_create_jobs.rb +12 -0
  53. data/db/migrate/20111031204701_create_blastn_jobs.rb +19 -0
  54. data/db/migrate/20111031204719_create_blastx_jobs.rb +19 -0
  55. data/db/migrate/20111031204733_create_blastp_jobs.rb +19 -0
  56. data/db/migrate/20111031204754_create_tblastn_jobs.rb +19 -0
  57. data/db/migrate/20111031204846_create_blastn_job_reports.rb +34 -0
  58. data/db/migrate/20111031204903_create_blastx_job_reports.rb +34 -0
  59. data/db/migrate/20111031204922_create_blastp_job_reports.rb +34 -0
  60. data/db/migrate/20111031204941_create_tblastn_job_reports.rb +34 -0
  61. data/lib/generators/quorum/install_generator.rb +68 -0
  62. data/lib/generators/quorum/styles_generator.rb +18 -0
  63. data/lib/generators/quorum/views_generator.rb +18 -0
  64. data/lib/generators/templates/README +25 -0
  65. data/lib/generators/templates/blast.rb +412 -0
  66. data/lib/generators/templates/logger.rb +43 -0
  67. data/lib/generators/templates/quorum_initializer.rb +36 -0
  68. data/lib/generators/templates/quorum_settings.yml +157 -0
  69. data/lib/generators/templates/search +141 -0
  70. data/lib/generators/templates/trollop.rb +781 -0
  71. data/lib/quorum/engine.rb +5 -0
  72. data/lib/quorum/helpers.rb +17 -0
  73. data/lib/quorum/sequence.rb +89 -0
  74. data/lib/quorum/version.rb +3 -0
  75. data/lib/quorum.rb +89 -0
  76. data/lib/tasks/blastdb/README +17 -0
  77. data/lib/tasks/blastdb/build_blast_db.rb +222 -0
  78. data/lib/tasks/quorum_resque.rake +3 -0
  79. data/lib/tasks/quorum_tasks.rake +50 -0
  80. data/lib/workers/quorum.rb +45 -0
  81. data/quorum.gemspec +29 -0
  82. data/script/rails +6 -0
  83. data/spec/data/nucl_prot_seqs.txt +36 -0
  84. data/spec/data/nucl_seqs.txt +32 -0
  85. data/spec/data/prot_seqs.txt +4 -0
  86. data/spec/data/seqs.docx +0 -0
  87. data/spec/data/seqs_not_fa.txt +16 -0
  88. data/spec/data/tmp/test.tgz +0 -0
  89. data/spec/dummy/Rakefile +7 -0
  90. data/spec/dummy/app/assets/javascripts/application.js +9 -0
  91. data/spec/dummy/app/assets/stylesheets/application.css +7 -0
  92. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  93. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  94. data/spec/dummy/app/mailers/.gitkeep +0 -0
  95. data/spec/dummy/app/models/.gitkeep +0 -0
  96. data/spec/dummy/app/models/blast.rb +2 -0
  97. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  98. data/spec/dummy/config/application.rb +46 -0
  99. data/spec/dummy/config/boot.rb +10 -0
  100. data/spec/dummy/config/environment.rb +20 -0
  101. data/spec/dummy/config/environments/development.rb +30 -0
  102. data/spec/dummy/config/environments/production.rb +60 -0
  103. data/spec/dummy/config/environments/test.rb +42 -0
  104. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  105. data/spec/dummy/config/initializers/inflections.rb +10 -0
  106. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  107. data/spec/dummy/config/initializers/quorum_initializer.rb +36 -0
  108. data/spec/dummy/config/initializers/resque.rb +1 -0
  109. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  110. data/spec/dummy/config/initializers/session_store.rb +8 -0
  111. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  112. data/spec/dummy/config/locales/en.yml +5 -0
  113. data/spec/dummy/config/locales/quorum.en.yml +8 -0
  114. data/spec/dummy/config/quorum_settings.yml +145 -0
  115. data/spec/dummy/config/routes.rb +9 -0
  116. data/spec/dummy/config.ru +4 -0
  117. data/spec/dummy/db/schema.rb +214 -0
  118. data/spec/dummy/lib/assets/.gitkeep +0 -0
  119. data/spec/dummy/log/.gitkeep +0 -0
  120. data/spec/dummy/public/404.html +26 -0
  121. data/spec/dummy/public/422.html +26 -0
  122. data/spec/dummy/public/500.html +26 -0
  123. data/spec/dummy/public/favicon.ico +0 -0
  124. data/spec/dummy/quorum/bin/search +141 -0
  125. data/spec/dummy/quorum/blastdb/test/contigs.fa +2 -0
  126. data/spec/dummy/quorum/blastdb/test/peptides.fa +2 -0
  127. data/spec/dummy/quorum/blastdb/test.nhd +1 -0
  128. data/spec/dummy/quorum/blastdb/test.nhi +0 -0
  129. data/spec/dummy/quorum/blastdb/test.nhr +0 -0
  130. data/spec/dummy/quorum/blastdb/test.nin +0 -0
  131. data/spec/dummy/quorum/blastdb/test.nog +0 -0
  132. data/spec/dummy/quorum/blastdb/test.nsd +1 -0
  133. data/spec/dummy/quorum/blastdb/test.nsi +0 -0
  134. data/spec/dummy/quorum/blastdb/test.nsq +0 -0
  135. data/spec/dummy/quorum/blastdb/test.phd +1 -0
  136. data/spec/dummy/quorum/blastdb/test.phi +0 -0
  137. data/spec/dummy/quorum/blastdb/test.phr +0 -0
  138. data/spec/dummy/quorum/blastdb/test.pin +0 -0
  139. data/spec/dummy/quorum/blastdb/test.pog +0 -0
  140. data/spec/dummy/quorum/blastdb/test.psd +1 -0
  141. data/spec/dummy/quorum/blastdb/test.psi +0 -0
  142. data/spec/dummy/quorum/blastdb/test.psq +0 -0
  143. data/spec/dummy/quorum/lib/logger.rb +43 -0
  144. data/spec/dummy/quorum/lib/search_tools/blast.rb +412 -0
  145. data/spec/dummy/quorum/lib/trollop.rb +781 -0
  146. data/spec/dummy/script/rails +6 -0
  147. data/spec/models/blastn_job_report_spec.rb +13 -0
  148. data/spec/models/blastn_job_spec.rb +103 -0
  149. data/spec/models/blastp_job_report_spec.rb +13 -0
  150. data/spec/models/blastp_job_spec.rb +103 -0
  151. data/spec/models/blastx_job_report_spec.rb +13 -0
  152. data/spec/models/blastx_job_spec.rb +103 -0
  153. data/spec/models/job_spec.rb +40 -0
  154. data/spec/models/tblastn_job_report_spec.rb +13 -0
  155. data/spec/models/tblastn_job_spec.rb +103 -0
  156. data/spec/quorum/quorum_sequence_spec.rb +64 -0
  157. data/spec/quorum_installed_spec.rb +64 -0
  158. data/spec/requests/jobs_spec.rb +138 -0
  159. data/spec/spec_helper.rb +45 -0
  160. data/spec/tasks/blastdb_rake_spec.rb +119 -0
  161. data/spec/templates/blast_spec.rb +8 -0
  162. data/spec/templates/logger_spec.rb +35 -0
  163. data/vendor/assets/javascripts/jquery.tipsy.js +241 -0
  164. data/vendor/assets/javascripts/underscore-min.js +30 -0
  165. 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
@@ -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,8 @@
1
+ require 'spec_helper'
2
+ require 'generators/templates/blast'
3
+
4
+ # Let the request specs handle Quorum::SearchTools
5
+ describe "Quorum::SearchTools::Blast" do
6
+ describe "#execute_blast" do
7
+ end
8
+ end
@@ -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,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")};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);