From b59f83f39bfca6408a2187f1b47692f022e2ba2d Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 15 Oct 2021 19:20:53 +0800 Subject: [PATCH 1/7] split `index.js` to separate files --- templates/repo/branch/list.tmpl | 2 +- web_src/js/code/linebutton.js | 11 - web_src/js/features/admin-common.js | 216 + web_src/js/features/admin-emails.js | 14 + web_src/js/features/clipboard.js | 37 +- web_src/js/features/common-global.js | 318 ++ web_src/js/features/common-issue.js | 42 + web_src/js/features/common-organization.js | 27 + web_src/js/features/comp/ColorPicker.js | 13 + web_src/js/features/comp/CommentSimpleMDE.js | 74 + web_src/js/features/comp/ImagePaste.js | 93 + web_src/js/features/comp/IssueCheckboxFix.js | 0 web_src/js/features/comp/LabelEdit.js | 32 + .../js/features/comp/MarkupContentPreview.js | 23 + web_src/js/features/comp/ReactionSelector.js | 50 + web_src/js/features/comp/SearchUserBox.js | 38 + web_src/js/features/comp/WebHookEditor.js | 42 + web_src/js/features/install.js | 94 + web_src/js/features/org-team.js | 39 + web_src/js/features/repo-branch.js | 9 + web_src/js/features/repo-code.js | 147 + web_src/js/features/repo-commit.js | 9 + web_src/js/features/repo-common.js | 108 + web_src/js/features/repo-diff.js | 88 + web_src/js/features/repo-editor.js | 182 + web_src/js/features/repo-home.js | 182 + web_src/js/features/repo-issue.js | 659 +++ web_src/js/features/repo-legacy.js | 575 +++ web_src/js/features/repo-migrate.js | 50 + web_src/js/features/repo-release.js | 31 + web_src/js/features/repo-settings.js | 68 + web_src/js/features/repo-template.js | 51 + web_src/js/features/repo-wiki.js | 176 + web_src/js/features/sshkey-helper.js | 12 + web_src/js/features/user-auth-u2f.js | 127 + web_src/js/features/user-auth.js | 48 + web_src/js/features/user-settings.js | 18 + web_src/js/index.js | 3557 +---------------- 38 files changed, 3782 insertions(+), 3480 deletions(-) delete mode 100644 web_src/js/code/linebutton.js create mode 100644 web_src/js/features/admin-common.js create mode 100644 web_src/js/features/admin-emails.js create mode 100644 web_src/js/features/common-global.js create mode 100644 web_src/js/features/common-issue.js create mode 100644 web_src/js/features/common-organization.js create mode 100644 web_src/js/features/comp/ColorPicker.js create mode 100644 web_src/js/features/comp/CommentSimpleMDE.js create mode 100644 web_src/js/features/comp/ImagePaste.js create mode 100644 web_src/js/features/comp/IssueCheckboxFix.js create mode 100644 web_src/js/features/comp/LabelEdit.js create mode 100644 web_src/js/features/comp/MarkupContentPreview.js create mode 100644 web_src/js/features/comp/ReactionSelector.js create mode 100644 web_src/js/features/comp/SearchUserBox.js create mode 100644 web_src/js/features/comp/WebHookEditor.js create mode 100644 web_src/js/features/install.js create mode 100644 web_src/js/features/org-team.js create mode 100644 web_src/js/features/repo-branch.js create mode 100644 web_src/js/features/repo-code.js create mode 100644 web_src/js/features/repo-commit.js create mode 100644 web_src/js/features/repo-common.js create mode 100644 web_src/js/features/repo-diff.js create mode 100644 web_src/js/features/repo-editor.js create mode 100644 web_src/js/features/repo-home.js create mode 100644 web_src/js/features/repo-issue.js create mode 100644 web_src/js/features/repo-legacy.js create mode 100644 web_src/js/features/repo-migrate.js create mode 100644 web_src/js/features/repo-release.js create mode 100644 web_src/js/features/repo-settings.js create mode 100644 web_src/js/features/repo-template.js create mode 100644 web_src/js/features/repo-wiki.js create mode 100644 web_src/js/features/sshkey-helper.js create mode 100644 web_src/js/features/user-auth-u2f.js create mode 100644 web_src/js/features/user-auth.js create mode 100644 web_src/js/features/user-settings.js diff --git a/templates/repo/branch/list.tmpl b/templates/repo/branch/list.tmpl index 1e21863ee18a9..cadf91df041c5 100644 --- a/templates/repo/branch/list.tmpl +++ b/templates/repo/branch/list.tmpl @@ -125,7 +125,7 @@ {{if .IsDeleted}} {{svg "octicon-reply"}} {{else}} - + {{svg "octicon-trash"}} {{end}} diff --git a/web_src/js/code/linebutton.js b/web_src/js/code/linebutton.js deleted file mode 100644 index c177ca665894e..0000000000000 --- a/web_src/js/code/linebutton.js +++ /dev/null @@ -1,11 +0,0 @@ -import {svg} from '../svg.js'; - -export function showLineButton() { - if ($('.code-line-menu').length === 0) return; - $('.code-line-button').remove(); - $('.code-view td.lines-code.active').closest('tr').find('td:eq(0)').first().prepend( - $(``) - ); - $('.code-line-menu').appendTo($('.code-view')); - $('.code-line-button').popup({popup: $('.code-line-menu'), on: 'click'}); -} diff --git a/web_src/js/features/admin-common.js b/web_src/js/features/admin-common.js new file mode 100644 index 0000000000000..bad176143a47b --- /dev/null +++ b/web_src/js/features/admin-common.js @@ -0,0 +1,216 @@ +const {csrf} = window.config; + +function initAdminCommon() { + if ($('.admin').length === 0) { + return; + } + + // New user + if ($('.admin.new.user').length > 0 || $('.admin.edit.user').length > 0) { + $('#login_type').on('change', function () { + if ($(this).val().substring(0, 1) === '0') { + $('#user_name').removeAttr('disabled'); + $('#login_name').removeAttr('required'); + $('.non-local').hide(); + $('.local').show(); + $('#user_name').focus(); + + if ($(this).data('password') === 'required') { + $('#password').attr('required', 'required'); + } + } else { + if ($('.admin.edit.user').length > 0) { + $('#user_name').attr('disabled', 'disabled'); + } + $('#login_name').attr('required', 'required'); + $('.non-local').show(); + $('.local').hide(); + $('#login_name').focus(); + + $('#password').removeAttr('required'); + } + }); + } + + function onSecurityProtocolChange() { + if ($('#security_protocol').val() > 0) { + $('.has-tls').show(); + } else { + $('.has-tls').hide(); + } + } + + function onUsePagedSearchChange() { + if ($('#use_paged_search').prop('checked')) { + $('.search-page-size').show() + .find('input').attr('required', 'required'); + } else { + $('.search-page-size').hide() + .find('input').removeAttr('required'); + } + } + + function onOAuth2Change(applyDefaultValues) { + $('.open_id_connect_auto_discovery_url, .oauth2_use_custom_url').hide(); + $('.open_id_connect_auto_discovery_url input[required]').removeAttr('required'); + + const provider = $('#oauth2_provider').val(); + switch (provider) { + case 'openidConnect': + $('.open_id_connect_auto_discovery_url input').attr('required', 'required'); + $('.open_id_connect_auto_discovery_url').show(); + break; + default: + if ($(`#${provider}_customURLSettings`).data('required')) { + $('#oauth2_use_custom_url').attr('checked', 'checked'); + } + if ($(`#${provider}_customURLSettings`).data('available')) { + $('.oauth2_use_custom_url').show(); + } + } + onOAuth2UseCustomURLChange(applyDefaultValues); + } + + function onOAuth2UseCustomURLChange(applyDefaultValues) { + const provider = $('#oauth2_provider').val(); + $('.oauth2_use_custom_url_field').hide(); + $('.oauth2_use_custom_url_field input[required]').removeAttr('required'); + + if ($('#oauth2_use_custom_url').is(':checked')) { + for (const custom of ['token_url', 'auth_url', 'profile_url', 'email_url', 'tenant']) { + if (applyDefaultValues) { + $(`#oauth2_${custom}`).val($(`#${provider}_${custom}`).val()); + } + if ($(`#${provider}_${custom}`).data('available')) { + $(`.oauth2_${custom} input`).attr('required', 'required'); + $(`.oauth2_${custom}`).show(); + } + } + } + } + + function onVerifyGroupMembershipChange() { + if ($('#groups_enabled').is(':checked')) { + $('#groups_enabled_change').show(); + } else { + $('#groups_enabled_change').hide(); + } + } + + // New authentication + if ($('.admin.new.authentication').length > 0) { + $('#auth_type').on('change', function () { + $('.ldap, .dldap, .smtp, .pam, .oauth2, .has-tls, .search-page-size, .sspi').hide(); + + $('.ldap input[required], .binddnrequired input[required], .dldap input[required], .smtp input[required], .pam input[required], .oauth2 input[required], .has-tls input[required], .sspi input[required]').removeAttr('required'); + $('.binddnrequired').removeClass('required'); + + const authType = $(this).val(); + switch (authType) { + case '2': // LDAP + $('.ldap').show(); + $('.binddnrequired input, .ldap div.required:not(.dldap) input').attr('required', 'required'); + $('.binddnrequired').addClass('required'); + break; + case '3': // SMTP + $('.smtp').show(); + $('.has-tls').show(); + $('.smtp div.required input, .has-tls').attr('required', 'required'); + break; + case '4': // PAM + $('.pam').show(); + $('.pam input').attr('required', 'required'); + break; + case '5': // LDAP + $('.dldap').show(); + $('.dldap div.required:not(.ldap) input').attr('required', 'required'); + break; + case '6': // OAuth2 + $('.oauth2').show(); + $('.oauth2 div.required:not(.oauth2_use_custom_url,.oauth2_use_custom_url_field,.open_id_connect_auto_discovery_url) input').attr('required', 'required'); + onOAuth2Change(true); + break; + case '7': // SSPI + $('.sspi').show(); + $('.sspi div.required input').attr('required', 'required'); + break; + } + if (authType === '2' || authType === '5') { + onSecurityProtocolChange(); + onVerifyGroupMembershipChange(); + } + if (authType === '2') { + onUsePagedSearchChange(); + } + }); + $('#auth_type').trigger('change'); + $('#security_protocol').on('change', onSecurityProtocolChange); + $('#use_paged_search').on('change', onUsePagedSearchChange); + $('#oauth2_provider').on('change', () => onOAuth2Change(true)); + $('#oauth2_use_custom_url').on('change', () => onOAuth2UseCustomURLChange(true)); + $('#groups_enabled').on('change', onVerifyGroupMembershipChange); + } + // Edit authentication + if ($('.admin.edit.authentication').length > 0) { + const authType = $('#auth_type').val(); + if (authType === '2' || authType === '5') { + $('#security_protocol').on('change', onSecurityProtocolChange); + $('#groups_enabled').on('change', onVerifyGroupMembershipChange); + onVerifyGroupMembershipChange(); + if (authType === '2') { + $('#use_paged_search').on('change', onUsePagedSearchChange); + } + } else if (authType === '6') { + $('#oauth2_provider').on('change', () => onOAuth2Change(true)); + $('#oauth2_use_custom_url').on('change', () => onOAuth2UseCustomURLChange(false)); + onOAuth2Change(false); + } + } + + // Notice + if ($('.admin.notice')) { + const $detailModal = $('#detail-modal'); + + // Attach view detail modals + $('.view-detail').on('click', function () { + $detailModal.find('.content pre').text($(this).parents('tr').find('.notice-description').text()); + $detailModal.find('.sub.header').text($(this).parents('tr').find('.notice-created-time').text()); + $detailModal.modal('show'); + return false; + }); + + // Select actions + const $checkboxes = $('.select.table .ui.checkbox'); + $('.select.action').on('click', function () { + switch ($(this).data('action')) { + case 'select-all': + $checkboxes.checkbox('check'); + break; + case 'deselect-all': + $checkboxes.checkbox('uncheck'); + break; + case 'inverse': + $checkboxes.checkbox('toggle'); + break; + } + }); + $('#delete-selection').on('click', function () { + const $this = $(this); + $this.addClass('loading disabled'); + const ids = []; + $checkboxes.each(function () { + if ($(this).checkbox('is checked')) { + ids.push($(this).data('id')); + } + }); + $.post($this.data('link'), { + _csrf: csrf, + ids + }).done(() => { + window.location.href = $this.data('redirect'); + }); + }); + } +} + +export {initAdminCommon}; diff --git a/web_src/js/features/admin-emails.js b/web_src/js/features/admin-emails.js new file mode 100644 index 0000000000000..630ae5aee9bad --- /dev/null +++ b/web_src/js/features/admin-emails.js @@ -0,0 +1,14 @@ +function initAdminEmails() { + function linkEmailAction(e) { + const $this = $(this); + $('#form-uid').val($this.data('uid')); + $('#form-email').val($this.data('email')); + $('#form-primary').val($this.data('primary')); + $('#form-activate').val($this.data('activate')); + $('#change-email-modal').modal('show'); + e.preventDefault(); + } + $('.link-email-action').on('click', linkEmailAction); +} + +export {initAdminEmails}; diff --git a/web_src/js/features/clipboard.js b/web_src/js/features/clipboard.js index 12486a208d5a3..6d55a3b3e442f 100644 --- a/web_src/js/features/clipboard.js +++ b/web_src/js/features/clipboard.js @@ -1,4 +1,4 @@ -const selector = '[data-clipboard-target], [data-clipboard-text]'; +// For all DOM elements with [data-clipboard-target] or [data-clipboard-text], this copy-to-clipboard will work for them // TODO: replace these with toast-style notifications function onSuccess(btn) { @@ -16,23 +16,22 @@ function onError(btn) { btn.dataset.content = btn.dataset.original; } -export default async function initClipboard() { - for (const btn of document.querySelectorAll(selector) || []) { - btn.addEventListener('click', async () => { - let text; - if (btn.dataset.clipboardText) { - text = btn.dataset.clipboardText; - } else if (btn.dataset.clipboardTarget) { - text = document.querySelector(btn.dataset.clipboardTarget)?.value; - } - if (!text) return; +export default function initGlobalCopyToClipboardListener() { + document.addEventListener('click', async (e) => { + const target = e.target; + let text; + if (target.dataset.clipboardText) { + text = target.dataset.clipboardText; + } else if (target.dataset.clipboardTarget) { + text = document.querySelector(target.dataset.clipboardTarget)?.value; + } + if (!text) return; - try { - await navigator.clipboard.writeText(text); - onSuccess(btn); - } catch { - onError(btn); - } - }); - } + try { + await navigator.clipboard.writeText(text); + onSuccess(target); + } catch { + onError(target); + } + }); } diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js new file mode 100644 index 0000000000000..243fca4b7897c --- /dev/null +++ b/web_src/js/features/common-global.js @@ -0,0 +1,318 @@ +import {mqBinarySearch} from '../utils.js'; +import createDropzone from './dropzone.js'; +import {initCompColorPicker} from './comp/ColorPicker.js'; + +import 'jquery.are-you-sure'; + +const {csrf} = window.config; + +function initGlobalFormDirtyLeaveConfirm() { + // Warn users that try to leave a page after entering data into a form. + // Except on sign-in pages, and for forms marked as 'ignore-dirty'. + if ($('.user.signin').length === 0) { + $('form:not(.ignore-dirty)').areYouSure(); + } +} + +function initHeadNavbarContentToggle() { + const content = $('#navbar'); + const toggle = $('#navbar-expand-toggle'); + let isExpanded = false; + toggle.on('click', () => { + isExpanded = !isExpanded; + if (isExpanded) { + content.addClass('shown'); + toggle.addClass('active'); + } else { + content.removeClass('shown'); + toggle.removeClass('active'); + } + }); +} + +function initFootLanguageMenu() { + function linkLanguageAction() { + const $this = $(this); + $.post($this.data('url')).always(() => { + window.location.reload(); + }); + } + + $('.language-menu a[lang]').on('click', linkLanguageAction); +} + + +function initGlobalEnterQuickSubmit() { + $('.js-quick-submit').on('keydown', function (e) { + if (((e.ctrlKey && !e.altKey) || e.metaKey) && (e.keyCode === 13 || e.keyCode === 10)) { + $(this).closest('form').trigger('submit'); + } + }); +} + +function initGlobalButtonClickOnEnter() { + $(document).on('keypress', '.ui.button', (e) => { + if (e.keyCode === 13 || e.keyCode === 32) { // enter key or space bar + $(e.target).trigger('click'); + } + }); +} + +function initGlobalCommon() { + // Show exact time + $('.time-since').each(function () { + $(this) + .addClass('poping up') + .attr('data-content', $(this).attr('title')) + .attr('data-variation', 'inverted tiny') + .attr('title', ''); + }); + + // Undo Safari emoji glitch fix at high enough zoom levels + if (navigator.userAgent.match('Safari')) { + $(window).resize(() => { + const px = mqBinarySearch('width', 0, 4096, 1, 'px'); + const em = mqBinarySearch('width', 0, 1024, 0.01, 'em'); + if (em * 16 * 1.25 - px <= -1) { + $('body').addClass('safari-above125'); + } else { + $('body').removeClass('safari-above125'); + } + }); + } + + // Semantic UI modules. + $('.dropdown:not(.custom)').dropdown({ + fullTextSearch: 'exact' + }); + $('.jump.dropdown').dropdown({ + action: 'hide', + onShow() { + $('.poping.up').popup('hide'); + }, + fullTextSearch: 'exact' + }); + $('.slide.up.dropdown').dropdown({ + transition: 'slide up', + fullTextSearch: 'exact' + }); + $('.upward.dropdown').dropdown({ + direction: 'upward', + fullTextSearch: 'exact' + }); + $('.ui.checkbox').checkbox(); + $('.ui.progress').progress({ + showActivity: false + }); + $('.poping.up').popup(); + $('.top.menu .poping.up').popup({ + onShow() { + if ($('.top.menu .menu.transition').hasClass('visible')) { + return false; + } + } + }); + $('.tabular.menu .item').tab(); + $('.tabable.menu .item').tab(); + + $('.toggle.button').on('click', function () { + $($(this).data('target')).slideToggle(100); + }); + + // make table element clickable like a link + $('tr[data-href]').on('click', function () { + window.location = $(this).data('href'); + }); + + // make table element clickable like a link + $('td[data-href]').click(function () { + window.location = $(this).data('href'); + }); +} + +async function initGlobalDropzone() { + // Dropzone + for (const el of document.querySelectorAll('.dropzone')) { + const $dropzone = $(el); + await createDropzone(el, { + url: $dropzone.data('upload-url'), + headers: {'X-Csrf-Token': csrf}, + maxFiles: $dropzone.data('max-file'), + maxFilesize: $dropzone.data('max-size'), + acceptedFiles: (['*/*', ''].includes($dropzone.data('accepts'))) ? null : $dropzone.data('accepts'), + addRemoveLinks: true, + dictDefaultMessage: $dropzone.data('default-message'), + dictInvalidFileType: $dropzone.data('invalid-input-type'), + dictFileTooBig: $dropzone.data('file-too-big'), + dictRemoveFile: $dropzone.data('remove-file'), + timeout: 0, + thumbnailMethod: 'contain', + thumbnailWidth: 480, + thumbnailHeight: 480, + init() { + this.on('success', (_file, data) => { + const input = $(``).val(data.uuid); + $dropzone.find('.files').append(input); + }); + this.on('removedfile', (file) => { + $(`#${file.uuid}`).remove(); + if ($dropzone.data('remove-url')) { + $.post($dropzone.data('remove-url'), { + file: file.uuid, + _csrf: csrf, + }); + } + }); + }, + }); + } +} + +function initGlobalLinkActions() { + function showDeletePopup() { + const $this = $(this); + const dataArray = $this.data(); + let filter = ''; + if ($this.data('modal-id')) { + filter += `#${$this.data('modal-id')}`; + } + + const dialog = $(`.delete.modal${filter}`); + dialog.find('.name').text($this.data('name')); + for (const [key, value] of Object.entries(dataArray)) { + if (key && key.startsWith('data')) { + dialog.find(`.${key}`).text(value); + } + } + + dialog.modal({ + closable: false, + onApprove() { + if ($this.data('type') === 'form') { + $($this.data('form')).trigger('submit'); + return; + } + + const postData = { + _csrf: csrf, + }; + for (const [key, value] of Object.entries(dataArray)) { + if (key && key.startsWith('data')) { + postData[key.substr(4)] = value; + } + if (key === 'id') { + postData['id'] = value; + } + } + + $.post($this.data('url'), postData).done((data) => { + window.location.href = data.redirect; + }); + } + }).modal('show'); + return false; + } + + function showAddAllPopup() { + const $this = $(this); + let filter = ''; + if ($this.attr('id')) { + filter += `#${$this.attr('id')}`; + } + + const dialog = $(`.addall.modal${filter}`); + dialog.find('.name').text($this.data('name')); + + dialog.modal({ + closable: false, + onApprove() { + if ($this.data('type') === 'form') { + $($this.data('form')).trigger('submit'); + return; + } + + $.post($this.data('url'), { + _csrf: csrf, + id: $this.data('id') + }).done((data) => { + window.location.href = data.redirect; + }); + } + }).modal('show'); + return false; + } + + function linkAction(e) { + e.preventDefault(); + const $this = $(this); + const redirect = $this.data('redirect'); + $.post($this.data('url'), { + _csrf: csrf + }).done((data) => { + if (data.redirect) { + window.location.href = data.redirect; + } else if (redirect) { + window.location.href = redirect; + } else { + window.location.reload(); + } + }); + } + + // Helpers. + $('.delete-button').on('click', showDeletePopup); + $('.link-action').on('click', linkAction); + + // FIXME: this function is only used once, and not common, not well designed. should be refactored later + $('.add-all-button').on('click', showAddAllPopup); + + // FIXME: this is only used once, and should be replace with `link-action` instead + $('.undo-button').on('click', function () { + const $this = $(this); + $.post($this.data('url'), { + _csrf: csrf, + id: $this.data('id') + }).done((data) => { + window.location.href = data.redirect; + }); + }); +} + +function initGlobalButtons() { + $('.show-panel.button').on('click', function () { + $($(this).data('panel')).show(); + }); + + $('.hide-panel.button').on('click', function () { + $($(this).data('panel')).hide(); + }); + + $('.show-modal.button').on('click', function () { + $($(this).data('modal')).modal('show'); + const colorPickers = $($(this).data('modal')).find('.color-picker'); + if (colorPickers.length > 0) { + initCompColorPicker(); + } + }); + + $('.delete-post.button').on('click', function () { + const $this = $(this); + $.post($this.data('request-url'), { + _csrf: csrf + }).done(() => { + window.location.href = $this.data('done-url'); + }); + }); +} + +export { + initHeadNavbarContentToggle, + initFootLanguageMenu, + initGlobalEnterQuickSubmit, + initGlobalFormDirtyLeaveConfirm, + initGlobalButtonClickOnEnter, + initGlobalCommon, + initGlobalDropzone, + initGlobalLinkActions, + initGlobalButtons, +}; diff --git a/web_src/js/features/common-issue.js b/web_src/js/features/common-issue.js new file mode 100644 index 0000000000000..caa452adeb765 --- /dev/null +++ b/web_src/js/features/common-issue.js @@ -0,0 +1,42 @@ +import {updateIssuesMeta} from './repo-issue.js'; + +function initCommonIssue() { + $('.issue-checkbox').on('click', () => { + const numChecked = $('.issue-checkbox').children('input:checked').length; + if (numChecked > 0) { + $('#issue-filters').addClass('hide'); + $('#issue-actions').removeClass('hide'); + } else { + $('#issue-filters').removeClass('hide'); + $('#issue-actions').addClass('hide'); + } + }); + + $('.issue-action').on('click', function () { + let {action, elementId, url} = this.dataset; + const issueIDs = $('.issue-checkbox').children('input:checked').map((_, el) => { + return el.dataset.issueId; + }).get().join(','); + if (elementId === '0' && url.substr(-9) === '/assignee') { + elementId = ''; + action = 'clear'; + } + updateIssuesMeta(url, action, issueIDs, elementId, '').then(() => { + // NOTICE: This reset of checkbox state targets Firefox caching behaviour, as the checkboxes stay checked after reload + if (action === 'close' || action === 'open') { + // uncheck all checkboxes + $('.issue-checkbox input[type="checkbox"]').each((_, e) => { e.checked = false }); + } + window.location.reload(); + }); + }); + + // NOTICE: This event trigger targets Firefox caching behaviour, as the checkboxes stay checked after reload + // trigger ckecked event, if checkboxes are checked on load + $('.issue-checkbox input[type="checkbox"]:checked').first().each((_, e) => { + e.checked = false; + $(e).trigger('click'); + }); +} + +export {initCommonIssue}; diff --git a/web_src/js/features/common-organization.js b/web_src/js/features/common-organization.js new file mode 100644 index 0000000000000..62074b1205923 --- /dev/null +++ b/web_src/js/features/common-organization.js @@ -0,0 +1,27 @@ +import {initCompLabelEdit} from './comp/LabelEdit.js'; + +function initCommonOrganization() { + if ($('.organization').length === 0) { + return; + } + + // Options + if ($('.organization.settings.options').length > 0) { + $('#org_name').on('keyup', function () { + const $prompt = $('#org-name-change-prompt'); + const $prompt_redirect = $('#org-name-change-redirect-prompt'); + if ($(this).val().toString().toLowerCase() !== $(this).data('org-name').toString().toLowerCase()) { + $prompt.show(); + $prompt_redirect.show(); + } else { + $prompt.hide(); + $prompt_redirect.hide(); + } + }); + } + + // Labels + initCompLabelEdit('.organization.settings.labels'); +} + +export {initCommonOrganization}; diff --git a/web_src/js/features/comp/ColorPicker.js b/web_src/js/features/comp/ColorPicker.js new file mode 100644 index 0000000000000..d16ae6404fba4 --- /dev/null +++ b/web_src/js/features/comp/ColorPicker.js @@ -0,0 +1,13 @@ +import createColorPicker from '../colorpicker.js'; + +function initCompColorPicker() { + createColorPicker($('.color-picker')); + + $('.precolors .color').on('click', function () { + const color_hex = $(this).data('color-hex'); + $('.color-picker').val(color_hex); + $('.minicolors-swatch-color').css('background-color', color_hex); + }); +} + +export {initCompColorPicker}; diff --git a/web_src/js/features/comp/CommentSimpleMDE.js b/web_src/js/features/comp/CommentSimpleMDE.js new file mode 100644 index 0000000000000..806a63b184c67 --- /dev/null +++ b/web_src/js/features/comp/CommentSimpleMDE.js @@ -0,0 +1,74 @@ +import attachTribute from '../tribute.js'; + +function createCommentSimpleMDE($editArea) { + if ($editArea.length === 0) { + return null; + } + + const simplemde = new SimpleMDE({ + autoDownloadFontAwesome: false, + element: $editArea[0], + forceSync: true, + renderingConfig: { + singleLineBreaks: false + }, + indentWithTabs: false, + tabSize: 4, + spellChecker: false, + toolbar: ['bold', 'italic', 'strikethrough', '|', + 'heading-1', 'heading-2', 'heading-3', 'heading-bigger', 'heading-smaller', '|', + 'code', 'quote', '|', { + name: 'checkbox-empty', + action(e) { + const cm = e.codemirror; + cm.replaceSelection(`\n- [ ] ${cm.getSelection()}`); + cm.focus(); + }, + className: 'fa fa-square-o', + title: 'Add Checkbox (empty)', + }, + { + name: 'checkbox-checked', + action(e) { + const cm = e.codemirror; + cm.replaceSelection(`\n- [x] ${cm.getSelection()}`); + cm.focus(); + }, + className: 'fa fa-check-square-o', + title: 'Add Checkbox (checked)', + }, '|', + 'unordered-list', 'ordered-list', '|', + 'link', 'image', 'table', 'horizontal-rule', '|', + 'clean-block', '|', + { + name: 'revert-to-textarea', + action(e) { + e.toTextArea(); + }, + className: 'fa fa-file', + title: 'Revert to simple textarea', + }, + ] + }); + $(simplemde.codemirror.getInputField()).addClass('js-quick-submit'); + simplemde.codemirror.setOption('extraKeys', { + Enter: () => { + const tributeContainer = document.querySelector('.tribute-container'); + if (!tributeContainer || tributeContainer.style.display === 'none') { + return CodeMirror.Pass; + } + }, + Backspace: (cm) => { + if (cm.getInputField().trigger) { + cm.getInputField().trigger('input'); + } + cm.execCommand('delCharBefore'); + } + }); + attachTribute(simplemde.codemirror.getInputField(), {mentions: true, emoji: true}); + $editArea.data('simplemde', simplemde); + $(simplemde.codemirror.getInputField()).data('simplemde', simplemde); + return simplemde; +} + +export {createCommentSimpleMDE}; diff --git a/web_src/js/features/comp/ImagePaste.js b/web_src/js/features/comp/ImagePaste.js new file mode 100644 index 0000000000000..939391362776c --- /dev/null +++ b/web_src/js/features/comp/ImagePaste.js @@ -0,0 +1,93 @@ +const {AppSubUrl, csrf} = window.config; + +async function uploadFile(file, uploadUrl) { + const formData = new FormData(); + formData.append('file', file, file.name); + + const res = await fetch(uploadUrl, { + method: 'POST', + headers: {'X-Csrf-Token': csrf}, + body: formData, + }); + return await res.json(); +} + +function clipboardPastedImages(e) { + if (!e.clipboardData) return []; + + const files = []; + for (const item of e.clipboardData.items || []) { + if (!item.type || !item.type.startsWith('image/')) continue; + files.push(item.getAsFile()); + } + + if (files.length) { + e.preventDefault(); + e.stopPropagation(); + } + return files; +} + + +function insertAtCursor(field, value) { + if (field.selectionStart || field.selectionStart === 0) { + const startPos = field.selectionStart; + const endPos = field.selectionEnd; + field.value = field.value.substring(0, startPos) + value + field.value.substring(endPos, field.value.length); + field.selectionStart = startPos + value.length; + field.selectionEnd = startPos + value.length; + } else { + field.value += value; + } +} + +function replaceAndKeepCursor(field, oldval, newval) { + if (field.selectionStart || field.selectionStart === 0) { + const startPos = field.selectionStart; + const endPos = field.selectionEnd; + field.value = field.value.replace(oldval, newval); + field.selectionStart = startPos + newval.length - oldval.length; + field.selectionEnd = endPos + newval.length - oldval.length; + } else { + field.value = field.value.replace(oldval, newval); + } +} + +function initCompImagePaste($target) { + $target.each(function () { + const dropzone = this.querySelector('.dropzone'); + if (!dropzone) { + return; + } + const uploadUrl = dropzone.dataset.uploadUrl; + const dropzoneFiles = dropzone.querySelector('.files'); + for (const textarea of this.querySelectorAll('textarea')) { + textarea.addEventListener('paste', async (e) => { + for (const img of clipboardPastedImages(e)) { + const name = img.name.substr(0, img.name.lastIndexOf('.')); + insertAtCursor(textarea, `![${name}]()`); + const data = await uploadFile(img, uploadUrl); + replaceAndKeepCursor(textarea, `![${name}]()`, `![${name}](${AppSubUrl}/attachments/${data.uuid})`); + const input = $(``).val(data.uuid); + dropzoneFiles.appendChild(input[0]); + } + }, false); + } + }); +} + +function initSimpleMDEImagePaste(simplemde, dropzone, files) { + const uploadUrl = dropzone.dataset.uploadUrl; + simplemde.codemirror.on('paste', async (_, e) => { + for (const img of clipboardPastedImages(e)) { + const name = img.name.substr(0, img.name.lastIndexOf('.')); + const data = await uploadFile(img, uploadUrl); + const pos = simplemde.codemirror.getCursor(); + simplemde.codemirror.replaceRange(`![${name}](${AppSubUrl}/attachments/${data.uuid})`, pos); + const input = $(``).val(data.uuid); + files.append(input); + } + }); +} + +export {initCompImagePaste, initSimpleMDEImagePaste}; diff --git a/web_src/js/features/comp/IssueCheckboxFix.js b/web_src/js/features/comp/IssueCheckboxFix.js new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/web_src/js/features/comp/LabelEdit.js b/web_src/js/features/comp/LabelEdit.js new file mode 100644 index 0000000000000..80b5be2aaac37 --- /dev/null +++ b/web_src/js/features/comp/LabelEdit.js @@ -0,0 +1,32 @@ +import {initCompColorPicker} from './ColorPicker.js'; + +function initCompLabelEdit(selector) { + if (!$(selector).length) return; + // Create label + const $newLabelPanel = $('.new-label.segment'); + $('.new-label.button').on('click', () => { + $newLabelPanel.show(); + }); + $('.new-label.segment .cancel').on('click', () => { + $newLabelPanel.hide(); + }); + + initCompColorPicker(); + + $('.edit-label-button').on('click', function () { + $('.edit-label .color-picker').minicolors('value', $(this).data('color')); + $('#label-modal-id').val($(this).data('id')); + $('.edit-label .new-label-input').val($(this).data('title')); + $('.edit-label .new-label-desc-input').val($(this).data('description')); + $('.edit-label .color-picker').val($(this).data('color')); + $('.edit-label .minicolors-swatch-color').css('background-color', $(this).data('color')); + $('.edit-label.modal').modal({ + onApprove() { + $('.edit-label.form').trigger('submit'); + } + }).modal('show'); + return false; + }); +} + +export {initCompLabelEdit}; diff --git a/web_src/js/features/comp/MarkupContentPreview.js b/web_src/js/features/comp/MarkupContentPreview.js new file mode 100644 index 0000000000000..4364267d17ebb --- /dev/null +++ b/web_src/js/features/comp/MarkupContentPreview.js @@ -0,0 +1,23 @@ +import {initMarkupContent} from '../../markup/content.js'; + +const {csrf} = window.config; + +function initCompMarkupContentPreviewTab($form) { + const $tabMenu = $form.find('.tabular.menu'); + $tabMenu.find('.item').tab(); + $tabMenu.find(`.item[data-tab="${$tabMenu.data('preview')}"]`).on('click', function () { + const $this = $(this); + $.post($this.data('url'), { + _csrf: csrf, + mode: 'comment', + context: $this.data('context'), + text: $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val() + }, (data) => { + const $previewPanel = $form.find(`.tab[data-tab="${$tabMenu.data('preview')}"]`); + $previewPanel.html(data); + initMarkupContent(); + }); + }); +} + +export {initCompMarkupContentPreviewTab}; diff --git a/web_src/js/features/comp/ReactionSelector.js b/web_src/js/features/comp/ReactionSelector.js new file mode 100644 index 0000000000000..7bae4f99024c7 --- /dev/null +++ b/web_src/js/features/comp/ReactionSelector.js @@ -0,0 +1,50 @@ +const {csrf} = window.config; + +function initCompReactionSelector(parent) { + let reactions = ''; + if (!parent) { + parent = $(document); + reactions = '.reactions > '; + } + + parent.find(`${reactions}a.label`).popup({position: 'bottom left', metadata: {content: 'title', title: 'none'}}); + + parent.find(`.select-reaction > .menu > .item, ${reactions}a.label`).on('click', function (e) { + e.preventDefault(); + + if ($(this).hasClass('disabled')) return; + + const actionURL = $(this).hasClass('item') ? $(this).closest('.select-reaction').data('action-url') : $(this).data('action-url'); + const url = `${actionURL}/${$(this).hasClass('blue') ? 'unreact' : 'react'}`; + $.ajax({ + type: 'POST', + url, + data: { + _csrf: csrf, + content: $(this).data('content') + } + }).done((resp) => { + if (resp && (resp.html || resp.empty)) { + const content = $(this).closest('.content'); + let react = content.find('.segment.reactions'); + if ((!resp.empty || resp.html === '') && react.length > 0) { + react.remove(); + } + if (!resp.empty) { + react = $('
'); + const attachments = content.find('.segment.bottom:first'); + if (attachments.length > 0) { + react.insertBefore(attachments); + } else { + react.appendTo(content); + } + react.html(resp.html); + react.find('.dropdown').dropdown(); + initCompReactionSelector(react); + } + } + }); + }); +} + +export {initCompReactionSelector}; diff --git a/web_src/js/features/comp/SearchUserBox.js b/web_src/js/features/comp/SearchUserBox.js new file mode 100644 index 0000000000000..4a111fbcd1cdc --- /dev/null +++ b/web_src/js/features/comp/SearchUserBox.js @@ -0,0 +1,38 @@ +import {htmlEscape} from 'escape-goat'; + +const {AppSubUrl} = window.config; + +function initSearchUserBox() { + const $searchUserBox = $('#search-user-box'); + $searchUserBox.search({ + minCharacters: 2, + apiSettings: { + url: `${AppSubUrl}/api/v1/users/search?q={query}`, + onResponse(response) { + const items = []; + const searchQueryUppercase = $searchUserBox.find('input').val().toUpperCase(); + $.each(response.data, (_i, item) => { + let title = item.login; + if (item.full_name && item.full_name.length > 0) { + title += ` (${htmlEscape(item.full_name)})`; + } + const resultItem = { + title, + image: item.avatar_url + }; + if (searchQueryUppercase === item.login.toUpperCase()) { + items.unshift(resultItem); + } else { + items.push(resultItem); + } + }); + + return {results: items}; + } + }, + searchFields: ['login', 'full_name'], + showNoResults: false + }); +} + +export {initSearchUserBox}; diff --git a/web_src/js/features/comp/WebHookEditor.js b/web_src/js/features/comp/WebHookEditor.js new file mode 100644 index 0000000000000..3b10b742612a2 --- /dev/null +++ b/web_src/js/features/comp/WebHookEditor.js @@ -0,0 +1,42 @@ +const {csrf} = window.config; + +function initWebHookEditor() { + if ($('.new.webhook').length === 0) { + return; + } + + $('.events.checkbox input').on('change', function () { + if ($(this).is(':checked')) { + $('.events.fields').show(); + } + }); + $('.non-events.checkbox input').on('change', function () { + if ($(this).is(':checked')) { + $('.events.fields').hide(); + } + }); + + const updateContentType = function () { + const visible = $('#http_method').val() === 'POST'; + $('#content_type').parent().parent()[visible ? 'show' : 'hide'](); + }; + updateContentType(); + $('#http_method').on('change', () => { + updateContentType(); + }); + + // Test delivery + $('#test-delivery').on('click', function () { + const $this = $(this); + $this.addClass('loading disabled'); + $.post($this.data('link'), { + _csrf: csrf + }).done( + setTimeout(() => { + window.location.href = $this.data('redirect'); + }, 5000) + ); + }); +} + +export {initWebHookEditor}; diff --git a/web_src/js/features/install.js b/web_src/js/features/install.js new file mode 100644 index 0000000000000..876a610377faf --- /dev/null +++ b/web_src/js/features/install.js @@ -0,0 +1,94 @@ + +function initInstall() { + if ($('.install').length === 0) { + return; + } + + if ($('#db_host').val() === '') { + $('#db_host').val('127.0.0.1:3306'); + $('#db_user').val('gitea'); + $('#db_name').val('gitea'); + } + + // Database type change detection. + $('#db_type').on('change', function () { + const sqliteDefault = 'data/gitea.db'; + const tidbDefault = 'data/gitea_tidb'; + + const dbType = $(this).val(); + if (dbType === 'SQLite3') { + $('#sql_settings').hide(); + $('#pgsql_settings').hide(); + $('#mysql_settings').hide(); + $('#sqlite_settings').show(); + + if (dbType === 'SQLite3' && $('#db_path').val() === tidbDefault) { + $('#db_path').val(sqliteDefault); + } + return; + } + + const dbDefaults = { + MySQL: '127.0.0.1:3306', + PostgreSQL: '127.0.0.1:5432', + MSSQL: '127.0.0.1:1433' + }; + + $('#sqlite_settings').hide(); + $('#sql_settings').show(); + + $('#pgsql_settings').toggle(dbType === 'PostgreSQL'); + $('#mysql_settings').toggle(dbType === 'MySQL'); + $.each(dbDefaults, (_type, defaultHost) => { + if ($('#db_host').val() === defaultHost) { + $('#db_host').val(dbDefaults[dbType]); + return false; + } + }); + }); + + // TODO: better handling of exclusive relations. + $('#offline-mode input').on('change', function () { + if ($(this).is(':checked')) { + $('#disable-gravatar').checkbox('check'); + $('#federated-avatar-lookup').checkbox('uncheck'); + } + }); + $('#disable-gravatar input').on('change', function () { + if ($(this).is(':checked')) { + $('#federated-avatar-lookup').checkbox('uncheck'); + } else { + $('#offline-mode').checkbox('uncheck'); + } + }); + $('#federated-avatar-lookup input').on('change', function () { + if ($(this).is(':checked')) { + $('#disable-gravatar').checkbox('uncheck'); + $('#offline-mode').checkbox('uncheck'); + } + }); + $('#enable-openid-signin input').on('change', function () { + if ($(this).is(':checked')) { + if (!$('#disable-registration input').is(':checked')) { + $('#enable-openid-signup').checkbox('check'); + } + } else { + $('#enable-openid-signup').checkbox('uncheck'); + } + }); + $('#disable-registration input').on('change', function () { + if ($(this).is(':checked')) { + $('#enable-captcha').checkbox('uncheck'); + $('#enable-openid-signup').checkbox('uncheck'); + } else { + $('#enable-openid-signup').checkbox('check'); + } + }); + $('#enable-captcha input').on('change', function () { + if ($(this).is(':checked')) { + $('#disable-registration').checkbox('uncheck'); + } + }); +} + +export {initInstall}; diff --git a/web_src/js/features/org-team.js b/web_src/js/features/org-team.js new file mode 100644 index 0000000000000..9f99721b9a6b3 --- /dev/null +++ b/web_src/js/features/org-team.js @@ -0,0 +1,39 @@ +const {AppSubUrl} = window.config; + +function initOrgTeamSettings() { + // Change team access mode + $('.organization.new.team input[name=permission]').on('change', () => { + const val = $('input[name=permission]:checked', '.organization.new.team').val(); + if (val === 'admin') { + $('.organization.new.team .team-units').hide(); + } else { + $('.organization.new.team .team-units').show(); + } + }); +} + + +function initOrgTeamSearchRepoBox() { + const $searchRepoBox = $('#search-repo-box'); + $searchRepoBox.search({ + minCharacters: 2, + apiSettings: { + url: `${AppSubUrl}/api/v1/repos/search?q={query}&uid=${$searchRepoBox.data('uid')}`, + onResponse(response) { + const items = []; + $.each(response.data, (_i, item) => { + items.push({ + title: item.full_name.split('/')[1], + description: item.full_name + }); + }); + + return {results: items}; + } + }, + searchFields: ['full_name'], + showNoResults: false + }); +} + +export {initOrgTeamSettings, initOrgTeamSearchRepoBox}; diff --git a/web_src/js/features/repo-branch.js b/web_src/js/features/repo-branch.js new file mode 100644 index 0000000000000..210ee125a73bc --- /dev/null +++ b/web_src/js/features/repo-branch.js @@ -0,0 +1,9 @@ +function initRepoBranchButton() { + $('.show-create-branch-modal.button').on('click', function () { + $('#create-branch-form')[0].action = $('#create-branch-form').data('base-action') + $(this).data('branch-from'); + $('#modal-create-branch-from-span').text($(this).data('branch-from')); + $($(this).data('modal')).modal('show'); + }); +} + +export {initRepoBranchButton}; diff --git a/web_src/js/features/repo-code.js b/web_src/js/features/repo-code.js new file mode 100644 index 0000000000000..5026f3043a3e8 --- /dev/null +++ b/web_src/js/features/repo-code.js @@ -0,0 +1,147 @@ +import {svg} from '../svg.js'; + +function changeHash(hash) { + if (window.history.pushState) { + window.history.pushState(null, null, hash); + } else { + window.location.hash = hash; + } +} + +function selectRange($list, $select, $from) { + $list.removeClass('active'); + + // add hashchange to permalink + const $issue = $('a.ref-in-new-issue'); + const $copyPermalink = $('a.copy-line-permalink'); + + if ($issue.length === 0 || $copyPermalink.length === 0) { + return; + } + + const updateIssueHref = function(anchor) { + let href = $issue.attr('href'); + href = `${href.replace(/%23L\d+$|%23L\d+-L\d+$/, '')}%23${anchor}`; + $issue.attr('href', href); + }; + + const updateCopyPermalinkHref = function(anchor) { + let link = $copyPermalink.attr('data-clipboard-text'); + link = `${link.replace(/#L\d+$|#L\d+-L\d+$/, '')}#${anchor}`; + $copyPermalink.attr('data-clipboard-text', link); + }; + + if ($from) { + let a = parseInt($select.attr('rel').substr(1)); + let b = parseInt($from.attr('rel').substr(1)); + let c; + if (a !== b) { + if (a > b) { + c = a; + a = b; + b = c; + } + const classes = []; + for (let i = a; i <= b; i++) { + classes.push(`[rel=L${i}]`); + } + $list.filter(classes.join(',')).addClass('active'); + changeHash(`#L${a}-L${b}`); + + updateIssueHref(`L${a}-L${b}`); + updateCopyPermalinkHref(`L${a}-L${b}`); + return; + } + } + $select.addClass('active'); + changeHash(`#${$select.attr('rel')}`); + + updateIssueHref($select.attr('rel')); + updateCopyPermalinkHref($select.attr('rel')); +} + +function showLineButton() { + if ($('.code-line-menu').length === 0) return; + $('.code-line-button').remove(); + $('.code-view td.lines-code.active').closest('tr').find('td:eq(0)').first().prepend( + $(``) + ); + $('.code-line-menu').appendTo($('.code-view')); + $('.code-line-button').popup({popup: $('.code-line-menu'), on: 'click'}); +} + +function initRepoCodeView() { + if ($('.code-view .lines-num').length > 0) { + $(document).on('click', '.lines-num span', function (e) { + const $select = $(this); + let $list; + if ($('div.blame').length) { + $list = $('.code-view td.lines-code.blame-code'); + } else { + $list = $('.code-view td.lines-code'); + } + selectRange($list, $list.filter(`[rel=${$select.attr('id')}]`), (e.shiftKey ? $list.filter('.active').eq(0) : null)); + + if (window.getSelection) { + window.getSelection().removeAllRanges(); + } else { + document.selection.empty(); + } + + // show code view menu marker (don't show in blame page) + if ($('div.blame').length === 0) { + showLineButton(); + } + }); + + $(window).on('hashchange', () => { + let m = window.location.hash.match(/^#(L\d+)-(L\d+)$/); + let $list; + if ($('div.blame').length) { + $list = $('.code-view td.lines-code.blame-code'); + } else { + $list = $('.code-view td.lines-code'); + } + let $first; + if (m) { + $first = $list.filter(`[rel=${m[1]}]`); + selectRange($list, $first, $list.filter(`[rel=${m[2]}]`)); + + // show code view menu marker (don't show in blame page) + if ($('div.blame').length === 0) { + showLineButton(); + } + + $('html, body').scrollTop($first.offset().top - 200); + return; + } + m = window.location.hash.match(/^#(L|n)(\d+)$/); + if (m) { + $first = $list.filter(`[rel=L${m[2]}]`); + selectRange($list, $first); + + // show code view menu marker (don't show in blame page) + if ($('div.blame').length === 0) { + showLineButton(); + } + + $('html, body').scrollTop($first.offset().top - 200); + } + }).trigger('hashchange'); + } + $(document).on('click', '.fold-file', ({currentTarget}) => { + const box = currentTarget.closest('.file-content'); + const chevron = currentTarget.querySelector('a.chevron'); + const folded = box.dataset.folded !== 'true'; + chevron.innerHTML = svg(`octicon-chevron-${folded ? 'right' : 'down'}`, 18); + box.dataset.folded = String(folded); + }); + $(document).on('click', '.blob-excerpt', async ({currentTarget}) => { + const {url, query, anchor} = currentTarget.dataset; + if (!url) return; + const blob = await $.get(`${url}?${query}&anchor=${anchor}`); + currentTarget.closest('tr').outerHTML = blob; + }); +} + +export {initRepoCodeView}; diff --git a/web_src/js/features/repo-commit.js b/web_src/js/features/repo-commit.js new file mode 100644 index 0000000000000..147c9849e34aa --- /dev/null +++ b/web_src/js/features/repo-commit.js @@ -0,0 +1,9 @@ +function initRepoCommitButton() { + $('.commit-button').on('click', function (e) { + e.preventDefault(); + $(this).parent().find('.commit-body').toggle(); + }); +} + +export {initRepoCommitButton}; + diff --git a/web_src/js/features/repo-common.js b/web_src/js/features/repo-common.js new file mode 100644 index 0000000000000..1941b8243ab69 --- /dev/null +++ b/web_src/js/features/repo-common.js @@ -0,0 +1,108 @@ +const {csrf} = window.config; + +function getArchive($target, url, first) { + $.ajax({ + url, + type: 'POST', + data: { + _csrf: csrf, + }, + complete(xhr) { + if (xhr.status === 200) { + if (!xhr.responseJSON) { + // XXX Shouldn't happen? + $target.closest('.dropdown').children('i').removeClass('loading'); + return; + } + + if (!xhr.responseJSON.complete) { + $target.closest('.dropdown').children('i').addClass('loading'); + // Wait for only three quarters of a second initially, in case it's + // quickly archived. + setTimeout(() => { + getArchive($target, url, false); + }, first ? 750 : 2000); + } else { + // We don't need to continue checking. + $target.closest('.dropdown').children('i').removeClass('loading'); + window.location.href = url; + } + } + }, + }); +} + +function initRepoArchiveLinks() { + $('.archive-link').on('click', function (event) { + event.preventDefault(); + const url = $(this).data('url'); + if (!url) return; + getArchive($(event.target), url, true); + }); +} + +function initRepoClone() { + // Quick start and repository home + $('#repo-clone-ssh').on('click', function () { + $('.clone-url').text($(this).data('link')); + $('#repo-clone-url').val($(this).data('link')); + $(this).addClass('primary'); + $('#repo-clone-https').removeClass('primary'); + localStorage.setItem('repo-clone-protocol', 'ssh'); + }); + $('#repo-clone-https').on('click', function () { + $('.clone-url').text($(this).data('link')); + $('#repo-clone-url').val($(this).data('link')); + $(this).addClass('primary'); + if ($('#repo-clone-ssh').length > 0) { + $('#repo-clone-ssh').removeClass('primary'); + localStorage.setItem('repo-clone-protocol', 'https'); + } + }); + $('#repo-clone-url').on('click', function () { + $(this).select(); + }); +} + +function initRepoCommonBranchOrTagDropdown(selector) { + $(selector).each(function () { + const $dropdown = $(this); + $dropdown.find('.reference.column').on('click', function () { + $dropdown.find('.scrolling.reference-list-menu').hide(); + $($(this).data('target')).show(); + return false; + }); + }); +} + +function initRepoCommonFilterSearchDropdown(selector) { + const $dropdown = $(selector); + $dropdown.dropdown({ + fullTextSearch: true, + selectOnKeydown: false, + onChange(_text, _value, $choice) { + if ($choice.data('url')) { + window.location.href = $choice.data('url'); + } + }, + message: {noResults: $dropdown.data('no-results')}, + }); +} + +function initRepoCommonLanguageStats() { + // Language stats + if ($('.language-stats').length > 0) { + $('.language-stats').on('click', (e) => { + e.preventDefault(); + $('.language-stats-details, .repository-menu').slideToggle(); + }); + } +} + +export { + initRepoArchiveLinks, + initRepoClone, + initRepoCommonFilterSearchDropdown, + initRepoCommonLanguageStats, + initRepoCommonBranchOrTagDropdown, +}; diff --git a/web_src/js/features/repo-diff.js b/web_src/js/features/repo-diff.js new file mode 100644 index 0000000000000..484bc4de91525 --- /dev/null +++ b/web_src/js/features/repo-diff.js @@ -0,0 +1,88 @@ +import {initCompReactionSelector} from './comp/ReactionSelector.js'; + +const {csrf} = window.config; + +function initRepoDiffReviewButton() { + $(document).on('click', 'button[name="is_review"]', (e) => { + $(e.target).closest('form').append(''); + }); +} + +function initRepoDiffFileViewToggle() { + $('.file-view-toggle').on('click', function () { + const $this = $(this); + $this.parent().children().removeClass('active'); + $this.addClass('active'); + + const $target = $($this.data('toggle-selector')); + $target.parent().children().addClass('hide'); + $target.removeClass('hide'); + }); +} + +function initRepoDiffConversationForm() { + $('.conversation-holder form').on('submit', async (e) => { + e.preventDefault(); + const form = $(e.target); + const newConversationHolder = $(await $.post(form.attr('action'), form.serialize())); + const {path, side, idx} = newConversationHolder.data(); + + form.closest('.conversation-holder').replaceWith(newConversationHolder); + if (form.closest('tr').data('line-type') === 'same') { + $(`a.add-code-comment[data-path="${path}"][data-idx="${idx}"]`).addClass('invisible'); + } else { + $(`a.add-code-comment[data-path="${path}"][data-side="${side}"][data-idx="${idx}"]`).addClass('invisible'); + } + newConversationHolder.find('.dropdown').dropdown(); + initCompReactionSelector(newConversationHolder); + }); + + + $('.resolve-conversation').on('click', async function (e) { + e.preventDefault(); + const comment_id = $(this).data('comment-id'); + const origin = $(this).data('origin'); + const action = $(this).data('action'); + const url = $(this).data('update-url'); + + const data = await $.post(url, {_csrf: csrf, origin, action, comment_id}); + + if ($(this).closest('.conversation-holder').length) { + const conversation = $(data); + $(this).closest('.conversation-holder').replaceWith(conversation); + conversation.find('.dropdown').dropdown(); + initCompReactionSelector(conversation); + } else { + window.location.reload(); + } + }); +} + +function initRepoDiffConversationNav() { + // Previous/Next code review conversation + $(document).on('click', '.previous-conversation', (e) => { + const $conversation = $(e.currentTarget).closest('.comment-code-cloud'); + const $conversations = $('.comment-code-cloud:not(.hide)'); + const index = $conversations.index($conversation); + const previousIndex = index > 0 ? index - 1 : $conversations.length - 1; + const $previousConversation = $conversations.eq(previousIndex); + const anchor = $previousConversation.find('.comment').first().attr('id'); + window.location.href = `#${anchor}`; + }); + $(document).on('click', '.next-conversation', (e) => { + const $conversation = $(e.currentTarget).closest('.comment-code-cloud'); + const $conversations = $('.comment-code-cloud:not(.hide)'); + const index = $conversations.index($conversation); + const nextIndex = index < $conversations.length - 1 ? index + 1 : 0; + const $nextConversation = $conversations.eq(nextIndex); + const anchor = $nextConversation.find('.comment').first().attr('id'); + window.location.href = `#${anchor}`; + }); +} + +export { + initRepoDiffReviewButton, + initRepoDiffFileViewToggle, + initRepoDiffConversationForm, + initRepoDiffConversationNav, +}; diff --git a/web_src/js/features/repo-editor.js b/web_src/js/features/repo-editor.js new file mode 100644 index 0000000000000..0a227f7c9b29d --- /dev/null +++ b/web_src/js/features/repo-editor.js @@ -0,0 +1,182 @@ +import {initMarkupContent} from '../markup/content.js'; +import {createCodeEditor} from './codeeditor.js'; + +const {csrf} = window.config; + +let previewFileModes; + +function initEditPreviewTab($form) { + const $tabMenu = $form.find('.tabular.menu'); + $tabMenu.find('.item').tab(); + const $previewTab = $tabMenu.find(`.item[data-tab="${$tabMenu.data('preview')}"]`); + if ($previewTab.length) { + previewFileModes = $previewTab.data('preview-file-modes').split(','); + $previewTab.on('click', function () { + const $this = $(this); + let context = `${$this.data('context')}/`; + const mode = $this.data('markdown-mode') || 'comment'; + const treePathEl = $form.find('input#tree_path'); + if (treePathEl.length > 0) { + context += treePathEl.val(); + } + context = context.substring(0, context.lastIndexOf('/')); + $.post($this.data('url'), { + _csrf: csrf, + mode, + context, + text: $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val() + }, (data) => { + const $previewPanel = $form.find(`.tab[data-tab="${$tabMenu.data('preview')}"]`); + $previewPanel.html(data); + initMarkupContent(); + }); + }); + } +} + +function initEditDiffTab($form) { + const $tabMenu = $form.find('.tabular.menu'); + $tabMenu.find('.item').tab(); + $tabMenu.find(`.item[data-tab="${$tabMenu.data('diff')}"]`).on('click', function () { + const $this = $(this); + $.post($this.data('url'), { + _csrf: csrf, + context: $this.data('context'), + content: $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val() + }, (data) => { + const $diffPreviewPanel = $form.find(`.tab[data-tab="${$tabMenu.data('diff')}"]`); + $diffPreviewPanel.html(data); + }); + }); +} + +function initEditorForm() { + if ($('.repository .edit.form').length === 0) { + return; + } + + initEditPreviewTab($('.repository .edit.form')); + initEditDiffTab($('.repository .edit.form')); +} + + +function getCursorPosition($e) { + const el = $e.get(0); + let pos = 0; + if ('selectionStart' in el) { + pos = el.selectionStart; + } else if ('selection' in document) { + el.focus(); + const Sel = document.selection.createRange(); + const SelLength = document.selection.createRange().text.length; + Sel.moveStart('character', -el.value.length); + pos = Sel.text.length - SelLength; + } + return pos; +} + +async function initRepoEditor() { + initEditorForm(); + + $('.js-quick-pull-choice-option').on('change', function () { + if ($(this).val() === 'commit-to-new-branch') { + $('.quick-pull-branch-name').show(); + $('.quick-pull-branch-name input').prop('required', true); + } else { + $('.quick-pull-branch-name').hide(); + $('.quick-pull-branch-name input').prop('required', false); + } + $('#commit-button').text($(this).attr('button_text')); + }); + + const $editFilename = $('#file-name'); + $editFilename.on('keyup', function (e) { + const $section = $('.breadcrumb span.section'); + const $divider = $('.breadcrumb div.divider'); + let value; + let parts; + + if (e.keyCode === 8 && getCursorPosition($(this)) === 0 && $section.length > 0) { + value = $section.last().find('a').text(); + $(this).val(value + $(this).val()); + $(this)[0].setSelectionRange(value.length, value.length); + $section.last().remove(); + $divider.last().remove(); + } + if (e.keyCode === 191) { + parts = $(this).val().split('/'); + for (let i = 0; i < parts.length; ++i) { + value = parts[i]; + if (i < parts.length - 1) { + if (value.length) { + $(`${value}`).insertBefore($(this)); + $('
/
').insertBefore($(this)); + } + } else { + $(this).val(value); + } + $(this)[0].setSelectionRange(0, 0); + } + } + parts = []; + $('.breadcrumb span.section').each(function () { + const element = $(this); + if (element.find('a').length) { + parts.push(element.find('a').text()); + } else { + parts.push(element.text()); + } + }); + if ($(this).val()) parts.push($(this).val()); + $('#tree_path').val(parts.join('/')); + }).trigger('keyup'); + + const $editArea = $('.repository.editor textarea#edit_area'); + if (!$editArea.length) return; + + const editor = await createCodeEditor($editArea[0], $editFilename[0], previewFileModes); + + // Using events from https://github.com/codedance/jquery.AreYouSure#advanced-usage + // to enable or disable the commit button + const $commitButton = $('#commit-button'); + const $editForm = $('.ui.edit.form'); + const dirtyFileClass = 'dirty-file'; + + // Disabling the button at the start + if ($('input[name="page_has_posted"]').val() !== 'true') { + $commitButton.prop('disabled', true); + } + + // Registering a custom listener for the file path and the file content + $editForm.areYouSure({ + silent: true, + dirtyClass: dirtyFileClass, + fieldSelector: ':input:not(.commit-form-wrapper :input)', + change() { + const dirty = $(this).hasClass(dirtyFileClass); + $commitButton.prop('disabled', !dirty); + } + }); + + // Update the editor from query params, if available, + // only after the dirtyFileClass initialization + const params = new URLSearchParams(window.location.search); + const value = params.get('value'); + if (value) { + editor.setValue(value); + } + + $commitButton.on('click', (event) => { + // A modal which asks if an empty file should be committed + if ($editArea.val().length === 0) { + $('#edit-empty-content-modal').modal({ + onApprove() { + $('.edit.form').trigger('submit'); + } + }).modal('show'); + event.preventDefault(); + } + }); +} + +export {initRepoEditor}; diff --git a/web_src/js/features/repo-home.js b/web_src/js/features/repo-home.js new file mode 100644 index 0000000000000..0774a398bdca9 --- /dev/null +++ b/web_src/js/features/repo-home.js @@ -0,0 +1,182 @@ +import {stripTags} from '../utils.js'; + +const {AppSubUrl, csrf} = window.config; + +function initRepoTopicBar() { + const mgrBtn = $('#manage_topic'); + const editDiv = $('#topic_edit'); + const viewDiv = $('#repo-topics'); + const saveBtn = $('#save_topic'); + const topicDropdown = $('#topic_edit .dropdown'); + const topicForm = $('#topic_edit.ui.form'); + const topicPrompts = getPrompts(); + + mgrBtn.on('click', () => { + viewDiv.hide(); + editDiv.css('display', ''); // show Semantic UI Grid + }); + + function getPrompts() { + const hidePrompt = $('div.hide#validate_prompt'); + const prompts = { + countPrompt: hidePrompt.children('#count_prompt').text(), + formatPrompt: hidePrompt.children('#format_prompt').text() + }; + hidePrompt.remove(); + return prompts; + } + + saveBtn.on('click', () => { + const topics = $('input[name=topics]').val(); + + $.post(saveBtn.data('link'), { + _csrf: csrf, + topics + }, (_data, _textStatus, xhr) => { + if (xhr.responseJSON.status === 'ok') { + viewDiv.children('.topic').remove(); + if (topics.length) { + const topicArray = topics.split(','); + + const last = viewDiv.children('a').last(); + for (let i = 0; i < topicArray.length; i++) { + const link = $(''); + link.attr('href', `${AppSubUrl}/explore/repos?q=${encodeURIComponent(topicArray[i])}&topic=1`); + link.text(topicArray[i]); + link.insertBefore(last); + } + } + editDiv.css('display', 'none'); + viewDiv.show(); + } + }).fail((xhr) => { + if (xhr.status === 422) { + if (xhr.responseJSON.invalidTopics.length > 0) { + topicPrompts.formatPrompt = xhr.responseJSON.message; + + const {invalidTopics} = xhr.responseJSON; + const topicLables = topicDropdown.children('a.ui.label'); + + topics.split(',').forEach((value, index) => { + for (let i = 0; i < invalidTopics.length; i++) { + if (invalidTopics[i] === value) { + topicLables.eq(index).removeClass('green').addClass('red'); + } + } + }); + } else { + topicPrompts.countPrompt = xhr.responseJSON.message; + } + } + }).always(() => { + topicForm.form('validate form'); + }); + }); + + topicDropdown.dropdown({ + allowAdditions: true, + forceSelection: false, + fullTextSearch: 'exact', + fields: {name: 'description', value: 'data-value'}, + saveRemoteData: false, + label: { + transition: 'horizontal flip', + duration: 200, + variation: false, + blue: true, + basic: true, + }, + className: { + label: 'ui small label' + }, + apiSettings: { + url: `${AppSubUrl}/api/v1/topics/search?q={query}`, + throttle: 500, + cache: false, + onResponse(res) { + const formattedResponse = { + success: false, + results: [], + }; + const query = stripTags(this.urlData.query.trim()); + let found_query = false; + const current_topics = []; + topicDropdown.find('div.label.visible.topic,a.label.visible').each((_, e) => { current_topics.push(e.dataset.value) }); + + if (res.topics) { + let found = false; + for (let i = 0; i < res.topics.length; i++) { + // skip currently added tags + if (current_topics.includes(res.topics[i].topic_name)) { + continue; + } + + if (res.topics[i].topic_name.toLowerCase() === query.toLowerCase()) { + found_query = true; + } + formattedResponse.results.push({description: res.topics[i].topic_name, 'data-value': res.topics[i].topic_name}); + found = true; + } + formattedResponse.success = found; + } + + if (query.length > 0 && !found_query) { + formattedResponse.success = true; + formattedResponse.results.unshift({description: query, 'data-value': query}); + } else if (query.length > 0 && found_query) { + formattedResponse.results.sort((a, b) => { + if (a.description.toLowerCase() === query.toLowerCase()) return -1; + if (b.description.toLowerCase() === query.toLowerCase()) return 1; + if (a.description > b.description) return -1; + if (a.description < b.description) return 1; + return 0; + }); + } + + return formattedResponse; + }, + }, + onLabelCreate(value) { + value = value.toLowerCase().trim(); + this.attr('data-value', value).contents().first().replaceWith(value); + return $(this); + }, + onAdd(addedValue, _addedText, $addedChoice) { + addedValue = addedValue.toLowerCase().trim(); + $($addedChoice).attr('data-value', addedValue); + $($addedChoice).attr('data-text', addedValue); + } + }); + + $.fn.form.settings.rules.validateTopic = function (_values, regExp) { + const topics = topicDropdown.children('a.ui.label'); + const status = topics.length === 0 || topics.last().attr('data-value').match(regExp); + if (!status) { + topics.last().removeClass('green').addClass('red'); + } + return status && topicDropdown.children('a.ui.label.red').length === 0; + }; + + topicForm.form({ + on: 'change', + inline: true, + fields: { + topics: { + identifier: 'topics', + rules: [ + { + type: 'validateTopic', + value: /^[a-z0-9][a-z0-9-]{0,35}$/, + prompt: topicPrompts.formatPrompt + }, + { + type: 'maxCount[25]', + prompt: topicPrompts.countPrompt + } + ] + }, + } + }); +} + +export {initRepoTopicBar}; diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js new file mode 100644 index 0000000000000..5351b2ad920df --- /dev/null +++ b/web_src/js/features/repo-issue.js @@ -0,0 +1,659 @@ +import {htmlEscape} from 'escape-goat'; +import attachTribute from './tribute.js'; +import {createCommentSimpleMDE} from './comp/CommentSimpleMDE.js'; +import {initCompImagePaste} from './comp/ImagePaste.js'; +import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js'; + +const {AppSubUrl, csrf} = window.config; + +function initRepoIssueTimeTracking() { + $(document).on('click', '.issue-add-time', () => { + $('.issue-start-time-modal').modal({ + duration: 200, + onApprove() { + $('#add_time_manual_form').trigger('submit'); + }, + }).modal('show'); + $('.issue-start-time-modal input').on('keydown', (e) => { + if ((e.keyCode || e.key) === 13) { + $('#add_time_manual_form').trigger('submit'); + } + }); + }); + $(document).on('click', '.issue-start-time, .issue-stop-time', () => { + $('#toggle_stopwatch_form').trigger('submit'); + }); + $(document).on('click', '.issue-cancel-time', () => { + $('#cancel_stopwatch_form').trigger('submit'); + }); + $(document).on('click', 'button.issue-delete-time', function () { + const sel = `.issue-delete-time-modal[data-id="${$(this).data('id')}"]`; + $(sel).modal({ + duration: 200, + onApprove() { + $(`${sel} form`).trigger('submit'); + }, + }).modal('show'); + }); +} + +function updateDeadline(deadlineString) { + $('#deadline-err-invalid-date').hide(); + $('#deadline-loader').addClass('loading'); + + let realDeadline = null; + if (deadlineString !== '') { + const newDate = Date.parse(deadlineString); + + if (Number.isNaN(newDate)) { + $('#deadline-loader').removeClass('loading'); + $('#deadline-err-invalid-date').show(); + return false; + } + realDeadline = new Date(newDate); + } + + $.ajax(`${$('#update-issue-deadline-form').attr('action')}/deadline`, { + data: JSON.stringify({ + due_date: realDeadline, + }), + headers: { + 'X-Csrf-Token': csrf, + 'X-Remote': true, + }, + contentType: 'application/json', + type: 'POST', + success() { + window.location.reload(); + }, + error() { + $('#deadline-loader').removeClass('loading'); + $('#deadline-err-invalid-date').show(); + }, + }); +} + +function initRepoIssueDue() { + $(document).on('click', '.issue-due-edit', () => { + $('#deadlineForm').fadeToggle(150); + }); + $(document).on('click', '.issue-due-remove', () => { + updateDeadline(''); + }); + $(document).on('submit', '.issue-due-form', () => { + updateDeadline($('#deadlineDate').val()); + return false; + }); +} + +function initRepoIssueList() { + const repolink = $('#repolink').val(); + const repoId = $('#repoId').val(); + const crossRepoSearch = $('#crossRepoSearch').val(); + const tp = $('#type').val(); + let issueSearchUrl = `${AppSubUrl}/api/v1/repos/${repolink}/issues?q={query}&type=${tp}`; + if (crossRepoSearch === 'true') { + issueSearchUrl = `${AppSubUrl}/api/v1/repos/issues/search?q={query}&priority_repo_id=${repoId}&type=${tp}`; + } + $('#new-dependency-drop-list') + .dropdown({ + apiSettings: { + url: issueSearchUrl, + onResponse(response) { + const filteredResponse = {success: true, results: []}; + const currIssueId = $('#new-dependency-drop-list').data('issue-id'); + // Parse the response from the api to work with our dropdown + $.each(response, (_i, issue) => { + // Don't list current issue in the dependency list. + if (issue.id === currIssueId) { + return; + } + filteredResponse.results.push({ + name: `#${issue.number} ${htmlEscape(issue.title) + }
${htmlEscape(issue.repository.full_name)}
`, + value: issue.id, + }); + }); + return filteredResponse; + }, + cache: false, + }, + + fullTextSearch: true, + }); + + function excludeLabel(item) { + const href = $(item).attr('href'); + const id = $(item).data('label-id'); + + const regStr = `labels=((?:-?[0-9]+%2c)*)(${id})((?:%2c-?[0-9]+)*)&`; + const newStr = 'labels=$1-$2$3&'; + + window.location = href.replace(new RegExp(regStr), newStr); + } + + $('.menu a.label-filter-item').each(function () { + $(this).on('click', function (e) { + if (e.altKey) { + e.preventDefault(); + excludeLabel(this); + } + }); + }); + + $('.menu .ui.dropdown.label-filter').on('keydown', (e) => { + if (e.altKey && e.keyCode === 13) { + const selectedItems = $('.menu .ui.dropdown.label-filter .menu .item.selected'); + if (selectedItems.length > 0) { + excludeLabel($(selectedItems[0])); + } + } + }); +} + +function initRepoIssueCommentDelete() { + // Delete comment + $(document).on('click', '.delete-comment', function () { + const $this = $(this); + if (window.confirm($this.data('locale'))) { + $.post($this.data('url'), { + _csrf: csrf, + }).done(() => { + const $conversationHolder = $this.closest('.conversation-holder'); + $(`#${$this.data('comment-id')}`).remove(); + if ($conversationHolder.length && !$conversationHolder.find('.comment').length) { + const path = $conversationHolder.data('path'); + const side = $conversationHolder.data('side'); + const idx = $conversationHolder.data('idx'); + const lineType = $conversationHolder.closest('tr').data('line-type'); + if (lineType === 'same') { + $(`a.add-code-comment[data-path="${path}"][data-idx="${idx}"]`).removeClass('invisible'); + } else { + $(`a.add-code-comment[data-path="${path}"][data-side="${side}"][data-idx="${idx}"]`).removeClass('invisible'); + } + $conversationHolder.remove(); + } + }); + } + return false; + }); +} + +function initRepoIssueDependencyDelete() { + // Delete Issue dependency + $(document).on('click', '.delete-dependency-button', (e) => { + const {id, type} = e.currentTarget.dataset; + + $('.remove-dependency').modal({ + closable: false, + duration: 200, + onApprove: () => { + $('#removeDependencyID').val(id); + $('#dependencyType').val(type); + $('#removeDependencyForm').trigger('submit'); + }, + }).modal('show'); + }); +} + +function initRepoIssueCodeCommentCancel() { + // Cancel inline code comment + $(document).on('click', '.cancel-code-comment', (e) => { + const form = $(e.currentTarget).closest('form'); + if (form.length > 0 && form.hasClass('comment-form')) { + form.addClass('hide'); + form.closest('.comment-code-cloud').find('button.comment-form-reply').show(); + } else { + form.closest('.comment-code-cloud').remove(); + } + }); +} + +function initRepoIssueStatusButton() { + // Change status + const $statusButton = $('#status-button'); + $('#comment-form textarea').on('keyup', function () { + const $simplemde = $(this).data('simplemde'); + const value = ($simplemde && $simplemde.value()) ? $simplemde.value() : $(this).val(); + $statusButton.text($statusButton.data(value.length === 0 ? 'status' : 'status-and-comment')); + }); + $statusButton.on('click', () => { + $('#status').val($statusButton.data('status-val')); + $('#comment-form').trigger('submit'); + }); +} + +function initRepoPullRequestMerge() { + // Pull Request merge button + const $mergeButton = $('.merge-button > button'); + $mergeButton.on('click', function (e) { + e.preventDefault(); + $(`.${$(this).data('do')}-fields`).show(); + $(this).parent().hide(); + $('.instruct-toggle').hide(); + $('.instruct-content').hide(); + }); + $('.merge-button > .dropdown').dropdown({ + onChange(_text, _value, $choice) { + if ($choice.data('do')) { + $mergeButton.find('.button-text').text($choice.text()); + $mergeButton.data('do', $choice.data('do')); + } + } + }); + $('.merge-cancel').on('click', function (e) { + e.preventDefault(); + $(this).closest('.form').hide(); + $mergeButton.parent().show(); + $('.instruct-toggle').show(); + }); +} + +function initRepoPullRequestUpdate() { + // Pull Request update button + const $pullUpdateButton = $('.update-button > button'); + $pullUpdateButton.on('click', function (e) { + e.preventDefault(); + const $this = $(this); + const redirect = $this.data('redirect'); + $this.addClass('loading'); + $.post($this.data('do'), { + _csrf: csrf + }).done((data) => { + if (data.redirect) { + window.location.href = data.redirect; + } else if (redirect) { + window.location.href = redirect; + } else { + window.location.reload(); + } + }); + }); + + $('.update-button > .dropdown').dropdown({ + onChange(_text, _value, $choice) { + const $url = $choice.data('do'); + if ($url) { + $pullUpdateButton.find('.button-text').text($choice.text()); + $pullUpdateButton.data('do', $url); + } + } + }); +} + +function initRepoPullRequestMergeInstruction() { + $('.show-instruction').on('click', () => { + $('.instruct-content').toggle(); + }); +} + +function initRepoIssueReferenceRepositorySearch() { + $('.issue_reference_repository_search') + .dropdown({ + apiSettings: { + url: `${AppSubUrl}/api/v1/repos/search?q={query}&limit=20`, + onResponse(response) { + const filteredResponse = {success: true, results: []}; + $.each(response.data, (_r, repo) => { + filteredResponse.results.push({ + name: htmlEscape(repo.full_name), + value: repo.full_name + }); + }); + return filteredResponse; + }, + cache: false, + }, + onChange(_value, _text, $choice) { + const $form = $choice.closest('form'); + $form.attr('action', `${AppSubUrl}/${_text}/issues/new`); + }, + fullTextSearch: true + }); +} + + +function initRepoIssueWipTitle() { + $('.title_wip_desc > a').on('click', (e) => { + e.preventDefault(); + + const $issueTitle = $('#issue_title'); + $issueTitle.focus(); + const value = $issueTitle.val().trim().toUpperCase(); + + const wipPrefixes = $('.title_wip_desc').data('wip-prefixes'); + for (const prefix of wipPrefixes) { + if (value.startsWith(prefix.toUpperCase())) { + return; + } + } + + $issueTitle.val(`${wipPrefixes[0]} ${$issueTitle.val()}`); + }); +} + +function updateIssuesMeta(url, action, issueIds, elementId) { + return new Promise((resolve) => { + $.ajax({ + type: 'POST', + url, + data: { + _csrf: csrf, + action, + issue_ids: issueIds, + id: elementId, + }, + success: resolve + }); + }); +} + +function initRepoIssueComments() { + if ($('.repository.view.issue .timeline').length === 0) return; + + $('.re-request-review').on('click', function (event) { + const url = $(this).data('update-url'); + const issueId = $(this).data('issue-id'); + const id = $(this).data('id'); + const isChecked = $(this).hasClass('checked'); + + event.preventDefault(); + updateIssuesMeta( + url, + isChecked ? 'detach' : 'attach', + issueId, + id, + ).then(() => { + window.location.reload(); + }); + return false; + }); + + $('.dismiss-review-btn').on('click', function (e) { + e.preventDefault(); + const $this = $(this); + const $dismissReviewModal = $this.next(); + $dismissReviewModal.modal('show'); + }); + + $(document).on('click', (event) => { + const urlTarget = $(':target'); + if (urlTarget.length === 0) return; + + const urlTargetId = urlTarget.attr('id'); + if (!urlTargetId) return; + if (!/^(issue|pull)(comment)?-\d+$/.test(urlTargetId)) return; + + const $target = $(event.target); + + if ($target.closest(`#${urlTargetId}`).length === 0) { + const scrollPosition = $(window).scrollTop(); + window.location.hash = ''; + $(window).scrollTop(scrollPosition); + window.history.pushState(null, null, ' '); + } + }); +} + + +function assignMenuAttributes(menu) { + const id = Math.floor(Math.random() * Math.floor(1000000)); + menu.attr('data-write', menu.attr('data-write') + id); + menu.attr('data-preview', menu.attr('data-preview') + id); + menu.find('.item').each(function () { + const tab = $(this).attr('data-tab') + id; + $(this).attr('data-tab', tab); + }); + menu.parent().find("*[data-tab='write']").attr('data-tab', `write${id}`); + menu.parent().find("*[data-tab='preview']").attr('data-tab', `preview${id}`); + initCompMarkupContentPreviewTab(menu.parent('.form')); + return id; +} + +function initRepoPullRequestReview() { + if (window.location.hash && window.location.hash.startsWith('#issuecomment-')) { + const commentDiv = $(window.location.hash); + if (commentDiv) { + // get the name of the parent id + const groupID = commentDiv.closest('div[id^="code-comments-"]').attr('id'); + if (groupID && groupID.startsWith('code-comments-')) { + const id = groupID.substr(14); + $(`#show-outdated-${id}`).addClass('hide'); + $(`#code-comments-${id}`).removeClass('hide'); + $(`#code-preview-${id}`).removeClass('hide'); + $(`#hide-outdated-${id}`).removeClass('hide'); + commentDiv[0].scrollIntoView(); + } + } + } + + $(document).on('click', '.show-outdated', function (e) { + e.preventDefault(); + const id = $(this).data('comment'); + $(this).addClass('hide'); + $(`#code-comments-${id}`).removeClass('hide'); + $(`#code-preview-${id}`).removeClass('hide'); + $(`#hide-outdated-${id}`).removeClass('hide'); + }); + + $(document).on('click', '.hide-outdated', function (e) { + e.preventDefault(); + const id = $(this).data('comment'); + $(this).addClass('hide'); + $(`#code-comments-${id}`).addClass('hide'); + $(`#code-preview-${id}`).addClass('hide'); + $(`#show-outdated-${id}`).removeClass('hide'); + }); + + $(document).on('click', 'button.comment-form-reply', function (e) { + e.preventDefault(); + $(this).hide(); + const form = $(this).closest('.comment-code-cloud').find('.comment-form'); + form.removeClass('hide'); + const $textarea = form.find('textarea'); + let $simplemde; + if ($textarea.data('simplemde')) { + $simplemde = $textarea.data('simplemde'); + } else { + attachTribute($textarea.get(), {mentions: true, emoji: true}); + $simplemde = createCommentSimpleMDE($textarea); + $textarea.data('simplemde', $simplemde); + } + $textarea.focus(); + $simplemde.codemirror.focus(); + assignMenuAttributes(form.find('.menu')); + }); + + const $reviewBox = $('.review-box'); + if ($reviewBox.length === 1) { + createCommentSimpleMDE($reviewBox.find('textarea')); + initCompImagePaste($reviewBox); + } + + // The following part is only for diff views + if ($('.repository.pull.diff').length === 0) { + return; + } + + $('.btn-review').on('click', function (e) { + e.preventDefault(); + $(this).closest('.dropdown').find('.menu').toggle('visible'); + }).closest('.dropdown').find('.close').on('click', function (e) { + e.preventDefault(); + $(this).closest('.menu').toggle('visible'); + }); + + $('a.add-code-comment').on('click', async function (e) { + if ($(e.target).hasClass('btn-add-single')) return; // https://github.com/go-gitea/gitea/issues/4745 + e.preventDefault(); + + const isSplit = $(this).closest('.code-diff').hasClass('code-diff-split'); + const side = $(this).data('side'); + const idx = $(this).data('idx'); + const path = $(this).data('path'); + const tr = $(this).closest('tr'); + const lineType = tr.data('line-type'); + + let ntr = tr.next(); + if (!ntr.hasClass('add-comment')) { + ntr = $(` + + ${isSplit ? ` + + + + + + + ` : ` + + + `} + `); + tr.after(ntr); + } + + const td = ntr.find(`.add-comment-${side}`); + let commentCloud = td.find('.comment-code-cloud'); + if (commentCloud.length === 0 && !ntr.find('button[name="is_review"]').length) { + const data = await $.get($(this).data('new-comment-url')); + td.html(data); + commentCloud = td.find('.comment-code-cloud'); + assignMenuAttributes(commentCloud.find('.menu')); + td.find("input[name='line']").val(idx); + td.find("input[name='side']").val(side === 'left' ? 'previous' : 'proposed'); + td.find("input[name='path']").val(path); + const $textarea = commentCloud.find('textarea'); + attachTribute($textarea.get(), {mentions: true, emoji: true}); + const $simplemde = createCommentSimpleMDE($textarea); + $textarea.focus(); + $simplemde.codemirror.focus(); + } + }); +} + +function initRepoIssueReferenceIssue() { + // Reference issue + $(document).on('click', '.reference-issue', function (event) { + const $this = $(this); + $this.closest('.dropdown').find('.menu').toggle('visible'); + + const content = $(`#comment-${$this.data('target')}`).text(); + const poster = $this.data('poster-username'); + const reference = $this.data('reference'); + const $modal = $($this.data('modal')); + $modal.find('textarea[name="content"]').val(`${content}\n\n_Originally posted by @${poster} in ${reference}_`); + $modal.modal('show'); + + event.preventDefault(); + }); +} + +function initRepoIssueWipToggle() { + // Toggle WIP + $('.toggle-wip a, .toggle-wip button').on('click', async (e) => { + e.preventDefault(); + const {title, wipPrefix, updateUrl} = e.currentTarget.closest('.toggle-wip').dataset; + await $.post(updateUrl, { + _csrf: csrf, + title: title?.startsWith(wipPrefix) ? title.substr(wipPrefix.length).trim() : `${wipPrefix.trim()} ${title}`, + }); + window.location.reload(); + }); +} + + +function initRepoIssueTitleEdit() { + // Edit issue title + const $issueTitle = $('#issue-title'); + const $editInput = $('#edit-title-input input'); + + const editTitleToggle = function () { + $issueTitle.toggle(); + $('.not-in-edit').toggle(); + $('#edit-title-input').toggle(); + $('#pull-desc').toggle(); + $('#pull-desc-edit').toggle(); + $('.in-edit').toggle(); + $('#issue-title-wrapper').toggleClass('edit-active'); + $editInput.focus(); + return false; + }; + + $('#edit-title').on('click', editTitleToggle); + $('#cancel-edit-title').on('click', editTitleToggle); + $('#save-edit-title').on('click', editTitleToggle).on('click', function () { + const pullrequest_targetbranch_change = function (update_url) { + const targetBranch = $('#pull-target-branch').data('branch'); + const $branchTarget = $('#branch_target'); + if (targetBranch === $branchTarget.text()) { + return false; + } + $.post(update_url, { + _csrf: csrf, + target_branch: targetBranch + }).done((data) => { + $branchTarget.text(data.base_branch); + }).always(() => { + window.location.reload(); + }); + }; + + const pullrequest_target_update_url = $(this).data('target-update-url'); + if ($editInput.val().length === 0 || $editInput.val() === $issueTitle.text()) { + $editInput.val($issueTitle.text()); + pullrequest_targetbranch_change(pullrequest_target_update_url); + } else { + $.post($(this).data('update-url'), { + _csrf: csrf, + title: $editInput.val() + }, (data) => { + $editInput.val(data.title); + $issueTitle.text(data.title); + pullrequest_targetbranch_change(pullrequest_target_update_url); + window.location.reload(); + }); + } + return false; + }); +} + +function initRepoIssueBranchSelect() { + const changeBranchSelect = function () { + const selectionTextField = $('#pull-target-branch'); + + const baseName = selectionTextField.data('basename'); + const branchNameNew = $(this).data('branch'); + const branchNameOld = selectionTextField.data('branch'); + + // Replace branch name to keep translation from HTML template + selectionTextField.html(selectionTextField.html().replace( + `${baseName}:${branchNameOld}`, + `${baseName}:${branchNameNew}` + )); + selectionTextField.data('branch', branchNameNew); // update branch name in setting + }; + $('#branch-select > .item').on('click', changeBranchSelect); +} + +export { + updateIssuesMeta, + initRepoIssueTimeTracking, + initRepoIssueDue, + initRepoIssueList, + initRepoIssueCommentDelete, + initRepoIssueDependencyDelete, + initRepoIssueCodeCommentCancel, + initRepoIssueStatusButton, + initRepoPullRequestMerge, + initRepoPullRequestUpdate, + initRepoPullRequestMergeInstruction, + initRepoIssueReferenceRepositorySearch, + initRepoIssueWipTitle, + initRepoIssueComments, + initRepoPullRequestReview, + initRepoIssueReferenceIssue, + initRepoIssueWipToggle, + initRepoIssueTitleEdit, + initRepoIssueBranchSelect, +}; diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js new file mode 100644 index 0000000000000..ac150965ab43c --- /dev/null +++ b/web_src/js/features/repo-legacy.js @@ -0,0 +1,575 @@ +import {createCommentSimpleMDE} from './comp/CommentSimpleMDE.js'; +import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js'; +import {initCompImagePaste, initSimpleMDEImagePaste} from './comp/ImagePaste.js'; +import { + initRepoIssueBranchSelect, initRepoIssueCodeCommentCancel, + initRepoIssueCommentDelete, + initRepoIssueComments, initRepoIssueDependencyDelete, + initRepoIssueReferenceIssue, initRepoIssueStatusButton, + initRepoIssueTitleEdit, + initRepoIssueWipToggle, initRepoPullRequestMerge, initRepoPullRequestUpdate, + updateIssuesMeta, +} from './repo-issue.js'; +import {svg} from '../svg.js'; +import {htmlEscape} from 'escape-goat'; +import {initRepoBranchTagDropdown} from '../components/RepoBranchTagDropdown.js'; +import { + initRepoClone, + initRepoCommonBranchOrTagDropdown, + initRepoCommonFilterSearchDropdown, + initRepoCommonLanguageStats, +} from './repo-common.js'; +import {initCompLabelEdit} from './comp/LabelEdit.js'; +import {initRepoDiffConversationNav} from './repo-diff.js'; +import attachTribute from './tribute.js'; +import createDropzone from './dropzone.js'; +import {initCommentContent, initMarkupContent} from '../markup/content.js'; +import {initCompReactionSelector} from './comp/ReactionSelector.js'; +import {initRepoSettingBranches} from './repo-settings.js'; + +const {csrf} = window.config; + +const commentMDEditors = {}; + +let autoSimpleMDE; + +function initRepoCommentForm() { + if ($('.comment.form').length === 0) { + return; + } + + function initBranchSelector() { + const $selectBranch = $('.ui.select-branch'); + const $branchMenu = $selectBranch.find('.reference-list-menu'); + const $isNewIssue = $branchMenu.hasClass('new-issue'); + $branchMenu.find('.item:not(.no-select)').click(function () { + const selectedValue = $(this).data('id'); + const editMode = $('#editing_mode').val(); + $($(this).data('id-selector')).val(selectedValue); + if ($isNewIssue) { + $selectBranch.find('.ui .branch-name').text($(this).data('name')); + return; + } + + if (editMode === 'true') { + const form = $('#update_issueref_form'); + $.post(form.attr('action'), {_csrf: csrf, ref: selectedValue}, window.location.reload); + } else if (editMode === '') { + $selectBranch.find('.ui .branch-name').text(selectedValue); + } + }); + $selectBranch.find('.reference.column').on('click', function () { + $selectBranch.find('.scrolling.reference-list-menu').css('display', 'none'); + $selectBranch.find('.reference .text').removeClass('black'); + $($(this).data('target')).css('display', 'block'); + $(this).find('.text').addClass('black'); + return false; + }); + } + + autoSimpleMDE = createCommentSimpleMDE($('.comment.form textarea:not(.review-textarea)')); + initBranchSelector(); + initCompMarkupContentPreviewTab($('.comment.form')); + initCompImagePaste($('.comment.form')); + + // Listsubmit + function initListSubmits(selector, outerSelector) { + const $list = $(`.ui.${outerSelector}.list`); + const $noSelect = $list.find('.no-select'); + const $listMenu = $(`.${selector} .menu`); + let hasUpdateAction = $listMenu.data('action') === 'update'; + const items = {}; + + $(`.${selector}`).dropdown('setting', 'onHide', () => { + hasUpdateAction = $listMenu.data('action') === 'update'; // Update the var + if (hasUpdateAction) { + const promises = []; + Object.keys(items).forEach((elementId) => { + const item = items[elementId]; + const promise = updateIssuesMeta( + item['update-url'], + item.action, + item['issue-id'], + elementId, + ); + promises.push(promise); + }); + Promise.all(promises).then(window.location.reload); + } + }); + + $listMenu.find('.item:not(.no-select)').on('click', function (e) { + e.preventDefault(); + if ($(this).hasClass('ban-change')) { + return false; + } + + hasUpdateAction = $listMenu.data('action') === 'update'; // Update the var + if ($(this).hasClass('checked')) { + $(this).removeClass('checked'); + $(this).find('.octicon-check').addClass('invisible'); + if (hasUpdateAction) { + if (!($(this).data('id') in items)) { + items[$(this).data('id')] = { + 'update-url': $listMenu.data('update-url'), + action: 'detach', + 'issue-id': $listMenu.data('issue-id'), + }; + } else { + delete items[$(this).data('id')]; + } + } + } else { + $(this).addClass('checked'); + $(this).find('.octicon-check').removeClass('invisible'); + if (hasUpdateAction) { + if (!($(this).data('id') in items)) { + items[$(this).data('id')] = { + 'update-url': $listMenu.data('update-url'), + action: 'attach', + 'issue-id': $listMenu.data('issue-id'), + }; + } else { + delete items[$(this).data('id')]; + } + } + } + + // TODO: Which thing should be done for choosing review requests + // to make chosen items be shown on time here? + if (selector === 'select-reviewers-modify' || selector === 'select-assignees-modify') { + return false; + } + + const listIds = []; + $(this).parent().find('.item').each(function () { + if ($(this).hasClass('checked')) { + listIds.push($(this).data('id')); + $($(this).data('id-selector')).removeClass('hide'); + } else { + $($(this).data('id-selector')).addClass('hide'); + } + }); + if (listIds.length === 0) { + $noSelect.removeClass('hide'); + } else { + $noSelect.addClass('hide'); + } + $($(this).parent().data('id')).val(listIds.join(',')); + return false; + }); + $listMenu.find('.no-select.item').on('click', function (e) { + e.preventDefault(); + if (hasUpdateAction) { + updateIssuesMeta( + $listMenu.data('update-url'), + 'clear', + $listMenu.data('issue-id'), + '', + ).then(window.location.reload); + } + + $(this).parent().find('.item').each(function () { + $(this).removeClass('checked'); + $(this).find('.octicon').addClass('invisible'); + }); + + if (selector === 'select-reviewers-modify' || selector === 'select-assignees-modify') { + return false; + } + + $list.find('.item').each(function () { + $(this).addClass('hide'); + }); + $noSelect.removeClass('hide'); + $($(this).parent().data('id')).val(''); + }); + } + + // Init labels and assignees + initListSubmits('select-label', 'labels'); + initListSubmits('select-assignees', 'assignees'); + initListSubmits('select-assignees-modify', 'assignees'); + initListSubmits('select-reviewers-modify', 'assignees'); + + function selectItem(select_id, input_id) { + const $menu = $(`${select_id} .menu`); + const $list = $(`.ui${select_id}.list`); + const hasUpdateAction = $menu.data('action') === 'update'; + + $menu.find('.item:not(.no-select)').on('click', function () { + $(this).parent().find('.item').each(function () { + $(this).removeClass('selected active'); + }); + + $(this).addClass('selected active'); + if (hasUpdateAction) { + updateIssuesMeta( + $menu.data('update-url'), + '', + $menu.data('issue-id'), + $(this).data('id'), + ).then(window.location.reload); + } + + let icon = ''; + if (input_id === '#milestone_id') { + icon = svg('octicon-milestone', 18, 'mr-3'); + } else if (input_id === '#project_id') { + icon = svg('octicon-project', 18, 'mr-3'); + } else if (input_id === '#assignee_id') { + icon = ``; + } + + $list.find('.selected').html(` + + ${icon} + ${htmlEscape($(this).text())} + + `); + + $(`.ui${select_id}.list .no-select`).addClass('hide'); + $(input_id).val($(this).data('id')); + }); + $menu.find('.no-select.item').on('click', function () { + $(this).parent().find('.item:not(.no-select)').each(function () { + $(this).removeClass('selected active'); + }); + + if (hasUpdateAction) { + updateIssuesMeta( + $menu.data('update-url'), + '', + $menu.data('issue-id'), + $(this).data('id'), + ).then(window.location.reload); + } + + $list.find('.selected').html(''); + $list.find('.no-select').removeClass('hide'); + $(input_id).val(''); + }); + } + + // Milestone, Assignee, Project + selectItem('.select-project', '#project_id'); + selectItem('.select-milestone', '#milestone_id'); + selectItem('.select-assignee', '#assignee_id'); +} + + +async function initRepository() { + if ($('.repository').length === 0) { + return; + } + + + // Commit statuses + $('.commit-statuses-trigger').each(function () { + $(this) + .popup({ + on: 'click', + position: ($('.repository.file.list').length > 0 ? 'right center' : 'left center'), + }); + }); + + // File list and commits + if ($('.repository.file.list').length > 0 || + $('.repository.commits').length > 0 || $('.repository.release').length > 0) { + initRepoBranchTagDropdown('.choose.reference .dropdown'); + } + + // Wiki + if ($('.repository.wiki.view').length > 0) { + initRepoCommonFilterSearchDropdown('.choose.page .dropdown'); + } + + // Options + if ($('.repository.settings.options').length > 0) { + // Enable or select internal/external wiki system and issue tracker. + $('.enable-system').on('change', function () { + if (this.checked) { + $($(this).data('target')).removeClass('disabled'); + if (!$(this).data('context')) $($(this).data('context')).addClass('disabled'); + } else { + $($(this).data('target')).addClass('disabled'); + if (!$(this).data('context')) $($(this).data('context')).removeClass('disabled'); + } + }); + $('.enable-system-radio').on('change', function () { + if (this.value === 'false') { + $($(this).data('target')).addClass('disabled'); + if (typeof $(this).data('context') !== 'undefined') $($(this).data('context')).removeClass('disabled'); + } else if (this.value === 'true') { + $($(this).data('target')).removeClass('disabled'); + if (typeof $(this).data('context') !== 'undefined') $($(this).data('context')).addClass('disabled'); + } + }); + } + + // Labels + initCompLabelEdit('.repository.labels'); + + // Milestones + if ($('.repository.new.milestone').length > 0) { + $('#clear-date').on('click', () => { + $('#deadline').val(''); + return false; + }); + } + + // Repo Creation + if ($('.repository.new.repo').length > 0) { + $('input[name="gitignores"], input[name="license"]').on('change', () => { + const gitignores = $('input[name="gitignores"]').val(); + const license = $('input[name="license"]').val(); + if (gitignores || license) { + $('input[name="auto_init"]').prop('checked', true); + } + }); + } + + // Issues + if ($('.repository.view.issue').length > 0) { + initRepoIssueBranchSelect(); + initRepoIssueTitleEdit(); + initRepoIssueWipToggle(); + initRepoIssueComments(); + + // Issue/PR Context Menus + $('.context-dropdown').dropdown({ + action: 'hide', + }); + + initRepoDiffConversationNav(); + initRepoIssueQuoteReply(); + initRepoIssueReferenceIssue(); + + // Edit issue or comment content + $(document).on('click', '.edit-content', async function (event) { + $(this).closest('.dropdown').find('.menu').toggle('visible'); + const $segment = $(this).closest('.header').next(); + const $editContentZone = $segment.find('.edit-content-zone'); + const $renderContent = $segment.find('.render-content'); + const $rawContent = $segment.find('.raw-content'); + let $textarea; + let $simplemde; + + // Setup new form + if ($editContentZone.html().length === 0) { + $editContentZone.html($('#edit-content-form').html()); + $textarea = $editContentZone.find('textarea'); + attachTribute($textarea.get(), {mentions: true, emoji: true}); + + let dz; + const $dropzone = $editContentZone.find('.dropzone'); + if ($dropzone.length === 1) { + $dropzone.data('saved', false); + + const fileUuidDict = {}; + dz = await createDropzone($dropzone[0], { + url: $dropzone.data('upload-url'), + headers: {'X-Csrf-Token': csrf}, + maxFiles: $dropzone.data('max-file'), + maxFilesize: $dropzone.data('max-size'), + acceptedFiles: (['*/*', ''].includes($dropzone.data('accepts'))) ? null : $dropzone.data('accepts'), + addRemoveLinks: true, + dictDefaultMessage: $dropzone.data('default-message'), + dictInvalidFileType: $dropzone.data('invalid-input-type'), + dictFileTooBig: $dropzone.data('file-too-big'), + dictRemoveFile: $dropzone.data('remove-file'), + timeout: 0, + thumbnailMethod: 'contain', + thumbnailWidth: 480, + thumbnailHeight: 480, + init() { + this.on('success', (file, data) => { + fileUuidDict[file.uuid] = { + submitted: false, + }; + const input = $(``).val(data.uuid); + $dropzone.find('.files').append(input); + }); + this.on('removedfile', (file) => { + $(`#${file.uuid}`).remove(); + if ($dropzone.data('remove-url') && !fileUuidDict[file.uuid].submitted) { + $.post($dropzone.data('remove-url'), { + file: file.uuid, + _csrf: csrf, + }); + } + }); + this.on('submit', () => { + $.each(fileUuidDict, (fileUuid) => { + fileUuidDict[fileUuid].submitted = true; + }); + }); + this.on('reload', () => { + $.getJSON($editContentZone.data('attachment-url'), (data) => { + dz.removeAllFiles(true); + $dropzone.find('.files').empty(); + $.each(data, function () { + const imgSrc = `${$dropzone.data('link-url')}/${this.uuid}`; + dz.emit('addedfile', this); + dz.emit('thumbnail', this, imgSrc); + dz.emit('complete', this); + dz.files.push(this); + fileUuidDict[this.uuid] = { + submitted: true, + }; + $dropzone.find(`img[src='${imgSrc}']`).css('max-width', '100%'); + const input = $(``).val(this.uuid); + $dropzone.find('.files').append(input); + }); + }); + }); + }, + }); + dz.emit('reload'); + } + // Give new write/preview data-tab name to distinguish from others + const $editContentForm = $editContentZone.find('.ui.comment.form'); + const $tabMenu = $editContentForm.find('.tabular.menu'); + $tabMenu.attr('data-write', $editContentZone.data('write')); + $tabMenu.attr('data-preview', $editContentZone.data('preview')); + $tabMenu.find('.write.item').attr('data-tab', $editContentZone.data('write')); + $tabMenu.find('.preview.item').attr('data-tab', $editContentZone.data('preview')); + $editContentForm.find('.write').attr('data-tab', $editContentZone.data('write')); + $editContentForm.find('.preview').attr('data-tab', $editContentZone.data('preview')); + $simplemde = createCommentSimpleMDE($textarea); + commentMDEditors[$editContentZone.data('write')] = $simplemde; + initCompMarkupContentPreviewTab($editContentForm); + if ($dropzone.length === 1) { + initSimpleMDEImagePaste($simplemde, $dropzone[0], $dropzone.find('.files')); + } + + $editContentZone.find('.cancel.button').on('click', () => { + $renderContent.show(); + $editContentZone.hide(); + if (dz) { + dz.emit('reload'); + } + }); + $editContentZone.find('.save.button').on('click', () => { + $renderContent.show(); + $editContentZone.hide(); + const $attachments = $dropzone.find('.files').find('[name=files]').map(function () { + return $(this).val(); + }).get(); + $.post($editContentZone.data('update-url'), { + _csrf: csrf, + content: $textarea.val(), + context: $editContentZone.data('context'), + files: $attachments, + }, (data) => { + if (data.length === 0 || data.content.length === 0) { + $renderContent.html($('#no-content').html()); + $rawContent.text(''); + } else { + $renderContent.html(data.content); + $rawContent.text($textarea.val()); + } + const $content = $segment; + if (!$content.find('.dropzone-attachments').length) { + if (data.attachments !== '') { + $content.append(` +
+
+ `); + $content.find('.dropzone-attachments').replaceWith(data.attachments); + } + } else if (data.attachments === '') { + $content.find('.dropzone-attachments').remove(); + } else { + $content.find('.dropzone-attachments').replaceWith(data.attachments); + } + if (dz) { + dz.emit('submit'); + dz.emit('reload'); + } + initMarkupContent(); + initCommentContent(); + }); + }); + } else { + $textarea = $segment.find('textarea'); + $simplemde = commentMDEditors[$editContentZone.data('write')]; + } + + // Show write/preview tab and copy raw content as needed + $editContentZone.show(); + $renderContent.hide(); + if ($textarea.val().length === 0) { + $textarea.val($rawContent.text()); + $simplemde.value($rawContent.text()); + } + requestAnimationFrame(() => { + $textarea.focus(); + $simplemde.codemirror.focus(); + }); + event.preventDefault(); + }); + + initRepoIssueCommentDelete(); + initRepoIssueDependencyDelete(); + initRepoIssueCodeCommentCancel(); + initRepoIssueStatusButton(); + initRepoPullRequestMerge(); + initRepoPullRequestUpdate(); + initCompReactionSelector(); + } + + initRepoClone(); + + // Compare or pull request + const $repoDiff = $('.repository.diff'); + if ($repoDiff.length) { + initRepoCommonBranchOrTagDropdown('.choose.branch .dropdown'); + initRepoCommonFilterSearchDropdown('.choose.branch .dropdown'); + } + + // Pull request + const $repoComparePull = $('.repository.compare.pull'); + if ($repoComparePull.length > 0) { + // show pull request form + $repoComparePull.find('button.show-form').on('click', function (e) { + e.preventDefault(); + $repoComparePull.find('.pullrequest-form').show(); + autoSimpleMDE.codemirror.refresh(); + $(this).parent().hide(); + }); + } + + initRepoSettingBranches(); + initRepoCommonLanguageStats(); +} + +function initRepoIssueQuoteReply() { + // Quote reply + $(document).on('click', '.quote-reply', function (event) { + $(this).closest('.dropdown').find('.menu').toggle('visible'); + const target = $(this).data('target'); + const quote = $(`#comment-${target}`).text().replace(/\n/g, '\n> '); + const content = `> ${quote}\n\n`; + let $simplemde = autoSimpleMDE; + if ($(this).hasClass('quote-reply-diff')) { + const $parent = $(this).closest('.comment-code-cloud'); + $parent.find('button.comment-form-reply').trigger('click'); + $simplemde = $parent.find('[name="content"]').data('simplemde'); + } + if ($simplemde !== null) { + if ($simplemde.value() !== '') { + $simplemde.value(`${$simplemde.value()}\n\n${content}`); + } else { + $simplemde.value(`${content}`); + } + } + requestAnimationFrame(() => { + $simplemde.codemirror.focus(); + $simplemde.codemirror.setCursor($simplemde.codemirror.lineCount(), 0); + }); + event.preventDefault(); + }); +} + +export {initRepoCommentForm, initRepository}; diff --git a/web_src/js/features/repo-migrate.js b/web_src/js/features/repo-migrate.js new file mode 100644 index 0000000000000..a8a0e6cca22bd --- /dev/null +++ b/web_src/js/features/repo-migrate.js @@ -0,0 +1,50 @@ +const {AppSubUrl, csrf} = window.config; + +function initRepoMigrationStatusChecker() { + const migrating = $('#repo_migrating'); + $('#repo_migrating_failed').hide(); + $('#repo_migrating_failed_image').hide(); + $('#repo_migrating_progress_message').hide(); + if (migrating) { + const task = migrating.attr('task'); + if (typeof task === 'undefined') { + return; + } + $.ajax({ + type: 'GET', + url: `${AppSubUrl}/user/task/${task}`, + data: { + _csrf: csrf, + }, + complete(xhr) { + if (xhr.status === 200 && xhr.responseJSON) { + if (xhr.responseJSON.status === 4) { + window.location.reload(); + return; + } else if (xhr.responseJSON.status === 3) { + $('#repo_migrating_progress').hide(); + $('#repo_migrating').hide(); + $('#repo_migrating_failed').show(); + $('#repo_migrating_failed_image').show(); + $('#repo_migrating_failed_error').text(xhr.responseJSON.message); + return; + } + if (xhr.responseJSON.message) { + $('#repo_migrating_progress_message').show(); + $('#repo_migrating_progress_message').text(xhr.responseJSON.message); + } + setTimeout(() => { + initRepoMigrationStatusChecker(); + }, 2000); + return; + } + $('#repo_migrating_progress').hide(); + $('#repo_migrating').hide(); + $('#repo_migrating_failed').show(); + $('#repo_migrating_failed_image').show(); + } + }); + } +} + +export {initRepoMigrationStatusChecker}; diff --git a/web_src/js/features/repo-release.js b/web_src/js/features/repo-release.js new file mode 100644 index 0000000000000..16a1601b85bab --- /dev/null +++ b/web_src/js/features/repo-release.js @@ -0,0 +1,31 @@ +import attachTribute from './tribute.js'; +import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js'; +import {initSimpleMDEImagePaste} from './comp/ImagePaste.js'; +import {createCommentSimpleMDE} from './comp/CommentSimpleMDE.js'; + +function initRepoRelease() { + $(document).on('click', '.remove-rel-attach', function() { + const uuid = $(this).data('uuid'); + const id = $(this).data('id'); + $(`input[name='attachment-del-${uuid}']`).attr('value', true); + $(`#attachment-${id}`).hide(); + }); +} + + +function initRepoReleaseEditor() { + const $editor = $('.repository.new.release .content-editor'); + if ($editor.length === 0) { + return false; + } + + const $textarea = $editor.find('textarea'); + attachTribute($textarea.get(), {mentions: false, emoji: true}); + const $files = $editor.parent().find('.files'); + const $simplemde = createCommentSimpleMDE($textarea); + initCompMarkupContentPreviewTab($editor); + const dropzone = $editor.parent().find('.dropzone')[0]; + initSimpleMDEImagePaste($simplemde, dropzone, $files); +} + +export {initRepoRelease, initRepoReleaseEditor}; diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js new file mode 100644 index 0000000000000..0cded32a3f65f --- /dev/null +++ b/web_src/js/features/repo-settings.js @@ -0,0 +1,68 @@ +import {createMonaco} from './codeeditor.js'; +import {initRepoCommonFilterSearchDropdown} from './repo-common.js'; + +const {AppSubUrl, csrf} = window.config; + +function initRepoSettingsCollaboration() { + // Change collaborator access mode + $('.access-mode.menu .item').on('click', function () { + const $menu = $(this).parent(); + $.post($menu.data('url'), { + _csrf: csrf, + uid: $menu.data('uid'), + mode: $(this).data('value') + }); + }); +} + +function initRepoSettingSearchTeamBox() { + const $searchTeamBox = $('#search-team-box'); + $searchTeamBox.search({ + minCharacters: 2, + apiSettings: { + url: `${AppSubUrl}/api/v1/orgs/${$searchTeamBox.data('org')}/teams/search?q={query}`, + headers: {'X-Csrf-Token': csrf}, + onResponse(response) { + const items = []; + $.each(response.data, (_i, item) => { + const title = `${item.name} (${item.permission} access)`; + items.push({ + title, + }); + }); + + return {results: items}; + } + }, + searchFields: ['name', 'description'], + showNoResults: false + }); +} + + +async function initRepoSettingGitHook() { + if ($('.edit.githook').length === 0) return; + const filename = document.querySelector('.hook-filename').textContent; + await createMonaco($('#content')[0], filename, {language: 'shell'}); +} + +function initRepoSettingBranches() { + // Branches + if ($('.repository.settings.branches').length > 0) { + initRepoCommonFilterSearchDropdown('.protected-branches .dropdown'); + $('.enable-protection, .enable-whitelist, .enable-statuscheck').on('change', function () { + if (this.checked) { + $($(this).data('target')).removeClass('disabled'); + } else { + $($(this).data('target')).addClass('disabled'); + } + }); + $('.disable-whitelist').on('change', function () { + if (this.checked) { + $($(this).data('target')).addClass('disabled'); + } + }); + } +} + +export {initRepoSettingsCollaboration, initRepoSettingSearchTeamBox, initRepoSettingGitHook, initRepoSettingBranches}; diff --git a/web_src/js/features/repo-template.js b/web_src/js/features/repo-template.js new file mode 100644 index 0000000000000..a6f2867f5fd80 --- /dev/null +++ b/web_src/js/features/repo-template.js @@ -0,0 +1,51 @@ +import {htmlEscape} from 'escape-goat'; + +const {AppSubUrl} = window.config; + +function initRepoTemplateSearch() { + const $repoTemplate = $('#repo_template'); + const checkTemplate = function () { + const $templateUnits = $('#template_units'); + const $nonTemplate = $('#non_template'); + if ($repoTemplate.val() !== '' && $repoTemplate.val() !== '0') { + $templateUnits.show(); + $nonTemplate.hide(); + } else { + $templateUnits.hide(); + $nonTemplate.show(); + } + }; + $repoTemplate.on('change', checkTemplate); + checkTemplate(); + + const changeOwner = function () { + $('#repo_template_search') + .dropdown({ + apiSettings: { + url: `${AppSubUrl}/api/v1/repos/search?q={query}&template=true&priority_owner_id=${$('#uid').val()}`, + onResponse(response) { + const filteredResponse = {success: true, results: []}; + filteredResponse.results.push({ + name: '', + value: '' + }); + // Parse the response from the api to work with our dropdown + $.each(response.data, (_r, repo) => { + filteredResponse.results.push({ + name: htmlEscape(repo.full_name), + value: repo.id + }); + }); + return filteredResponse; + }, + cache: false, + }, + + fullTextSearch: true + }); + }; + $('#uid').on('change', changeOwner); + changeOwner(); +} + +export {initRepoTemplateSearch}; diff --git a/web_src/js/features/repo-wiki.js b/web_src/js/features/repo-wiki.js new file mode 100644 index 0000000000000..280f7a680b948 --- /dev/null +++ b/web_src/js/features/repo-wiki.js @@ -0,0 +1,176 @@ +import {initMarkupContent} from '../markup/content.js'; +import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js'; + +const {csrf} = window.config; + +function initRepoWikiForm() { + const $editArea = $('.repository.wiki textarea#edit_area'); + let sideBySideChanges = 0; + let sideBySideTimeout = null; + let hasSimpleMDE = true; + if ($editArea.length > 0) { + const simplemde = new SimpleMDE({ + autoDownloadFontAwesome: false, + element: $editArea[0], + forceSync: true, + previewRender(plainText, preview) { // Async method + // FIXME: still send render request when return back to edit mode + const render = function () { + sideBySideChanges = 0; + if (sideBySideTimeout !== null) { + clearTimeout(sideBySideTimeout); + sideBySideTimeout = null; + } + $.post($editArea.data('url'), { + _csrf: csrf, + mode: 'gfm', + context: $editArea.data('context'), + text: plainText, + wiki: true + }, (data) => { + preview.innerHTML = `
${data}
`; + initMarkupContent(); + }); + }; + + setTimeout(() => { + if (!simplemde.isSideBySideActive()) { + render(); + } else { + // delay preview by keystroke counting + sideBySideChanges++; + if (sideBySideChanges > 10) { + render(); + } + // or delay preview by timeout + if (sideBySideTimeout !== null) { + clearTimeout(sideBySideTimeout); + sideBySideTimeout = null; + } + sideBySideTimeout = setTimeout(render, 600); + } + }, 0); + if (!simplemde.isSideBySideActive()) { + return 'Loading...'; + } + return preview.innerHTML; + }, + renderingConfig: { + singleLineBreaks: false + }, + indentWithTabs: false, + tabSize: 4, + spellChecker: false, + toolbar: ['bold', 'italic', 'strikethrough', '|', + 'heading-1', 'heading-2', 'heading-3', 'heading-bigger', 'heading-smaller', '|', + { + name: 'code-inline', + action(e) { + const cm = e.codemirror; + const selection = cm.getSelection(); + cm.replaceSelection(`\`${selection}\``); + if (!selection) { + const cursorPos = cm.getCursor(); + cm.setCursor(cursorPos.line, cursorPos.ch - 1); + } + cm.focus(); + }, + className: 'fa fa-angle-right', + title: 'Add Inline Code', + }, 'code', 'quote', '|', { + name: 'checkbox-empty', + action(e) { + const cm = e.codemirror; + cm.replaceSelection(`\n- [ ] ${cm.getSelection()}`); + cm.focus(); + }, + className: 'fa fa-square-o', + title: 'Add Checkbox (empty)', + }, + { + name: 'checkbox-checked', + action(e) { + const cm = e.codemirror; + cm.replaceSelection(`\n- [x] ${cm.getSelection()}`); + cm.focus(); + }, + className: 'fa fa-check-square-o', + title: 'Add Checkbox (checked)', + }, '|', + 'unordered-list', 'ordered-list', '|', + 'link', 'image', 'table', 'horizontal-rule', '|', + 'clean-block', 'preview', 'fullscreen', 'side-by-side', '|', + { + name: 'revert-to-textarea', + action(e) { + e.toTextArea(); + hasSimpleMDE = false; + const $form = $('.repository.wiki.new .ui.form'); + const $root = $form.find('.field.content'); + const loading = $root.data('loading'); + $root.append(`
${loading}
`); + initCompMarkupContentPreviewTab($form); + }, + className: 'fa fa-file', + title: 'Revert to simple textarea', + }, + ] + }); + $(simplemde.codemirror.getInputField()).addClass('js-quick-submit'); + + setTimeout(() => { + const $bEdit = $('.repository.wiki.new .previewtabs a[data-tab="write"]'); + const $bPrev = $('.repository.wiki.new .previewtabs a[data-tab="preview"]'); + const $toolbar = $('.editor-toolbar'); + const $bPreview = $('.editor-toolbar button.preview'); + const $bSideBySide = $('.editor-toolbar a.fa-columns'); + $bEdit.on('click', (e) => { + if (!hasSimpleMDE) { + return false; + } + e.stopImmediatePropagation(); + if ($toolbar.hasClass('disabled-for-preview')) { + $bPreview.trigger('click'); + } + + return false; + }); + $bPrev.on('click', (e) => { + if (!hasSimpleMDE) { + return false; + } + e.stopImmediatePropagation(); + if (!$toolbar.hasClass('disabled-for-preview')) { + $bPreview.trigger('click'); + } + return false; + }); + $bPreview.on('click', () => { + setTimeout(() => { + if ($toolbar.hasClass('disabled-for-preview')) { + if ($bEdit.hasClass('active')) { + $bEdit.removeClass('active'); + } + if (!$bPrev.hasClass('active')) { + $bPrev.addClass('active'); + } + } else { + if (!$bEdit.hasClass('active')) { + $bEdit.addClass('active'); + } + if ($bPrev.hasClass('active')) { + $bPrev.removeClass('active'); + } + } + }, 0); + + return false; + }); + $bSideBySide.on('click', () => { + sideBySideChanges = 10; + }); + }, 0); + } +} + +export {initRepoWikiForm}; diff --git a/web_src/js/features/sshkey-helper.js b/web_src/js/features/sshkey-helper.js new file mode 100644 index 0000000000000..462406bbf8246 --- /dev/null +++ b/web_src/js/features/sshkey-helper.js @@ -0,0 +1,12 @@ +function initSshKeyFormParser() { +// Parse SSH Key + $('#ssh-key-content').on('change paste keyup', function () { + const arrays = $(this).val().split(' '); + const $title = $('#ssh-key-title'); + if ($title.val() === '' && arrays.length === 3 && arrays[2] !== '') { + $title.val(arrays[2]); + } + }); +} + +export {initSshKeyFormParser}; diff --git a/web_src/js/features/user-auth-u2f.js b/web_src/js/features/user-auth-u2f.js new file mode 100644 index 0000000000000..8997d9370829b --- /dev/null +++ b/web_src/js/features/user-auth-u2f.js @@ -0,0 +1,127 @@ +const {AppSubUrl, csrf} = window.config; + +function initUserAuthU2fAuth() { + if ($('#wait-for-key').length === 0) { + return; + } + u2fApi.ensureSupport().then(() => { + $.getJSON(`${AppSubUrl}/user/u2f/challenge`).done((req) => { + u2fApi.sign(req.appId, req.challenge, req.registeredKeys, 30) + .then(u2fSigned) + .catch((err) => { + if (err === undefined) { + u2fError(1); + return; + } + u2fError(err.metaData.code); + }); + }); + }).catch(() => { + // Fallback in case browser do not support U2F + window.location.href = `${AppSubUrl}/user/two_factor`; + }); +} + +function u2fSigned(resp) { + $.ajax({ + url: `${AppSubUrl}/user/u2f/sign`, + type: 'POST', + headers: {'X-Csrf-Token': csrf}, + data: JSON.stringify(resp), + contentType: 'application/json; charset=utf-8', + }).done((res) => { + window.location.replace(res); + }).fail(() => { + u2fError(1); + }); +} + +function u2fRegistered(resp) { + if (checkError(resp)) { + return; + } + $.ajax({ + url: `${AppSubUrl}/user/settings/security/u2f/register`, + type: 'POST', + headers: {'X-Csrf-Token': csrf}, + data: JSON.stringify(resp), + contentType: 'application/json; charset=utf-8', + success() { + window.location.reload(); + }, + fail() { + u2fError(1); + } + }); +} + +function checkError(resp) { + if (!('errorCode' in resp)) { + return false; + } + if (resp.errorCode === 0) { + return false; + } + u2fError(resp.errorCode); + return true; +} + +function u2fError(errorType) { + const u2fErrors = { + browser: $('#unsupported-browser'), + 1: $('#u2f-error-1'), + 2: $('#u2f-error-2'), + 3: $('#u2f-error-3'), + 4: $('#u2f-error-4'), + 5: $('.u2f_error_5') + }; + u2fErrors[errorType].removeClass('hide'); + + Object.keys(u2fErrors).forEach((type) => { + if (type !== `${errorType}`) { + u2fErrors[type].addClass('hide'); + } + }); + $('#u2f-error').modal('show'); +} + +function initUserAuthU2fRegister() { + $('#register-device').modal({allowMultiple: false}); + $('#u2f-error').modal({allowMultiple: false}); + $('#register-security-key').on('click', (e) => { + e.preventDefault(); + u2fApi.ensureSupport() + .then(u2fRegisterRequest) + .catch(() => { + u2fError('browser'); + }); + }); +} + +function u2fRegisterRequest() { + $.post(`${AppSubUrl}/user/settings/security/u2f/request_register`, { + _csrf: csrf, + name: $('#nickname').val() + }).done((req) => { + $('#nickname').closest('div.field').removeClass('error'); + $('#register-device').modal('show'); + if (req.registeredKeys === null) { + req.registeredKeys = []; + } + u2fApi.register(req.appId, req.registerRequests, req.registeredKeys, 30) + .then(u2fRegistered) + .catch((reason) => { + if (reason === undefined) { + u2fError(1); + return; + } + u2fError(reason.metaData.code); + }); + }).fail((xhr) => { + if (xhr.status === 409) { + $('#nickname').closest('div.field').addClass('error'); + } + }); +} + +export {initUserAuthU2fRegister, initUserAuthU2fAuth}; diff --git a/web_src/js/features/user-auth.js b/web_src/js/features/user-auth.js new file mode 100644 index 0000000000000..bf9548e16aa17 --- /dev/null +++ b/web_src/js/features/user-auth.js @@ -0,0 +1,48 @@ +function initUserAuthOauth2() { + const $oauth2LoginNav = $('#oauth2-login-navigator'); + if ($oauth2LoginNav.length === 0) return; + + $oauth2LoginNav.find('.oauth-login-image').click(() => { + const oauthLoader = $('#oauth2-login-loader'); + const oauthNav = $('#oauth2-login-navigator'); + + oauthNav.hide(); + oauthLoader.removeClass('disabled'); + + setTimeout(() => { + // recover previous content to let user try again + // usually redirection will be performed before this action + oauthLoader.addClass('disabled'); + oauthNav.show(); + }, 5000); + }); +} + +function initUserAuthLinkAccountView() { + const $lnkUserPage = $('.page-content.user.link-account'); + if ($lnkUserPage.length === 0) { + return false; + } + + const $signinTab = $lnkUserPage.find('.item[data-tab="auth-link-signin-tab"]'); + const $signUpTab = $lnkUserPage.find('.item[data-tab="auth-link-signup-tab"]'); + const $signInView = $lnkUserPage.find('.tab[data-tab="auth-link-signin-tab"]'); + const $signUpView = $lnkUserPage.find('.tab[data-tab="auth-link-signup-tab"]'); + + $signUpTab.on('click', () => { + $signinTab.removeClass('active'); + $signInView.removeClass('active'); + $signUpTab.addClass('active'); + $signUpView.addClass('active'); + return false; + }); + + $signinTab.on('click', () => { + $signUpTab.removeClass('active'); + $signUpView.removeClass('active'); + $signinTab.addClass('active'); + $signInView.addClass('active'); + }); +} + +export {initUserAuthOauth2, initUserAuthLinkAccountView}; diff --git a/web_src/js/features/user-settings.js b/web_src/js/features/user-settings.js new file mode 100644 index 0000000000000..9164d49dd9038 --- /dev/null +++ b/web_src/js/features/user-settings.js @@ -0,0 +1,18 @@ +function initUserSettings() { + // Options + if ($('.user.settings.profile').length > 0) { + $('#username').on('keyup', function () { + const $prompt = $('#name-change-prompt'); + const $prompt_redirect = $('#name-change-redirect-prompt'); + if ($(this).val().toString().toLowerCase() !== $(this).data('name').toString().toLowerCase()) { + $prompt.show(); + $prompt_redirect.show(); + } else { + $prompt.hide(); + $prompt_redirect.hide(); + } + }); + } +} + +export {initUserSettings}; diff --git a/web_src/js/index.js b/web_src/js/index.js index e6269c8abfe63..658980946a699 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -1,17 +1,11 @@ import './publicpath.js'; -import {htmlEscape} from 'escape-goat'; -import 'jquery.are-you-sure'; - import {initVueEnv} from './components/VueComponentLoader.js'; import {initRepoActivityTopAuthorsChart} from './components/RepoActivityTopAuthors.vue'; import {initDashboardRepoList} from './components/DashboardRepoList.js'; -import {initRepoBranchTagDropdown} from './components/RepoBranchTagDropdown.js'; import attachTribute from './features/tribute.js'; -import createColorPicker from './features/colorpicker.js'; -import createDropzone from './features/dropzone.js'; -import initClipboard from './features/clipboard.js'; +import initGlobalCopyToClipboardListener from './features/clipboard.js'; import initContextPopups from './features/contextpopup.js'; import initGitGraph from './features/gitgraph.js'; import initHeatmap from './features/heatmap.js'; @@ -21,3485 +15,150 @@ import initProject from './features/projects.js'; import initServiceWorker from './features/serviceworker.js'; import initTableSort from './features/tablesort.js'; import {initAdminUserListSearchForm} from './features/admin-users.js'; -import {createCodeEditor, createMonaco} from './features/codeeditor.js'; import {initMarkupAnchors} from './markup/anchors.js'; -import {initNotificationsTable, initNotificationCount} from './features/notification.js'; +import {initNotificationCount, initNotificationsTable} from './features/notification.js'; import {initLastCommitLoader} from './features/lastcommitloader.js'; import {initIssueContentHistory} from './features/issue-content-history.js'; import {initStopwatch} from './features/stopwatch.js'; -import {showLineButton} from './code/linebutton.js'; -import {initMarkupContent, initCommentContent} from './markup/content.js'; -import {stripTags, mqBinarySearch} from './utils.js'; -import {svg} from './svg.js'; - -const {AppSubUrl, csrf} = window.config; - -let previewFileModes; -const commentMDEditors = {}; +import {initCommentContent, initMarkupContent} from './markup/content.js'; + +import {initUserAuthLinkAccountView, initUserAuthOauth2} from './features/user-auth.js'; +import { + initRepoDiffConversationForm, + initRepoDiffFileViewToggle, + initRepoDiffReviewButton, +} from './features/repo-diff.js'; +import { + initRepoIssueDue, + initRepoIssueList, + initRepoIssueReferenceRepositorySearch, + initRepoIssueTimeTracking, + initRepoIssueWipTitle, + initRepoPullRequestMergeInstruction, + initRepoPullRequestReview, +} from './features/repo-issue.js'; +import {initRepoCommitButton} from './features/repo-commit.js'; +import { + initFootLanguageMenu, + initGlobalButtonClickOnEnter, + initGlobalButtons, + initGlobalCommon, + initGlobalDropzone, + initGlobalEnterQuickSubmit, + initGlobalFormDirtyLeaveConfirm, + initGlobalLinkActions, + initHeadNavbarContentToggle, +} from './features/common-global.js'; +import {initRepoTopicBar} from './features/repo-home.js'; +import {initAdminEmails} from './features/admin-emails.js'; +import {initAdminCommon} from './features/admin-common.js'; +import {initRepoTemplateSearch} from './features/repo-template.js'; +import {initRepoCodeView} from './features/repo-code.js'; +import {initSshKeyFormParser} from './features/sshkey-helper.js'; +import {initUserSettings} from './features/user-settings.js'; +import {initRepoArchiveLinks} from './features/repo-common.js'; +import {initRepoMigrationStatusChecker} from './features/repo-migrate.js'; +import { + initRepoSettingGitHook, + initRepoSettingsCollaboration, + initRepoSettingSearchTeamBox, +} from './features/repo-settings.js'; +import {initOrgTeamSearchRepoBox, initOrgTeamSettings} from './features/org-team.js'; +import {initUserAuthU2fAuth, initUserAuthU2fRegister} from './features/user-auth-u2f.js'; +import {initRepoRelease, initRepoReleaseEditor} from './features/repo-release.js'; +import {initRepoEditor} from './features/repo-editor.js'; +import {initSearchUserBox} from './features/comp/SearchUserBox.js'; +import {initInstall} from './features/install.js'; +import {initWebHookEditor} from './features/comp/WebHookEditor.js'; +import {initCommonIssue} from './features/common-issue.js'; +import {initRepoBranchButton} from './features/repo-branch.js'; +import {initCommonOrganization} from './features/common-organization.js'; +import {initRepoWikiForm} from './features/repo-wiki.js'; +import {initRepoCommentForm, initRepository} from './features/repo-legacy.js'; // Silence fomantic's error logging when tabs are used without a target content element $.fn.tab.settings.silent = true; -initVueEnv(); - -function initCommentPreviewTab($form) { - const $tabMenu = $form.find('.tabular.menu'); - $tabMenu.find('.item').tab(); - $tabMenu.find(`.item[data-tab="${$tabMenu.data('preview')}"]`).on('click', function () { - const $this = $(this); - $.post($this.data('url'), { - _csrf: csrf, - mode: 'comment', - context: $this.data('context'), - text: $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val() - }, (data) => { - const $previewPanel = $form.find(`.tab[data-tab="${$tabMenu.data('preview')}"]`); - $previewPanel.html(data); - initMarkupContent(); - }); - }); - - buttonsClickOnEnter(); -} - -function initEditPreviewTab($form) { - const $tabMenu = $form.find('.tabular.menu'); - $tabMenu.find('.item').tab(); - const $previewTab = $tabMenu.find(`.item[data-tab="${$tabMenu.data('preview')}"]`); - if ($previewTab.length) { - previewFileModes = $previewTab.data('preview-file-modes').split(','); - $previewTab.on('click', function () { - const $this = $(this); - let context = `${$this.data('context')}/`; - const mode = $this.data('markdown-mode') || 'comment'; - const treePathEl = $form.find('input#tree_path'); - if (treePathEl.length > 0) { - context += treePathEl.val(); - } - context = context.substring(0, context.lastIndexOf('/')); - $.post($this.data('url'), { - _csrf: csrf, - mode, - context, - text: $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val() - }, (data) => { - const $previewPanel = $form.find(`.tab[data-tab="${$tabMenu.data('preview')}"]`); - $previewPanel.html(data); - initMarkupContent(); - }); - }); - } -} - -function initEditDiffTab($form) { - const $tabMenu = $form.find('.tabular.menu'); - $tabMenu.find('.item').tab(); - $tabMenu.find(`.item[data-tab="${$tabMenu.data('diff')}"]`).on('click', function () { - const $this = $(this); - $.post($this.data('url'), { - _csrf: csrf, - context: $this.data('context'), - content: $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val() - }, (data) => { - const $diffPreviewPanel = $form.find(`.tab[data-tab="${$tabMenu.data('diff')}"]`); - $diffPreviewPanel.html(data); - }); - }); -} - -function initEditForm() { - if ($('.edit.form').length === 0) { - return; - } - - initEditPreviewTab($('.edit.form')); - initEditDiffTab($('.edit.form')); -} - -function initBranchSelector() { - const $selectBranch = $('.ui.select-branch'); - const $branchMenu = $selectBranch.find('.reference-list-menu'); - const $isNewIssue = $branchMenu.hasClass('new-issue'); - $branchMenu.find('.item:not(.no-select)').click(function () { - const selectedValue = $(this).data('id'); - const editMode = $('#editing_mode').val(); - $($(this).data('id-selector')).val(selectedValue); - if ($isNewIssue) { - $selectBranch.find('.ui .branch-name').text($(this).data('name')); - return; - } - - if (editMode === 'true') { - const form = $('#update_issueref_form'); - - $.post(form.attr('action'), { - _csrf: csrf, - ref: selectedValue - }, - () => { - window.location.reload(); - }); - } else if (editMode === '') { - $selectBranch.find('.ui .branch-name').text(selectedValue); - } - }); - $selectBranch.find('.reference.column').on('click', function () { - $selectBranch.find('.scrolling.reference-list-menu').css('display', 'none'); - $selectBranch.find('.reference .text').removeClass('black'); - $($(this).data('target')).css('display', 'block'); - $(this).find('.text').addClass('black'); - return false; - }); -} - -function initLabelEdit() { -// Create label - const $newLabelPanel = $('.new-label.segment'); - $('.new-label.button').on('click', () => { - $newLabelPanel.show(); - }); - $('.new-label.segment .cancel').on('click', () => { - $newLabelPanel.hide(); - }); - - initColorPicker(); - - $('.edit-label-button').on('click', function () { - $('.edit-label .color-picker').minicolors('value', $(this).data('color')); - $('#label-modal-id').val($(this).data('id')); - $('.edit-label .new-label-input').val($(this).data('title')); - $('.edit-label .new-label-desc-input').val($(this).data('description')); - $('.edit-label .color-picker').val($(this).data('color')); - $('.edit-label .minicolors-swatch-color').css('background-color', $(this).data('color')); - $('.edit-label.modal').modal({ - onApprove() { - $('.edit-label.form').trigger('submit'); - } - }).modal('show'); - return false; - }); -} - -function initColorPicker() { - createColorPicker($('.color-picker')); - - $('.precolors .color').on('click', function () { - const color_hex = $(this).data('color-hex'); - $('.color-picker').val(color_hex); - $('.minicolors-swatch-color').css('background-color', color_hex); - }); -} - -function updateIssuesMeta(url, action, issueIds, elementId) { - return new Promise(((resolve) => { - $.ajax({ - type: 'POST', - url, - data: { - _csrf: csrf, - action, - issue_ids: issueIds, - id: elementId, - }, - success: resolve - }); - })); -} - -function initRepoStatusChecker() { - const migrating = $('#repo_migrating'); - $('#repo_migrating_failed').hide(); - $('#repo_migrating_failed_image').hide(); - $('#repo_migrating_progress_message').hide(); - if (migrating) { - const task = migrating.attr('task'); - if (typeof task === 'undefined') { - return; - } - $.ajax({ - type: 'GET', - url: `${AppSubUrl}/user/task/${task}`, - data: { - _csrf: csrf, - }, - complete(xhr) { - if (xhr.status === 200 && xhr.responseJSON) { - if (xhr.responseJSON.status === 4) { - window.location.reload(); - return; - } else if (xhr.responseJSON.status === 3) { - $('#repo_migrating_progress').hide(); - $('#repo_migrating').hide(); - $('#repo_migrating_failed').show(); - $('#repo_migrating_failed_image').show(); - $('#repo_migrating_failed_error').text(xhr.responseJSON.message); - return; - } - if (xhr.responseJSON.message) { - $('#repo_migrating_progress_message').show(); - $('#repo_migrating_progress_message').text(xhr.responseJSON.message); - } - setTimeout(() => { - initRepoStatusChecker(); - }, 2000); - return; - } - $('#repo_migrating_progress').hide(); - $('#repo_migrating').hide(); - $('#repo_migrating_failed').show(); - $('#repo_migrating_failed_image').show(); - } - }); - } -} - -function initReactionSelector(parent) { - let reactions = ''; - if (!parent) { - parent = $(document); - reactions = '.reactions > '; - } - - parent.find(`${reactions}a.label`).popup({position: 'bottom left', metadata: {content: 'title', title: 'none'}}); - - parent.find(`.select-reaction > .menu > .item, ${reactions}a.label`).on('click', function (e) { - e.preventDefault(); - - if ($(this).hasClass('disabled')) return; - - const actionURL = $(this).hasClass('item') ? $(this).closest('.select-reaction').data('action-url') : $(this).data('action-url'); - const url = `${actionURL}/${$(this).hasClass('blue') ? 'unreact' : 'react'}`; - $.ajax({ - type: 'POST', - url, - data: { - _csrf: csrf, - content: $(this).data('content') - } - }).done((resp) => { - if (resp && (resp.html || resp.empty)) { - const content = $(this).closest('.content'); - let react = content.find('.segment.reactions'); - if ((!resp.empty || resp.html === '') && react.length > 0) { - react.remove(); - } - if (!resp.empty) { - react = $('
'); - const attachments = content.find('.segment.bottom:first'); - if (attachments.length > 0) { - react.insertBefore(attachments); - } else { - react.appendTo(content); - } - react.html(resp.html); - react.find('.dropdown').dropdown(); - initReactionSelector(react); - } - } - }); - }); -} - -function insertAtCursor(field, value) { - if (field.selectionStart || field.selectionStart === 0) { - const startPos = field.selectionStart; - const endPos = field.selectionEnd; - field.value = field.value.substring(0, startPos) + value + field.value.substring(endPos, field.value.length); - field.selectionStart = startPos + value.length; - field.selectionEnd = startPos + value.length; - } else { - field.value += value; - } -} - -function replaceAndKeepCursor(field, oldval, newval) { - if (field.selectionStart || field.selectionStart === 0) { - const startPos = field.selectionStart; - const endPos = field.selectionEnd; - field.value = field.value.replace(oldval, newval); - field.selectionStart = startPos + newval.length - oldval.length; - field.selectionEnd = endPos + newval.length - oldval.length; - } else { - field.value = field.value.replace(oldval, newval); - } -} - -function getPastedImages(e) { - if (!e.clipboardData) return []; - - const files = []; - for (const item of e.clipboardData.items || []) { - if (!item.type || !item.type.startsWith('image/')) continue; - files.push(item.getAsFile()); - } - - if (files.length) { - e.preventDefault(); - e.stopPropagation(); - } - return files; -} - -async function uploadFile(file, uploadUrl) { - const formData = new FormData(); - formData.append('file', file, file.name); - - const res = await fetch(uploadUrl, { - method: 'POST', - headers: {'X-Csrf-Token': csrf}, - body: formData, - }); - return await res.json(); -} - -function reload() { - window.location.reload(); -} - -function initImagePaste(target) { - target.each(function () { - const dropzone = this.querySelector('.dropzone'); - if (!dropzone) { - return; - } - const uploadUrl = dropzone.dataset.uploadUrl; - const dropzoneFiles = dropzone.querySelector('.files'); - for (const textarea of this.querySelectorAll('textarea')) { - textarea.addEventListener('paste', async (e) => { - for (const img of getPastedImages(e)) { - const name = img.name.substr(0, img.name.lastIndexOf('.')); - insertAtCursor(textarea, `![${name}]()`); - const data = await uploadFile(img, uploadUrl); - replaceAndKeepCursor(textarea, `![${name}]()`, `![${name}](${AppSubUrl}/attachments/${data.uuid})`); - const input = $(``).val(data.uuid); - dropzoneFiles.appendChild(input[0]); - } - }, false); - } - }); -} - -function initSimpleMDEImagePaste(simplemde, dropzone, files) { - const uploadUrl = dropzone.dataset.uploadUrl; - simplemde.codemirror.on('paste', async (_, e) => { - for (const img of getPastedImages(e)) { - const name = img.name.substr(0, img.name.lastIndexOf('.')); - const data = await uploadFile(img, uploadUrl); - const pos = simplemde.codemirror.getCursor(); - simplemde.codemirror.replaceRange(`![${name}](${AppSubUrl}/attachments/${data.uuid})`, pos); - const input = $(``).val(data.uuid); - files.append(input); - } - }); -} - -let autoSimpleMDE; - -function initCommentForm() { - if ($('.comment.form').length === 0) { - return; - } - - autoSimpleMDE = setCommentSimpleMDE($('.comment.form textarea:not(.review-textarea)')); - initBranchSelector(); - initCommentPreviewTab($('.comment.form')); - initImagePaste($('.comment.form')); - - // Listsubmit - function initListSubmits(selector, outerSelector) { - const $list = $(`.ui.${outerSelector}.list`); - const $noSelect = $list.find('.no-select'); - const $listMenu = $(`.${selector} .menu`); - let hasUpdateAction = $listMenu.data('action') === 'update'; - const items = {}; - - $(`.${selector}`).dropdown('setting', 'onHide', () => { - hasUpdateAction = $listMenu.data('action') === 'update'; // Update the var - if (hasUpdateAction) { - const promises = []; - Object.keys(items).forEach((elementId) => { - const item = items[elementId]; - const promise = updateIssuesMeta( - item['update-url'], - item.action, - item['issue-id'], - elementId, - ); - promises.push(promise); - }); - Promise.all(promises).then(reload); - } - }); - - $listMenu.find('.item:not(.no-select)').on('click', function (e) { - e.preventDefault(); - if ($(this).hasClass('ban-change')) { - return false; - } - - hasUpdateAction = $listMenu.data('action') === 'update'; // Update the var - if ($(this).hasClass('checked')) { - $(this).removeClass('checked'); - $(this).find('.octicon-check').addClass('invisible'); - if (hasUpdateAction) { - if (!($(this).data('id') in items)) { - items[$(this).data('id')] = { - 'update-url': $listMenu.data('update-url'), - action: 'detach', - 'issue-id': $listMenu.data('issue-id'), - }; - } else { - delete items[$(this).data('id')]; - } - } - } else { - $(this).addClass('checked'); - $(this).find('.octicon-check').removeClass('invisible'); - if (hasUpdateAction) { - if (!($(this).data('id') in items)) { - items[$(this).data('id')] = { - 'update-url': $listMenu.data('update-url'), - action: 'attach', - 'issue-id': $listMenu.data('issue-id'), - }; - } else { - delete items[$(this).data('id')]; - } - } - } - - // TODO: Which thing should be done for choosing review requests - // to make chosen items be shown on time here? - if (selector === 'select-reviewers-modify' || selector === 'select-assignees-modify') { - return false; - } - - const listIds = []; - $(this).parent().find('.item').each(function () { - if ($(this).hasClass('checked')) { - listIds.push($(this).data('id')); - $($(this).data('id-selector')).removeClass('hide'); - } else { - $($(this).data('id-selector')).addClass('hide'); - } - }); - if (listIds.length === 0) { - $noSelect.removeClass('hide'); - } else { - $noSelect.addClass('hide'); - } - $($(this).parent().data('id')).val(listIds.join(',')); - return false; - }); - $listMenu.find('.no-select.item').on('click', function (e) { - e.preventDefault(); - if (hasUpdateAction) { - updateIssuesMeta( - $listMenu.data('update-url'), - 'clear', - $listMenu.data('issue-id'), - '', - ).then(reload); - } - - $(this).parent().find('.item').each(function () { - $(this).removeClass('checked'); - $(this).find('.octicon').addClass('invisible'); - }); - - if (selector === 'select-reviewers-modify' || selector === 'select-assignees-modify') { - return false; - } - - $list.find('.item').each(function () { - $(this).addClass('hide'); - }); - $noSelect.removeClass('hide'); - $($(this).parent().data('id')).val(''); - }); - } - - // Init labels and assignees - initListSubmits('select-label', 'labels'); - initListSubmits('select-assignees', 'assignees'); - initListSubmits('select-assignees-modify', 'assignees'); - initListSubmits('select-reviewers-modify', 'assignees'); - - function selectItem(select_id, input_id) { - const $menu = $(`${select_id} .menu`); - const $list = $(`.ui${select_id}.list`); - const hasUpdateAction = $menu.data('action') === 'update'; - - $menu.find('.item:not(.no-select)').on('click', function () { - $(this).parent().find('.item').each(function () { - $(this).removeClass('selected active'); - }); - - $(this).addClass('selected active'); - if (hasUpdateAction) { - updateIssuesMeta( - $menu.data('update-url'), - '', - $menu.data('issue-id'), - $(this).data('id'), - ).then(reload); - } - - let icon = ''; - if (input_id === '#milestone_id') { - icon = svg('octicon-milestone', 18, 'mr-3'); - } else if (input_id === '#project_id') { - icon = svg('octicon-project', 18, 'mr-3'); - } else if (input_id === '#assignee_id') { - icon = ``; - } - - $list.find('.selected').html(` - - ${icon} - ${htmlEscape($(this).text())} - - `); - - $(`.ui${select_id}.list .no-select`).addClass('hide'); - $(input_id).val($(this).data('id')); - }); - $menu.find('.no-select.item').on('click', function () { - $(this).parent().find('.item:not(.no-select)').each(function () { - $(this).removeClass('selected active'); - }); - - if (hasUpdateAction) { - updateIssuesMeta( - $menu.data('update-url'), - '', - $menu.data('issue-id'), - $(this).data('id'), - ).then(reload); - } - - $list.find('.selected').html(''); - $list.find('.no-select').removeClass('hide'); - $(input_id).val(''); - }); - } - - // Milestone, Assignee, Project - selectItem('.select-project', '#project_id'); - selectItem('.select-milestone', '#milestone_id'); - selectItem('.select-assignee', '#assignee_id'); -} - -function initInstall() { - if ($('.install').length === 0) { - return; - } - - if ($('#db_host').val() === '') { - $('#db_host').val('127.0.0.1:3306'); - $('#db_user').val('gitea'); - $('#db_name').val('gitea'); - } - - // Database type change detection. - $('#db_type').on('change', function () { - const sqliteDefault = 'data/gitea.db'; - const tidbDefault = 'data/gitea_tidb'; - - const dbType = $(this).val(); - if (dbType === 'SQLite3') { - $('#sql_settings').hide(); - $('#pgsql_settings').hide(); - $('#mysql_settings').hide(); - $('#sqlite_settings').show(); - - if (dbType === 'SQLite3' && $('#db_path').val() === tidbDefault) { - $('#db_path').val(sqliteDefault); - } - return; - } - - const dbDefaults = { - MySQL: '127.0.0.1:3306', - PostgreSQL: '127.0.0.1:5432', - MSSQL: '127.0.0.1:1433' - }; - - $('#sqlite_settings').hide(); - $('#sql_settings').show(); - - $('#pgsql_settings').toggle(dbType === 'PostgreSQL'); - $('#mysql_settings').toggle(dbType === 'MySQL'); - $.each(dbDefaults, (_type, defaultHost) => { - if ($('#db_host').val() === defaultHost) { - $('#db_host').val(dbDefaults[dbType]); - return false; - } - }); - }); - - // TODO: better handling of exclusive relations. - $('#offline-mode input').on('change', function () { - if ($(this).is(':checked')) { - $('#disable-gravatar').checkbox('check'); - $('#federated-avatar-lookup').checkbox('uncheck'); - } - }); - $('#disable-gravatar input').on('change', function () { - if ($(this).is(':checked')) { - $('#federated-avatar-lookup').checkbox('uncheck'); - } else { - $('#offline-mode').checkbox('uncheck'); - } - }); - $('#federated-avatar-lookup input').on('change', function () { - if ($(this).is(':checked')) { - $('#disable-gravatar').checkbox('uncheck'); - $('#offline-mode').checkbox('uncheck'); - } - }); - $('#enable-openid-signin input').on('change', function () { - if ($(this).is(':checked')) { - if (!$('#disable-registration input').is(':checked')) { - $('#enable-openid-signup').checkbox('check'); - } - } else { - $('#enable-openid-signup').checkbox('uncheck'); - } - }); - $('#disable-registration input').on('change', function () { - if ($(this).is(':checked')) { - $('#enable-captcha').checkbox('uncheck'); - $('#enable-openid-signup').checkbox('uncheck'); - } else { - $('#enable-openid-signup').checkbox('check'); - } - }); - $('#enable-captcha input').on('change', function () { - if ($(this).is(':checked')) { - $('#disable-registration').checkbox('uncheck'); - } - }); -} - -function initIssueComments() { - if ($('.repository.view.issue .timeline').length === 0) return; - - $('.re-request-review').on('click', function (event) { - const url = $(this).data('update-url'); - const issueId = $(this).data('issue-id'); - const id = $(this).data('id'); - const isChecked = $(this).hasClass('checked'); - - event.preventDefault(); - updateIssuesMeta( - url, - isChecked ? 'detach' : 'attach', - issueId, - id, - ).then(reload); - return false; - }); - - $('.dismiss-review-btn').on('click', function (e) { - e.preventDefault(); - const $this = $(this); - const $dismissReviewModal = $this.next(); - $dismissReviewModal.modal('show'); - }); - - $(document).on('click', (event) => { - const urlTarget = $(':target'); - if (urlTarget.length === 0) return; - - const urlTargetId = urlTarget.attr('id'); - if (!urlTargetId) return; - if (!/^(issue|pull)(comment)?-\d+$/.test(urlTargetId)) return; - - const $target = $(event.target); - - if ($target.closest(`#${urlTargetId}`).length === 0) { - const scrollPosition = $(window).scrollTop(); - window.location.hash = ''; - $(window).scrollTop(scrollPosition); - window.history.pushState(null, null, ' '); - } - }); -} - -function getArchive($target, url, first) { - $.ajax({ - url, - type: 'POST', - data: { - _csrf: csrf, - }, - complete(xhr) { - if (xhr.status === 200) { - if (!xhr.responseJSON) { - // XXX Shouldn't happen? - $target.closest('.dropdown').children('i').removeClass('loading'); - return; - } - - if (!xhr.responseJSON.complete) { - $target.closest('.dropdown').children('i').addClass('loading'); - // Wait for only three quarters of a second initially, in case it's - // quickly archived. - setTimeout(() => { - getArchive($target, url, false); - }, first ? 750 : 2000); - } else { - // We don't need to continue checking. - $target.closest('.dropdown').children('i').removeClass('loading'); - window.location.href = url; - } - } - } - }); -} - -function initArchiveLinks() { - if ($('.archive-link').length === 0) { - return; - } - - $('.archive-link').on('click', function (event) { - const url = $(this).data('url'); - if (typeof url === 'undefined') { - return; - } - - event.preventDefault(); - getArchive($(event.target), url, true); - }); -} - -async function initRepository() { - if ($('.repository').length === 0) { - return; - } - - function initFilterSearchDropdown(selector) { - const $dropdown = $(selector); - $dropdown.dropdown({ - fullTextSearch: true, - selectOnKeydown: false, - onChange(_text, _value, $choice) { - if ($choice.data('url')) { - window.location.href = $choice.data('url'); - } - }, - message: {noResults: $dropdown.data('no-results')} - }); - } - - // Commit statuses - $('.commit-statuses-trigger').each(function () { - $(this) - .popup({ - on: 'click', - position: ($('.repository.file.list').length > 0 ? 'right center' : 'left center'), - }); - }); - - // File list and commits - if ($('.repository.file.list').length > 0 || - $('.repository.commits').length > 0 || $('.repository.release').length > 0) { - initRepoBranchTagDropdown('.choose.reference .dropdown'); - } - - // Wiki - if ($('.repository.wiki.view').length > 0) { - initFilterSearchDropdown('.choose.page .dropdown'); - } - - // Options - if ($('.repository.settings.options').length > 0) { - // Enable or select internal/external wiki system and issue tracker. - $('.enable-system').on('change', function () { - if (this.checked) { - $($(this).data('target')).removeClass('disabled'); - if (!$(this).data('context')) $($(this).data('context')).addClass('disabled'); - } else { - $($(this).data('target')).addClass('disabled'); - if (!$(this).data('context')) $($(this).data('context')).removeClass('disabled'); - } - }); - $('.enable-system-radio').on('change', function () { - if (this.value === 'false') { - $($(this).data('target')).addClass('disabled'); - if (typeof $(this).data('context') !== 'undefined') $($(this).data('context')).removeClass('disabled'); - } else if (this.value === 'true') { - $($(this).data('target')).removeClass('disabled'); - if (typeof $(this).data('context') !== 'undefined') $($(this).data('context')).addClass('disabled'); - } - }); - } - - // Labels - if ($('.repository.labels').length > 0) { - initLabelEdit(); - } - - // Milestones - if ($('.repository.new.milestone').length > 0) { - $('#clear-date').on('click', () => { - $('#deadline').val(''); - return false; - }); - } - - // Repo Creation - if ($('.repository.new.repo').length > 0) { - $('input[name="gitignores"], input[name="license"]').on('change', () => { - const gitignores = $('input[name="gitignores"]').val(); - const license = $('input[name="license"]').val(); - if (gitignores || license) { - $('input[name="auto_init"]').prop('checked', true); - } - }); - } - - // Issues - if ($('.repository.view.issue').length > 0) { - // Edit issue title - const $issueTitle = $('#issue-title'); - const $editInput = $('#edit-title-input input'); - const editTitleToggle = function () { - $issueTitle.toggle(); - $('.not-in-edit').toggle(); - $('#edit-title-input').toggle(); - $('#pull-desc').toggle(); - $('#pull-desc-edit').toggle(); - $('.in-edit').toggle(); - $('#issue-title-wrapper').toggleClass('edit-active'); - $editInput.focus(); - return false; - }; - - const changeBranchSelect = function () { - const selectionTextField = $('#pull-target-branch'); - - const baseName = selectionTextField.data('basename'); - const branchNameNew = $(this).data('branch'); - const branchNameOld = selectionTextField.data('branch'); - - // Replace branch name to keep translation from HTML template - selectionTextField.html(selectionTextField.html().replace( - `${baseName}:${branchNameOld}`, - `${baseName}:${branchNameNew}` - )); - selectionTextField.data('branch', branchNameNew); // update branch name in setting - }; - $('#branch-select > .item').on('click', changeBranchSelect); - - $('#edit-title').on('click', editTitleToggle); - $('#cancel-edit-title').on('click', editTitleToggle); - $('#save-edit-title').on('click', editTitleToggle).on('click', function () { - const pullrequest_targetbranch_change = function (update_url) { - const targetBranch = $('#pull-target-branch').data('branch'); - const $branchTarget = $('#branch_target'); - if (targetBranch === $branchTarget.text()) { - return false; - } - $.post(update_url, { - _csrf: csrf, - target_branch: targetBranch - }).done((data) => { - $branchTarget.text(data.base_branch); - }).always(() => { - reload(); - }); - }; - - const pullrequest_target_update_url = $(this).data('target-update-url'); - if ($editInput.val().length === 0 || $editInput.val() === $issueTitle.text()) { - $editInput.val($issueTitle.text()); - pullrequest_targetbranch_change(pullrequest_target_update_url); - } else { - $.post($(this).data('update-url'), { - _csrf: csrf, - title: $editInput.val() - }, (data) => { - $editInput.val(data.title); - $issueTitle.text(data.title); - pullrequest_targetbranch_change(pullrequest_target_update_url); - reload(); - }); - } - return false; - }); - - // Toggle WIP - $('.toggle-wip a, .toggle-wip button').on('click', async (e) => { - e.preventDefault(); - const {title, wipPrefix, updateUrl} = e.currentTarget.closest('.toggle-wip').dataset; - await $.post(updateUrl, { - _csrf: csrf, - title: title?.startsWith(wipPrefix) ? title.substr(wipPrefix.length).trim() : `${wipPrefix.trim()} ${title}`, - }); - reload(); - }); - - // Issue Comments - initIssueComments(); - - // Issue/PR Context Menus - $('.context-dropdown').dropdown({ - action: 'hide' - }); - - // Previous/Next code review conversation - $(document).on('click', '.previous-conversation', (e) => { - const $conversation = $(e.currentTarget).closest('.comment-code-cloud'); - const $conversations = $('.comment-code-cloud:not(.hide)'); - const index = $conversations.index($conversation); - const previousIndex = index > 0 ? index - 1 : $conversations.length - 1; - const $previousConversation = $conversations.eq(previousIndex); - const anchor = $previousConversation.find('.comment').first().attr('id'); - window.location.href = `#${anchor}`; - }); - $(document).on('click', '.next-conversation', (e) => { - const $conversation = $(e.currentTarget).closest('.comment-code-cloud'); - const $conversations = $('.comment-code-cloud:not(.hide)'); - const index = $conversations.index($conversation); - const nextIndex = index < $conversations.length - 1 ? index + 1 : 0; - const $nextConversation = $conversations.eq(nextIndex); - const anchor = $nextConversation.find('.comment').first().attr('id'); - window.location.href = `#${anchor}`; - }); - - // Quote reply - $(document).on('click', '.quote-reply', function (event) { - $(this).closest('.dropdown').find('.menu').toggle('visible'); - const target = $(this).data('target'); - const quote = $(`#comment-${target}`).text().replace(/\n/g, '\n> '); - const content = `> ${quote}\n\n`; - let $simplemde = autoSimpleMDE; - if ($(this).hasClass('quote-reply-diff')) { - const $parent = $(this).closest('.comment-code-cloud'); - $parent.find('button.comment-form-reply').trigger('click'); - $simplemde = $parent.find('[name="content"]').data('simplemde'); - } - if ($simplemde !== null) { - if ($simplemde.value() !== '') { - $simplemde.value(`${$simplemde.value()}\n\n${content}`); - } else { - $simplemde.value(`${content}`); - } - } - requestAnimationFrame(() => { - $simplemde.codemirror.focus(); - $simplemde.codemirror.setCursor($simplemde.codemirror.lineCount(), 0); - }); - event.preventDefault(); - }); - - // Reference issue - $(document).on('click', '.reference-issue', function (event) { - const $this = $(this); - - $this.closest('.dropdown').find('.menu').toggle('visible'); - - const content = $(`#comment-${$this.data('target')}`).text(); - - const poster = $this.data('poster-username'); - const reference = $this.data('reference'); - - const $modal = $($this.data('modal')); - $modal.find('textarea[name="content"]').val(`${content}\n\n_Originally posted by @${poster} in ${reference}_`); - - $modal.modal('show'); - - event.preventDefault(); - }); - - // Edit issue or comment content - $(document).on('click', '.edit-content', async function (event) { - $(this).closest('.dropdown').find('.menu').toggle('visible'); - const $segment = $(this).closest('.header').next(); - const $editContentZone = $segment.find('.edit-content-zone'); - const $renderContent = $segment.find('.render-content'); - const $rawContent = $segment.find('.raw-content'); - let $textarea; - let $simplemde; - - // Setup new form - if ($editContentZone.html().length === 0) { - $editContentZone.html($('#edit-content-form').html()); - $textarea = $editContentZone.find('textarea'); - attachTribute($textarea.get(), {mentions: true, emoji: true}); - - let dz; - const $dropzone = $editContentZone.find('.dropzone'); - if ($dropzone.length === 1) { - $dropzone.data('saved', false); - - const fileUuidDict = {}; - dz = await createDropzone($dropzone[0], { - url: $dropzone.data('upload-url'), - headers: {'X-Csrf-Token': csrf}, - maxFiles: $dropzone.data('max-file'), - maxFilesize: $dropzone.data('max-size'), - acceptedFiles: (['*/*', ''].includes($dropzone.data('accepts'))) ? null : $dropzone.data('accepts'), - addRemoveLinks: true, - dictDefaultMessage: $dropzone.data('default-message'), - dictInvalidFileType: $dropzone.data('invalid-input-type'), - dictFileTooBig: $dropzone.data('file-too-big'), - dictRemoveFile: $dropzone.data('remove-file'), - timeout: 0, - thumbnailMethod: 'contain', - thumbnailWidth: 480, - thumbnailHeight: 480, - init() { - this.on('success', (file, data) => { - fileUuidDict[file.uuid] = { - submitted: false - }; - const input = $(``).val(data.uuid); - $dropzone.find('.files').append(input); - }); - this.on('removedfile', (file) => { - $(`#${file.uuid}`).remove(); - if ($dropzone.data('remove-url') && !fileUuidDict[file.uuid].submitted) { - $.post($dropzone.data('remove-url'), { - file: file.uuid, - _csrf: csrf, - }); - } - }); - this.on('submit', () => { - $.each(fileUuidDict, (fileUuid) => { - fileUuidDict[fileUuid].submitted = true; - }); - }); - this.on('reload', () => { - $.getJSON($editContentZone.data('attachment-url'), (data) => { - dz.removeAllFiles(true); - $dropzone.find('.files').empty(); - $.each(data, function () { - const imgSrc = `${$dropzone.data('link-url')}/${this.uuid}`; - dz.emit('addedfile', this); - dz.emit('thumbnail', this, imgSrc); - dz.emit('complete', this); - dz.files.push(this); - fileUuidDict[this.uuid] = { - submitted: true, - }; - $dropzone.find(`img[src='${imgSrc}']`).css('max-width', '100%'); - const input = $(``).val(this.uuid); - $dropzone.find('.files').append(input); - }); - }); - }); - } - }); - dz.emit('reload'); - } - // Give new write/preview data-tab name to distinguish from others - const $editContentForm = $editContentZone.find('.ui.comment.form'); - const $tabMenu = $editContentForm.find('.tabular.menu'); - $tabMenu.attr('data-write', $editContentZone.data('write')); - $tabMenu.attr('data-preview', $editContentZone.data('preview')); - $tabMenu.find('.write.item').attr('data-tab', $editContentZone.data('write')); - $tabMenu.find('.preview.item').attr('data-tab', $editContentZone.data('preview')); - $editContentForm.find('.write').attr('data-tab', $editContentZone.data('write')); - $editContentForm.find('.preview').attr('data-tab', $editContentZone.data('preview')); - $simplemde = setCommentSimpleMDE($textarea); - commentMDEditors[$editContentZone.data('write')] = $simplemde; - initCommentPreviewTab($editContentForm); - if ($dropzone.length === 1) { - initSimpleMDEImagePaste($simplemde, $dropzone[0], $dropzone.find('.files')); - } - - $editContentZone.find('.cancel.button').on('click', () => { - $renderContent.show(); - $editContentZone.hide(); - if (dz) { - dz.emit('reload'); - } - }); - $editContentZone.find('.save.button').on('click', () => { - $renderContent.show(); - $editContentZone.hide(); - const $attachments = $dropzone.find('.files').find('[name=files]').map(function () { - return $(this).val(); - }).get(); - $.post($editContentZone.data('update-url'), { - _csrf: csrf, - content: $textarea.val(), - context: $editContentZone.data('context'), - files: $attachments - }, (data) => { - if (data.length === 0 || data.content.length === 0) { - $renderContent.html($('#no-content').html()); - $rawContent.text(''); - } else { - $renderContent.html(data.content); - $rawContent.text($textarea.val()); - } - const $content = $segment; - if (!$content.find('.dropzone-attachments').length) { - if (data.attachments !== '') { - $content.append(` -
-
- `); - $content.find('.dropzone-attachments').replaceWith(data.attachments); - } - } else if (data.attachments === '') { - $content.find('.dropzone-attachments').remove(); - } else { - $content.find('.dropzone-attachments').replaceWith(data.attachments); - } - if (dz) { - dz.emit('submit'); - dz.emit('reload'); - } - initMarkupContent(); - initCommentContent(); - }); - }); - } else { - $textarea = $segment.find('textarea'); - $simplemde = commentMDEditors[$editContentZone.data('write')]; - } - - // Show write/preview tab and copy raw content as needed - $editContentZone.show(); - $renderContent.hide(); - if ($textarea.val().length === 0) { - $textarea.val($rawContent.text()); - $simplemde.value($rawContent.text()); - } - requestAnimationFrame(() => { - $textarea.focus(); - $simplemde.codemirror.focus(); - }); - event.preventDefault(); - }); - - // Delete comment - $(document).on('click', '.delete-comment', function () { - const $this = $(this); - if (window.confirm($this.data('locale'))) { - $.post($this.data('url'), { - _csrf: csrf - }).done(() => { - const $conversationHolder = $this.closest('.conversation-holder'); - $(`#${$this.data('comment-id')}`).remove(); - if ($conversationHolder.length && !$conversationHolder.find('.comment').length) { - const path = $conversationHolder.data('path'); - const side = $conversationHolder.data('side'); - const idx = $conversationHolder.data('idx'); - const lineType = $conversationHolder.closest('tr').data('line-type'); - if (lineType === 'same') { - $(`a.add-code-comment[data-path="${path}"][data-idx="${idx}"]`).removeClass('invisible'); - } else { - $(`a.add-code-comment[data-path="${path}"][data-side="${side}"][data-idx="${idx}"]`).removeClass('invisible'); - } - $conversationHolder.remove(); - } - }); - } - return false; - }); - - // Delete Issue dependency - $(document).on('click', '.delete-dependency-button', (e) => { - const {id, type} = e.currentTarget.dataset; - - $('.remove-dependency').modal({ - closable: false, - duration: 200, - onApprove: () => { - $('#removeDependencyID').val(id); - $('#dependencyType').val(type); - $('#removeDependencyForm').trigger('submit'); - } - }).modal('show'); - }); - - // Cancel inline code comment - $(document).on('click', '.cancel-code-comment', (e) => { - const form = $(e.currentTarget).closest('form'); - if (form.length > 0 && form.hasClass('comment-form')) { - form.addClass('hide'); - form.closest('.comment-code-cloud').find('button.comment-form-reply').show(); - } else { - form.closest('.comment-code-cloud').remove(); - } - }); - - // Change status - const $statusButton = $('#status-button'); - $('#comment-form textarea').on('keyup', function () { - const $simplemde = $(this).data('simplemde'); - const value = ($simplemde && $simplemde.value()) ? $simplemde.value() : $(this).val(); - $statusButton.text($statusButton.data(value.length === 0 ? 'status' : 'status-and-comment')); - }); - $statusButton.on('click', () => { - $('#status').val($statusButton.data('status-val')); - $('#comment-form').trigger('submit'); - }); - - // Pull Request merge button - const $mergeButton = $('.merge-button > button'); - $mergeButton.on('click', function (e) { - e.preventDefault(); - $(`.${$(this).data('do')}-fields`).show(); - $(this).parent().hide(); - $('.instruct-toggle').hide(); - $('.instruct-content').hide(); - }); - $('.merge-button > .dropdown').dropdown({ - onChange(_text, _value, $choice) { - if ($choice.data('do')) { - $mergeButton.find('.button-text').text($choice.text()); - $mergeButton.data('do', $choice.data('do')); - } - } - }); - $('.merge-cancel').on('click', function (e) { - e.preventDefault(); - $(this).closest('.form').hide(); - $mergeButton.parent().show(); - $('.instruct-toggle').show(); - }); - - // Pull Request update button - const $pullUpdateButton = $('.update-button > button'); - $pullUpdateButton.on('click', function (e) { - e.preventDefault(); - const $this = $(this); - const redirect = $this.data('redirect'); - $this.addClass('loading'); - $.post($this.data('do'), { - _csrf: csrf - }).done((data) => { - if (data.redirect) { - window.location.href = data.redirect; - } else if (redirect) { - window.location.href = redirect; - } else { - window.location.reload(); - } - }); - }); - - $('.update-button > .dropdown').dropdown({ - onChange(_text, _value, $choice) { - const $url = $choice.data('do'); - if ($url) { - $pullUpdateButton.find('.button-text').text($choice.text()); - $pullUpdateButton.data('do', $url); - } - } - }); - - initReactionSelector(); - } - - // Quick start and repository home - $('#repo-clone-ssh').on('click', function () { - $('.clone-url').text($(this).data('link')); - $('#repo-clone-url').val($(this).data('link')); - $(this).addClass('primary'); - $('#repo-clone-https').removeClass('primary'); - localStorage.setItem('repo-clone-protocol', 'ssh'); - }); - $('#repo-clone-https').on('click', function () { - $('.clone-url').text($(this).data('link')); - $('#repo-clone-url').val($(this).data('link')); - $(this).addClass('primary'); - if ($('#repo-clone-ssh').length > 0) { - $('#repo-clone-ssh').removeClass('primary'); - localStorage.setItem('repo-clone-protocol', 'https'); - } - }); - $('#repo-clone-url').on('click', function () { - $(this).select(); - }); - - // Compare or pull request - const $repoDiff = $('.repository.diff'); - if ($repoDiff.length) { - initBranchOrTagDropdown('.choose.branch .dropdown'); - initFilterSearchDropdown('.choose.branch .dropdown'); - } - - // Pull request - const $repoComparePull = $('.repository.compare.pull'); - if ($repoComparePull.length > 0) { - // show pull request form - $repoComparePull.find('button.show-form').on('click', function (e) { - e.preventDefault(); - $repoComparePull.find('.pullrequest-form').show(); - autoSimpleMDE.codemirror.refresh(); - $(this).parent().hide(); - }); - } - - // Branches - if ($('.repository.settings.branches').length > 0) { - initFilterSearchDropdown('.protected-branches .dropdown'); - $('.enable-protection, .enable-whitelist, .enable-statuscheck').on('change', function () { - if (this.checked) { - $($(this).data('target')).removeClass('disabled'); - } else { - $($(this).data('target')).addClass('disabled'); - } - }); - $('.disable-whitelist').on('change', function () { - if (this.checked) { - $($(this).data('target')).addClass('disabled'); - } - }); - } - - // Language stats - if ($('.language-stats').length > 0) { - $('.language-stats').on('click', (e) => { - e.preventDefault(); - $('.language-stats-details, .repository-menu').slideToggle(); - }); - } -} - -function initPullRequestMergeInstruction() { - $('.show-instruction').on('click', () => { - $('.instruct-content').toggle(); - }); -} - -function initRelease() { - $(document).on('click', '.remove-rel-attach', function() { - const uuid = $(this).data('uuid'); - const id = $(this).data('id'); - $(`input[name='attachment-del-${uuid}']`).attr('value', true); - $(`#attachment-${id}`).hide(); - }); -} - -function initPullRequestReview() { - if (window.location.hash && window.location.hash.startsWith('#issuecomment-')) { - const commentDiv = $(window.location.hash); - if (commentDiv) { - // get the name of the parent id - const groupID = commentDiv.closest('div[id^="code-comments-"]').attr('id'); - if (groupID && groupID.startsWith('code-comments-')) { - const id = groupID.substr(14); - $(`#show-outdated-${id}`).addClass('hide'); - $(`#code-comments-${id}`).removeClass('hide'); - $(`#code-preview-${id}`).removeClass('hide'); - $(`#hide-outdated-${id}`).removeClass('hide'); - commentDiv[0].scrollIntoView(); - } - } - } - - $(document).on('click', '.show-outdated', function (e) { - e.preventDefault(); - const id = $(this).data('comment'); - $(this).addClass('hide'); - $(`#code-comments-${id}`).removeClass('hide'); - $(`#code-preview-${id}`).removeClass('hide'); - $(`#hide-outdated-${id}`).removeClass('hide'); - }); - - $(document).on('click', '.hide-outdated', function (e) { - e.preventDefault(); - const id = $(this).data('comment'); - $(this).addClass('hide'); - $(`#code-comments-${id}`).addClass('hide'); - $(`#code-preview-${id}`).addClass('hide'); - $(`#show-outdated-${id}`).removeClass('hide'); - }); - - $(document).on('click', 'button.comment-form-reply', function (e) { - e.preventDefault(); - $(this).hide(); - const form = $(this).closest('.comment-code-cloud').find('.comment-form'); - form.removeClass('hide'); - const $textarea = form.find('textarea'); - let $simplemde; - if ($textarea.data('simplemde')) { - $simplemde = $textarea.data('simplemde'); - } else { - attachTribute($textarea.get(), {mentions: true, emoji: true}); - $simplemde = setCommentSimpleMDE($textarea); - $textarea.data('simplemde', $simplemde); - } - $textarea.focus(); - $simplemde.codemirror.focus(); - assignMenuAttributes(form.find('.menu')); - }); - - const $reviewBox = $('.review-box'); - if ($reviewBox.length === 1) { - setCommentSimpleMDE($reviewBox.find('textarea')); - initImagePaste($reviewBox); - } - - // The following part is only for diff views - if ($('.repository.pull.diff').length === 0) { - return; - } - - $('.btn-review').on('click', function (e) { - e.preventDefault(); - $(this).closest('.dropdown').find('.menu').toggle('visible'); - }).closest('.dropdown').find('.close').on('click', function (e) { - e.preventDefault(); - $(this).closest('.menu').toggle('visible'); - }); - - $('a.add-code-comment').on('click', async function (e) { - if ($(e.target).hasClass('btn-add-single')) return; // https://github.com/go-gitea/gitea/issues/4745 - e.preventDefault(); - - const isSplit = $(this).closest('.code-diff').hasClass('code-diff-split'); - const side = $(this).data('side'); - const idx = $(this).data('idx'); - const path = $(this).data('path'); - const tr = $(this).closest('tr'); - const lineType = tr.data('line-type'); - - let ntr = tr.next(); - if (!ntr.hasClass('add-comment')) { - ntr = $(` - - ${isSplit ? ` - - - - - - - ` : ` - - - `} - `); - tr.after(ntr); - } - - const td = ntr.find(`.add-comment-${side}`); - let commentCloud = td.find('.comment-code-cloud'); - if (commentCloud.length === 0 && !ntr.find('button[name="is_review"]').length) { - const data = await $.get($(this).data('new-comment-url')); - td.html(data); - commentCloud = td.find('.comment-code-cloud'); - assignMenuAttributes(commentCloud.find('.menu')); - td.find("input[name='line']").val(idx); - td.find("input[name='side']").val(side === 'left' ? 'previous' : 'proposed'); - td.find("input[name='path']").val(path); - const $textarea = commentCloud.find('textarea'); - attachTribute($textarea.get(), {mentions: true, emoji: true}); - const $simplemde = setCommentSimpleMDE($textarea); - $textarea.focus(); - $simplemde.codemirror.focus(); - } - }); -} - -function assignMenuAttributes(menu) { - const id = Math.floor(Math.random() * Math.floor(1000000)); - menu.attr('data-write', menu.attr('data-write') + id); - menu.attr('data-preview', menu.attr('data-preview') + id); - menu.find('.item').each(function () { - const tab = $(this).attr('data-tab') + id; - $(this).attr('data-tab', tab); - }); - menu.parent().find("*[data-tab='write']").attr('data-tab', `write${id}`); - menu.parent().find("*[data-tab='preview']").attr('data-tab', `preview${id}`); - initCommentPreviewTab(menu.parent('.form')); - return id; -} - -function initRepositoryCollaboration() { - // Change collaborator access mode - $('.access-mode.menu .item').on('click', function () { - const $menu = $(this).parent(); - $.post($menu.data('url'), { - _csrf: csrf, - uid: $menu.data('uid'), - mode: $(this).data('value') - }); - }); -} - -function initTeamSettings() { - // Change team access mode - $('.organization.new.team input[name=permission]').on('change', () => { - const val = $('input[name=permission]:checked', '.organization.new.team').val(); - if (val === 'admin') { - $('.organization.new.team .team-units').hide(); - } else { - $('.organization.new.team .team-units').show(); - } - }); -} - -function initWikiForm() { - const $editArea = $('.repository.wiki textarea#edit_area'); - let sideBySideChanges = 0; - let sideBySideTimeout = null; - let hasSimpleMDE = true; - if ($editArea.length > 0) { - const simplemde = new SimpleMDE({ - autoDownloadFontAwesome: false, - element: $editArea[0], - forceSync: true, - previewRender(plainText, preview) { // Async method - // FIXME: still send render request when return back to edit mode - const render = function () { - sideBySideChanges = 0; - if (sideBySideTimeout !== null) { - clearTimeout(sideBySideTimeout); - sideBySideTimeout = null; - } - $.post($editArea.data('url'), { - _csrf: csrf, - mode: 'gfm', - context: $editArea.data('context'), - text: plainText, - wiki: true - }, (data) => { - preview.innerHTML = `
${data}
`; - initMarkupContent(); - }); - }; - - setTimeout(() => { - if (!simplemde.isSideBySideActive()) { - render(); - } else { - // delay preview by keystroke counting - sideBySideChanges++; - if (sideBySideChanges > 10) { - render(); - } - // or delay preview by timeout - if (sideBySideTimeout !== null) { - clearTimeout(sideBySideTimeout); - sideBySideTimeout = null; - } - sideBySideTimeout = setTimeout(render, 600); - } - }, 0); - if (!simplemde.isSideBySideActive()) { - return 'Loading...'; - } - return preview.innerHTML; - }, - renderingConfig: { - singleLineBreaks: false - }, - indentWithTabs: false, - tabSize: 4, - spellChecker: false, - toolbar: ['bold', 'italic', 'strikethrough', '|', - 'heading-1', 'heading-2', 'heading-3', 'heading-bigger', 'heading-smaller', '|', - { - name: 'code-inline', - action(e) { - const cm = e.codemirror; - const selection = cm.getSelection(); - cm.replaceSelection(`\`${selection}\``); - if (!selection) { - const cursorPos = cm.getCursor(); - cm.setCursor(cursorPos.line, cursorPos.ch - 1); - } - cm.focus(); - }, - className: 'fa fa-angle-right', - title: 'Add Inline Code', - }, 'code', 'quote', '|', { - name: 'checkbox-empty', - action(e) { - const cm = e.codemirror; - cm.replaceSelection(`\n- [ ] ${cm.getSelection()}`); - cm.focus(); - }, - className: 'fa fa-square-o', - title: 'Add Checkbox (empty)', - }, - { - name: 'checkbox-checked', - action(e) { - const cm = e.codemirror; - cm.replaceSelection(`\n- [x] ${cm.getSelection()}`); - cm.focus(); - }, - className: 'fa fa-check-square-o', - title: 'Add Checkbox (checked)', - }, '|', - 'unordered-list', 'ordered-list', '|', - 'link', 'image', 'table', 'horizontal-rule', '|', - 'clean-block', 'preview', 'fullscreen', 'side-by-side', '|', - { - name: 'revert-to-textarea', - action(e) { - e.toTextArea(); - hasSimpleMDE = false; - const $form = $('.repository.wiki.new .ui.form'); - const $root = $form.find('.field.content'); - const loading = $root.data('loading'); - $root.append(`
${loading}
`); - initCommentPreviewTab($form); - }, - className: 'fa fa-file', - title: 'Revert to simple textarea', - }, - ] - }); - $(simplemde.codemirror.getInputField()).addClass('js-quick-submit'); - - setTimeout(() => { - const $bEdit = $('.repository.wiki.new .previewtabs a[data-tab="write"]'); - const $bPrev = $('.repository.wiki.new .previewtabs a[data-tab="preview"]'); - const $toolbar = $('.editor-toolbar'); - const $bPreview = $('.editor-toolbar button.preview'); - const $bSideBySide = $('.editor-toolbar a.fa-columns'); - $bEdit.on('click', (e) => { - if (!hasSimpleMDE) { - return false; - } - e.stopImmediatePropagation(); - if ($toolbar.hasClass('disabled-for-preview')) { - $bPreview.trigger('click'); - } - - return false; - }); - $bPrev.on('click', (e) => { - if (!hasSimpleMDE) { - return false; - } - e.stopImmediatePropagation(); - if (!$toolbar.hasClass('disabled-for-preview')) { - $bPreview.trigger('click'); - } - return false; - }); - $bPreview.on('click', () => { - setTimeout(() => { - if ($toolbar.hasClass('disabled-for-preview')) { - if ($bEdit.hasClass('active')) { - $bEdit.removeClass('active'); - } - if (!$bPrev.hasClass('active')) { - $bPrev.addClass('active'); - } - } else { - if (!$bEdit.hasClass('active')) { - $bEdit.addClass('active'); - } - if ($bPrev.hasClass('active')) { - $bPrev.removeClass('active'); - } - } - }, 0); - - return false; - }); - $bSideBySide.on('click', () => { - sideBySideChanges = 10; - }); - }, 0); - } -} - -// Adding function to get the cursor position in a text field to jQuery object. -$.fn.getCursorPosition = function () { - const el = $(this).get(0); - let pos = 0; - if ('selectionStart' in el) { - pos = el.selectionStart; - } else if ('selection' in document) { - el.focus(); - const Sel = document.selection.createRange(); - const SelLength = document.selection.createRange().text.length; - Sel.moveStart('character', -el.value.length); - pos = Sel.text.length - SelLength; - } - return pos; -}; - -function setCommentSimpleMDE($editArea) { - if ($editArea.length === 0) { - return null; - } - - const simplemde = new SimpleMDE({ - autoDownloadFontAwesome: false, - element: $editArea[0], - forceSync: true, - renderingConfig: { - singleLineBreaks: false - }, - indentWithTabs: false, - tabSize: 4, - spellChecker: false, - toolbar: ['bold', 'italic', 'strikethrough', '|', - 'heading-1', 'heading-2', 'heading-3', 'heading-bigger', 'heading-smaller', '|', - 'code', 'quote', '|', { - name: 'checkbox-empty', - action(e) { - const cm = e.codemirror; - cm.replaceSelection(`\n- [ ] ${cm.getSelection()}`); - cm.focus(); - }, - className: 'fa fa-square-o', - title: 'Add Checkbox (empty)', - }, - { - name: 'checkbox-checked', - action(e) { - const cm = e.codemirror; - cm.replaceSelection(`\n- [x] ${cm.getSelection()}`); - cm.focus(); - }, - className: 'fa fa-check-square-o', - title: 'Add Checkbox (checked)', - }, '|', - 'unordered-list', 'ordered-list', '|', - 'link', 'image', 'table', 'horizontal-rule', '|', - 'clean-block', '|', - { - name: 'revert-to-textarea', - action(e) { - e.toTextArea(); - }, - className: 'fa fa-file', - title: 'Revert to simple textarea', - }, - ] - }); - $(simplemde.codemirror.getInputField()).addClass('js-quick-submit'); - simplemde.codemirror.setOption('extraKeys', { - Enter: () => { - const tributeContainer = document.querySelector('.tribute-container'); - if (!tributeContainer || tributeContainer.style.display === 'none') { - return CodeMirror.Pass; - } - }, - Backspace: (cm) => { - if (cm.getInputField().trigger) { - cm.getInputField().trigger('input'); - } - cm.execCommand('delCharBefore'); - } - }); - attachTribute(simplemde.codemirror.getInputField(), {mentions: true, emoji: true}); - $editArea.data('simplemde', simplemde); - $(simplemde.codemirror.getInputField()).data('simplemde', simplemde); - return simplemde; -} - -async function initEditor() { - $('.js-quick-pull-choice-option').on('change', function () { - if ($(this).val() === 'commit-to-new-branch') { - $('.quick-pull-branch-name').show(); - $('.quick-pull-branch-name input').prop('required', true); - } else { - $('.quick-pull-branch-name').hide(); - $('.quick-pull-branch-name input').prop('required', false); - } - $('#commit-button').text($(this).attr('button_text')); - }); - - const $editFilename = $('#file-name'); - $editFilename.on('keyup', function (e) { - const $section = $('.breadcrumb span.section'); - const $divider = $('.breadcrumb div.divider'); - let value; - let parts; - - if (e.keyCode === 8 && $(this).getCursorPosition() === 0 && $section.length > 0) { - value = $section.last().find('a').text(); - $(this).val(value + $(this).val()); - $(this)[0].setSelectionRange(value.length, value.length); - $section.last().remove(); - $divider.last().remove(); - } - if (e.keyCode === 191) { - parts = $(this).val().split('/'); - for (let i = 0; i < parts.length; ++i) { - value = parts[i]; - if (i < parts.length - 1) { - if (value.length) { - $(`${value}`).insertBefore($(this)); - $('
/
').insertBefore($(this)); - } - } else { - $(this).val(value); - } - $(this)[0].setSelectionRange(0, 0); - } - } - parts = []; - $('.breadcrumb span.section').each(function () { - const element = $(this); - if (element.find('a').length) { - parts.push(element.find('a').text()); - } else { - parts.push(element.text()); - } - }); - if ($(this).val()) parts.push($(this).val()); - $('#tree_path').val(parts.join('/')); - }).trigger('keyup'); - - const $editArea = $('.repository.editor textarea#edit_area'); - if (!$editArea.length) return; - const editor = await createCodeEditor($editArea[0], $editFilename[0], previewFileModes); - - // Using events from https://github.com/codedance/jquery.AreYouSure#advanced-usage - // to enable or disable the commit button - const $commitButton = $('#commit-button'); - const $editForm = $('.ui.edit.form'); - const dirtyFileClass = 'dirty-file'; - - // Disabling the button at the start - if ($('input[name="page_has_posted"]').val() !== 'true') { - $commitButton.prop('disabled', true); - } - - // Registering a custom listener for the file path and the file content - $editForm.areYouSure({ - silent: true, - dirtyClass: dirtyFileClass, - fieldSelector: ':input:not(.commit-form-wrapper :input)', - change() { - const dirty = $(this).hasClass(dirtyFileClass); - $commitButton.prop('disabled', !dirty); - } - }); - - // Update the editor from query params, if available, - // only after the dirtyFileClass initialization - const params = new URLSearchParams(window.location.search); - const value = params.get('value'); - if (value) { - editor.setValue(value); - } - - $commitButton.on('click', (event) => { - // A modal which asks if an empty file should be committed - if ($editArea.val().length === 0) { - $('#edit-empty-content-modal').modal({ - onApprove() { - $('.edit.form').trigger('submit'); - } - }).modal('show'); - event.preventDefault(); - } - }); -} - -function initReleaseEditor() { - const $editor = $('.repository.new.release .content-editor'); - if ($editor.length === 0) { - return false; - } - - const $textarea = $editor.find('textarea'); - attachTribute($textarea.get(), {mentions: false, emoji: true}); - const $files = $editor.parent().find('.files'); - const $simplemde = setCommentSimpleMDE($textarea); - initCommentPreviewTab($editor); - const dropzone = $editor.parent().find('.dropzone')[0]; - initSimpleMDEImagePaste($simplemde, dropzone, $files); -} - -function initOrganization() { - if ($('.organization').length === 0) { - return; - } - - // Options - if ($('.organization.settings.options').length > 0) { - $('#org_name').on('keyup', function () { - const $prompt = $('#org-name-change-prompt'); - const $prompt_redirect = $('#org-name-change-redirect-prompt'); - if ($(this).val().toString().toLowerCase() !== $(this).data('org-name').toString().toLowerCase()) { - $prompt.show(); - $prompt_redirect.show(); - } else { - $prompt.hide(); - $prompt_redirect.hide(); - } - }); - } - - // Labels - if ($('.organization.settings.labels').length > 0) { - initLabelEdit(); - } -} - -function initUserSettings() { - // Options - if ($('.user.settings.profile').length > 0) { - $('#username').on('keyup', function () { - const $prompt = $('#name-change-prompt'); - const $prompt_redirect = $('#name-change-redirect-prompt'); - if ($(this).val().toString().toLowerCase() !== $(this).data('name').toString().toLowerCase()) { - $prompt.show(); - $prompt_redirect.show(); - } else { - $prompt.hide(); - $prompt_redirect.hide(); - } - }); - } -} - -async function initGithook() { - if ($('.edit.githook').length === 0) return; - const filename = document.querySelector('.hook-filename').textContent; - await createMonaco($('#content')[0], filename, {language: 'shell'}); -} - -function initWebhook() { - if ($('.new.webhook').length === 0) { - return; - } - - $('.events.checkbox input').on('change', function () { - if ($(this).is(':checked')) { - $('.events.fields').show(); - } - }); - $('.non-events.checkbox input').on('change', function () { - if ($(this).is(':checked')) { - $('.events.fields').hide(); - } - }); - - const updateContentType = function () { - const visible = $('#http_method').val() === 'POST'; - $('#content_type').parent().parent()[visible ? 'show' : 'hide'](); - }; - updateContentType(); - $('#http_method').on('change', () => { - updateContentType(); - }); - - // Test delivery - $('#test-delivery').on('click', function () { - const $this = $(this); - $this.addClass('loading disabled'); - $.post($this.data('link'), { - _csrf: csrf - }).done( - setTimeout(() => { - window.location.href = $this.data('redirect'); - }, 5000) - ); - }); -} - -function initAdmin() { - if ($('.admin').length === 0) { - return; - } - - // New user - if ($('.admin.new.user').length > 0 || $('.admin.edit.user').length > 0) { - $('#login_type').on('change', function () { - if ($(this).val().substring(0, 1) === '0') { - $('#user_name').removeAttr('disabled'); - $('#login_name').removeAttr('required'); - $('.non-local').hide(); - $('.local').show(); - $('#user_name').focus(); - - if ($(this).data('password') === 'required') { - $('#password').attr('required', 'required'); - } - } else { - if ($('.admin.edit.user').length > 0) { - $('#user_name').attr('disabled', 'disabled'); - } - $('#login_name').attr('required', 'required'); - $('.non-local').show(); - $('.local').hide(); - $('#login_name').focus(); - - $('#password').removeAttr('required'); - } - }); - } - - function onSecurityProtocolChange() { - if ($('#security_protocol').val() > 0) { - $('.has-tls').show(); - } else { - $('.has-tls').hide(); - } - } - - function onUsePagedSearchChange() { - if ($('#use_paged_search').prop('checked')) { - $('.search-page-size').show() - .find('input').attr('required', 'required'); - } else { - $('.search-page-size').hide() - .find('input').removeAttr('required'); - } - } - - function onOAuth2Change(applyDefaultValues) { - $('.open_id_connect_auto_discovery_url, .oauth2_use_custom_url').hide(); - $('.open_id_connect_auto_discovery_url input[required]').removeAttr('required'); - - const provider = $('#oauth2_provider').val(); - switch (provider) { - case 'openidConnect': - $('.open_id_connect_auto_discovery_url input').attr('required', 'required'); - $('.open_id_connect_auto_discovery_url').show(); - break; - default: - if ($(`#${provider}_customURLSettings`).data('required')) { - $('#oauth2_use_custom_url').attr('checked', 'checked'); - } - if ($(`#${provider}_customURLSettings`).data('available')) { - $('.oauth2_use_custom_url').show(); - } - } - onOAuth2UseCustomURLChange(applyDefaultValues); - } - - function onOAuth2UseCustomURLChange(applyDefaultValues) { - const provider = $('#oauth2_provider').val(); - $('.oauth2_use_custom_url_field').hide(); - $('.oauth2_use_custom_url_field input[required]').removeAttr('required'); - - if ($('#oauth2_use_custom_url').is(':checked')) { - for (const custom of ['token_url', 'auth_url', 'profile_url', 'email_url', 'tenant']) { - if (applyDefaultValues) { - $(`#oauth2_${custom}`).val($(`#${provider}_${custom}`).val()); - } - if ($(`#${provider}_${custom}`).data('available')) { - $(`.oauth2_${custom} input`).attr('required', 'required'); - $(`.oauth2_${custom}`).show(); - } - } - } - } - - function onVerifyGroupMembershipChange() { - if ($('#groups_enabled').is(':checked')) { - $('#groups_enabled_change').show(); - } else { - $('#groups_enabled_change').hide(); - } - } - - // New authentication - if ($('.admin.new.authentication').length > 0) { - $('#auth_type').on('change', function () { - $('.ldap, .dldap, .smtp, .pam, .oauth2, .has-tls, .search-page-size, .sspi').hide(); - - $('.ldap input[required], .binddnrequired input[required], .dldap input[required], .smtp input[required], .pam input[required], .oauth2 input[required], .has-tls input[required], .sspi input[required]').removeAttr('required'); - $('.binddnrequired').removeClass('required'); - - const authType = $(this).val(); - switch (authType) { - case '2': // LDAP - $('.ldap').show(); - $('.binddnrequired input, .ldap div.required:not(.dldap) input').attr('required', 'required'); - $('.binddnrequired').addClass('required'); - break; - case '3': // SMTP - $('.smtp').show(); - $('.has-tls').show(); - $('.smtp div.required input, .has-tls').attr('required', 'required'); - break; - case '4': // PAM - $('.pam').show(); - $('.pam input').attr('required', 'required'); - break; - case '5': // LDAP - $('.dldap').show(); - $('.dldap div.required:not(.ldap) input').attr('required', 'required'); - break; - case '6': // OAuth2 - $('.oauth2').show(); - $('.oauth2 div.required:not(.oauth2_use_custom_url,.oauth2_use_custom_url_field,.open_id_connect_auto_discovery_url) input').attr('required', 'required'); - onOAuth2Change(true); - break; - case '7': // SSPI - $('.sspi').show(); - $('.sspi div.required input').attr('required', 'required'); - break; - } - if (authType === '2' || authType === '5') { - onSecurityProtocolChange(); - onVerifyGroupMembershipChange(); - } - if (authType === '2') { - onUsePagedSearchChange(); - } - }); - $('#auth_type').trigger('change'); - $('#security_protocol').on('change', onSecurityProtocolChange); - $('#use_paged_search').on('change', onUsePagedSearchChange); - $('#oauth2_provider').on('change', () => onOAuth2Change(true)); - $('#oauth2_use_custom_url').on('change', () => onOAuth2UseCustomURLChange(true)); - $('#groups_enabled').on('change', onVerifyGroupMembershipChange); - } - // Edit authentication - if ($('.admin.edit.authentication').length > 0) { - const authType = $('#auth_type').val(); - if (authType === '2' || authType === '5') { - $('#security_protocol').on('change', onSecurityProtocolChange); - $('#groups_enabled').on('change', onVerifyGroupMembershipChange); - onVerifyGroupMembershipChange(); - if (authType === '2') { - $('#use_paged_search').on('change', onUsePagedSearchChange); - } - } else if (authType === '6') { - $('#oauth2_provider').on('change', () => onOAuth2Change(true)); - $('#oauth2_use_custom_url').on('change', () => onOAuth2UseCustomURLChange(false)); - onOAuth2Change(false); - } - } - - // Notice - if ($('.admin.notice')) { - const $detailModal = $('#detail-modal'); - - // Attach view detail modals - $('.view-detail').on('click', function () { - $detailModal.find('.content pre').text($(this).parents('tr').find('.notice-description').text()); - $detailModal.find('.sub.header').text($(this).parents('tr').find('.notice-created-time').text()); - $detailModal.modal('show'); - return false; - }); - - // Select actions - const $checkboxes = $('.select.table .ui.checkbox'); - $('.select.action').on('click', function () { - switch ($(this).data('action')) { - case 'select-all': - $checkboxes.checkbox('check'); - break; - case 'deselect-all': - $checkboxes.checkbox('uncheck'); - break; - case 'inverse': - $checkboxes.checkbox('toggle'); - break; - } - }); - $('#delete-selection').on('click', function () { - const $this = $(this); - $this.addClass('loading disabled'); - const ids = []; - $checkboxes.each(function () { - if ($(this).checkbox('is checked')) { - ids.push($(this).data('id')); - } - }); - $.post($this.data('link'), { - _csrf: csrf, - ids - }).done(() => { - window.location.href = $this.data('redirect'); - }); - }); - } -} - -function buttonsClickOnEnter() { - $('.ui.button').on('keypress', function (e) { - if (e.keyCode === 13 || e.keyCode === 32) { // enter key or space bar - $(this).trigger('click'); - } - }); -} - -function searchUsers() { - const $searchUserBox = $('#search-user-box'); - $searchUserBox.search({ - minCharacters: 2, - apiSettings: { - url: `${AppSubUrl}/api/v1/users/search?q={query}`, - onResponse(response) { - const items = []; - const searchQueryUppercase = $searchUserBox.find('input').val().toUpperCase(); - $.each(response.data, (_i, item) => { - let title = item.login; - if (item.full_name && item.full_name.length > 0) { - title += ` (${htmlEscape(item.full_name)})`; - } - const resultItem = { - title, - image: item.avatar_url - }; - if (searchQueryUppercase === item.login.toUpperCase()) { - items.unshift(resultItem); - } else { - items.push(resultItem); - } - }); - - return {results: items}; - } - }, - searchFields: ['login', 'full_name'], - showNoResults: false - }); -} - -function searchTeams() { - const $searchTeamBox = $('#search-team-box'); - $searchTeamBox.search({ - minCharacters: 2, - apiSettings: { - url: `${AppSubUrl}/api/v1/orgs/${$searchTeamBox.data('org')}/teams/search?q={query}`, - headers: {'X-Csrf-Token': csrf}, - onResponse(response) { - const items = []; - $.each(response.data, (_i, item) => { - const title = `${item.name} (${item.permission} access)`; - items.push({ - title, - }); - }); - - return {results: items}; - } - }, - searchFields: ['name', 'description'], - showNoResults: false - }); -} - -function searchRepositories() { - const $searchRepoBox = $('#search-repo-box'); - $searchRepoBox.search({ - minCharacters: 2, - apiSettings: { - url: `${AppSubUrl}/api/v1/repos/search?q={query}&uid=${$searchRepoBox.data('uid')}`, - onResponse(response) { - const items = []; - $.each(response.data, (_i, item) => { - items.push({ - title: item.full_name.split('/')[1], - description: item.full_name - }); - }); - - return {results: items}; - } - }, - searchFields: ['full_name'], - showNoResults: false - }); -} - -function initCodeView() { - if ($('.code-view .lines-num').length > 0) { - $(document).on('click', '.lines-num span', function (e) { - const $select = $(this); - let $list; - if ($('div.blame').length) { - $list = $('.code-view td.lines-code.blame-code'); - } else { - $list = $('.code-view td.lines-code'); - } - selectRange($list, $list.filter(`[rel=${$select.attr('id')}]`), (e.shiftKey ? $list.filter('.active').eq(0) : null)); - deSelect(); - - // show code view menu marker (don't show in blame page) - if ($('div.blame').length === 0) { - showLineButton(); - } - }); - - $(window).on('hashchange', () => { - let m = window.location.hash.match(/^#(L\d+)-(L\d+)$/); - let $list; - if ($('div.blame').length) { - $list = $('.code-view td.lines-code.blame-code'); - } else { - $list = $('.code-view td.lines-code'); - } - let $first; - if (m) { - $first = $list.filter(`[rel=${m[1]}]`); - selectRange($list, $first, $list.filter(`[rel=${m[2]}]`)); - - // show code view menu marker (don't show in blame page) - if ($('div.blame').length === 0) { - showLineButton(); - } - - $('html, body').scrollTop($first.offset().top - 200); - return; - } - m = window.location.hash.match(/^#(L|n)(\d+)$/); - if (m) { - $first = $list.filter(`[rel=L${m[2]}]`); - selectRange($list, $first); - - // show code view menu marker (don't show in blame page) - if ($('div.blame').length === 0) { - showLineButton(); - } - - $('html, body').scrollTop($first.offset().top - 200); - } - }).trigger('hashchange'); - } - $(document).on('click', '.fold-file', ({currentTarget}) => { - const box = currentTarget.closest('.file-content'); - const chevron = currentTarget.querySelector('a.chevron'); - const folded = box.dataset.folded !== 'true'; - chevron.innerHTML = svg(`octicon-chevron-${folded ? 'right' : 'down'}`, 18); - box.dataset.folded = String(folded); - }); - $(document).on('click', '.blob-excerpt', async ({currentTarget}) => { - const {url, query, anchor} = currentTarget.dataset; - if (!url) return; - const blob = await $.get(`${url}?${query}&anchor=${anchor}`); - currentTarget.closest('tr').outerHTML = blob; - }); -} - -function initU2FAuth() { - if ($('#wait-for-key').length === 0) { - return; - } - u2fApi.ensureSupport() - .then(() => { - $.getJSON(`${AppSubUrl}/user/u2f/challenge`).done((req) => { - u2fApi.sign(req.appId, req.challenge, req.registeredKeys, 30) - .then(u2fSigned) - .catch((err) => { - if (err === undefined) { - u2fError(1); - return; - } - u2fError(err.metaData.code); - }); - }); - }).catch(() => { - // Fallback in case browser do not support U2F - window.location.href = `${AppSubUrl}/user/two_factor`; - }); -} -function u2fSigned(resp) { - $.ajax({ - url: `${AppSubUrl}/user/u2f/sign`, - type: 'POST', - headers: {'X-Csrf-Token': csrf}, - data: JSON.stringify(resp), - contentType: 'application/json; charset=utf-8', - }).done((res) => { - window.location.replace(res); - }).fail(() => { - u2fError(1); - }); -} - -function u2fRegistered(resp) { - if (checkError(resp)) { - return; - } - $.ajax({ - url: `${AppSubUrl}/user/settings/security/u2f/register`, - type: 'POST', - headers: {'X-Csrf-Token': csrf}, - data: JSON.stringify(resp), - contentType: 'application/json; charset=utf-8', - success() { - reload(); - }, - fail() { - u2fError(1); - } - }); -} - -function checkError(resp) { - if (!('errorCode' in resp)) { - return false; - } - if (resp.errorCode === 0) { - return false; - } - u2fError(resp.errorCode); - return true; -} - -function u2fError(errorType) { - const u2fErrors = { - browser: $('#unsupported-browser'), - 1: $('#u2f-error-1'), - 2: $('#u2f-error-2'), - 3: $('#u2f-error-3'), - 4: $('#u2f-error-4'), - 5: $('.u2f_error_5') - }; - u2fErrors[errorType].removeClass('hide'); - - Object.keys(u2fErrors).forEach((type) => { - if (type !== `${errorType}`) { - u2fErrors[type].addClass('hide'); - } - }); - $('#u2f-error').modal('show'); -} - -function initU2FRegister() { - $('#register-device').modal({allowMultiple: false}); - $('#u2f-error').modal({allowMultiple: false}); - $('#register-security-key').on('click', (e) => { - e.preventDefault(); - u2fApi.ensureSupport() - .then(u2fRegisterRequest) - .catch(() => { - u2fError('browser'); - }); - }); -} - -function u2fRegisterRequest() { - $.post(`${AppSubUrl}/user/settings/security/u2f/request_register`, { - _csrf: csrf, - name: $('#nickname').val() - }).done((req) => { - $('#nickname').closest('div.field').removeClass('error'); - $('#register-device').modal('show'); - if (req.registeredKeys === null) { - req.registeredKeys = []; - } - u2fApi.register(req.appId, req.registerRequests, req.registeredKeys, 30) - .then(u2fRegistered) - .catch((reason) => { - if (reason === undefined) { - u2fError(1); - return; - } - u2fError(reason.metaData.code); - }); - }).fail((xhr) => { - if (xhr.status === 409) { - $('#nickname').closest('div.field').addClass('error'); - } - }); -} - -function initWipTitle() { - $('.title_wip_desc > a').on('click', (e) => { - e.preventDefault(); - - const $issueTitle = $('#issue_title'); - $issueTitle.focus(); - const value = $issueTitle.val().trim().toUpperCase(); - - const wipPrefixes = $('.title_wip_desc').data('wip-prefixes'); - for (const prefix of wipPrefixes) { - if (value.startsWith(prefix.toUpperCase())) { - return; - } - } - - $issueTitle.val(`${wipPrefixes[0]} ${$issueTitle.val()}`); - }); -} - -function initTemplateSearch() { - const $repoTemplate = $('#repo_template'); - const checkTemplate = function () { - const $templateUnits = $('#template_units'); - const $nonTemplate = $('#non_template'); - if ($repoTemplate.val() !== '' && $repoTemplate.val() !== '0') { - $templateUnits.show(); - $nonTemplate.hide(); - } else { - $templateUnits.hide(); - $nonTemplate.show(); - } - }; - $repoTemplate.on('change', checkTemplate); - checkTemplate(); - - const changeOwner = function () { - $('#repo_template_search') - .dropdown({ - apiSettings: { - url: `${AppSubUrl}/api/v1/repos/search?q={query}&template=true&priority_owner_id=${$('#uid').val()}`, - onResponse(response) { - const filteredResponse = {success: true, results: []}; - filteredResponse.results.push({ - name: '', - value: '' - }); - // Parse the response from the api to work with our dropdown - $.each(response.data, (_r, repo) => { - filteredResponse.results.push({ - name: htmlEscape(repo.full_name), - value: repo.id - }); - }); - return filteredResponse; - }, - cache: false, - }, - - fullTextSearch: true - }); - }; - $('#uid').on('change', changeOwner); - changeOwner(); -} - -function initIssueReferenceRepositorySearch() { - $('.issue_reference_repository_search') - .dropdown({ - apiSettings: { - url: `${AppSubUrl}/api/v1/repos/search?q={query}&limit=20`, - onResponse(response) { - const filteredResponse = {success: true, results: []}; - $.each(response.data, (_r, repo) => { - filteredResponse.results.push({ - name: htmlEscape(repo.full_name), - value: repo.full_name - }); - }); - return filteredResponse; - }, - cache: false, - }, - onChange(_value, _text, $choice) { - const $form = $choice.closest('form'); - $form.attr('action', `${AppSubUrl}/${_text}/issues/new`); - }, - fullTextSearch: true - }); -} - -function initFileViewToggle() { - $('.file-view-toggle').on('click', function() { - const $this = $(this); - $this.parent().children().removeClass('active'); - $this.addClass('active'); - - const $target = $($this.data('toggle-selector')); - $target.parent().children().addClass('hide'); - $target.removeClass('hide'); - }); -} - -function initLinkAccountView() { - const $lnkUserPage = $('.page-content.user.link-account'); - if ($lnkUserPage.length === 0) { - return false; - } - - const $signinTab = $lnkUserPage.find('.item[data-tab="auth-link-signin-tab"]'); - const $signUpTab = $lnkUserPage.find('.item[data-tab="auth-link-signup-tab"]'); - const $signInView = $lnkUserPage.find('.tab[data-tab="auth-link-signin-tab"]'); - const $signUpView = $lnkUserPage.find('.tab[data-tab="auth-link-signup-tab"]'); - - $signUpTab.on('click', () => { - $signinTab.removeClass('active'); - $signInView.removeClass('active'); - $signUpTab.addClass('active'); - $signUpView.addClass('active'); - return false; - }); - - $signinTab.on('click', () => { - $signUpTab.removeClass('active'); - $signUpView.removeClass('active'); - $signinTab.addClass('active'); - $signInView.addClass('active'); - }); -} +initVueEnv(); $(document).ready(async () => { - // Show exact time - $('.time-since').each(function () { - $(this) - .addClass('poping up') - .attr('data-content', $(this).attr('title')) - .attr('data-variation', 'inverted tiny') - .attr('title', ''); - }); - - // Undo Safari emoji glitch fix at high enough zoom levels - if (navigator.userAgent.match('Safari')) { - $(window).resize(() => { - const px = mqBinarySearch('width', 0, 4096, 1, 'px'); - const em = mqBinarySearch('width', 0, 1024, 0.01, 'em'); - if (em * 16 * 1.25 - px <= -1) { - $('body').addClass('safari-above125'); - } else { - $('body').removeClass('safari-above125'); - } - }); - } - - // Semantic UI modules. - $('.dropdown:not(.custom)').dropdown({ - fullTextSearch: 'exact' - }); - $('.jump.dropdown').dropdown({ - action: 'hide', - onShow() { - $('.poping.up').popup('hide'); - }, - fullTextSearch: 'exact' - }); - $('.slide.up.dropdown').dropdown({ - transition: 'slide up', - fullTextSearch: 'exact' - }); - $('.upward.dropdown').dropdown({ - direction: 'upward', - fullTextSearch: 'exact' - }); - $('.ui.checkbox').checkbox(); - $('.ui.progress').progress({ - showActivity: false - }); - $('.poping.up').popup(); - $('.top.menu .poping.up').popup({ - onShow() { - if ($('.top.menu .menu.transition').hasClass('visible')) { - return false; - } - } - }); - $('.tabular.menu .item').tab(); - $('.tabable.menu .item').tab(); - - $('.toggle.button').on('click', function () { - $($(this).data('target')).slideToggle(100); - }); - - // make table element clickable like a link - $('tr[data-href]').on('click', function () { - window.location = $(this).data('href'); - }); - - // make table element clickable like a link - $('td[data-href]').click(function () { - window.location = $(this).data('href'); - }); - - // link-account tab handle - initLinkAccountView(); - - // Dropzone - for (const el of document.querySelectorAll('.dropzone')) { - const $dropzone = $(el); - await createDropzone(el, { - url: $dropzone.data('upload-url'), - headers: {'X-Csrf-Token': csrf}, - maxFiles: $dropzone.data('max-file'), - maxFilesize: $dropzone.data('max-size'), - acceptedFiles: (['*/*', ''].includes($dropzone.data('accepts'))) ? null : $dropzone.data('accepts'), - addRemoveLinks: true, - dictDefaultMessage: $dropzone.data('default-message'), - dictInvalidFileType: $dropzone.data('invalid-input-type'), - dictFileTooBig: $dropzone.data('file-too-big'), - dictRemoveFile: $dropzone.data('remove-file'), - timeout: 0, - thumbnailMethod: 'contain', - thumbnailWidth: 480, - thumbnailHeight: 480, - init() { - this.on('success', (_file, data) => { - const input = $(``).val(data.uuid); - $dropzone.find('.files').append(input); - }); - this.on('removedfile', (file) => { - $(`#${file.uuid}`).remove(); - if ($dropzone.data('remove-url')) { - $.post($dropzone.data('remove-url'), { - file: file.uuid, - _csrf: csrf - }); - } - }); - }, - }); - } - - // Helpers. - $('.delete-button').on('click', showDeletePopup); - $('.add-all-button').on('click', showAddAllPopup); - $('.link-action').on('click', linkAction); - $('.language-menu a[lang]').on('click', linkLanguageAction); - $('.link-email-action').on('click', linkEmailAction); + initGlobalCommon(); + initGlobalDropzone(); + initGlobalLinkActions(); + initGlobalButtons(); + initRepoBranchButton(); - $('.delete-branch-button').on('click', showDeletePopup); + initCommonIssue(); - $('.undo-button').on('click', function () { - const $this = $(this); - $.post($this.data('url'), { - _csrf: csrf, - id: $this.data('id') - }).done((data) => { - window.location.href = data.redirect; - }); - }); - $('.show-panel.button').on('click', function () { - $($(this).data('panel')).show(); - }); - $('.hide-panel.button').on('click', function () { - $($(this).data('panel')).hide(); - }); - $('.show-create-branch-modal.button').on('click', function () { - $('#create-branch-form')[0].action = $('#create-branch-form').data('base-action') + $(this).data('branch-from'); - $('#modal-create-branch-from-span').text($(this).data('branch-from')); - $($(this).data('modal')).modal('show'); - }); - $('.show-modal.button').on('click', function () { - $($(this).data('modal')).modal('show'); - const colorPickers = $($(this).data('modal')).find('.color-picker'); - if (colorPickers.length > 0) { - initColorPicker(); - } - }); - $('.delete-post.button').on('click', function () { - const $this = $(this); - $.post($this.data('request-url'), { - _csrf: csrf - }).done(() => { - window.location.href = $this.data('done-url'); - }); - }); - - $('.issue-checkbox').on('click', () => { - const numChecked = $('.issue-checkbox').children('input:checked').length; - if (numChecked > 0) { - $('#issue-filters').addClass('hide'); - $('#issue-actions').removeClass('hide'); - } else { - $('#issue-filters').removeClass('hide'); - $('#issue-actions').addClass('hide'); - } - }); - - $('.issue-action').on('click', function () { - let {action, elementId, url} = this.dataset; - const issueIDs = $('.issue-checkbox').children('input:checked').map((_, el) => { - return el.dataset.issueId; - }).get().join(','); - if (elementId === '0' && url.substr(-9) === '/assignee') { - elementId = ''; - action = 'clear'; - } - updateIssuesMeta(url, action, issueIDs, elementId, '').then(() => { - // NOTICE: This reset of checkbox state targets Firefox caching behaviour, as the checkboxes stay checked after reload - if (action === 'close' || action === 'open') { - // uncheck all checkboxes - $('.issue-checkbox input[type="checkbox"]').each((_, e) => { e.checked = false }); - } - reload(); - }); - }); - - // NOTICE: This event trigger targets Firefox caching behaviour, as the checkboxes stay checked after reload - // trigger ckecked event, if checkboxes are checked on load - $('.issue-checkbox input[type="checkbox"]:checked').first().each((_, e) => { - e.checked = false; - $(e).trigger('click'); - }); - - $(document).on('click', '.resolve-conversation', async function (e) { - e.preventDefault(); - const comment_id = $(this).data('comment-id'); - const origin = $(this).data('origin'); - const action = $(this).data('action'); - const url = $(this).data('update-url'); - - const data = await $.post(url, {_csrf: csrf, origin, action, comment_id}); - - if ($(this).closest('.conversation-holder').length) { - const conversation = $(data); - $(this).closest('.conversation-holder').replaceWith(conversation); - conversation.find('.dropdown').dropdown(); - initReactionSelector(conversation); - initClipboard(); - } else { - reload(); - } - }); - - buttonsClickOnEnter(); - searchUsers(); - searchTeams(); - searchRepositories(); + initSearchUserBox(); + initRepoSettingSearchTeamBox(); + initOrgTeamSearchRepoBox(); + initGlobalButtonClickOnEnter(); initMarkupAnchors(); initCommentContent(); - initCommentForm(); + initRepoCommentForm(); initInstall(); - initArchiveLinks(); + initRepoArchiveLinks(); initRepository(); initMigration(); - initWikiForm(); - initEditForm(); - initEditor(); - initOrganization(); - initWebhook(); - initAdmin(); - initCodeView(); + initRepoWikiForm(); + initRepoEditor(); + initCommonOrganization(); + initWebHookEditor(); + initAdminCommon(); + initRepoCodeView(); initRepoActivityTopAuthorsChart(); initDashboardRepoList(); - initTeamSettings(); - initCtrlEnterSubmit(); - initNavbarContentToggle(); - initTopicbar(); - initU2FAuth(); - initU2FRegister(); - initIssueList(); - initIssueTimetracking(); - initIssueDue(); - initWipTitle(); - initPullRequestReview(); - initRepoStatusChecker(); - initTemplateSearch(); - initIssueReferenceRepositorySearch(); + initOrgTeamSettings(); + initGlobalEnterQuickSubmit(); + initHeadNavbarContentToggle(); + initFootLanguageMenu(); + initRepoTopicBar(); + initUserAuthU2fAuth(); + initUserAuthU2fRegister(); + initRepoIssueList(); + initRepoIssueTimeTracking(); + initRepoIssueDue(); + initRepoIssueWipTitle(); + initRepoPullRequestReview(); + initRepoMigrationStatusChecker(); + initRepoTemplateSearch(); + initRepoIssueReferenceRepositorySearch(); initContextPopups(); initTableSort(); initNotificationsTable(); initLastCommitLoader(); - initPullRequestMergeInstruction(); - initFileViewToggle(); - initReleaseEditor(); - initRelease(); + initRepoPullRequestMergeInstruction(); + initRepoDiffFileViewToggle(); + initRepoReleaseEditor(); + initRepoRelease(); initIssueContentHistory(); initAdminUserListSearchForm(); - - const routes = { - 'div.user.settings': initUserSettings, - 'div.repository.settings.collaboration': initRepositoryCollaboration - }; - - for (const [selector, fn] of Object.entries(routes)) { - if ($(selector).length > 0) { - fn(); - break; - } - } + initGlobalCopyToClipboardListener(); + initUserAuthOauth2(); + initRepoDiffReviewButton(); + initRepoCommitButton(); + initAdminEmails(); + initGlobalEnterQuickSubmit(); + initSshKeyFormParser(); + initGlobalFormDirtyLeaveConfirm(); + initUserSettings(); + initRepoSettingsCollaboration(); + initUserAuthLinkAccountView(); + initRepoDiffConversationForm(); // parallel init of async loaded features await Promise.all([ attachTribute(document.querySelectorAll('#content, .emoji-input')), initGitGraph(), - initClipboard(), initHeatmap(), initProject(), initServiceWorker(), initNotificationCount(), initStopwatch(), initMarkupContent(), - initGithook(), + initRepoSettingGitHook(), initImageDiff(), ]); }); - -function changeHash(hash) { - if (window.history.pushState) { - window.history.pushState(null, null, hash); - } else { - window.location.hash = hash; - } -} - -function deSelect() { - if (window.getSelection) { - window.getSelection().removeAllRanges(); - } else { - document.selection.empty(); - } -} - -function selectRange($list, $select, $from) { - $list.removeClass('active'); - - // add hashchange to permalink - const $issue = $('a.ref-in-new-issue'); - const $copyPermalink = $('a.copy-line-permalink'); - - if ($issue.length === 0 || $copyPermalink.length === 0) { - return; - } - - const updateIssueHref = function(anchor) { - let href = $issue.attr('href'); - href = `${href.replace(/%23L\d+$|%23L\d+-L\d+$/, '')}%23${anchor}`; - $issue.attr('href', href); - }; - - const updateCopyPermalinkHref = function(anchor) { - let link = $copyPermalink.attr('data-clipboard-text'); - link = `${link.replace(/#L\d+$|#L\d+-L\d+$/, '')}#${anchor}`; - $copyPermalink.attr('data-clipboard-text', link); - }; - - if ($from) { - let a = parseInt($select.attr('rel').substr(1)); - let b = parseInt($from.attr('rel').substr(1)); - let c; - if (a !== b) { - if (a > b) { - c = a; - a = b; - b = c; - } - const classes = []; - for (let i = a; i <= b; i++) { - classes.push(`[rel=L${i}]`); - } - $list.filter(classes.join(',')).addClass('active'); - changeHash(`#L${a}-L${b}`); - - updateIssueHref(`L${a}-L${b}`); - updateCopyPermalinkHref(`L${a}-L${b}`); - return; - } - } - $select.addClass('active'); - changeHash(`#${$select.attr('rel')}`); - - updateIssueHref($select.attr('rel')); - updateCopyPermalinkHref($select.attr('rel')); -} - -$(() => { - // Warn users that try to leave a page after entering data into a form. - // Except on sign-in pages, and for forms marked as 'ignore-dirty'. - if ($('.user.signin').length === 0) { - $('form:not(.ignore-dirty)').areYouSure(); - } - - // Parse SSH Key - $('#ssh-key-content').on('change paste keyup', function () { - const arrays = $(this).val().split(' '); - const $title = $('#ssh-key-title'); - if ($title.val() === '' && arrays.length === 3 && arrays[2] !== '') { - $title.val(arrays[2]); - } - }); -}); - -function showDeletePopup() { - const $this = $(this); - const dataArray = $this.data(); - let filter = ''; - if ($this.data('modal-id')) { - filter += `#${$this.data('modal-id')}`; - } - - const dialog = $(`.delete.modal${filter}`); - dialog.find('.name').text($this.data('name')); - for (const [key, value] of Object.entries(dataArray)) { - if (key && key.startsWith('data')) { - dialog.find(`.${key}`).text(value); - } - } - - dialog.modal({ - closable: false, - onApprove() { - if ($this.data('type') === 'form') { - $($this.data('form')).trigger('submit'); - return; - } - - const postData = { - _csrf: csrf, - }; - for (const [key, value] of Object.entries(dataArray)) { - if (key && key.startsWith('data')) { - postData[key.substr(4)] = value; - } - if (key === 'id') { - postData['id'] = value; - } - } - - $.post($this.data('url'), postData).done((data) => { - window.location.href = data.redirect; - }); - } - }).modal('show'); - return false; -} - -function showAddAllPopup() { - const $this = $(this); - let filter = ''; - if ($this.attr('id')) { - filter += `#${$this.attr('id')}`; - } - - const dialog = $(`.addall.modal${filter}`); - dialog.find('.name').text($this.data('name')); - - dialog.modal({ - closable: false, - onApprove() { - if ($this.data('type') === 'form') { - $($this.data('form')).trigger('submit'); - return; - } - - $.post($this.data('url'), { - _csrf: csrf, - id: $this.data('id') - }).done((data) => { - window.location.href = data.redirect; - }); - } - }).modal('show'); - return false; -} - -function linkAction(e) { - e.preventDefault(); - const $this = $(this); - const redirect = $this.data('redirect'); - $.post($this.data('url'), { - _csrf: csrf - }).done((data) => { - if (data.redirect) { - window.location.href = data.redirect; - } else if (redirect) { - window.location.href = redirect; - } else { - window.location.reload(); - } - }); -} - -function linkLanguageAction() { - const $this = $(this); - $.post($this.data('url')).always(() => { - window.location.reload(); - }); -} - -function linkEmailAction(e) { - const $this = $(this); - $('#form-uid').val($this.data('uid')); - $('#form-email').val($this.data('email')); - $('#form-primary').val($this.data('primary')); - $('#form-activate').val($this.data('activate')); - $('#form-uid').val($this.data('uid')); - $('#change-email-modal').modal('show'); - e.preventDefault(); -} - -function initCtrlEnterSubmit() { - $('.js-quick-submit').on('keydown', function (e) { - if (((e.ctrlKey && !e.altKey) || e.metaKey) && (e.keyCode === 13 || e.keyCode === 10)) { - $(this).closest('form').trigger('submit'); - } - }); -} - -function initIssueTimetracking() { - $(document).on('click', '.issue-add-time', () => { - $('.issue-start-time-modal').modal({ - duration: 200, - onApprove() { - $('#add_time_manual_form').trigger('submit'); - } - }).modal('show'); - $('.issue-start-time-modal input').on('keydown', (e) => { - if ((e.keyCode || e.key) === 13) { - $('#add_time_manual_form').trigger('submit'); - } - }); - }); - $(document).on('click', '.issue-start-time, .issue-stop-time', () => { - $('#toggle_stopwatch_form').trigger('submit'); - }); - $(document).on('click', '.issue-cancel-time', () => { - $('#cancel_stopwatch_form').trigger('submit'); - }); - $(document).on('click', 'button.issue-delete-time', function () { - const sel = `.issue-delete-time-modal[data-id="${$(this).data('id')}"]`; - $(sel).modal({ - duration: 200, - onApprove() { - $(`${sel} form`).trigger('submit'); - } - }).modal('show'); - }); -} - -function initBranchOrTagDropdown(selector) { - $(selector).each(function() { - const $dropdown = $(this); - $dropdown.find('.reference.column').on('click', function () { - $dropdown.find('.scrolling.reference-list-menu').hide(); - $($(this).data('target')).show(); - return false; - }); - }); -} - - -$('.commit-button').on('click', function (e) { - e.preventDefault(); - $(this).parent().find('.commit-body').toggle(); -}); - -function initNavbarContentToggle() { - const content = $('#navbar'); - const toggle = $('#navbar-expand-toggle'); - let isExpanded = false; - toggle.on('click', () => { - isExpanded = !isExpanded; - if (isExpanded) { - content.addClass('shown'); - toggle.addClass('active'); - } else { - content.removeClass('shown'); - toggle.removeClass('active'); - } - }); -} - -function initTopicbar() { - const mgrBtn = $('#manage_topic'); - const editDiv = $('#topic_edit'); - const viewDiv = $('#repo-topics'); - const saveBtn = $('#save_topic'); - const topicDropdown = $('#topic_edit .dropdown'); - const topicForm = $('#topic_edit.ui.form'); - const topicPrompts = getPrompts(); - - mgrBtn.on('click', () => { - viewDiv.hide(); - editDiv.css('display', ''); // show Semantic UI Grid - }); - - function getPrompts() { - const hidePrompt = $('div.hide#validate_prompt'); - const prompts = { - countPrompt: hidePrompt.children('#count_prompt').text(), - formatPrompt: hidePrompt.children('#format_prompt').text() - }; - hidePrompt.remove(); - return prompts; - } - - saveBtn.on('click', () => { - const topics = $('input[name=topics]').val(); - - $.post(saveBtn.data('link'), { - _csrf: csrf, - topics - }, (_data, _textStatus, xhr) => { - if (xhr.responseJSON.status === 'ok') { - viewDiv.children('.topic').remove(); - if (topics.length) { - const topicArray = topics.split(','); - - const last = viewDiv.children('a').last(); - for (let i = 0; i < topicArray.length; i++) { - const link = $(''); - link.attr('href', `${AppSubUrl}/explore/repos?q=${encodeURIComponent(topicArray[i])}&topic=1`); - link.text(topicArray[i]); - link.insertBefore(last); - } - } - editDiv.css('display', 'none'); - viewDiv.show(); - } - }).fail((xhr) => { - if (xhr.status === 422) { - if (xhr.responseJSON.invalidTopics.length > 0) { - topicPrompts.formatPrompt = xhr.responseJSON.message; - - const {invalidTopics} = xhr.responseJSON; - const topicLables = topicDropdown.children('a.ui.label'); - - topics.split(',').forEach((value, index) => { - for (let i = 0; i < invalidTopics.length; i++) { - if (invalidTopics[i] === value) { - topicLables.eq(index).removeClass('green').addClass('red'); - } - } - }); - } else { - topicPrompts.countPrompt = xhr.responseJSON.message; - } - } - }).always(() => { - topicForm.form('validate form'); - }); - }); - - topicDropdown.dropdown({ - allowAdditions: true, - forceSelection: false, - fullTextSearch: 'exact', - fields: {name: 'description', value: 'data-value'}, - saveRemoteData: false, - label: { - transition: 'horizontal flip', - duration: 200, - variation: false, - blue: true, - basic: true, - }, - className: { - label: 'ui small label' - }, - apiSettings: { - url: `${AppSubUrl}/api/v1/topics/search?q={query}`, - throttle: 500, - cache: false, - onResponse(res) { - const formattedResponse = { - success: false, - results: [], - }; - const query = stripTags(this.urlData.query.trim()); - let found_query = false; - const current_topics = []; - topicDropdown.find('div.label.visible.topic,a.label.visible').each((_, e) => { current_topics.push(e.dataset.value) }); - - if (res.topics) { - let found = false; - for (let i = 0; i < res.topics.length; i++) { - // skip currently added tags - if (current_topics.includes(res.topics[i].topic_name)) { - continue; - } - - if (res.topics[i].topic_name.toLowerCase() === query.toLowerCase()) { - found_query = true; - } - formattedResponse.results.push({description: res.topics[i].topic_name, 'data-value': res.topics[i].topic_name}); - found = true; - } - formattedResponse.success = found; - } - - if (query.length > 0 && !found_query) { - formattedResponse.success = true; - formattedResponse.results.unshift({description: query, 'data-value': query}); - } else if (query.length > 0 && found_query) { - formattedResponse.results.sort((a, b) => { - if (a.description.toLowerCase() === query.toLowerCase()) return -1; - if (b.description.toLowerCase() === query.toLowerCase()) return 1; - if (a.description > b.description) return -1; - if (a.description < b.description) return 1; - return 0; - }); - } - - return formattedResponse; - }, - }, - onLabelCreate(value) { - value = value.toLowerCase().trim(); - this.attr('data-value', value).contents().first().replaceWith(value); - return $(this); - }, - onAdd(addedValue, _addedText, $addedChoice) { - addedValue = addedValue.toLowerCase().trim(); - $($addedChoice).attr('data-value', addedValue); - $($addedChoice).attr('data-text', addedValue); - } - }); - - $.fn.form.settings.rules.validateTopic = function (_values, regExp) { - const topics = topicDropdown.children('a.ui.label'); - const status = topics.length === 0 || topics.last().attr('data-value').match(regExp); - if (!status) { - topics.last().removeClass('green').addClass('red'); - } - return status && topicDropdown.children('a.ui.label.red').length === 0; - }; - - topicForm.form({ - on: 'change', - inline: true, - fields: { - topics: { - identifier: 'topics', - rules: [ - { - type: 'validateTopic', - value: /^[a-z0-9][a-z0-9-]{0,35}$/, - prompt: topicPrompts.formatPrompt - }, - { - type: 'maxCount[25]', - prompt: topicPrompts.countPrompt - } - ] - }, - } - }); -} - -function updateDeadline(deadlineString) { - $('#deadline-err-invalid-date').hide(); - $('#deadline-loader').addClass('loading'); - - let realDeadline = null; - if (deadlineString !== '') { - const newDate = Date.parse(deadlineString); - - if (Number.isNaN(newDate)) { - $('#deadline-loader').removeClass('loading'); - $('#deadline-err-invalid-date').show(); - return false; - } - realDeadline = new Date(newDate); - } - - $.ajax(`${$('#update-issue-deadline-form').attr('action')}/deadline`, { - data: JSON.stringify({ - due_date: realDeadline, - }), - headers: { - 'X-Csrf-Token': csrf, - 'X-Remote': true, - }, - contentType: 'application/json', - type: 'POST', - success() { - reload(); - }, - error() { - $('#deadline-loader').removeClass('loading'); - $('#deadline-err-invalid-date').show(); - } - }); -} - -function initIssueDue() { - $(document).on('click', '.issue-due-edit', () => { - $('#deadlineForm').fadeToggle(150); - }); - $(document).on('click', '.issue-due-remove', () => { - updateDeadline(''); - }); - $(document).on('submit', '.issue-due-form', () => { - updateDeadline($('#deadlineDate').val()); - return false; - }); -} - -function initIssueList() { - const repolink = $('#repolink').val(); - const repoId = $('#repoId').val(); - const crossRepoSearch = $('#crossRepoSearch').val(); - const tp = $('#type').val(); - let issueSearchUrl = `${AppSubUrl}/api/v1/repos/${repolink}/issues?q={query}&type=${tp}`; - if (crossRepoSearch === 'true') { - issueSearchUrl = `${AppSubUrl}/api/v1/repos/issues/search?q={query}&priority_repo_id=${repoId}&type=${tp}`; - } - $('#new-dependency-drop-list') - .dropdown({ - apiSettings: { - url: issueSearchUrl, - onResponse(response) { - const filteredResponse = {success: true, results: []}; - const currIssueId = $('#new-dependency-drop-list').data('issue-id'); - // Parse the response from the api to work with our dropdown - $.each(response, (_i, issue) => { - // Don't list current issue in the dependency list. - if (issue.id === currIssueId) { - return; - } - filteredResponse.results.push({ - name: `#${issue.number} ${htmlEscape(issue.title) - }
${htmlEscape(issue.repository.full_name)}
`, - value: issue.id - }); - }); - return filteredResponse; - }, - cache: false, - }, - - fullTextSearch: true - }); - - function excludeLabel (item) { - const href = $(item).attr('href'); - const id = $(item).data('label-id'); - - const regStr = `labels=((?:-?[0-9]+%2c)*)(${id})((?:%2c-?[0-9]+)*)&`; - const newStr = 'labels=$1-$2$3&'; - - window.location = href.replace(new RegExp(regStr), newStr); - } - - $('.menu a.label-filter-item').each(function () { - $(this).on('click', function (e) { - if (e.altKey) { - e.preventDefault(); - excludeLabel(this); - } - }); - }); - - $('.menu .ui.dropdown.label-filter').on('keydown', (e) => { - if (e.altKey && e.keyCode === 13) { - const selectedItems = $('.menu .ui.dropdown.label-filter .menu .item.selected'); - if (selectedItems.length > 0) { - excludeLabel($(selectedItems[0])); - } - } - }); -} - -$(document).on('click', 'button[name="is_review"]', (e) => { - $(e.target).closest('form').append(''); -}); - -$(document).on('submit', '.conversation-holder form', async (e) => { - e.preventDefault(); - const form = $(e.target); - const newConversationHolder = $(await $.post(form.attr('action'), form.serialize())); - const {path, side, idx} = newConversationHolder.data(); - - form.closest('.conversation-holder').replaceWith(newConversationHolder); - if (form.closest('tr').data('line-type') === 'same') { - $(`a.add-code-comment[data-path="${path}"][data-idx="${idx}"]`).addClass('invisible'); - } else { - $(`a.add-code-comment[data-path="${path}"][data-side="${side}"][data-idx="${idx}"]`).addClass('invisible'); - } - newConversationHolder.find('.dropdown').dropdown(); - initReactionSelector(newConversationHolder); - initClipboard(); -}); - -$(document).on('click', '.oauth-login-image', () => { - const oauthLoader = $('#oauth2-login-loader'); - const oauthNav = $('#oauth2-login-navigator'); - - oauthNav.hide(); - oauthLoader.removeClass('disabled'); - - setTimeout(() => { - // recover previous content to let user try again - // usually redirection will be performed before this action - oauthLoader.addClass('disabled'); - oauthNav.show(); - }, 5000); -}); From 8beded8fb2337063f4abd105da8808c466a74f39 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 15 Oct 2021 19:40:58 +0800 Subject: [PATCH 2/7] tune clipboard --- web_src/js/features/clipboard.js | 34 +++++++++++++++++++------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/web_src/js/features/clipboard.js b/web_src/js/features/clipboard.js index 6d55a3b3e442f..61e300b7f613b 100644 --- a/web_src/js/features/clipboard.js +++ b/web_src/js/features/clipboard.js @@ -18,20 +18,26 @@ function onError(btn) { export default function initGlobalCopyToClipboardListener() { document.addEventListener('click', async (e) => { - const target = e.target; - let text; - if (target.dataset.clipboardText) { - text = target.dataset.clipboardText; - } else if (target.dataset.clipboardTarget) { - text = document.querySelector(target.dataset.clipboardTarget)?.value; - } - if (!text) return; - - try { - await navigator.clipboard.writeText(text); - onSuccess(target); - } catch { - onError(target); + let target = e.target; + // in case , so we just search up to 3 levels for performance. + for (let i = 0; i < 3 && target; i++) { + let text; + if (target.dataset.clipboardText) { + text = target.dataset.clipboardText; + } else if (target.dataset.clipboardTarget) { + text = document.querySelector(target.dataset.clipboardTarget)?.value; + } + if (text) { + try { + await navigator.clipboard.writeText(text); + onSuccess(target); + } catch { + onError(target); + } + e.preventDefault(); + break; + } + target = target.parentElement; } }); } From f61cc81a506bb363e1ce94fa63c7e09321690f41 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 15 Oct 2021 20:01:07 +0800 Subject: [PATCH 3/7] fix promise --- web_src/js/features/repo-issue.js | 5 +++-- web_src/js/features/repo-legacy.js | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index 5351b2ad920df..cea7583f207c3 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -333,7 +333,7 @@ function initRepoIssueWipTitle() { } function updateIssuesMeta(url, action, issueIds, elementId) { - return new Promise((resolve) => { + return new Promise((resolve, reject) => { $.ajax({ type: 'POST', url, @@ -343,7 +343,8 @@ function updateIssuesMeta(url, action, issueIds, elementId) { issue_ids: issueIds, id: elementId, }, - success: resolve + success: resolve, + error: reject, }); }); } diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index ac150965ab43c..8be9cbaec262d 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -53,7 +53,7 @@ function initRepoCommentForm() { if (editMode === 'true') { const form = $('#update_issueref_form'); - $.post(form.attr('action'), {_csrf: csrf, ref: selectedValue}, window.location.reload); + $.post(form.attr('action'), {_csrf: csrf, ref: selectedValue}, () => window.location.reload()); } else if (editMode === '') { $selectBranch.find('.ui .branch-name').text(selectedValue); } @@ -94,7 +94,7 @@ function initRepoCommentForm() { ); promises.push(promise); }); - Promise.all(promises).then(window.location.reload); + Promise.all(promises).then(() => window.location.reload()); } }); @@ -166,7 +166,7 @@ function initRepoCommentForm() { 'clear', $listMenu.data('issue-id'), '', - ).then(window.location.reload); + ).then(() => window.location.reload()); } $(this).parent().find('.item').each(function () { @@ -209,7 +209,7 @@ function initRepoCommentForm() { '', $menu.data('issue-id'), $(this).data('id'), - ).then(window.location.reload); + ).then(() => window.location.reload()); } let icon = ''; @@ -242,7 +242,7 @@ function initRepoCommentForm() { '', $menu.data('issue-id'), $(this).data('id'), - ).then(window.location.reload); + ).then(() => window.location.reload()); } $list.find('.selected').html(''); From 5e68c9207aea1a0f8f29eecb7f52cfd1535ada73 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 15 Oct 2021 20:20:31 +0800 Subject: [PATCH 4/7] fix document --- docs/content/doc/developers/guidelines-frontend.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/docs/content/doc/developers/guidelines-frontend.md b/docs/content/doc/developers/guidelines-frontend.md index 86286127aa409..f30b0d1cbd886 100644 --- a/docs/content/doc/developers/guidelines-frontend.md +++ b/docs/content/doc/developers/guidelines-frontend.md @@ -39,13 +39,6 @@ We recommend [Google HTML/CSS Style Guide](https://google.github.io/styleguide/h 6. The backend can pass complex data to the frontend by using `ctx.PageData["myModuleData"] = map[]{}` 7. Simple pages and SEO-related pages use Go HTML Template render to generate static Fomantic-UI HTML output. Complex pages can use Vue2 (or Vue3 in future). -## Legacy Problems and Solutions - -### Too much code in `web_src/index.js` - -Previously, most JavaScript code was written into `web_src/index.js` directly, making the file unmaintainable. -Try to keep this file small by creating new modules instead. These modules can be put in the `web_src/js/features` directory for now. - ### Vue2/Vue3 and JSX Gitea is using Vue2 now, we plan to upgrade to Vue3. We decided not to introduce JSX to keep the HTML and the JavaScript code separated. From baad23537487bfe51a98377e4ffe886642337371 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 15 Oct 2021 20:51:45 +0800 Subject: [PATCH 5/7] remove intermediate empty file --- web_src/js/features/comp/IssueCheckboxFix.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 web_src/js/features/comp/IssueCheckboxFix.js diff --git a/web_src/js/features/comp/IssueCheckboxFix.js b/web_src/js/features/comp/IssueCheckboxFix.js deleted file mode 100644 index e69de29bb2d1d..0000000000000 From c81263397dfcbb0c31e24b1ee6a105b6370ebfb0 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 16 Oct 2021 12:51:56 +0800 Subject: [PATCH 6/7] fix async event listener --- web_src/js/features/clipboard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/js/features/clipboard.js b/web_src/js/features/clipboard.js index 61e300b7f613b..4781f8d6ff4a4 100644 --- a/web_src/js/features/clipboard.js +++ b/web_src/js/features/clipboard.js @@ -28,13 +28,13 @@ export default function initGlobalCopyToClipboardListener() { text = document.querySelector(target.dataset.clipboardTarget)?.value; } if (text) { + e.preventDefault(); try { await navigator.clipboard.writeText(text); onSuccess(target); } catch { onError(target); } - e.preventDefault(); break; } target = target.parentElement; From fb144248643e5326ae94619bfd80945b8c88ad2f Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 17 Oct 2021 00:00:51 +0800 Subject: [PATCH 7/7] use `export function` instead of `export {}`, add more comments --- web_src/js/components/DashboardRepoList.js | 4 +- .../js/components/RepoActivityTopAuthors.vue | 5 +- .../js/components/RepoBranchTagDropdown.js | 7 +-- web_src/js/components/VueComponentLoader.js | 11 ++-- web_src/js/features/admin-common.js | 4 +- web_src/js/features/admin-emails.js | 4 +- web_src/js/features/common-global.js | 30 +++------- web_src/js/features/common-issue.js | 4 +- web_src/js/features/common-organization.js | 5 +- web_src/js/features/comp/ColorPicker.js | 4 +- web_src/js/features/comp/CommentSimpleMDE.js | 4 +- web_src/js/features/comp/ImagePaste.js | 6 +- web_src/js/features/comp/LabelEdit.js | 4 +- .../js/features/comp/MarkupContentPreview.js | 4 +- web_src/js/features/comp/ReactionSelector.js | 4 +- web_src/js/features/comp/SearchUserBox.js | 4 +- web_src/js/features/comp/WebHookEditor.js | 4 +- web_src/js/features/install.js | 5 +- web_src/js/features/org-team.js | 6 +- web_src/js/features/repo-branch.js | 4 +- web_src/js/features/repo-code.js | 4 +- web_src/js/features/repo-commit.js | 5 +- web_src/js/features/repo-common.js | 18 ++---- web_src/js/features/repo-diff.js | 15 ++--- web_src/js/features/repo-editor.js | 4 +- web_src/js/features/repo-home.js | 4 +- web_src/js/features/repo-issue.js | 60 ++++++------------- web_src/js/features/repo-legacy.js | 7 +-- web_src/js/features/repo-migrate.js | 4 +- web_src/js/features/repo-release.js | 6 +- web_src/js/features/repo-settings.js | 10 ++-- web_src/js/features/repo-template.js | 4 +- web_src/js/features/repo-wiki.js | 4 +- web_src/js/features/sshkey-helper.js | 4 +- web_src/js/features/user-auth-u2f.js | 6 +- web_src/js/features/user-auth.js | 6 +- web_src/js/features/user-settings.js | 5 +- 37 files changed, 86 insertions(+), 203 deletions(-) diff --git a/web_src/js/components/DashboardRepoList.js b/web_src/js/components/DashboardRepoList.js index 7ae62af0a012c..04eb304c80077 100644 --- a/web_src/js/components/DashboardRepoList.js +++ b/web_src/js/components/DashboardRepoList.js @@ -348,7 +348,7 @@ function initVueComponents() { } -function initDashboardRepoList() { +export function initDashboardRepoList() { const el = document.getElementById('dashboard-repo-list'); const dashboardRepoListData = pageData.dashboardRepoList || null; if (!el || !dashboardRepoListData) return; @@ -366,5 +366,3 @@ function initDashboardRepoList() { }, }); } - -export {initDashboardRepoList}; diff --git a/web_src/js/components/RepoActivityTopAuthors.vue b/web_src/js/components/RepoActivityTopAuthors.vue index d510695b1df63..37b6df91878f9 100644 --- a/web_src/js/components/RepoActivityTopAuthors.vue +++ b/web_src/js/components/RepoActivityTopAuthors.vue @@ -101,10 +101,9 @@ const sfc = { } }; -function initRepoActivityTopAuthorsChart() { +export function initRepoActivityTopAuthorsChart() { initVueApp('#repo-activity-top-authors-chart', sfc); } -export default sfc; -export {initRepoActivityTopAuthorsChart}; +export default sfc; // this line is necessary to activate the IDE's Vue plugin diff --git a/web_src/js/components/RepoBranchTagDropdown.js b/web_src/js/components/RepoBranchTagDropdown.js index a0be57ab3d3b6..50c71d5bacc02 100644 --- a/web_src/js/components/RepoBranchTagDropdown.js +++ b/web_src/js/components/RepoBranchTagDropdown.js @@ -1,6 +1,7 @@ import Vue from 'vue'; +import {vueDelimiters} from './VueComponentLoader.js'; -function initRepoBranchTagDropdown(selector) { +export function initRepoBranchTagDropdown(selector) { $(selector).each(function () { const $dropdown = $(this); const $data = $dropdown.find('.data'); @@ -26,7 +27,7 @@ function initRepoBranchTagDropdown(selector) { $data.remove(); new Vue({ el: this, - delimiters: ['${', '}'], + delimiters: vueDelimiters, data, computed: { filteredItems() { @@ -157,5 +158,3 @@ function initRepoBranchTagDropdown(selector) { }); }); } - -export {initRepoBranchTagDropdown}; diff --git a/web_src/js/components/VueComponentLoader.js b/web_src/js/components/VueComponentLoader.js index 6b2a2cbd58f62..110782bbb1d8f 100644 --- a/web_src/js/components/VueComponentLoader.js +++ b/web_src/js/components/VueComponentLoader.js @@ -1,10 +1,10 @@ import Vue from 'vue'; import {svgs} from '../svg.js'; -const vueDelimiters = ['${', '}']; +export const vueDelimiters = ['${', '}']; let vueEnvInited = false; -function initVueEnv() { +export function initVueEnv() { if (vueEnvInited) return; vueEnvInited = true; @@ -14,7 +14,7 @@ function initVueEnv() { } let vueSvgInited = false; -function initVueSvg() { +export function initVueSvg() { if (vueSvgInited) return; vueSvgInited = true; @@ -36,8 +36,7 @@ function initVueSvg() { } } - -function initVueApp(el, opts = {}) { +export function initVueApp(el, opts = {}) { if (typeof el === 'string') { el = document.querySelector(el); } @@ -48,5 +47,3 @@ function initVueApp(el, opts = {}) { delimiters: vueDelimiters, }, opts)); } - -export {vueDelimiters, initVueEnv, initVueSvg, initVueApp}; diff --git a/web_src/js/features/admin-common.js b/web_src/js/features/admin-common.js index bad176143a47b..4f12c1846dc12 100644 --- a/web_src/js/features/admin-common.js +++ b/web_src/js/features/admin-common.js @@ -1,6 +1,6 @@ const {csrf} = window.config; -function initAdminCommon() { +export function initAdminCommon() { if ($('.admin').length === 0) { return; } @@ -212,5 +212,3 @@ function initAdminCommon() { }); } } - -export {initAdminCommon}; diff --git a/web_src/js/features/admin-emails.js b/web_src/js/features/admin-emails.js index 630ae5aee9bad..f69629d42b580 100644 --- a/web_src/js/features/admin-emails.js +++ b/web_src/js/features/admin-emails.js @@ -1,4 +1,4 @@ -function initAdminEmails() { +export function initAdminEmails() { function linkEmailAction(e) { const $this = $(this); $('#form-uid').val($this.data('uid')); @@ -10,5 +10,3 @@ function initAdminEmails() { } $('.link-email-action').on('click', linkEmailAction); } - -export {initAdminEmails}; diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index 243fca4b7897c..79cdac5def032 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -6,7 +6,7 @@ import 'jquery.are-you-sure'; const {csrf} = window.config; -function initGlobalFormDirtyLeaveConfirm() { +export function initGlobalFormDirtyLeaveConfirm() { // Warn users that try to leave a page after entering data into a form. // Except on sign-in pages, and for forms marked as 'ignore-dirty'. if ($('.user.signin').length === 0) { @@ -14,7 +14,7 @@ function initGlobalFormDirtyLeaveConfirm() { } } -function initHeadNavbarContentToggle() { +export function initHeadNavbarContentToggle() { const content = $('#navbar'); const toggle = $('#navbar-expand-toggle'); let isExpanded = false; @@ -30,7 +30,7 @@ function initHeadNavbarContentToggle() { }); } -function initFootLanguageMenu() { +export function initFootLanguageMenu() { function linkLanguageAction() { const $this = $(this); $.post($this.data('url')).always(() => { @@ -42,7 +42,7 @@ function initFootLanguageMenu() { } -function initGlobalEnterQuickSubmit() { +export function initGlobalEnterQuickSubmit() { $('.js-quick-submit').on('keydown', function (e) { if (((e.ctrlKey && !e.altKey) || e.metaKey) && (e.keyCode === 13 || e.keyCode === 10)) { $(this).closest('form').trigger('submit'); @@ -50,7 +50,7 @@ function initGlobalEnterQuickSubmit() { }); } -function initGlobalButtonClickOnEnter() { +export function initGlobalButtonClickOnEnter() { $(document).on('keypress', '.ui.button', (e) => { if (e.keyCode === 13 || e.keyCode === 32) { // enter key or space bar $(e.target).trigger('click'); @@ -58,7 +58,7 @@ function initGlobalButtonClickOnEnter() { }); } -function initGlobalCommon() { +export function initGlobalCommon() { // Show exact time $('.time-since').each(function () { $(this) @@ -130,7 +130,7 @@ function initGlobalCommon() { }); } -async function initGlobalDropzone() { +export async function initGlobalDropzone() { // Dropzone for (const el of document.querySelectorAll('.dropzone')) { const $dropzone = $(el); @@ -168,7 +168,7 @@ async function initGlobalDropzone() { } } -function initGlobalLinkActions() { +export function initGlobalLinkActions() { function showDeletePopup() { const $this = $(this); const dataArray = $this.data(); @@ -278,7 +278,7 @@ function initGlobalLinkActions() { }); } -function initGlobalButtons() { +export function initGlobalButtons() { $('.show-panel.button').on('click', function () { $($(this).data('panel')).show(); }); @@ -304,15 +304,3 @@ function initGlobalButtons() { }); }); } - -export { - initHeadNavbarContentToggle, - initFootLanguageMenu, - initGlobalEnterQuickSubmit, - initGlobalFormDirtyLeaveConfirm, - initGlobalButtonClickOnEnter, - initGlobalCommon, - initGlobalDropzone, - initGlobalLinkActions, - initGlobalButtons, -}; diff --git a/web_src/js/features/common-issue.js b/web_src/js/features/common-issue.js index caa452adeb765..e2cf51f2a0c0d 100644 --- a/web_src/js/features/common-issue.js +++ b/web_src/js/features/common-issue.js @@ -1,6 +1,6 @@ import {updateIssuesMeta} from './repo-issue.js'; -function initCommonIssue() { +export function initCommonIssue() { $('.issue-checkbox').on('click', () => { const numChecked = $('.issue-checkbox').children('input:checked').length; if (numChecked > 0) { @@ -38,5 +38,3 @@ function initCommonIssue() { $(e).trigger('click'); }); } - -export {initCommonIssue}; diff --git a/web_src/js/features/common-organization.js b/web_src/js/features/common-organization.js index 62074b1205923..9496f8ff22060 100644 --- a/web_src/js/features/common-organization.js +++ b/web_src/js/features/common-organization.js @@ -1,11 +1,10 @@ import {initCompLabelEdit} from './comp/LabelEdit.js'; -function initCommonOrganization() { +export function initCommonOrganization() { if ($('.organization').length === 0) { return; } - // Options if ($('.organization.settings.options').length > 0) { $('#org_name').on('keyup', function () { const $prompt = $('#org-name-change-prompt'); @@ -23,5 +22,3 @@ function initCommonOrganization() { // Labels initCompLabelEdit('.organization.settings.labels'); } - -export {initCommonOrganization}; diff --git a/web_src/js/features/comp/ColorPicker.js b/web_src/js/features/comp/ColorPicker.js index d16ae6404fba4..669e1d1f18f0a 100644 --- a/web_src/js/features/comp/ColorPicker.js +++ b/web_src/js/features/comp/ColorPicker.js @@ -1,6 +1,6 @@ import createColorPicker from '../colorpicker.js'; -function initCompColorPicker() { +export function initCompColorPicker() { createColorPicker($('.color-picker')); $('.precolors .color').on('click', function () { @@ -9,5 +9,3 @@ function initCompColorPicker() { $('.minicolors-swatch-color').css('background-color', color_hex); }); } - -export {initCompColorPicker}; diff --git a/web_src/js/features/comp/CommentSimpleMDE.js b/web_src/js/features/comp/CommentSimpleMDE.js index 806a63b184c67..fbc0ec8bafbf3 100644 --- a/web_src/js/features/comp/CommentSimpleMDE.js +++ b/web_src/js/features/comp/CommentSimpleMDE.js @@ -1,6 +1,6 @@ import attachTribute from '../tribute.js'; -function createCommentSimpleMDE($editArea) { +export function createCommentSimpleMDE($editArea) { if ($editArea.length === 0) { return null; } @@ -70,5 +70,3 @@ function createCommentSimpleMDE($editArea) { $(simplemde.codemirror.getInputField()).data('simplemde', simplemde); return simplemde; } - -export {createCommentSimpleMDE}; diff --git a/web_src/js/features/comp/ImagePaste.js b/web_src/js/features/comp/ImagePaste.js index 939391362776c..b6881dd282ead 100644 --- a/web_src/js/features/comp/ImagePaste.js +++ b/web_src/js/features/comp/ImagePaste.js @@ -53,7 +53,7 @@ function replaceAndKeepCursor(field, oldval, newval) { } } -function initCompImagePaste($target) { +export function initCompImagePaste($target) { $target.each(function () { const dropzone = this.querySelector('.dropzone'); if (!dropzone) { @@ -76,7 +76,7 @@ function initCompImagePaste($target) { }); } -function initSimpleMDEImagePaste(simplemde, dropzone, files) { +export function initSimpleMDEImagePaste(simplemde, dropzone, files) { const uploadUrl = dropzone.dataset.uploadUrl; simplemde.codemirror.on('paste', async (_, e) => { for (const img of clipboardPastedImages(e)) { @@ -89,5 +89,3 @@ function initSimpleMDEImagePaste(simplemde, dropzone, files) { } }); } - -export {initCompImagePaste, initSimpleMDEImagePaste}; diff --git a/web_src/js/features/comp/LabelEdit.js b/web_src/js/features/comp/LabelEdit.js index 80b5be2aaac37..7d71e6effaa2a 100644 --- a/web_src/js/features/comp/LabelEdit.js +++ b/web_src/js/features/comp/LabelEdit.js @@ -1,6 +1,6 @@ import {initCompColorPicker} from './ColorPicker.js'; -function initCompLabelEdit(selector) { +export function initCompLabelEdit(selector) { if (!$(selector).length) return; // Create label const $newLabelPanel = $('.new-label.segment'); @@ -28,5 +28,3 @@ function initCompLabelEdit(selector) { return false; }); } - -export {initCompLabelEdit}; diff --git a/web_src/js/features/comp/MarkupContentPreview.js b/web_src/js/features/comp/MarkupContentPreview.js index 4364267d17ebb..0b05c4efaea5d 100644 --- a/web_src/js/features/comp/MarkupContentPreview.js +++ b/web_src/js/features/comp/MarkupContentPreview.js @@ -2,7 +2,7 @@ import {initMarkupContent} from '../../markup/content.js'; const {csrf} = window.config; -function initCompMarkupContentPreviewTab($form) { +export function initCompMarkupContentPreviewTab($form) { const $tabMenu = $form.find('.tabular.menu'); $tabMenu.find('.item').tab(); $tabMenu.find(`.item[data-tab="${$tabMenu.data('preview')}"]`).on('click', function () { @@ -19,5 +19,3 @@ function initCompMarkupContentPreviewTab($form) { }); }); } - -export {initCompMarkupContentPreviewTab}; diff --git a/web_src/js/features/comp/ReactionSelector.js b/web_src/js/features/comp/ReactionSelector.js index 7bae4f99024c7..d11c9667b947a 100644 --- a/web_src/js/features/comp/ReactionSelector.js +++ b/web_src/js/features/comp/ReactionSelector.js @@ -1,6 +1,6 @@ const {csrf} = window.config; -function initCompReactionSelector(parent) { +export function initCompReactionSelector(parent) { let reactions = ''; if (!parent) { parent = $(document); @@ -46,5 +46,3 @@ function initCompReactionSelector(parent) { }); }); } - -export {initCompReactionSelector}; diff --git a/web_src/js/features/comp/SearchUserBox.js b/web_src/js/features/comp/SearchUserBox.js index 4a111fbcd1cdc..9019f17de39a3 100644 --- a/web_src/js/features/comp/SearchUserBox.js +++ b/web_src/js/features/comp/SearchUserBox.js @@ -2,7 +2,7 @@ import {htmlEscape} from 'escape-goat'; const {AppSubUrl} = window.config; -function initSearchUserBox() { +export function initSearchUserBox() { const $searchUserBox = $('#search-user-box'); $searchUserBox.search({ minCharacters: 2, @@ -34,5 +34,3 @@ function initSearchUserBox() { showNoResults: false }); } - -export {initSearchUserBox}; diff --git a/web_src/js/features/comp/WebHookEditor.js b/web_src/js/features/comp/WebHookEditor.js index 3b10b742612a2..6911c6cb1675d 100644 --- a/web_src/js/features/comp/WebHookEditor.js +++ b/web_src/js/features/comp/WebHookEditor.js @@ -1,6 +1,6 @@ const {csrf} = window.config; -function initWebHookEditor() { +export function initWebHookEditor() { if ($('.new.webhook').length === 0) { return; } @@ -38,5 +38,3 @@ function initWebHookEditor() { ); }); } - -export {initWebHookEditor}; diff --git a/web_src/js/features/install.js b/web_src/js/features/install.js index 876a610377faf..6083fa7a585b6 100644 --- a/web_src/js/features/install.js +++ b/web_src/js/features/install.js @@ -1,5 +1,4 @@ - -function initInstall() { +export function initInstall() { if ($('.install').length === 0) { return; } @@ -90,5 +89,3 @@ function initInstall() { } }); } - -export {initInstall}; diff --git a/web_src/js/features/org-team.js b/web_src/js/features/org-team.js index 9f99721b9a6b3..d6492965ff210 100644 --- a/web_src/js/features/org-team.js +++ b/web_src/js/features/org-team.js @@ -1,6 +1,6 @@ const {AppSubUrl} = window.config; -function initOrgTeamSettings() { +export function initOrgTeamSettings() { // Change team access mode $('.organization.new.team input[name=permission]').on('change', () => { const val = $('input[name=permission]:checked', '.organization.new.team').val(); @@ -13,7 +13,7 @@ function initOrgTeamSettings() { } -function initOrgTeamSearchRepoBox() { +export function initOrgTeamSearchRepoBox() { const $searchRepoBox = $('#search-repo-box'); $searchRepoBox.search({ minCharacters: 2, @@ -35,5 +35,3 @@ function initOrgTeamSearchRepoBox() { showNoResults: false }); } - -export {initOrgTeamSettings, initOrgTeamSearchRepoBox}; diff --git a/web_src/js/features/repo-branch.js b/web_src/js/features/repo-branch.js index 210ee125a73bc..c9119997caef0 100644 --- a/web_src/js/features/repo-branch.js +++ b/web_src/js/features/repo-branch.js @@ -1,9 +1,7 @@ -function initRepoBranchButton() { +export function initRepoBranchButton() { $('.show-create-branch-modal.button').on('click', function () { $('#create-branch-form')[0].action = $('#create-branch-form').data('base-action') + $(this).data('branch-from'); $('#modal-create-branch-from-span').text($(this).data('branch-from')); $($(this).data('modal')).modal('show'); }); } - -export {initRepoBranchButton}; diff --git a/web_src/js/features/repo-code.js b/web_src/js/features/repo-code.js index 5026f3043a3e8..74b3c1fba8189 100644 --- a/web_src/js/features/repo-code.js +++ b/web_src/js/features/repo-code.js @@ -70,7 +70,7 @@ function showLineButton() { $('.code-line-button').popup({popup: $('.code-line-menu'), on: 'click'}); } -function initRepoCodeView() { +export function initRepoCodeView() { if ($('.code-view .lines-num').length > 0) { $(document).on('click', '.lines-num span', function (e) { const $select = $(this); @@ -143,5 +143,3 @@ function initRepoCodeView() { currentTarget.closest('tr').outerHTML = blob; }); } - -export {initRepoCodeView}; diff --git a/web_src/js/features/repo-commit.js b/web_src/js/features/repo-commit.js index 147c9849e34aa..336a37d654725 100644 --- a/web_src/js/features/repo-commit.js +++ b/web_src/js/features/repo-commit.js @@ -1,9 +1,6 @@ -function initRepoCommitButton() { +export function initRepoCommitButton() { $('.commit-button').on('click', function (e) { e.preventDefault(); $(this).parent().find('.commit-body').toggle(); }); } - -export {initRepoCommitButton}; - diff --git a/web_src/js/features/repo-common.js b/web_src/js/features/repo-common.js index 1941b8243ab69..c050dba34c813 100644 --- a/web_src/js/features/repo-common.js +++ b/web_src/js/features/repo-common.js @@ -32,7 +32,7 @@ function getArchive($target, url, first) { }); } -function initRepoArchiveLinks() { +export function initRepoArchiveLinks() { $('.archive-link').on('click', function (event) { event.preventDefault(); const url = $(this).data('url'); @@ -41,7 +41,7 @@ function initRepoArchiveLinks() { }); } -function initRepoClone() { +export function initRepoClone() { // Quick start and repository home $('#repo-clone-ssh').on('click', function () { $('.clone-url').text($(this).data('link')); @@ -64,7 +64,7 @@ function initRepoClone() { }); } -function initRepoCommonBranchOrTagDropdown(selector) { +export function initRepoCommonBranchOrTagDropdown(selector) { $(selector).each(function () { const $dropdown = $(this); $dropdown.find('.reference.column').on('click', function () { @@ -75,7 +75,7 @@ function initRepoCommonBranchOrTagDropdown(selector) { }); } -function initRepoCommonFilterSearchDropdown(selector) { +export function initRepoCommonFilterSearchDropdown(selector) { const $dropdown = $(selector); $dropdown.dropdown({ fullTextSearch: true, @@ -89,7 +89,7 @@ function initRepoCommonFilterSearchDropdown(selector) { }); } -function initRepoCommonLanguageStats() { +export function initRepoCommonLanguageStats() { // Language stats if ($('.language-stats').length > 0) { $('.language-stats').on('click', (e) => { @@ -98,11 +98,3 @@ function initRepoCommonLanguageStats() { }); } } - -export { - initRepoArchiveLinks, - initRepoClone, - initRepoCommonFilterSearchDropdown, - initRepoCommonLanguageStats, - initRepoCommonBranchOrTagDropdown, -}; diff --git a/web_src/js/features/repo-diff.js b/web_src/js/features/repo-diff.js index 484bc4de91525..4d6a1a011d3f0 100644 --- a/web_src/js/features/repo-diff.js +++ b/web_src/js/features/repo-diff.js @@ -2,13 +2,13 @@ import {initCompReactionSelector} from './comp/ReactionSelector.js'; const {csrf} = window.config; -function initRepoDiffReviewButton() { +export function initRepoDiffReviewButton() { $(document).on('click', 'button[name="is_review"]', (e) => { $(e.target).closest('form').append(''); }); } -function initRepoDiffFileViewToggle() { +export function initRepoDiffFileViewToggle() { $('.file-view-toggle').on('click', function () { const $this = $(this); $this.parent().children().removeClass('active'); @@ -20,7 +20,7 @@ function initRepoDiffFileViewToggle() { }); } -function initRepoDiffConversationForm() { +export function initRepoDiffConversationForm() { $('.conversation-holder form').on('submit', async (e) => { e.preventDefault(); const form = $(e.target); @@ -58,7 +58,7 @@ function initRepoDiffConversationForm() { }); } -function initRepoDiffConversationNav() { +export function initRepoDiffConversationNav() { // Previous/Next code review conversation $(document).on('click', '.previous-conversation', (e) => { const $conversation = $(e.currentTarget).closest('.comment-code-cloud'); @@ -79,10 +79,3 @@ function initRepoDiffConversationNav() { window.location.href = `#${anchor}`; }); } - -export { - initRepoDiffReviewButton, - initRepoDiffFileViewToggle, - initRepoDiffConversationForm, - initRepoDiffConversationNav, -}; diff --git a/web_src/js/features/repo-editor.js b/web_src/js/features/repo-editor.js index 0a227f7c9b29d..831b621fde84a 100644 --- a/web_src/js/features/repo-editor.js +++ b/web_src/js/features/repo-editor.js @@ -75,7 +75,7 @@ function getCursorPosition($e) { return pos; } -async function initRepoEditor() { +export async function initRepoEditor() { initEditorForm(); $('.js-quick-pull-choice-option').on('change', function () { @@ -178,5 +178,3 @@ async function initRepoEditor() { } }); } - -export {initRepoEditor}; diff --git a/web_src/js/features/repo-home.js b/web_src/js/features/repo-home.js index 0774a398bdca9..142698f0747bb 100644 --- a/web_src/js/features/repo-home.js +++ b/web_src/js/features/repo-home.js @@ -2,7 +2,7 @@ import {stripTags} from '../utils.js'; const {AppSubUrl, csrf} = window.config; -function initRepoTopicBar() { +export function initRepoTopicBar() { const mgrBtn = $('#manage_topic'); const editDiv = $('#topic_edit'); const viewDiv = $('#repo-topics'); @@ -178,5 +178,3 @@ function initRepoTopicBar() { } }); } - -export {initRepoTopicBar}; diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index cea7583f207c3..858398aac9888 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -6,7 +6,7 @@ import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js'; const {AppSubUrl, csrf} = window.config; -function initRepoIssueTimeTracking() { +export function initRepoIssueTimeTracking() { $(document).on('click', '.issue-add-time', () => { $('.issue-start-time-modal').modal({ duration: 200, @@ -73,7 +73,7 @@ function updateDeadline(deadlineString) { }); } -function initRepoIssueDue() { +export function initRepoIssueDue() { $(document).on('click', '.issue-due-edit', () => { $('#deadlineForm').fadeToggle(150); }); @@ -86,7 +86,7 @@ function initRepoIssueDue() { }); } -function initRepoIssueList() { +export function initRepoIssueList() { const repolink = $('#repolink').val(); const repoId = $('#repoId').val(); const crossRepoSearch = $('#crossRepoSearch').val(); @@ -151,7 +151,7 @@ function initRepoIssueList() { }); } -function initRepoIssueCommentDelete() { +export function initRepoIssueCommentDelete() { // Delete comment $(document).on('click', '.delete-comment', function () { const $this = $(this); @@ -179,7 +179,7 @@ function initRepoIssueCommentDelete() { }); } -function initRepoIssueDependencyDelete() { +export function initRepoIssueDependencyDelete() { // Delete Issue dependency $(document).on('click', '.delete-dependency-button', (e) => { const {id, type} = e.currentTarget.dataset; @@ -196,7 +196,7 @@ function initRepoIssueDependencyDelete() { }); } -function initRepoIssueCodeCommentCancel() { +export function initRepoIssueCodeCommentCancel() { // Cancel inline code comment $(document).on('click', '.cancel-code-comment', (e) => { const form = $(e.currentTarget).closest('form'); @@ -209,7 +209,7 @@ function initRepoIssueCodeCommentCancel() { }); } -function initRepoIssueStatusButton() { +export function initRepoIssueStatusButton() { // Change status const $statusButton = $('#status-button'); $('#comment-form textarea').on('keyup', function () { @@ -223,7 +223,7 @@ function initRepoIssueStatusButton() { }); } -function initRepoPullRequestMerge() { +export function initRepoPullRequestMerge() { // Pull Request merge button const $mergeButton = $('.merge-button > button'); $mergeButton.on('click', function (e) { @@ -249,7 +249,7 @@ function initRepoPullRequestMerge() { }); } -function initRepoPullRequestUpdate() { +export function initRepoPullRequestUpdate() { // Pull Request update button const $pullUpdateButton = $('.update-button > button'); $pullUpdateButton.on('click', function (e) { @@ -281,13 +281,13 @@ function initRepoPullRequestUpdate() { }); } -function initRepoPullRequestMergeInstruction() { +export function initRepoPullRequestMergeInstruction() { $('.show-instruction').on('click', () => { $('.instruct-content').toggle(); }); } -function initRepoIssueReferenceRepositorySearch() { +export function initRepoIssueReferenceRepositorySearch() { $('.issue_reference_repository_search') .dropdown({ apiSettings: { @@ -313,7 +313,7 @@ function initRepoIssueReferenceRepositorySearch() { } -function initRepoIssueWipTitle() { +export function initRepoIssueWipTitle() { $('.title_wip_desc > a').on('click', (e) => { e.preventDefault(); @@ -332,7 +332,7 @@ function initRepoIssueWipTitle() { }); } -function updateIssuesMeta(url, action, issueIds, elementId) { +export function updateIssuesMeta(url, action, issueIds, elementId) { return new Promise((resolve, reject) => { $.ajax({ type: 'POST', @@ -349,7 +349,7 @@ function updateIssuesMeta(url, action, issueIds, elementId) { }); } -function initRepoIssueComments() { +export function initRepoIssueComments() { if ($('.repository.view.issue .timeline').length === 0) return; $('.re-request-review').on('click', function (event) { @@ -411,7 +411,7 @@ function assignMenuAttributes(menu) { return id; } -function initRepoPullRequestReview() { +export function initRepoPullRequestReview() { if (window.location.hash && window.location.hash.startsWith('#issuecomment-')) { const commentDiv = $(window.location.hash); if (commentDiv) { @@ -533,7 +533,7 @@ function initRepoPullRequestReview() { }); } -function initRepoIssueReferenceIssue() { +export function initRepoIssueReferenceIssue() { // Reference issue $(document).on('click', '.reference-issue', function (event) { const $this = $(this); @@ -550,7 +550,7 @@ function initRepoIssueReferenceIssue() { }); } -function initRepoIssueWipToggle() { +export function initRepoIssueWipToggle() { // Toggle WIP $('.toggle-wip a, .toggle-wip button').on('click', async (e) => { e.preventDefault(); @@ -564,7 +564,7 @@ function initRepoIssueWipToggle() { } -function initRepoIssueTitleEdit() { +export function initRepoIssueTitleEdit() { // Edit issue title const $issueTitle = $('#issue-title'); const $editInput = $('#edit-title-input input'); @@ -619,7 +619,7 @@ function initRepoIssueTitleEdit() { }); } -function initRepoIssueBranchSelect() { +export function initRepoIssueBranchSelect() { const changeBranchSelect = function () { const selectionTextField = $('#pull-target-branch'); @@ -636,25 +636,3 @@ function initRepoIssueBranchSelect() { }; $('#branch-select > .item').on('click', changeBranchSelect); } - -export { - updateIssuesMeta, - initRepoIssueTimeTracking, - initRepoIssueDue, - initRepoIssueList, - initRepoIssueCommentDelete, - initRepoIssueDependencyDelete, - initRepoIssueCodeCommentCancel, - initRepoIssueStatusButton, - initRepoPullRequestMerge, - initRepoPullRequestUpdate, - initRepoPullRequestMergeInstruction, - initRepoIssueReferenceRepositorySearch, - initRepoIssueWipTitle, - initRepoIssueComments, - initRepoPullRequestReview, - initRepoIssueReferenceIssue, - initRepoIssueWipToggle, - initRepoIssueTitleEdit, - initRepoIssueBranchSelect, -}; diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index 8be9cbaec262d..a1c405d719af1 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -31,9 +31,10 @@ const {csrf} = window.config; const commentMDEditors = {}; +// FIXME: the usage of `autoSimpleMDE` is quite messy, the refactor should be done very carefully in future. let autoSimpleMDE; -function initRepoCommentForm() { +export function initRepoCommentForm() { if ($('.comment.form').length === 0) { return; } @@ -258,7 +259,7 @@ function initRepoCommentForm() { } -async function initRepository() { +export async function initRepository() { if ($('.repository').length === 0) { return; } @@ -571,5 +572,3 @@ function initRepoIssueQuoteReply() { event.preventDefault(); }); } - -export {initRepoCommentForm, initRepository}; diff --git a/web_src/js/features/repo-migrate.js b/web_src/js/features/repo-migrate.js index a8a0e6cca22bd..872dbc34f55b8 100644 --- a/web_src/js/features/repo-migrate.js +++ b/web_src/js/features/repo-migrate.js @@ -1,6 +1,6 @@ const {AppSubUrl, csrf} = window.config; -function initRepoMigrationStatusChecker() { +export function initRepoMigrationStatusChecker() { const migrating = $('#repo_migrating'); $('#repo_migrating_failed').hide(); $('#repo_migrating_failed_image').hide(); @@ -46,5 +46,3 @@ function initRepoMigrationStatusChecker() { }); } } - -export {initRepoMigrationStatusChecker}; diff --git a/web_src/js/features/repo-release.js b/web_src/js/features/repo-release.js index 16a1601b85bab..08e3e9e026513 100644 --- a/web_src/js/features/repo-release.js +++ b/web_src/js/features/repo-release.js @@ -3,7 +3,7 @@ import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js'; import {initSimpleMDEImagePaste} from './comp/ImagePaste.js'; import {createCommentSimpleMDE} from './comp/CommentSimpleMDE.js'; -function initRepoRelease() { +export function initRepoRelease() { $(document).on('click', '.remove-rel-attach', function() { const uuid = $(this).data('uuid'); const id = $(this).data('id'); @@ -13,7 +13,7 @@ function initRepoRelease() { } -function initRepoReleaseEditor() { +export function initRepoReleaseEditor() { const $editor = $('.repository.new.release .content-editor'); if ($editor.length === 0) { return false; @@ -27,5 +27,3 @@ function initRepoReleaseEditor() { const dropzone = $editor.parent().find('.dropzone')[0]; initSimpleMDEImagePaste($simplemde, dropzone, $files); } - -export {initRepoRelease, initRepoReleaseEditor}; diff --git a/web_src/js/features/repo-settings.js b/web_src/js/features/repo-settings.js index 0cded32a3f65f..e06344893643d 100644 --- a/web_src/js/features/repo-settings.js +++ b/web_src/js/features/repo-settings.js @@ -3,7 +3,7 @@ import {initRepoCommonFilterSearchDropdown} from './repo-common.js'; const {AppSubUrl, csrf} = window.config; -function initRepoSettingsCollaboration() { +export function initRepoSettingsCollaboration() { // Change collaborator access mode $('.access-mode.menu .item').on('click', function () { const $menu = $(this).parent(); @@ -15,7 +15,7 @@ function initRepoSettingsCollaboration() { }); } -function initRepoSettingSearchTeamBox() { +export function initRepoSettingSearchTeamBox() { const $searchTeamBox = $('#search-team-box'); $searchTeamBox.search({ minCharacters: 2, @@ -40,13 +40,13 @@ function initRepoSettingSearchTeamBox() { } -async function initRepoSettingGitHook() { +export async function initRepoSettingGitHook() { if ($('.edit.githook').length === 0) return; const filename = document.querySelector('.hook-filename').textContent; await createMonaco($('#content')[0], filename, {language: 'shell'}); } -function initRepoSettingBranches() { +export function initRepoSettingBranches() { // Branches if ($('.repository.settings.branches').length > 0) { initRepoCommonFilterSearchDropdown('.protected-branches .dropdown'); @@ -64,5 +64,3 @@ function initRepoSettingBranches() { }); } } - -export {initRepoSettingsCollaboration, initRepoSettingSearchTeamBox, initRepoSettingGitHook, initRepoSettingBranches}; diff --git a/web_src/js/features/repo-template.js b/web_src/js/features/repo-template.js index a6f2867f5fd80..9385e2acb8aa1 100644 --- a/web_src/js/features/repo-template.js +++ b/web_src/js/features/repo-template.js @@ -2,7 +2,7 @@ import {htmlEscape} from 'escape-goat'; const {AppSubUrl} = window.config; -function initRepoTemplateSearch() { +export function initRepoTemplateSearch() { const $repoTemplate = $('#repo_template'); const checkTemplate = function () { const $templateUnits = $('#template_units'); @@ -47,5 +47,3 @@ function initRepoTemplateSearch() { $('#uid').on('change', changeOwner); changeOwner(); } - -export {initRepoTemplateSearch}; diff --git a/web_src/js/features/repo-wiki.js b/web_src/js/features/repo-wiki.js index 280f7a680b948..aad3161fdb846 100644 --- a/web_src/js/features/repo-wiki.js +++ b/web_src/js/features/repo-wiki.js @@ -3,7 +3,7 @@ import {initCompMarkupContentPreviewTab} from './comp/MarkupContentPreview.js'; const {csrf} = window.config; -function initRepoWikiForm() { +export function initRepoWikiForm() { const $editArea = $('.repository.wiki textarea#edit_area'); let sideBySideChanges = 0; let sideBySideTimeout = null; @@ -172,5 +172,3 @@ function initRepoWikiForm() { }, 0); } } - -export {initRepoWikiForm}; diff --git a/web_src/js/features/sshkey-helper.js b/web_src/js/features/sshkey-helper.js index 462406bbf8246..bb3c8accf44d8 100644 --- a/web_src/js/features/sshkey-helper.js +++ b/web_src/js/features/sshkey-helper.js @@ -1,4 +1,4 @@ -function initSshKeyFormParser() { +export function initSshKeyFormParser() { // Parse SSH Key $('#ssh-key-content').on('change paste keyup', function () { const arrays = $(this).val().split(' '); @@ -8,5 +8,3 @@ function initSshKeyFormParser() { } }); } - -export {initSshKeyFormParser}; diff --git a/web_src/js/features/user-auth-u2f.js b/web_src/js/features/user-auth-u2f.js index 8997d9370829b..25255213a89c1 100644 --- a/web_src/js/features/user-auth-u2f.js +++ b/web_src/js/features/user-auth-u2f.js @@ -1,6 +1,6 @@ const {AppSubUrl, csrf} = window.config; -function initUserAuthU2fAuth() { +export function initUserAuthU2fAuth() { if ($('#wait-for-key').length === 0) { return; } @@ -85,7 +85,7 @@ function u2fError(errorType) { $('#u2f-error').modal('show'); } -function initUserAuthU2fRegister() { +export function initUserAuthU2fRegister() { $('#register-device').modal({allowMultiple: false}); $('#u2f-error').modal({allowMultiple: false}); $('#register-security-key').on('click', (e) => { @@ -123,5 +123,3 @@ function u2fRegisterRequest() { } }); } - -export {initUserAuthU2fRegister, initUserAuthU2fAuth}; diff --git a/web_src/js/features/user-auth.js b/web_src/js/features/user-auth.js index bf9548e16aa17..bc294803bfdc6 100644 --- a/web_src/js/features/user-auth.js +++ b/web_src/js/features/user-auth.js @@ -1,4 +1,4 @@ -function initUserAuthOauth2() { +export function initUserAuthOauth2() { const $oauth2LoginNav = $('#oauth2-login-navigator'); if ($oauth2LoginNav.length === 0) return; @@ -18,7 +18,7 @@ function initUserAuthOauth2() { }); } -function initUserAuthLinkAccountView() { +export function initUserAuthLinkAccountView() { const $lnkUserPage = $('.page-content.user.link-account'); if ($lnkUserPage.length === 0) { return false; @@ -44,5 +44,3 @@ function initUserAuthLinkAccountView() { $signInView.addClass('active'); }); } - -export {initUserAuthOauth2, initUserAuthLinkAccountView}; diff --git a/web_src/js/features/user-settings.js b/web_src/js/features/user-settings.js index 9164d49dd9038..c2f00dc362d3e 100644 --- a/web_src/js/features/user-settings.js +++ b/web_src/js/features/user-settings.js @@ -1,5 +1,4 @@ -function initUserSettings() { - // Options +export function initUserSettings() { if ($('.user.settings.profile').length > 0) { $('#username').on('keyup', function () { const $prompt = $('#name-change-prompt'); @@ -14,5 +13,3 @@ function initUserSettings() { }); } } - -export {initUserSettings};