mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-03 21:43:21 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			225 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			225 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
// Exported for unit testing
 | 
						|
exports.is_using_input_method = false;
 | 
						|
 | 
						|
exports.narrow_or_search_for_term = function (search_string) {
 | 
						|
    const search_query_box = $("#search_query");
 | 
						|
    if (exports.is_using_input_method) {
 | 
						|
        // Neither narrow nor search when using input tools as
 | 
						|
        // `updater` is also triggered when 'enter' is triggered
 | 
						|
        // while using input tool
 | 
						|
        return search_query_box.val();
 | 
						|
    }
 | 
						|
    ui_util.change_tab_to('#home');
 | 
						|
 | 
						|
    let operators;
 | 
						|
    if (page_params.search_pills_enabled) {
 | 
						|
        // We have to take care to append the new pill before calling this
 | 
						|
        // function, so that the base_query includes the suggestion selected
 | 
						|
        // along with query corresponding to the existing pills.
 | 
						|
        const base_query = search_pill.get_search_string_for_current_filter(
 | 
						|
            search_pill_widget.widget);
 | 
						|
        operators = Filter.parse(base_query);
 | 
						|
    } else {
 | 
						|
        operators = Filter.parse(search_string);
 | 
						|
    }
 | 
						|
    narrow.activate(operators, {trigger: 'search'});
 | 
						|
 | 
						|
    // It's sort of annoying that this is not in a position to
 | 
						|
    // blur the search box, because it means that Esc won't
 | 
						|
    // unnarrow, it'll leave the searchbox.
 | 
						|
 | 
						|
    // Narrowing will have already put some operators in the search box,
 | 
						|
    // so leave the current text in.
 | 
						|
    if (!page_params.search_pills_enabled) {
 | 
						|
        search_query_box.blur();
 | 
						|
    }
 | 
						|
    return search_query_box.val();
 | 
						|
};
 | 
						|
 | 
						|
