aleph_analytics 0.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (527) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +0 -0
  3. data/Gemfile +70 -0
  4. data/Gemfile.lock +489 -0
  5. data/LICENSE +21 -0
  6. data/Procfile +2 -0
  7. data/README.md +1 -0
  8. data/ROADMAP.md +7 -0
  9. data/Rakefile +9 -0
  10. data/app/assets/fonts/octicons-local.ttf +0 -0
  11. data/app/assets/fonts/octicons.eot +0 -0
  12. data/app/assets/fonts/octicons.svg +183 -0
  13. data/app/assets/fonts/octicons.ttf +0 -0
  14. data/app/assets/fonts/octicons.woff +0 -0
  15. data/app/assets/javascripts/angular/aleph.js.es6 +90 -0
  16. data/app/assets/javascripts/angular/config/schema_completer_config.js.es6 +71 -0
  17. data/app/assets/javascripts/angular/controllers/alerts/alert_index_controller.js.es6 +16 -0
  18. data/app/assets/javascripts/angular/controllers/alerts/alert_show_controller.js.es6 +99 -0
  19. data/app/assets/javascripts/angular/controllers/controllers.js.es6 +65 -0
  20. data/app/assets/javascripts/angular/controllers/query/query_index_controller.js.es6 +80 -0
  21. data/app/assets/javascripts/angular/controllers/query/query_repl_controller.js.es6 +147 -0
  22. data/app/assets/javascripts/angular/controllers/query/query_show_controller.js.es6 +43 -0
  23. data/app/assets/javascripts/angular/controllers/schema/schema_index_controller.js.es6 +61 -0
  24. data/app/assets/javascripts/angular/controllers/snippet/snippet_index_controller.js.es6 +80 -0
  25. data/app/assets/javascripts/angular/directives/directives.js.es6 +30 -0
  26. data/app/assets/javascripts/angular/directives/query/query_details_directive.js.es6 +87 -0
  27. data/app/assets/javascripts/angular/directives/query/query_version_sidebar_directive.js.es6 +65 -0
  28. data/app/assets/javascripts/angular/directives/result/result_directive.js.es6 +59 -0
  29. data/app/assets/javascripts/angular/directives/result/results_directive.js.es6 +30 -0
  30. data/app/assets/javascripts/angular/directives/visualization_directive.js.es6 +101 -0
  31. data/app/assets/javascripts/angular/filters/filters.js.es6 +27 -0
  32. data/app/assets/javascripts/angular/services/alert/alert.js.es6 +51 -0
  33. data/app/assets/javascripts/angular/services/alert/alert_resource.js.es6 +10 -0
  34. data/app/assets/javascripts/angular/services/lib/ace_completers.js.es6 +29 -0
  35. data/app/assets/javascripts/angular/services/lib/ace_sql_parse.js.es6 +156 -0
  36. data/app/assets/javascripts/angular/services/lib/action_handler.js.es6 +73 -0
  37. data/app/assets/javascripts/angular/services/lib/alert_flash.js.es6 +30 -0
  38. data/app/assets/javascripts/angular/services/lib/default_ace_configurator_service.js.es6 +74 -0
  39. data/app/assets/javascripts/angular/services/lib/github_url_service.js.es6 +53 -0
  40. data/app/assets/javascripts/angular/services/lib/keyword_completer.js.es6 +36 -0
  41. data/app/assets/javascripts/angular/services/lib/local_resource.js.es6 +20 -0
  42. data/app/assets/javascripts/angular/services/lib/lock_service.js.es6 +48 -0
  43. data/app/assets/javascripts/angular/services/lib/locking_poll_service.js.es6 +42 -0
  44. data/app/assets/javascripts/angular/services/lib/matcher_runner.js.es6 +65 -0
  45. data/app/assets/javascripts/angular/services/lib/navigation_guard.js.es6 +45 -0
  46. data/app/assets/javascripts/angular/services/lib/open_repl_service.js.es6 +56 -0
  47. data/app/assets/javascripts/angular/services/lib/page_title_manager.js.es6 +38 -0
  48. data/app/assets/javascripts/angular/services/lib/pagination.js.es6 +114 -0
  49. data/app/assets/javascripts/angular/services/lib/pagination_components.js.es6 +91 -0
  50. data/app/assets/javascripts/angular/services/lib/parameter_methods.js.es6 +35 -0
  51. data/app/assets/javascripts/angular/services/lib/role_model.js.es6 +21 -0
  52. data/app/assets/javascripts/angular/services/lib/schema_completer.js.es6 +56 -0
  53. data/app/assets/javascripts/angular/services/lib/selection_tag_input.js.es6 +42 -0
  54. data/app/assets/javascripts/angular/services/lib/server_configurations.js.es6 +15 -0
  55. data/app/assets/javascripts/angular/services/lib/spinner_state.js.es6 +45 -0
  56. data/app/assets/javascripts/angular/services/lib/tag_resource.js.es6 +14 -0
  57. data/app/assets/javascripts/angular/services/model/collection_dirty_awareness.js.es6 +33 -0
  58. data/app/assets/javascripts/angular/services/model/collection_model_base.js.es6 +75 -0
  59. data/app/assets/javascripts/angular/services/model/dirty_aware_collection_model.js.es6 +31 -0
  60. data/app/assets/javascripts/angular/services/model/dirty_aware_model.js.es6 +50 -0
  61. data/app/assets/javascripts/angular/services/model/model_base.js.es6 +80 -0
  62. data/app/assets/javascripts/angular/services/model/model_state.js.es6 +51 -0
  63. data/app/assets/javascripts/angular/services/model/models.js.es6 +15 -0
  64. data/app/assets/javascripts/angular/services/model/unpersisted_model.js.es6 +20 -0
  65. data/app/assets/javascripts/angular/services/model_generation/model_factory.js.es6 +28 -0
  66. data/app/assets/javascripts/angular/services/model_generation/model_generation.js.es6 +13 -0
  67. data/app/assets/javascripts/angular/services/model_generation/model_manager.js.es6 +86 -0
  68. data/app/assets/javascripts/angular/services/model_generation/resource_factory.js.es6 +38 -0
  69. data/app/assets/javascripts/angular/services/model_generation/standard_collection_model.js.es6 +25 -0
  70. data/app/assets/javascripts/angular/services/model_generation/standard_model.js.es6 +38 -0
  71. data/app/assets/javascripts/angular/services/query/query.js.es6 +55 -0
  72. data/app/assets/javascripts/angular/services/query/query_handler.js.es6 +40 -0
  73. data/app/assets/javascripts/angular/services/query/query_loader.js.es6 +54 -0
  74. data/app/assets/javascripts/angular/services/query/query_request_transformers.js.es6 +26 -0
  75. data/app/assets/javascripts/angular/services/query/query_resource.js.es6 +21 -0
  76. data/app/assets/javascripts/angular/services/query/query_tab.js.es6 +63 -0
  77. data/app/assets/javascripts/angular/services/query/query_versions.js.es6 +21 -0
  78. data/app/assets/javascripts/angular/services/result/result.js.es6 +26 -0
  79. data/app/assets/javascripts/angular/services/result/result_poller.js.es6 +30 -0
  80. data/app/assets/javascripts/angular/services/result/result_runner.js.es6 +48 -0
  81. data/app/assets/javascripts/angular/services/result/results.js.es6 +10 -0
  82. data/app/assets/javascripts/angular/services/schema/schema_column.js.es6 +93 -0
  83. data/app/assets/javascripts/angular/services/schema/schema_column_resource.js.es6 +12 -0
  84. data/app/assets/javascripts/angular/services/schema/schema_columns.js.es6 +57 -0
  85. data/app/assets/javascripts/angular/services/schema/schema_comment_resource.js.es6 +12 -0
  86. data/app/assets/javascripts/angular/services/services.js.es6 +140 -0
  87. data/app/assets/javascripts/angular/services/snippet/ace_snippet_manager.js.es6 +44 -0
  88. data/app/assets/javascripts/angular/services/visualization/source_renderer.js.es6 +70 -0
  89. data/app/assets/javascripts/angular/services/visualization/visualization.js.es6 +47 -0
  90. data/app/assets/javascripts/angular/services/visualization/visualization_service.js.es6 +67 -0
  91. data/app/assets/javascripts/angular/services/visualization/visualizations.js.es6 +11 -0
  92. data/app/assets/javascripts/application.js +22 -0
  93. data/app/assets/javascripts/lib/key_binding.js +24 -0
  94. data/app/assets/javascripts/lib/serialized_storage.js +11 -0
  95. data/app/assets/javascripts/lib/underscore_contrib/array_builders.js +190 -0
  96. data/app/assets/javascripts/lib/underscore_contrib/function_predicates.js +95 -0
  97. data/app/assets/javascripts/lib/underscore_contrib/object_builders.js +111 -0
  98. data/app/assets/javascripts/lib/underscore_contrib/object_selectors.js +112 -0
  99. data/app/assets/javascripts/lib/underscore_contrib/util_existential.js +15 -0
  100. data/app/assets/javascripts/lib/utils.js +29 -0
  101. data/app/assets/stylesheets/alert_bar.css.sass +13 -0
  102. data/app/assets/stylesheets/alerts.css.sass +66 -0
  103. data/app/assets/stylesheets/application.css.sass +35 -0
  104. data/app/assets/stylesheets/comments.css.sass +7 -0
  105. data/app/assets/stylesheets/index_components.css.sass +105 -0
  106. data/app/assets/stylesheets/queries.css.sass +110 -0
  107. data/app/assets/stylesheets/repl.css.sass +59 -0
  108. data/app/assets/stylesheets/results.css.sass +25 -0
  109. data/app/assets/stylesheets/schemas.css.sass +56 -0
  110. data/app/assets/stylesheets/sessions.css.sass +9 -0
  111. data/app/assets/stylesheets/shared.css.sass +157 -0
  112. data/app/assets/stylesheets/sidebar.css.sass +85 -0
  113. data/app/assets/stylesheets/snippets.css.sass +15 -0
  114. data/app/assets/stylesheets/sort_bar.css.sass +10 -0
  115. data/app/assets/stylesheets/spinners.css.sass +67 -0
  116. data/app/assets/stylesheets/summary.css.sass +16 -0
  117. data/app/assets/stylesheets/variables.css.sass +110 -0
  118. data/app/assets/stylesheets/visualizations.css.sass +15 -0
  119. data/app/controllers/alerts_controller.rb +72 -0
  120. data/app/controllers/application_controller.rb +38 -0
  121. data/app/controllers/columns_controller.rb +21 -0
  122. data/app/controllers/queries_controller.rb +85 -0
  123. data/app/controllers/query_versions_controller.rb +38 -0
  124. data/app/controllers/result_csvs_controller.rb +29 -0
  125. data/app/controllers/results_controller.rb +53 -0
  126. data/app/controllers/roles_controller.rb +9 -0
  127. data/app/controllers/schema_comments_controller.rb +47 -0
  128. data/app/controllers/snippets_controller.rb +67 -0
  129. data/app/controllers/tags_controller.rb +19 -0
  130. data/app/controllers/visualizations_controller.rb +61 -0
  131. data/app/mailers/alert_mailer.rb +8 -0
  132. data/app/models/ability.rb +31 -0
  133. data/app/models/alert.rb +139 -0
  134. data/app/models/alert_execution.rb +25 -0
  135. data/app/models/query.rb +85 -0
  136. data/app/models/query_execution.rb +39 -0
  137. data/app/models/query_role.rb +3 -0
  138. data/app/models/query_version.rb +61 -0
  139. data/app/models/result.rb +67 -0
  140. data/app/models/schema_comment.rb +3 -0
  141. data/app/models/snippet.rb +3 -0
  142. data/app/models/user.rb +19 -0
  143. data/app/models/visualization.rb +7 -0
  144. data/app/serializers/alert_serializer.rb +10 -0
  145. data/app/serializers/query_index_serializer.rb +7 -0
  146. data/app/serializers/query_serializer.rb +8 -0
  147. data/app/serializers/query_version_serializer.rb +3 -0
  148. data/app/serializers/result_serializer.rb +3 -0
  149. data/app/views/alert_mailer/alert_failing_email.html.haml +17 -0
  150. data/app/views/alerts/_index.html.haml +10 -0
  151. data/app/views/alerts/_index_item.html.haml +25 -0
  152. data/app/views/alerts/_index_sidebar.html.haml +20 -0
  153. data/app/views/alerts/_index_sort_bar.html.haml +22 -0
  154. data/app/views/alerts/_show.html.haml +90 -0
  155. data/app/views/application/_rollbar.html.erb +9 -0
  156. data/app/views/application/index.html.haml +1 -0
  157. data/app/views/application/unauthorized.html.haml +5 -0
  158. data/app/views/application/unauthorized.json +3 -0
  159. data/app/views/devise/sessions/new.html.haml +22 -0
  160. data/app/views/layouts/application.html.haml +78 -0
  161. data/app/views/queries/_alert.html.haml +6 -0
  162. data/app/views/queries/_comments.html.haml +5 -0
  163. data/app/views/queries/_index.html.haml +15 -0
  164. data/app/views/queries/_index_item.html.haml +37 -0
  165. data/app/views/queries/_index_sidebar.html.haml +58 -0
  166. data/app/views/queries/_index_sort_bar.html.haml +29 -0
  167. data/app/views/queries/_query_details.html.haml +81 -0
  168. data/app/views/queries/_query_version_sidebar.html.haml +46 -0
  169. data/app/views/queries/_repl.html.haml +79 -0
  170. data/app/views/queries/_results.html.haml +29 -0
  171. data/app/views/queries/_role_template.html.haml +3 -0
  172. data/app/views/queries/_show.html.haml +28 -0
  173. data/app/views/queries/_tag_template.html.haml +3 -0
  174. data/app/views/results/_show.html.haml +45 -0
  175. data/app/views/schemas/_index.html.haml +47 -0
  176. data/app/views/snippets/_index.html.haml +30 -0
  177. data/app/views/snippets/_index_item.html.haml +20 -0
  178. data/app/views/visualizations/_show.html.haml +83 -0
  179. data/bin/aleph +71 -0
  180. data/bin/bundle +3 -0
  181. data/bin/executables/bundle_runner.rb +12 -0
  182. data/bin/executables/clock.rb +7 -0
  183. data/bin/executables/deps.rb +10 -0
  184. data/bin/executables/import_env_variables.rb +17 -0
  185. data/bin/executables/init_db.rb +7 -0
  186. data/bin/executables/lib/config_generator.rb +51 -0
  187. data/bin/executables/lib/env_writer.rb +25 -0
  188. data/bin/executables/lib/import_env_file.rb +19 -0
  189. data/bin/executables/lib/seeder.rb +14 -0
  190. data/bin/executables/lib/utils.rb +30 -0
  191. data/bin/executables/playground.rb +130 -0
  192. data/bin/executables/seed_playground_db.rb +9 -0
  193. data/bin/executables/update_db.rb +7 -0
  194. data/bin/executables/web_server.rb +12 -0
  195. data/bin/executables/worker.rb +7 -0
  196. data/bin/rails +4 -0
  197. data/bin/rake +4 -0
  198. data/config/application.rb +59 -0
  199. data/config/attribute-map.yml +4 -0
  200. data/config/boot.rb +4 -0
  201. data/config/database.yml +40 -0
  202. data/config/environment.rb +5 -0
  203. data/config/environments/development.rb +37 -0
  204. data/config/environments/playground.rb +55 -0
  205. data/config/environments/production.rb +68 -0
  206. data/config/environments/staging.rb +75 -0
  207. data/config/environments/test.rb +46 -0
  208. data/config/initializers/01_internalize_configurations.rb +70 -0
  209. data/config/initializers/action_controller_renderers.rb +15 -0
  210. data/config/initializers/active_model_serializer.rb +7 -0
  211. data/config/initializers/assets.rb +8 -0
  212. data/config/initializers/backtrace_silencers.rb +7 -0
  213. data/config/initializers/cookies_serializer.rb +3 -0
  214. data/config/initializers/default_enviorment_variables.rb +8 -0
  215. data/config/initializers/devise.rb +297 -0
  216. data/config/initializers/filter_parameter_logging.rb +4 -0
  217. data/config/initializers/haml.rb +1 -0
  218. data/config/initializers/inflections.rb +16 -0
  219. data/config/initializers/mime_types.rb +4 -0
  220. data/config/initializers/net_http.rb +1 -0
  221. data/config/initializers/pester.rb +15 -0
  222. data/config/initializers/rails_admin.rb +27 -0
  223. data/config/initializers/redis.rb +14 -0
  224. data/config/initializers/rollbar.rb +45 -0
  225. data/config/initializers/session_store.rb +3 -0
  226. data/config/initializers/sql_logging_patch.rb +7 -0
  227. data/config/initializers/to_bool.rb +29 -0
  228. data/config/initializers/wrap_parameters.rb +14 -0
  229. data/config/locales/devise.en.yml +62 -0
  230. data/config/locales/en.yml +23 -0
  231. data/config/puma.rb +10 -0
  232. data/config/resque-pool.yml +15 -0
  233. data/config/resque.yml +10 -0
  234. data/config/routes.rb +33 -0
  235. data/config/secrets.yml +26 -0
  236. data/config.ru +4 -0
  237. data/db/migrate/20140710204753_database_begin.rb +150 -0
  238. data/lib/aleph_log_formatter.rb +5 -0
  239. data/lib/authentication.rb +17 -0
  240. data/lib/aws_s3.rb +7 -0
  241. data/lib/clock.rb +7 -0
  242. data/lib/compilable_parameter.rb +50 -0
  243. data/lib/count_publisher.rb +26 -0
  244. data/lib/csv_helper/aws.rb +30 -0
  245. data/lib/csv_helper/base.rb +20 -0
  246. data/lib/csv_helper/local.rb +18 -0
  247. data/lib/csv_serializer.rb +23 -0
  248. data/lib/csv_service.rb +12 -0
  249. data/lib/github/blob.rb +23 -0
  250. data/lib/github/commit.rb +37 -0
  251. data/lib/github/pusher.rb +94 -0
  252. data/lib/github/refs.rb +39 -0
  253. data/lib/github/tree.rb +29 -0
  254. data/lib/github.rb +64 -0
  255. data/lib/interaction/query_creation.rb +27 -0
  256. data/lib/interaction/query_interaction.rb +35 -0
  257. data/lib/interaction/query_update.rb +62 -0
  258. data/lib/interaction/result_creation.rb +48 -0
  259. data/lib/pagination_search/attribute_set.rb +61 -0
  260. data/lib/pagination_search/base_relation.rb +80 -0
  261. data/lib/pagination_search/hash_paginate.rb +74 -0
  262. data/lib/pagination_search/paginated_records.rb +16 -0
  263. data/lib/pagination_search/pagination.rb +12 -0
  264. data/lib/pagination_search/search_conditions.rb +59 -0
  265. data/lib/query_version_support.rb +14 -0
  266. data/lib/redis_result_row_count.rb +26 -0
  267. data/lib/redshift_connection_pool.rb +8 -0
  268. data/lib/redshift_pg/connection.rb +44 -0
  269. data/lib/redshift_pg/redshift_pg.rb +15 -0
  270. data/lib/result_csv_generator.rb +40 -0
  271. data/lib/role.rb +40 -0
  272. data/lib/sample_skimmer.rb +30 -0
  273. data/lib/schema_comment_matcher.rb +27 -0
  274. data/lib/schemas/descriptor.rb +85 -0
  275. data/lib/schemas/descriptors.rb +10 -0
  276. data/lib/schemas/paginate.rb +21 -0
  277. data/lib/schemas/redis_store.rb +22 -0
  278. data/lib/simple_cache.rb +13 -0
  279. data/lib/sql_compiler.rb +33 -0
  280. data/lib/summarizer.rb +18 -0
  281. data/lib/tasks/karma.rake +32 -0
  282. data/lib/tasks/resque_setup.rake +18 -0
  283. data/playground_data/1.csv +13 -0
  284. data/playground_data/aleph.playground.sqlite3 +0 -0
  285. data/public/404.html +26 -0
  286. data/public/422.html +26 -0
  287. data/public/500.html +25 -0
  288. data/public/aleph.png +0 -0
  289. data/public/assets/.sprockets-manifest-24b5998edb20c5b4d6edf39d44cd3ff7.json +1 -0
  290. data/public/assets/FontAwesome-9ccfa32dd4cd1b8e83f68899d85bd5e6.otf +0 -0
  291. data/public/assets/ZeroClipboard-8b5c117b88fe37e32fd34a70fdf60026.swf +0 -0
  292. data/public/assets/angular/aleph.js-19b4df67407de5dab249abfc391c0eaf.es6 +90 -0
  293. data/public/assets/angular/config/schema_completer_config.js-1b696920b5e04f75620630d75117b201.es6 +71 -0
  294. data/public/assets/angular/controllers/alerts/alert_index_controller.js-4344581b1e9b456c3f991f96bb4d403d.es6 +16 -0
  295. data/public/assets/angular/controllers/alerts/alert_show_controller.js-a984dc82fbcac380cdc8682bf8986933.es6 +99 -0
  296. data/public/assets/angular/controllers/controllers.js-7431ae468ea2f0d392661448a61cfdf3.es6 +65 -0
  297. data/public/assets/angular/controllers/query/query_index_controller.js-828eadeee6a971b11b5c07f65f0c6def.es6 +80 -0
  298. data/public/assets/angular/controllers/query/query_repl_controller.js-3c4bac22e41766e2e83e720d53acade3.es6 +147 -0
  299. data/public/assets/angular/controllers/query/query_show_controller.js-020c83fd94ece7d55bd94b9981a40083.es6 +43 -0
  300. data/public/assets/angular/controllers/schema/schema_index_controller.js-d0d790d168b49caefa4b8f7993c928f1.es6 +61 -0
  301. data/public/assets/angular/controllers/snippet/snippet_index_controller.js-9191ee2e37d16d5807b8a7f45530a6c2.es6 +80 -0
  302. data/public/assets/angular/directives/directives.js-44fda752585bddc5d026feaab8b14108.es6 +30 -0
  303. data/public/assets/angular/directives/query/query_details_directive.js-2dd979e2463826558f166d9dc9e2c8a9.es6 +87 -0
  304. data/public/assets/angular/directives/query/query_version_sidebar_directive.js-b19ba8a9bf4e66c5740e9b9f9495cee1.es6 +65 -0
  305. data/public/assets/angular/directives/result/result_directive.js-76c151e9277e35578f92b076c2fa5b06.es6 +59 -0
  306. data/public/assets/angular/directives/result/results_directive.js-fcbf9750790823a11ca313e630bd2b0f.es6 +30 -0
  307. data/public/assets/angular/directives/visualization_directive.js-82cb45a52eba62deb283a3699add1b33.es6 +101 -0
  308. data/public/assets/angular/filters/filters.js-48cdb16fc89337e861c9f18ad57fd9bf.es6 +27 -0
  309. data/public/assets/angular/services/alert/alert.js-a76ea96e948de324fcc00f3ef3ecae4e.es6 +51 -0
  310. data/public/assets/angular/services/alert/alert_resource.js-ac68ea99936eb213c0fb37d4fbb1511a.es6 +10 -0
  311. data/public/assets/angular/services/lib/ace_completers.js-5353182bc538210dad12865ad6a060fd.es6 +29 -0
  312. data/public/assets/angular/services/lib/ace_sql_parse.js-4114a8ad4fe4dbf4f72c0ab7fca22e83.es6 +156 -0
  313. data/public/assets/angular/services/lib/action_handler.js-85a8f75f507efcf2fe950fa7717b6976.es6 +73 -0
  314. data/public/assets/angular/services/lib/alert_flash.js-7a4fe7ef5510a224677a12ecf2fe973b.es6 +30 -0
  315. data/public/assets/angular/services/lib/default_ace_configurator_service.js-9b83d6b53495dafe10bfed5074c3ec01.es6 +74 -0
  316. data/public/assets/angular/services/lib/github_url_service.js-719084a625f60d20c24097f00813b481.es6 +53 -0
  317. data/public/assets/angular/services/lib/keyword_completer.js-560b51694457619401c72431dd7b647e.es6 +36 -0
  318. data/public/assets/angular/services/lib/local_resource.js-9b57357ed203c783708e48154721242a.es6 +20 -0
  319. data/public/assets/angular/services/lib/lock_service.js-32eb33fda9c27fb5643b25c63099b52c.es6 +48 -0
  320. data/public/assets/angular/services/lib/locking_poll_service.js-d0271cc07d20ec73291e7175e1714d42.es6 +42 -0
  321. data/public/assets/angular/services/lib/matcher_runner.js-bd63f4c6ad965716c5b793d3c4602563.es6 +65 -0
  322. data/public/assets/angular/services/lib/navigation_guard.js-5c7497900f99319f3ef76b83662c20cd.es6 +45 -0
  323. data/public/assets/angular/services/lib/open_repl_service.js-2332490470ea58f3e5ce7f6fe5dd2659.es6 +56 -0
  324. data/public/assets/angular/services/lib/page_title_manager.js-227f9ddb49d23bfa934bf216ef96ccbd.es6 +38 -0
  325. data/public/assets/angular/services/lib/pagination.js-baa9a496d01b87f90c588c51864d1533.es6 +114 -0
  326. data/public/assets/angular/services/lib/pagination_components.js-4fcc03120d1299713347b487f1e8fd44.es6 +91 -0
  327. data/public/assets/angular/services/lib/parameter_methods.js-16ecb25ffb24d5870dfc17f9f408fa5b.es6 +35 -0
  328. data/public/assets/angular/services/lib/role_model.js-3d5072f279676294e7d17cbb4bb3adb5.es6 +21 -0
  329. data/public/assets/angular/services/lib/schema_completer.js-d04d49642967fd9f8e2c95e0df81f2ae.es6 +56 -0
  330. data/public/assets/angular/services/lib/selection_tag_input.js-86cc6d564c108d722951b6fcd2b51221.es6 +42 -0
  331. data/public/assets/angular/services/lib/server_configurations.js-fd8c16a3d02926e7597240566808c141.es6 +15 -0
  332. data/public/assets/angular/services/lib/spinner_state.js-315bb62df18f9370a89f8b865eb1766a.es6 +45 -0
  333. data/public/assets/angular/services/lib/tag_resource.js-8849bbdd579bd38b36a362bed926c2f2.es6 +14 -0
  334. data/public/assets/angular/services/model/collection_dirty_awareness.js-8a78450bd8b565fb03633ab735893508.es6 +33 -0
  335. data/public/assets/angular/services/model/collection_model_base.js-8092335acde425d28718c1a9fc677d39.es6 +75 -0
  336. data/public/assets/angular/services/model/dirty_aware_collection_model.js-6f4d2b409a033f5ffc6bd5a747df2046.es6 +31 -0
  337. data/public/assets/angular/services/model/dirty_aware_model.js-ec1a086f3ec459827dbd5381bd970975.es6 +50 -0
  338. data/public/assets/angular/services/model/model_base.js-9fd807dfdf4f26afe60d8e282ca08e6c.es6 +80 -0
  339. data/public/assets/angular/services/model/model_state.js-3e96e381ea7a9eff857081ffbf3a8bab.es6 +51 -0
  340. data/public/assets/angular/services/model/models.js-6798762a6e263be75c7c4e9f8a19da66.es6 +15 -0
  341. data/public/assets/angular/services/model/unpersisted_model.js-49597959c989e68c713fd826743bb7b5.es6 +20 -0
  342. data/public/assets/angular/services/model_generation/model_factory.js-0bccc6a2e9b4738c67230fc68d9780ae.es6 +28 -0
  343. data/public/assets/angular/services/model_generation/model_generation.js-f08c6eff199b5ad02b2e2bdf866d69ff.es6 +13 -0
  344. data/public/assets/angular/services/model_generation/model_manager.js-b05d8f00bc4499c50098ae31013de88c.es6 +86 -0
  345. data/public/assets/angular/services/model_generation/resource_factory.js-e8ce2f4be9383d885dbb965c8e425bab.es6 +38 -0
  346. data/public/assets/angular/services/model_generation/standard_collection_model.js-19c44dc3a937a85d825c5a2f020a6769.es6 +25 -0
  347. data/public/assets/angular/services/model_generation/standard_model.js-74439b041eebfc7e0fad454cf6e32192.es6 +38 -0
  348. data/public/assets/angular/services/query/query.js-7b6228d0a5c1a6ea76242f4aa49aafd0.es6 +55 -0
  349. data/public/assets/angular/services/query/query_handler.js-d8cb3c66bcf6d16cd13bb5bcd0921496.es6 +40 -0
  350. data/public/assets/angular/services/query/query_loader.js-1fbc8b859778d46b73b9adc300984851.es6 +54 -0
  351. data/public/assets/angular/services/query/query_request_transformers.js-522901477c7848324cd5c014005a85c8.es6 +26 -0
  352. data/public/assets/angular/services/query/query_resource.js-b809fbfd1c379619fa07ef8e52509810.es6 +21 -0
  353. data/public/assets/angular/services/query/query_tab.js-fe34f5e6d0c813d2d92838be14cd6783.es6 +63 -0
  354. data/public/assets/angular/services/query/query_versions.js-aa3aec9021354a7394797101ea500887.es6 +21 -0
  355. data/public/assets/angular/services/result/result.js-be8178c80c767a651a97537b2b0149d5.es6 +26 -0
  356. data/public/assets/angular/services/result/result_poller.js-b15617d53f164cbe5636bb5573a70504.es6 +30 -0
  357. data/public/assets/angular/services/result/result_runner.js-ce2c5b3121c8e67903c302e600749c14.es6 +48 -0
  358. data/public/assets/angular/services/result/results.js-53088d6088f6b4783b274a64322d361c.es6 +10 -0
  359. data/public/assets/angular/services/schema/schema_column.js-3908faecc9874fa08484d7490701a7d4.es6 +93 -0
  360. data/public/assets/angular/services/schema/schema_column_resource.js-a6982ce9698b104192c4e455300df455.es6 +12 -0
  361. data/public/assets/angular/services/schema/schema_columns.js-c63a16e2e0778a885bd27c7cf2ed7d88.es6 +57 -0
  362. data/public/assets/angular/services/schema/schema_comment_resource.js-20254ea1a4e7b4850c9f21f9619130ce.es6 +12 -0
  363. data/public/assets/angular/services/services.js-6225c5ea24a9082506d1932d7884b53b.es6 +140 -0
  364. data/public/assets/angular/services/snippet/ace_snippet_manager.js-dbbba5d8ad5040042b469b294c4d7228.es6 +44 -0
  365. data/public/assets/angular/services/visualization/source_renderer.js-d7274c88b067a35f2e91bf7f39be7d47.es6 +70 -0
  366. data/public/assets/angular/services/visualization/visualization.js-23e36c87721aa263dd3f99769027cabb.es6 +47 -0
  367. data/public/assets/angular/services/visualization/visualization_service.js-25de86b5ceef60cf2b1dfde5fb0e9216.es6 +67 -0
  368. data/public/assets/angular/services/visualization/visualizations.js-1227bc3dcd67e85c8050d82360a26d53.es6 +11 -0
  369. data/public/assets/application-9153e0bf570016651e5eb8c67e037d3a.css +10 -0
  370. data/public/assets/application-ef5c2ae95804ac9785f190a9aea7ad8b.js +149 -0
  371. data/public/assets/bootstrap/glyphicons-halflings-regular-448c34a56d699c29117adc64c43affeb.woff2 +0 -0
  372. data/public/assets/bootstrap/glyphicons-halflings-regular-89889688147bd7575d6327160d64e760.svg +288 -0
  373. data/public/assets/bootstrap/glyphicons-halflings-regular-e18bbf611f2a2e43afc071aa2f4e1512.ttf +0 -0
  374. data/public/assets/bootstrap/glyphicons-halflings-regular-f4769f9bdb7466be65088239c12046d1.eot +0 -0
  375. data/public/assets/bootstrap/glyphicons-halflings-regular-fa2772327f55d8198301fdb8bcfc8158.woff +0 -0
  376. data/public/assets/fontawesome-webfont-4f0022f25672c7f501c339cbf98d9117.ttf +0 -0
  377. data/public/assets/fontawesome-webfont-776d58f453c8fe5d6a89e9c31ee223ff.svg +504 -0
  378. data/public/assets/fontawesome-webfont-90186830c9c50a0fed932494581761d9.eot +0 -0
  379. data/public/assets/fontawesome-webfont-926c93d201fe51c8f351e858468980c3.woff2 +0 -0
  380. data/public/assets/fontawesome-webfont-fdf491ce5ff5b2da02708cd0e9864719.woff +0 -0
  381. data/public/assets/glyphicons-halflings-regular-0a5c48c69a25a93e37ed62db813387fa.svg +229 -0
  382. data/public/assets/glyphicons-halflings-regular-47da44498fc073d9fff9ab0cdb0bef8e.ttf +0 -0
  383. data/public/assets/glyphicons-halflings-regular-5eae1f7217b606d3580dd70ac840fea1.woff +0 -0
  384. data/public/assets/glyphicons-halflings-regular-aa16cd35628e6dddf56e766c9aa4ae63.eot +0 -0
  385. data/public/assets/jquery-ui/ui-bg_flat_0_aaaaaa_40x100-2a44fbdb7360c60122bcf6dcef0387d8.png +0 -0
  386. data/public/assets/jquery-ui/ui-bg_flat_75_ffffff_40x100-8692e6efddf882acbff144c38ea7dfdf.png +0 -0
  387. data/public/assets/jquery-ui/ui-bg_glass_55_fbf9ee_1x400-f8f4558e0b92ff2cd6136781533902ec.png +0 -0
  388. data/public/assets/jquery-ui/ui-bg_glass_65_ffffff_1x400-e5a8f32e28fd5c27bf0fed33c8a8b9b5.png +0 -0
  389. data/public/assets/jquery-ui/ui-bg_glass_75_dadada_1x400-c12c6510dad3ebfa64c8a30e959a2469.png +0 -0
  390. data/public/assets/jquery-ui/ui-bg_glass_75_e6e6e6_1x400-f4254356c2a8c9a383205ef2c4de22c4.png +0 -0
  391. data/public/assets/jquery-ui/ui-bg_glass_95_fef1ec_1x400-5a3be2d8fff8324d59aec3df7b0a0c83.png +0 -0
  392. data/public/assets/jquery-ui/ui-bg_highlight-soft_75_cccccc_1x100-72c593d16e998952cd8d798fee33c6f3.png +0 -0
  393. data/public/assets/jquery-ui/ui-icons_222222_256x240-9129e086dc488d8bcaf808510bc646ba.png +0 -0
  394. data/public/assets/jquery-ui/ui-icons_2e83ff_256x240-25162bf857a8eb83ea932a58436e1049.png +0 -0
  395. data/public/assets/jquery-ui/ui-icons_454545_256x240-771099482bdc1571ece41073b1752596.png +0 -0
  396. data/public/assets/jquery-ui/ui-icons_888888_256x240-faf6f5dc44e713178784c1fb053990aa.png +0 -0
  397. data/public/assets/jquery-ui/ui-icons_cd0a0a_256x240-5d8808d43cefca6f6781a5316d176632.png +0 -0
  398. data/public/assets/octicons-0a82c1edade24862533bbe96ebeaea47.eot +0 -0
  399. data/public/assets/octicons-103abd6cc0199e2519ef6f1aac4bb0e0.ttf +0 -0
  400. data/public/assets/octicons-adc17600a2b7a648eba306c2e1426b85.svg +183 -0
  401. data/public/assets/octicons-be82065223a03ba577de159d97a5d63d.woff +0 -0
  402. data/public/assets/octicons-local-72e4167c13648cb89e9e96cdd212cb82.ttf +0 -0
  403. data/public/assets/rails_admin/aristo/images/bg_fallback-a7547e71656b1e538b93ddb49dfc98d3.png +0 -0
  404. data/public/assets/rails_admin/aristo/images/icon_sprite-415c1eec8a2d31d217d4d1d5a1d44345.png +0 -0
  405. data/public/assets/rails_admin/aristo/images/progress_bar-449b2fab5cf4122c17a416f6e26569fe.gif +0 -0
  406. data/public/assets/rails_admin/aristo/images/slider_handles-ba57b6efb64ca6263f77ce2624ad71ee.png +0 -0
  407. data/public/assets/rails_admin/aristo/images/ui-icons_222222_256x240-a2c05c5e967f9cf4dae51c1243b3b93c.png +0 -0
  408. data/public/assets/rails_admin/aristo/images/ui-icons_454545_256x240-a7a7eff72c4f562524dc472824aab656.png +0 -0
  409. data/public/assets/rails_admin/base/README-f7dd742c2237cb1959438b94c7503a41.txt +2 -0
  410. data/public/assets/rails_admin/bootstrap/glyphicons-halflings-d4fef5262f29f592560fb59918a1c052.png +0 -0
  411. data/public/assets/rails_admin/bootstrap/glyphicons-halflings-white-9bbc6e9602998a385c2ea13df56470fd.png +0 -0
  412. data/public/assets/rails_admin/bullet_black-06d345566cf9e0f97a23bb07ab3d50d9.png +0 -0
  413. data/public/assets/rails_admin/bullet_white-469b537fa21b7143f719578f7426825d.png +0 -0
  414. data/public/assets/rails_admin/calendar-697f5a44753da70028cfd04b98ac45d2.png +0 -0
  415. data/public/assets/rails_admin/clock-0cc82b549af3df4f730a02335f54b0df.png +0 -0
  416. data/public/assets/rails_admin/colorpicker/colorpicker_background-7aaa2a726200c0ac3ba43c01f073a93d.png +0 -0
  417. data/public/assets/rails_admin/colorpicker/colorpicker_hex-cbc06f4015cbb276a3dd966e1be37c25.png +0 -0
  418. data/public/assets/rails_admin/colorpicker/colorpicker_hsb_b-2315aba3c2fbbcfcc22869b21836c31d.png +0 -0
  419. data/public/assets/rails_admin/colorpicker/colorpicker_hsb_h-3120023551e3601467cf52e1c8aaae9e.png +0 -0
  420. data/public/assets/rails_admin/colorpicker/colorpicker_hsb_s-77a77892bdef0906fdfc3426d06108fd.png +0 -0
  421. data/public/assets/rails_admin/colorpicker/colorpicker_indic-f485d07540a89502e36dc1a55cec05d0.gif +0 -0
  422. data/public/assets/rails_admin/colorpicker/colorpicker_overlay-4b2dd6f78c4aac5cc932e845698b2db5.png +0 -0
  423. data/public/assets/rails_admin/colorpicker/colorpicker_rgb_b-2315aba3c2fbbcfcc22869b21836c31d.png +0 -0
  424. data/public/assets/rails_admin/colorpicker/colorpicker_rgb_g-475afe3f4f69e59ff30a445384a8d961.png +0 -0
  425. data/public/assets/rails_admin/colorpicker/colorpicker_rgb_r-02de28945293fe2fd026c9be3b89955a.png +0 -0
  426. data/public/assets/rails_admin/colorpicker/colorpicker_select-cdf55197d05f18c552dc35bdb858fa2a.gif +0 -0
  427. data/public/assets/rails_admin/colorpicker/colorpicker_submit-9912e5cf348e0be359ff9b1078fbb7da.png +0 -0
  428. data/public/assets/rails_admin/colorpicker/custom_background-a0ab8ff3e2b85b1f8ed5a4d86967471b.png +0 -0
  429. data/public/assets/rails_admin/colorpicker/custom_hex-065e780d7b89f7dda63a472caa7c2bdd.png +0 -0
  430. data/public/assets/rails_admin/colorpicker/custom_hsb_b-66f5963cf1a0a2680acbcac2bb77f40d.png +0 -0
  431. data/public/assets/rails_admin/colorpicker/custom_hsb_h-cd1f2e27122aeca17af416f328b758cd.png +0 -0
  432. data/public/assets/rails_admin/colorpicker/custom_hsb_s-ca6f3e4f0c1fc2cb110c788018369e07.png +0 -0
  433. data/public/assets/rails_admin/colorpicker/custom_indic-04660eb352eb259581a36a0fe8da2d4d.gif +0 -0
  434. data/public/assets/rails_admin/colorpicker/custom_rgb_b-64bc0c5110f84efe32e82c58d50660f6.png +0 -0
  435. data/public/assets/rails_admin/colorpicker/custom_rgb_g-33fe011475956750a457b1f93f1b9075.png +0 -0
  436. data/public/assets/rails_admin/colorpicker/custom_rgb_r-18f2ee37089d850c52b212d8a31aac92.png +0 -0
  437. data/public/assets/rails_admin/colorpicker/custom_submit-9d0a14eebb9ae04edfb8099827a42968.png +0 -0
  438. data/public/assets/rails_admin/colorpicker/select-76bd5c10f34de24b73ff4454735d3e18.png +0 -0
  439. data/public/assets/rails_admin/colorpicker/select2-d3fea9a451ac2c29bcd4a82b6e4f326a.png +0 -0
  440. data/public/assets/rails_admin/colorpicker/slider-db040ce7280fc74269e330a862e738e6.png +0 -0
  441. data/public/assets/rails_admin/jquery.colorpicker-7c36c2fac54a031f6e0dfe6ad137cfb1.js +1 -0
  442. data/public/assets/rails_admin/jquery.colorpicker-e402a30af31a4ba5957fec816dc9239c.css +1 -0
  443. data/public/assets/rails_admin/logo-9b479a57b493ee824767163101a5409f.png +0 -0
  444. data/public/assets/rails_admin/magnifier-4fcdfd820ff2e951458af3d1309ab20c.png +0 -0
  445. data/public/assets/rails_admin/multiselect/icon_sprite-415c1eec8a2d31d217d4d1d5a1d44345.png +0 -0
  446. data/public/assets/rails_admin/multiselect/ui-icon-circle-triangle-n-dark-32c1cfdd900379bf62658e475fb2ba38.png +0 -0
  447. data/public/assets/rails_admin/multiselect/ui-icon-circle-triangle-n-light-73160d919a189ba13bd4387e3623b7e9.png +0 -0
  448. data/public/assets/rails_admin/multiselect/ui-icon-circle-triangle-s-dark-c15e6bff59c80c5473236cf77870f31d.png +0 -0
  449. data/public/assets/rails_admin/multiselect/ui-icon-circle-triangle-s-light-fbf43811420f67d49d4dc64c7817f72c.png +0 -0
  450. data/public/assets/rails_admin/rails_admin-d25a684b9f690652469ceb212fbe7b95.js +328 -0
  451. data/public/assets/rails_admin/rails_admin-d85fb3c8bbff377491ed5af5239cf185.css +8 -0
  452. data/public/assets/resque_web/application-b29b34115c3ad5c539f26114fc91e584.css +4 -0
  453. data/public/assets/resque_web/application-d2bc6e68cf7c6215e804fe9e3f46f1d0.js +111 -0
  454. data/public/assets/resque_web/idle-53474088772d9917bd463b19efa32fc3.png +0 -0
  455. data/public/assets/resque_web/lifebuoy-0281230b31ba4078685f4df3e8169534.png +0 -0
  456. data/public/assets/resque_web/poll-af53f85832c41a9cde68b08cb09711f1.png +0 -0
  457. data/public/assets/resque_web/rails-9c0a079bdd7701d7e729bd956823d153.png +0 -0
  458. data/public/assets/resque_web/working-26a36c5119cbdcc64ebade2c5f8c389d.png +0 -0
  459. data/public/assets/twitter/bootstrap/glyphicons-halflings-2516339970d710819585f90773aebe0a.png +0 -0
  460. data/public/assets/twitter/bootstrap/glyphicons-halflings-white-9bbc6e9602998a385c2ea13df56470fd.png +0 -0
  461. data/public/bg_big.svg +2 -0
  462. data/public/bg_new.svg +2 -0
  463. data/public/favicon.ico +0 -0
  464. data/public/resources/keyword_config.json +51 -0
  465. data/public/resources/visualization/area.html +17 -0
  466. data/public/resources/visualization/area_step.html +17 -0
  467. data/public/resources/visualization/bar.html +17 -0
  468. data/public/resources/visualization/custom.html +6 -0
  469. data/public/resources/visualization/line.html +16 -0
  470. data/public/resources/visualization/presets.json +13 -0
  471. data/public/resources/visualization/scatter.html +17 -0
  472. data/public/resources/visualization/spline.html +17 -0
  473. data/public/resources/visualization/spline_area.html +17 -0
  474. data/public/resources/visualization/stacked_area.html +18 -0
  475. data/public/resources/visualization/stacked_bar.html +18 -0
  476. data/public/resources/visualization/stacked_spline_area.html +18 -0
  477. data/public/resources/visualization/template.json +12 -0
  478. data/public/robots.txt +2 -0
  479. data/vendor/assets/javascripts/ace/ace.js +18655 -0
  480. data/vendor/assets/javascripts/ace/ext-language_tools.js +1937 -0
  481. data/vendor/assets/javascripts/ace/mode-html.js +2794 -0
  482. data/vendor/assets/javascripts/ace/mode-pgsql.js +1503 -0
  483. data/vendor/assets/javascripts/ace/snippets/coffee.js +102 -0
  484. data/vendor/assets/javascripts/ace/snippets/css.js +974 -0
  485. data/vendor/assets/javascripts/ace/snippets/haml.js +27 -0
  486. data/vendor/assets/javascripts/ace/snippets/html.js +835 -0
  487. data/vendor/assets/javascripts/ace/snippets/html_elixir.js +7 -0
  488. data/vendor/assets/javascripts/ace/snippets/html_ruby.js +7 -0
  489. data/vendor/assets/javascripts/ace/snippets/javascript.js +202 -0
  490. data/vendor/assets/javascripts/ace/snippets/json.js +7 -0
  491. data/vendor/assets/javascripts/ace/snippets/jsp.js +106 -0
  492. data/vendor/assets/javascripts/ace/snippets/jsx.js +7 -0
  493. data/vendor/assets/javascripts/ace/snippets/less.js +7 -0
  494. data/vendor/assets/javascripts/ace/snippets/mysql.js +7 -0
  495. data/vendor/assets/javascripts/ace/snippets/pgsql.js +7 -0
  496. data/vendor/assets/javascripts/ace/snippets/plain_text.js +7 -0
  497. data/vendor/assets/javascripts/ace/snippets/rhtml.js +7 -0
  498. data/vendor/assets/javascripts/ace/snippets/sass.js +7 -0
  499. data/vendor/assets/javascripts/ace/snippets/scss.js +7 -0
  500. data/vendor/assets/javascripts/ace/snippets/snippets.js +16 -0
  501. data/vendor/assets/javascripts/ace/snippets/sql.js +33 -0
  502. data/vendor/assets/javascripts/ace/snippets/sqlserver.js +76 -0
  503. data/vendor/assets/javascripts/ace/snippets/svg.js +7 -0
  504. data/vendor/assets/javascripts/ace/snippets/text.js +7 -0
  505. data/vendor/assets/javascripts/ace/snippets/textile.js +37 -0
  506. data/vendor/assets/javascripts/ace/theme-monokai.js +105 -0
  507. data/vendor/assets/javascripts/angular-filter/angular-filter.js +2271 -0
  508. data/vendor/assets/javascripts/angular-hotkeys/hotkeys.js +1523 -0
  509. data/vendor/assets/javascripts/angular-selection-model/selection-model.js +563 -0
  510. data/vendor/assets/javascripts/angular-spinner/treasure-overlay-spinner.js +53 -0
  511. data/vendor/assets/javascripts/angular-ui-ace/ui-ace.js +298 -0
  512. data/vendor/assets/javascripts/angular-ui-bootstrap/ui-bootstrap-tpls-0.14.3.js +8503 -0
  513. data/vendor/assets/javascripts/d3/c3.min.js +5 -0
  514. data/vendor/assets/javascripts/d3/d3.v3.min.js +5 -0
  515. data/vendor/assets/javascripts/infinite-scroll/infinite-scroll.js +186 -0
  516. data/vendor/assets/javascripts/ng-clip/ngClip.js +84 -0
  517. data/vendor/assets/javascripts/ng-tags-input/ng-tags-input.min.js +1 -0
  518. data/vendor/assets/javascripts/pluralize/pluralize.js +453 -0
  519. data/vendor/assets/javascripts/underscore-js/underscore.js +1548 -0
  520. data/vendor/assets/stylesheets/angular-hotkeys/hotkeys.css +110 -0
  521. data/vendor/assets/stylesheets/angular-spinner/treasure-overlay-spinner.css +136 -0
  522. data/vendor/assets/stylesheets/c3/c3.min.css +1 -0
  523. data/vendor/assets/stylesheets/ng-tags-input/ng-tags-input.bootstrap.min.css +1 -0
  524. data/vendor/assets/stylesheets/ng-tags-input/ng-tags-input.min.css +1 -0
  525. data/vendor/assets/stylesheets/octicons/README.md +1 -0
  526. data/vendor/assets/stylesheets/octicons/sprockets-octicons.scss +217 -0
  527. metadata +600 -0
