summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/search_autocomplete.js
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/search_autocomplete.js')
-rw-r--r--app/assets/javascripts/search_autocomplete.js360
1 files changed, 360 insertions, 0 deletions
diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js
new file mode 100644
index 00000000000..990f6536eb2
--- /dev/null
+++ b/app/assets/javascripts/search_autocomplete.js
@@ -0,0 +1,360 @@
+(function() {
+ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
+
+ this.SearchAutocomplete = (function() {
+ var KEYCODE;
+
+ KEYCODE = {
+ ESCAPE: 27,
+ BACKSPACE: 8,
+ ENTER: 13
+ };
+
+ function SearchAutocomplete(opts) {
+ var ref, ref1, ref2, ref3, ref4;
+ if (opts == null) {
+ opts = {};
+ }
+ this.onSearchInputBlur = bind(this.onSearchInputBlur, this);
+ this.onClearInputClick = bind(this.onClearInputClick, this);
+ this.onSearchInputFocus = bind(this.onSearchInputFocus, this);
+ this.onSearchInputClick = bind(this.onSearchInputClick, this);
+ this.onSearchInputKeyUp = bind(this.onSearchInputKeyUp, this);
+ this.onSearchInputKeyDown = bind(this.onSearchInputKeyDown, this);
+ this.wrap = (ref = opts.wrap) != null ? ref : $('.search'), this.optsEl = (ref1 = opts.optsEl) != null ? ref1 : this.wrap.find('.search-autocomplete-opts'), this.autocompletePath = (ref2 = opts.autocompletePath) != null ? ref2 : this.optsEl.data('autocomplete-path'), this.projectId = (ref3 = opts.projectId) != null ? ref3 : this.optsEl.data('autocomplete-project-id') || '', this.projectRef = (ref4 = opts.projectRef) != null ? ref4 : this.optsEl.data('autocomplete-project-ref') || '';
+ this.dropdown = this.wrap.find('.dropdown');
+ this.dropdownContent = this.dropdown.find('.dropdown-content');
+ this.locationBadgeEl = this.getElement('.location-badge');
+ this.scopeInputEl = this.getElement('#scope');
+ this.searchInput = this.getElement('.search-input');
+ this.projectInputEl = this.getElement('#search_project_id');
+ this.groupInputEl = this.getElement('#group_id');
+ this.searchCodeInputEl = this.getElement('#search_code');
+ this.repositoryInputEl = this.getElement('#repository_ref');
+ this.clearInput = this.getElement('.js-clear-input');
+ this.saveOriginalState();
+ if (gon.current_user_id) {
+ this.createAutocomplete();
+ }
+ this.searchInput.addClass('disabled');
+ this.saveTextLength();
+ this.bindEvents();
+ }
+
+ SearchAutocomplete.prototype.getElement = function(selector) {
+ return this.wrap.find(selector);
+ };
+
+ SearchAutocomplete.prototype.saveOriginalState = function() {
+ return this.originalState = this.serializeState();
+ };
+
+ SearchAutocomplete.prototype.saveTextLength = function() {
+ return this.lastTextLength = this.searchInput.val().length;
+ };
+
+ SearchAutocomplete.prototype.createAutocomplete = function() {
+ return this.searchInput.glDropdown({
+ filterInputBlur: false,
+ filterable: true,
+ filterRemote: true,
+ highlight: true,
+ enterCallback: false,
+ filterInput: 'input#search',
+ search: {
+ fields: ['text']
+ },
+ data: this.getData.bind(this),
+ selectable: true,
+ clicked: this.onClick.bind(this)
+ });
+ };
+
+ SearchAutocomplete.prototype.getData = function(term, callback) {
+ var _this, contents, jqXHR;
+ _this = this;
+ if (!term) {
+ if (contents = this.getCategoryContents()) {
+ this.searchInput.data('glDropdown').filter.options.callback(contents);
+ this.enableAutocomplete();
+ }
+ return;
+ }
+ if (this.loadingSuggestions) {
+ return;
+ }
+ this.loadingSuggestions = true;
+ return jqXHR = $.get(this.autocompletePath, {
+ project_id: this.projectId,
+ project_ref: this.projectRef,
+ term: term
+ }, function(response) {
+ var data, firstCategory, i, lastCategory, len, suggestion;
+ if (!response.length) {
+ _this.disableAutocomplete();
+ return;
+ }
+ data = [];
+ firstCategory = true;
+ for (i = 0, len = response.length; i < len; i++) {
+ suggestion = response[i];
+ if (lastCategory !== suggestion.category) {
+ if (!firstCategory) {
+ data.push('separator');
+ }
+ if (firstCategory) {
+ firstCategory = false;
+ }
+ data.push({
+ header: suggestion.category
+ });
+ lastCategory = suggestion.category;
+ }
+ data.push({
+ id: (suggestion.category.toLowerCase()) + "-" + suggestion.id,
+ category: suggestion.category,
+ text: suggestion.label,
+ url: suggestion.url
+ });
+ }
+ if (data.length) {
+ data.push('separator');
+ data.push({
+ text: "Result name contains \"" + term + "\"",
+ url: "/search?search=" + term + "&project_id=" + (_this.projectInputEl.val()) + "&group_id=" + (_this.groupInputEl.val())
+ });
+ }
+ return callback(data);
+ }).always(function() {
+ return _this.loadingSuggestions = false;
+ });
+ };
+
+ SearchAutocomplete.prototype.getCategoryContents = function() {
+ var dashboardOptions, groupOptions, issuesPath, items, mrPath, name, options, projectOptions, userId, utils;
+ userId = gon.current_user_id;
+ utils = gl.utils, projectOptions = gl.projectOptions, groupOptions = gl.groupOptions, dashboardOptions = gl.dashboardOptions;
+ if (utils.isInGroupsPage() && groupOptions) {
+ options = groupOptions[utils.getGroupSlug()];
+ } else if (utils.isInProjectPage() && projectOptions) {
+ options = projectOptions[utils.getProjectSlug()];
+ } else if (dashboardOptions) {
+ options = dashboardOptions;
+ }
+ issuesPath = options.issuesPath, mrPath = options.mrPath, name = options.name;
+ items = [
+ {
+ header: "" + name
+ }, {
+ text: 'Issues assigned to me',
+ url: issuesPath + "/?assignee_id=" + userId
+ }, {
+ text: "Issues I've created",
+ url: issuesPath + "/?author_id=" + userId
+ }, 'separator', {
+ text: 'Merge requests assigned to me',
+ url: mrPath + "/?assignee_id=" + userId
+ }, {
+ text: "Merge requests I've created",
+ url: mrPath + "/?author_id=" + userId
+ }
+ ];
+ if (!name) {
+ items.splice(0, 1);
+ }
+ return items;
+ };
+
+ SearchAutocomplete.prototype.serializeState = function() {
+ return {
+ search_project_id: this.projectInputEl.val(),
+ group_id: this.groupInputEl.val(),
+ search_code: this.searchCodeInputEl.val(),
+ repository_ref: this.repositoryInputEl.val(),
+ scope: this.scopeInputEl.val(),
+ _location: this.locationBadgeEl.text()
+ };
+ };
+
+ SearchAutocomplete.prototype.bindEvents = function() {
+ this.searchInput.on('keydown', this.onSearchInputKeyDown);
+ this.searchInput.on('keyup', this.onSearchInputKeyUp);
+ this.searchInput.on('click', this.onSearchInputClick);
+ this.searchInput.on('focus', this.onSearchInputFocus);
+ this.searchInput.on('blur', this.onSearchInputBlur);
+ this.clearInput.on('click', this.onClearInputClick);
+ return this.locationBadgeEl.on('click', (function(_this) {
+ return function() {
+ return _this.searchInput.focus();
+ };
+ })(this));
+ };
+
+ SearchAutocomplete.prototype.enableAutocomplete = function() {
+ var _this;
+ if (!gon.current_user_id) {
+ return;
+ }
+ if (!this.dropdown.hasClass('open')) {
+ _this = this;
+ this.loadingSuggestions = false;
+ this.dropdown.addClass('open').trigger('shown.bs.dropdown');
+ return this.searchInput.removeClass('disabled');
+ }
+ };
+
+ SearchAutocomplete.prototype.onSearchInputKeyDown = function() {
+ return this.saveTextLength();
+ };
+
+ SearchAutocomplete.prototype.onSearchInputKeyUp = function(e) {
+ switch (e.keyCode) {
+ case KEYCODE.BACKSPACE:
+ if (this.lastTextLength === 0 && this.badgePresent()) {
+ this.removeLocationBadge();
+ }
+ if (this.lastTextLength === 1) {
+ this.disableAutocomplete();
+ }
+ if (this.lastTextLength > 1) {
+ this.enableAutocomplete();
+ }
+ break;
+ case KEYCODE.ESCAPE:
+ this.restoreOriginalState();
+ break;
+ default:
+ if (this.searchInput.val() === '') {
+ this.disableAutocomplete();
+ } else {
+ if (e.keyCode !== KEYCODE.ENTER) {
+ this.enableAutocomplete();
+ }
+ }
+ }
+ this.wrap.toggleClass('has-value', !!e.target.value);
+ };
+
+ SearchAutocomplete.prototype.onSearchInputClick = function(e) {
+ return e.stopImmediatePropagation();
+ };
+
+ SearchAutocomplete.prototype.onSearchInputFocus = function() {
+ this.isFocused = true;
+ this.wrap.addClass('search-active');
+ if (this.getValue() === '') {
+ return this.getData();
+ }
+ };
+
+ SearchAutocomplete.prototype.getValue = function() {
+ return this.searchInput.val();
+ };
+
+ SearchAutocomplete.prototype.onClearInputClick = function(e) {
+ e.preventDefault();
+ return this.searchInput.val('').focus();
+ };
+
+ SearchAutocomplete.prototype.onSearchInputBlur = function(e) {
+ this.isFocused = false;
+ this.wrap.removeClass('search-active');
+ if (this.searchInput.val() === '') {
+ return this.restoreOriginalState();
+ }
+ };
+
+ SearchAutocomplete.prototype.addLocationBadge = function(item) {
+ var badgeText, category, value;
+ category = item.category != null ? item.category + ": " : '';
+ value = item.value != null ? item.value : '';
+ badgeText = "" + category + value;
+ this.locationBadgeEl.text(badgeText).show();
+ return this.wrap.addClass('has-location-badge');
+ };
+
+ SearchAutocomplete.prototype.hasLocationBadge = function() {
+ return this.wrap.is('.has-location-badge');
+ };
+
+ SearchAutocomplete.prototype.restoreOriginalState = function() {
+ var i, input, inputs, len;
+ inputs = Object.keys(this.originalState);
+ for (i = 0, len = inputs.length; i < len; i++) {
+ input = inputs[i];
+ this.getElement("#" + input).val(this.originalState[input]);
+ }
+ if (this.originalState._location === '') {
+ return this.locationBadgeEl.hide();
+ } else {
+ return this.addLocationBadge({
+ value: this.originalState._location
+ });
+ }
+ };
+
+ SearchAutocomplete.prototype.badgePresent = function() {
+ return this.locationBadgeEl.length;
+ };
+
+ SearchAutocomplete.prototype.resetSearchState = function() {
+ var i, input, inputs, len, results;
+ inputs = Object.keys(this.originalState);
+ results = [];
+ for (i = 0, len = inputs.length; i < len; i++) {
+ input = inputs[i];
+ if (input === '_location') {
+ break;
+ }
+ results.push(this.getElement("#" + input).val(''));
+ }
+ return results;
+ };
+
+ SearchAutocomplete.prototype.removeLocationBadge = function() {
+ this.locationBadgeEl.hide();
+ this.resetSearchState();
+ this.wrap.removeClass('has-location-badge');
+ return this.disableAutocomplete();
+ };
+
+ SearchAutocomplete.prototype.disableAutocomplete = function() {
+ this.searchInput.addClass('disabled');
+ this.dropdown.removeClass('open');
+ return this.restoreMenu();
+ };
+
+ SearchAutocomplete.prototype.restoreMenu = function() {
+ var html;
+ html = "<ul> <li><a class='dropdown-menu-empty-link is-focused'>Loading...</a></li> </ul>";
+ return this.dropdownContent.html(html);
+ };
+
+ SearchAutocomplete.prototype.onClick = function(item, $el, e) {
+ if (location.pathname.indexOf(item.url) !== -1) {
+ e.preventDefault();
+ if (!this.badgePresent) {
+ if (item.category === 'Projects') {
+ this.projectInputEl.val(item.id);
+ this.addLocationBadge({
+ value: 'This project'
+ });
+ }
+ if (item.category === 'Groups') {
+ this.groupInputEl.val(item.id);
+ this.addLocationBadge({
+ value: 'This group'
+ });
+ }
+ }
+ $el.removeClass('is-active');
+ this.disableAutocomplete();
+ return this.searchInput.val('').focus();
+ }
+ };
+
+ return SearchAutocomplete;
+
+ })();
+
+}).call(this);