geoblacklight_admin 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ });