X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/bd1f0b637be6c97374b31ed5c442ff88d25e626e..824680f81d5c5d243f49096be975568b786fac2a:/apps/workbench/app/assets/javascripts/filterable.js?ds=sidebyside
diff --git a/apps/workbench/app/assets/javascripts/filterable.js b/apps/workbench/app/assets/javascripts/filterable.js
index d14551cc9a..40df5c7174 100644
--- a/apps/workbench/app/assets/javascripts/filterable.js
+++ b/apps/workbench/app/assets/javascripts/filterable.js
@@ -1,5 +1,100 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+// filterable.js shows/hides content when the user operates
+// search/select widgets. For "infinite scroll" content, it passes the
+// filters to the server and retrieves new content. For other content,
+// it filters the existing DOM elements using jQuery show/hide.
+//
+// Usage:
+//
+// 1. Add the "filterable" class to each filterable content item.
+// Typically, each item is a 'tr' or a 'div class="row"'.
+//
+//
+//
First row
+//
Second row
+//
+//
+// 2. Add the "filterable-control" class to each search/select widget.
+// Also add a data-filterable-target attribute with a jQuery selector
+// for an ancestor of the filterable items, i.e., the container in
+// which this widget should apply filtering.
+//
+//
+//
+// Supported widgets:
+//
+//
+//
+// The input value is used as a regular expression. Rows with content
+// matching the regular expression are shown.
+//
+//
+//
+// When the user selects the "Foo" option, rows with
+// data-example-attr="foo" are shown, and all others are hidden. When
+// the user selects the "Show all" option, all rows are shown.
+//
+// Notes:
+//
+// When multiple filterable-control widgets operate on the same
+// data-filterable-target, items must pass _all_ filters in order to
+// be shown.
+//
+// If one data-filterable-target is the parent of another
+// data-filterable-target, results are undefined. Don't do this.
+//
+// Combining "select" filterable-controls with infinite-scroll is not
+// yet supported.
+
+function updateFilterableQueryNow($target) {
+ var newquery = $target.data('filterable-query-new');
+ var params = $target.data('infinite-content-params-filterable') || {};
+ var tsquery = to_tsquery(newquery);
+ if (tsquery == null) {
+ params.filters = [];
+ } else {
+ params.filters = [['any', '@@', tsquery]];
+ }
+ $(".modal-dialog-preview-pane").html("");
+ $target.data('infinite-content-params-filterable', params);
+ $target.data('filterable-query', newquery);
+}
+
$(document).
- on('paste keyup input', 'input[type=text].filterable-control', function() {
+ on('ready ajax:success', function() {
+ // Copy any initial input values into
+ // data-filterable-query[-new].
+ $('input[type=text].filterable-control').each(function() {
+ var $this = $(this);
+ var $target = $($this.attr('data-filterable-target'));
+ if ($target.data('filterable-query-new') === undefined) {
+ $target.data('filterable-query', $this.val());
+ $target.data('filterable-query-new', $this.val());
+ updateFilterableQueryNow($target);
+ }
+ });
+ $('[data-infinite-scroller]').on('refresh-content', '[data-filterable-query]', function(e) {
+ // If some other event causes a refresh-content event while there
+ // is a new query waiting to cooloff, we should use the new query
+ // right away -- otherwise we'd launch an extra ajax request that
+ // would have to be reloaded as soon as the cooloff period ends.
+ if (this != e.target)
+ return;
+ if ($(this).data('filterable-query') == $(this).data('filterable-query-new'))
+ return;
+ updateFilterableQueryNow($(this));
+ });
+ }).
+ on('paste keyup input', 'input[type=text].filterable-control', function(e) {
+ var regexp;
+ if (this != e.target) return;
var $target = $($(this).attr('data-filterable-target'));
var currentquery = $target.data('filterable-query');
if (currentquery === undefined) currentquery = '';
@@ -23,19 +118,26 @@ $(document).
// in the next 1/4 second (like type or erase
// characters in the search box), hide the stale
// content and ask the server for new results.
- var newquery = $target.data('filterable-query-new');
- var params = $target.data('infinite-content-params-filterable') || {};
- params.filters = [['any', 'ilike', '%' + newquery + '%']];
- $target.data('infinite-content-params-filterable', params);
- $target.data('filterable-query', newquery);
+ updateFilterableQueryNow($target);
$target.trigger('refresh-content');
}, 250));
} else {
// Target does not have infinite-scroll capability. Just
// filter the rows in the browser using a RegExp.
+ regexp = undefined;
+ try {
+ regexp = new RegExp($(this).val(), 'i');
+ } catch(e) {
+ if (e instanceof SyntaxError) {
+ // Invalid/partial regexp. See 'has-error' below.
+ } else {
+ throw e;
+ }
+ }
$target.
+ toggleClass('has-error', regexp === undefined).
addClass('filterable-container').
- data('q', new RegExp($(this).val(), 'i')).
+ data('q', regexp).
trigger('refresh');
}
}).on('refresh', '.filterable-container', function() {