diff --git a/.gitignore b/.gitignore index 664e6d7042..cfb5a7fb23 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ pkg/* .internal_test_app .vagrant /spec/examples.txt +node_modules/* diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000000..1bba312420 --- /dev/null +++ b/.npmignore @@ -0,0 +1,23 @@ +.* +Gemfile +Rakefile +Vagrantfile +app/assets/images +app/controllers +app/helpers +app/models +app/presenters +app/services +app/views +blacklight.gemspec +config/locales +config/routes.rb +coverage +db +lib +pkg +provision.sh +solr +spec +tasks +template.demo.rb diff --git a/README.md b/README.md index 999af6819f..8d3e745758 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ You can use Blacklight to enable searching and browsing of your collections. Blacklight uses the [Apache Solr](http://lucene.apache.org/solr) search engine to search full text and/or metadata. Blacklight has a highly configurable Ruby on Rails front-end. Blacklight was originally developed at -the University of Virginia Library and is made public under an Apache 2.0 license. +the University of Virginia Library and is made public under an Apache 2.0 license. ## Installation @@ -38,5 +38,81 @@ rails generate blacklight:install * Bundler * Rails 5.0+ -## Configuring Apache Solr +## Configuring Apache Solr You'll also want some information about how Blacklight expects [Apache Solr](http://lucene.apache.org/solr ) to run, which you can find in [README_SOLR](https://github.com/projectblacklight/blacklight/wiki/README_SOLR) + +## Building the javascript +The javascript is built by npm from sources in `app/javascript` into a bundle +in `app/assets/javascripts/blacklight/blacklight.js`. This file should not be edited +by hand as any changes would be overwritten. + +This is accomplished with the following steps: +1. Install npm +1. run `npm install` to download dependencies +1. run `npm run-script js-compile-bundle` to build the bundle +1. run `npm publish` to push the javascript package to https://npmjs.org/package/blacklight-frontend + +## Using the javascript +Install Webpacker +Add blacklight-frontend as a dependency by doing: +``` +yarn add blacklight-frontend +``` + +Then add these lines to `config/webpack/environment.js` as per https://getbootstrap.com/docs/4.0/getting-started/webpack/ +and https://github.com/rails/webpacker/blob/master/docs/webpack.md#plugins + +```js +const webpack = require('webpack') + +environment.plugins.set( + 'Provide', + new webpack.ProvidePlugin({ + $: 'jquery', + jQuery: 'jquery', + jquery: 'jquery', + 'window.jQuery': 'jquery', + Popper: ['popper.js', 'default'], + }) +) + +module.exports = environment +``` + +In you pack file (`app/javascript/packs/application.js`), require blacklight: +``` +require('blacklight-frontend/app/assets/javascripts/blacklight/blacklight') +``` +Then remove these requires from `app/assets/javascripts/application.js`: + +``` +//= require jquery +//= require popper +//= require twitter/typeahead +//= require bootstrap +``` + +Add the following to the app/views/layouts/blacklight/base.html.erb (maybe this can be simpler) +``` +<%= javascript_pack_tag 'application' %> +``` +You can probably remove the `<%= javascript_include_tag %>` + +### Using sprockets (not webpacker) + +If you want to use sprockets rather than webpacker, you must ensure these dependencies are in your Gemfile (done automatically by the install generator): + +``` +gem 'bootstrap', '~> 4.0' +gem 'popper_js' +gem 'twitter-typeahead-rails', '0.11.1.pre.corejavascript' +``` + +Then insure these requires are in `app/assets/javascripts/application.js` (done automatically by the install generator): + +``` +//= require jquery +//= require popper +//= require twitter/typeahead +//= require bootstrap +``` diff --git a/app/assets/javascripts/blacklight/blacklight.js b/app/assets/javascripts/blacklight/blacklight.js index 7a25620038..c138612539 100644 --- a/app/assets/javascripts/blacklight/blacklight.js +++ b/app/assets/javascripts/blacklight/blacklight.js @@ -1,61 +1,489 @@ -// This file is generated by Blacklight. You probably don't want to edit -// this file directly, or you'll have to manually merge your changes if later -// versions of Blacklight change this file. Instead, use your own JS file -// which over-rides things in this JS file, as described below. -// -// These javascript files are compiled in via the Rails asset pipeline: +Blacklight = function () { + var buffer = new Array(); + return { + onLoad: function (func) { + buffer.push(func); + }, + + activate: function () { + for (var i = 0; i < buffer.length; i++) { + buffer[i].call(); + } + }, + + listeners: function () { + var listeners = []; + if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) { + // Turbolinks 5 + if (Turbolinks.BrowserAdapter) { + listeners.push('turbolinks:load'); + } else { + // Turbolinks < 5 + listeners.push('page:load', 'ready'); + } + } else { + listeners.push('ready'); + } + + return listeners.join(' '); + } + }; +}(); + +// turbolinks triggers page:load events on page transition +// If app isn't using turbolinks, this event will never be triggered, no prob. +$(document).on(Blacklight.listeners(), function () { + Blacklight.activate(); +}); + +$('.no-js').removeClass('no-js').addClass('js'); +/*global Bloodhound */ + +Blacklight.onLoad(function () { + 'use strict'; + + $('[data-autocomplete-enabled="true"]').each(function () { + var $el = $(this); + if ($el.hasClass('tt-hint')) { + return; + } + var suggestUrl = $el.data().autocompletePath; + + var terms = new Bloodhound({ + datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'), + queryTokenizer: Bloodhound.tokenizers.whitespace, + remote: { + url: suggestUrl + '?q=%QUERY', + wildcard: '%QUERY' + } + }); + + terms.initialize(); + + $el.typeahead({ + hint: true, + highlight: true, + minLength: 2 + }, { + name: 'terms', + displayKey: 'term', + source: terms.ttAdapter() + }); + }); +}); +(function ($) { + //change form submit toggle to checkbox + Blacklight.do_bookmark_toggle_behavior = function () { + $(Blacklight.do_bookmark_toggle_behavior.selector).bl_checkbox_submit({ + // css_class is added to elements added, plus used for id base + css_class: "toggle-bookmark", + success: function (checked, response) { + if (response.bookmarks) { + $('[data-role=bookmark-counter]').text(response.bookmarks.count); + } + } + }); + }; + Blacklight.do_bookmark_toggle_behavior.selector = "form.bookmark-toggle"; + + Blacklight.onLoad(function () { + Blacklight.do_bookmark_toggle_behavior(); + }); +})(jQuery); +/* A JQuery plugin (should this be implemented as a widget instead? not sure) + that will convert a "toggle" form, with single submit button to add/remove + something, like used for Bookmarks, into an AJAXy checkbox instead. + + Apply to a form. Does require certain assumption about the form: + 1) The same form 'action' href must be used for both ADD and REMOVE + actions, with the different being the hidden input name="_method" + being set to "put" or "delete" -- that's the Rails method to pretend + to be doing a certain HTTP verb. So same URL, PUT to add, DELETE + to remove. This plugin assumes that. + + Plus, the form this is applied to should provide a data-doc-id + attribute (HTML5-style doc-*) that contains the id/primary key + of the object in question -- used by plugin for a unique value for + DOM id's. + + Uses HTML for a checkbox compatible with Bootstrap 3. + + Pass in options for your class name and labels: + $("form.something").bl_checkbox_submit({ + checked_label: "Selected", + unchecked_label: "Select", + progress_label: "Saving...", + //css_class is added to elements added, plus used for id base + css_class: "toggle_my_kinda_form", + success: function(after_success_check_state) { + #optional callback + } + }); +*/ +(function ($) { + $.fn.bl_checkbox_submit = function (arg_opts) { + + this.each(function () { + var options = $.extend({}, $.fn.bl_checkbox_submit.defaults, arg_opts); + + var form = $(this); + form.children().hide(); + //We're going to use the existing form to actually send our add/removes + //This works conveneintly because the exact same action href is used + //for both bookmarks/$doc_id. But let's take out the irrelevant parts + //of the form to avoid any future confusion. + form.find("input[type=submit]").remove(); + + //View needs to set data-doc-id so we know a unique value + //for making DOM id + var unique_id = form.attr("data-doc-id") || Math.random(); + // if form is currently using method delete to change state, + // then checkbox is currently checked + var checked = form.find("input[name=_method][value=delete]").size() != 0; + + var checkbox = $('').addClass(options.css_class).attr("id", options.css_class + "_" + unique_id); + var label = $('