naf 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (295) hide show
  1. data/.gitignore +16 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +17 -0
  4. data/Gemfile +17 -0
  5. data/LICENSE +2 -0
  6. data/README.rdoc +22 -0
  7. data/RELEASE_NOTES.rdoc +18 -0
  8. data/Rakefile +43 -0
  9. data/app/assets/images/bg-grad.png +0 -0
  10. data/app/assets/images/clock.png +0 -0
  11. data/app/assets/images/control_play_blue.png +0 -0
  12. data/app/assets/images/down_arrow.gif +0 -0
  13. data/app/assets/images/papertrail_job.png +0 -0
  14. data/app/assets/images/papertrail_machine.png +0 -0
  15. data/app/assets/images/papertrail_machine_runner.png +0 -0
  16. data/app/assets/images/terminate.png +0 -0
  17. data/app/assets/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  18. data/app/assets/images/ui-bg_flat_0_ffffff_40x100.png +0 -0
  19. data/app/assets/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  20. data/app/assets/images/ui-bg_glass_0_f4f4f4_1x400.png +0 -0
  21. data/app/assets/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  22. data/app/assets/images/ui-bg_glass_65_f4f4f4_1x400.png +0 -0
  23. data/app/assets/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  24. data/app/assets/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  25. data/app/assets/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  26. data/app/assets/images/ui-bg_glass_75_f4f4f4_1x400.png +0 -0
  27. data/app/assets/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  28. data/app/assets/images/ui-bg_highlight-soft_0_f4f4f4_1x100.png +0 -0
  29. data/app/assets/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  30. data/app/assets/images/ui-icons_222222_256x240.png +0 -0
  31. data/app/assets/images/ui-icons_2e83ff_256x240.png +0 -0
  32. data/app/assets/images/ui-icons_454545_256x240.png +0 -0
  33. data/app/assets/images/ui-icons_888888_256x240.png +0 -0
  34. data/app/assets/images/ui-icons_cd0a0a_256x240.png +0 -0
  35. data/app/assets/images/up_arrow.gif +0 -0
  36. data/app/assets/javascripts/dataTablesTemplates/applications.js +94 -0
  37. data/app/assets/javascripts/dataTablesTemplates/jobs.js +163 -0
  38. data/app/assets/javascripts/dataTablesTemplates/machine_runner_invocations.js +60 -0
  39. data/app/assets/javascripts/dataTablesTemplates/machine_runners.js +82 -0
  40. data/app/assets/javascripts/dataTablesTemplates/machines.js +93 -0
  41. data/app/assets/javascripts/date.js +104 -0
  42. data/app/assets/javascripts/iso8601.js +41 -0
  43. data/app/assets/javascripts/jquery.dataTables.custom.js +62 -0
  44. data/app/assets/javascripts/jquery.dataTables.js +6862 -0
  45. data/app/assets/javascripts/naf.js +30 -0
  46. data/app/assets/javascripts/underscore.js +713 -0
  47. data/app/assets/stylesheets/jquery_ui/jquery-ui-1.8.5.custom.css.erb +572 -0
  48. data/app/assets/stylesheets/min_naf.css +14 -0
  49. data/app/assets/stylesheets/min_naf/layout.css.scss +355 -0
  50. data/app/assets/stylesheets/naf.css +14 -0
  51. data/app/assets/stylesheets/naf/layout.css.scss +497 -0
  52. data/app/controllers/naf/affinities_controller.rb +61 -0
  53. data/app/controllers/naf/application_controller.rb +43 -0
  54. data/app/controllers/naf/application_schedule_affinity_tabs_controller.rb +75 -0
  55. data/app/controllers/naf/applications_controller.rb +153 -0
  56. data/app/controllers/naf/historical_job_affinity_tabs_controller.rb +65 -0
  57. data/app/controllers/naf/historical_jobs_controller.rb +159 -0
  58. data/app/controllers/naf/janitorial_assignments_controller.rb +77 -0
  59. data/app/controllers/naf/logger_names_controller.rb +58 -0
  60. data/app/controllers/naf/logger_styles_controller.rb +59 -0
  61. data/app/controllers/naf/machine_affinity_slots_controller.rb +69 -0
  62. data/app/controllers/naf/machine_runner_invocations_controller.rb +59 -0
  63. data/app/controllers/naf/machine_runners_controller.rb +26 -0
  64. data/app/controllers/naf/machines_controller.rb +95 -0
  65. data/app/helpers/naf/application_helper.rb +275 -0
  66. data/app/models/log4r/papertrail_outputter.rb +19 -0
  67. data/app/models/logical/naf/application.rb +183 -0
  68. data/app/models/logical/naf/construction_zone/ad_hoc_work_order.rb +22 -0
  69. data/app/models/logical/naf/construction_zone/application_schedule_work_order.rb +15 -0
  70. data/app/models/logical/naf/construction_zone/application_work_order.rb +25 -0
  71. data/app/models/logical/naf/construction_zone/boss.rb +123 -0
  72. data/app/models/logical/naf/construction_zone/foreman.rb +53 -0
  73. data/app/models/logical/naf/construction_zone/proletariat.rb +40 -0
  74. data/app/models/logical/naf/construction_zone/work_order.rb +100 -0
  75. data/app/models/logical/naf/create_infrastructure.rb +48 -0
  76. data/app/models/logical/naf/job.rb +357 -0
  77. data/app/models/logical/naf/job_creator.rb +155 -0
  78. data/app/models/logical/naf/job_fetcher.rb +167 -0
  79. data/app/models/logical/naf/job_statuses/errored.rb +27 -0
  80. data/app/models/logical/naf/job_statuses/finished.rb +26 -0
  81. data/app/models/logical/naf/job_statuses/finished_less_minute.rb +25 -0
  82. data/app/models/logical/naf/job_statuses/queued.rb +32 -0
  83. data/app/models/logical/naf/job_statuses/running.rb +34 -0
  84. data/app/models/logical/naf/job_statuses/terminated.rb +25 -0
  85. data/app/models/logical/naf/job_statuses/waiting.rb +43 -0
  86. data/app/models/logical/naf/machine.rb +85 -0
  87. data/app/models/logical/naf/machine_runner.rb +46 -0
  88. data/app/models/logical/naf/machine_runner_invocation.rb +50 -0
  89. data/app/models/logical/naf/pickler.rb +74 -0
  90. data/app/models/logical/naf/unpickler.rb +98 -0
  91. data/app/models/naf/affinity.rb +145 -0
  92. data/app/models/naf/affinity_classification.rb +44 -0
  93. data/app/models/naf/application.rb +100 -0
  94. data/app/models/naf/application_run_group_restriction.rb +39 -0
  95. data/app/models/naf/application_schedule.rb +181 -0
  96. data/app/models/naf/application_schedule_affinity_tab.rb +86 -0
  97. data/app/models/naf/application_schedule_prerequisite.rb +50 -0
  98. data/app/models/naf/application_type.rb +72 -0
  99. data/app/models/naf/by_historical_job_id.rb +86 -0
  100. data/app/models/naf/historical_job.rb +334 -0
  101. data/app/models/naf/historical_job_affinity_tab.rb +61 -0
  102. data/app/models/naf/historical_job_prerequisite.rb +19 -0
  103. data/app/models/naf/janitorial_archive_assignment.rb +36 -0
  104. data/app/models/naf/janitorial_assignment.rb +37 -0
  105. data/app/models/naf/janitorial_create_assignment.rb +36 -0
  106. data/app/models/naf/janitorial_drop_assignment.rb +36 -0
  107. data/app/models/naf/logger_level.rb +21 -0
  108. data/app/models/naf/logger_name.rb +23 -0
  109. data/app/models/naf/logger_style.rb +58 -0
  110. data/app/models/naf/logger_style_name.rb +28 -0
  111. data/app/models/naf/machine.rb +257 -0
  112. data/app/models/naf/machine_affinity_slot.rb +78 -0
  113. data/app/models/naf/machine_runner.rb +51 -0
  114. data/app/models/naf/machine_runner_invocation.rb +71 -0
  115. data/app/models/naf/naf_base.rb +9 -0
  116. data/app/models/naf/queued_job.rb +164 -0
  117. data/app/models/naf/running_job.rb +80 -0
  118. data/app/models/process/naf/application.rb +164 -0
  119. data/app/models/process/naf/janitor.rb +117 -0
  120. data/app/models/process/naf/machine_manager.rb +150 -0
  121. data/app/models/process/naf/machine_upgrader.rb +112 -0
  122. data/app/models/process/naf/runner.rb +539 -0
  123. data/app/views/naf/affinities/_form.html.erb +50 -0
  124. data/app/views/naf/affinities/edit.html.erb +11 -0
  125. data/app/views/naf/affinities/index.html.erb +57 -0
  126. data/app/views/naf/affinities/new.html.erb +15 -0
  127. data/app/views/naf/affinities/show.html.erb +48 -0
  128. data/app/views/naf/application_schedule_affinity_tabs/_form.html.erb +31 -0
  129. data/app/views/naf/application_schedule_affinity_tabs/edit.html.erb +12 -0
  130. data/app/views/naf/application_schedule_affinity_tabs/new.html.erb +11 -0
  131. data/app/views/naf/applications/_application_schedule.html.erb +80 -0
  132. data/app/views/naf/applications/_application_schedule_prerequisites.html.erb +14 -0
  133. data/app/views/naf/applications/_form.html.erb +109 -0
  134. data/app/views/naf/applications/_search_container.html.erb +94 -0
  135. data/app/views/naf/applications/_show.html.erb +34 -0
  136. data/app/views/naf/applications/edit.html.erb +11 -0
  137. data/app/views/naf/applications/index.html.erb +51 -0
  138. data/app/views/naf/applications/index.json.erb +11 -0
  139. data/app/views/naf/applications/new.html.erb +11 -0
  140. data/app/views/naf/applications/show.html.erb +203 -0
  141. data/app/views/naf/datatable.html.erb +49 -0
  142. data/app/views/naf/historical_job_affinity_tabs/_form.html.erb +36 -0
  143. data/app/views/naf/historical_job_affinity_tabs/edit.html.erb +11 -0
  144. data/app/views/naf/historical_job_affinity_tabs/new.html.erb +11 -0
  145. data/app/views/naf/historical_jobs/_form.html.erb +94 -0
  146. data/app/views/naf/historical_jobs/_runners.html.erb +22 -0
  147. data/app/views/naf/historical_jobs/_search_container.html.erb +140 -0
  148. data/app/views/naf/historical_jobs/edit.html.erb +11 -0
  149. data/app/views/naf/historical_jobs/index.html.erb +48 -0
  150. data/app/views/naf/historical_jobs/index.json.erb +26 -0
  151. data/app/views/naf/historical_jobs/new.html.erb +61 -0
  152. data/app/views/naf/historical_jobs/show.html.erb +201 -0
  153. data/app/views/naf/janitorial_assignments/_form.html.erb +38 -0
  154. data/app/views/naf/janitorial_assignments/_rows.html.erb +17 -0
  155. data/app/views/naf/janitorial_assignments/edit.html.erb +11 -0
  156. data/app/views/naf/janitorial_assignments/index.html.erb +56 -0
  157. data/app/views/naf/janitorial_assignments/index.js.erb +1 -0
  158. data/app/views/naf/janitorial_assignments/new.html.erb +11 -0
  159. data/app/views/naf/layouts/jquery_datatables.json.erb +6 -0
  160. data/app/views/naf/logger_names/_form.html.erb +18 -0
  161. data/app/views/naf/logger_names/edit.html.erb +11 -0
  162. data/app/views/naf/logger_names/new.html.erb +11 -0
  163. data/app/views/naf/logger_names/show.html.erb +44 -0
  164. data/app/views/naf/logger_styles/_form.html.erb +30 -0
  165. data/app/views/naf/logger_styles/_logger_style_names.html.erb +19 -0
  166. data/app/views/naf/logger_styles/edit.html.erb +11 -0
  167. data/app/views/naf/logger_styles/new.html.erb +11 -0
  168. data/app/views/naf/logger_styles/show.html.erb +48 -0
  169. data/app/views/naf/machine_affinity_slots/_form.html.erb +36 -0
  170. data/app/views/naf/machine_affinity_slots/edit.html.erb +11 -0
  171. data/app/views/naf/machine_affinity_slots/new.html.erb +11 -0
  172. data/app/views/naf/machine_runner_invocations/_filter.html.erb +21 -0
  173. data/app/views/naf/machine_runner_invocations/index.html.erb +36 -0
  174. data/app/views/naf/machine_runner_invocations/index.json.erb +16 -0
  175. data/app/views/naf/machine_runner_invocations/show.html.erb +91 -0
  176. data/app/views/naf/machine_runners/index.html.erb +82 -0
  177. data/app/views/naf/machine_runners/index.json.erb +16 -0
  178. data/app/views/naf/machine_runners/show.html.erb +113 -0
  179. data/app/views/naf/machines/_filter.html.erb +26 -0
  180. data/app/views/naf/machines/_form.html.erb +62 -0
  181. data/app/views/naf/machines/_show.html.erb +169 -0
  182. data/app/views/naf/machines/edit.html.erb +11 -0
  183. data/app/views/naf/machines/index.html.erb +51 -0
  184. data/app/views/naf/machines/index.json.erb +23 -0
  185. data/app/views/naf/machines/new.html.erb +11 -0
  186. data/app/views/naf/machines/show.html.erb +92 -0
  187. data/app/views/naf/record.html.erb +46 -0
  188. data/app/views/naf/shared/_application.html.erb +50 -0
  189. data/app/views/naf/shared/_information_container.html.erb +19 -0
  190. data/app/views/naf/shared/_select_per_page.html.erb +72 -0
  191. data/ci/test-build.sh +17 -0
  192. data/ci/travis.sh +26 -0
  193. data/config/initializers/naf.rb +3 -0
  194. data/config/routes.rb +38 -0
  195. data/db/migrate/20120820023848_naf_schema.rb +413 -0
  196. data/doc/README_FOR_APP +2 -0
  197. data/lib/generators/naf_generator.rb +45 -0
  198. data/lib/generators/templates/config/logging/af.yml +26 -0
  199. data/lib/generators/templates/config/logging/naf.yml +22 -0
  200. data/lib/generators/templates/config/logging/nafjob.yml +16 -0
  201. data/lib/generators/templates/config/logging/nafrunner.yml +17 -0
  202. data/lib/generators/templates/naf.rb +11 -0
  203. data/lib/generators/templates/naf_layout.html.erb +15 -0
  204. data/lib/naf.rb +48 -0
  205. data/lib/naf/configuration.rb +23 -0
  206. data/lib/naf/engine.rb +18 -0
  207. data/lib/naf/version.rb +3 -0
  208. data/lib/tasks/naf_tasks.rake +370 -0
  209. data/naf.gemspec +30 -0
  210. data/script/rails +10 -0
  211. data/spec/controllers/naf/affinities_controller_spec.rb +79 -0
  212. data/spec/controllers/naf/application_controller_spec.rb +10 -0
  213. data/spec/controllers/naf/application_schedule_affinity_tabs_controller_spec.rb +106 -0
  214. data/spec/controllers/naf/applications_controller_spec.rb +109 -0
  215. data/spec/controllers/naf/historical_job_affinity_tabs_controller_spec.rb +96 -0
  216. data/spec/controllers/naf/historical_jobs_controller_spec.rb +19 -0
  217. data/spec/controllers/naf/machine_affinity_slots_controller_spec.rb +109 -0
  218. data/spec/controllers/naf/machines_controller_spec.rb +74 -0
  219. data/spec/dummy/.gitignore +12 -0
  220. data/spec/dummy/README +19 -0
  221. data/spec/dummy/Rakefile +7 -0
  222. data/spec/dummy/app/assets/javascripts/application.js +16 -0
  223. data/spec/dummy/app/assets/stylesheets/application.css +14 -0
  224. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  225. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  226. data/spec/dummy/app/models/my_script.rb +8 -0
  227. data/spec/dummy/app/models/other/base.rb.sample +10 -0
  228. data/spec/dummy/app/views/layouts/application.html.erb +15 -0
  229. data/spec/dummy/app/views/layouts/naf_layout.html.erb +15 -0
  230. data/spec/dummy/config.ru +4 -0
  231. data/spec/dummy/config/application.rb +62 -0
  232. data/spec/dummy/config/boot.rb +10 -0
  233. data/spec/dummy/config/database-non_primary.yml +20 -0
  234. data/spec/dummy/config/database-primary.yml +16 -0
  235. data/spec/dummy/config/environment.rb +5 -0
  236. data/spec/dummy/config/environments/development.rb +37 -0
  237. data/spec/dummy/config/environments/production.rb +67 -0
  238. data/spec/dummy/config/environments/test.rb +37 -0
  239. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  240. data/spec/dummy/config/initializers/inflections.rb +15 -0
  241. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  242. data/spec/dummy/config/initializers/naf.rb.non_primary +4 -0
  243. data/spec/dummy/config/initializers/naf.rb.primary +3 -0
  244. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  245. data/spec/dummy/config/initializers/session_store.rb +8 -0
  246. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  247. data/spec/dummy/config/locales/en.yml +5 -0
  248. data/spec/dummy/config/logging/af.yml +26 -0
  249. data/spec/dummy/config/logging/naf.yml +22 -0
  250. data/spec/dummy/config/logging/nafjob.yml +16 -0
  251. data/spec/dummy/config/logging/nafrunner.yml +17 -0
  252. data/spec/dummy/config/routes.rb +5 -0
  253. data/spec/dummy/db/.gitignore +2 -0
  254. data/spec/dummy/lib/tasks/dummy.rake +60 -0
  255. data/spec/dummy/public/404.html +26 -0
  256. data/spec/dummy/public/422.html +26 -0
  257. data/spec/dummy/public/500.html +25 -0
  258. data/spec/dummy/public/favicon.ico +0 -0
  259. data/spec/dummy/script/rails +6 -0
  260. data/spec/factories/naf.rb +433 -0
  261. data/spec/helpers/naf/application_helper_spec.rb +0 -0
  262. data/spec/models/logical/naf/application_spec.rb +69 -0
  263. data/spec/models/logical/naf/job_creator_spec.rb +32 -0
  264. data/spec/models/logical/naf/job_fetcher_spec.rb +140 -0
  265. data/spec/models/logical/naf/job_spec.rb +282 -0
  266. data/spec/models/logical/naf/machine_spec.rb +61 -0
  267. data/spec/models/naf/affinity_classification_spec.rb +56 -0
  268. data/spec/models/naf/affinity_spec.rb +100 -0
  269. data/spec/models/naf/application_run_group_restriction_spec.rb +57 -0
  270. data/spec/models/naf/application_schedule_affinity_tab_spec.rb +85 -0
  271. data/spec/models/naf/application_schedule_prerequisite_spec.rb +35 -0
  272. data/spec/models/naf/application_schedule_spec.rb +166 -0
  273. data/spec/models/naf/application_spec.rb +128 -0
  274. data/spec/models/naf/application_type_spec.rb +104 -0
  275. data/spec/models/naf/historical_job_affinity_tab_spec.rb +59 -0
  276. data/spec/models/naf/historical_job_prerequisite_spec.rb +25 -0
  277. data/spec/models/naf/historical_job_spec.rb +334 -0
  278. data/spec/models/naf/logger_level_spec.rb +34 -0
  279. data/spec/models/naf/logger_name_spec.rb +35 -0
  280. data/spec/models/naf/logger_style_name_spec.rb +39 -0
  281. data/spec/models/naf/logger_style_spec.rb +89 -0
  282. data/spec/models/naf/machine_affinity_slot_spec.rb +77 -0
  283. data/spec/models/naf/machine_runner_invocation_spec.rb +38 -0
  284. data/spec/models/naf/machine_runner_spec.rb +37 -0
  285. data/spec/models/naf/machine_spec.rb +425 -0
  286. data/spec/models/naf/naf_base_spec.rb +14 -0
  287. data/spec/models/naf/queued_job_spec.rb +171 -0
  288. data/spec/models/naf/running_job_spec.rb +107 -0
  289. data/spec/models/process/naf/application_spec.rb +8 -0
  290. data/spec/models/process/naf/janitor_spec.rb +10 -0
  291. data/spec/models/process/naf/runner_spec.rb +10 -0
  292. data/spec/spec_helper.rb +32 -0
  293. data/spec/support/engine_routing.rb +27 -0
  294. data/spec/support/script_spec_helper.rb +58 -0
  295. metadata +590 -0
