import './publicpath.js'; import Vue from 'vue'; import {htmlEscape} from 'escape-goat'; import 'jquery.are-you-sure'; import ActivityTopAuthors from './components/ActivityTopAuthors.vue'; 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 initContextPopups from './features/contextpopup.js'; import initGitGraph from './features/gitgraph.js'; import initHeatmap from './features/heatmap.js'; import initImageDiff from './features/imagediff.js'; import initMigration from './features/migration.js'; import initProject from './features/projects.js'; import initServiceWorker from './features/serviceworker.js'; import initTableSort from './features/tablesort.js'; import {createCodeEditor, createMonaco} from './features/codeeditor.js'; import {initMarkupAnchors} from './markup/anchors.js'; import {initNotificationsTable, initNotificationCount} from './features/notification.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, svgs} from './svg.js'; const {AppSubUrl, AssetUrlPrefix, csrf} = window.config; let previewFileModes; const commentMDEditors = {}; // Silence fomantic's error logging when tabs are used without a target content element $.fn.tab.settings.silent = true; // Silence Vue's console advertisements in dev mode // To use the Vue browser extension, enable the devtools option temporarily Vue.config.productionTip = false; Vue.config.devtools = false; 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(); }); 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); }); $('.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 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) { initFilterBranchTagDropdown('.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 subject = content.split('\n', 1)[0].slice(0, 255); const poster = $this.data('poster'); const reference = $this.data('reference'); const $modal = $($this.data('modal')); $modal.find('input[name="title"').val(subject); $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(); }); 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 = $(`