geoblacklight_admin 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (346) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +18 -0
  3. data/.gitignore +75 -0
  4. data/.standard.yml +4 -0
  5. data/Gemfile +59 -0
  6. data/LICENSE +21 -0
  7. data/README.md +84 -0
  8. data/Rakefile +72 -0
  9. data/app/assets/images/.keep +0 -0
  10. data/app/assets/images/aerial_photo.jpg +0 -0
  11. data/app/assets/images/archive-solid.svg +1 -0
  12. data/app/assets/images/bookmark-regular.svg +1 -0
  13. data/app/assets/images/bookmark-solid.svg +1 -0
  14. data/app/assets/images/btaa-logo.jpg +0 -0
  15. data/app/assets/images/cloud-download-alt-solid.svg +1 -0
  16. data/app/assets/images/file-import-solid.svg +1 -0
  17. data/app/assets/images/trash-alt-solid.svg +1 -0
  18. data/app/assets/javascripts/geoblacklight_admin/chosen.js +4 -0
  19. data/app/assets/javascripts/geoblacklight_admin/date_range_picker.js +173 -0
  20. data/app/assets/javascripts/geoblacklight_admin/datepicker.js +22 -0
  21. data/app/assets/javascripts/geoblacklight_admin/elements.js +38 -0
  22. data/app/assets/javascripts/geoblacklight_admin/exports.js +3 -0
  23. data/app/assets/javascripts/geoblacklight_admin/fields.js +11 -0
  24. data/app/assets/javascripts/geoblacklight_admin/geoblacklight_admin.js +2 -0
  25. data/app/assets/javascripts/geoblacklight_admin/inputmask.js +4 -0
  26. data/app/assets/javascripts/geoblacklight_admin/locale.js +5 -0
  27. data/app/assets/javascripts/geoblacklight_admin/sortable.js +2544 -0
  28. data/app/assets/javascripts/geoblacklight_admin/tooltips.js +3 -0
  29. data/app/assets/javascripts/geoblacklight_admin/truncate.js +4 -0
  30. data/app/assets/javascripts/geoblacklight_admin.js +40 -0
  31. data/app/assets/stylesheets/geoblacklight_admin/_core.scss +47 -0
  32. data/app/assets/stylesheets/geoblacklight_admin/modules/_autocomplete.scss +32 -0
  33. data/app/assets/stylesheets/geoblacklight_admin/modules/_blazer.scss +3 -0
  34. data/app/assets/stylesheets/geoblacklight_admin/modules/_code.scss +3 -0
  35. data/app/assets/stylesheets/geoblacklight_admin/modules/_datepicker.scss +477 -0
  36. data/app/assets/stylesheets/geoblacklight_admin/modules/_elements.scss +3 -0
  37. data/app/assets/stylesheets/geoblacklight_admin/modules/_forms.scss +28 -0
  38. data/app/assets/stylesheets/geoblacklight_admin/modules/_icons.scss +15 -0
  39. data/app/assets/stylesheets/geoblacklight_admin/modules/_nav.scss +9 -0
  40. data/app/assets/stylesheets/geoblacklight_admin/modules/_tables.scss +4 -0
  41. data/app/assets/stylesheets/geoblacklight_admin/modules/_toasts.scss +7 -0
  42. data/app/assets/stylesheets/geoblacklight_admin/modules/_visibility.scss +3 -0
  43. data/app/assets/stylesheets/geoblacklight_admin/settings/_variables.scss +1 -0
  44. data/app/channels/export_channel.rb +12 -0
  45. data/app/controllers/admin/admin_controller.rb +16 -0
  46. data/app/controllers/admin/advanced_search_controller.rb +365 -0
  47. data/app/controllers/admin/api_controller.rb +386 -0
  48. data/app/controllers/admin/bookmarks_controller.rb +68 -0
  49. data/app/controllers/admin/bulk_actions_controller.rb +94 -0
  50. data/app/controllers/admin/concerns/.keep +0 -0
  51. data/app/controllers/admin/concerns/upload_util.rb +8 -0
  52. data/app/controllers/admin/document_accesses_controller.rb +125 -0
  53. data/app/controllers/admin/document_assets_controller.rb +198 -0
  54. data/app/controllers/admin/document_downloads_controller.rb +127 -0
  55. data/app/controllers/admin/documents_controller.rb +219 -0
  56. data/app/controllers/admin/elements_controller.rb +81 -0
  57. data/app/controllers/admin/form_elements_controller.rb +81 -0
  58. data/app/controllers/admin/ids_controller.rb +379 -0
  59. data/app/controllers/admin/import_documents_controller.rb +18 -0
  60. data/app/controllers/admin/imports_controller.rb +105 -0
  61. data/app/controllers/admin/mappings_controller.rb +95 -0
  62. data/app/controllers/admin/notifications_controller.rb +51 -0
  63. data/app/controllers/admin/search_controller.rb +10 -0
  64. data/app/controllers/admin/users_controller.rb +10 -0
  65. data/app/helpers/bulk_actions_helper.rb +9 -0
  66. data/app/helpers/document_helper.rb +60 -0
  67. data/app/helpers/form_input_helper.rb +73 -0
  68. data/app/helpers/geoblacklight_admin_helper.rb +118 -0
  69. data/app/helpers/import_documents_helper.rb +5 -0
  70. data/app/helpers/mappings_helper.rb +27 -0
  71. data/app/indexers/document_indexer.rb +34 -0
  72. data/app/jobs/bulk_action_revert_document_job.rb +49 -0
  73. data/app/jobs/bulk_action_revert_job.rb +25 -0
  74. data/app/jobs/bulk_action_run_document_job.rb +42 -0
  75. data/app/jobs/bulk_action_run_job.rb +27 -0
  76. data/app/jobs/export_job.rb +72 -0
  77. data/app/jobs/export_json_job.rb +120 -0
  78. data/app/jobs/import_document_job.rb +25 -0
  79. data/app/jobs/import_run_job.rb +33 -0
  80. data/app/models/active_storage_attachment.rb +5 -0
  81. data/app/models/active_storage_blob.rb +5 -0
  82. data/app/models/api_search_builder.rb +12 -0
  83. data/app/models/application_record.rb +6 -0
  84. data/app/models/blacklight_api.rb +93 -0
  85. data/app/models/blacklight_api_facets.rb +15 -0
  86. data/app/models/blacklight_api_ids.rb +103 -0
  87. data/app/models/bookmark.rb +17 -0
  88. data/app/models/bulk_action.rb +88 -0
  89. data/app/models/bulk_action_document.rb +22 -0
  90. data/app/models/bulk_action_document_state_machine.rb +16 -0
  91. data/app/models/bulk_action_document_transition.rb +19 -0
  92. data/app/models/bulk_action_state_machine.rb +16 -0
  93. data/app/models/bulk_action_transition.rb +26 -0
  94. data/app/models/concerns/.keep +0 -0
  95. data/app/models/document/bbox_validator.rb +70 -0
  96. data/app/models/document/controlled_lists.rb +104 -0
  97. data/app/models/document/date_range_validator.rb +63 -0
  98. data/app/models/document/geom_validator.rb +117 -0
  99. data/app/models/document/reference.rb +107 -0
  100. data/app/models/document.rb +264 -0
  101. data/app/models/document_access.rb +35 -0
  102. data/app/models/document_download.rb +35 -0
  103. data/app/models/document_state_machine.rb +14 -0
  104. data/app/models/document_transition.rb +26 -0
  105. data/app/models/element.rb +90 -0
  106. data/app/models/form_control.rb +2 -0
  107. data/app/models/form_element.rb +19 -0
  108. data/app/models/form_feature.rb +2 -0
  109. data/app/models/form_group.rb +2 -0
  110. data/app/models/form_header.rb +2 -0
  111. data/app/models/geoblacklight_admin/field_mappings_btaa_aardvark.rb +379 -0
  112. data/app/models/geoblacklight_admin/field_mappings_gblv1.rb +344 -0
  113. data/app/models/geoblacklight_admin/iso_language_codes.rb +515 -0
  114. data/app/models/geoblacklight_admin/schema.rb +194 -0
  115. data/app/models/geoblacklight_admin.rb +3 -0
  116. data/app/models/import/csv_duplicates_validator.rb +36 -0
  117. data/app/models/import/csv_header_validator.rb +24 -0
  118. data/app/models/import.rb +186 -0
  119. data/app/models/import_btaa.rb +91 -0
  120. data/app/models/import_btaa_aardvark.rb +109 -0
  121. data/app/models/import_document.rb +34 -0
  122. data/app/models/import_document_state_machine.rb +12 -0
  123. data/app/models/import_document_transition.rb +19 -0
  124. data/app/models/import_gblv1.rb +92 -0
  125. data/app/models/import_state_machine.rb +16 -0
  126. data/app/models/import_transition.rb +26 -0
  127. data/app/models/mapping.rb +10 -0
  128. data/app/models/notification.rb +9 -0
  129. data/app/models/user.rb +26 -0
  130. data/app/notifications/export_notification.rb +30 -0
  131. data/app/services/export_csv_document_access_links_service.rb +47 -0
  132. data/app/services/export_csv_document_downloads_service.rb +47 -0
  133. data/app/services/export_csv_service.rb +44 -0
  134. data/app/services/export_json_service.rb +35 -0
  135. data/app/services/zip_file_generator.rb +56 -0
  136. data/app/views/admin/advanced_search/facets.json.jbuilder +80 -0
  137. data/app/views/admin/advanced_search/index.json.jbuilder +90 -0
  138. data/app/views/admin/api/_field.json.jbuilder +10 -0
  139. data/app/views/admin/api/fetch.json.jbuilder +86 -0
  140. data/app/views/admin/api/index.json.jbuilder +90 -0
  141. data/app/views/admin/bookmarks/_bookmark.json.jbuilder +4 -0
  142. data/app/views/admin/bookmarks/create.js.erb +7 -0
  143. data/app/views/admin/bookmarks/destroy.js.erb +7 -0
  144. data/app/views/admin/bookmarks/index.html.erb +21 -0
  145. data/app/views/admin/bookmarks/index.json.jbuilder +3 -0
  146. data/app/views/admin/bulk_actions/_bulk_action.json.jbuilder +4 -0
  147. data/app/views/admin/bulk_actions/_form.html.erb +17 -0
  148. data/app/views/admin/bulk_actions/edit.html.erb +6 -0
  149. data/app/views/admin/bulk_actions/index.html.erb +54 -0
  150. data/app/views/admin/bulk_actions/index.json.jbuilder +3 -0
  151. data/app/views/admin/bulk_actions/new.html.erb +5 -0
  152. data/app/views/admin/bulk_actions/show.html.erb +100 -0
  153. data/app/views/admin/bulk_actions/show.json.jbuilder +3 -0
  154. data/app/views/admin/devise/confirmations/new.html.erb +16 -0
  155. data/app/views/admin/devise/invitations/edit.html.erb +15 -0
  156. data/app/views/admin/devise/invitations/new.html.erb +15 -0
  157. data/app/views/admin/devise/mailer/invitation_instructions.html.erb +11 -0
  158. data/app/views/admin/devise/mailer/invitation_instructions.text.erb +11 -0
  159. data/app/views/admin/devise/passwords/edit.html.erb +26 -0
  160. data/app/views/admin/devise/passwords/new.html.erb +16 -0
  161. data/app/views/admin/devise/registrations/edit.html.erb +35 -0
  162. data/app/views/admin/devise/registrations/new.html.erb +30 -0
  163. data/app/views/admin/devise/sessions/new.html.erb +28 -0
  164. data/app/views/admin/devise/shared/_links.html.erb +27 -0
  165. data/app/views/admin/devise/unlocks/new.html.erb +16 -0
  166. data/app/views/admin/document_accesses/_document_access.json.jbuilder +4 -0
  167. data/app/views/admin/document_accesses/_form.html.erb +17 -0
  168. data/app/views/admin/document_accesses/destroy_all.html.erb +70 -0
  169. data/app/views/admin/document_accesses/edit.html.erb +5 -0
  170. data/app/views/admin/document_accesses/import.html.erb +70 -0
  171. data/app/views/admin/document_accesses/index.html.erb +74 -0
  172. data/app/views/admin/document_accesses/index.json.jbuilder +3 -0
  173. data/app/views/admin/document_accesses/new.html.erb +6 -0
  174. data/app/views/admin/document_accesses/show.html.erb +19 -0
  175. data/app/views/admin/document_accesses/show.json.jbuilder +3 -0
  176. data/app/views/admin/document_assets/display_attach_form.html.erb +44 -0
  177. data/app/views/admin/document_assets/index.html.erb +65 -0
  178. data/app/views/admin/document_downloads/_document_download.json.jbuilder +4 -0
  179. data/app/views/admin/document_downloads/_form.html.erb +15 -0
  180. data/app/views/admin/document_downloads/destroy_all.html.erb +70 -0
  181. data/app/views/admin/document_downloads/edit.html.erb +6 -0
  182. data/app/views/admin/document_downloads/import.html.erb +74 -0
  183. data/app/views/admin/document_downloads/index.html.erb +70 -0
  184. data/app/views/admin/document_downloads/index.json.jbuilder +3 -0
  185. data/app/views/admin/document_downloads/new.html.erb +5 -0
  186. data/app/views/admin/document_downloads/show.html.erb +24 -0
  187. data/app/views/admin/document_downloads/show.json.jbuilder +3 -0
  188. data/app/views/admin/documents/_document.html.erb +29 -0
  189. data/app/views/admin/documents/_document_bookmark.html.erb +17 -0
  190. data/app/views/admin/documents/_document_fields.html.erb +24 -0
  191. data/app/views/admin/documents/_form.html.erb +49 -0
  192. data/app/views/admin/documents/_form_control.html.erb +86 -0
  193. data/app/views/admin/documents/_form_group.html.erb +1 -0
  194. data/app/views/admin/documents/_form_header.html.erb +1 -0
  195. data/app/views/admin/documents/_form_nav.html.erb +21 -0
  196. data/app/views/admin/documents/_json_aardvark.jbuilder +47 -0
  197. data/app/views/admin/documents/_json_btaa_aardvark.jbuilder +16 -0
  198. data/app/views/admin/documents/_json_gbl_v1.jbuilder +32 -0
  199. data/app/views/admin/documents/_result_facets.html.erb +35 -0
  200. data/app/views/admin/documents/_result_pagination.html.erb +21 -0
  201. data/app/views/admin/documents/_result_selected_options.html.erb +54 -0
  202. data/app/views/admin/documents/_result_selection_options.html.erb +10 -0
  203. data/app/views/admin/documents/_result_toggle.html.erb +13 -0
  204. data/app/views/admin/documents/edit.html.erb +7 -0
  205. data/app/views/admin/documents/features/_assets.html.erb +3 -0
  206. data/app/views/admin/documents/features/_institutional_access_links.html.erb +3 -0
  207. data/app/views/admin/documents/features/_multiple_download_links.html.erb +27 -0
  208. data/app/views/admin/documents/fetch.json_aardvark.jbuilder +9 -0
  209. data/app/views/admin/documents/fetch.json_btaa_aardvark.jbuilder +9 -0
  210. data/app/views/admin/documents/fetch.json_gbl_v1.jbuilder +9 -0
  211. data/app/views/admin/documents/index.html.erb +49 -0
  212. data/app/views/admin/documents/index.json_aardvark.jbuilder +9 -0
  213. data/app/views/admin/documents/index.json_btaa_aardvark.jbuilder +9 -0
  214. data/app/views/admin/documents/index.json_gbl_v1.jbuilder +9 -0
  215. data/app/views/admin/documents/show.json_aardvark.jbuilder +3 -0
  216. data/app/views/admin/documents/show.json_btaa_aardvark.jbuilder +3 -0
  217. data/app/views/admin/documents/show.json_gbl_v1.jbuilder +3 -0
  218. data/app/views/admin/documents/versions.html.erb +110 -0
  219. data/app/views/admin/elements/_element.json.jbuilder +2 -0
  220. data/app/views/admin/elements/_form.html.erb +45 -0
  221. data/app/views/admin/elements/edit.html.erb +6 -0
  222. data/app/views/admin/elements/index.html.erb +68 -0
  223. data/app/views/admin/elements/index.json.jbuilder +1 -0
  224. data/app/views/admin/elements/new.html.erb +5 -0
  225. data/app/views/admin/elements/show.html.erb +121 -0
  226. data/app/views/admin/elements/show.json.jbuilder +1 -0
  227. data/app/views/admin/form_elements/_form.html.erb +46 -0
  228. data/app/views/admin/form_elements/_form_element.json.jbuilder +2 -0
  229. data/app/views/admin/form_elements/edit.html.erb +6 -0
  230. data/app/views/admin/form_elements/index.html.erb +49 -0
  231. data/app/views/admin/form_elements/index.json.jbuilder +1 -0
  232. data/app/views/admin/form_elements/new.html.erb +5 -0
  233. data/app/views/admin/form_elements/show.html.erb +19 -0
  234. data/app/views/admin/form_elements/show.json.jbuilder +1 -0
  235. data/app/views/admin/ids/fetch.json.jbuilder +86 -0
  236. data/app/views/admin/ids/index.json.jbuilder +41 -0
  237. data/app/views/admin/import_documents/show.html.haml +20 -0
  238. data/app/views/admin/imports/_form.html.erb +27 -0
  239. data/app/views/admin/imports/_import.json.jbuilder +4 -0
  240. data/app/views/admin/imports/_show_failed_tab.html.erb +37 -0
  241. data/app/views/admin/imports/_show_success_tab.html.erb +31 -0
  242. data/app/views/admin/imports/edit.html.erb +8 -0
  243. data/app/views/admin/imports/index.html.erb +56 -0
  244. data/app/views/admin/imports/index.json.jbuilder +3 -0
  245. data/app/views/admin/imports/new.html.erb +7 -0
  246. data/app/views/admin/imports/show.html.erb +123 -0
  247. data/app/views/admin/imports/show.json.jbuilder +3 -0
  248. data/app/views/admin/layouts/application.html.erb +30 -0
  249. data/app/views/admin/layouts/blazer/application.html.erb +23 -0
  250. data/app/views/admin/layouts/mailer.html.erb +13 -0
  251. data/app/views/admin/layouts/mailer.text.erb +1 -0
  252. data/app/views/admin/mappings/_form.html.erb +17 -0
  253. data/app/views/admin/mappings/_mapping.json.jbuilder +4 -0
  254. data/app/views/admin/mappings/edit.html.erb +6 -0
  255. data/app/views/admin/mappings/index.html.erb +37 -0
  256. data/app/views/admin/mappings/index.json.jbuilder +3 -0
  257. data/app/views/admin/mappings/new.html.erb +5 -0
  258. data/app/views/admin/mappings/show.html.erb +27 -0
  259. data/app/views/admin/mappings/show.json.jbuilder +3 -0
  260. data/app/views/admin/notifications/_notification.html.haml +31 -0
  261. data/app/views/admin/notifications/index.html.erb +54 -0
  262. data/app/views/admin/notifications/update.js.erb +7 -0
  263. data/app/views/admin/search/index.html.erb +38 -0
  264. data/app/views/admin/shared/_flash_messages.html.erb +5 -0
  265. data/app/views/admin/shared/_footer.html.erb +56 -0
  266. data/app/views/admin/shared/_js_behaviors.html.erb +147 -0
  267. data/app/views/admin/shared/_navbar.html.erb +78 -0
  268. data/app/views/admin/shared/_navbar_blazer.html.erb +42 -0
  269. data/app/views/admin/shared/_schema_timestamp.html.erb +16 -0
  270. data/app/views/admin/shared/_toast.html.erb +13 -0
  271. data/app/views/admin/users/index.html.erb +21 -0
  272. data/config/authorities/formats.yml +25 -0
  273. data/config/authorities/languages.yml +50 -0
  274. data/config/authorities/resource_type.yml +82 -0
  275. data/config/blazer.yml +79 -0
  276. data/config/gbl_v1_schema.json +118 -0
  277. data/config/geomg_aardvark_schema.json +211 -0
  278. data/config/geomg_schema.json +123 -0
  279. data/config/locales/devise.en.yml +65 -0
  280. data/config/locales/devise_invitable.en.yml +31 -0
  281. data/config/locales/documents.en.yml +189 -0
  282. data/config/locales/en.yml +51 -0
  283. data/config/locales/geoblacklight.en.yml +178 -0
  284. data/config/locales/simple_form.en.yml +31 -0
  285. data/config/routes.rb +162 -0
  286. data/db/migrate/20230316183001_add_geoblacklight_admin.rb +399 -0
  287. data/db/seeds.rb +26 -0
  288. data/db/seeds_elements.csv +56 -0
  289. data/db/seeds_elements.numbers +0 -0
  290. data/db/seeds_form_elements.csv +71 -0
  291. data/db/seeds_form_elements.numbers +0 -0
  292. data/db/structure.sql +816 -0
  293. data/geoblacklight_admin.gemspec +67 -0
  294. data/lib/generators/geoblacklight_admin/config_generator.rb +336 -0
  295. data/lib/generators/geoblacklight_admin/example_docs_generator.rb +18 -0
  296. data/lib/generators/geoblacklight_admin/helpers_generator.rb +18 -0
  297. data/lib/generators/geoblacklight_admin/install_generator.rb +60 -0
  298. data/lib/generators/geoblacklight_admin/jobs_generator.rb +21 -0
  299. data/lib/generators/geoblacklight_admin/models_generator.rb +23 -0
  300. data/lib/generators/geoblacklight_admin/templates/.env.development.example +28 -0
  301. data/lib/generators/geoblacklight_admin/templates/.solr_wrapper.yml +7 -0
  302. data/lib/generators/geoblacklight_admin/templates/_user_util_links.html.erb +30 -0
  303. data/lib/generators/geoblacklight_admin/templates/config/database.yml +18 -0
  304. data/lib/generators/geoblacklight_admin/templates/config/geomg_aardvark_schema.json +211 -0
  305. data/lib/generators/geoblacklight_admin/templates/config/initializers/geoblacklight_admin.rb +25 -0
  306. data/lib/generators/geoblacklight_admin/templates/config/initializers/kithe.rb +31 -0
  307. data/lib/generators/geoblacklight_admin/templates/config/initializers/pagy.rb +158 -0
  308. data/lib/generators/geoblacklight_admin/templates/config/initializers/statesman.rb +7 -0
  309. data/lib/generators/geoblacklight_admin/templates/javascript/channels/consumer.js +6 -0
  310. data/lib/generators/geoblacklight_admin/templates/javascript/channels/export_channel.js +30 -0
  311. data/lib/generators/geoblacklight_admin/templates/javascript/channels/index.js +5 -0
  312. data/lib/generators/geoblacklight_admin/templates/javascript/controllers/application_controller.js +17 -0
  313. data/lib/generators/geoblacklight_admin/templates/javascript/controllers/document_controller.js +26 -0
  314. data/lib/generators/geoblacklight_admin/templates/javascript/controllers/index.js +10 -0
  315. data/lib/generators/geoblacklight_admin/templates/javascript/controllers/results_controller.js +310 -0
  316. data/lib/generators/geoblacklight_admin/templates/javascript/packs/application.js +23 -0
  317. data/lib/generators/geoblacklight_admin/templates/javascript/src/js/admin/qa_autocomplete.js +1134 -0
  318. data/lib/generators/geoblacklight_admin/templates/package.json +32 -0
  319. data/lib/generators/geoblacklight_admin/templates/solr/README-geoblacklight-solr-development.txt +3 -0
  320. data/lib/generators/geoblacklight_admin/templates/solr/conf/_rest_managed.json +3 -0
  321. data/lib/generators/geoblacklight_admin/templates/solr/conf/admin-extra.html +31 -0
  322. data/lib/generators/geoblacklight_admin/templates/solr/conf/core.properties +5 -0
  323. data/lib/generators/geoblacklight_admin/templates/solr/conf/elevate.xml +36 -0
  324. data/lib/generators/geoblacklight_admin/templates/solr/conf/mapping-ISOLatin1Accent.txt +246 -0
  325. data/lib/generators/geoblacklight_admin/templates/solr/conf/protwords.txt +21 -0
  326. data/lib/generators/geoblacklight_admin/templates/solr/conf/schema.xml +229 -0
  327. data/lib/generators/geoblacklight_admin/templates/solr/conf/scripts.conf +24 -0
  328. data/lib/generators/geoblacklight_admin/templates/solr/conf/solrconfig.xml +266 -0
  329. data/lib/generators/geoblacklight_admin/templates/solr/conf/spellings.txt +2 -0
  330. data/lib/generators/geoblacklight_admin/templates/solr/conf/stopwords.txt +58 -0
  331. data/lib/generators/geoblacklight_admin/templates/solr/conf/stopwords_en.txt +58 -0
  332. data/lib/generators/geoblacklight_admin/templates/solr/conf/synonyms.txt +43 -0
  333. data/lib/generators/geoblacklight_admin/templates/solr/conf/xslt/example.xsl +132 -0
  334. data/lib/generators/geoblacklight_admin/templates/solr/conf/xslt/example_atom.xsl +67 -0
  335. data/lib/generators/geoblacklight_admin/templates/solr/conf/xslt/example_rss.xsl +66 -0
  336. data/lib/generators/geoblacklight_admin/templates/solr/conf/xslt/luke.xsl +337 -0
  337. data/lib/generators/geoblacklight_admin/templates/solr/snapshots/.keep +0 -0
  338. data/lib/generators/geoblacklight_admin/views_generator.rb +19 -0
  339. data/lib/geoblacklight_admin/engine.rb +20 -0
  340. data/lib/geoblacklight_admin/version.rb +5 -0
  341. data/lib/geoblacklight_admin.rb +7 -0
  342. data/lib/tasks/.keep +0 -0
  343. data/lib/tasks/geoblacklight_admin.rake +209 -0
  344. data/spec/test_app_templates/lib/generators/test_app_generator.rb +35 -0
  345. data/template.rb +16 -0
  346. metadata +1037 -0