function update_buttons_with_focus(focused) {
 | 
						|
    const search_query_box = $('#search_query');
 | 
						|
 | 
						|
    // Show buttons iff the search input is focused, or has non-empty contents,
 | 
						|
    // or we are narrowed.
 | 
						|
    if (focused
 | 
						|
        || search_query_box.val()
 | 
						|
        || narrow_state.active()) {
 | 
						|
        $('.search_button').prop('disabled', false);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
exports.update_button_visibility = function () {
 | 
						|
    update_buttons_with_focus($('#search_query').is(':focus'));
 | 
						|
};
 | 
						|
 | 
						|
exports.initialize = function () {
 | 
						|
    const search_query_box = $('#search_query');
 | 
						|
    const searchbox_form = $('#searchbox_form');
 | 
						|
    const searchbox = $('#searchbox');
 | 
						|
 | 
						|
    // Data storage for the typeahead.
 | 
						|
    // This maps a search string to an object with a "description" field.
 | 
						|
    // (It's a bit of legacy that we have an object with only one important
 | 
						|
    // field.  There's also a "search_string" field on each element that actually
 | 
						|
    // just represents the key of the hash, so it's redundant.)
 | 
						|
    let search_map = new Map();
 | 
						|
 | 
						|
    search_query_box.typeahead({
 | 
						|
        source: function (query) {
 | 
						|
            let base_query = '';
 | 
						|
            if (page_params.search_pills_enabled) {
 | 
						|
                base_query = search_pill.get_search_string_for_current_filter(
 | 
						|
                    search_pill_widget.widget);
 | 
						|
            }
 | 
						|
            const suggestions = search_suggestion.get_suggestions(base_query, query);
 | 
						|
            // Update our global search_map hash
 | 
						|
            search_map = suggestions.lookup_table;
 | 
						|
            return suggestions.strings;
 | 
						|
        },
 | 
						|
        fixed: true,
 | 
						|
        items: search_suggestion.max_num_of_search_results,
 | 
						|
        helpOnEmptyStrings: true,
 | 
						|
        naturalSearch: true,
 | 
						|
        highlighter: function (item) {
 | 
						|
            const obj = search_map.get(item);
 | 
						|
            return obj.description;
 | 
						|
        },
 | 
						|
        matcher: function () {
 | 
						|
            return true;
 | 
						|
        },
 | 
						|
        updater: function (search_string) {
 | 
						|
            if (page_params.search_pills_enabled) {
 | 
						|
                search_pill.append_search_string(search_string,
 | 
						|
                                                 search_pill_widget.widget);
 | 
						|
                if (search_query_box.is(':focus')) {
 | 
						|
                    // We usually allow the user to continue
 | 
						|
                    // typing until the enter key is pressed.
 | 
						|
                    // But we narrow when the user clicks on a
 | 
						|
                    // typeahead suggestion. This change in behaviour
 | 
						|
                    // is a workaround to be able to display the
 | 
						|
                    // navbar every time search_query_box loses focus.
 | 
						|
                    return search_query_box.val();
 | 
						|
                }
 | 
						|
            }
 | 
						|
            return exports.narrow_or_search_for_term(search_string);
 | 
						|
        },
 | 
						|
        sorter: function (items) {
 | 
						|
            return items;
 | 
						|
        },
 | 
						|
        stopAdvance: page_params.search_pills_enabled,
 | 
						|
        advanceKeyCodes: [8],
 | 
						|
 | 
						|
        on_move: function () {
 | 
						|
            if (page_params.search_pills_enabled) {
 | 
						|
                ui_util.place_caret_at_end(search_query_box[0]);
 | 
						|
                return true;
 | 
						|
            }
 | 
						|
        },
 | 
						|
        // Use our custom typeahead `on_escape` hook to exit
 | 
						|
        // the search bar as soon as the user hits Esc.
 | 
						|
        on_escape: tab_bar.exit_search,
 | 
						|
    });
 | 
						|
 | 
						|
    searchbox_form.on('compositionend', () => {
 | 
						|
        // Set `is_using_input_method` to true if enter is pressed to exit
 | 
						|
        // the input tool popover and get the text in the search bar. Then
 | 
						|
        // we suppress searching triggered by this enter key by checking
 | 
						|
        // `is_using_input_method` before searching.
 | 
						|
        // More details in the commit message that added this line.
 | 
						|
        exports.is_using_input_method = true;
 | 
						|
    });
 | 
						|
 | 
						|
    searchbox_form.keydown((e) => {
 | 
						|
        exports.update_button_visibility();
 | 
						|
        const code = e.which;
 | 
						|
        if (code === 13 && search_query_box.is(":focus")) {
 | 
						|
            // Don't submit the form so that the typeahead can instead
 | 
						|
            // handle our Enter keypress. Any searching that needs
 | 
						|
            // to be done will be handled in the keyup.
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
    }).keyup((e) => {
 | 
						|
        if (exports.is_using_input_method) {
 | 
						|
            exports.is_using_input_method = false;
 | 
						|
            return;
 | 
						|
        }
 | 
						|
        const code = e.which;
 | 
						|
        if (code === 13 && search_query_box.is(":focus")) {
 | 
						|
            // We just pressed enter and the box had focus, which
 | 
						|
            // means we didn't use the typeahead at all.  In that
 | 
						|
            // case, we should act as though we're searching by
 | 
						|
            // operators.  (The reason the other actions don't call
 | 
						|
            // this codepath is that they first all blur the box to
 | 
						|
            // indicate that they've done what they need to do)
 | 
						|
 | 
						|
            // Pill is already added during keydown event of input pills.
 | 
						|
            exports.narrow_or_search_for_term(search_query_box.val());
 | 
						|
            search_query_box.blur();
 | 
						|
            update_buttons_with_focus(false);
 | 
						|
        }
 | 
						|
    });
 | 
						|
 | 
						|
    // Some of these functions don't actually need to be exported,
 | 
						|
    // but the code was moved here from elsewhere, and it would be
 | 
						|
    // more work to re-order everything and make them private.
 | 
						|
 | 
						|
    search_query_box.on('focus', exports.focus_search);
 | 
						|
    search_query_box.on('blur', (e) => {
 | 
						|
        // The search query box is a visual cue as to
 | 
						|
        // whether search or narrowing is active.  If
 | 
						|
        // the user blurs the search box, then we should
 | 
						|
        // update the search string to reflect the current
 | 
						|
        // narrow (or lack of narrow).
 | 
						|
        //
 | 
						|
        // But we can't do this right away, because
 | 
						|
        // selecting something in the typeahead menu causes
 | 
						|
        // the box to lose focus a moment before.
 | 
						|
        //
 | 
						|
        // The workaround is to check 300ms later -- long
 | 
						|
        // enough for the search to have gone through, but
 | 
						|
        // short enough that the user won't notice (though
 | 
						|
        // really it would be OK if they did).
 | 
						|
 | 
						|
        if (page_params.search_pills_enabled) {
 | 
						|
            const pill_id = $(e.relatedTarget).closest(".pill").data('id');
 | 
						|
            const search_pill = search_pill_widget.widget.getByID(pill_id);
 | 
						|
            if (search_pill) {
 | 
						|
                // The searchbox loses focus while the search
 | 
						|
                // pill element gains focus.
 | 
						|
                // We do not consider the searchbox to actually
 | 
						|
                // lose focus when a pill inside it gets selected
 | 
						|
                // or deleted by a click.
 | 
						|
                return;
 | 
						|
            }
 | 
						|
        }
 | 
						|
        setTimeout(() => {
 | 
						|
            exports.update_button_visibility();
 | 
						|
            tab_bar.close_search_bar_and_open_narrow_description();
 | 
						|
            searchbox.css({"box-shadow": "unset"});
 | 
						|
        }, 300);
 | 
						|
    });
 | 
						|
};
 | 
						|
 | 
						|
exports.focus_search = function () {
 | 
						|
    // The search bar is not focused yet, but will be.
 | 
						|
    update_buttons_with_focus(true);
 | 
						|
};
 | 
						|
 | 
						|
exports.initiate_search = function () {
 | 
						|
    tab_bar.open_search_bar_and_close_narrow_description();
 | 
						|
    $('#searchbox').css({"box-shadow": "inset 0px 0px 0px 2px hsl(204, 20%, 74%)"});
 | 
						|
    $('#search_query').typeahead('lookup').select();
 | 
						|
    if (page_params.search_pills_enabled) {
 | 
						|
        $('#search_query').focus();
 | 
						|
        ui_util.place_caret_at_end($('#search_query')[0]);
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
exports.clear_search_form = function () {
 | 
						|
    $('#search_query').val('');
 | 
						|
    $('#search_query').blur();
 | 
						|
    $('.search_button').prop('disabled', true);
 | 
						|
};
 | 
						|
 | 
						|
window.search = exports;
 |