@@ -0,0 +1,30 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
13
+ //= require jquery
14
+ //= require jquery_ujs
15
+ //= require date
16
+ //= require iso8601
17
+ //= require underscore
18
+ //= require jquery.dataTables
19
+ //= require jquery.dataTables.custom
20
+
21
+ function remove_fields(link) {
22
+ jQuery(link).prev("input[type=hidden]").val("1");
23
+ jQuery(link).closest(".fields").hide();
24
+ }
25
+
26
+ function add_fields(link, association, content) {
27
+ var new_id = new Date().getTime();
28
+ var regexp = new RegExp("new_" + association, "g");
29
+ jQuery(link).before(content.replace(regexp, new_id));
30
+ }
@@ -0,0 +1,713 @@
1
+ // Underscore.js
2
+ // (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
3
+ // Underscore is freely distributable under the terms of the MIT license.
4
+ // Portions of Underscore are inspired by or borrowed from Prototype.js,
5
+ // Oliver Steele's Functional, and John Resig's Micro-Templating.
6
+ // For all details and documentation:
7
+ // http://documentcloud.github.com/underscore
8
+
9
+ (function() {
10
+ // ------------------------- Baseline setup ---------------------------------
11
+
12
+ // Establish the root object, "window" in the browser, or "global" on the server.
13
+ var root = this;
14
+
15
+ // Save the previous value of the "_" variable.
16
+ var previousUnderscore = root._;
17
+
18
+ // Establish the object that gets thrown to break out of a loop iteration.
19
+ var breaker = typeof StopIteration !== 'undefined' ? StopIteration : '__break__';
20
+
21
+ // Quick regexp-escaping function, because JS doesn't have RegExp.escape().
22
+ var escapeRegExp = function(s) { return s.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1'); };
23
+
24
+ // Save bytes in the minified (but not gzipped) version:
25
+ var ArrayProto = Array.prototype, ObjProto = Object.prototype;
26
+
27
+ // Create quick reference variables for speed access to core prototypes.
28
+ var slice = ArrayProto.slice,
29
+ unshift = ArrayProto.unshift,
30
+ toString = ObjProto.toString,
31
+ hasOwnProperty = ObjProto.hasOwnProperty,
32
+ propertyIsEnumerable = ObjProto.propertyIsEnumerable;
33
+
34
+ // All ECMA5 native implementations we hope to use are declared here.
35
+ var
36
+ nativeForEach = ArrayProto.forEach,
37
+ nativeMap = ArrayProto.map,
38
+ nativeReduce = ArrayProto.reduce,
39
+ nativeReduceRight = ArrayProto.reduceRight,
40
+ nativeFilter = ArrayProto.filter,
41
+ nativeEvery = ArrayProto.every,
42
+ nativeSome = ArrayProto.some,
43
+ nativeIndexOf = ArrayProto.indexOf,
44
+ nativeLastIndexOf = ArrayProto.lastIndexOf,
45
+ nativeIsArray = Array.isArray,
46
+ nativeKeys = Object.keys;
47
+
48
+ // Create a safe reference to the Underscore object for use below.
49
+ var _ = function(obj) { return new wrapper(obj); };
50
+
51
+ // Export the Underscore object for CommonJS.
52
+ if (typeof exports !== 'undefined') exports._ = _;
53
+
54
+ // Export underscore to global scope.
55
+ root._ = _;
56
+
57
+ // Current version.
58
+ _.VERSION = '1.1.0';
59
+
60
+ // ------------------------ Collection Functions: ---------------------------
61
+
62
+ // The cornerstone, an each implementation.
63
+ // Handles objects implementing forEach, arrays, and raw objects.
64
+ // Delegates to JavaScript 1.6's native forEach if available.
65
+ var each = _.forEach = function(obj, iterator, context) {
66
+ try {
67
+ if (nativeForEach && obj.forEach === nativeForEach) {
68
+ obj.forEach(iterator, context);
69
+ } else if (_.isNumber(obj.length)) {
70
+ for (var i = 0, l = obj.length; i < l; i++) iterator.call(context, obj[i], i, obj);
71
+ } else {
72
+ for (var key in obj) {
73
+ if (hasOwnProperty.call(obj, key)) iterator.call(context, obj[key], key, obj);
74
+ }
75
+ }
76
+ } catch(e) {
77
+ if (e != breaker) throw e;
78
+ }
79
+ return obj;
80
+ };
81
+
82
+ // Return the results of applying the iterator to each element.
83
+ // Delegates to JavaScript 1.6's native map if available.
84
+ _.map = function(obj, iterator, context) {
85
+ if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
86
+ var results = [];
87
+ each(obj, function(value, index, list) {
88
+ results.push(iterator.call(context, value, index, list));
89
+ });
90
+ return results;
91
+ };
92
+
93
+ // Reduce builds up a single result from a list of values, aka inject, or foldl.
94
+ // Delegates to JavaScript 1.8's native reduce if available.
95
+ _.reduce = function(obj, iterator, memo, context) {
96
+ if (nativeReduce && obj.reduce === nativeReduce) {
97
+ if (context) iterator = _.bind(iterator, context);
98
+ return obj.reduce(iterator, memo);
99
+ }
100
+ each(obj, function(value, index, list) {
101
+ memo = iterator.call(context, memo, value, index, list);
102
+ });
103
+ return memo;
104
+ };
105
+
106
+ // The right-associative version of reduce, also known as foldr. Uses
107
+ // Delegates to JavaScript 1.8's native reduceRight if available.
108
+ _.reduceRight = function(obj, iterator, memo, context) {
109
+ if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
110
+ if (context) iterator = _.bind(iterator, context);
111
+ return obj.reduceRight(iterator, memo);
112
+ }
113
+ var reversed = _.clone(_.toArray(obj)).reverse();
114
+ return _.reduce(reversed, iterator, memo, context);
115
+ };
116
+
117
+ // Return the first value which passes a truth test.
118
+ _.detect = function(obj, iterator, context) {
119
+ var result;
120
+ each(obj, function(value, index, list) {
121
+ if (iterator.call(context, value, index, list)) {
122
+ result = value;
123
+ _.breakLoop();
124
+ }
125
+ });
126
+ return result;
127
+ };
128
+
129
+ // Return all the elements that pass a truth test.
130
+ // Delegates to JavaScript 1.6's native filter if available.
131
+ _.filter = function(obj, iterator, context) {
132
+ if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
133
+ var results = [];
134
+ each(obj, function(value, index, list) {
135
+ iterator.call(context, value, index, list) && results.push(value);
136
+ });
137
+ return results;
138
+ };
139
+
140
+ // Return all the elements for which a truth test fails.
141
+ _.reject = function(obj, iterator, context) {
142
+ var results = [];
143
+ each(obj, function(value, index, list) {
144
+ !iterator.call(context, value, index, list) && results.push(value);
145
+ });
146
+ return results;
147
+ };
148
+
149
+ // Determine whether all of the elements match a truth test.
150
+ // Delegates to JavaScript 1.6's native every if available.
151
+ _.every = function(obj, iterator, context) {
152
+ iterator = iterator || _.identity;
153
+ if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
154
+ var result = true;
155
+ each(obj, function(value, index, list) {
156
+ if (!(result = result && iterator.call(context, value, index, list))) _.breakLoop();
157
+ });
158
+ return result;
159
+ };
160
+
161
+ // Determine if at least one element in the object matches a truth test.
162
+ // Delegates to JavaScript 1.6's native some if available.
163
+ _.some = function(obj, iterator, context) {
164
+ iterator = iterator || _.identity;
165
+ if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
166
+ var result = false;
167
+ each(obj, function(value, index, list) {
168
+ if (result = iterator.call(context, value, index, list)) _.breakLoop();
169
+ });
170
+ return result;
171
+ };
172
+
173
+ // Determine if a given value is included in the array or object using '==='.
174
+ _.include = function(obj, target) {
175
+ if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
176
+ var found = false;
177
+ each(obj, function(value) {
178
+ if (found = value === target) _.breakLoop();
179
+ });
180
+ return found;
181
+ };
182
+
183
+ // Invoke a method with arguments on every item in a collection.
184
+ _.invoke = function(obj, method) {
185
+ var args = _.rest(arguments, 2);
186
+ return _.map(obj, function(value) {
187
+ return (method ? value[method] : value).apply(value, args);
188
+ });
189
+ };
190
+
191
+ // Convenience version of a common use case of map: fetching a property.
192
+ _.pluck = function(obj, key) {
193
+ return _.map(obj, function(value){ return value[key]; });
194
+ };
195
+
196
+ // Return the maximum item or (item-based computation).
197
+ _.max = function(obj, iterator, context) {
198
+ if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
199
+ var result = {computed : -Infinity};
200
+ each(obj, function(value, index, list) {
201
+ var computed = iterator ? iterator.call(context, value, index, list) : value;
202
+ computed >= result.computed && (result = {value : value, computed : computed});
203
+ });
204
+ return result.value;
205
+ };
206
+
207
+ // Return the minimum element (or element-based computation).
208
+ _.min = function(obj, iterator, context) {
209
+ if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
210
+ var result = {computed : Infinity};
211
+ each(obj, function(value, index, list) {
212
+ var computed = iterator ? iterator.call(context, value, index, list) : value;
213
+ computed < result.computed && (result = {value : value, computed : computed});
214
+ });
215
+ return result.value;
216
+ };
217
+
218
+ // Sort the object's values by a criterion produced by an iterator.
219
+ _.sortBy = function(obj, iterator, context) {
220
+ return _.pluck(_.map(obj, function(value, index, list) {
221
+ return {
222
+ value : value,
223
+ criteria : iterator.call(context, value, index, list)
224
+ };
225
+ }).sort(function(left, right) {
226
+ var a = left.criteria, b = right.criteria;
227
+ return a < b ? -1 : a > b ? 1 : 0;
228
+ }), 'value');
229
+ };
230
+
231
+ // Use a comparator function to figure out at what index an object should
232
+ // be inserted so as to maintain order. Uses binary search.
233
+ _.sortedIndex = function(array, obj, iterator) {
234
+ iterator = iterator || _.identity;
235
+ var low = 0, high = array.length;
236
+ while (low < high) {
237
+ var mid = (low + high) >> 1;
238
+ iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
239
+ }
240
+ return low;
241
+ };
242
+
243
+ // Convert anything iterable into a real, live array.
244
+ _.toArray = function(iterable) {
245
+ if (!iterable) return [];
246
+ if (iterable.toArray) return iterable.toArray();
247
+ if (_.isArray(iterable)) return iterable;
248
+ if (_.isArguments(iterable)) return slice.call(iterable);
249
+ return _.values(iterable);
250
+ };
251
+
252
+ // Return the number of elements in an object.
253
+ _.size = function(obj) {
254
+ return _.toArray(obj).length;
255
+ };
256
+
257
+ // -------------------------- Array Functions: ------------------------------
258
+
259
+ // Get the first element of an array. Passing "n" will return the first N
260
+ // values in the array. Aliased as "head". The "guard" check allows it to work
261
+ // with _.map.
262
+ _.first = function(array, n, guard) {
263
+ return n && !guard ? slice.call(array, 0, n) : array[0];
264
+ };
265
+
266
+ // Returns everything but the first entry of the array. Aliased as "tail".
267
+ // Especially useful on the arguments object. Passing an "index" will return
268
+ // the rest of the values in the array from that index onward. The "guard"
269
+ //check allows it to work with _.map.
270
+ _.rest = function(array, index, guard) {
271
+ return slice.call(array, _.isUndefined(index) || guard ? 1 : index);
272
+ };
273
+
274
+ // Get the last element of an array.
275
+ _.last = function(array) {
276
+ return array[array.length - 1];
277
+ };
278
+
279
+ // Trim out all falsy values from an array.
280
+ _.compact = function(array) {
281
+ return _.filter(array, function(value){ return !!value; });
282
+ };
283
+
284
+ // Return a completely flattened version of an array.
285
+ _.flatten = function(array) {
286
+ return _.reduce(array, function(memo, value) {
287
+ if (_.isArray(value)) return memo.concat(_.flatten(value));
288
+ memo.push(value);
289
+ return memo;
290
+ }, []);
291
+ };
292
+
293
+ // Return a version of the array that does not contain the specified value(s).
294
+ _.without = function(array) {
295
+ var values = _.rest(arguments);
296
+ return _.filter(array, function(value){ return !_.include(values, value); });
297
+ };
298
+
299
+ // Produce a duplicate-free version of the array. If the array has already
300
+ // been sorted, you have the option of using a faster algorithm.
301
+ _.uniq = function(array, isSorted) {
302
+ return _.reduce(array, function(memo, el, i) {
303
+ if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo.push(el);
304
+ return memo;
305
+ }, []);
306
+ };
307
+
308
+ // Produce an array that contains every item shared between all the
309
+ // passed-in arrays.
310
+ _.intersect = function(array) {
311
+ var rest = _.rest(arguments);
312
+ return _.filter(_.uniq(array), function(item) {
313
+ return _.every(rest, function(other) {
314
+ return _.indexOf(other, item) >= 0;
315
+ });
316
+ });
317
+ };
318
+
319
+ // Zip together multiple lists into a single array -- elements that share
320
+ // an index go together.
321
+ _.zip = function() {
322
+ var args = _.toArray(arguments);
323
+ var length = _.max(_.pluck(args, 'length'));
324
+ var results = new Array(length);
325
+ for (var i = 0; i < length; i++) results[i] = _.pluck(args, String(i));
326
+ return results;
327
+ };
328
+
329
+ // If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),
330
+ // we need this function. Return the position of the first occurence of an
331
+ // item in an array, or -1 if the item is not included in the array.
332
+ // Delegates to JavaScript 1.8's native indexOf if available.
333
+ _.indexOf = function(array, item) {
334
+ if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
335
+ for (var i = 0, l = array.length; i < l; i++) if (array[i] === item) return i;
336
+ return -1;
337
+ };
338
+
339
+
340
+ // Delegates to JavaScript 1.6's native lastIndexOf if available.
341
+ _.lastIndexOf = function(array, item) {
342
+ if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
343
+ var i = array.length;
344
+ while (i--) if (array[i] === item) return i;
345
+ return -1;
346
+ };
347
+
348
+ // Generate an integer Array containing an arithmetic progression. A port of
349
+ // the native Python range() function. See:
350
+ // http://docs.python.org/library/functions.html#range
351
+ _.range = function(start, stop, step) {
352
+ var a = _.toArray(arguments);
353
+ var solo = a.length <= 1;
354
+ var start = solo ? 0 : a[0], stop = solo ? a[0] : a[1], step = a[2] || 1;
355
+ var len = Math.ceil((stop - start) / step);
356
+ if (len <= 0) return [];
357
+ var range = new Array(len);
358
+ for (var i = start, idx = 0; true; i += step) {
359
+ if ((step > 0 ? i - stop : stop - i) >= 0) return range;
360
+ range[idx++] = i;
361
+ }
362
+ };
363
+
364
+ // ----------------------- Function Functions: ------------------------------
365
+
366
+ // Create a function bound to a given object (assigning 'this', and arguments,
367
+ // optionally). Binding with arguments is also known as 'curry'.
368
+ _.bind = function(func, obj) {
369
+ var args = _.rest(arguments, 2);
370
+ return function() {
371
+ return func.apply(obj || {}, args.concat(_.toArray(arguments)));
372
+ };
373
+ };
374
+
375
+ // Bind all of an object's methods to that object. Useful for ensuring that
376
+ // all callbacks defined on an object belong to it.
377
+ _.bindAll = function(obj) {
378
+ var funcs = _.rest(arguments);
379
+ if (funcs.length == 0) funcs = _.functions(obj);
380
+ each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
381
+ return obj;
382
+ };
383
+
384
+ // Memoize an expensive function by storing its results.
385
+ _.memoize = function(func, hasher) {
386
+ var memo = {};
387
+ hasher = hasher || _.identity;
388
+ return function() {
389
+ var key = hasher.apply(this, arguments);
390
+ return key in memo ? memo[key] : (memo[key] = func.apply(this, arguments));
391
+ };
392
+ };
393
+
394
+ // Delays a function for the given number of milliseconds, and then calls
395
+ // it with the arguments supplied.
396
+ _.delay = function(func, wait) {
397
+ var args = _.rest(arguments, 2);
398
+ return setTimeout(function(){ return func.apply(func, args); }, wait);
399
+ };
400
+
401
+ // Defers a function, scheduling it to run after the current call stack has
402
+ // cleared.
403
+ _.defer = function(func) {
404
+ return _.delay.apply(_, [func, 1].concat(_.rest(arguments)));
405
+ };
406
+
407
+ // Returns the first function passed as an argument to the second,
408
+ // allowing you to adjust arguments, run code before and after, and
409
+ // conditionally execute the original function.
410
+ _.wrap = function(func, wrapper) {
411
+ return function() {
412
+ var args = [func].concat(_.toArray(arguments));
413
+ return wrapper.apply(wrapper, args);
414
+ };
415
+ };
416
+
417
+ // Returns a function that is the composition of a list of functions, each
418
+ // consuming the return value of the function that follows.
419
+ _.compose = function() {
420
+ var funcs = _.toArray(arguments);
421
+ return function() {
422
+ var args = _.toArray(arguments);
423
+ for (var i=funcs.length-1; i >= 0; i--) {
424
+ args = [funcs[i].apply(this, args)];
425
+ }
426
+ return args[0];
427
+ };
428
+ };
429
+
430
+ // ------------------------- Object Functions: ------------------------------
431
+
432
+ // Retrieve the names of an object's properties.
433
+ // Delegates to ECMA5's native Object.keys
434
+ _.keys = nativeKeys || function(obj) {
435
+ if (_.isArray(obj)) return _.range(0, obj.length);
436
+ var keys = [];
437
+ for (var key in obj) if (hasOwnProperty.call(obj, key)) keys.push(key);
438
+ return keys;
439
+ };
440
+
441
+ // Retrieve the values of an object's properties.
442
+ _.values = function(obj) {
443
+ return _.map(obj, _.identity);
444
+ };
445
+
446
+ // Return a sorted list of the function names available on the object.
447
+ _.functions = function(obj) {
448
+ return _.filter(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort();
449
+ };
450
+
451
+ // Extend a given object with all the properties in passed-in object(s).
452
+ _.extend = function(obj) {
453
+ each(_.rest(arguments), function(source) {
454
+ for (var prop in source) obj[prop] = source[prop];
455
+ });
456
+ return obj;
457
+ };
458
+
459
+ // Create a (shallow-cloned) duplicate of an object.
460
+ _.clone = function(obj) {
461
+ if (_.isArray(obj)) return obj.slice(0);
462
+ return _.extend({}, obj);
463
+ };
464
+
465
+ // Invokes interceptor with the obj, and then returns obj.
466
+ // The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
467
+ _.tap = function(obj, interceptor) {
468
+ interceptor(obj);
469
+ return obj;
470
+ };
471
+
472
+ // Perform a deep comparison to check if two objects are equal.
473
+ _.isEqual = function(a, b) {
474
+ // Check object identity.
475
+ if (a === b) return true;
476
+ // Different types?
477
+ var atype = typeof(a), btype = typeof(b);
478
+ if (atype != btype) return false;
479
+ // Basic equality test (watch out for coercions).
480
+ if (a == b) return true;
481
+ // One is falsy and the other truthy.
482
+ if ((!a && b) || (a && !b)) return false;
483
+ // One of them implements an isEqual()?
484
+ if (a.isEqual) return a.isEqual(b);
485
+ // Check dates' integer values.
486
+ if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();
487
+ // Both are NaN?
488
+ if (_.isNaN(a) && _.isNaN(b)) return false;
489
+ // Compare regular expressions.
490
+ if (_.isRegExp(a) && _.isRegExp(b))
491
+ return a.source === b.source &&
492
+ a.global === b.global &&
493
+ a.ignoreCase === b.ignoreCase &&
494
+ a.multiline === b.multiline;
495
+ // If a is not an object by this point, we can't handle it.
496
+ if (atype !== 'object') return false;
497
+ // Check for different array lengths before comparing contents.
498
+ if (a.length && (a.length !== b.length)) return false;
499
+ // Nothing else worked, deep compare the contents.
500
+ var aKeys = _.keys(a), bKeys = _.keys(b);
501
+ // Different object sizes?
502
+ if (aKeys.length != bKeys.length) return false;
503
+ // Recursive comparison of contents.
504
+ for (var key in a) if (!(key in b) || !_.isEqual(a[key], b[key])) return false;
505
+ return true;
506
+ };
507
+
508
+ // Is a given array or object empty?
509
+ _.isEmpty = function(obj) {
510
+ if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
511
+ for (var key in obj) if (hasOwnProperty.call(obj, key)) return false;
512
+ return true;
513
+ };
514
+
515
+ // Is a given value a DOM element?
516
+ _.isElement = function(obj) {
517
+ return !!(obj && obj.nodeType == 1);
518
+ };
519
+
520
+ // Is a given value an array?
521
+ // Delegates to ECMA5's native Array.isArray
522
+ _.isArray = nativeIsArray || function(obj) {
523
+ return !!(obj && obj.concat && obj.unshift && !obj.callee);
524
+ };
525
+
526
+ // Is a given variable an arguments object?
527
+ _.isArguments = function(obj) {
528
+ return obj && obj.callee;
529
+ };
530
+
531
+ // Is a given value a function?
532
+ _.isFunction = function(obj) {
533
+ return !!(obj && obj.constructor && obj.call && obj.apply);
534
+ };
535
+
536
+ // Is a given value a string?
537
+ _.isString = function(obj) {
538
+ return !!(obj === '' || (obj && obj.charCodeAt && obj.substr));
539
+ };
540
+
541
+ // Is a given value a number?
542
+ _.isNumber = function(obj) {
543
+ return (obj === +obj) || (toString.call(obj) === '[object Number]');
544
+ };
545
+
546
+ // Is a given value a boolean?
547
+ _.isBoolean = function(obj) {
548
+ return obj === true || obj === false;
549
+ };
550
+
551
+ // Is a given value a date?
552
+ _.isDate = function(obj) {
553
+ return !!(obj && obj.getTimezoneOffset && obj.setUTCFullYear);
554
+ };
555
+
556
+ // Is the given value a regular expression?
557
+ _.isRegExp = function(obj) {
558
+ return !!(obj && obj.test && obj.exec && (obj.ignoreCase || obj.ignoreCase === false));
559
+ };
560
+
561
+ // Is the given value NaN -- this one is interesting. NaN != NaN, and
562
+ // isNaN(undefined) == true, so we make sure it's a number first.
563
+ _.isNaN = function(obj) {
564
+ return _.isNumber(obj) && isNaN(obj);
565
+ };
566
+
567
+ // Is a given value equal to null?
568
+ _.isNull = function(obj) {
569
+ return obj === null;
570
+ };
571
+
572
+ // Is a given variable undefined?
573
+ _.isUndefined = function(obj) {
574
+ return typeof obj == 'undefined';
575
+ };
576
+
577
+ // -------------------------- Utility Functions: ----------------------------
578
+
579
+ // Run Underscore.js in noConflict mode, returning the '_' variable to its
580
+ // previous owner. Returns a reference to the Underscore object.
581
+ _.noConflict = function() {
582
+ root._ = previousUnderscore;
583
+ return this;
584
+ };
585
+
586
+ // Keep the identity function around for default iterators.
587
+ _.identity = function(value) {
588
+ return value;
589
+ };
590
+
591
+ // Run a function n times.
592
+ _.times = function (n, iterator, context) {
593
+ for (var i = 0; i < n; i++) iterator.call(context, i);
594
+ };
595
+
596
+ // Break out of the middle of an iteration.
597
+ _.breakLoop = function() {
598
+ throw breaker;
599
+ };
600
+
601
+ // Add your own custom functions to the Underscore object, ensuring that
602
+ // they're correctly added to the OOP wrapper as well.
603
+ _.mixin = function(obj) {
604
+ each(_.functions(obj), function(name){
605
+ addToWrapper(name, _[name] = obj[name]);
606
+ });
607
+ };
608
+
609
+ // Generate a unique integer id (unique within the entire client session).
610
+ // Useful for temporary DOM ids.
611
+ var idCounter = 0;
612
+ _.uniqueId = function(prefix) {
613
+ var id = idCounter++;
614
+ return prefix ? prefix + id : id;
615
+ };
616
+
617
+ // By default, Underscore uses ERB-style template delimiters, change the
618
+ // following template settings to use alternative delimiters.
619
+ _.templateSettings = {
620
+ start : '<%',
621
+ end : '%>',
622
+ interpolate : /<%=(.+?)%>/g
623
+ };
624
+
625
+ // JavaScript templating a-la ERB, pilfered from John Resig's
626
+ // "Secrets of the JavaScript Ninja", page 83.
627
+ // Single-quote fix from Rick Strahl's version.
628
+ // With alterations for arbitrary delimiters, and to preserve whitespace.
629
+ _.template = function(str, data) {
630
+ var c = _.templateSettings;
631
+ var endMatch = new RegExp("'(?=[^"+c.end.substr(0, 1)+"]*"+escapeRegExp(c.end)+")","g");
632
+ var fn = new Function('obj',
633
+ 'var p=[],print=function(){p.push.apply(p,arguments);};' +
634
+ 'with(obj||{}){p.push(\'' +
635
+ str.replace(/\r/g, '\\r')
636
+ .replace(/\n/g, '\\n')
637
+ .replace(/\t/g, '\\t')
638
+ .replace(endMatch,"✄")
639
+ .split("'").join("\\'")
640
+ .split("✄").join("'")
641
+ .replace(c.interpolate, "',$1,'")
642
+ .split(c.start).join("');")
643
+ .split(c.end).join("p.push('")
644
+ + "');}return p.join('');");
645
+ return data ? fn(data) : fn;
646
+ };
647
+
648
+ // ------------------------------- Aliases ----------------------------------
649
+
650
+ _.each = _.forEach;
651
+ _.foldl = _.inject = _.reduce;
652
+ _.foldr = _.reduceRight;
653
+ _.select = _.filter;
654
+ _.all = _.every;
655
+ _.any = _.some;
656
+ _.contains = _.include;
657
+ _.head = _.first;
658
+ _.tail = _.rest;
659
+ _.methods = _.functions;
660
+
661
+ // ------------------------ Setup the OOP Wrapper: --------------------------
662
+
663
+ // If Underscore is called as a function, it returns a wrapped object that
664
+ // can be used OO-style. This wrapper holds altered versions of all the
665
+ // underscore functions. Wrapped objects may be chained.
666
+ var wrapper = function(obj) { this._wrapped = obj; };
667
+
668
+ // Helper function to continue chaining intermediate results.
669
+ var result = function(obj, chain) {
670
+ return chain ? _(obj).chain() : obj;
671
+ };
672
+
673
+ // A method to easily add functions to the OOP wrapper.
674
+ var addToWrapper = function(name, func) {
675
+ wrapper.prototype[name] = function() {
676
+ var args = _.toArray(arguments);
677
+ unshift.call(args, this._wrapped);
678
+ return result(func.apply(_, args), this._chain);
679
+ };
680
+ };
681
+
682
+ // Add all of the Underscore functions to the wrapper object.
683
+ _.mixin(_);
684
+
685
+ // Add all mutator Array functions to the wrapper.
686
+ each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
687
+ var method = ArrayProto[name];
688
+ wrapper.prototype[name] = function() {
689
+ method.apply(this._wrapped, arguments);
690
+ return result(this._wrapped, this._chain);
691
+ };
692
+ });
693
+
694
+ // Add all accessor Array functions to the wrapper.
695
+ each(['concat', 'join', 'slice'], function(name) {
696
+ var method = ArrayProto[name];
697
+ wrapper.prototype[name] = function() {
698
+ return result(method.apply(this._wrapped, arguments), this._chain);
699
+ };
700
+ });
701
+
702
+ // Start chaining a wrapped Underscore object.
703
+ wrapper.prototype.chain = function() {
704
+ this._chain = true;
705
+ return this;
706
+ };
707
+
708
+ // Extracts the result from a wrapped and chained object.
709
+ wrapper.prototype.value = function() {
710
+ return this._wrapped;
711
+ };
712
+
713
+ })();