@@ -0,0 +1,1134 @@
1
+ // Provide auto-suggest from qa (questioning authority) endpoints. (Or really anything that returns results
2
+ // in the same format as qa, but qa is what it is intended for).
3
+ //
4
+ // Add to a text field the HTML attribute `data-scihist-qa-autocomplete=/path/to/qa/search`, and your
5
+ // text field, as you type in it, will get dropdown menu of sugggestions from the QA-delivered vocabulary.
6
+ // You can still enter 'manual' entries that were not from vocabulary.
7
+ //
8
+ // For qa vocabularly sources that return a :label and a :value, like FAST, the :label shows up
9
+ // in the suggestions, but when chosen the :value is what gets put in the textbox. For FAST,
10
+ // qa delivers results such as label=`Pennsylvania--Philadelphie USE Pennsylvania--Philadelphia`,
11
+ // value=`Pennsylvania--Philadelphia`.
12
+ //
13
+ // The `id` attribute returned in qa JSON response (which might be unique identifier from FAST or
14
+ // other vocabulary) -- is currently ignored, not displayed or preserved in any way.
15
+ //
16
+ // All this behavior is meant to match what we had in chf_sufia with hydra-editor or whatever other samvera
17
+ // dependencies were providing (not sure)
18
+ //
19
+ //
20
+ // ## Implementation
21
+ //
22
+ // The auto-suggest from remote AJAX source uses the devbridge-autocomplete/jquery-autocomplete
23
+ // jQuery plugin. https://github.com/devbridge/jQuery-Autocomplete . This was the most suitable
24
+ // pre-made thing I found with extensive looking (including trying to find things not dependent
25
+ // on jQuery).
26
+ //
27
+ // It still required a bit of hacking to get it to work. In particular, to get the behavior above
28
+ // for "USE" examples, where the string shown in the suggestions is not the thing that ends up
29
+ // in your text box if you select it -- that really confused the jquery-autocomplete plugin,
30
+ // it kept wanting to show a new set of suggestions when to our UX nothing had changed.
31
+ //
32
+ // This was made worse by a bug around onFocus (https://github.com/devbridge/jQuery-Autocomplete/issues/766)
33
+ // -- although really we wanted to disable "show suggestions onFocus" altogether to match
34
+ // chf_sufia behavior.
35
+ //
36
+ // We fix things up mostly with a few kind of hacky private API interventions into the plugin --
37
+ // but hey, this still isn't much code, and the plugin isn't getting much development so
38
+ // it probably won't break.
39
+ //
40
+ // We could try to add features we need upstream (https://github.com/devbridge/jQuery-Autocomplete/issues/769),
41
+ // we could consider forking this (it's not actually much code), or we could in the future provide a
42
+ // better alternate tool for a JS auto-suggest UI.
43
+
44
+ import 'jquery'
45
+
46
+ // https://github.com/devbridge/jQuery-Autocomplete
47
+ /**
48
+ * Ajax Autocomplete for jQuery, version 1.4.10
49
+ * (c) 2017 Tomas Kirda
50
+ *
51
+ * Ajax Autocomplete for jQuery is freely distributable under the terms of an MIT-style license.
52
+ * For details, see the web site: https://github.com/devbridge/jQuery-Autocomplete
53
+ */
54
+
55
+ /*jslint browser: true, white: true, single: true, this: true, multivar: true */
56
+ /*global define, window, document, jQuery, exports, require */
57
+
58
+ // Expose plugin as an AMD module if AMD loader is present:
59
+ (function (factory) {
60
+ "use strict";
61
+ if (typeof define === 'function' && define.amd) {
62
+ // AMD. Register as an anonymous module.
63
+ define(['jquery'], factory);
64
+ } else if (typeof exports === 'object' && typeof require === 'function') {
65
+ // Browserify
66
+ factory(require('jquery'));
67
+ } else {
68
+ // Browser globals
69
+ factory(jQuery);
70
+ }
71
+ }(function ($) {
72
+ 'use strict';
73
+
74
+ var
75
+ utils = (function () {
76
+ return {
77
+ escapeRegExChars: function (value) {
78
+ return value.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&");
79
+ },
80
+ createNode: function (containerClass) {
81
+ var div = document.createElement('div');
82
+ div.className = containerClass;
83
+ div.style.position = 'absolute';
84
+ div.style.display = 'none';
85
+ return div;
86
+ }
87
+ };
88
+ }()),
89
+
90
+ keys = {
91
+ ESC: 27,
92
+ TAB: 9,
93
+ RETURN: 13,
94
+ LEFT: 37,
95
+ UP: 38,
96
+ RIGHT: 39,
97
+ DOWN: 40
98
+ },
99
+
100
+ noop = $.noop;
101
+
102
+ function Autocomplete(el, options) {
103
+ var that = this;
104
+
105
+ // Shared variables:
106
+ that.element = el;
107
+ that.el = $(el);
108
+ that.suggestions = [];
109
+ that.badQueries = [];
110
+ that.selectedIndex = -1;
111
+ that.currentValue = that.element.value;
112
+ that.timeoutId = null;
113
+ that.cachedResponse = {};
114
+ that.onChangeTimeout = null;
115
+ that.onChange = null;
116
+ that.isLocal = false;
117
+ that.suggestionsContainer = null;
118
+ that.noSuggestionsContainer = null;
119
+ that.options = $.extend(true, {}, Autocomplete.defaults, options);
120
+ that.classes = {
121
+ selected: 'autocomplete-selected',
122
+ suggestion: 'autocomplete-suggestion'
123
+ };
124
+ that.hint = null;
125
+ that.hintValue = '';
126
+ that.selection = null;
127
+
128
+ // Initialize and set options:
129
+ that.initialize();
130
+ that.setOptions(options);
131
+ }
132
+
133
+ Autocomplete.utils = utils;
134
+
135
+ $.Autocomplete = Autocomplete;
136
+
137
+ Autocomplete.defaults = {
138
+ ajaxSettings: {},
139
+ autoSelectFirst: false,
140
+ appendTo: 'body',
141
+ serviceUrl: null,
142
+ lookup: null,
143
+ onSelect: null,
144
+ width: 'auto',
145
+ minChars: 1,
146
+ maxHeight: 300,
147
+ deferRequestBy: 0,
148
+ params: {},
149
+ formatResult: _formatResult,
150
+ formatGroup: _formatGroup,
151
+ delimiter: null,
152
+ zIndex: 9999,
153
+ type: 'GET',
154
+ noCache: false,
155
+ onSearchStart: noop,
156
+ onSearchComplete: noop,
157
+ onSearchError: noop,
158
+ preserveInput: false,
159
+ containerClass: 'autocomplete-suggestions',
160
+ tabDisabled: false,
161
+ dataType: 'text',
162
+ currentRequest: null,
163
+ triggerSelectOnValidInput: true,
164
+ preventBadQueries: true,
165
+ lookupFilter: _lookupFilter,
166
+ paramName: 'query',
167
+ transformResult: _transformResult,
168
+ showNoSuggestionNotice: false,
169
+ noSuggestionNotice: 'No results',
170
+ orientation: 'bottom',
171
+ forceFixPosition: false
172
+ };
173
+
174
+ function _lookupFilter(suggestion, originalQuery, queryLowerCase) {
175
+ return suggestion.value.toLowerCase().indexOf(queryLowerCase) !== -1;
176
+ };
177
+
178
+ function _transformResult(response) {
179
+ return typeof response === 'string' ? $.parseJSON(response) : response;
180
+ };
181
+
182
+ function _formatResult(suggestion, currentValue) {
183
+ // Do not replace anything if the current value is empty
184
+ if (!currentValue) {
185
+ return suggestion.value;
186
+ }
187
+
188
+ var pattern = '(' + utils.escapeRegExChars(currentValue) + ')';
189
+
190
+ return suggestion.value
191
+ .replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>')
192
+ .replace(/&/g, '&amp;')
193
+ .replace(/</g, '&lt;')
194
+ .replace(/>/g, '&gt;')
195
+ .replace(/"/g, '&quot;')
196
+ .replace(/&lt;(\/?strong)&gt;/g, '<$1>');
197
+ };
198
+
199
+ function _formatGroup(suggestion, category) {
200
+ return '<div class="autocomplete-group">' + category + '</div>';
201
+ };
202
+
203
+ Autocomplete.prototype = {
204
+
205
+ initialize: function () {
206
+ var that = this,
207
+ suggestionSelector = '.' + that.classes.suggestion,
208
+ selected = that.classes.selected,
209
+ options = that.options,
210
+ container;
211
+
212
+ that.element.setAttribute('autocomplete', 'off');
213
+
214
+ // html() deals with many types: htmlString or Element or Array or jQuery
215
+ that.noSuggestionsContainer = $('<div class="autocomplete-no-suggestion"></div>')
216
+ .html(this.options.noSuggestionNotice).get(0);
217
+
218
+ that.suggestionsContainer = Autocomplete.utils.createNode(options.containerClass);
219
+
220
+ container = $(that.suggestionsContainer);
221
+
222
+ container.appendTo(options.appendTo || 'body');
223
+
224
+ // Only set width if it was provided:
225
+ if (options.width !== 'auto') {
226
+ container.css('width', options.width);
227
+ }
228
+
229
+ // Listen for mouse over event on suggestions list:
230
+ container.on('mouseover.autocomplete', suggestionSelector, function () {
231
+ that.activate($(this).data('index'));
232
+ });
233
+
234
+ // Deselect active element when mouse leaves suggestions container:
235
+ container.on('mouseout.autocomplete', function () {
236
+ that.selectedIndex = -1;
237
+ container.children('.' + selected).removeClass(selected);
238
+ });
239
+
240
+ // Listen for click event on suggestions list:
241
+ container.on('click.autocomplete', suggestionSelector, function () {
242
+ that.select($(this).data('index'));
243
+ });
244
+
245
+ container.on('click.autocomplete', function () {
246
+ clearTimeout(that.blurTimeoutId);
247
+ })
248
+
249
+ that.fixPositionCapture = function () {
250
+ if (that.visible) {
251
+ that.fixPosition();
252
+ }
253
+ };
254
+
255
+ $(window).on('resize.autocomplete', that.fixPositionCapture);
256
+
257
+ that.el.on('keydown.autocomplete', function (e) { that.onKeyPress(e); });
258
+ that.el.on('keyup.autocomplete', function (e) { that.onKeyUp(e); });
259
+ that.el.on('blur.autocomplete', function () { that.onBlur(); });
260
+ that.el.on('focus.autocomplete', function () { that.onFocus(); });
261
+ that.el.on('change.autocomplete', function (e) { that.onKeyUp(e); });
262
+ that.el.on('input.autocomplete', function (e) { that.onKeyUp(e); });
263
+ },
264
+
265
+ onFocus: function () {
266
+ var that = this;
267
+
268
+ that.fixPosition();
269
+
270
+ if (that.el.val().length >= that.options.minChars) {
271
+ that.onValueChange();
272
+ }
273
+ },
274
+
275
+ onBlur: function () {
276
+ var that = this,
277
+ options = that.options,
278
+ value = that.el.val(),
279
+ query = that.getQuery(value);
280
+
281
+ // If user clicked on a suggestion, hide() will
282
+ // be canceled, otherwise close suggestions
283
+ that.blurTimeoutId = setTimeout(function () {
284
+ that.hide();
285
+
286
+ if (that.selection && that.currentValue !== query) {
287
+ (options.onInvalidateSelection || $.noop).call(that.element);
288
+ }
289
+ }, 200);
290
+ },
291
+
292
+ abortAjax: function () {
293
+ var that = this;
294
+ if (that.currentRequest) {
295
+ that.currentRequest.abort();
296
+ that.currentRequest = null;
297
+ }
298
+ },
299
+
300
+ setOptions: function (suppliedOptions) {
301
+ var that = this,
302
+ options = $.extend({}, that.options, suppliedOptions);
303
+
304
+ that.isLocal = Array.isArray(options.lookup);
305
+
306
+ if (that.isLocal) {
307
+ options.lookup = that.verifySuggestionsFormat(options.lookup);
308
+ }
309
+
310
+ options.orientation = that.validateOrientation(options.orientation, 'bottom');
311
+
312
+ // Adjust height, width and z-index:
313
+ $(that.suggestionsContainer).css({
314
+ 'max-height': options.maxHeight + 'px',
315
+ 'width': options.width + 'px',
316
+ 'z-index': options.zIndex
317
+ });
318
+
319
+ this.options = options;
320
+ },
321
+
322
+
323
+ clearCache: function () {
324
+ this.cachedResponse = {};
325
+ this.badQueries = [];
326
+ },
327
+
328
+ clear: function () {
329
+ this.clearCache();
330
+ this.currentValue = '';
331
+ this.suggestions = [];
332
+ },
333
+
334
+ disable: function () {
335
+ var that = this;
336
+ that.disabled = true;
337
+ clearTimeout(that.onChangeTimeout);
338
+ that.abortAjax();
339
+ },
340
+
341
+ enable: function () {
342
+ this.disabled = false;
343
+ },
344
+
345
+ fixPosition: function () {
346
+ // Use only when container has already its content
347
+
348
+ var that = this,
349
+ $container = $(that.suggestionsContainer),
350
+ containerParent = $container.parent().get(0);
351
+ // Fix position automatically when appended to body.
352
+ // In other cases force parameter must be given.
353
+ if (containerParent !== document.body && !that.options.forceFixPosition) {
354
+ return;
355
+ }
356
+
357
+ // Choose orientation
358
+ var orientation = that.options.orientation,
359
+ containerHeight = $container.outerHeight(),
360
+ height = that.el.outerHeight(),
361
+ offset = that.el.offset(),
362
+ styles = { 'top': offset.top, 'left': offset.left };
363
+
364
+ if (orientation === 'auto') {
365
+ var viewPortHeight = $(window).height(),
366
+ scrollTop = $(window).scrollTop(),
367
+ topOverflow = -scrollTop + offset.top - containerHeight,
368
+ bottomOverflow = scrollTop + viewPortHeight - (offset.top + height + containerHeight);
369
+
370
+ orientation = (Math.max(topOverflow, bottomOverflow) === topOverflow) ? 'top' : 'bottom';
371
+ }
372
+
373
+ if (orientation === 'top') {
374
+ styles.top += -containerHeight;
375
+ } else {
376
+ styles.top += height;
377
+ }
378
+
379
+ // If container is not positioned to body,
380
+ // correct its position using offset parent offset
381
+ if(containerParent !== document.body) {
382
+ var opacity = $container.css('opacity'),
383
+ parentOffsetDiff;
384
+
385
+ if (!that.visible){
386
+ $container.css('opacity', 0).show();
387
+ }
388
+
389
+ parentOffsetDiff = $container.offsetParent().offset();
390
+ styles.top -= parentOffsetDiff.top;
391
+ styles.top += containerParent.scrollTop;
392
+ styles.left -= parentOffsetDiff.left;
393
+
394
+ if (!that.visible){
395
+ $container.css('opacity', opacity).hide();
396
+ }
397
+ }
398
+
399
+ if (that.options.width === 'auto') {
400
+ styles.width = that.el.outerWidth() + 'px';
401
+ }
402
+
403
+ $container.css(styles);
404
+ },
405
+
406
+ isCursorAtEnd: function () {
407
+ var that = this,
408
+ valLength = that.el.val().length,
409
+ selectionStart = that.element.selectionStart,
410
+ range;
411
+
412
+ if (typeof selectionStart === 'number') {
413
+ return selectionStart === valLength;
414
+ }
415
+ if (document.selection) {
416
+ range = document.selection.createRange();
417
+ range.moveStart('character', -valLength);
418
+ return valLength === range.text.length;
419
+ }
420
+ return true;
421
+ },
422
+
423
+ onKeyPress: function (e) {
424
+ var that = this;
425
+
426
+ // If suggestions are hidden and user presses arrow down, display suggestions:
427
+ if (!that.disabled && !that.visible && e.which === keys.DOWN && that.currentValue) {
428
+ that.suggest();
429
+ return;
430
+ }
431
+
432
+ if (that.disabled || !that.visible) {
433
+ return;
434
+ }
435
+
436
+ switch (e.which) {
437
+ case keys.ESC:
438
+ that.el.val(that.currentValue);
439
+ that.hide();
440
+ break;
441
+ case keys.RIGHT:
442
+ if (that.hint && that.options.onHint && that.isCursorAtEnd()) {
443
+ that.selectHint();
444
+ break;
445
+ }
446
+ return;
447
+ case keys.TAB:
448
+ if (that.hint && that.options.onHint) {
449
+ that.selectHint();
450
+ return;
451
+ }
452
+ if (that.selectedIndex === -1) {
453
+ that.hide();
454
+ return;
455
+ }
456
+ that.select(that.selectedIndex);
457
+ if (that.options.tabDisabled === false) {
458
+ return;
459
+ }
460
+ break;
461
+ case keys.RETURN:
462
+ if (that.selectedIndex === -1) {
463
+ that.hide();
464
+ return;
465
+ }
466
+ that.select(that.selectedIndex);
467
+ break;
468
+ case keys.UP:
469
+ that.moveUp();
470
+ break;
471
+ case keys.DOWN:
472
+ that.moveDown();
473
+ break;
474
+ default:
475
+ return;
476
+ }
477
+
478
+ // Cancel event if function did not return:
479
+ e.stopImmediatePropagation();
480
+ e.preventDefault();
481
+ },
482
+
483
+ onKeyUp: function (e) {
484
+ var that = this;
485
+
486
+ if (that.disabled) {
487
+ return;
488
+ }
489
+
490
+ switch (e.which) {
491
+ case keys.UP:
492
+ case keys.DOWN:
493
+ return;
494
+ }
495
+
496
+ clearTimeout(that.onChangeTimeout);
497
+
498
+ if (that.currentValue !== that.el.val()) {
499
+ that.findBestHint();
500
+ if (that.options.deferRequestBy > 0) {
501
+ // Defer lookup in case when value changes very quickly:
502
+ that.onChangeTimeout = setTimeout(function () {
503
+ that.onValueChange();
504
+ }, that.options.deferRequestBy);
505
+ } else {
506
+ that.onValueChange();
507
+ }
508
+ }
509
+ },
510
+
511
+ onValueChange: function () {
512
+ if (this.ignoreValueChange) {
513
+ this.ignoreValueChange = false;
514
+ return;
515
+ }
516
+
517
+ var that = this,
518
+ options = that.options,
519
+ value = that.el.val(),
520
+ query = that.getQuery(value);
521
+
522
+ if (that.selection && that.currentValue !== query) {
523
+ that.selection = null;
524
+ (options.onInvalidateSelection || $.noop).call(that.element);
525
+ }
526
+
527
+ clearTimeout(that.onChangeTimeout);
528
+ that.currentValue = value;
529
+ that.selectedIndex = -1;
530
+
531
+ // Check existing suggestion for the match before proceeding:
532
+ if (options.triggerSelectOnValidInput && that.isExactMatch(query)) {
533
+ that.select(0);
534
+ return;
535
+ }
536
+
537
+ if (query.length < options.minChars) {
538
+ that.hide();
539
+ } else {
540
+ that.getSuggestions(query);
541
+ }
542
+ },
543
+
544
+ isExactMatch: function (query) {
545
+ var suggestions = this.suggestions;
546
+
547
+ return (suggestions.length === 1 && suggestions[0].value.toLowerCase() === query.toLowerCase());
548
+ },
549
+
550
+ getQuery: function (value) {
551
+ var delimiter = this.options.delimiter,
552
+ parts;
553
+
554
+ if (!delimiter) {
555
+ return value;
556
+ }
557
+ parts = value.split(delimiter);
558
+ return $.trim(parts[parts.length - 1]);
559
+ },
560
+
561
+ getSuggestionsLocal: function (query) {
562
+ var that = this,
563
+ options = that.options,
564
+ queryLowerCase = query.toLowerCase(),
565
+ filter = options.lookupFilter,
566
+ limit = parseInt(options.lookupLimit, 10),
567
+ data;
568
+
569
+ data = {
570
+ suggestions: $.grep(options.lookup, function (suggestion) {
571
+ return filter(suggestion, query, queryLowerCase);
572
+ })
573
+ };
574
+
575
+ if (limit && data.suggestions.length > limit) {
576
+ data.suggestions = data.suggestions.slice(0, limit);
577
+ }
578
+
579
+ return data;
580
+ },
581
+
582
+ getSuggestions: function (q) {
583
+ var response,
584
+ that = this,
585
+ options = that.options,
586
+ serviceUrl = options.serviceUrl,
587
+ params,
588
+ cacheKey,
589
+ ajaxSettings;
590
+
591
+ options.params[options.paramName] = q;
592
+
593
+ if (options.onSearchStart.call(that.element, options.params) === false) {
594
+ return;
595
+ }
596
+
597
+ params = options.ignoreParams ? null : options.params;
598
+
599
+ if ($.isFunction(options.lookup)){
600
+ options.lookup(q, function (data) {
601
+ that.suggestions = data.suggestions;
602
+ that.suggest();
603
+ options.onSearchComplete.call(that.element, q, data.suggestions);
604
+ });
605
+ return;
606
+ }
607
+
608
+ if (that.isLocal) {
609
+ response = that.getSuggestionsLocal(q);
610
+ } else {
611
+ if ($.isFunction(serviceUrl)) {
612
+ serviceUrl = serviceUrl.call(that.element, q);
613
+ }
614
+ cacheKey = serviceUrl + '?' + $.param(params || {});
615
+ response = that.cachedResponse[cacheKey];
616
+ }
617
+
618
+ if (response && Array.isArray(response.suggestions)) {
619
+ that.suggestions = response.suggestions;
620
+ that.suggest();
621
+ options.onSearchComplete.call(that.element, q, response.suggestions);
622
+ } else if (!that.isBadQuery(q)) {
623
+ that.abortAjax();
624
+
625
+ ajaxSettings = {
626
+ url: serviceUrl,
627
+ data: params,
628
+ type: options.type,
629
+ dataType: options.dataType
630
+ };
631
+
632
+ $.extend(ajaxSettings, options.ajaxSettings);
633
+
634
+ that.currentRequest = $.ajax(ajaxSettings).done(function (data) {
635
+ var result;
636
+ that.currentRequest = null;
637
+ result = options.transformResult(data, q);
638
+ that.processResponse(result, q, cacheKey);
639
+ options.onSearchComplete.call(that.element, q, result.suggestions);
640
+ }).fail(function (jqXHR, textStatus, errorThrown) {
641
+ options.onSearchError.call(that.element, q, jqXHR, textStatus, errorThrown);
642
+ });
643
+ } else {
644
+ options.onSearchComplete.call(that.element, q, []);
645
+ }
646
+ },
647
+
648
+ isBadQuery: function (q) {
649
+ if (!this.options.preventBadQueries){
650
+ return false;
651
+ }
652
+
653
+ var badQueries = this.badQueries,
654
+ i = badQueries.length;
655
+
656
+ while (i--) {
657
+ if (q.indexOf(badQueries[i]) === 0) {
658
+ return true;
659
+ }
660
+ }
661
+
662
+ return false;
663
+ },
664
+
665
+ hide: function () {
666
+ var that = this,
667
+ container = $(that.suggestionsContainer);
668
+
669
+ if ($.isFunction(that.options.onHide) && that.visible) {
670
+ that.options.onHide.call(that.element, container);
671
+ }
672
+
673
+ that.visible = false;
674
+ that.selectedIndex = -1;
675
+ clearTimeout(that.onChangeTimeout);
676
+ $(that.suggestionsContainer).hide();
677
+ that.signalHint(null);
678
+ },
679
+
680
+ suggest: function () {
681
+ if (!this.suggestions.length) {
682
+ if (this.options.showNoSuggestionNotice) {
683
+ this.noSuggestions();
684
+ } else {
685
+ this.hide();
686
+ }
687
+ return;
688
+ }
689
+
690
+ var that = this,
691
+ options = that.options,
692
+ groupBy = options.groupBy,
693
+ formatResult = options.formatResult,
694
+ value = that.getQuery(that.currentValue),
695
+ className = that.classes.suggestion,
696
+ classSelected = that.classes.selected,
697
+ container = $(that.suggestionsContainer),
698
+ noSuggestionsContainer = $(that.noSuggestionsContainer),
699
+ beforeRender = options.beforeRender,
700
+ html = '',
701
+ category,
702
+ formatGroup = function (suggestion, index) {
703
+ var currentCategory = suggestion.data[groupBy];
704
+
705
+ if (category === currentCategory){
706
+ return '';
707
+ }
708
+
709
+ category = currentCategory;
710
+
711
+ return options.formatGroup(suggestion, category);
712
+ };
713
+
714
+ if (options.triggerSelectOnValidInput && that.isExactMatch(value)) {
715
+ that.select(0);
716
+ return;
717
+ }
718
+
719
+ // Build suggestions inner HTML:
720
+ $.each(that.suggestions, function (i, suggestion) {
721
+ if (groupBy){
722
+ html += formatGroup(suggestion, value, i);
723
+ }
724
+
725
+ html += '<div class="' + className + '" data-index="' + i + '">' + formatResult(suggestion, value, i) + '</div>';
726
+ });
727
+
728
+ this.adjustContainerWidth();
729
+
730
+ noSuggestionsContainer.detach();
731
+ container.html(html);
732
+
733
+ if ($.isFunction(beforeRender)) {
734
+ beforeRender.call(that.element, container, that.suggestions);
735
+ }
736
+
737
+ that.fixPosition();
738
+ container.show();
739
+
740
+ // Select first value by default:
741
+ if (options.autoSelectFirst) {
742
+ that.selectedIndex = 0;
743
+ container.scrollTop(0);
744
+ container.children('.' + className).first().addClass(classSelected);
745
+ }
746
+
747
+ that.visible = true;
748
+ that.findBestHint();
749
+ },
750
+
751
+ noSuggestions: function() {
752
+ var that = this,
753
+ beforeRender = that.options.beforeRender,
754
+ container = $(that.suggestionsContainer),
755
+ noSuggestionsContainer = $(that.noSuggestionsContainer);
756
+
757
+ this.adjustContainerWidth();
758
+
759
+ // Some explicit steps. Be careful here as it easy to get
760
+ // noSuggestionsContainer removed from DOM if not detached properly.
761
+ noSuggestionsContainer.detach();
762
+
763
+ // clean suggestions if any
764
+ container.empty();
765
+ container.append(noSuggestionsContainer);
766
+
767
+ if ($.isFunction(beforeRender)) {
768
+ beforeRender.call(that.element, container, that.suggestions);
769
+ }
770
+
771
+ that.fixPosition();
772
+
773
+ container.show();
774
+ that.visible = true;
775
+ },
776
+
777
+ adjustContainerWidth: function() {
778
+ var that = this,
779
+ options = that.options,
780
+ width,
781
+ container = $(that.suggestionsContainer);
782
+
783
+ // If width is auto, adjust width before displaying suggestions,
784
+ // because if instance was created before input had width, it will be zero.
785
+ // Also it adjusts if input width has changed.
786
+ if (options.width === 'auto') {
787
+ width = that.el.outerWidth();
788
+ container.css('width', width > 0 ? width : 300);
789
+ } else if(options.width === 'flex') {
790
+ // Trust the source! Unset the width property so it will be the max length
791
+ // the containing elements.
792
+ container.css('width', '');
793
+ }
794
+ },
795
+
796
+ findBestHint: function () {
797
+ var that = this,
798
+ value = that.el.val().toLowerCase(),
799
+ bestMatch = null;
800
+
801
+ if (!value) {
802
+ return;
803
+ }
804
+
805
+ $.each(that.suggestions, function (i, suggestion) {
806
+ var foundMatch = suggestion.value.toLowerCase().indexOf(value) === 0;
807
+ if (foundMatch) {
808
+ bestMatch = suggestion;
809
+ }
810
+ return !foundMatch;
811
+ });
812
+
813
+ that.signalHint(bestMatch);
814
+ },
815
+
816
+ signalHint: function (suggestion) {
817
+ var hintValue = '',
818
+ that = this;
819
+ if (suggestion) {
820
+ hintValue = that.currentValue + suggestion.value.substr(that.currentValue.length);
821
+ }
822
+ if (that.hintValue !== hintValue) {
823
+ that.hintValue = hintValue;
824
+ that.hint = suggestion;
825
+ (this.options.onHint || $.noop)(hintValue);
826
+ }
827
+ },
828
+
829
+ verifySuggestionsFormat: function (suggestions) {
830
+ // If suggestions is string array, convert them to supported format:
831
+ if (suggestions.length && typeof suggestions[0] === 'string') {
832
+ return $.map(suggestions, function (value) {
833
+ return { value: value, data: null };
834
+ });
835
+ }
836
+
837
+ return suggestions;
838
+ },
839
+
840
+ validateOrientation: function(orientation, fallback) {
841
+ orientation = $.trim(orientation || '').toLowerCase();
842
+
843
+ if($.inArray(orientation, ['auto', 'bottom', 'top']) === -1){
844
+ orientation = fallback;
845
+ }
846
+
847
+ return orientation;
848
+ },
849
+
850
+ processResponse: function (result, originalQuery, cacheKey) {
851
+ var that = this,
852
+ options = that.options;
853
+
854
+ result.suggestions = that.verifySuggestionsFormat(result.suggestions);
855
+
856
+ // Cache results if cache is not disabled:
857
+ if (!options.noCache) {
858
+ that.cachedResponse[cacheKey] = result;
859
+ if (options.preventBadQueries && !result.suggestions.length) {
860
+ that.badQueries.push(originalQuery);
861
+ }
862
+ }
863
+
864
+ // Return if originalQuery is not matching current query:
865
+ if (originalQuery !== that.getQuery(that.currentValue)) {
866
+ return;
867
+ }
868
+
869
+ that.suggestions = result.suggestions;
870
+ that.suggest();
871
+ },
872
+
873
+ activate: function (index) {
874
+ var that = this,
875
+ activeItem,
876
+ selected = that.classes.selected,
877
+ container = $(that.suggestionsContainer),
878
+ children = container.find('.' + that.classes.suggestion);
879
+
880
+ container.find('.' + selected).removeClass(selected);
881
+
882
+ that.selectedIndex = index;
883
+
884
+ if (that.selectedIndex !== -1 && children.length > that.selectedIndex) {
885
+ activeItem = children.get(that.selectedIndex);
886
+ $(activeItem).addClass(selected);
887
+ return activeItem;
888
+ }
889
+
890
+ return null;
891
+ },
892
+
893
+ selectHint: function () {
894
+ var that = this,
895
+ i = $.inArray(that.hint, that.suggestions);
896
+
897
+ that.select(i);
898
+ },
899
+
900
+ select: function (i) {
901
+ var that = this;
902
+ that.hide();
903
+ that.onSelect(i);
904
+ },
905
+
906
+ moveUp: function () {
907
+ var that = this;
908
+
909
+ if (that.selectedIndex === -1) {
910
+ return;
911
+ }
912
+
913
+ if (that.selectedIndex === 0) {
914
+ $(that.suggestionsContainer).children('.' + that.classes.suggestion).first().removeClass(that.classes.selected);
915
+ that.selectedIndex = -1;
916
+ that.ignoreValueChange = false;
917
+ that.el.val(that.currentValue);
918
+ that.findBestHint();
919
+ return;
920
+ }
921
+
922
+ that.adjustScroll(that.selectedIndex - 1);
923
+ },
924
+
925
+ moveDown: function () {
926
+ var that = this;
927
+
928
+ if (that.selectedIndex === (that.suggestions.length - 1)) {
929
+ return;
930
+ }
931
+
932
+ that.adjustScroll(that.selectedIndex + 1);
933
+ },
934
+
935
+ adjustScroll: function (index) {
936
+ var that = this,
937
+ activeItem = that.activate(index);
938
+
939
+ if (!activeItem) {
940
+ return;
941
+ }
942
+
943
+ var offsetTop,
944
+ upperBound,
945
+ lowerBound,
946
+ heightDelta = $(activeItem).outerHeight();
947
+
948
+ offsetTop = activeItem.offsetTop;
949
+ upperBound = $(that.suggestionsContainer).scrollTop();
950
+ lowerBound = upperBound + that.options.maxHeight - heightDelta;
951
+
952
+ if (offsetTop < upperBound) {
953
+ $(that.suggestionsContainer).scrollTop(offsetTop);
954
+ } else if (offsetTop > lowerBound) {
955
+ $(that.suggestionsContainer).scrollTop(offsetTop - that.options.maxHeight + heightDelta);
956
+ }
957
+
958
+ if (!that.options.preserveInput) {
959
+ // During onBlur event, browser will trigger "change" event,
960
+ // because value has changed, to avoid side effect ignore,
961
+ // that event, so that correct suggestion can be selected
962
+ // when clicking on suggestion with a mouse
963
+ that.ignoreValueChange = true;
964
+ that.el.val(that.getValue(that.suggestions[index].value));
965
+ }
966
+
967
+ that.signalHint(null);
968
+ },
969
+
970
+ onSelect: function (index) {
971
+ var that = this,
972
+ onSelectCallback = that.options.onSelect,
973
+ suggestion = that.suggestions[index];
974
+
975
+ that.currentValue = that.getValue(suggestion.value);
976
+
977
+ if (that.currentValue !== that.el.val() && !that.options.preserveInput) {
978
+ that.el.val(that.currentValue);
979
+ }
980
+
981
+ that.signalHint(null);
982
+ that.suggestions = [];
983
+ that.selection = suggestion;
984
+
985
+ if ($.isFunction(onSelectCallback)) {
986
+ onSelectCallback.call(that.element, suggestion);
987
+ }
988
+ },
989
+
990
+ getValue: function (value) {
991
+ var that = this,
992
+ delimiter = that.options.delimiter,
993
+ currentValue,
994
+ parts;
995
+
996
+ if (!delimiter) {
997
+ return value;
998
+ }
999
+
1000
+ currentValue = that.currentValue;
1001
+ parts = currentValue.split(delimiter);
1002
+
1003
+ if (parts.length === 1) {
1004
+ return value;
1005
+ }
1006
+
1007
+ return currentValue.substr(0, currentValue.length - parts[parts.length - 1].length) + value;
1008
+ },
1009
+
1010
+ dispose: function () {
1011
+ var that = this;
1012
+ that.el.off('.autocomplete').removeData('autocomplete');
1013
+ $(window).off('resize.autocomplete', that.fixPositionCapture);
1014
+ $(that.suggestionsContainer).remove();
1015
+ }
1016
+ };
1017
+
1018
+ // Create chainable jQuery plugin:
1019
+ $.fn.devbridgeAutocomplete = function (options, args) {
1020
+ var dataKey = 'autocomplete';
1021
+ // If function invoked without argument return
1022
+ // instance of the first matched element:
1023
+ if (!arguments.length) {
1024
+ return this.first().data(dataKey);
1025
+ }
1026
+
1027
+ return this.each(function () {
1028
+ var inputElement = $(this),
1029
+ instance = inputElement.data(dataKey);
1030
+
1031
+ if (typeof options === 'string') {
1032
+ if (instance && typeof instance[options] === 'function') {
1033
+ instance[options](args);
1034
+ }
1035
+ } else {
1036
+ // If instance already exists, destroy it:
1037
+ if (instance && instance.dispose) {
1038
+ instance.dispose();
1039
+ }
1040
+ instance = new Autocomplete(this, options);
1041
+ inputElement.data(dataKey, instance);
1042
+ }
1043
+ });
1044
+ };
1045
+
1046
+ // Don't overwrite if it already exists
1047
+ if (!$.fn.autocomplete) {
1048
+ $.fn.autocomplete = $.fn.devbridgeAutocomplete;
1049
+ }
1050
+ }));
1051
+
1052
+ var errorContainer = $('<div class="autocomplete-no-suggestion"></div>')
1053
+ .html("<i class='fa fa-exclamation-triangle' aria-hidden='true'></i> Error fetching results").get(0);
1054
+
1055
+ function addAutocomplete(element) {
1056
+ var input_el = $(element);
1057
+ console.log(input_el);
1058
+
1059
+ var qa_search_url = input_el.data("scihist-qa-autocomplete");
1060
+
1061
+ input_el.autocomplete({
1062
+ deferRequestBy: 600, // bunch keystrokes into 600ms groups for requests
1063
+ paramName: "q",
1064
+ minChars: 2,
1065
+ maxHeight: 700,
1066
+ preserveInput: true, // our custom onSelect instead
1067
+ showNoSuggestionNotice: true,
1068
+ serviceUrl: qa_search_url,
1069
+
1070
+ onSearchError: function (query, jqXHR, textStatus, errorThrown) {
1071
+ console.log("autocomplete error fetching results: " + textStatus + ": " + errorThrown);
1072
+
1073
+ // Pretty hacky way to show error message in dropdown, reaching into
1074
+ // autocomplete internals. Based on how autocomplete shows no-results message.
1075
+ var container = $($(this).autocomplete().suggestionsContainer)
1076
+ container.empty();
1077
+ container.append(errorContainer);
1078
+ container.show();
1079
+ },
1080
+
1081
+ transformResult: function(response) {
1082
+ // not sure why it's a string instead of json already...
1083
+ if (typeof response == "string") {
1084
+ response = JSON.parse(response);
1085
+ }
1086
+
1087
+ return {
1088
+ suggestions: $.map(response, function(dataItem) {
1089
+ return { value: dataItem.label, data: dataItem.value };
1090
+ })
1091
+ };
1092
+ },
1093
+
1094
+ onSelect: function(suggestion) {
1095
+ // Set the text input to `data`, not the `value` that was being shown.
1096
+ //
1097
+ // However, the autocomplete JS is confused if we set a value to the textbox
1098
+ // different than the the string that was displayed in the menu, and wants
1099
+ // to show suggestions for the new string. We disable and re-enable
1100
+ // only on additional text entry in the box, to get around this in a hacky way.
1101
+ //
1102
+ // Still has SOME weird cases where autosuggest will show up when you don't think
1103
+ // you've changed anything, but minimized and that's not too horrible.
1104
+ var input = $(this);
1105
+ input.autocomplete('disable');
1106
+ this.value = suggestion.data || suggestion.value;
1107
+
1108
+ $(this).one("keydown", function() { input.autocomplete("enable") })
1109
+ }
1110
+ });
1111
+
1112
+ // hack to break in and disable the autocomplete onFocus, cause we don't
1113
+ // WANT to autocomplete on focus, plus it's broken with 'disabled' state,
1114
+ // that keeps us from working around re-suggest on select
1115
+ input_el.autocomplete().onFocus = function() {};
1116
+ }
1117
+
1118
+ // Uses jquery, assumes it exists in `window` for now.
1119
+
1120
+ jQuery( document ).ready(function() {
1121
+ // When an input is focused, see if it already has autocomplete plugin added,
1122
+ // if not add it. This makes it work for suitable new input elements added
1123
+ // to the DOM (in response to 'add another' links), even if they weren't
1124
+ // there on page at load.
1125
+ $("body").on("focus", "*[data-scihist-qa-autocomplete]", function(event) {
1126
+ var el = $(event.target);
1127
+ console.log(el);
1128
+
1129
+ if (! el.data("autocomplete")) {
1130
+ // hasn't already had autocomplete plugin added, so add it.
1131
+ addAutocomplete(event.target)
1132
+ }
1133
+ });
1134
+ });