Export old code into es6 modules
This commit is contained in:
parent
bcb14a0dbb
commit
aa90e8ea5b
13 changed files with 731 additions and 749 deletions
|
@ -15,7 +15,7 @@ import GroupLabelSubscription from './group_label_subscription';
|
|||
import BuildArtifacts from './build_artifacts';
|
||||
import CILintEditor from './ci_lint_editor';
|
||||
import groupsSelect from './groups_select';
|
||||
/* global Search */
|
||||
import Search from './search';
|
||||
/* global Admin */
|
||||
import NamespaceSelect from './namespace_select';
|
||||
import NewCommitForm from './new_commit_form';
|
||||
|
@ -24,7 +24,7 @@ import projectAvatar from './project_avatar';
|
|||
/* global MergeRequest */
|
||||
import Compare from './compare';
|
||||
import initCompareAutocomplete from './compare_autocomplete';
|
||||
/* global ProjectFindFile */
|
||||
import ProjectFindFile from './project_find_file';
|
||||
import ProjectNew from './project_new';
|
||||
import projectImport from './project_import';
|
||||
import Labels from './labels';
|
||||
|
@ -91,6 +91,7 @@ import DueDateSelectors from './due_date_select';
|
|||
import Diff from './diff';
|
||||
import ProjectLabelSubscription from './project_label_subscription';
|
||||
import ProjectVariables from './project_variables';
|
||||
import SearchAutocomplete from './search_autocomplete';
|
||||
|
||||
(function() {
|
||||
var Dispatcher;
|
||||
|
@ -683,7 +684,7 @@ import ProjectVariables from './project_variables';
|
|||
Dispatcher.prototype.initSearch = function() {
|
||||
// Only when search form is present
|
||||
if ($('.search').length) {
|
||||
return new gl.SearchAutocomplete();
|
||||
return new SearchAutocomplete();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -60,15 +60,10 @@ import './notifications_dropdown';
|
|||
import './notifications_form';
|
||||
import './pager';
|
||||
import './preview_markdown';
|
||||
import './project_find_file';
|
||||
import './project_import';
|
||||
import './projects_dropdown';
|
||||
import './projects_list';
|
||||
import './syntax_highlight';
|
||||
import './render_gfm';
|
||||
import './right_sidebar';
|
||||
import './search';
|
||||
import './search_autocomplete';
|
||||
import initBreadcrumbs from './breadcrumb';
|
||||
|
||||
import './dispatcher';
|
||||
|
|
|
@ -10,6 +10,7 @@ import './mixins/line_conflict_actions';
|
|||
import './components/diff_file_editor';
|
||||
import './components/inline_conflict_lines';
|
||||
import './components/parallel_conflict_lines';
|
||||
import syntaxHighlight from '../syntax_highlight';
|
||||
|
||||
$(() => {
|
||||
const INTERACTIVE_RESOLVE_MODE = 'interactive';
|
||||
|
@ -53,7 +54,7 @@ $(() => {
|
|||
mergeConflictsStore.setLoadingState(false);
|
||||
|
||||
this.$nextTick(() => {
|
||||
$('.js-syntax-highlight').syntaxHighlight();
|
||||
syntaxHighlight($('.js-syntax-highlight'));
|
||||
});
|
||||
});
|
||||
},
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
import { getLocationHash } from './lib/utils/url_utility';
|
||||
import initDiscussionTab from './image_diff/init_discussion_tab';
|
||||
import Diff from './diff';
|
||||
import syntaxHighlight from './syntax_highlight';
|
||||
|
||||
/* eslint-disable max-len */
|
||||
// MergeRequestTabs
|
||||
|
@ -295,7 +296,7 @@ import Diff from './diff';
|
|||
}
|
||||
|
||||
gl.utils.localTimeAgo($('.js-timeago', 'div#diffs'));
|
||||
$('#diffs .js-syntax-highlight').syntaxHighlight();
|
||||
syntaxHighlight($('#diffs .js-syntax-highlight'));
|
||||
|
||||
if (this.diffViewType() === 'parallel' && this.isDiffAction(this.currentAction)) {
|
||||
this.expandViewContainer();
|
||||
|
|
|
@ -2,169 +2,163 @@
|
|||
|
||||
import fuzzaldrinPlus from 'fuzzaldrin-plus';
|
||||
|
||||
(function() {
|
||||
this.ProjectFindFile = (function() {
|
||||
var highlighter;
|
||||
|
||||
function ProjectFindFile(element1, options) {
|
||||
this.element = element1;
|
||||
this.options = options;
|
||||
this.goToBlob = this.goToBlob.bind(this);
|
||||
this.goToTree = this.goToTree.bind(this);
|
||||
this.selectRowDown = this.selectRowDown.bind(this);
|
||||
this.selectRowUp = this.selectRowUp.bind(this);
|
||||
this.filePaths = {};
|
||||
this.inputElement = this.element.find(".file-finder-input");
|
||||
// init event
|
||||
this.initEvent();
|
||||
// focus text input box
|
||||
this.inputElement.focus();
|
||||
// load file list
|
||||
this.load(this.options.url);
|
||||
}
|
||||
|
||||
ProjectFindFile.prototype.initEvent = function() {
|
||||
this.inputElement.off("keyup");
|
||||
this.inputElement.on("keyup", (function(_this) {
|
||||
return function(event) {
|
||||
var oldValue, ref, target, value;
|
||||
target = $(event.target);
|
||||
value = target.val();
|
||||
oldValue = (ref = target.data("oldValue")) != null ? ref : "";
|
||||
if (value !== oldValue) {
|
||||
target.data("oldValue", value);
|
||||
_this.findFile();
|
||||
return _this.element.find("tr.tree-item").eq(0).addClass("selected").focus();
|
||||
}
|
||||
};
|
||||
})(this));
|
||||
};
|
||||
|
||||
ProjectFindFile.prototype.findFile = function() {
|
||||
var result, searchText;
|
||||
searchText = this.inputElement.val();
|
||||
result = searchText.length > 0 ? fuzzaldrinPlus.filter(this.filePaths, searchText) : this.filePaths;
|
||||
return this.renderList(result, searchText);
|
||||
// find file
|
||||
};
|
||||
|
||||
// files pathes load
|
||||
ProjectFindFile.prototype.load = function(url) {
|
||||
return $.ajax({
|
||||
url: url,
|
||||
method: "get",
|
||||
dataType: "json",
|
||||
success: (function(_this) {
|
||||
return function(data) {
|
||||
_this.element.find(".loading").hide();
|
||||
_this.filePaths = data;
|
||||
_this.findFile();
|
||||
return _this.element.find(".files-slider tr.tree-item").eq(0).addClass("selected").focus();
|
||||
};
|
||||
})(this)
|
||||
});
|
||||
};
|
||||
|
||||
// render result
|
||||
ProjectFindFile.prototype.renderList = function(filePaths, searchText) {
|
||||
var blobItemUrl, filePath, html, i, j, len, matches, results;
|
||||
this.element.find(".tree-table > tbody").empty();
|
||||
results = [];
|
||||
for (i = j = 0, len = filePaths.length; j < len; i = (j += 1)) {
|
||||
filePath = filePaths[i];
|
||||
if (i === 20) {
|
||||
break;
|
||||
}
|
||||
if (searchText) {
|
||||
matches = fuzzaldrinPlus.match(filePath, searchText);
|
||||
}
|
||||
blobItemUrl = this.options.blobUrlTemplate + "/" + filePath;
|
||||
html = this.makeHtml(filePath, matches, blobItemUrl);
|
||||
results.push(this.element.find(".tree-table > tbody").append(html));
|
||||
}
|
||||
return results;
|
||||
};
|
||||
|
||||
// highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> )
|
||||
highlighter = function(element, text, matches) {
|
||||
var highlightText, j, lastIndex, len, matchIndex, matchedChars, unmatched;
|
||||
lastIndex = 0;
|
||||
highlightText = "";
|
||||
matchedChars = [];
|
||||
for (j = 0, len = matches.length; j < len; j += 1) {
|
||||
matchIndex = matches[j];
|
||||
unmatched = text.substring(lastIndex, matchIndex);
|
||||
if (unmatched) {
|
||||
if (matchedChars.length) {
|
||||
element.append(matchedChars.join("").bold());
|
||||
}
|
||||
matchedChars = [];
|
||||
element.append(document.createTextNode(unmatched));
|
||||
}
|
||||
matchedChars.push(text[matchIndex]);
|
||||
lastIndex = matchIndex + 1;
|
||||
}
|
||||
// highlight text(awefwbwgtc -> <b>a</b>wefw<b>b</b>wgt<b>c</b> )
|
||||
const highlighter = function(element, text, matches) {
|
||||
var highlightText, j, lastIndex, len, matchIndex, matchedChars, unmatched;
|
||||
lastIndex = 0;
|
||||
highlightText = "";
|
||||
matchedChars = [];
|
||||
for (j = 0, len = matches.length; j < len; j += 1) {
|
||||
matchIndex = matches[j];
|
||||
unmatched = text.substring(lastIndex, matchIndex);
|
||||
if (unmatched) {
|
||||
if (matchedChars.length) {
|
||||
element.append(matchedChars.join("").bold());
|
||||
}
|
||||
return element.append(document.createTextNode(text.substring(lastIndex)));
|
||||
};
|
||||
matchedChars = [];
|
||||
element.append(document.createTextNode(unmatched));
|
||||
}
|
||||
matchedChars.push(text[matchIndex]);
|
||||
lastIndex = matchIndex + 1;
|
||||
}
|
||||
if (matchedChars.length) {
|
||||
element.append(matchedChars.join("").bold());
|
||||
}
|
||||
return element.append(document.createTextNode(text.substring(lastIndex)));
|
||||
};
|
||||
|
||||
// make tbody row html
|
||||
ProjectFindFile.prototype.makeHtml = function(filePath, matches, blobItemUrl) {
|
||||
var $tr;
|
||||
$tr = $("<tr class='tree-item'><td class='tree-item-file-name link-container'><a><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'></span></a></td></tr>");
|
||||
if (matches) {
|
||||
$tr.find("a").replaceWith(highlighter($tr.find("a"), filePath, matches).attr("href", blobItemUrl));
|
||||
} else {
|
||||
$tr.find("a").attr("href", blobItemUrl);
|
||||
$tr.find(".str-truncated").text(filePath);
|
||||
}
|
||||
return $tr;
|
||||
};
|
||||
export default class ProjectFindFile {
|
||||
constructor(element1, options) {
|
||||
this.element = element1;
|
||||
this.options = options;
|
||||
this.goToBlob = this.goToBlob.bind(this);
|
||||
this.goToTree = this.goToTree.bind(this);
|
||||
this.selectRowDown = this.selectRowDown.bind(this);
|
||||
this.selectRowUp = this.selectRowUp.bind(this);
|
||||
this.filePaths = {};
|
||||
this.inputElement = this.element.find(".file-finder-input");
|
||||
// init event
|
||||
this.initEvent();
|
||||
// focus text input box
|
||||
this.inputElement.focus();
|
||||
// load file list
|
||||
this.load(this.options.url);
|
||||
}
|
||||
|
||||
ProjectFindFile.prototype.selectRow = function(type) {
|
||||
var next, rows, selectedRow;
|
||||
rows = this.element.find(".files-slider tr.tree-item");
|
||||
selectedRow = this.element.find(".files-slider tr.tree-item.selected");
|
||||
if (rows && rows.length > 0) {
|
||||
if (selectedRow && selectedRow.length > 0) {
|
||||
if (type === "UP") {
|
||||
next = selectedRow.prev();
|
||||
} else if (type === "DOWN") {
|
||||
next = selectedRow.next();
|
||||
}
|
||||
if (next.length > 0) {
|
||||
selectedRow.removeClass("selected");
|
||||
selectedRow = next;
|
||||
}
|
||||
} else {
|
||||
selectedRow = rows.eq(0);
|
||||
initEvent() {
|
||||
this.inputElement.off("keyup");
|
||||
this.inputElement.on("keyup", (function(_this) {
|
||||
return function(event) {
|
||||
var oldValue, ref, target, value;
|
||||
target = $(event.target);
|
||||
value = target.val();
|
||||
oldValue = (ref = target.data("oldValue")) != null ? ref : "";
|
||||
if (value !== oldValue) {
|
||||
target.data("oldValue", value);
|
||||
_this.findFile();
|
||||
return _this.element.find("tr.tree-item").eq(0).addClass("selected").focus();
|
||||
}
|
||||
return selectedRow.addClass("selected").focus();
|
||||
};
|
||||
})(this));
|
||||
}
|
||||
|
||||
findFile() {
|
||||
var result, searchText;
|
||||
searchText = this.inputElement.val();
|
||||
result = searchText.length > 0 ? fuzzaldrinPlus.filter(this.filePaths, searchText) : this.filePaths;
|
||||
return this.renderList(result, searchText);
|
||||
// find file
|
||||
}
|
||||
|
||||
// files pathes load
|
||||
load(url) {
|
||||
return $.ajax({
|
||||
url: url,
|
||||
method: "get",
|
||||
dataType: "json",
|
||||
success: (function(_this) {
|
||||
return function(data) {
|
||||
_this.element.find(".loading").hide();
|
||||
_this.filePaths = data;
|
||||
_this.findFile();
|
||||
return _this.element.find(".files-slider tr.tree-item").eq(0).addClass("selected").focus();
|
||||
};
|
||||
})(this)
|
||||
});
|
||||
}
|
||||
|
||||
// render result
|
||||
renderList(filePaths, searchText) {
|
||||
var blobItemUrl, filePath, html, i, j, len, matches, results;
|
||||
this.element.find(".tree-table > tbody").empty();
|
||||
results = [];
|
||||
for (i = j = 0, len = filePaths.length; j < len; i = (j += 1)) {
|
||||
filePath = filePaths[i];
|
||||
if (i === 20) {
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
ProjectFindFile.prototype.selectRowUp = function() {
|
||||
return this.selectRow("UP");
|
||||
};
|
||||
|
||||
ProjectFindFile.prototype.selectRowDown = function() {
|
||||
return this.selectRow("DOWN");
|
||||
};
|
||||
|
||||
ProjectFindFile.prototype.goToTree = function() {
|
||||
return location.href = this.options.treeUrl;
|
||||
};
|
||||
|
||||
ProjectFindFile.prototype.goToBlob = function() {
|
||||
var $link = this.element.find(".tree-item.selected .tree-item-file-name a");
|
||||
|
||||
if ($link.length) {
|
||||
$link.get(0).click();
|
||||
if (searchText) {
|
||||
matches = fuzzaldrinPlus.match(filePath, searchText);
|
||||
}
|
||||
};
|
||||
blobItemUrl = this.options.blobUrlTemplate + "/" + filePath;
|
||||
html = ProjectFindFile.makeHtml(filePath, matches, blobItemUrl);
|
||||
results.push(this.element.find(".tree-table > tbody").append(html));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
return ProjectFindFile;
|
||||
})();
|
||||
}).call(window);
|
||||
// make tbody row html
|
||||
static makeHtml(filePath, matches, blobItemUrl) {
|
||||
var $tr;
|
||||
$tr = $("<tr class='tree-item'><td class='tree-item-file-name link-container'><a><i class='fa fa-file-text-o fa-fw'></i><span class='str-truncated'></span></a></td></tr>");
|
||||
if (matches) {
|
||||
$tr.find("a").replaceWith(highlighter($tr.find("a"), filePath, matches).attr("href", blobItemUrl));
|
||||
} else {
|
||||
$tr.find("a").attr("href", blobItemUrl);
|
||||
$tr.find(".str-truncated").text(filePath);
|
||||
}
|
||||
return $tr;
|
||||
}
|
||||
|
||||
selectRow(type) {
|
||||
var next, rows, selectedRow;
|
||||
rows = this.element.find(".files-slider tr.tree-item");
|
||||
selectedRow = this.element.find(".files-slider tr.tree-item.selected");
|
||||
if (rows && rows.length > 0) {
|
||||
if (selectedRow && selectedRow.length > 0) {
|
||||
if (type === "UP") {
|
||||
next = selectedRow.prev();
|
||||
} else if (type === "DOWN") {
|
||||
next = selectedRow.next();
|
||||
}
|
||||
if (next.length > 0) {
|
||||
selectedRow.removeClass("selected");
|
||||
selectedRow = next;
|
||||
}
|
||||
} else {
|
||||
selectedRow = rows.eq(0);
|
||||
}
|
||||
return selectedRow.addClass("selected").focus();
|
||||
}
|
||||
}
|
||||
|
||||
selectRowUp() {
|
||||
return this.selectRow("UP");
|
||||
}
|
||||
|
||||
selectRowDown() {
|
||||
return this.selectRow("DOWN");
|
||||
}
|
||||
|
||||
goToTree() {
|
||||
return location.href = this.options.treeUrl;
|
||||
}
|
||||
|
||||
goToBlob() {
|
||||
var $link = this.element.find(".tree-item.selected .tree-item-file-name a");
|
||||
|
||||
if ($link.length) {
|
||||
$link.get(0).click();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import renderMath from './render_math';
|
||||
import renderMermaid from './render_mermaid';
|
||||
|
||||
import syntaxHighlight from './syntax_highlight';
|
||||
// Render Gitlab flavoured Markdown
|
||||
//
|
||||
// Delegates to syntax highlight and render math & mermaid diagrams.
|
||||
//
|
||||
$.fn.renderGFM = function renderGFM() {
|
||||
this.find('.js-syntax-highlight').syntaxHighlight();
|
||||
syntaxHighlight(this.find('.js-syntax-highlight'));
|
||||
renderMath(this.find('.js-render-math'));
|
||||
renderMermaid(this.find('.js-render-mermaid'));
|
||||
return this;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script>
|
||||
/* global LineHighlighter */
|
||||
import { mapGetters } from 'vuex';
|
||||
import syntaxHighlight from '../../syntax_highlight';
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
|
@ -13,7 +14,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
highlightFile() {
|
||||
$(this.$el).find('.file-content').syntaxHighlight();
|
||||
syntaxHighlight($(this.$el).find('.file-content'));
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
|
|
|
@ -1,118 +1,113 @@
|
|||
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, object-shorthand, prefer-arrow-callback, comma-dangle, prefer-template, quotes, no-else-return, max-len */
|
||||
import Flash from './flash';
|
||||
import Api from './api';
|
||||
|
||||
(function() {
|
||||
this.Search = (function() {
|
||||
function Search() {
|
||||
var $groupDropdown, $projectDropdown;
|
||||
$groupDropdown = $('.js-search-group-dropdown');
|
||||
$projectDropdown = $('.js-search-project-dropdown');
|
||||
this.groupId = $groupDropdown.data('group-id');
|
||||
this.eventListeners();
|
||||
$groupDropdown.glDropdown({
|
||||
selectable: true,
|
||||
filterable: true,
|
||||
fieldName: 'group_id',
|
||||
search: {
|
||||
fields: ['full_name']
|
||||
},
|
||||
data: function(term, callback) {
|
||||
return Api.groups(term, {}, function(data) {
|
||||
export default class Search {
|
||||
constructor() {
|
||||
const $groupDropdown = $('.js-search-group-dropdown');
|
||||
const $projectDropdown = $('.js-search-project-dropdown');
|
||||
|
||||
this.searchInput = '.js-search-input';
|
||||
this.searchClear = '.js-search-clear';
|
||||
|
||||
this.groupId = $groupDropdown.data('group-id');
|
||||
this.eventListeners();
|
||||
|
||||
$groupDropdown.glDropdown({
|
||||
selectable: true,
|
||||
filterable: true,
|
||||
fieldName: 'group_id',
|
||||
search: {
|
||||
fields: ['full_name'],
|
||||
},
|
||||
data(term, callback) {
|
||||
return Api.groups(term, {}, (data) => {
|
||||
data.unshift({
|
||||
full_name: 'Any',
|
||||
});
|
||||
data.splice(1, 0, 'divider');
|
||||
return callback(data);
|
||||
});
|
||||
},
|
||||
id(obj) {
|
||||
return obj.id;
|
||||
},
|
||||
text(obj) {
|
||||
return obj.full_name;
|
||||
},
|
||||
toggleLabel(obj) {
|
||||
return `${($groupDropdown.data('default-label'))} ${obj.full_name}`;
|
||||
},
|
||||
clicked: () => Search.submitSearch(),
|
||||
});
|
||||
|
||||
$projectDropdown.glDropdown({
|
||||
selectable: true,
|
||||
filterable: true,
|
||||
fieldName: 'project_id',
|
||||
search: {
|
||||
fields: ['name'],
|
||||
},
|
||||
data: (term, callback) => {
|
||||
this.getProjectsData(term)
|
||||
.then((data) => {
|
||||
data.unshift({
|
||||
full_name: 'Any'
|
||||
name_with_namespace: 'Any',
|
||||
});
|
||||
data.splice(1, 0, 'divider');
|
||||
return callback(data);
|
||||
});
|
||||
},
|
||||
id: function(obj) {
|
||||
return obj.id;
|
||||
},
|
||||
text: function(obj) {
|
||||
return obj.full_name;
|
||||
},
|
||||
toggleLabel: function(obj) {
|
||||
return ($groupDropdown.data('default-label')) + " " + obj.full_name;
|
||||
},
|
||||
clicked: (function(_this) {
|
||||
return function() {
|
||||
return _this.submitSearch();
|
||||
};
|
||||
})(this)
|
||||
});
|
||||
$projectDropdown.glDropdown({
|
||||
selectable: true,
|
||||
filterable: true,
|
||||
fieldName: 'project_id',
|
||||
search: {
|
||||
fields: ['name']
|
||||
},
|
||||
data: (term, callback) => {
|
||||
this.getProjectsData(term)
|
||||
.then((data) => {
|
||||
data.unshift({
|
||||
name_with_namespace: 'Any'
|
||||
});
|
||||
data.splice(1, 0, 'divider');
|
||||
|
||||
return data;
|
||||
})
|
||||
.then(data => callback(data))
|
||||
.catch(() => new Flash('Error fetching projects'));
|
||||
},
|
||||
id: function(obj) {
|
||||
return obj.id;
|
||||
},
|
||||
text: function(obj) {
|
||||
return obj.name_with_namespace;
|
||||
},
|
||||
toggleLabel: function(obj) {
|
||||
return ($projectDropdown.data('default-label')) + " " + obj.name_with_namespace;
|
||||
},
|
||||
clicked: (function(_this) {
|
||||
return function() {
|
||||
return _this.submitSearch();
|
||||
};
|
||||
})(this)
|
||||
});
|
||||
return data;
|
||||
})
|
||||
.then(data => callback(data))
|
||||
.catch(() => new Flash('Error fetching projects'));
|
||||
},
|
||||
id(obj) {
|
||||
return obj.id;
|
||||
},
|
||||
text(obj) {
|
||||
return obj.name_with_namespace;
|
||||
},
|
||||
toggleLabel(obj) {
|
||||
return `${($projectDropdown.data('default-label'))} ${obj.name_with_namespace}`;
|
||||
},
|
||||
clicked: () => Search.submitSearch(),
|
||||
});
|
||||
}
|
||||
|
||||
eventListeners() {
|
||||
$(document)
|
||||
.off('keyup', this.searchInput)
|
||||
.on('keyup', this.searchInput, this.searchKeyUp);
|
||||
$(document)
|
||||
.off('click', this.searchClear)
|
||||
.on('click', this.searchClear, this.clearSearchField.bind(this));
|
||||
}
|
||||
|
||||
static submitSearch() {
|
||||
return $('.js-search-form').submit();
|
||||
}
|
||||
|
||||
searchKeyUp() {
|
||||
const $input = $(this);
|
||||
if ($input.val() === '') {
|
||||
$('.js-search-clear').addClass('hidden');
|
||||
} else {
|
||||
$('.js-search-clear').removeClass('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
Search.prototype.eventListeners = function() {
|
||||
$(document).off('keyup', '.js-search-input').on('keyup', '.js-search-input', this.searchKeyUp);
|
||||
return $(document).off('click', '.js-search-clear').on('click', '.js-search-clear', this.clearSearchField);
|
||||
};
|
||||
clearSearchField() {
|
||||
return $(this.searchInput).val('').trigger('keyup').focus();
|
||||
}
|
||||
|
||||
Search.prototype.submitSearch = function() {
|
||||
return $('.js-search-form').submit();
|
||||
};
|
||||
|
||||
Search.prototype.searchKeyUp = function() {
|
||||
var $input;
|
||||
$input = $(this);
|
||||
if ($input.val() === '') {
|
||||
return $('.js-search-clear').addClass('hidden');
|
||||
getProjectsData(term) {
|
||||
return new Promise((resolve) => {
|
||||
if (this.groupId) {
|
||||
Api.groupProjects(this.groupId, term, resolve);
|
||||
} else {
|
||||
return $('.js-search-clear').removeClass('hidden');
|
||||
Api.projects(term, {
|
||||
order_by: 'id',
|
||||
}, resolve);
|
||||
}
|
||||
};
|
||||
|
||||
Search.prototype.clearSearchField = function() {
|
||||
return $('.js-search-input').val('').trigger('keyup').focus();
|
||||
};
|
||||
|
||||
Search.prototype.getProjectsData = function(term) {
|
||||
return new Promise((resolve) => {
|
||||
if (this.groupId) {
|
||||
Api.groupProjects(this.groupId, term, resolve);
|
||||
} else {
|
||||
Api.projects(term, {
|
||||
order_by: 'id',
|
||||
}, resolve);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return Search;
|
||||
})();
|
||||
}).call(window);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,448 +8,445 @@ import { isInGroupsPage, isInProjectPage, getGroupSlug, getProjectSlug } from '.
|
|||
* When the user clicks `x` button it cleans the input and closes the dropdown.
|
||||
*/
|
||||
|
||||
((global) => {
|
||||
const KEYCODE = {
|
||||
ESCAPE: 27,
|
||||
BACKSPACE: 8,
|
||||
ENTER: 13,
|
||||
UP: 38,
|
||||
DOWN: 40,
|
||||
};
|
||||
const KEYCODE = {
|
||||
ESCAPE: 27,
|
||||
BACKSPACE: 8,
|
||||
ENTER: 13,
|
||||
UP: 38,
|
||||
DOWN: 40,
|
||||
};
|
||||
|
||||
class SearchAutocomplete {
|
||||
constructor({ wrap, optsEl, autocompletePath, projectId, projectRef } = {}) {
|
||||
this.bindEventContext();
|
||||
this.wrap = wrap || $('.search');
|
||||
this.optsEl = optsEl || this.wrap.find('.search-autocomplete-opts');
|
||||
this.autocompletePath = autocompletePath || this.optsEl.data('autocomplete-path');
|
||||
this.projectId = projectId || (this.optsEl.data('autocomplete-project-id') || '');
|
||||
this.projectRef = projectRef || (this.optsEl.data('autocomplete-project-ref') || '');
|
||||
this.dropdown = this.wrap.find('.dropdown');
|
||||
this.dropdownToggle = this.wrap.find('.js-dropdown-search-toggle');
|
||||
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();
|
||||
function setSearchOptions() {
|
||||
var $projectOptionsDataEl = $('.js-search-project-options');
|
||||
var $groupOptionsDataEl = $('.js-search-group-options');
|
||||
var $dashboardOptionsDataEl = $('.js-search-dashboard-options');
|
||||
|
||||
// Only when user is logged in
|
||||
if (gon.current_user_id) {
|
||||
this.createAutocomplete();
|
||||
if ($projectOptionsDataEl.length) {
|
||||
gl.projectOptions = gl.projectOptions || {};
|
||||
|
||||
var projectPath = $projectOptionsDataEl.data('project-path');
|
||||
|
||||
gl.projectOptions[projectPath] = {
|
||||
name: $projectOptionsDataEl.data('name'),
|
||||
issuesPath: $projectOptionsDataEl.data('issues-path'),
|
||||
issuesDisabled: $projectOptionsDataEl.data('issues-disabled'),
|
||||
mrPath: $projectOptionsDataEl.data('mr-path'),
|
||||
};
|
||||
}
|
||||
|
||||
if ($groupOptionsDataEl.length) {
|
||||
gl.groupOptions = gl.groupOptions || {};
|
||||
|
||||
var groupPath = $groupOptionsDataEl.data('group-path');
|
||||
|
||||
gl.groupOptions[groupPath] = {
|
||||
name: $groupOptionsDataEl.data('name'),
|
||||
issuesPath: $groupOptionsDataEl.data('issues-path'),
|
||||
mrPath: $groupOptionsDataEl.data('mr-path'),
|
||||
};
|
||||
}
|
||||
|
||||
if ($dashboardOptionsDataEl.length) {
|
||||
gl.dashboardOptions = {
|
||||
issuesPath: $dashboardOptionsDataEl.data('issues-path'),
|
||||
mrPath: $dashboardOptionsDataEl.data('mr-path'),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default class SearchAutocomplete {
|
||||
constructor({ wrap, optsEl, autocompletePath, projectId, projectRef } = {}) {
|
||||
setSearchOptions();
|
||||
this.bindEventContext();
|
||||
this.wrap = wrap || $('.search');
|
||||
this.optsEl = optsEl || this.wrap.find('.search-autocomplete-opts');
|
||||
this.autocompletePath = autocompletePath || this.optsEl.data('autocomplete-path');
|
||||
this.projectId = projectId || (this.optsEl.data('autocomplete-project-id') || '');
|
||||
this.projectRef = projectRef || (this.optsEl.data('autocomplete-project-ref') || '');
|
||||
this.dropdown = this.wrap.find('.dropdown');
|
||||
this.dropdownToggle = this.wrap.find('.js-dropdown-search-toggle');
|
||||
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();
|
||||
|
||||
// Only when user is logged in
|
||||
if (gon.current_user_id) {
|
||||
this.createAutocomplete();
|
||||
}
|
||||
|
||||
this.searchInput.addClass('disabled');
|
||||
this.saveTextLength();
|
||||
this.bindEvents();
|
||||
this.dropdownToggle.dropdown();
|
||||
}
|
||||
|
||||
// Finds an element inside wrapper element
|
||||
bindEventContext() {
|
||||
this.onSearchInputBlur = this.onSearchInputBlur.bind(this);
|
||||
this.onClearInputClick = this.onClearInputClick.bind(this);
|
||||
this.onSearchInputFocus = this.onSearchInputFocus.bind(this);
|
||||
this.onSearchInputKeyUp = this.onSearchInputKeyUp.bind(this);
|
||||
this.onSearchInputKeyDown = this.onSearchInputKeyDown.bind(this);
|
||||
}
|
||||
getElement(selector) {
|
||||
return this.wrap.find(selector);
|
||||
}
|
||||
|
||||
saveOriginalState() {
|
||||
return this.originalState = this.serializeState();
|
||||
}
|
||||
|
||||
saveTextLength() {
|
||||
return this.lastTextLength = this.searchInput.val().length;
|
||||
}
|
||||
|
||||
createAutocomplete() {
|
||||
return this.searchInput.glDropdown({
|
||||
filterInputBlur: false,
|
||||
filterable: true,
|
||||
filterRemote: true,
|
||||
highlight: true,
|
||||
enterCallback: false,
|
||||
filterInput: 'input#search',
|
||||
search: {
|
||||
fields: ['text'],
|
||||
},
|
||||
id: this.getSearchText,
|
||||
data: this.getData.bind(this),
|
||||
selectable: true,
|
||||
clicked: this.onClick.bind(this),
|
||||
});
|
||||
}
|
||||
|
||||
getSearchText(selectedObject, el) {
|
||||
return selectedObject.id ? selectedObject.text : '';
|
||||
}
|
||||
|
||||
getData(term, callback) {
|
||||
if (!term) {
|
||||
const contents = this.getCategoryContents();
|
||||
if (contents) {
|
||||
this.searchInput.data('glDropdown').filter.options.callback(contents);
|
||||
this.enableAutocomplete();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent multiple ajax calls
|
||||
if (this.loadingSuggestions) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loadingSuggestions = true;
|
||||
|
||||
return $.get(this.autocompletePath, {
|
||||
project_id: this.projectId,
|
||||
project_ref: this.projectRef,
|
||||
term: term,
|
||||
}, (response) => {
|
||||
var firstCategory, i, lastCategory, len, suggestion;
|
||||
// Hide dropdown menu if no suggestions returns
|
||||
if (!response.length) {
|
||||
this.disableAutocomplete();
|
||||
return;
|
||||
}
|
||||
|
||||
this.searchInput.addClass('disabled');
|
||||
this.saveTextLength();
|
||||
this.bindEvents();
|
||||
this.dropdownToggle.dropdown();
|
||||
const data = [];
|
||||
// List results
|
||||
firstCategory = true;
|
||||
for (i = 0, len = response.length; i < len; i += 1) {
|
||||
suggestion = response[i];
|
||||
// Add group header before list each group
|
||||
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,
|
||||
});
|
||||
}
|
||||
// Add option to proceed with the search
|
||||
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(() => { this.loadingSuggestions = false; });
|
||||
}
|
||||
|
||||
getCategoryContents() {
|
||||
const userId = gon.current_user_id;
|
||||
const userName = gon.current_username;
|
||||
const { projectOptions, groupOptions, dashboardOptions } = gl;
|
||||
|
||||
// Get options
|
||||
let options;
|
||||
if (isInGroupsPage() && groupOptions) {
|
||||
options = groupOptions[getGroupSlug()];
|
||||
} else if (isInProjectPage() && projectOptions) {
|
||||
options = projectOptions[getProjectSlug()];
|
||||
} else if (dashboardOptions) {
|
||||
options = dashboardOptions;
|
||||
}
|
||||
|
||||
// Finds an element inside wrapper element
|
||||
bindEventContext() {
|
||||
this.onSearchInputBlur = this.onSearchInputBlur.bind(this);
|
||||
this.onClearInputClick = this.onClearInputClick.bind(this);
|
||||
this.onSearchInputFocus = this.onSearchInputFocus.bind(this);
|
||||
this.onSearchInputKeyUp = this.onSearchInputKeyUp.bind(this);
|
||||
this.onSearchInputKeyDown = this.onSearchInputKeyDown.bind(this);
|
||||
}
|
||||
getElement(selector) {
|
||||
return this.wrap.find(selector);
|
||||
}
|
||||
const { issuesPath, mrPath, name, issuesDisabled } = options;
|
||||
const baseItems = [];
|
||||
|
||||
saveOriginalState() {
|
||||
return this.originalState = this.serializeState();
|
||||
}
|
||||
|
||||
saveTextLength() {
|
||||
return this.lastTextLength = this.searchInput.val().length;
|
||||
}
|
||||
|
||||
createAutocomplete() {
|
||||
return this.searchInput.glDropdown({
|
||||
filterInputBlur: false,
|
||||
filterable: true,
|
||||
filterRemote: true,
|
||||
highlight: true,
|
||||
enterCallback: false,
|
||||
filterInput: 'input#search',
|
||||
search: {
|
||||
fields: ['text'],
|
||||
},
|
||||
id: this.getSearchText,
|
||||
data: this.getData.bind(this),
|
||||
selectable: true,
|
||||
clicked: this.onClick.bind(this),
|
||||
if (name) {
|
||||
baseItems.push({
|
||||
header: `${name}`,
|
||||
});
|
||||
}
|
||||
|
||||
getSearchText(selectedObject, el) {
|
||||
return selectedObject.id ? selectedObject.text : '';
|
||||
const issueItems = [
|
||||
{
|
||||
text: 'Issues assigned to me',
|
||||
url: `${issuesPath}/?assignee_username=${userName}`,
|
||||
},
|
||||
{
|
||||
text: "Issues I've created",
|
||||
url: `${issuesPath}/?author_username=${userName}`,
|
||||
},
|
||||
];
|
||||
const mergeRequestItems = [
|
||||
{
|
||||
text: 'Merge requests assigned to me',
|
||||
url: `${mrPath}/?assignee_username=${userName}`,
|
||||
},
|
||||
{
|
||||
text: "Merge requests I've created",
|
||||
url: `${mrPath}/?author_username=${userName}`,
|
||||
},
|
||||
];
|
||||
|
||||
let items;
|
||||
if (issuesDisabled) {
|
||||
items = baseItems.concat(mergeRequestItems);
|
||||
} else {
|
||||
items = baseItems.concat(...issueItems, 'separator', ...mergeRequestItems);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
serializeState() {
|
||||
return {
|
||||
// Search Criteria
|
||||
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 badge
|
||||
_location: this.locationBadgeEl.text(),
|
||||
};
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
this.searchInput.on('keydown', this.onSearchInputKeyDown);
|
||||
this.searchInput.on('keyup', this.onSearchInputKeyUp);
|
||||
this.searchInput.on('focus', this.onSearchInputFocus);
|
||||
this.searchInput.on('blur', this.onSearchInputBlur);
|
||||
this.clearInput.on('click', this.onClearInputClick);
|
||||
this.locationBadgeEl.on('click', () => this.searchInput.focus());
|
||||
}
|
||||
|
||||
enableAutocomplete() {
|
||||
// No need to enable anything if user is not logged in
|
||||
if (!gon.current_user_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
getData(term, callback) {
|
||||
if (!term) {
|
||||
const contents = this.getCategoryContents();
|
||||
if (contents) {
|
||||
this.searchInput.data('glDropdown').filter.options.callback(contents);
|
||||
this.enableAutocomplete();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent multiple ajax calls
|
||||
if (this.loadingSuggestions) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loadingSuggestions = true;
|
||||
|
||||
return $.get(this.autocompletePath, {
|
||||
project_id: this.projectId,
|
||||
project_ref: this.projectRef,
|
||||
term: term,
|
||||
}, (response) => {
|
||||
var firstCategory, i, lastCategory, len, suggestion;
|
||||
// Hide dropdown menu if no suggestions returns
|
||||
if (!response.length) {
|
||||
this.disableAutocomplete();
|
||||
return;
|
||||
}
|
||||
|
||||
const data = [];
|
||||
// List results
|
||||
firstCategory = true;
|
||||
for (i = 0, len = response.length; i < len; i += 1) {
|
||||
suggestion = response[i];
|
||||
// Add group header before list each group
|
||||
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,
|
||||
});
|
||||
}
|
||||
// Add option to proceed with the search
|
||||
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(() => { this.loadingSuggestions = false; });
|
||||
}
|
||||
|
||||
getCategoryContents() {
|
||||
const userId = gon.current_user_id;
|
||||
const userName = gon.current_username;
|
||||
const { projectOptions, groupOptions, dashboardOptions } = gl;
|
||||
|
||||
// Get options
|
||||
let options;
|
||||
if (isInGroupsPage() && groupOptions) {
|
||||
options = groupOptions[getGroupSlug()];
|
||||
} else if (isInProjectPage() && projectOptions) {
|
||||
options = projectOptions[getProjectSlug()];
|
||||
} else if (dashboardOptions) {
|
||||
options = dashboardOptions;
|
||||
}
|
||||
|
||||
const { issuesPath, mrPath, name, issuesDisabled } = options;
|
||||
const baseItems = [];
|
||||
|
||||
if (name) {
|
||||
baseItems.push({
|
||||
header: `${name}`,
|
||||
});
|
||||
}
|
||||
|
||||
const issueItems = [
|
||||
{
|
||||
text: 'Issues assigned to me',
|
||||
url: `${issuesPath}/?assignee_username=${userName}`,
|
||||
},
|
||||
{
|
||||
text: "Issues I've created",
|
||||
url: `${issuesPath}/?author_username=${userName}`,
|
||||
},
|
||||
];
|
||||
const mergeRequestItems = [
|
||||
{
|
||||
text: 'Merge requests assigned to me',
|
||||
url: `${mrPath}/?assignee_username=${userName}`,
|
||||
},
|
||||
{
|
||||
text: "Merge requests I've created",
|
||||
url: `${mrPath}/?author_username=${userName}`,
|
||||
},
|
||||
];
|
||||
|
||||
let items;
|
||||
if (issuesDisabled) {
|
||||
items = baseItems.concat(mergeRequestItems);
|
||||
} else {
|
||||
items = baseItems.concat(...issueItems, 'separator', ...mergeRequestItems);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
serializeState() {
|
||||
return {
|
||||
// Search Criteria
|
||||
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 badge
|
||||
_location: this.locationBadgeEl.text(),
|
||||
};
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
this.searchInput.on('keydown', this.onSearchInputKeyDown);
|
||||
this.searchInput.on('keyup', this.onSearchInputKeyUp);
|
||||
this.searchInput.on('focus', this.onSearchInputFocus);
|
||||
this.searchInput.on('blur', this.onSearchInputBlur);
|
||||
this.clearInput.on('click', this.onClearInputClick);
|
||||
this.locationBadgeEl.on('click', () => this.searchInput.focus());
|
||||
}
|
||||
|
||||
enableAutocomplete() {
|
||||
// No need to enable anything if user is not logged in
|
||||
if (!gon.current_user_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the dropdown is closed, we'll open it
|
||||
if (!this.dropdown.hasClass('open')) {
|
||||
this.loadingSuggestions = false;
|
||||
this.dropdownToggle.dropdown('toggle');
|
||||
return this.searchInput.removeClass('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
// Saves last length of the entered text
|
||||
onSearchInputKeyDown() {
|
||||
return this.saveTextLength();
|
||||
}
|
||||
|
||||
onSearchInputKeyUp(e) {
|
||||
switch (e.keyCode) {
|
||||
case KEYCODE.BACKSPACE:
|
||||
// when trying to remove the location badge
|
||||
if (this.lastTextLength === 0 && this.badgePresent()) {
|
||||
this.removeLocationBadge();
|
||||
}
|
||||
// When removing the last character and no badge is present
|
||||
if (this.lastTextLength === 1) {
|
||||
this.disableAutocomplete();
|
||||
}
|
||||
// When removing any character from existin value
|
||||
if (this.lastTextLength > 1) {
|
||||
this.enableAutocomplete();
|
||||
}
|
||||
break;
|
||||
case KEYCODE.ESCAPE:
|
||||
this.restoreOriginalState();
|
||||
break;
|
||||
case KEYCODE.ENTER:
|
||||
this.disableAutocomplete();
|
||||
break;
|
||||
case KEYCODE.UP:
|
||||
case KEYCODE.DOWN:
|
||||
return;
|
||||
default:
|
||||
// Handle the case when deleting the input value other than backspace
|
||||
// e.g. Pressing ctrl + backspace or ctrl + x
|
||||
if (this.searchInput.val() === '') {
|
||||
this.disableAutocomplete();
|
||||
} else {
|
||||
// We should display the menu only when input is not empty
|
||||
if (e.keyCode !== KEYCODE.ENTER) {
|
||||
this.enableAutocomplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
this.wrap.toggleClass('has-value', !!e.target.value);
|
||||
}
|
||||
|
||||
onSearchInputFocus() {
|
||||
this.isFocused = true;
|
||||
this.wrap.addClass('search-active');
|
||||
if (this.getValue() === '') {
|
||||
return this.getData();
|
||||
}
|
||||
}
|
||||
|
||||
getValue() {
|
||||
return this.searchInput.val();
|
||||
}
|
||||
|
||||
onClearInputClick(e) {
|
||||
e.preventDefault();
|
||||
this.wrap.toggleClass('has-value', !!e.target.value);
|
||||
return this.searchInput.val('').focus();
|
||||
}
|
||||
|
||||
onSearchInputBlur(e) {
|
||||
this.isFocused = false;
|
||||
this.wrap.removeClass('search-active');
|
||||
// If input is blank then restore state
|
||||
if (this.searchInput.val() === '') {
|
||||
return this.restoreOriginalState();
|
||||
}
|
||||
}
|
||||
|
||||
addLocationBadge(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');
|
||||
}
|
||||
|
||||
hasLocationBadge() {
|
||||
return this.wrap.is('.has-location-badge');
|
||||
}
|
||||
|
||||
restoreOriginalState() {
|
||||
var i, input, inputs, len;
|
||||
inputs = Object.keys(this.originalState);
|
||||
for (i = 0, len = inputs.length; i < len; i += 1) {
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
badgePresent() {
|
||||
return this.locationBadgeEl.length;
|
||||
}
|
||||
|
||||
resetSearchState() {
|
||||
var i, input, inputs, len, results;
|
||||
inputs = Object.keys(this.originalState);
|
||||
results = [];
|
||||
for (i = 0, len = inputs.length; i < len; i += 1) {
|
||||
input = inputs[i];
|
||||
// _location isnt a input
|
||||
if (input === '_location') {
|
||||
break;
|
||||
}
|
||||
results.push(this.getElement("#" + input).val(''));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
removeLocationBadge() {
|
||||
this.locationBadgeEl.hide();
|
||||
this.resetSearchState();
|
||||
this.wrap.removeClass('has-location-badge');
|
||||
return this.disableAutocomplete();
|
||||
}
|
||||
|
||||
disableAutocomplete() {
|
||||
if (!this.searchInput.hasClass('disabled') && this.dropdown.hasClass('open')) {
|
||||
this.searchInput.addClass('disabled');
|
||||
this.dropdown.removeClass('open').trigger('hidden.bs.dropdown');
|
||||
this.restoreMenu();
|
||||
}
|
||||
}
|
||||
|
||||
restoreMenu() {
|
||||
var html;
|
||||
html = '<ul><li class="dropdown-menu-empty-item"><a>Loading...</a></li></ul>';
|
||||
return this.dropdownContent.html(html);
|
||||
}
|
||||
|
||||
onClick(item, $el, e) {
|
||||
if (location.pathname.indexOf(item.url) !== -1) {
|
||||
if (!e.metaKey) 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();
|
||||
}
|
||||
// If the dropdown is closed, we'll open it
|
||||
if (!this.dropdown.hasClass('open')) {
|
||||
this.loadingSuggestions = false;
|
||||
this.dropdownToggle.dropdown('toggle');
|
||||
return this.searchInput.removeClass('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
global.SearchAutocomplete = SearchAutocomplete;
|
||||
// Saves last length of the entered text
|
||||
onSearchInputKeyDown() {
|
||||
return this.saveTextLength();
|
||||
}
|
||||
|
||||
$(function() {
|
||||
var $projectOptionsDataEl = $('.js-search-project-options');
|
||||
var $groupOptionsDataEl = $('.js-search-group-options');
|
||||
var $dashboardOptionsDataEl = $('.js-search-dashboard-options');
|
||||
|
||||
if ($projectOptionsDataEl.length) {
|
||||
gl.projectOptions = gl.projectOptions || {};
|
||||
|
||||
var projectPath = $projectOptionsDataEl.data('project-path');
|
||||
|
||||
gl.projectOptions[projectPath] = {
|
||||
name: $projectOptionsDataEl.data('name'),
|
||||
issuesPath: $projectOptionsDataEl.data('issues-path'),
|
||||
issuesDisabled: $projectOptionsDataEl.data('issues-disabled'),
|
||||
mrPath: $projectOptionsDataEl.data('mr-path'),
|
||||
};
|
||||
onSearchInputKeyUp(e) {
|
||||
switch (e.keyCode) {
|
||||
case KEYCODE.BACKSPACE:
|
||||
// when trying to remove the location badge
|
||||
if (this.lastTextLength === 0 && this.badgePresent()) {
|
||||
this.removeLocationBadge();
|
||||
}
|
||||
// When removing the last character and no badge is present
|
||||
if (this.lastTextLength === 1) {
|
||||
this.disableAutocomplete();
|
||||
}
|
||||
// When removing any character from existin value
|
||||
if (this.lastTextLength > 1) {
|
||||
this.enableAutocomplete();
|
||||
}
|
||||
break;
|
||||
case KEYCODE.ESCAPE:
|
||||
this.restoreOriginalState();
|
||||
break;
|
||||
case KEYCODE.ENTER:
|
||||
this.disableAutocomplete();
|
||||
break;
|
||||
case KEYCODE.UP:
|
||||
case KEYCODE.DOWN:
|
||||
return;
|
||||
default:
|
||||
// Handle the case when deleting the input value other than backspace
|
||||
// e.g. Pressing ctrl + backspace or ctrl + x
|
||||
if (this.searchInput.val() === '') {
|
||||
this.disableAutocomplete();
|
||||
} else {
|
||||
// We should display the menu only when input is not empty
|
||||
if (e.keyCode !== KEYCODE.ENTER) {
|
||||
this.enableAutocomplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
this.wrap.toggleClass('has-value', !!e.target.value);
|
||||
}
|
||||
|
||||
if ($groupOptionsDataEl.length) {
|
||||
gl.groupOptions = gl.groupOptions || {};
|
||||
|
||||
var groupPath = $groupOptionsDataEl.data('group-path');
|
||||
|
||||
gl.groupOptions[groupPath] = {
|
||||
name: $groupOptionsDataEl.data('name'),
|
||||
issuesPath: $groupOptionsDataEl.data('issues-path'),
|
||||
mrPath: $groupOptionsDataEl.data('mr-path'),
|
||||
};
|
||||
onSearchInputFocus() {
|
||||
this.isFocused = true;
|
||||
this.wrap.addClass('search-active');
|
||||
if (this.getValue() === '') {
|
||||
return this.getData();
|
||||
}
|
||||
}
|
||||
|
||||
if ($dashboardOptionsDataEl.length) {
|
||||
gl.dashboardOptions = {
|
||||
issuesPath: $dashboardOptionsDataEl.data('issues-path'),
|
||||
mrPath: $dashboardOptionsDataEl.data('mr-path'),
|
||||
};
|
||||
getValue() {
|
||||
return this.searchInput.val();
|
||||
}
|
||||
|
||||
onClearInputClick(e) {
|
||||
e.preventDefault();
|
||||
this.wrap.toggleClass('has-value', !!e.target.value);
|
||||
return this.searchInput.val('').focus();
|
||||
}
|
||||
|
||||
onSearchInputBlur(e) {
|
||||
this.isFocused = false;
|
||||
this.wrap.removeClass('search-active');
|
||||
// If input is blank then restore state
|
||||
if (this.searchInput.val() === '') {
|
||||
return this.restoreOriginalState();
|
||||
}
|
||||
});
|
||||
})(window.gl || (window.gl = {}));
|
||||
}
|
||||
|
||||
addLocationBadge(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');
|
||||
}
|
||||
|
||||
hasLocationBadge() {
|
||||
return this.wrap.is('.has-location-badge');
|
||||
}
|
||||
|
||||
restoreOriginalState() {
|
||||
var i, input, inputs, len;
|
||||
inputs = Object.keys(this.originalState);
|
||||
for (i = 0, len = inputs.length; i < len; i += 1) {
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
badgePresent() {
|
||||
return this.locationBadgeEl.length;
|
||||
}
|
||||
|
||||
resetSearchState() {
|
||||
var i, input, inputs, len, results;
|
||||
inputs = Object.keys(this.originalState);
|
||||
results = [];
|
||||
for (i = 0, len = inputs.length; i < len; i += 1) {
|
||||
input = inputs[i];
|
||||
// _location isnt a input
|
||||
if (input === '_location') {
|
||||
break;
|
||||
}
|
||||
results.push(this.getElement("#" + input).val(''));
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
removeLocationBadge() {
|
||||
this.locationBadgeEl.hide();
|
||||
this.resetSearchState();
|
||||
this.wrap.removeClass('has-location-badge');
|
||||
return this.disableAutocomplete();
|
||||
}
|
||||
|
||||
disableAutocomplete() {
|
||||
if (!this.searchInput.hasClass('disabled') && this.dropdown.hasClass('open')) {
|
||||
this.searchInput.addClass('disabled');
|
||||
this.dropdown.removeClass('open').trigger('hidden.bs.dropdown');
|
||||
this.restoreMenu();
|
||||
}
|
||||
}
|
||||
|
||||
restoreMenu() {
|
||||
var html;
|
||||
html = '<ul><li class="dropdown-menu-empty-item"><a>Loading...</a></li></ul>';
|
||||
return this.dropdownContent.html(html);
|
||||
}
|
||||
|
||||
onClick(item, $el, e) {
|
||||
if (location.pathname.indexOf(item.url) !== -1) {
|
||||
if (!e.metaKey) 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import FilesCommentButton from './files_comment_button';
|
||||
import imageDiffHelper from './image_diff/helpers/index';
|
||||
import syntaxHighlight from './syntax_highlight';
|
||||
|
||||
const WRAPPER = '<div class="diff-content"></div>';
|
||||
const LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>';
|
||||
|
@ -64,7 +65,7 @@ export default class SingleFileDiff {
|
|||
_this.loadingContent.hide();
|
||||
if (data.html) {
|
||||
_this.content = $(data.html);
|
||||
_this.content.syntaxHighlight();
|
||||
syntaxHighlight(_this.content);
|
||||
} else {
|
||||
_this.hasError = true;
|
||||
_this.content = $(ERROR_HTML);
|
||||
|
|
|
@ -10,17 +10,15 @@
|
|||
// <div class="js-syntax-highlight"></div>
|
||||
//
|
||||
|
||||
$.fn.syntaxHighlight = function() {
|
||||
var $children;
|
||||
|
||||
if ($(this).hasClass('js-syntax-highlight')) {
|
||||
export default function syntaxHighlight(el) {
|
||||
if ($(el).hasClass('js-syntax-highlight')) {
|
||||
// Given the element itself, apply highlighting
|
||||
return $(this).addClass(gon.user_color_scheme);
|
||||
return $(el).addClass(gon.user_color_scheme);
|
||||
} else {
|
||||
// Given a parent element, recurse to any of its applicable children
|
||||
$children = $(this).find('.js-syntax-highlight');
|
||||
const $children = $(el).find('.js-syntax-highlight');
|
||||
if ($children.length) {
|
||||
return $children.syntaxHighlight();
|
||||
return syntaxHighlight($children);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* eslint-disable space-before-function-paren, max-len, no-var, one-var, one-var-declaration-per-line, no-unused-expressions, consistent-return, no-param-reassign, default-case, no-return-assign, comma-dangle, object-shorthand, prefer-template, quotes, new-parens, vars-on-top, new-cap, max-len */
|
||||
|
||||
import '~/gl_dropdown';
|
||||
import '~/search_autocomplete';
|
||||
import SearchAutocomplete from '~/search_autocomplete';
|
||||
import '~/lib/utils/common_utils';
|
||||
import * as urlUtils from '~/lib/utils/url_utility';
|
||||
|
||||
|
@ -128,7 +128,7 @@ import * as urlUtils from '~/lib/utils/url_utility';
|
|||
window.gon.current_user_id = userId;
|
||||
window.gon.current_username = userName;
|
||||
|
||||
return widget = new gl.SearchAutocomplete;
|
||||
return widget = new SearchAutocomplete();
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
|
|
|
@ -1,44 +1,42 @@
|
|||
/* eslint-disable space-before-function-paren, no-var, no-return-assign, quotes */
|
||||
|
||||
import '~/syntax_highlight';
|
||||
import syntaxHighlight from '~/syntax_highlight';
|
||||
|
||||
(function() {
|
||||
describe('Syntax Highlighter', function() {
|
||||
var stubUserColorScheme;
|
||||
stubUserColorScheme = function(value) {
|
||||
if (window.gon == null) {
|
||||
window.gon = {};
|
||||
}
|
||||
return window.gon.user_color_scheme = value;
|
||||
};
|
||||
describe('on a js-syntax-highlight element', function() {
|
||||
beforeEach(function() {
|
||||
return setFixtures('<div class="js-syntax-highlight"></div>');
|
||||
});
|
||||
return it('applies syntax highlighting', function() {
|
||||
stubUserColorScheme('monokai');
|
||||
$('.js-syntax-highlight').syntaxHighlight();
|
||||
return expect($('.js-syntax-highlight')).toHaveClass('monokai');
|
||||
});
|
||||
describe('Syntax Highlighter', function() {
|
||||
var stubUserColorScheme;
|
||||
stubUserColorScheme = function(value) {
|
||||
if (window.gon == null) {
|
||||
window.gon = {};
|
||||
}
|
||||
return window.gon.user_color_scheme = value;
|
||||
};
|
||||
describe('on a js-syntax-highlight element', function() {
|
||||
beforeEach(function() {
|
||||
return setFixtures('<div class="js-syntax-highlight"></div>');
|
||||
});
|
||||
return describe('on a parent element', function() {
|
||||
beforeEach(function() {
|
||||
return setFixtures("<div class=\"parent\">\n <div class=\"js-syntax-highlight\"></div>\n <div class=\"foo\"></div>\n <div class=\"js-syntax-highlight\"></div>\n</div>");
|
||||
});
|
||||
it('applies highlighting to all applicable children', function() {
|
||||
stubUserColorScheme('monokai');
|
||||
$('.parent').syntaxHighlight();
|
||||
expect($('.parent, .foo')).not.toHaveClass('monokai');
|
||||
return expect($('.monokai').length).toBe(2);
|
||||
});
|
||||
return it('prevents an infinite loop when no matches exist', function() {
|
||||
var highlight;
|
||||
setFixtures('<div></div>');
|
||||
highlight = function() {
|
||||
return $('div').syntaxHighlight();
|
||||
};
|
||||
return expect(highlight).not.toThrow();
|
||||
});
|
||||
return it('applies syntax highlighting', function() {
|
||||
stubUserColorScheme('monokai');
|
||||
syntaxHighlight($('.js-syntax-highlight'));
|
||||
return expect($('.js-syntax-highlight')).toHaveClass('monokai');
|
||||
});
|
||||
});
|
||||
}).call(window);
|
||||
return describe('on a parent element', function() {
|
||||
beforeEach(function() {
|
||||
return setFixtures("<div class=\"parent\">\n <div class=\"js-syntax-highlight\"></div>\n <div class=\"foo\"></div>\n <div class=\"js-syntax-highlight\"></div>\n</div>");
|
||||
});
|
||||
it('applies highlighting to all applicable children', function() {
|
||||
stubUserColorScheme('monokai');
|
||||
syntaxHighlight($('.parent'));
|
||||
expect($('.parent, .foo')).not.toHaveClass('monokai');
|
||||
return expect($('.monokai').length).toBe(2);
|
||||
});
|
||||
return it('prevents an infinite loop when no matches exist', function() {
|
||||
var highlight;
|
||||
setFixtures('<div></div>');
|
||||
highlight = function() {
|
||||
return syntaxHighlight($('div'));
|
||||
};
|
||||
return expect(highlight).not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue