diff options
Diffstat (limited to 'app')
-rw-r--r-- | app/assets/javascripts/dispatcher.js | 7 | ||||
-rw-r--r-- | app/assets/javascripts/main.js | 5 | ||||
-rw-r--r-- | app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js | 3 | ||||
-rw-r--r-- | app/assets/javascripts/merge_request_tabs.js | 3 | ||||
-rw-r--r-- | app/assets/javascripts/project_find_file.js | 298 | ||||
-rw-r--r-- | app/assets/javascripts/render_gfm.js | 4 | ||||
-rw-r--r-- | app/assets/javascripts/repo/components/repo_preview.vue | 3 | ||||
-rw-r--r-- | app/assets/javascripts/search.js | 207 | ||||
-rw-r--r-- | app/assets/javascripts/search_autocomplete.js | 769 | ||||
-rw-r--r-- | app/assets/javascripts/single_file_diff.js | 3 | ||||
-rw-r--r-- | app/assets/javascripts/syntax_highlight.js | 14 |
11 files changed, 650 insertions, 666 deletions
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js index 299e43a4e90..12402fd645f 100644 --- a/app/assets/javascripts/dispatcher.js +++ b/app/assets/javascripts/dispatcher.js @@ -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(); } }; diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js index 9e4047b6840..30cdb523bef 100644 --- a/app/assets/javascripts/main.js +++ b/app/assets/javascripts/main.js @@ -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'; diff --git a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js index 17591829b76..94561d6b7c3 100644 --- a/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js +++ b/app/assets/javascripts/merge_conflicts/merge_conflicts_bundle.js @@ -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')); }); }); }, diff --git a/app/assets/javascripts/merge_request_tabs.js b/app/assets/javascripts/merge_request_tabs.js index 589e65647ac..a630daa6bdc 100644 --- a/app/assets/javascripts/merge_request_tabs.js +++ b/app/assets/javascripts/merge_request_tabs.js @@ -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(); diff --git a/app/assets/javascripts/project_find_file.js b/app/assets/javascripts/project_find_file.js index 19682b20a4a..0da32b4a3cc 100644 --- a/app/assets/javascripts/project_find_file.js +++ b/app/assets/javascripts/project_find_file.js @@ -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); +// 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()); + } + matchedChars = []; + element.append(document.createTextNode(unmatched)); } - - 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 - }; + matchedChars.push(text[matchIndex]); + lastIndex = matchIndex + 1; + } + if (matchedChars.length) { + element.append(matchedChars.join("").bold()); + } + return element.append(document.createTextNode(text.substring(lastIndex))); +}; + +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); + } + + 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(); + } + }; + })(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 - 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) - }); - }; + 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 - 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; - } - if (matchedChars.length) { - element.append(matchedChars.join("").bold()); + 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; } - 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); + if (searchText) { + matches = fuzzaldrinPlus.match(filePath, searchText); } - return $tr; - }; - - 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); + blobItemUrl = this.options.blobUrlTemplate + "/" + filePath; + html = ProjectFindFile.makeHtml(filePath, matches, blobItemUrl); + results.push(this.element.find(".tree-table > tbody").append(html)); + } + return results; + } + + // 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; } - return selectedRow.addClass("selected").focus(); + } else { + selectedRow = rows.eq(0); } - }; - - ProjectFindFile.prototype.selectRowUp = function() { - return this.selectRow("UP"); - }; + return selectedRow.addClass("selected").focus(); + } + } - ProjectFindFile.prototype.selectRowDown = function() { - return this.selectRow("DOWN"); - }; + selectRowUp() { + return this.selectRow("UP"); + } - ProjectFindFile.prototype.goToTree = function() { - return location.href = this.options.treeUrl; - }; + selectRowDown() { + return this.selectRow("DOWN"); + } - ProjectFindFile.prototype.goToBlob = function() { - var $link = this.element.find(".tree-item.selected .tree-item-file-name a"); + goToTree() { + return location.href = this.options.treeUrl; + } - if ($link.length) { - $link.get(0).click(); - } - }; + goToBlob() { + var $link = this.element.find(".tree-item.selected .tree-item-file-name a"); - return ProjectFindFile; - })(); -}).call(window); + if ($link.length) { + $link.get(0).click(); + } + } +} diff --git a/app/assets/javascripts/render_gfm.js b/app/assets/javascripts/render_gfm.js index c91a0d9ba41..5482c55f8bb 100644 --- a/app/assets/javascripts/render_gfm.js +++ b/app/assets/javascripts/render_gfm.js @@ -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; diff --git a/app/assets/javascripts/repo/components/repo_preview.vue b/app/assets/javascripts/repo/components/repo_preview.vue index 6ce9267f598..425c55fafb5 100644 --- a/app/assets/javascripts/repo/components/repo_preview.vue +++ b/app/assets/javascripts/repo/components/repo_preview.vue @@ -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() { diff --git a/app/assets/javascripts/search.js b/app/assets/javascripts/search.js index 07fee53d814..363322af47a 100644 --- a/app/assets/javascripts/search.js +++ b/app/assets/javascripts/search.js @@ -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(), + }); + } - 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); - }; + 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)); + } - Search.prototype.submitSearch = function() { - return $('.js-search-form').submit(); - }; + static submitSearch() { + return $('.js-search-form').submit(); + } - Search.prototype.searchKeyUp = function() { - var $input; - $input = $(this); - if ($input.val() === '') { - return $('.js-search-clear').addClass('hidden'); - } else { - return $('.js-search-clear').removeClass('hidden'); - } - }; - - Search.prototype.clearSearchField = function() { - return $('.js-search-input').val('').trigger('keyup').focus(); - }; + searchKeyUp() { + const $input = $(this); + if ($input.val() === '') { + $('.js-search-clear').addClass('hidden'); + } else { + $('.js-search-clear').removeClass('hidden'); + } + } - 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); - } - }); - }; + clearSearchField() { + return $(this.searchInput).val('').trigger('keyup').focus(); + } - return Search; - })(); -}).call(window); + getProjectsData(term) { + return new Promise((resolve) => { + if (this.groupId) { + Api.groupProjects(this.groupId, term, resolve); + } else { + Api.projects(term, { + order_by: 'id', + }, resolve); + } + }); + } +} diff --git a/app/assets/javascripts/search_autocomplete.js b/app/assets/javascripts/search_autocomplete.js index e40a3596200..98b524f7e3f 100644 --- a/app/assets/javascripts/search_autocomplete.js +++ b/app/assets/javascripts/search_autocomplete.js @@ -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, - }; - - 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(); - - // Only when user is logged in - if (gon.current_user_id) { - this.createAutocomplete(); - } +const KEYCODE = { + ESCAPE: 27, + BACKSPACE: 8, + ENTER: 13, + UP: 38, + DOWN: 40, +}; + +function setSearchOptions() { + 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'), + }; + } - this.searchInput.addClass('disabled'); - this.saveTextLength(); - this.bindEvents(); - this.dropdownToggle.dropdown(); - } + if ($groupOptionsDataEl.length) { + gl.groupOptions = gl.groupOptions || {}; - // 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); - } + var groupPath = $groupOptionsDataEl.data('group-path'); - saveOriginalState() { - return this.originalState = this.serializeState(); - } + gl.groupOptions[groupPath] = { + name: $groupOptionsDataEl.data('name'), + issuesPath: $groupOptionsDataEl.data('issues-path'), + mrPath: $groupOptionsDataEl.data('mr-path'), + }; + } - saveTextLength() { - return this.lastTextLength = this.searchInput.val().length; + 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(); } - 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), - }); + 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; } - getSearchText(selectedObject, el) { - return selectedObject.id ? selectedObject.text : ''; + // Prevent multiple ajax calls + if (this.loadingSuggestions) { + return; } - getData(term, callback) { - if (!term) { - const contents = this.getCategoryContents(); - if (contents) { - this.searchInput.data('glDropdown').filter.options.callback(contents); - this.enableAutocomplete(); - } - return; - } + this.loadingSuggestions = true; - // Prevent multiple ajax calls - if (this.loadingSuggestions) { + 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.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; + 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({ - 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()), + header: suggestion.category, }); + lastCategory = suggestion.category; } - 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; + data.push({ + id: (suggestion.category.toLowerCase()) + "-" + suggestion.id, + category: suggestion.category, + text: suggestion.label, + url: suggestion.url, + }); } - - const { issuesPath, mrPath, name, issuesDisabled } = options; - const baseItems = []; - - if (name) { - baseItems.push({ - header: `${name}`, + // 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; }); + } - 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; + 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; } - 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(), - }; + const { issuesPath, mrPath, name, issuesDisabled } = options; + const baseItems = []; + + if (name) { + baseItems.push({ + header: `${name}`, + }); } - 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()); + 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; + } - enableAutocomplete() { - // No need to enable anything if user is not logged in - if (!gon.current_user_id) { - return; - } + 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(), + }; + } - // 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'); - } + 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; } - // Saves last length of the entered text - onSearchInputKeyDown() { - return this.saveTextLength(); + // 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'); } + } - 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: + // 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(); - 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(); - } + } 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); + } } + this.wrap.toggleClass('has-value', !!e.target.value); + } - onSearchInputFocus() { - this.isFocused = true; - this.wrap.addClass('search-active'); - if (this.getValue() === '') { - return this.getData(); - } + onSearchInputFocus() { + this.isFocused = true; + this.wrap.addClass('search-active'); + if (this.getValue() === '') { + return this.getData(); } + } - getValue() { - return this.searchInput.val(); - } + getValue() { + return this.searchInput.val(); + } - onClearInputClick(e) { - e.preventDefault(); - this.wrap.toggleClass('has-value', !!e.target.value); - return this.searchInput.val('').focus(); - } + 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(); - } + 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'); - } + 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'); - } + 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, - }); - } + 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]); } - - badgePresent() { - return this.locationBadgeEl.length; + if (this.originalState._location === '') { + return this.locationBadgeEl.hide(); + } else { + return this.addLocationBadge({ + value: this.originalState._location, + }); } + } - 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('')); + 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; } - return results; + results.push(this.getElement("#" + input).val('')); } + return results; + } - removeLocationBadge() { - this.locationBadgeEl.hide(); - this.resetSearchState(); - this.wrap.removeClass('has-location-badge'); - return this.disableAutocomplete(); - } + 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(); - } + 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); - } + 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', - }); - } + 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(); } + $el.removeClass('is-active'); + this.disableAutocomplete(); + return this.searchInput.val('').focus(); } } - - global.SearchAutocomplete = SearchAutocomplete; - - $(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'), - }; - } - - 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'), - }; - } - }); -})(window.gl || (window.gl = {})); +} diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js index 3f811c59cb9..95e51bc4e7a 100644 --- a/app/assets/javascripts/single_file_diff.js +++ b/app/assets/javascripts/single_file_diff.js @@ -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); diff --git a/app/assets/javascripts/syntax_highlight.js b/app/assets/javascripts/syntax_highlight.js index 662d6b36c16..62bdef76c55 100644 --- a/app/assets/javascripts/syntax_highlight.js +++ b/app/assets/javascripts/syntax_highlight.js @@ -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); } } -}; +} |