diff options
Diffstat (limited to 'askbot/media/js/live_search.js')
-rw-r--r-- | askbot/media/js/live_search.js | 1016 |
1 files changed, 719 insertions, 297 deletions
diff --git a/askbot/media/js/live_search.js b/askbot/media/js/live_search.js index f7d89c2a..5dffb677 100644 --- a/askbot/media/js/live_search.js +++ b/askbot/media/js/live_search.js @@ -1,3 +1,185 @@ +var SearchDropMenu = function() { + WrappedElement.call(this); + this._data = undefined; + this._selectedItemIndex = 0; + this._askButtonEnabled = true; +} +inherits(SearchDropMenu, WrappedElement); + +SearchDropMenu.prototype.setData = function(data) { + this._data = data; +}; + +SearchDropMenu.prototype.setAskHandler = function(handler) { + this._askHandler = handler; +}; + +SearchDropMenu.prototype.setAskButtonEnabled = function(isEnabled) { + this._askButtonEnabled = isEnabled; +}; + +/** + * assumes that data is already set + */ +SearchDropMenu.prototype.render = function() { + var list = this._resultsList; + list.empty(); + var me = this; + $.each(this._data, function(idx, item) { + var listItem = me.makeElement('li'); + var link = me.makeElement('a'); + link.attr('href', item['url']); + link.html(item['title']); + listItem.append(link); + list.append(listItem); + }); + if (this._data.length === 0) { + list.addClass('empty'); + } else { + list.removeClass('empty'); + } +}; + +/** + * @param {number} idx position of item starting from 1 for the topmost + * Selects item inentified by position. + * Scrolls the list to make top of the item visible. + */ +SearchDropMenu.prototype.selectItem = function(idx) { + //idx is 1-based index + this._selectedItemIndex = idx; + var list = this._resultsList; + list.find('li').removeClass('selected'); + var item = this.getItem(idx); + if (item && idx > 0) { + item.addClass('selected'); + var itemTopY = item.position().top;//relative to visible area + var curScrollTop = list.scrollTop(); + + /* if item is clipped on top, scroll down */ + if (itemTopY < 0) { + list.scrollTop(curScrollTop + itemTopY); + return; + } + + var listHeight = list.outerHeight(); + /* pixels above the lower border of the list */ + var itemPeepHeight = listHeight - itemTopY; + /* pixels below the lower border */ + var itemSinkHeight = item.outerHeight() - itemPeepHeight; + if (itemSinkHeight > 0) { + list.scrollTop(curScrollTop + itemSinkHeight); + } + } + +}; + +SearchDropMenu.prototype.getItem = function(idx) { + return $(this._resultsList.find('li')[idx - 1]); +}; + +SearchDropMenu.prototype.getItemCount = function() { + return this._resultsList.find('li').length; +}; + +SearchDropMenu.prototype.getSelectedItemIndex = function() { + return this._selectedItemIndex; +}; + +SearchDropMenu.prototype.navigateToItem = function(idx) { + var item = this.getItem(idx); + if (item) { + window.location.href = item.find('a').attr('href'); + } +}; + +SearchDropMenu.prototype.makeKeyHandler = function() { + var me = this; + return function(e) { + var keyCode = getKeyCode(e); + if (keyCode === 27) {//escape + me.hide(); + return false; + } + if (keyCode !== 38 && keyCode !== 40 && keyCode !== 13) { + return; + } + var itemCount = me.getItemCount(); + if (itemCount > 0) { + var curItem = me.getSelectedItemIndex(); + if (keyCode === 38) {//upArrow + if (curItem > 0) { + curItem = curItem - 1; + } + } else if (keyCode === 40) {//downArrow + if (curItem < itemCount) { + curItem = curItem + 1; + } + } else if (keyCode === 13) {//enter + me.navigateToItem(curItem); + return false; + } + me.selectItem(curItem); + return false + } + }; +}; + +SearchDropMenu.prototype.createDom = function() { + this._element = this.makeElement('div'); + this._element.addClass('search-drop-menu'); + this._element.hide(); + + this._resultsList = this.makeElement('ul'); + this._element.append(this._resultsList); + this._element.addClass('empty'); + + //add ask button, @todo: make into separate class? + var footer = this.makeElement('div'); + this._element.append(footer); + this._footer = footer; + + if (this._askButtonEnabled) { + footer.addClass('footer'); + var button = this.makeElement('button'); + button.addClass('submit'); + button.html(gettext('Ask Your Question')) + footer.append(button); + var handler = this._askHandler; + setupButtonEventHandlers(button, handler); + } + + $(document).keydown(this.makeKeyHandler()); +}; + +SearchDropMenu.prototype.isOpen = function() { + return this._element.is(':visible'); +}; + +SearchDropMenu.prototype.show = function() { + var searchBar = this._element.prev(); + var searchBarHeight = searchBar.outerHeight(); + var topOffset = searchBar.offset().top + searchBarHeight; + this._element.show();//show so that size calcs work + var footerHeight = this._footer.outerHeight(); + var windowHeight = $(window).height(); + this._resultsList.css( + 'max-height', + windowHeight - topOffset - footerHeight - 40 //what is this number? + ); +}; + +SearchDropMenu.prototype.hide = function() { + this._element.hide(); +}; + +SearchDropMenu.prototype.reset = function() { + this._data = undefined; + this._resultsList.empty(); + this._selectedItemIndex = 0; + this._element.hide(); +}; + var TagWarningBox = function(){ WrappedElement.call(this); this._tags = []; @@ -6,9 +188,8 @@ inherits(TagWarningBox, WrappedElement); TagWarningBox.prototype.createDom = function(){ this._element = this.makeElement('div'); - this._element - .css('display', 'block') - .css('margin', '0 0 13px 2px'); + this._element.css('display', 'block'); + this._element.css('margin', '0 0 13px 2px'); this._element.addClass('non-existing-tags'); this._warning = this.makeElement('p'); this._element.append(this._warning); @@ -51,355 +232,596 @@ TagWarningBox.prototype.showWarning = function(){ this._warning.show(); }; -var liveSearch = function(query_string) { - var query = $('input#keywords'); - var query_val = function () {return $.trim(query.val());}; - var prev_text = query_val(); - var running = false; - var q_list_sel = 'question-list';//id of question listing div - var search_url = askbot['urls']['questions']; - var x_button = $('input[name=reset_query]'); - var tag_warning_box = new TagWarningBox(); +/** + * @constructor + * tool tip to be shown on top of the search input + */ +var InputToolTip = function() { + WrappedElement.call(this); +}; +inherits(InputToolTip, WrappedElement); - //the tag search input is optional in askbot - $('#ab-tag-search').parent().before( - tag_warning_box.getElement() - ); +InputToolTip.prototype.show = function() { + this._element.removeClass('dimmed'); + this._element.show(); +}; - var run_tag_search = function(){ - var search_tags = $('#ab-tag-search').val().split(/\s+/); - if (search_tags.length === 0) { - return; - } - /** @todo: the questions/ might need translation... */ - query_string = '/questions/scope:all/sort:activity-desc/page:1/' - $.each(search_tags, function(idx, tag) { - query_string = QSutils.add_search_tag(query_string, search_tags); - }); - var url = search_url + query_string; - $.ajax({ - url: url, - dataType: 'json', - success: function(data, text_status, xhr){ - render_result(data, text_status, xhr); - $('#ab-tag-search').val(''); - }, - }); - updateHistory(url); - }; +InputToolTip.prototype.hide = function() { + this._element.removeClass('dimmed'); + this._element.hide(); +}; - var activate_tag_search_input = function(){ - //the autocomplete is set up in tag_selector.js - var button = $('#ab-tag-search-add'); - if (button.length === 0){//may be absent - return; - } - var ac = new AutoCompleter({ - url: askbot['urls']['get_tag_list'], - preloadData: true, - minChars: 1, - useCache: true, - matchInside: true, - maxCacheLength: 100, - maxItemsToShow: 20, - onItemSelect: run_tag_search, - delay: 10 - }); - ac.decorate($('#ab-tag-search')); - setupButtonEventHandlers(button, run_tag_search); - //var tag_search_input = $('#ab-tag-search'); - //tag_search_input.keydown( - // makeKeyHandler(13, run_tag_search) - //); - }; +InputToolTip.prototype.dim = function() { + this._element.addClass('dimmed'); +}; - var render_tag_warning = function(tag_list){ - if ( !tag_list ) { - return; - } - tag_warning_box.clear(); - $.each(tag_list, function(idx, tag_name){ - tag_warning_box.addTag(tag_name); - }); - tag_warning_box.showWarning(); - }; +InputToolTip.prototype.setClickHandler = function(handler) { + this._clickHandler = handler; +}; - var refresh_x_button = function(){ - if(query_val().length > 0){ - if (query.hasClass('searchInput')){ - query.attr('class', 'searchInputCancelable'); - x_button.show(); - } - } else { - x_button.hide(); - query.attr('class', 'searchInput'); - } - }; +InputToolTip.prototype.createDom = function() { + var element = this.makeElement('div'); + this._element = element; - var restart_query = function() { - sortMethod = 'activity-desc'; - query.val(''); - refresh_x_button(); - send_query(); - }; + element.html(gettext('search or ask your question')); + element.addClass('input-tool-tip'); - var eval_query = function(){ - cur_query = query_val(); - if (cur_query !== prev_text && running === false){ - if (cur_query.length >= minSearchWordLength){ - send_query(cur_query); - } else if (cur_query.length === 0){ - restart_query(); - } + var handler = this._clickHandler; + var me = this; + element.click(function() { + handler(); + me.dim(); + return false; + }); + $(document).click(function() { + if (element.css('display') === 'block') { + element.removeClass('dimmed'); } - }; + }); +}; + + +/** + * @constructor + * provides full text search functionality + * which re-draws contents of the main page + * in response to the search query + */ +var FullTextSearch = function() { + WrappedElement.call(this); + this._running = false; + this._baseUrl = askbot['urls']['questions']; + this._q_list_sel = 'question-list';//id of question listing div + /** @todo: the questions/ needs translation... */ + this._searchUrl = '/scope:all/sort:activity-desc/page:1/' + this._askButtonEnabled = true; +}; +inherits(FullTextSearch, WrappedElement); + +/** + * @param {{boolean=}} optional, if given then function is setter + * otherwise it is a getter + * isRunning returns `true` when search is running + */ +FullTextSearch.prototype.isRunning = function(val) { + if (val === undefined) { + return this._running; + } else { + this._running = val; + } +}; - var update_query_string = function(query_text){ - if(query_text === undefined) { // handle missing parameter - query_text = query_val(); - } - query_string = QSutils.patch_query_string( - query_string, - 'query:' + encodeURIComponent(query_text), - query_text === '' // remove if empty - ); - return query_text; - }; +FullTextSearch.prototype.setAskButtonEnabled = function(isEnabled) { + this._askButtonEnabled = isEnabled; +} - var send_query = function(query_text){ - running = true; - if(!prev_text && query_text && showSortByRelevance) { - // If there was no query but there is some query now - and we support relevance search - then switch to it */ - query_string = QSutils.patch_query_string(query_string, 'sort:relevance-desc'); - } - prev_text = update_query_string(query_text); - query_string = QSutils.patch_query_string(query_string, 'page:1'); /* if something has changed, then reset the page no. */ - var url = search_url + query_string; - $.ajax({ - url: url, - dataType: 'json', - success: render_result, - complete: function(){ - running = false; - eval_query(); - }, - cache: false - }); - updateHistory(url); - }; +/** + * @param {{string}} url for the page displaying search results + */ +FullTextSearch.prototype.setSearchUrl = function(url) { + this._searchUrl = url; +}; - var updateHistory = function(url) { - var context = { state:1, rand:Math.random() }; - History.pushState( context, "Questions", url ); - setTimeout(function (){ - /* HACK: For some weird reson, sometimes something overrides the above pushState so we re-aplly it - This might be caused by some other JS plugin. - The delay of 10msec allows the other plugin to override the URL. - */ - History.replaceState( context, "Questions", url ); - }, 10); - }; +FullTextSearch.prototype.getSearchUrl = function() { + return this._searchUrl; +}; + +FullTextSearch.prototype.renderTagWarning = function(tag_list){ + if ( !tag_list ) { + return; + } + var tagWarningBox = this._tag_warning_box; + tagWarningBox.clear(); + $.each(tag_list, function(idx, tag_name){ + tagWarningBox.addTag(tag_name); + }); + tagWarningBox.showWarning(); +}; - /* *********************************** */ +FullTextSearch.prototype.runTagSearch = function() { + var search_tags = $('#ab-tag-search').val().split(/\s+/); + if (search_tags.length === 0) { + return; + } + var searchUrl = this.getSearchUrl(); + //add all tags to the url + searchUrl = QSutils.add_search_tag(searchUrl, search_tags); + var url = this._baseUrl + searchUrl; + var me = this; + $.ajax({ + url: url, + dataType: 'json', + success: function(data, text_status, xhr){ + me.renderFullTextResult(data, text_status, xhr); + $('#ab-tag-search').val(''); + }, + }); + this.updateHistory(url); +}; - var render_related_tags = function(tags, query_string){ - if (tags.length === 0) return; +FullTextSearch.prototype.updateHistory = function(url) { + var context = { state:1, rand:Math.random() }; + History.pushState( context, "Questions", url ); + setTimeout(function(){ + /* HACK: For some weird reson, sometimes + * overrides the above pushState so we re-aplly it + * This might be caused by some other JS plugin. + * The delay of 10msec allows the other plugin to override the URL. + */ + History.replaceState(context, "Questions", url); + }, + 10 + ); +}; - var html_list = []; - for (var i=0; i<tags.length; i++){ - var tag = new Tag(); - tag.setName(tags[i]['name']); - tag.setDeletable(false); - tag.setLinkable(true); - tag.setUrlParams(query_string); - - html_list.push(tag.getElement().outerHTML()); - html_list.push('<span class="tag-number">× '); - html_list.push(tags[i]['used_count']); - html_list.push('</span>'); - html_list.push('<br />'); +FullTextSearch.prototype.activateTagSearchInput = function() { + //the autocomplete is set up in tag_selector.js + var button = $('#ab-tag-search-add'); + if (button.length === 0){//may be absent + return; + } + var me = this; + var ac = new AutoCompleter({ + url: askbot['urls']['get_tag_list'], + preloadData: true, + minChars: 1, + useCache: true, + matchInside: true, + maxCacheLength: 100, + maxItemsToShow: 20, + onItemSelect: function(){ this.runTagSearch(); }, + delay: 10 + }); + ac.decorate($('#ab-tag-search')); + setupButtonEventHandlers( + button, + function() { me.runTagSearch() } + ); +}; + +FullTextSearch.prototype.sendTitleSearchQuery = function(query_text) { + this.isRunning(true); + this._prevText = query_text; + var data = {query_text: query_text}; + var me = this; + $.ajax({ + url: askbot['urls']['titleSearch'], + data: data, + dataType: 'json', + success: function(data, text_status, xhr){ + me.renderTitleSearchResult(data, text_status, xhr); + }, + complete: function(){ + me.isRunning(false); + me.evalTitleSearchQuery(); + }, + cache: false + }); +}; + + +FullTextSearch.prototype.sendFullTextSearchQuery = function(query_text) { + this.isRunning(true); + var searchUrl = this.getSearchUrl(); + var prevText = this._prevText; + if(!prevText && query_text && askbot['settings']['showSortByRelevance']) { + /* If there was no query but there is some + * query now - and we support relevance search + * - then switch to it + */ + searchUrl = QSutils.patch_query_string( + searchUrl, 'sort:relevance-desc' + ); + } + this._prevText = this.updateQueryString(query_text); + + /* if something has changed, then reset the page no. */ + searchUrl = QSutils.patch_query_string(searchUrl, 'page:1'); + var url = this._baseUrl + searchUrl; + var me = this; + $.ajax({ + url: url, + dataType: 'json', + success: function(data, text_status, xhr){ + me.renderFullTextSearchResult(data, text_status, xhr); + }, + complete: function(){ + me.isRunning(false); + }, + cache: false + }); + this.updateHistory(url); +}; + +FullTextSearch.prototype.refresh = function() { + this.sendFullTextSearchQuery();/* used for tag search, maybe not necessary */ +}; + +FullTextSearch.prototype.getSearchQuery = function() { + return $.trim(this._query.val()); +}; + +/** + * renders title search result in the dropdown under the search input + */ +FullTextSearch.prototype.renderTitleSearchResult = function(data) { + var menu = this._dropMenu; + menu.setData(data); + menu.render(); + menu.show(); +}; + +FullTextSearch.prototype.renderFullTextSearchResult = function(data) { + if (data['questions'].length === 0) { + return; + } + + $('#pager').toggle(data['paginator'] !== '').html(data['paginator']); + $('#questionCount').html(data['question_counter']); + this.renderSearchTags(data['query_data']['tags'], data['query_string']); + if(data['faces'].length > 0) { + $('#contrib-users > a').remove(); + $('#contrib-users').append(data['faces'].join('')); + } + this.renderRelatedTags(data['related_tags'], data['query_string']); + this.renderRelevanceSortTab(data['query_string']); + this.renderTagWarning(data['non_existing_tags']); + this.setActiveSortTab( + data['query_data']['sort_order'], + data['query_string'] + ); + if(data['feed_url']){ + // Change RSS URL + $("#ContentLeft a.rss:first").attr("href", data['feed_url']); + } + + // Patch scope selectors + var baseUrl = this._baseUrl; + $('#scopeWrapper > a.scope-selector').each(function(index) { + var old_qs = $(this).attr('href').replace(baseUrl, ''); + var scope = QSutils.get_query_string_selector_value(old_qs, 'scope'); + qs = QSutils.patch_query_string(data['query_string'], 'scope:' + scope); + $(this).attr('href', baseUrl + qs); + }); + + // Patch "Ask your question" + var askButton = $('#askButton'); + var askHrefBase = askButton.attr('href').split('?')[0]; + askButton.attr( + 'href', + askHrefBase + data['query_data']['ask_query_string'] + ); /* INFO: ask_query_string should already be URL-encoded! */ + + this._query.focus(); + + var old_list = $('#' + this._q_list_sel); + var new_list = $('<div></div>').hide().html(data['questions']); + new_list.find('.timeago').timeago(); + + var q_list_sel = this._q_list_sel; + old_list.stop(true).after(new_list).fadeOut(200, function() { + //show new div with a fadeIn effect + old_list.remove(); + new_list.attr('id', q_list_sel); + new_list.fadeIn(400); + }); +}; + +FullTextSearch.prototype.evalTitleSearchQuery = function() { + var cur_query = this.getSearchQuery(); + var prevText = this._prevText; + if (cur_query !== prevText && this.isRunning() === false){ + if (cur_query.length >= askbot['settings']['minSearchWordLength']){ + this.sendTitleSearchQuery(cur_query); + } else if (cur_query.length === 0){ + this.reset(); } - $('#related-tags').html(html_list.join('')); - }; + } +}; - var render_search_tags = function(tags, query_string){ - var search_tags = $('#searchTags'); - search_tags.empty(); - if (tags.length === 0){ - $('#listSearchTags').hide(); - $('#search-tips').hide();//wrong - if there are search users - } else { - $('#listSearchTags').show(); - $('#search-tips').show(); - $.each(tags, function(idx, tag_name){ - var tag = new Tag(); - tag.setName(tag_name); - tag.setLinkable(false); - tag.setDeletable(true); - tag.setDeleteHandler( - function(){ - remove_search_tag(tag_name, query_string); - } - ); - search_tags.append(tag.getElement()); - }); +FullTextSearch.prototype.reset = function() { + this._prevText = ''; + this._dropMenu.reset(); + this._element.val(''); + this._element.focus(); + this._xButton.hide(); + this._toolTip.show(); +}; + +FullTextSearch.prototype.refreshXButton = function() { + if(this.getSearchQuery().length > 0){ + if (this._query.hasClass('searchInput')){ + $('#searchBar').attr('class', 'cancelable'); + this._xButton.show(); } - }; + } else { + this._xButton.hide(); + $('#searchBar').removeClass('cancelable'); + } +}; - var create_relevance_tab = function(query_string){ - relevance_tab = $('<a></a>'); - href = search_url + QSutils.patch_query_string(query_string, 'sort:relevance-desc'); - relevance_tab.attr('href', href); - relevance_tab.attr('id', 'by_relevance'); - relevance_tab.html('<span>' + sortButtonData['relevance']['label'] + '</span>'); - return relevance_tab; - }; +FullTextSearch.prototype.updateQueryString = function(query_text) { + if (query_text === undefined) { // handle missing parameter + query_text = this.getSearchQuery(); + } + var newUrl = QSutils.patch_query_string( + this._searchUrl, + 'query:' + encodeURIComponent(query_text), + query_text === '' // remove if empty + ); + this.setSearchUrl(newUrl); + return query_text; +}; - /* *************************************** */ +FullTextSearch.prototype.renderRelatedTags = function(tags, query_string){ + if (tags.length === 0) return; - var remove_search_tag = function(tag){ - query_string = QSutils.remove_search_tag(query_string, tag); - send_query(); - }; + var html_list = []; + for (var i=0; i<tags.length; i++){ + var tag = new Tag(); + tag.setName(tags[i]['name']); + tag.setDeletable(false); + tag.setLinkable(true); + tag.setUrlParams(query_string); + + html_list.push(tag.getElement().outerHTML()); + html_list.push('<span class="tag-number">× '); + html_list.push(tags[i]['used_count']); + html_list.push('</span>'); + html_list.push('<br />'); + } + $('#related-tags').html(html_list.join('')); +}; - var set_active_sort_tab = function(sort_method, query_string){ - var tabs = $('#sort_tabs > a'); - tabs.attr('class', 'off'); - tabs.each(function(index, element){ - var tab = $(element); - if ( tab.attr('id') ) { - var tab_name = tab.attr('id').replace(/^by_/,''); - if (tab_name in sortButtonData){ - href = search_url + QSutils.patch_query_string( - query_string, - 'sort:' + tab_name + '-desc' - ); - tab.attr('href', href); - tab.attr('title', sortButtonData[tab_name]['desc_tooltip']); - tab.html(sortButtonData[tab_name]['label']); +FullTextSearch.prototype.renderSearchTags = function(tags, query_string){ + var search_tags = $('#searchTags'); + search_tags.empty(); + var me = this; + if (tags.length === 0){ + $('#listSearchTags').hide(); + $('#search-tips').hide();//wrong - if there are search users + } else { + $('#listSearchTags').show(); + $('#search-tips').show(); + $.each(tags, function(idx, tag_name){ + var tag = new Tag(); + tag.setName(tag_name); + tag.setLinkable(false); + tag.setDeletable(true); + tag.setDeleteHandler( + function(){ + this.removeSearchTag(tag_name, query_string); } - } + ); + search_tags.append(tag.getElement()); }); - var bits = sort_method.split('-', 2); - var name = bits[0]; - var sense = bits[1];//sense of sort - var antisense = (sense == 'asc' ? 'desc':'asc'); - var arrow = (sense == 'asc' ? ' ▲':' ▼'); - var active_tab = $('#by_' + name); - active_tab.attr('class', 'on'); - active_tab.attr('title', sortButtonData[name][antisense + '_tooltip']); - active_tab.html(sortButtonData[name]['label'] + arrow); - }; + } +}; - var render_relevance_sort_tab = function(query_string){ - if (showSortByRelevance === false){ - return; - } - var relevance_tab = $('#by_relevance'); - if (prev_text && prev_text.length > 0){ - if (relevance_tab.length == 0){ - relevance_tab = create_relevance_tab(query_string); - $('#sort_tabs>span').after(relevance_tab); +FullTextSearch.prototype.createRelevanceTab = function(query_string){ + var relevance_tab = $('<a></a>'); + href = this._baseUrl + QSutils.patch_query_string(query_string, 'sort:relevance-desc'); + relevance_tab.attr('href', href); + relevance_tab.attr('id', 'by_relevance'); + relevance_tab.html( + '<span>' + askbot['data']['sortButtonData']['relevance']['label'] + '</span>' + ); + return relevance_tab; +}; + +FullTextSearch.prototype.removeSearchTag = function(tag) { + var searchUrl = this.getSearchUrl() + searchUrl = QSutils.remove_search_tag(searchUrl, tag); + this.setSearchUrl(searchUrl); + this.sendFullTextSearchQuery(); +}; + +FullTextSearch.prototype.setActiveSortTab = function(sort_method, query_string){ + var tabs = $('#sort_tabs > a'); + tabs.attr('class', 'off'); + var baseUrl = this._baseUrl; + tabs.each(function(index, element){ + var tab = $(element); + if ( tab.attr('id') ) { + var tab_name = tab.attr('id').replace(/^by_/,''); + if (tab_name in askbot['data']['sortButtonData']){ + href = baseUrl + QSutils.patch_query_string( + query_string, + 'sort:' + tab_name + '-desc' + ); + tab.attr('href', href); + tab.attr( + 'title', + askbot['data']['sortButtonData'][tab_name]['desc_tooltip'] + ); + tab.html( + askbot['data']['sortButtonData'][tab_name]['label'] + ); } } - else { - if (relevance_tab.length > 0){ - relevance_tab.remove(); - } + }); + var bits = sort_method.split('-', 2); + var name = bits[0]; + var sense = bits[1];//sense of sort + var antisense = (sense == 'asc' ? 'desc':'asc'); + var arrow = (sense == 'asc' ? ' ▲':' ▼'); + var active_tab = $('#by_' + name); + active_tab.attr('class', 'on'); + active_tab.attr( + 'title', + askbot['data']['sortButtonData'][name][antisense + '_tooltip'] + ); + active_tab.html( + askbot['data']['sortButtonData'][name]['label'] + arrow + ); +}; + +FullTextSearch.prototype.renderRelevanceSortTab = function(query_string) { + if (askbot['settings']['showSortByRelevance'] === false){ + return; + } + var relevance_tab = $('#by_relevance'); + var prevText = this._prevText; + if (prevText && prevText.length > 0){ + if (relevance_tab.length == 0){ + relevance_tab = this.createRelevanceTab(query_string); + $('#sort_tabs>span').after(relevance_tab); } + } else { + if (relevance_tab.length > 0){ + relevance_tab.remove(); + } + } +}; + +FullTextSearch.prototype.makeAskHandler = function() { + var me = this; + return function() { + var query = me.getSearchQuery(); + window.location.href = askbot['urls']['ask'] + '?title=' + query; + return false; }; +}; - var render_result = function(data, text_status, xhr){ - if (data['questions'].length > 0){ - $('#pager').toggle(data['paginator'] !== '').html(data['paginator']); - $('#questionCount').html(data['question_counter']); - render_search_tags(data['query_data']['tags'], data['query_string']); - if(data['faces'].length > 0) { - $('#contrib-users > a').remove(); - $('#contrib-users').append(data['faces'].join('')); - } - render_related_tags(data['related_tags'], data['query_string']); - render_relevance_sort_tab(data['query_string']); - render_tag_warning(data['non_existing_tags']); - set_active_sort_tab(data['query_data']['sort_order'], data['query_string']); - if(data['feed_url']){ - // Change RSS URL - $("#ContentLeft a.rss:first").attr("href", data['feed_url']); +FullTextSearch.prototype.updateToolTip = function() { + var query = this.getSearchQuery(); + if (query === '') { + this._toolTip.show(); + } else { + this._toolTip.hide(); + } +}; + +/** + * keydown handler operates on the tooltip and the X button + * keyup is not good enough, because in that case + * tooltip will be displayed with the input box simultaneously + */ +FullTextSearch.prototype.makeKeyDownHandler = function() { + var me = this; + var toolTip = this._toolTip; + var xButton = this._xButton; + var dropMenu = this._dropMenu; + return function(e) {//don't like the keyup delay to + var keyCode = getKeyCode(e); + + if (keyCode === 27) {//escape key + if (dropMenu.isOpen() === false) { + me.reset(); + return false; } + } - // Patch scope selectors - $('#scopeWrapper > a.scope-selector').each(function(index) { - var old_qs = $(this).attr('href').replace(search_url, ''); - var scope = QSutils.get_query_string_selector_value(old_qs, 'scope'); - qs = QSutils.patch_query_string(data['query_string'], 'scope:' + scope); - $(this).attr('href', search_url + qs); - }); - - // Patch "Ask your question" - var askButton = $('#askButton'); - var askHrefBase = askButton.attr('href').split('?')[0]; - askButton.attr('href', askHrefBase + data['query_data']['ask_query_string']); /* INFO: ask_query_string should already be URL-encoded! */ - - query.focus(); - - var old_list = $('#' + q_list_sel); - var new_list = $('<div></div>').hide().html(data['questions']); - new_list.find('.timeago').timeago(); - old_list.stop(true).after(new_list).fadeOut(200, function() { - //show new div with a fadeIn effect - old_list.remove(); - new_list.attr('id', q_list_sel); - new_list.fadeIn(400); - }); + var query = me.getSearchQuery(); + if (query.length === 0) { + if (keyCode !== 8 && keyCode !== 48) {//del and backspace + toolTip.hide(); + //xButton.show();//causes a jump of search input... + } + } else { + me.updateToolTip(); + me.refreshXButton(); } }; +}; + +FullTextSearch.prototype.decorate = function(element) { + this._element = element;/* this is a bit artificial we don't use _element */ + this._query = element; + this._xButton = $('input[name=reset_query]'); + this._prevText = this.getSearchQuery(); + this._tag_warning_box = new TagWarningBox(); + + var toolTip = new InputToolTip(); + toolTip.setClickHandler(function() { + element.focus(); + }); + this._element.after(toolTip.getElement()); + this._toolTip = toolTip; + + var dropMenu = new SearchDropMenu(); + dropMenu.setAskHandler(this.makeAskHandler()); + dropMenu.setAskButtonEnabled(this._askButtonEnabled); + this._dropMenu = dropMenu; + element.parent().after(this._dropMenu.getElement()); + + var menuCloser = function(){ + dropMenu.reset(); + }; + $(element).click(function(e){ return false }); + $(document).click(menuCloser); - /* *********************************** */ + //the tag search input is optional in askbot + $('#ab-tag-search').parent().before( + this._tag_warning_box.getElement() + ); - // Wire search tags + // make search tags functional var search_tags = $('#searchTags .tag-left'); + var searchUrl = this.getSearchUrl(); + var me = this; $.each(search_tags, function(idx, element){ var tag = new Tag(); tag.decorate($(element)); //todo: setDeleteHandler and setHandler //must work after decorate & must have getName tag.setDeleteHandler( - function(){ - remove_search_tag(tag.getName(), query_string); - } + function(){ + me.removeSearchTag(tag.getName(), searchUrl); + } ); }); - - // Wire X button - x_button.click(function () { - restart_query(); /* wrapped in closure because it's not yet defined at this point */ + // enable x button (search reset) + this._xButton.click(function () { + /* wrapped in closure because it's not yet defined at this point */ + me.reset(); }); - refresh_x_button(); + this.refreshXButton(); - // Wire query box + // enable query box var main_page_eval_handle; - query.keyup(function(e){ - refresh_x_button(); - if (running === false){ + this._query.keydown(this.makeKeyDownHandler()); + this._query.keyup(function(e){ + me.updateToolTip(); + me.refreshXButton(); + if (me.isRunning() === false){ clearTimeout(main_page_eval_handle); - main_page_eval_handle = setTimeout(eval_query, 400); + main_page_eval_handle = setTimeout( + function() { me.evalTitleSearchQuery() }, + 400 + ); } }); - activate_tag_search_input(); + this.activateTagSearchInput(); + var baseUrl = this._baseUrl; + var searchUrl = this.getSearchUrl(); $("form#searchForm").submit(function(event) { // if user clicks the button the s(h)e probably wants page reload, // so provide that experience but first update the query string event.preventDefault(); - update_query_string(); - window.location.href = search_url + query_string; + me.updateQueryString(); + window.location.href = baseUrl + searchUrl; }); - - /* *********************************** */ - - // Hook for tag_selector.js - liveSearch.refresh = function () { - send_query(); - }; }; |