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..3bebec1bf8 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,43 @@ 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](https://www.npmjs.com/get-npm) +1. run `npm install` to download dependencies +1. run `npm run 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 +Blacklight ships with Javascript that can be compiled either by webpacker or by +sprockets. To use Webpacker see the directions at https://github.com/projectblacklight/blacklight/wiki/Using-Webpacker-to-compile-javascript-assets + + +### Using sprockets (not Webpacker) + +If you want to use sprockets rather than Webpacker, you must ensure these +dependencies are in your Gemfile. The Blacklight install generator does this for +you: + +``` +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..585cbe85f9 100644 --- a/app/assets/javascripts/blacklight/blacklight.js +++ b/app/assets/javascripts/blacklight/blacklight.js @@ -1,61 +1,499 @@ -// 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.doBookmarkToggleBehavior = function () { + if (typeof Blacklight.do_bookmark_toggle_behavior == 'function') { + console.warn("do_bookmark_toggle_behavior is deprecated. Use doBookmarkToggleBehavior instead."); + return Blacklight.do_bookmark_toggle_behavior(); + } + $(Blacklight.doBookmarkToggleBehavior.selector).blCheckboxSubmit({ + // cssClass is added to elements added, plus used for id base + cssClass: 'toggle-bookmark', + success: function (checked, response) { + if (response.bookmarks) { + $('[data-role=bookmark-counter]').text(response.bookmarks.count); + } + } + }); + }; + Blacklight.doBookmarkToggleBehavior.selector = 'form.bookmark-toggle'; + + Blacklight.onLoad(function () { + Blacklight.doBookmarkToggleBehavior(); + }); +})(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").blCheckboxSubmit({ + checked_label: "Selected", + unchecked_label: "Select", + progress_label: "Saving...", + //cssClass is added to elements added, plus used for id base + cssClass: "toggle_my_kinda_form", + success: function(after_success_check_state) { + #optional callback + } + }); +*/ +(function ($) { + $.fn.blCheckboxSubmit = function (argOpts) { + this.each(function () { + var options = $.extend({}, $.fn.blCheckboxSubmit.defaults, argOpts); + + 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 uniqueId = 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.cssClass).attr('id', options.cssClass + '_' + uniqueId); + var label = $('