~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/app/javascript/picker/picker.js

Merged bug-824435-failure-reporting-2 into bug-824435-failure-reporting-3.

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
    C_SEARCH_SLOT = getCN(PICKER, 'search-slot'),
32
32
    C_FOOTER_SLOT = getCN(PICKER, 'footer-slot'),
33
33
    C_SEARCH_MODE = getCN(PICKER, 'search-mode'),
 
34
    C_FILTER = getCN(PICKER, 'filter'),
34
35
    C_RESULTS = getCN(PICKER, 'results'),
35
36
    C_RESULT_TITLE = getCN(PICKER, 'result-title'),
36
37
    C_RESULT_DESCRIPTION = getCN(PICKER, 'result-description'),
60
61
    BINDUI = "bindUI",
61
62
    ASSOCIATED_FIELD_ID = 'associated_field_id',
62
63
    SELECTED_VALUE = 'selected_value',
63
 
    SELECTED_VALUE_METADATA = 'selected_value_metadata';
 
64
    SELECTED_VALUE_METADATA = 'selected_value_metadata',
 
65
    FILTER_OPTIONS = 'filter_options',
 
66
    CURRENT_FILTER_VALUE = 'current_filter_value';
64
67
 
65
68
 
66
69
var Picker = function () {
91
94
    _search_button: null,
92
95
 
93
96
    /**
 
97
     * The node containing filter options.
 
98
     *
 
99
     * @property _filter_box
 
100
     * @type Node
 
101
     * @private
 
102
     */
 
103
    _filter_box: null,
 
104
 
 
105
    /**
94
106
     * The node containing search results.
95
107
     *
96
108
     * @property _results_box
200
212
            if (Y.Lang.isValue(cfg[SELECTED_VALUE])) {
201
213
                this.set(SELECTED_VALUE, cfg[SELECTED_VALUE]);
202
214
            }
 
215
            // Optional filter support
 
216
            if (Y.Lang.isValue(cfg[FILTER_OPTIONS])) {
 
217
                this.set(FILTER_OPTIONS, cfg[FILTER_OPTIONS]);
 
218
                if (Y.Lang.isValue(cfg[CURRENT_FILTER_VALUE])) {
 
219
                    this.set(CURRENT_FILTER_VALUE, cfg[CURRENT_FILTER_VALUE]);
 
220
                }
 
221
            }
203
222
        }
204
223
    },
205
224
 
422
441
        if (data.badges) {
423
442
            var badges = Y.Node.create('<div>Affiliation:</div>')
424
443
                .addClass('badge');
425
 
            var already_processed = new Array();
 
444
            var already_processed = [];
426
445
            Y.each(data.badges, function(badge_info) {
427
446
                var badge_url = badge_info.url;
428
447
                var badge_alt = badge_info.alt;
532
551
        // Remove any previous results.
533
552
        Y.Event.purgeElement(this._results_box, true);
534
553
        this._results_box.set('innerHTML', '');
 
554
        this._filter_box.set('innerHTML', '');
535
555
 
536
556
        var expander_id = this.get(BOUNDING_BOX).get('id');
537
557
        Y.Array.each(results, function(data, i) {
586
606
                    {query: this._search_input.get('value')})));
587
607
            this._results_box.appendChild(msg);
588
608
            this._results_box.addClass(C_NO_RESULTS);
 
609
            this._syncFilterUI();
589
610
        } else {
590
611
            this._results_box.removeClass(C_NO_RESULTS);
 
612
            if (results.length) {
 
613
                var filters = this.get(FILTER_OPTIONS);
 
614
                var current_filter_value = this.get(CURRENT_FILTER_VALUE);
 
615
                if (filters.length  > 0 &&
 
616
                        !Y.Lang.isValue(current_filter_value)) {
 
617
                    this.set(CURRENT_FILTER_VALUE, filters[0].title);
 
618
                }
 
619
                this._syncFilterUI();
 
620
            }
591
621
        }
592
622
 
593
623
        if (results.length) {
600
630
    },
601
631
 
602
632
    /**
 
633
     * Update the filter UI based on the current filter value used for the
 
634
     * search.
 
635
     */
 
636
    _syncFilterUI: function() {
 
637
        // Check that we need to display the filter UI.
 
638
        var filters = this.get(FILTER_OPTIONS);
 
639
        if( filters.length === 0 ) {
 
640
            return;
 
641
        }
 
642
        var current_filter_value = this.get(CURRENT_FILTER_VALUE);
 
643
        if (!Y.Lang.isValue(current_filter_value)) {
 
644
            return;
 
645
        }
 
646
 
 
647
        var filter_msg = Y.substitute(
 
648
            'Showing <strong>{filter}</strong> matches for "{search_terms}".',
 
649
            {filter: current_filter_value,
 
650
            search_terms: this._search_input.get('value')});
 
651
        this._filter_box.appendChild(Y.Node.create(filter_msg));
 
652
 
 
653
        var filter_node = Y.Node.create('<div>Filter by:&nbsp;</div>');
 
654
        var picker = this;
 
655
        Y.Array.each(filters, function(filter, i) {
 
656
            var filter_link = Y.Node.create('<a></a>')
 
657
                .set('href', '#')
 
658
                .set('text', filter.title)
 
659
                .set('title', filter.description);
 
660
            if( filter.title === current_filter_value) {
 
661
                filter_link.addClass('invalid-link');
 
662
            } else {
 
663
                filter_link.addClass('js-action');
 
664
                // When a filter link is clicked, we simply fire off a search
 
665
                // event.
 
666
                filter_link.on('click', function (e) {
 
667
                    e.halt();
 
668
                    picker.set(CURRENT_FILTER_VALUE, filter.title);
 
669
                    var search_string = Y.Lang.trim(
 
670
                        picker._search_input.get('value'));
 
671
                    picker._performSearch(search_string, filter.name);
 
672
                });
 
673
            }
 
674
            filter_node.append(filter_link);
 
675
            if (i < filters.length - 2) {
 
676
                filter_node.append(Y.Node.create(',&nbsp;'));
 
677
            } else if (i === filters.length - 2) {
 
678
                filter_node.append(Y.Node.create(',&nbsp;or&nbsp;'));
 
679
            }
 
680
        });
 
681
        this._filter_box.appendChild(filter_node);
 
682
    },
 
683
 
 
684
    /**
603
685
     * Sync UI with search mode. Disable the search input and button.
604
686
     *
605
687
     * @method _syncSearchModeUI
671
753
        this._search_slot_box.addClass(C_SEARCH_SLOT);
672
754
        search_box.appendChild(this._search_slot_box);
673
755
 
 
756
        this._filter_box = Y.Node.create('<div></div>');
 
757
        this._filter_box.addClass(C_FILTER);
 
758
 
674
759
        this._results_box = Y.Node.create('<ul></ul>');
675
760
        this._results_box.addClass(C_RESULTS);
676
761
 
682
767
 
683
768
        var body = Y.Node.create('<div></div>');
684
769
        body.appendChild(search_box);
 
770
        body.appendChild(this._filter_box);
685
771
        body.appendChild(this._results_box);
686
772
        body.appendChild(this._batches_box);
687
773
        body.appendChild(this._footer_slot_box);
801
887
        this.set(BATCHES, null);
802
888
        this.set(BATCH_COUNT, null);
803
889
        this.set(SELECTED_BATCH, 0);
 
890
        this.set(CURRENT_FILTER_VALUE, null);
804
891
        this._search_input.set('value', '');
805
892
        this._results_box.set('innerHTML', '');
 
893
        this._filter_box.set('innerHTML', '');
806
894
    },
807
895
 
808
896
    /**
815
903
     */
816
904
    _defaultSearchUserAction: function(e) {
817
905
        e.preventDefault();
 
906
        this.set(CURRENT_FILTER_VALUE, null);
818
907
        var search_string = Y.Lang.trim(this._search_input.get('value'));
 
908
        this._performSearch(search_string);
 
909
    },
 
910
 
 
911
    /**
 
912
     * Fires the search event after checking the search string and reseting
 
913
     * the relevant picker data.
 
914
     * search event.
 
915
     * @param search_string The search term.
 
916
     * @param filter_name The name of a filter to use to limit the results.
 
917
     */
 
918
    _performSearch: function(search_string, filter_name) {
819
919
        if (search_string.length < this.get(MIN_SEARCH_CHARS)) {
820
920
            var msg =  Y.substitute(
821
921
                "Please enter at least {min} characters.",
828
928
                this.set(SELECTED_BATCH, 0);
829
929
            }
830
930
            this.set(CURRENT_SEARCH_STRING, search_string);
831
 
            this.fire(SEARCH, search_string);
 
931
            this.fire(SEARCH, search_string, undefined, undefined,
 
932
                filter_name);
832
933
        }
833
934
    },
834
935
 
978
1079
    current_search_string: {value: ''},
979
1080
 
980
1081
    /**
 
1082
     * The string representation of the current filter.
 
1083
     *
 
1084
     * @attribute current_filter_value
 
1085
     * @type String
 
1086
     */
 
1087
    current_filter_value: {value: null},
 
1088
 
 
1089
    /**
 
1090
     * A list of attribute name values used to construct the filtering options
 
1091
     * for this picker..
 
1092
     *
 
1093
     * @attribute filter_options
 
1094
     * @type Object
 
1095
     */
 
1096
    filter_options: {value: []},
 
1097
 
 
1098
    /**
981
1099
     * The string representation of the value selected by using this picker.
982
1100
     *
983
1101
     * @attribute selected_value