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.
- checksums.yaml +7 -0
- data/.editorconfig +18 -0
- data/.gitignore +75 -0
- data/.standard.yml +4 -0
- data/Gemfile +59 -0
- data/LICENSE +21 -0
- data/README.md +84 -0
- data/Rakefile +72 -0
- data/app/assets/images/.keep +0 -0
- data/app/assets/images/aerial_photo.jpg +0 -0
- data/app/assets/images/archive-solid.svg +1 -0
- data/app/assets/images/bookmark-regular.svg +1 -0
- data/app/assets/images/bookmark-solid.svg +1 -0
- data/app/assets/images/btaa-logo.jpg +0 -0
- data/app/assets/images/cloud-download-alt-solid.svg +1 -0
- data/app/assets/images/file-import-solid.svg +1 -0
- data/app/assets/images/trash-alt-solid.svg +1 -0
- data/app/assets/javascripts/geoblacklight_admin/chosen.js +4 -0
- data/app/assets/javascripts/geoblacklight_admin/date_range_picker.js +173 -0
- data/app/assets/javascripts/geoblacklight_admin/datepicker.js +22 -0
- data/app/assets/javascripts/geoblacklight_admin/elements.js +38 -0
- data/app/assets/javascripts/geoblacklight_admin/exports.js +3 -0
- data/app/assets/javascripts/geoblacklight_admin/fields.js +11 -0
- data/app/assets/javascripts/geoblacklight_admin/geoblacklight_admin.js +2 -0
- data/app/assets/javascripts/geoblacklight_admin/inputmask.js +4 -0
- data/app/assets/javascripts/geoblacklight_admin/locale.js +5 -0
- data/app/assets/javascripts/geoblacklight_admin/sortable.js +2544 -0
- data/app/assets/javascripts/geoblacklight_admin/tooltips.js +3 -0
- data/app/assets/javascripts/geoblacklight_admin/truncate.js +4 -0
- data/app/assets/javascripts/geoblacklight_admin.js +40 -0
- data/app/assets/stylesheets/geoblacklight_admin/_core.scss +47 -0
- data/app/assets/stylesheets/geoblacklight_admin/modules/_autocomplete.scss +32 -0
- data/app/assets/stylesheets/geoblacklight_admin/modules/_blazer.scss +3 -0
- data/app/assets/stylesheets/geoblacklight_admin/modules/_code.scss +3 -0
- data/app/assets/stylesheets/geoblacklight_admin/modules/_datepicker.scss +477 -0
- data/app/assets/stylesheets/geoblacklight_admin/modules/_elements.scss +3 -0
- data/app/assets/stylesheets/geoblacklight_admin/modules/_forms.scss +28 -0
- data/app/assets/stylesheets/geoblacklight_admin/modules/_icons.scss +15 -0
- data/app/assets/stylesheets/geoblacklight_admin/modules/_nav.scss +9 -0
- data/app/assets/stylesheets/geoblacklight_admin/modules/_tables.scss +4 -0
- data/app/assets/stylesheets/geoblacklight_admin/modules/_toasts.scss +7 -0
- data/app/assets/stylesheets/geoblacklight_admin/modules/_visibility.scss +3 -0
- data/app/assets/stylesheets/geoblacklight_admin/settings/_variables.scss +1 -0
- data/app/channels/export_channel.rb +12 -0
- data/app/controllers/admin/admin_controller.rb +16 -0
- data/app/controllers/admin/advanced_search_controller.rb +365 -0
- data/app/controllers/admin/api_controller.rb +386 -0
- data/app/controllers/admin/bookmarks_controller.rb +68 -0
- data/app/controllers/admin/bulk_actions_controller.rb +94 -0
- data/app/controllers/admin/concerns/.keep +0 -0
- data/app/controllers/admin/concerns/upload_util.rb +8 -0
- data/app/controllers/admin/document_accesses_controller.rb +125 -0
- data/app/controllers/admin/document_assets_controller.rb +198 -0
- data/app/controllers/admin/document_downloads_controller.rb +127 -0
- data/app/controllers/admin/documents_controller.rb +219 -0
- data/app/controllers/admin/elements_controller.rb +81 -0
- data/app/controllers/admin/form_elements_controller.rb +81 -0
- data/app/controllers/admin/ids_controller.rb +379 -0
- data/app/controllers/admin/import_documents_controller.rb +18 -0
- data/app/controllers/admin/imports_controller.rb +105 -0
- data/app/controllers/admin/mappings_controller.rb +95 -0
- data/app/controllers/admin/notifications_controller.rb +51 -0
- data/app/controllers/admin/search_controller.rb +10 -0
- data/app/controllers/admin/users_controller.rb +10 -0
- data/app/helpers/bulk_actions_helper.rb +9 -0
- data/app/helpers/document_helper.rb +60 -0
- data/app/helpers/form_input_helper.rb +73 -0
- data/app/helpers/geoblacklight_admin_helper.rb +118 -0
- data/app/helpers/import_documents_helper.rb +5 -0
- data/app/helpers/mappings_helper.rb +27 -0
- data/app/indexers/document_indexer.rb +34 -0
- data/app/jobs/bulk_action_revert_document_job.rb +49 -0
- data/app/jobs/bulk_action_revert_job.rb +25 -0
- data/app/jobs/bulk_action_run_document_job.rb +42 -0
- data/app/jobs/bulk_action_run_job.rb +27 -0
- data/app/jobs/export_job.rb +72 -0
- data/app/jobs/export_json_job.rb +120 -0
- data/app/jobs/import_document_job.rb +25 -0
- data/app/jobs/import_run_job.rb +33 -0
- data/app/models/active_storage_attachment.rb +5 -0
- data/app/models/active_storage_blob.rb +5 -0
- data/app/models/api_search_builder.rb +12 -0
- data/app/models/application_record.rb +6 -0
- data/app/models/blacklight_api.rb +93 -0
- data/app/models/blacklight_api_facets.rb +15 -0
- data/app/models/blacklight_api_ids.rb +103 -0
- data/app/models/bookmark.rb +17 -0
- data/app/models/bulk_action.rb +88 -0
- data/app/models/bulk_action_document.rb +22 -0
- data/app/models/bulk_action_document_state_machine.rb +16 -0
- data/app/models/bulk_action_document_transition.rb +19 -0
- data/app/models/bulk_action_state_machine.rb +16 -0
- data/app/models/bulk_action_transition.rb +26 -0
- data/app/models/concerns/.keep +0 -0
- data/app/models/document/bbox_validator.rb +70 -0
- data/app/models/document/controlled_lists.rb +104 -0
- data/app/models/document/date_range_validator.rb +63 -0
- data/app/models/document/geom_validator.rb +117 -0
- data/app/models/document/reference.rb +107 -0
- data/app/models/document.rb +264 -0
- data/app/models/document_access.rb +35 -0
- data/app/models/document_download.rb +35 -0
- data/app/models/document_state_machine.rb +14 -0
- data/app/models/document_transition.rb +26 -0
- data/app/models/element.rb +90 -0
- data/app/models/form_control.rb +2 -0
- data/app/models/form_element.rb +19 -0
- data/app/models/form_feature.rb +2 -0
- data/app/models/form_group.rb +2 -0
- data/app/models/form_header.rb +2 -0
- data/app/models/geoblacklight_admin/field_mappings_btaa_aardvark.rb +379 -0
- data/app/models/geoblacklight_admin/field_mappings_gblv1.rb +344 -0
- data/app/models/geoblacklight_admin/iso_language_codes.rb +515 -0
- data/app/models/geoblacklight_admin/schema.rb +194 -0
- data/app/models/geoblacklight_admin.rb +3 -0
- data/app/models/import/csv_duplicates_validator.rb +36 -0
- data/app/models/import/csv_header_validator.rb +24 -0
- data/app/models/import.rb +186 -0
- data/app/models/import_btaa.rb +91 -0
- data/app/models/import_btaa_aardvark.rb +109 -0
- data/app/models/import_document.rb +34 -0
- data/app/models/import_document_state_machine.rb +12 -0
- data/app/models/import_document_transition.rb +19 -0
- data/app/models/import_gblv1.rb +92 -0
- data/app/models/import_state_machine.rb +16 -0
- data/app/models/import_transition.rb +26 -0
- data/app/models/mapping.rb +10 -0
- data/app/models/notification.rb +9 -0
- data/app/models/user.rb +26 -0
- data/app/notifications/export_notification.rb +30 -0
- data/app/services/export_csv_document_access_links_service.rb +47 -0
- data/app/services/export_csv_document_downloads_service.rb +47 -0
- data/app/services/export_csv_service.rb +44 -0
- data/app/services/export_json_service.rb +35 -0
- data/app/services/zip_file_generator.rb +56 -0
- data/app/views/admin/advanced_search/facets.json.jbuilder +80 -0
- data/app/views/admin/advanced_search/index.json.jbuilder +90 -0
- data/app/views/admin/api/_field.json.jbuilder +10 -0
- data/app/views/admin/api/fetch.json.jbuilder +86 -0
- data/app/views/admin/api/index.json.jbuilder +90 -0
- data/app/views/admin/bookmarks/_bookmark.json.jbuilder +4 -0
- data/app/views/admin/bookmarks/create.js.erb +7 -0
- data/app/views/admin/bookmarks/destroy.js.erb +7 -0
- data/app/views/admin/bookmarks/index.html.erb +21 -0
- data/app/views/admin/bookmarks/index.json.jbuilder +3 -0
- data/app/views/admin/bulk_actions/_bulk_action.json.jbuilder +4 -0
- data/app/views/admin/bulk_actions/_form.html.erb +17 -0
- data/app/views/admin/bulk_actions/edit.html.erb +6 -0
- data/app/views/admin/bulk_actions/index.html.erb +54 -0
- data/app/views/admin/bulk_actions/index.json.jbuilder +3 -0
- data/app/views/admin/bulk_actions/new.html.erb +5 -0
- data/app/views/admin/bulk_actions/show.html.erb +100 -0
- data/app/views/admin/bulk_actions/show.json.jbuilder +3 -0
- data/app/views/admin/devise/confirmations/new.html.erb +16 -0
- data/app/views/admin/devise/invitations/edit.html.erb +15 -0
- data/app/views/admin/devise/invitations/new.html.erb +15 -0
- data/app/views/admin/devise/mailer/invitation_instructions.html.erb +11 -0
- data/app/views/admin/devise/mailer/invitation_instructions.text.erb +11 -0
- data/app/views/admin/devise/passwords/edit.html.erb +26 -0
- data/app/views/admin/devise/passwords/new.html.erb +16 -0
- data/app/views/admin/devise/registrations/edit.html.erb +35 -0
- data/app/views/admin/devise/registrations/new.html.erb +30 -0
- data/app/views/admin/devise/sessions/new.html.erb +28 -0
- data/app/views/admin/devise/shared/_links.html.erb +27 -0
- data/app/views/admin/devise/unlocks/new.html.erb +16 -0
- data/app/views/admin/document_accesses/_document_access.json.jbuilder +4 -0
- data/app/views/admin/document_accesses/_form.html.erb +17 -0
- data/app/views/admin/document_accesses/destroy_all.html.erb +70 -0
- data/app/views/admin/document_accesses/edit.html.erb +5 -0
- data/app/views/admin/document_accesses/import.html.erb +70 -0
- data/app/views/admin/document_accesses/index.html.erb +74 -0
- data/app/views/admin/document_accesses/index.json.jbuilder +3 -0
- data/app/views/admin/document_accesses/new.html.erb +6 -0
- data/app/views/admin/document_accesses/show.html.erb +19 -0
- data/app/views/admin/document_accesses/show.json.jbuilder +3 -0
- data/app/views/admin/document_assets/display_attach_form.html.erb +44 -0
- data/app/views/admin/document_assets/index.html.erb +65 -0
- data/app/views/admin/document_downloads/_document_download.json.jbuilder +4 -0
- data/app/views/admin/document_downloads/_form.html.erb +15 -0
- data/app/views/admin/document_downloads/destroy_all.html.erb +70 -0
- data/app/views/admin/document_downloads/edit.html.erb +6 -0
- data/app/views/admin/document_downloads/import.html.erb +74 -0
- data/app/views/admin/document_downloads/index.html.erb +70 -0
- data/app/views/admin/document_downloads/index.json.jbuilder +3 -0
- data/app/views/admin/document_downloads/new.html.erb +5 -0
- data/app/views/admin/document_downloads/show.html.erb +24 -0
- data/app/views/admin/document_downloads/show.json.jbuilder +3 -0
- data/app/views/admin/documents/_document.html.erb +29 -0
- data/app/views/admin/documents/_document_bookmark.html.erb +17 -0
- data/app/views/admin/documents/_document_fields.html.erb +24 -0
- data/app/views/admin/documents/_form.html.erb +49 -0
- data/app/views/admin/documents/_form_control.html.erb +86 -0
- data/app/views/admin/documents/_form_group.html.erb +1 -0
- data/app/views/admin/documents/_form_header.html.erb +1 -0
- data/app/views/admin/documents/_form_nav.html.erb +21 -0
- data/app/views/admin/documents/_json_aardvark.jbuilder +47 -0
- data/app/views/admin/documents/_json_btaa_aardvark.jbuilder +16 -0
- data/app/views/admin/documents/_json_gbl_v1.jbuilder +32 -0
- data/app/views/admin/documents/_result_facets.html.erb +35 -0
- data/app/views/admin/documents/_result_pagination.html.erb +21 -0
- data/app/views/admin/documents/_result_selected_options.html.erb +54 -0
- data/app/views/admin/documents/_result_selection_options.html.erb +10 -0
- data/app/views/admin/documents/_result_toggle.html.erb +13 -0
- data/app/views/admin/documents/edit.html.erb +7 -0
- data/app/views/admin/documents/features/_assets.html.erb +3 -0
- data/app/views/admin/documents/features/_institutional_access_links.html.erb +3 -0
- data/app/views/admin/documents/features/_multiple_download_links.html.erb +27 -0
- data/app/views/admin/documents/fetch.json_aardvark.jbuilder +9 -0
- data/app/views/admin/documents/fetch.json_btaa_aardvark.jbuilder +9 -0
- data/app/views/admin/documents/fetch.json_gbl_v1.jbuilder +9 -0
- data/app/views/admin/documents/index.html.erb +49 -0
- data/app/views/admin/documents/index.json_aardvark.jbuilder +9 -0
- data/app/views/admin/documents/index.json_btaa_aardvark.jbuilder +9 -0
- data/app/views/admin/documents/index.json_gbl_v1.jbuilder +9 -0
- data/app/views/admin/documents/show.json_aardvark.jbuilder +3 -0
- data/app/views/admin/documents/show.json_btaa_aardvark.jbuilder +3 -0
- data/app/views/admin/documents/show.json_gbl_v1.jbuilder +3 -0
- data/app/views/admin/documents/versions.html.erb +110 -0
- data/app/views/admin/elements/_element.json.jbuilder +2 -0
- data/app/views/admin/elements/_form.html.erb +45 -0
- data/app/views/admin/elements/edit.html.erb +6 -0
- data/app/views/admin/elements/index.html.erb +68 -0
- data/app/views/admin/elements/index.json.jbuilder +1 -0
- data/app/views/admin/elements/new.html.erb +5 -0
- data/app/views/admin/elements/show.html.erb +121 -0
- data/app/views/admin/elements/show.json.jbuilder +1 -0
- data/app/views/admin/form_elements/_form.html.erb +46 -0
- data/app/views/admin/form_elements/_form_element.json.jbuilder +2 -0
- data/app/views/admin/form_elements/edit.html.erb +6 -0
- data/app/views/admin/form_elements/index.html.erb +49 -0
- data/app/views/admin/form_elements/index.json.jbuilder +1 -0
- data/app/views/admin/form_elements/new.html.erb +5 -0
- data/app/views/admin/form_elements/show.html.erb +19 -0
- data/app/views/admin/form_elements/show.json.jbuilder +1 -0
- data/app/views/admin/ids/fetch.json.jbuilder +86 -0
- data/app/views/admin/ids/index.json.jbuilder +41 -0
- data/app/views/admin/import_documents/show.html.haml +20 -0
- data/app/views/admin/imports/_form.html.erb +27 -0
- data/app/views/admin/imports/_import.json.jbuilder +4 -0
- data/app/views/admin/imports/_show_failed_tab.html.erb +37 -0
- data/app/views/admin/imports/_show_success_tab.html.erb +31 -0
- data/app/views/admin/imports/edit.html.erb +8 -0
- data/app/views/admin/imports/index.html.erb +56 -0
- data/app/views/admin/imports/index.json.jbuilder +3 -0
- data/app/views/admin/imports/new.html.erb +7 -0
- data/app/views/admin/imports/show.html.erb +123 -0
- data/app/views/admin/imports/show.json.jbuilder +3 -0
- data/app/views/admin/layouts/application.html.erb +30 -0
- data/app/views/admin/layouts/blazer/application.html.erb +23 -0
- data/app/views/admin/layouts/mailer.html.erb +13 -0
- data/app/views/admin/layouts/mailer.text.erb +1 -0
- data/app/views/admin/mappings/_form.html.erb +17 -0
- data/app/views/admin/mappings/_mapping.json.jbuilder +4 -0
- data/app/views/admin/mappings/edit.html.erb +6 -0
- data/app/views/admin/mappings/index.html.erb +37 -0
- data/app/views/admin/mappings/index.json.jbuilder +3 -0
- data/app/views/admin/mappings/new.html.erb +5 -0
- data/app/views/admin/mappings/show.html.erb +27 -0
- data/app/views/admin/mappings/show.json.jbuilder +3 -0
- data/app/views/admin/notifications/_notification.html.haml +31 -0
- data/app/views/admin/notifications/index.html.erb +54 -0
- data/app/views/admin/notifications/update.js.erb +7 -0
- data/app/views/admin/search/index.html.erb +38 -0
- data/app/views/admin/shared/_flash_messages.html.erb +5 -0
- data/app/views/admin/shared/_footer.html.erb +56 -0
- data/app/views/admin/shared/_js_behaviors.html.erb +147 -0
- data/app/views/admin/shared/_navbar.html.erb +78 -0
- data/app/views/admin/shared/_navbar_blazer.html.erb +42 -0
- data/app/views/admin/shared/_schema_timestamp.html.erb +16 -0
- data/app/views/admin/shared/_toast.html.erb +13 -0
- data/app/views/admin/users/index.html.erb +21 -0
- data/config/authorities/formats.yml +25 -0
- data/config/authorities/languages.yml +50 -0
- data/config/authorities/resource_type.yml +82 -0
- data/config/blazer.yml +79 -0
- data/config/gbl_v1_schema.json +118 -0
- data/config/geomg_aardvark_schema.json +211 -0
- data/config/geomg_schema.json +123 -0
- data/config/locales/devise.en.yml +65 -0
- data/config/locales/devise_invitable.en.yml +31 -0
- data/config/locales/documents.en.yml +189 -0
- data/config/locales/en.yml +51 -0
- data/config/locales/geoblacklight.en.yml +178 -0
- data/config/locales/simple_form.en.yml +31 -0
- data/config/routes.rb +162 -0
- data/db/migrate/20230316183001_add_geoblacklight_admin.rb +399 -0
- data/db/seeds.rb +26 -0
- data/db/seeds_elements.csv +56 -0
- data/db/seeds_elements.numbers +0 -0
- data/db/seeds_form_elements.csv +71 -0
- data/db/seeds_form_elements.numbers +0 -0
- data/db/structure.sql +816 -0
- data/geoblacklight_admin.gemspec +67 -0
- data/lib/generators/geoblacklight_admin/config_generator.rb +336 -0
- data/lib/generators/geoblacklight_admin/example_docs_generator.rb +18 -0
- data/lib/generators/geoblacklight_admin/helpers_generator.rb +18 -0
- data/lib/generators/geoblacklight_admin/install_generator.rb +60 -0
- data/lib/generators/geoblacklight_admin/jobs_generator.rb +21 -0
- data/lib/generators/geoblacklight_admin/models_generator.rb +23 -0
- data/lib/generators/geoblacklight_admin/templates/.env.development.example +28 -0
- data/lib/generators/geoblacklight_admin/templates/.solr_wrapper.yml +7 -0
- data/lib/generators/geoblacklight_admin/templates/_user_util_links.html.erb +30 -0
- data/lib/generators/geoblacklight_admin/templates/config/database.yml +18 -0
- data/lib/generators/geoblacklight_admin/templates/config/geomg_aardvark_schema.json +211 -0
- data/lib/generators/geoblacklight_admin/templates/config/initializers/geoblacklight_admin.rb +25 -0
- data/lib/generators/geoblacklight_admin/templates/config/initializers/kithe.rb +31 -0
- data/lib/generators/geoblacklight_admin/templates/config/initializers/pagy.rb +158 -0
- data/lib/generators/geoblacklight_admin/templates/config/initializers/statesman.rb +7 -0
- data/lib/generators/geoblacklight_admin/templates/javascript/channels/consumer.js +6 -0
- data/lib/generators/geoblacklight_admin/templates/javascript/channels/export_channel.js +30 -0
- data/lib/generators/geoblacklight_admin/templates/javascript/channels/index.js +5 -0
- data/lib/generators/geoblacklight_admin/templates/javascript/controllers/application_controller.js +17 -0
- data/lib/generators/geoblacklight_admin/templates/javascript/controllers/document_controller.js +26 -0
- data/lib/generators/geoblacklight_admin/templates/javascript/controllers/index.js +10 -0
- data/lib/generators/geoblacklight_admin/templates/javascript/controllers/results_controller.js +310 -0
- data/lib/generators/geoblacklight_admin/templates/javascript/packs/application.js +23 -0
- data/lib/generators/geoblacklight_admin/templates/javascript/src/js/admin/qa_autocomplete.js +1134 -0
- data/lib/generators/geoblacklight_admin/templates/package.json +32 -0
- data/lib/generators/geoblacklight_admin/templates/solr/README-geoblacklight-solr-development.txt +3 -0
- data/lib/generators/geoblacklight_admin/templates/solr/conf/_rest_managed.json +3 -0
- data/lib/generators/geoblacklight_admin/templates/solr/conf/admin-extra.html +31 -0
- data/lib/generators/geoblacklight_admin/templates/solr/conf/core.properties +5 -0
- data/lib/generators/geoblacklight_admin/templates/solr/conf/elevate.xml +36 -0
- data/lib/generators/geoblacklight_admin/templates/solr/conf/mapping-ISOLatin1Accent.txt +246 -0
- data/lib/generators/geoblacklight_admin/templates/solr/conf/protwords.txt +21 -0
- data/lib/generators/geoblacklight_admin/templates/solr/conf/schema.xml +229 -0
- data/lib/generators/geoblacklight_admin/templates/solr/conf/scripts.conf +24 -0
- data/lib/generators/geoblacklight_admin/templates/solr/conf/solrconfig.xml +266 -0
- data/lib/generators/geoblacklight_admin/templates/solr/conf/spellings.txt +2 -0
- data/lib/generators/geoblacklight_admin/templates/solr/conf/stopwords.txt +58 -0
- data/lib/generators/geoblacklight_admin/templates/solr/conf/stopwords_en.txt +58 -0
- data/lib/generators/geoblacklight_admin/templates/solr/conf/synonyms.txt +43 -0
- data/lib/generators/geoblacklight_admin/templates/solr/conf/xslt/example.xsl +132 -0
- data/lib/generators/geoblacklight_admin/templates/solr/conf/xslt/example_atom.xsl +67 -0
- data/lib/generators/geoblacklight_admin/templates/solr/conf/xslt/example_rss.xsl +66 -0
- data/lib/generators/geoblacklight_admin/templates/solr/conf/xslt/luke.xsl +337 -0
- data/lib/generators/geoblacklight_admin/templates/solr/snapshots/.keep +0 -0
- data/lib/generators/geoblacklight_admin/views_generator.rb +19 -0
- data/lib/geoblacklight_admin/engine.rb +20 -0
- data/lib/geoblacklight_admin/version.rb +5 -0
- data/lib/geoblacklight_admin.rb +7 -0
- data/lib/tasks/.keep +0 -0
- data/lib/tasks/geoblacklight_admin.rake +209 -0
- data/spec/test_app_templates/lib/generators/test_app_generator.rb +35 -0
- data/template.rb +16 -0
- 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, '&')
|
193
|
+
.replace(/</g, '<')
|
194
|
+
.replace(/>/g, '>')
|
195
|
+
.replace(/"/g, '"')
|
196
|
+
.replace(/<(\/?strong)>/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
|
+
});
|