@@ -0,0 +1,1523 @@
1
+ /*!
2
+ * angular-hotkeys v1.4.5
3
+ * https://chieffancypants.github.io/angular-hotkeys
4
+ * Copyright (c) 2014 Wes Cruver
5
+ * License: MIT
6
+ */
7
+ /*
8
+ * angular-hotkeys
9
+ *
10
+ * Automatic keyboard shortcuts for your angular apps
11
+ *
12
+ * (c) 2014 Wes Cruver
13
+ * License: MIT
14
+ */
15
+
16
+ (function() {
17
+
18
+ 'use strict';
19
+
20
+ angular.module('cfp.hotkeys', []).provider('hotkeys', function() {
21
+
22
+ /**
23
+ * Configurable setting to disable the cheatsheet entirely
24
+ * @type {Boolean}
25
+ */
26
+ this.includeCheatSheet = true;
27
+
28
+ /**
29
+ * Configurable setting for the cheat sheet title
30
+ * @type {String}
31
+ */
32
+
33
+ this.templateTitle = 'Keyboard Shortcuts:';
34
+
35
+ /**
36
+ * Cheat sheet template in the event you want to totally customize it.
37
+ * @type {String}
38
+ */
39
+ this.template = '<div class="cfp-hotkeys-container fade" ng-class="{in: helpVisible}" style="display: none;"><div class="cfp-hotkeys">' +
40
+ '<h4 class="cfp-hotkeys-title">{{ title }}</h4>' +
41
+ '<table><tbody>' +
42
+ '<tr ng-repeat="hotkey in hotkeys | filter:{ description: \'!$$undefined$$\' }">' +
43
+ '<td class="cfp-hotkeys-keys">' +
44
+ '<span ng-repeat="key in hotkey.format() track by $index" class="cfp-hotkeys-key">{{ key }}</span>' +
45
+ '</td>' +
46
+ '<td class="cfp-hotkeys-text">{{ hotkey.description }}</td>' +
47
+ '</tr>' +
48
+ '</tbody></table>' +
49
+ '<div class="cfp-hotkeys-close" ng-click="toggleCheatSheet()">×</div>' +
50
+ '</div></div>';
51
+
52
+ /**
53
+ * Configurable setting for the cheat sheet hotkey
54
+ * @type {String}
55
+ */
56
+ this.cheatSheetHotkey = '?';
57
+
58
+ /**
59
+ * Configurable setting for the cheat sheet description
60
+ * @type {String}
61
+ */
62
+ this.cheatSheetDescription = 'Show / hide this help menu';
63
+
64
+ this.$get = ['$rootElement', '$rootScope', '$compile', '$window', '$document', function ($rootElement, $rootScope, $compile, $window, $document) {
65
+
66
+ // monkeypatch Mousetrap's stopCallback() function
67
+ // this version doesn't return true when the element is an INPUT, SELECT, or TEXTAREA
68
+ // (instead we will perform this check per-key in the _add() method)
69
+ Mousetrap.stopCallback = function(event, element) {
70
+ // if the element has the class "mousetrap" then no need to stop
71
+ if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
72
+ return false;
73
+ }
74
+
75
+ return (element.contentEditable && element.contentEditable == 'true');
76
+ };
77
+
78
+ /**
79
+ * Convert strings like cmd into symbols like ⌘
80
+ * @param {String} combo Key combination, e.g. 'mod+f'
81
+ * @return {String} The key combination with symbols
82
+ */
83
+ function symbolize (combo) {
84
+ var map = {
85
+ command : '⌘',
86
+ shift : '⇧',
87
+ left : '←',
88
+ right : '→',
89
+ up : '↑',
90
+ down : '↓',
91
+ 'return' : '↩',
92
+ backspace : '⌫'
93
+ };
94
+ combo = combo.split('+');
95
+
96
+ for (var i = 0; i < combo.length; i++) {
97
+ // try to resolve command / ctrl based on OS:
98
+ if (combo[i] === 'mod') {
99
+ if ($window.navigator && $window.navigator.platform.indexOf('Mac') >=0 ) {
100
+ combo[i] = 'command';
101
+ } else {
102
+ combo[i] = 'ctrl';
103
+ }
104
+ }
105
+
106
+ combo[i] = map[combo[i]] || combo[i];
107
+ }
108
+
109
+ return combo.join(' + ');
110
+ }
111
+
112
+ /**
113
+ * Hotkey object used internally for consistency
114
+ *
115
+ * @param {array} combo The keycombo. it's an array to support multiple combos
116
+ * @param {String} description Description for the keycombo
117
+ * @param {Function} callback function to execute when keycombo pressed
118
+ * @param {string} action the type of event to listen for (for mousetrap)
119
+ * @param {array} allowIn an array of tag names to allow this combo in ('INPUT', 'SELECT', and/or 'TEXTAREA')
120
+ * @param {Boolean} persistent Whether the hotkey persists navigation events
121
+ */
122
+ function Hotkey (combo, description, callback, action, allowIn, persistent) {
123
+ // TODO: Check that the values are sane because we could
124
+ // be trying to instantiate a new Hotkey with outside dev's
125
+ // supplied values
126
+
127
+ this.combo = combo instanceof Array ? combo : [combo];
128
+ this.description = description;
129
+ this.callback = callback;
130
+ this.action = action;
131
+ this.allowIn = allowIn;
132
+ this.persistent = persistent;
133
+ }
134
+
135
+ /**
136
+ * Helper method to format (symbolize) the key combo for display
137
+ *
138
+ * @return {[Array]} An array of the key combination sequence
139
+ * for example: "command+g c i" becomes ["⌘ + g", "c", "i"]
140
+ *
141
+ * TODO: this gets called a lot. We should cache the result
142
+ */
143
+ Hotkey.prototype.format = function() {
144
+
145
+ // Don't show all the possible key combos, just the first one. Not sure
146
+ // of usecase here, so open a ticket if my assumptions are wrong
147
+ var combo = this.combo[0];
148
+
149
+ var sequence = combo.split(/[\s]/);
150
+ for (var i = 0; i < sequence.length; i++) {
151
+ sequence[i] = symbolize(sequence[i]);
152
+ }
153
+
154
+ return sequence;
155
+ };
156
+
157
+ /**
158
+ * A new scope used internally for the cheatsheet
159
+ * @type {$rootScope.Scope}
160
+ */
161
+ var scope = $rootScope.$new();
162
+
163
+ /**
164
+ * Holds an array of Hotkey objects currently bound
165
+ * @type {Array}
166
+ */
167
+ scope.hotkeys = [];
168
+
169
+ /**
170
+ * Contains the state of the help's visibility
171
+ * @type {Boolean}
172
+ */
173
+ scope.helpVisible = false;
174
+
175
+ /**
176
+ * Holds the title string for the help menu
177
+ * @type {String}
178
+ */
179
+ scope.title = this.templateTitle;
180
+
181
+ /**
182
+ * Expose toggleCheatSheet to hotkeys scope so we can call it using
183
+ * ng-click from the template
184
+ * @type {function}
185
+ */
186
+ scope.toggleCheatSheet = toggleCheatSheet;
187
+
188
+
189
+ /**
190
+ * Holds references to the different scopes that have bound hotkeys
191
+ * attached. This is useful to catch when the scopes are `$destroy`d and
192
+ * then automatically unbind the hotkey.
193
+ *
194
+ * @type {Array}
195
+ */
196
+ var boundScopes = [];
197
+
198
+
199
+ $rootScope.$on('$routeChangeSuccess', function (event, route) {
200
+ purgeHotkeys();
201
+
202
+ if (route && route.hotkeys) {
203
+ angular.forEach(route.hotkeys, function (hotkey) {
204
+ // a string was given, which implies this is a function that is to be
205
+ // $eval()'d within that controller's scope
206
+ // TODO: hotkey here is super confusing. sometimes a function (that gets turned into an array), sometimes a string
207
+ var callback = hotkey[2];
208
+ if (typeof(callback) === 'string' || callback instanceof String) {
209
+ hotkey[2] = [callback, route];
210
+ }
211
+
212
+ // todo: perform check to make sure not already defined:
213
+ // this came from a route, so it's likely not meant to be persistent
214
+ hotkey[5] = false;
215
+ _add.apply(this, hotkey);
216
+ });
217
+ }
218
+ });
219
+
220
+
221
+ // Auto-create a help menu:
222
+ if (this.includeCheatSheet) {
223
+ var document = $document[0];
224
+ var element = $rootElement[0];
225
+ var helpMenu = angular.element(this.template);
226
+ _add(this.cheatSheetHotkey, this.cheatSheetDescription, toggleCheatSheet);
227
+
228
+ // If $rootElement is document or documentElement, then body must be used
229
+ if (element === document || element === document.documentElement) {
230
+ element = document.body;
231
+ }
232
+
233
+ angular.element(element).append($compile(helpMenu)(scope));
234
+ }
235
+
236
+
237
+ /**
238
+ * Purges all non-persistent hotkeys (such as those defined in routes)
239
+ *
240
+ * Without this, the same hotkey would get recreated everytime
241
+ * the route is accessed.
242
+ */
243
+ function purgeHotkeys() {
244
+ var i = scope.hotkeys.length;
245
+ while (i--) {
246
+ var hotkey = scope.hotkeys[i];
247
+ if (hotkey && !hotkey.persistent) {
248
+ _del(hotkey);
249
+ }
250
+ }
251
+ }
252
+
253
+ /**
254
+ * Toggles the help menu element's visiblity
255
+ */
256
+ var previousEsc = false;
257
+
258
+ function toggleCheatSheet() {
259
+ scope.helpVisible = !scope.helpVisible;
260
+
261
+ // Bind to esc to remove the cheat sheet. Ideally, this would be done
262
+ // as a directive in the template, but that would create a nasty
263
+ // circular dependency issue that I don't feel like sorting out.
264
+ if (scope.helpVisible) {
265
+ previousEsc = _get('esc');
266
+ _del('esc');
267
+
268
+ // Here's an odd way to do this: we're going to use the original
269
+ // description of the hotkey on the cheat sheet so that it shows up.
270
+ // without it, no entry for esc will ever show up (#22)
271
+ _add('esc', previousEsc.description, toggleCheatSheet);
272
+ } else {
273
+ _del('esc');
274
+
275
+ // restore the previously bound ESC key
276
+ if (previousEsc !== false) {
277
+ _add(previousEsc);
278
+ }
279
+ }
280
+ }
281
+
282
+ /**
283
+ * Creates a new Hotkey and creates the Mousetrap binding
284
+ *
285
+ * @param {string} combo mousetrap key binding
286
+ * @param {string} description description for the help menu
287
+ * @param {Function} callback method to call when key is pressed
288
+ * @param {string} action the type of event to listen for (for mousetrap)
289
+ * @param {array} allowIn an array of tag names to allow this combo in ('INPUT', 'SELECT', and/or 'TEXTAREA')
290
+ * @param {boolean} persistent if true, the binding is preserved upon route changes
291
+ */
292
+ function _add (combo, description, callback, action, allowIn, persistent) {
293
+
294
+ // used to save original callback for "allowIn" wrapping:
295
+ var _callback;
296
+
297
+ // these elements are prevented by the default Mousetrap.stopCallback():
298
+ var preventIn = ['INPUT', 'SELECT', 'TEXTAREA'];
299
+
300
+ // Determine if object format was given:
301
+ var objType = Object.prototype.toString.call(combo);
302
+
303
+ if (objType === '[object Object]') {
304
+ description = combo.description;
305
+ callback = combo.callback;
306
+ action = combo.action;
307
+ persistent = combo.persistent;
308
+ allowIn = combo.allowIn;
309
+ combo = combo.combo;
310
+ }
311
+
312
+ // description is optional:
313
+ if (description instanceof Function) {
314
+ action = callback;
315
+ callback = description;
316
+ description = '$$undefined$$';
317
+ } else if (angular.isUndefined(description)) {
318
+ description = '$$undefined$$';
319
+ }
320
+
321
+ // any items added through the public API are for controllers
322
+ // that persist through navigation, and thus undefined should mean
323
+ // true in this case.
324
+ if (persistent === undefined) {
325
+ persistent = true;
326
+ }
327
+
328
+ // if callback is defined, then wrap it in a function
329
+ // that checks if the event originated from a form element.
330
+ // the function blocks the callback from executing unless the element is specified
331
+ // in allowIn (emulates Mousetrap.stopCallback() on a per-key level)
332
+ if (typeof callback === 'function') {
333
+
334
+ // save the original callback
335
+ _callback = callback;
336
+
337
+ // make sure allowIn is an array
338
+ if (!(allowIn instanceof Array)) {
339
+ allowIn = [];
340
+ }
341
+
342
+ // remove anything from preventIn that's present in allowIn
343
+ var index;
344
+ for (var i=0; i < allowIn.length; i++) {
345
+ allowIn[i] = allowIn[i].toUpperCase();
346
+ index = preventIn.indexOf(allowIn[i]);
347
+ if (index !== -1) {
348
+ preventIn.splice(index, 1);
349
+ }
350
+ }
351
+
352
+ // create the new wrapper callback
353
+ callback = function(event) {
354
+ var shouldExecute = true;
355
+ var target = event.target || event.srcElement; // srcElement is IE only
356
+ var nodeName = target.nodeName.toUpperCase();
357
+
358
+ // check if the input has a mousetrap class, and skip checking preventIn if so
359
+ if ((' ' + target.className + ' ').indexOf(' mousetrap ') > -1) {
360
+ shouldExecute = true;
361
+ } else {
362
+ // don't execute callback if the event was fired from inside an element listed in preventIn
363
+ for (var i=0; i<preventIn.length; i++) {
364
+ if (preventIn[i] === nodeName) {
365
+ shouldExecute = false;
366
+ break;
367
+ }
368
+ }
369
+ }
370
+
371
+ if (shouldExecute) {
372
+ wrapApply(_callback.apply(this, arguments));
373
+ }
374
+ };
375
+ }
376
+
377
+ if (typeof(action) === 'string') {
378
+ Mousetrap.bind(combo, wrapApply(callback), action);
379
+ } else {
380
+ Mousetrap.bind(combo, wrapApply(callback));
381
+ }
382
+
383
+ var hotkey = new Hotkey(combo, description, callback, action, allowIn, persistent);
384
+ scope.hotkeys.push(hotkey);
385
+ return hotkey;
386
+ }
387
+
388
+ /**
389
+ * delete and unbind a Hotkey
390
+ *
391
+ * @param {mixed} hotkey Either the bound key or an instance of Hotkey
392
+ * @return {boolean} true if successful
393
+ */
394
+ function _del (hotkey) {
395
+ var combo = (hotkey instanceof Hotkey) ? hotkey.combo : hotkey;
396
+
397
+ Mousetrap.unbind(combo);
398
+
399
+ if (angular.isArray(combo)) {
400
+ var retStatus = true;
401
+ var i = combo.length;
402
+ while (i--) {
403
+ retStatus = _del(combo[i]) && retStatus;
404
+ }
405
+ return retStatus;
406
+ } else {
407
+ var index = scope.hotkeys.indexOf(_get(combo));
408
+
409
+ if (index > -1) {
410
+ // if the combo has other combos bound, don't unbind the whole thing, just the one combo:
411
+ if (scope.hotkeys[index].combo.length > 1) {
412
+ scope.hotkeys[index].combo.splice(scope.hotkeys[index].combo.indexOf(combo), 1);
413
+ } else {
414
+ scope.hotkeys.splice(index, 1);
415
+ }
416
+ return true;
417
+ }
418
+ }
419
+
420
+ return false;
421
+
422
+ }
423
+
424
+ /**
425
+ * Get a Hotkey object by key binding
426
+ *
427
+ * @param {[string]} combo the key the Hotkey is bound to
428
+ * @return {Hotkey} The Hotkey object
429
+ */
430
+ function _get (combo) {
431
+
432
+ var hotkey;
433
+
434
+ for (var i = 0; i < scope.hotkeys.length; i++) {
435
+ hotkey = scope.hotkeys[i];
436
+
437
+ if (hotkey.combo.indexOf(combo) > -1) {
438
+ return hotkey;
439
+ }
440
+ }
441
+
442
+ return false;
443
+ }
444
+
445
+ /**
446
+ * Binds the hotkey to a particular scope. Useful if the scope is
447
+ * destroyed, we can automatically destroy the hotkey binding.
448
+ *
449
+ * @param {Object} scope The scope to bind to
450
+ */
451
+ function bindTo (scope) {
452
+ // Only initialize once to allow multiple calls for same scope.
453
+ if (!(scope.$id in boundScopes)) {
454
+
455
+ // Add the scope to the list of bound scopes
456
+ boundScopes[scope.$id] = [];
457
+
458
+ scope.$on('$destroy', function () {
459
+ var i = boundScopes[scope.$id].length;
460
+ while (i--) {
461
+ _del(boundScopes[scope.$id][i]);
462
+ delete boundScopes[scope.$id][i];
463
+ }
464
+ });
465
+ }
466
+ // return an object with an add function so we can keep track of the
467
+ // hotkeys and their scope that we added via this chaining method
468
+ return {
469
+ add: function (args) {
470
+ var hotkey;
471
+
472
+ if (arguments.length > 1) {
473
+ hotkey = _add.apply(this, arguments);
474
+ } else {
475
+ hotkey = _add(args);
476
+ }
477
+
478
+ boundScopes[scope.$id].push(hotkey);
479
+ return this;
480
+ }
481
+ };
482
+ }
483
+
484
+ /**
485
+ * All callbacks sent to Mousetrap are wrapped using this function
486
+ * so that we can force a $scope.$apply()
487
+ *
488
+ * @param {Function} callback [description]
489
+ * @return {[type]} [description]
490
+ */
491
+ function wrapApply (callback) {
492
+ // return mousetrap a function to call
493
+ return function (event, combo) {
494
+
495
+ // if this is an array, it means we provided a route object
496
+ // because the scope wasn't available yet, so rewrap the callback
497
+ // now that the scope is available:
498
+ if (callback instanceof Array) {
499
+ var funcString = callback[0];
500
+ var route = callback[1];
501
+ callback = function (event) {
502
+ route.scope.$eval(funcString);
503
+ };
504
+ }
505
+
506
+ // this takes place outside angular, so we'll have to call
507
+ // $apply() to make sure angular's digest happens
508
+ $rootScope.$apply(function() {
509
+ // call the original hotkey callback with the keyboard event
510
+ callback(event, _get(combo));
511
+ });
512
+ };
513
+ }
514
+
515
+
516
+ var publicApi = {
517
+ add : _add,
518
+ del : _del,
519
+ get : _get,
520
+ bindTo : bindTo,
521
+ template : this.template,
522
+ toggleCheatSheet : toggleCheatSheet,
523
+ includeCheatSheet : this.includeCheatSheet,
524
+ cheatSheetHotkey : this.cheatSheetHotkey,
525
+ cheatSheetDescription : this.cheatSheetDescription,
526
+ purgeHotkeys : purgeHotkeys,
527
+ templateTitle : this.templateTitle
528
+ };
529
+
530
+ return publicApi;
531
+
532
+ }];
533
+ })
534
+
535
+ .directive('hotkey', ['hotkeys', function (hotkeys) {
536
+ return {
537
+ restrict: 'A',
538
+ link: function (scope, el, attrs) {
539
+ var key, allowIn;
540
+
541
+ angular.forEach(scope.$eval(attrs.hotkey), function (func, hotkey) {
542
+ // split and trim the hotkeys string into array
543
+ allowIn = typeof attrs.hotkeyAllowIn === "string" ? attrs.hotkeyAllowIn.split(/[\s,]+/) : [];
544
+
545
+ key = hotkey;
546
+
547
+ hotkeys.add({
548
+ combo: hotkey,
549
+ description: attrs.hotkeyDescription,
550
+ callback: func,
551
+ action: attrs.hotkeyAction,
552
+ allowIn: allowIn
553
+ });
554
+ });
555
+
556
+ // remove the hotkey if the directive is destroyed:
557
+ el.bind('$destroy', function() {
558
+ hotkeys.del(key);
559
+ });
560
+ }
561
+ };
562
+ }])
563
+
564
+ .run(['hotkeys', function(hotkeys) {
565
+ // force hotkeys to run by injecting it. Without this, hotkeys only runs
566
+ // when a controller or something else asks for it via DI.
567
+ }]);
568
+
569
+ })();
570
+
571
+ /*global define:false */
572
+ /**
573
+ * Copyright 2013 Craig Campbell
574
+ *
575
+ * Licensed under the Apache License, Version 2.0 (the "License");
576
+ * you may not use this file except in compliance with the License.
577
+ * You may obtain a copy of the License at
578
+ *
579
+ * http://www.apache.org/licenses/LICENSE-2.0
580
+ *
581
+ * Unless required by applicable law or agreed to in writing, software
582
+ * distributed under the License is distributed on an "AS IS" BASIS,
583
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
584
+ * See the License for the specific language governing permissions and
585
+ * limitations under the License.
586
+ *
587
+ * Mousetrap is a simple keyboard shortcut library for Javascript with
588
+ * no external dependencies
589
+ *
590
+ * @version 1.4.6
591
+ * @url craig.is/killing/mice
592
+ */
593
+ (function(window, document, undefined) {
594
+
595
+ /**
596
+ * mapping of special keycodes to their corresponding keys
597
+ *
598
+ * everything in this dictionary cannot use keypress events
599
+ * so it has to be here to map to the correct keycodes for
600
+ * keyup/keydown events
601
+ *
602
+ * @type {Object}
603
+ */
604
+ var _MAP = {
605
+ 8: 'backspace',
606
+ 9: 'tab',
607
+ 13: 'enter',
608
+ 16: 'shift',
609
+ 17: 'ctrl',
610
+ 18: 'alt',
611
+ 20: 'capslock',
612
+ 27: 'esc',
613
+ 32: 'space',
614
+ 33: 'pageup',
615
+ 34: 'pagedown',
616
+ 35: 'end',
617
+ 36: 'home',
618
+ 37: 'left',
619
+ 38: 'up',
620
+ 39: 'right',
621
+ 40: 'down',
622
+ 45: 'ins',
623
+ 46: 'del',
624
+ 91: 'meta',
625
+ 93: 'meta',
626
+ 224: 'meta'
627
+ },
628
+
629
+ /**
630
+ * mapping for special characters so they can support
631
+ *
632
+ * this dictionary is only used incase you want to bind a
633
+ * keyup or keydown event to one of these keys
634
+ *
635
+ * @type {Object}
636
+ */
637
+ _KEYCODE_MAP = {
638
+ 106: '*',
639
+ 107: '+',
640
+ 109: '-',
641
+ 110: '.',
642
+ 111 : '/',
643
+ 186: ';',
644
+ 187: '=',
645
+ 188: ',',
646
+ 189: '-',
647
+ 190: '.',
648
+ 191: '/',
649
+ 192: '`',
650
+ 219: '[',
651
+ 220: '\\',
652
+ 221: ']',
653
+ 222: '\''
654
+ },
655
+
656
+ /**
657
+ * this is a mapping of keys that require shift on a US keypad
658
+ * back to the non shift equivelents
659
+ *
660
+ * this is so you can use keyup events with these keys
661
+ *
662
+ * note that this will only work reliably on US keyboards
663
+ *
664
+ * @type {Object}
665
+ */
666
+ _SHIFT_MAP = {
667
+ '~': '`',
668
+ '!': '1',
669
+ '@': '2',
670
+ '#': '3',
671
+ '$': '4',
672
+ '%': '5',
673
+ '^': '6',
674
+ '&': '7',
675
+ '*': '8',
676
+ '(': '9',
677
+ ')': '0',
678
+ '_': '-',
679
+ '+': '=',
680
+ ':': ';',
681
+ '\"': '\'',
682
+ '<': ',',
683
+ '>': '.',
684
+ '?': '/',
685
+ '|': '\\'
686
+ },
687
+
688
+ /**
689
+ * this is a list of special strings you can use to map
690
+ * to modifier keys when you specify your keyboard shortcuts
691
+ *
692
+ * @type {Object}
693
+ */
694
+ _SPECIAL_ALIASES = {
695
+ 'option': 'alt',
696
+ 'command': 'meta',
697
+ 'return': 'enter',
698
+ 'escape': 'esc',
699
+ 'mod': /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? 'meta' : 'ctrl'
700
+ },
701
+
702
+ /**
703
+ * variable to store the flipped version of _MAP from above
704
+ * needed to check if we should use keypress or not when no action
705
+ * is specified
706
+ *
707
+ * @type {Object|undefined}
708
+ */
709
+ _REVERSE_MAP,
710
+
711
+ /**
712
+ * a list of all the callbacks setup via Mousetrap.bind()
713
+ *
714
+ * @type {Object}
715
+ */
716
+ _callbacks = {},
717
+
718
+ /**
719
+ * direct map of string combinations to callbacks used for trigger()
720
+ *
721
+ * @type {Object}
722
+ */
723
+ _directMap = {},
724
+
725
+ /**
726
+ * keeps track of what level each sequence is at since multiple
727
+ * sequences can start out with the same sequence
728
+ *
729
+ * @type {Object}
730
+ */
731
+ _sequenceLevels = {},
732
+
733
+ /**
734
+ * variable to store the setTimeout call
735
+ *
736
+ * @type {null|number}
737
+ */
738
+ _resetTimer,
739
+
740
+ /**
741
+ * temporary state where we will ignore the next keyup
742
+ *
743
+ * @type {boolean|string}
744
+ */
745
+ _ignoreNextKeyup = false,
746
+
747
+ /**
748
+ * temporary state where we will ignore the next keypress
749
+ *
750
+ * @type {boolean}
751
+ */
752
+ _ignoreNextKeypress = false,
753
+
754
+ /**
755
+ * are we currently inside of a sequence?
756
+ * type of action ("keyup" or "keydown" or "keypress") or false
757
+ *
758
+ * @type {boolean|string}
759
+ */
760
+ _nextExpectedAction = false;
761
+
762
+ /**
763
+ * loop through the f keys, f1 to f19 and add them to the map
764
+ * programatically
765
+ */
766
+ for (var i = 1; i < 20; ++i) {
767
+ _MAP[111 + i] = 'f' + i;
768
+ }
769
+
770
+ /**
771
+ * loop through to map numbers on the numeric keypad
772
+ */
773
+ for (i = 0; i <= 9; ++i) {
774
+ _MAP[i + 96] = i;
775
+ }
776
+
777
+ /**
778
+ * cross browser add event method
779
+ *
780
+ * @param {Element|HTMLDocument} object
781
+ * @param {string} type
782
+ * @param {Function} callback
783
+ * @returns void
784
+ */
785
+ function _addEvent(object, type, callback) {
786
+ if (object.addEventListener) {
787
+ object.addEventListener(type, callback, false);
788
+ return;
789
+ }
790
+
791
+ object.attachEvent('on' + type, callback);
792
+ }
793
+
794
+ /**
795
+ * takes the event and returns the key character
796
+ *
797
+ * @param {Event} e
798
+ * @return {string}
799
+ */
800
+ function _characterFromEvent(e) {
801
+
802
+ // for keypress events we should return the character as is
803
+ if (e.type == 'keypress') {
804
+ var character = String.fromCharCode(e.which);
805
+
806
+ // if the shift key is not pressed then it is safe to assume
807
+ // that we want the character to be lowercase. this means if
808
+ // you accidentally have caps lock on then your key bindings
809
+ // will continue to work
810
+ //
811
+ // the only side effect that might not be desired is if you
812
+ // bind something like 'A' cause you want to trigger an
813
+ // event when capital A is pressed caps lock will no longer
814
+ // trigger the event. shift+a will though.
815
+ if (!e.shiftKey) {
816
+ character = character.toLowerCase();
817
+ }
818
+
819
+ return character;
820
+ }
821
+
822
+ // for non keypress events the special maps are needed
823
+ if (_MAP[e.which]) {
824
+ return _MAP[e.which];
825
+ }
826
+
827
+ if (_KEYCODE_MAP[e.which]) {
828
+ return _KEYCODE_MAP[e.which];
829
+ }
830
+
831
+ // if it is not in the special map
832
+
833
+ // with keydown and keyup events the character seems to always
834
+ // come in as an uppercase character whether you are pressing shift
835
+ // or not. we should make sure it is always lowercase for comparisons
836
+ return String.fromCharCode(e.which).toLowerCase();
837
+ }
838
+
839
+ /**
840
+ * checks if two arrays are equal
841
+ *
842
+ * @param {Array} modifiers1
843
+ * @param {Array} modifiers2
844
+ * @returns {boolean}
845
+ */
846
+ function _modifiersMatch(modifiers1, modifiers2) {
847
+ return modifiers1.sort().join(',') === modifiers2.sort().join(',');
848
+ }
849
+
850
+ /**
851
+ * resets all sequence counters except for the ones passed in
852
+ *
853
+ * @param {Object} doNotReset
854
+ * @returns void
855
+ */
856
+ function _resetSequences(doNotReset) {
857
+ doNotReset = doNotReset || {};
858
+
859
+ var activeSequences = false,
860
+ key;
861
+
862
+ for (key in _sequenceLevels) {
863
+ if (doNotReset[key]) {
864
+ activeSequences = true;
865
+ continue;
866
+ }
867
+ _sequenceLevels[key] = 0;
868
+ }
869
+
870
+ if (!activeSequences) {
871
+ _nextExpectedAction = false;
872
+ }
873
+ }
874
+
875
+ /**
876
+ * finds all callbacks that match based on the keycode, modifiers,
877
+ * and action
878
+ *
879
+ * @param {string} character
880
+ * @param {Array} modifiers
881
+ * @param {Event|Object} e
882
+ * @param {string=} sequenceName - name of the sequence we are looking for
883
+ * @param {string=} combination
884
+ * @param {number=} level
885
+ * @returns {Array}
886
+ */
887
+ function _getMatches(character, modifiers, e, sequenceName, combination, level) {
888
+ var i,
889
+ callback,
890
+ matches = [],
891
+ action = e.type;
892
+
893
+ // if there are no events related to this keycode
894
+ if (!_callbacks[character]) {
895
+ return [];
896
+ }
897
+
898
+ // if a modifier key is coming up on its own we should allow it
899
+ if (action == 'keyup' && _isModifier(character)) {
900
+ modifiers = [character];
901
+ }
902
+
903
+ // loop through all callbacks for the key that was pressed
904
+ // and see if any of them match
905
+ for (i = 0; i < _callbacks[character].length; ++i) {
906
+ callback = _callbacks[character][i];
907
+
908
+ // if a sequence name is not specified, but this is a sequence at
909
+ // the wrong level then move onto the next match
910
+ if (!sequenceName && callback.seq && _sequenceLevels[callback.seq] != callback.level) {
911
+ continue;
912
+ }
913
+
914
+ // if the action we are looking for doesn't match the action we got
915
+ // then we should keep going
916
+ if (action != callback.action) {
917
+ continue;
918
+ }
919
+
920
+ // if this is a keypress event and the meta key and control key
921
+ // are not pressed that means that we need to only look at the
922
+ // character, otherwise check the modifiers as well
923
+ //
924
+ // chrome will not fire a keypress if meta or control is down
925
+ // safari will fire a keypress if meta or meta+shift is down
926
+ // firefox will fire a keypress if meta or control is down
927
+ if ((action == 'keypress' && !e.metaKey && !e.ctrlKey) || _modifiersMatch(modifiers, callback.modifiers)) {
928
+
929
+ // when you bind a combination or sequence a second time it
930
+ // should overwrite the first one. if a sequenceName or
931
+ // combination is specified in this call it does just that
932
+ //
933
+ // @todo make deleting its own method?
934
+ var deleteCombo = !sequenceName && callback.combo == combination;
935
+ var deleteSequence = sequenceName && callback.seq == sequenceName && callback.level == level;
936
+ if (deleteCombo || deleteSequence) {
937
+ _callbacks[character].splice(i, 1);
938
+ }
939
+
940
+ matches.push(callback);
941
+ }
942
+ }
943
+
944
+ return matches;
945
+ }
946
+
947
+ /**
948
+ * takes a key event and figures out what the modifiers are
949
+ *
950
+ * @param {Event} e
951
+ * @returns {Array}
952
+ */
953
+ function _eventModifiers(e) {
954
+ var modifiers = [];
955
+
956
+ if (e.shiftKey) {
957
+ modifiers.push('shift');
958
+ }
959
+
960
+ if (e.altKey) {
961
+ modifiers.push('alt');
962
+ }
963
+
964
+ if (e.ctrlKey) {
965
+ modifiers.push('ctrl');
966
+ }
967
+
968
+ if (e.metaKey) {
969
+ modifiers.push('meta');
970
+ }
971
+
972
+ return modifiers;
973
+ }
974
+
975
+ /**
976
+ * prevents default for this event
977
+ *
978
+ * @param {Event} e
979
+ * @returns void
980
+ */
981
+ function _preventDefault(e) {
982
+ if (e.preventDefault) {
983
+ e.preventDefault();
984
+ return;
985
+ }
986
+
987
+ e.returnValue = false;
988
+ }
989
+
990
+ /**
991
+ * stops propogation for this event
992
+ *
993
+ * @param {Event} e
994
+ * @returns void
995
+ */
996
+ function _stopPropagation(e) {
997
+ if (e.stopPropagation) {
998
+ e.stopPropagation();
999
+ return;
1000
+ }
1001
+
1002
+ e.cancelBubble = true;
1003
+ }
1004
+
1005
+ /**
1006
+ * actually calls the callback function
1007
+ *
1008
+ * if your callback function returns false this will use the jquery
1009
+ * convention - prevent default and stop propogation on the event
1010
+ *
1011
+ * @param {Function} callback
1012
+ * @param {Event} e
1013
+ * @returns void
1014
+ */
1015
+ function _fireCallback(callback, e, combo, sequence) {
1016
+
1017
+ // if this event should not happen stop here
1018
+ if (Mousetrap.stopCallback(e, e.target || e.srcElement, combo, sequence)) {
1019
+ return;
1020
+ }
1021
+
1022
+ if (callback(e, combo) === false) {
1023
+ _preventDefault(e);
1024
+ _stopPropagation(e);
1025
+ }
1026
+ }
1027
+
1028
+ /**
1029
+ * handles a character key event
1030
+ *
1031
+ * @param {string} character
1032
+ * @param {Array} modifiers
1033
+ * @param {Event} e
1034
+ * @returns void
1035
+ */
1036
+ function _handleKey(character, modifiers, e) {
1037
+ var callbacks = _getMatches(character, modifiers, e),
1038
+ i,
1039
+ doNotReset = {},
1040
+ maxLevel = 0,
1041
+ processedSequenceCallback = false;
1042
+
1043
+ // Calculate the maxLevel for sequences so we can only execute the longest callback sequence
1044
+ for (i = 0; i < callbacks.length; ++i) {
1045
+ if (callbacks[i].seq) {
1046
+ maxLevel = Math.max(maxLevel, callbacks[i].level);
1047
+ }
1048
+ }
1049
+
1050
+ // loop through matching callbacks for this key event
1051
+ for (i = 0; i < callbacks.length; ++i) {
1052
+
1053
+ // fire for all sequence callbacks
1054
+ // this is because if for example you have multiple sequences
1055
+ // bound such as "g i" and "g t" they both need to fire the
1056
+ // callback for matching g cause otherwise you can only ever
1057
+ // match the first one
1058
+ if (callbacks[i].seq) {
1059
+
1060
+ // only fire callbacks for the maxLevel to prevent
1061
+ // subsequences from also firing
1062
+ //
1063
+ // for example 'a option b' should not cause 'option b' to fire
1064
+ // even though 'option b' is part of the other sequence
1065
+ //
1066
+ // any sequences that do not match here will be discarded
1067
+ // below by the _resetSequences call
1068
+ if (callbacks[i].level != maxLevel) {
1069
+ continue;
1070
+ }
1071
+
1072
+ processedSequenceCallback = true;
1073
+
1074
+ // keep a list of which sequences were matches for later
1075
+ doNotReset[callbacks[i].seq] = 1;
1076
+ _fireCallback(callbacks[i].callback, e, callbacks[i].combo, callbacks[i].seq);
1077
+ continue;
1078
+ }
1079
+
1080
+ // if there were no sequence matches but we are still here
1081
+ // that means this is a regular match so we should fire that
1082
+ if (!processedSequenceCallback) {
1083
+ _fireCallback(callbacks[i].callback, e, callbacks[i].combo);
1084
+ }
1085
+ }
1086
+
1087
+ // if the key you pressed matches the type of sequence without
1088
+ // being a modifier (ie "keyup" or "keypress") then we should
1089
+ // reset all sequences that were not matched by this event
1090
+ //
1091
+ // this is so, for example, if you have the sequence "h a t" and you
1092
+ // type "h e a r t" it does not match. in this case the "e" will
1093
+ // cause the sequence to reset
1094
+ //
1095
+ // modifier keys are ignored because you can have a sequence
1096
+ // that contains modifiers such as "enter ctrl+space" and in most
1097
+ // cases the modifier key will be pressed before the next key
1098
+ //
1099
+ // also if you have a sequence such as "ctrl+b a" then pressing the
1100
+ // "b" key will trigger a "keypress" and a "keydown"
1101
+ //
1102
+ // the "keydown" is expected when there is a modifier, but the
1103
+ // "keypress" ends up matching the _nextExpectedAction since it occurs
1104
+ // after and that causes the sequence to reset
1105
+ //
1106
+ // we ignore keypresses in a sequence that directly follow a keydown
1107
+ // for the same character
1108
+ var ignoreThisKeypress = e.type == 'keypress' && _ignoreNextKeypress;
1109
+ if (e.type == _nextExpectedAction && !_isModifier(character) && !ignoreThisKeypress) {
1110
+ _resetSequences(doNotReset);
1111
+ }
1112
+
1113
+ _ignoreNextKeypress = processedSequenceCallback && e.type == 'keydown';
1114
+ }
1115
+
1116
+ /**
1117
+ * handles a keydown event
1118
+ *
1119
+ * @param {Event} e
1120
+ * @returns void
1121
+ */
1122
+ function _handleKeyEvent(e) {
1123
+
1124
+ // normalize e.which for key events
1125
+ // @see http://stackoverflow.com/questions/4285627/javascript-keycode-vs-charcode-utter-confusion
1126
+ if (typeof e.which !== 'number') {
1127
+ e.which = e.keyCode;
1128
+ }
1129
+
1130
+ var character = _characterFromEvent(e);
1131
+
1132
+ // no character found then stop
1133
+ if (!character) {
1134
+ return;
1135
+ }
1136
+
1137
+ // need to use === for the character check because the character can be 0
1138
+ if (e.type == 'keyup' && _ignoreNextKeyup === character) {
1139
+ _ignoreNextKeyup = false;
1140
+ return;
1141
+ }
1142
+
1143
+ Mousetrap.handleKey(character, _eventModifiers(e), e);
1144
+ }
1145
+
1146
+ /**
1147
+ * determines if the keycode specified is a modifier key or not
1148
+ *
1149
+ * @param {string} key
1150
+ * @returns {boolean}
1151
+ */
1152
+ function _isModifier(key) {
1153
+ return key == 'shift' || key == 'ctrl' || key == 'alt' || key == 'meta';
1154
+ }
1155
+
1156
+ /**
1157
+ * called to set a 1 second timeout on the specified sequence
1158
+ *
1159
+ * this is so after each key press in the sequence you have 1 second
1160
+ * to press the next key before you have to start over
1161
+ *
1162
+ * @returns void
1163
+ */
1164
+ function _resetSequenceTimer() {
1165
+ clearTimeout(_resetTimer);
1166
+ _resetTimer = setTimeout(_resetSequences, 1000);
1167
+ }
1168
+
1169
+ /**
1170
+ * reverses the map lookup so that we can look for specific keys
1171
+ * to see what can and can't use keypress
1172
+ *
1173
+ * @return {Object}
1174
+ */
1175
+ function _getReverseMap() {
1176
+ if (!_REVERSE_MAP) {
1177
+ _REVERSE_MAP = {};
1178
+ for (var key in _MAP) {
1179
+
1180
+ // pull out the numeric keypad from here cause keypress should
1181
+ // be able to detect the keys from the character
1182
+ if (key > 95 && key < 112) {
1183
+ continue;
1184
+ }
1185
+
1186
+ if (_MAP.hasOwnProperty(key)) {
1187
+ _REVERSE_MAP[_MAP[key]] = key;
1188
+ }
1189
+ }
1190
+ }
1191
+ return _REVERSE_MAP;
1192
+ }
1193
+
1194
+ /**
1195
+ * picks the best action based on the key combination
1196
+ *
1197
+ * @param {string} key - character for key
1198
+ * @param {Array} modifiers
1199
+ * @param {string=} action passed in
1200
+ */
1201
+ function _pickBestAction(key, modifiers, action) {
1202
+
1203
+ // if no action was picked in we should try to pick the one
1204
+ // that we think would work best for this key
1205
+ if (!action) {
1206
+ action = _getReverseMap()[key] ? 'keydown' : 'keypress';
1207
+ }
1208
+
1209
+ // modifier keys don't work as expected with keypress,
1210
+ // switch to keydown
1211
+ if (action == 'keypress' && modifiers.length) {
1212
+ action = 'keydown';
1213
+ }
1214
+
1215
+ return action;
1216
+ }
1217
+
1218
+ /**
1219
+ * binds a key sequence to an event
1220
+ *
1221
+ * @param {string} combo - combo specified in bind call
1222
+ * @param {Array} keys
1223
+ * @param {Function} callback
1224
+ * @param {string=} action
1225
+ * @returns void
1226
+ */
1227
+ function _bindSequence(combo, keys, callback, action) {
1228
+
1229
+ // start off by adding a sequence level record for this combination
1230
+ // and setting the level to 0
1231
+ _sequenceLevels[combo] = 0;
1232
+
1233
+ /**
1234
+ * callback to increase the sequence level for this sequence and reset
1235
+ * all other sequences that were active
1236
+ *
1237
+ * @param {string} nextAction
1238
+ * @returns {Function}
1239
+ */
1240
+ function _increaseSequence(nextAction) {
1241
+ return function() {
1242
+ _nextExpectedAction = nextAction;
1243
+ ++_sequenceLevels[combo];
1244
+ _resetSequenceTimer();
1245
+ };
1246
+ }
1247
+
1248
+ /**
1249
+ * wraps the specified callback inside of another function in order
1250
+ * to reset all sequence counters as soon as this sequence is done
1251
+ *
1252
+ * @param {Event} e
1253
+ * @returns void
1254
+ */
1255
+ function _callbackAndReset(e) {
1256
+ _fireCallback(callback, e, combo);
1257
+
1258
+ // we should ignore the next key up if the action is key down
1259
+ // or keypress. this is so if you finish a sequence and
1260
+ // release the key the final key will not trigger a keyup
1261
+ if (action !== 'keyup') {
1262
+ _ignoreNextKeyup = _characterFromEvent(e);
1263
+ }
1264
+
1265
+ // weird race condition if a sequence ends with the key
1266
+ // another sequence begins with
1267
+ setTimeout(_resetSequences, 10);
1268
+ }
1269
+
1270
+ // loop through keys one at a time and bind the appropriate callback
1271
+ // function. for any key leading up to the final one it should
1272
+ // increase the sequence. after the final, it should reset all sequences
1273
+ //
1274
+ // if an action is specified in the original bind call then that will
1275
+ // be used throughout. otherwise we will pass the action that the
1276
+ // next key in the sequence should match. this allows a sequence
1277
+ // to mix and match keypress and keydown events depending on which
1278
+ // ones are better suited to the key provided
1279
+ for (var i = 0; i < keys.length; ++i) {
1280
+ var isFinal = i + 1 === keys.length;
1281
+ var wrappedCallback = isFinal ? _callbackAndReset : _increaseSequence(action || _getKeyInfo(keys[i + 1]).action);
1282
+ _bindSingle(keys[i], wrappedCallback, action, combo, i);
1283
+ }
1284
+ }
1285
+
1286
+ /**
1287
+ * Converts from a string key combination to an array
1288
+ *
1289
+ * @param {string} combination like "command+shift+l"
1290
+ * @return {Array}
1291
+ */
1292
+ function _keysFromString(combination) {
1293
+ if (combination === '+') {
1294
+ return ['+'];
1295
+ }
1296
+
1297
+ return combination.split('+');
1298
+ }
1299
+
1300
+ /**
1301
+ * Gets info for a specific key combination
1302
+ *
1303
+ * @param {string} combination key combination ("command+s" or "a" or "*")
1304
+ * @param {string=} action
1305
+ * @returns {Object}
1306
+ */
1307
+ function _getKeyInfo(combination, action) {
1308
+ var keys,
1309
+ key,
1310
+ i,
1311
+ modifiers = [];
1312
+
1313
+ // take the keys from this pattern and figure out what the actual
1314
+ // pattern is all about
1315
+ keys = _keysFromString(combination);
1316
+
1317
+ for (i = 0; i < keys.length; ++i) {
1318
+ key = keys[i];
1319
+
1320
+ // normalize key names
1321
+ if (_SPECIAL_ALIASES[key]) {
1322
+ key = _SPECIAL_ALIASES[key];
1323
+ }
1324
+
1325
+ // if this is not a keypress event then we should
1326
+ // be smart about using shift keys
1327
+ // this will only work for US keyboards however
1328
+ if (action && action != 'keypress' && _SHIFT_MAP[key]) {
1329
+ key = _SHIFT_MAP[key];
1330
+ modifiers.push('shift');
1331
+ }
1332
+
1333
+ // if this key is a modifier then add it to the list of modifiers
1334
+ if (_isModifier(key)) {
1335
+ modifiers.push(key);
1336
+ }
1337
+ }
1338
+
1339
+ // depending on what the key combination is
1340
+ // we will try to pick the best event for it
1341
+ action = _pickBestAction(key, modifiers, action);
1342
+
1343
+ return {
1344
+ key: key,
1345
+ modifiers: modifiers,
1346
+ action: action
1347
+ };
1348
+ }
1349
+
1350
+ /**
1351
+ * binds a single keyboard combination
1352
+ *
1353
+ * @param {string} combination
1354
+ * @param {Function} callback
1355
+ * @param {string=} action
1356
+ * @param {string=} sequenceName - name of sequence if part of sequence
1357
+ * @param {number=} level - what part of the sequence the command is
1358
+ * @returns void
1359
+ */
1360
+ function _bindSingle(combination, callback, action, sequenceName, level) {
1361
+
1362
+ // store a direct mapped reference for use with Mousetrap.trigger
1363
+ _directMap[combination + ':' + action] = callback;
1364
+
1365
+ // make sure multiple spaces in a row become a single space
1366
+ combination = combination.replace(/\s+/g, ' ');
1367
+
1368
+ var sequence = combination.split(' '),
1369
+ info;
1370
+
1371
+ // if this pattern is a sequence of keys then run through this method
1372
+ // to reprocess each pattern one key at a time
1373
+ if (sequence.length > 1) {
1374
+ _bindSequence(combination, sequence, callback, action);
1375
+ return;
1376
+ }
1377
+
1378
+ info = _getKeyInfo(combination, action);
1379
+
1380
+ // make sure to initialize array if this is the first time
1381
+ // a callback is added for this key
1382
+ _callbacks[info.key] = _callbacks[info.key] || [];
1383
+
1384
+ // remove an existing match if there is one
1385
+ _getMatches(info.key, info.modifiers, {type: info.action}, sequenceName, combination, level);
1386
+
1387
+ // add this call back to the array
1388
+ // if it is a sequence put it at the beginning
1389
+ // if not put it at the end
1390
+ //
1391
+ // this is important because the way these are processed expects
1392
+ // the sequence ones to come first
1393
+ _callbacks[info.key][sequenceName ? 'unshift' : 'push']({
1394
+ callback: callback,
1395
+ modifiers: info.modifiers,
1396
+ action: info.action,
1397
+ seq: sequenceName,
1398
+ level: level,
1399
+ combo: combination
1400
+ });
1401
+ }
1402
+
1403
+ /**
1404
+ * binds multiple combinations to the same callback
1405
+ *
1406
+ * @param {Array} combinations
1407
+ * @param {Function} callback
1408
+ * @param {string|undefined} action
1409
+ * @returns void
1410
+ */
1411
+ function _bindMultiple(combinations, callback, action) {
1412
+ for (var i = 0; i < combinations.length; ++i) {
1413
+ _bindSingle(combinations[i], callback, action);
1414
+ }
1415
+ }
1416
+
1417
+ // start!
1418
+ _addEvent(document, 'keypress', _handleKeyEvent);
1419
+ _addEvent(document, 'keydown', _handleKeyEvent);
1420
+ _addEvent(document, 'keyup', _handleKeyEvent);
1421
+
1422
+ var Mousetrap = {
1423
+
1424
+ /**
1425
+ * binds an event to mousetrap
1426
+ *
1427
+ * can be a single key, a combination of keys separated with +,
1428
+ * an array of keys, or a sequence of keys separated by spaces
1429
+ *
1430
+ * be sure to list the modifier keys first to make sure that the
1431
+ * correct key ends up getting bound (the last key in the pattern)
1432
+ *
1433
+ * @param {string|Array} keys
1434
+ * @param {Function} callback
1435
+ * @param {string=} action - 'keypress', 'keydown', or 'keyup'
1436
+ * @returns void
1437
+ */
1438
+ bind: function(keys, callback, action) {
1439
+ keys = keys instanceof Array ? keys : [keys];
1440
+ _bindMultiple(keys, callback, action);
1441
+ return this;
1442
+ },
1443
+
1444
+ /**
1445
+ * unbinds an event to mousetrap
1446
+ *
1447
+ * the unbinding sets the callback function of the specified key combo
1448
+ * to an empty function and deletes the corresponding key in the
1449
+ * _directMap dict.
1450
+ *
1451
+ * TODO: actually remove this from the _callbacks dictionary instead
1452
+ * of binding an empty function
1453
+ *
1454
+ * the keycombo+action has to be exactly the same as
1455
+ * it was defined in the bind method
1456
+ *
1457
+ * @param {string|Array} keys
1458
+ * @param {string} action
1459
+ * @returns void
1460
+ */
1461
+ unbind: function(keys, action) {
1462
+ return Mousetrap.bind(keys, function() {}, action);
1463
+ },
1464
+
1465
+ /**
1466
+ * triggers an event that has already been bound
1467
+ *
1468
+ * @param {string} keys
1469
+ * @param {string=} action
1470
+ * @returns void
1471
+ */
1472
+ trigger: function(keys, action) {
1473
+ if (_directMap[keys + ':' + action]) {
1474
+ _directMap[keys + ':' + action]({}, keys);
1475
+ }
1476
+ return this;
1477
+ },
1478
+
1479
+ /**
1480
+ * resets the library back to its initial state. this is useful
1481
+ * if you want to clear out the current keyboard shortcuts and bind
1482
+ * new ones - for example if you switch to another page
1483
+ *
1484
+ * @returns void
1485
+ */
1486
+ reset: function() {
1487
+ _callbacks = {};
1488
+ _directMap = {};
1489
+ return this;
1490
+ },
1491
+
1492
+ /**
1493
+ * should we stop this event before firing off callbacks
1494
+ *
1495
+ * @param {Event} e
1496
+ * @param {Element} element
1497
+ * @return {boolean}
1498
+ */
1499
+ stopCallback: function(e, element) {
1500
+
1501
+ // if the element has the class "mousetrap" then no need to stop
1502
+ if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
1503
+ return false;
1504
+ }
1505
+
1506
+ // stop for input, select, and textarea
1507
+ return element.tagName == 'INPUT' || element.tagName == 'SELECT' || element.tagName == 'TEXTAREA' || element.isContentEditable;
1508
+ },
1509
+
1510
+ /**
1511
+ * exposes _handleKey publicly so it can be overwritten by extensions
1512
+ */
1513
+ handleKey: _handleKey
1514
+ };
1515
+
1516
+ // expose mousetrap to the global object
1517
+ window.Mousetrap = Mousetrap;
1518
+
1519
+ // expose mousetrap as an AMD module
1520
+ if (typeof define === 'function' && define.amd) {
1521
+ define(Mousetrap);
1522
+ }
1523
+ }) (window, document);