diff --git a/app/assets/javascripts/discourse/controllers/search_controller.js b/app/assets/javascripts/discourse/controllers/search_controller.js new file mode 100644 index 00000000000..5f9fe23b136 --- /dev/null +++ b/app/assets/javascripts/discourse/controllers/search_controller.js @@ -0,0 +1,89 @@ +/** + Support for searching + + @class SearchController + @extends Discourse.Controller + @namespace Discourse + @module Discourse +**/ +Discourse.SearchController = Em.ArrayController.extend(Discourse.Presence, { + + // If we need to perform another search + newSearchNeeded: function() { + this.set('noResults', false); + var term = this.get('term'); + if (term && term.length >= Discourse.SiteSettings.min_search_term_length) { + this.set('loading', true); + this.searchTerm(term, this.get('typeFilter')); + } else { + this.set('content', Em.A()); + } + this.set('selectedIndex', 0); + }.observes('term', 'typeFilter'), + + searchTerm: Discourse.debouncePromise(function(term, typeFilter) { + var searchController = this; + this.set('count', 0); + return Discourse.Search.forTerm(term, typeFilter).then(function(results) { + searchController.set('results', results); + if (results) { + searchController.set('noResults', results.length === 0); + + // Make it easy to find the results by type + var results_hashed = {}; + results.forEach(function(r) { results_hashed[r.type] = r }); + + // Default order + var order = ['topic', 'category', 'user']; + results = order.map(function(o) { return results_hashed[o] }).without(void 0); + + var index = 0; + results.forEach(function(r) { + r.results.each(function(item) { + item.index = index++; + }); + }); + searchController.set('count', index); + searchController.set('content', results); + } + + searchController.set('loading', false); + }); + }, 300), + + showCancelFilter: function() { + if (this.get('loading')) return false; + return this.present('typeFilter'); + }.property('typeFilter', 'loading'), + + termChanged: function() { + this.cancelType(); + }.observes('term'), + + moreOfType: function(type) { + this.set('typeFilter', type); + }, + + cancelType: function() { + this.set('typeFilter', null); + }, + + moveUp: function() { + if (this.get('selectedIndex') === 0) return; + this.set('selectedIndex', this.get('selectedIndex') - 1); + }, + + moveDown: function() { + if (this.get('resultCount') === (this.get('selectedIndex') + 1)) return; + this.set('selectedIndex', this.get('selectedIndex') + 1); + }, + + select: function() { + if (this.get('loading')) return; + var href = $('#search-dropdown li.selected a').prop('href'); + if (href) { + Discourse.URL.routeTo(href); + } + } + +}); \ No newline at end of file diff --git a/app/assets/javascripts/discourse/templates/header.js.handlebars b/app/assets/javascripts/discourse/templates/header.js.handlebars index 515ba31b76e..02f31ce432a 100644 --- a/app/assets/javascripts/discourse/templates/header.js.handlebars +++ b/app/assets/javascripts/discourse/templates/header.js.handlebars @@ -46,7 +46,7 @@ - {{view Discourse.SearchView currentUserBinding="view.currentUser"}} + {{render search}}
{{#if view.notifications}} diff --git a/app/assets/javascripts/discourse/templates/search.js.handlebars b/app/assets/javascripts/discourse/templates/search.js.handlebars index 73dbefa3c79..17ce55b86de 100644 --- a/app/assets/javascripts/discourse/templates/search.js.handlebars +++ b/app/assets/javascripts/discourse/templates/search.js.handlebars @@ -1,28 +1,26 @@ -{{textField value=view.term placeholderBinding="view.searchPlaceholder"}} -{{#with view}} - {{#unless loading}} - {{#unless noResults}} - {{#each content}} - + {{/each}} {{else}} -
+
+ {{i18n search.no_results}} +
{{/unless}} -{{/with}} +{{else}} +
+{{/unless}} diff --git a/app/assets/javascripts/discourse/views/search/search_results_type_view.js b/app/assets/javascripts/discourse/views/search/search_results_type_view.js index 9118744a842..d6db3b50bfc 100644 --- a/app/assets/javascripts/discourse/views/search/search_results_type_view.js +++ b/app/assets/javascripts/discourse/views/search/search_results_type_view.js @@ -10,18 +10,17 @@ Discourse.SearchResultsTypeView = Ember.CollectionView.extend({ tagName: 'ul', itemViewClass: Ember.View.extend({ tagName: 'li', - classNameBindings: ['selectedClass', 'parentView.type'], - selectedIndexBinding: 'parentView.parentView.selectedIndex', + classNameBindings: ['selectedClass'], - templateName: (function() { + templateName: function() { return "search/" + (this.get('parentView.type')) + "_result"; - }).property('parentView.type'), + }.property('parentView.type'), // Is this row currently selected by the keyboard? - selectedClass: (function() { - if (this.get('content.index') === this.get('selectedIndex')) return 'selected'; + selectedClass: function() { + if (this.get('content.index') === this.get('controller.selectedIndex')) return 'selected'; return null; - }).property('selectedIndex') + }.property('controller.selectedIndex') }) }); diff --git a/app/assets/javascripts/discourse/views/search/search_view.js b/app/assets/javascripts/discourse/views/search/search_view.js index 44ed5350f30..f03f8767197 100644 --- a/app/assets/javascripts/discourse/views/search/search_view.js +++ b/app/assets/javascripts/discourse/views/search/search_view.js @@ -14,125 +14,21 @@ Discourse.SearchView = Discourse.View.extend({ didInsertElement: function() { // Delegate ESC to the composer - var _this = this; + var controller = this.get('controller'); return $('body').on('keydown.search', function(e) { if ($('#search-dropdown').is(':visible')) { switch (e.which) { case 13: - return _this.select(); + return controller.select(); case 38: - return _this.moveUp(); + return controller.moveUp(); case 40: - return _this.moveDown(); + return controller.moveDown(); } } }); - }, - - searchPlaceholder: function() { - return Em.String.i18n("search.placeholder"); - }.property(), - - // If we need to perform another search - newSearchNeeded: function() { - this.set('noResults', false); - var term = this.get('term'); - if (term && term.length >= Discourse.SiteSettings.min_search_term_length) { - this.set('loading', true); - this.searchTerm(term, this.get('typeFilter')); - } else { - this.set('results', null); - } - return this.set('selectedIndex', 0); - }.observes('term', 'typeFilter'), - - searchTerm: Discourse.debouncePromise(function(term, typeFilter) { - var searchView = this; - return Discourse.Search.forTerm(term, typeFilter).then(function(results) { - searchView.set('results', results); - }); - }, 300), - - showCancelFilter: function() { - if (this.get('loading')) return false; - return this.present('typeFilter'); - }.property('typeFilter', 'loading'), - - termChanged: function() { - return this.cancelType(); - }.observes('term'), - - // We can re-order them based on the context - content: function() { - var index, order, path, results, results_hashed; - if (results = this.get('results')) { - // Make it easy to find the results by type - results_hashed = {}; - results.each(function(r) { - results_hashed[r.type] = r; - }); - path = Discourse.get('router.currentState.path'); - // Default order - order = ['topic', 'category', 'user']; - results = (order.map(function(o) { - return results_hashed[o]; - })).without(void 0); - index = 0; - results.each(function(result) { - return result.results.each(function(item) { - item.index = index++; - }); - }); - } - return results; - }.property('results'), - - updateProgress: function() { - var results; - if (results = this.get('results')) { - this.set('noResults', results.length === 0); - } - return this.set('loading', false); - }.observes('results'), - - resultCount: function() { - var count; - if (this.blank('content')) return 0; - count = 0; - this.get('content').each(function(result) { - count += result.results.length; - }); - return count; - }.property('content'), - - moreOfType: function(type) { - this.set('typeFilter', type); - return false; - }, - - cancelType: function() { - this.set('typeFilter', null); - return false; - }, - - moveUp: function() { - if (this.get('selectedIndex') === 0) return; - return this.set('selectedIndex', this.get('selectedIndex') - 1); - }, - - moveDown: function() { - if (this.get('resultCount') === (this.get('selectedIndex') + 1)) return; - return this.set('selectedIndex', this.get('selectedIndex') + 1); - }, - - select: function() { - if (this.get('loading')) return; - var href = $('#search-dropdown li.selected a').prop('href'); - if (href) { - Discourse.URL.routeTo(href); - } - return false; } + }); diff --git a/lib/search.rb b/lib/search.rb index bc9da30f0b0..660233f23d1 100644 --- a/lib/search.rb +++ b/lib/search.rb @@ -28,7 +28,6 @@ class Search end def initialize(term, opts=nil) - if term.present? @term = term.to_s @original_term = PG::Connection.escape_string(@term)