IN / NOT IN for filter somewhat working, but doesn't save/load yet
authorLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Tue, 25 Sep 2012 20:02:26 +0000 (16:02 -0400)
committerLebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Tue, 25 Sep 2012 20:02:26 +0000 (16:02 -0400)
Signed-off-by: Lebbeous Fogle-Weekley <lebbeous@esilibrary.com>
Open-ILS/web/js/dojo/openils/widget/PCrudFilterPane.js
Open-ILS/web/js/dojo/openils/widget/nls/PCrudFilterPane.js

index e19099a..ef24865 100644 (file)
@@ -101,6 +101,18 @@ if (!dojo._hasResource['openils.widget.PCrudFilterPane']) {
                         "param_count": 1,
                         "strict": true
                     }, {
+                        "name": "in",
+                        "label": pcFilterLocaleStrings.OPERATOR_IN,
+                        "param_count": null,    /* arbitrary number, special */
+                        "strict": true,
+                        "minimal": true
+                    }, {
+                        "name": "not in",
+                        "label": pcFilterLocaleStrings.OPERATOR_NOT_IN,
+                        "param_count": null,    /* arbitrary number, special */
+                        "strict": true,
+                        "minimal": true
+                    }, {
                         "name": "between",
                         "label": pcFilterLocaleStrings.OPERATOR_BETWEEN,
                         "param_count": 2,
@@ -153,7 +165,7 @@ if (!dojo._hasResource['openils.widget.PCrudFilterPane']) {
 
         var ops = openils.Util.objectProperties(clause);
         var op = ops.pop();
-        var matches = op.match(/^not (\w+)$/);
+        var matches = op.match(/^not [lb].+$/); /* "not in" needs no change */
         if (matches) {
             clause[matches[1]] = clause[op];
             delete clause[op];
@@ -162,6 +174,30 @@ if (!dojo._hasResource['openils.widget.PCrudFilterPane']) {
         return false;
     }
 
+    /* Given a value, add it to selector options if it's not already there,
+     * and select it. */
+    function _add_or_at_least_select(value, selector) {
+        var found = false;
+
+        for (var i = 0; i < selector.options.length; i++) {
+            var option = selector.options[i];
+            if (option.value == value) {
+                found = true;
+                option.selected = true;
+            }
+        }
+
+        if (!found) {
+            dojo.create(
+                "option", {
+                    "innerHTML": value,
+                    "value": value,
+                    "selected": "selected"
+                }, selector
+            );
+        }
+    }
+
     /* This is not the dijit per se. Search further in this file for
      * "dojo.declare" for the beginning of the dijit.
      *
@@ -472,12 +508,12 @@ if (!dojo._hasResource['openils.widget.PCrudFilterPane']) {
         };
 
         this._create_value_slot = function(use_element) {
+            var how = {"innerHTML": "-"};
+
             if (use_element)
-                this.value_slot = dojo.create(
-                    "span", {"innerHTML": "-"}, use_element
-                );
+                this.value_slot = dojo.create("span", how, use_element);
             else
-                this.value_slot = dojo.create("td",{"innerHTML":"-"},this.tr);
+                this.value_slot = dojo.create("td", how, this.tr);
         };
 
         this._create_remover = function(use_element) {
@@ -497,7 +533,10 @@ if (!dojo._hasResource['openils.widget.PCrudFilterPane']) {
         this._clear_value_slot = function() {
             if (this.value_widgets) {
                 this.value_widgets.forEach(
-                    function(autowidg) { autowidg.widget.destroy(); }
+                    function(autowidg) {
+                        if (autowidg.widget)
+                            autowidg.widget.destroy();
+                    }
                 );
                 delete this.value_widgets;
             }
@@ -513,29 +552,100 @@ if (!dojo._hasResource['openils.widget.PCrudFilterPane']) {
 
             this.value_widgets = [];
 
-            var param_count = this.operator_selector.item.param_count;
-
-            /* This is where find and deploy custom widget builders. */
+            /* This is where find custom widget builders to deploy shortly. */
             var widget_builder_key = this.selected_field_fm_class + ":" +
                 this.selected_field_fm_field;
             var constr =
                 this.filter_row_manager.widget_builders[widget_builder_key] ||
                 openils.widget.AutoFieldWidget;
 
-            for (var i = 0; i < param_count; i++) {
-                var widg = new constr({
-                    "fmClass": this.selected_field_fm_class,
-                    "fmField": this.selected_field_fm_field,
-                    "noDisablePkey": true,
-                    "parentNode": dojo.create("span", {}, this.value_slot),
-                    "dijitArgs": {"scrollOnFocus": false}
-                });
+            /* How many value widgets do we need for this operator? */
+            var param_count =
+                this.operator_selector.store.getValue(
+                    this.operator_selector.item, "param_count"
+                );
 
-                widg.build();
-                this.value_widgets.push(widg);
+            if (param_count === null) {
+                /* When param_count is null, we invoke the special case of
+                 * preparing widgets for building a dynamic set of values.
+                 * All other cases are handled by the else branch. */
+                this._build_set_value_widgets(constr);
+            } else {
+                for (var i = 0; i < param_count; i++) {
+                    this.value_widgets.push(
+                        this._build_one_value_widget(constr)
+                    );
+                }
             }
         };
 
+        this._build_set_value_widgets = function(constr) {
+            var value_widget = dojo.create(
+                "select", {
+                    "multiple": "multiple",
+                    "size": 4,
+                    "style": {
+                        "width": "6em",
+                        "verticalAlign": "middle",
+                        "margin": "0 0.75em"
+                    }
+                },
+                this.value_slot
+            );
+            var entry_widget = this._build_one_value_widget(constr);
+            var adder = dojo.create(
+                "a", {
+                    "href": "javascript:void(0);",
+                    "style": {"verticalAlign": "middle", "margin": "0 0.75em"},
+                    "innerHTML": "[+]", /* XXX i18n? */
+                    "onclick": dojo.hitch(this, function() {
+                        _add_or_at_least_select(
+                            this._value_of_widget(entry_widget),
+                            value_widget
+                        );
+                        entry_widget.widget.attr("value", ""); /* clear */
+                    })
+                }, this.value_slot
+            );
+            this.value_widgets.push(value_widget);
+        };
+
+
+        /* Create just one value widget (used by higher-level functions
+         * that worry about how many are needed). */
+        this._build_one_value_widget = function(constr) {
+            var widg = new constr({
+                "fmClass": this.selected_field_fm_class,
+                "fmField": this.selected_field_fm_field,
+                "noDisablePkey": true,
+                "parentNode": dojo.create(
+                    "span", {
+                        "style": {"verticalAlign": "middle"}
+                    }, this.value_slot
+                ),
+                "dijitArgs": {"scrollOnFocus": false}
+            });
+
+            widg.build();
+            return widg;
+        };
+
+        this._value_of_widget = function(widg) {
+            if (!widg.widget)   /* widg is <select> */
+                return dojo.filter(
+                    widg.options,
+                    function(o) { return o.selected; }
+                ).map(
+                    function(o) { return o.value; }
+                );
+            else if (widg.useCorrectly)
+                return widg.widget.attr("value");
+            else if (this.selected_field_is_indirect)
+                return widg.widget.attr("displayedValue");
+            else
+                return widg.getFormattedValue();
+        }
+
         /* for ugly special cases in compilation */
         this._null_clause = function() {
             var opname = this.get_selected_operator_name();
@@ -558,10 +668,14 @@ if (!dojo._hasResource['openils.widget.PCrudFilterPane']) {
         };
 
         this.get_selected_operator_name = function() {
-            var op = this.get_selected_operator();
-            return op ?
-                (dojo.isArray(op.name) ? op.name.shift() : op.name) :
-                null;
+            var item = this.get_selected_operator();
+            if (item)
+                return this.operator_selector.store.getValue(item, "name")
+            else
+                console.warn(
+                    "Could not determine selected operator. " +
+                    "Something is about to break."
+                );
         };
 
         this.update_selected_operator = function(value) {
@@ -617,14 +731,7 @@ if (!dojo._hasResource['openils.widget.PCrudFilterPane']) {
         this.compile = function() {
             if (this.value_widgets) {
                 var values = this.value_widgets.map(
-                    function(widg) {
-                        if (widg.useCorrectly)
-                            return widg.widget.attr("value");
-                        else if (self.selected_field_is_indirect)
-                            return widg.widget.attr("displayedValue");
-                        else
-                            return widg.getFormattedValue();
-                    }
+                    dojo.hitch(this, this._value_of_widget)
                 );
 
                 if (!values.length) {
@@ -633,7 +740,13 @@ if (!dojo._hasResource['openils.widget.PCrudFilterPane']) {
                     var clause = {};
                     var op = this.get_selected_operator_name();
 
-                    var prep_function = function(o) { return o; /* no-op */ };
+                    var prep_function = function(o) {
+                        if (dojo.isArray(o) && !o.length)
+                            throw new Error(pcFilterLocaleStrings.EMPTY_LIST);
+
+                        return o;
+                    };
+
                     if (String(op).match(/like/))
                         prep_function = this._add_like_wildcards;
 
index f86ba33..761c1d3 100644 (file)
@@ -7,6 +7,8 @@
     "OPERATOR_GT": "is greater than",
     "OPERATOR_LTE": "is less than or equal to",
     "OPERATOR_GTE": "is greater than or equal to",
+    "OPERATOR_IN": "is in the set",
+    "OPERATOR_NOT_IN": "is not in the set",
     "OPERATOR_BETWEEN": "is between",
     "OPERATOR_NOT_BETWEEN": "is not between",
     "OPERATOR_LIKE": "is like",
@@ -21,5 +23,6 @@
     "SAVE_FILTERS": "Save Filters",
     "CHOOSE_FILTER_TO_LOAD": "Choose filter sets to load",
     "NAME_SAVED_FILTER_SET": "Enter a name for your saved filter set:",
-    "NEED_NAME": "You must enter a name for the saved filters."
+    "NEED_NAME": "You must enter a name for the saved filters.",
+    "EMPTY_LIST": "Cannot compile search filter.  Empty lists not allowed."
 }