diff options
author | silverwind <me@silverwind.io> | 2019-11-14 22:39:51 +0100 |
---|---|---|
committer | zeripath <art27@cantab.net> | 2019-11-14 21:39:51 +0000 |
commit | 3621944c2d381d159cf2622328927cf282f0b3e0 (patch) | |
tree | 17c4b38f851fae79c0084ce47a425b39f99f56c7 /web_src/js/index.js | |
parent | 4b5ebb93e48f04227ec18cf042e916df4eafe830 (diff) | |
download | gitea-3621944c2d381d159cf2622328927cf282f0b3e0.tar.gz gitea-3621944c2d381d159cf2622328927cf282f0b3e0.zip |
modernize js and use babel (#8973)
* modernize js and use babel
- add babel toolchain to transform modern JS to ES5
- extend eslint config for modern rules
- fixes linting issues via `eslint --fix` and manual fixes
* run 'make css' to satisfy CI
* code style tweaks and set js indendation to 2 in .editorconfig
* regenerate js
Diffstat (limited to 'web_src/js/index.js')
-rw-r--r-- | web_src/js/index.js | 6084 |
1 files changed, 2968 insertions, 3116 deletions
diff --git a/web_src/js/index.js b/web_src/js/index.js index 2edd70c87d..4e53494eb0 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -1,10 +1,9 @@ /* globals wipPrefixes, issuesTribute, emojiTribute */ /* exported timeAddManual, toggleStopwatch, cancelStopwatch, initHeatmap */ /* exported toggleDeadlineForm, setDeadline, updateDeadline, deleteDependencyModal, cancelCodeComment, onOAuthLoginClick */ -'use strict'; function htmlEncode(text) { - return jQuery('<div />').text(text).html() + return jQuery('<div />').text(text).html(); } let csrf; @@ -14,3411 +13,3264 @@ let simpleMDEditor; let codeMirrorEditor; // Disable Dropzone auto-discover because it's manually initialized -if (typeof(Dropzone) !== "undefined") { - Dropzone.autoDiscover = false; -} - -// Polyfill for IE9+ support (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from) -if (!Array.from) { - Array.from = (function () { - const toStr = Object.prototype.toString; - const isCallable = function (fn) { - return typeof fn === 'function' || toStr.call(fn) === '[object Function]'; - }; - const toInteger = function (value) { - const number = Number(value); - if (isNaN(number)) { return 0; } - if (number === 0 || !isFinite(number)) { return number; } - return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number)); - }; - const maxSafeInteger = Math.pow(2, 53) - 1; - const toLength = function (value) { - const len = toInteger(value); - return Math.min(Math.max(len, 0), maxSafeInteger); - }; - - // The length property of the from method is 1. - return function from(arrayLike/*, mapFn, thisArg */) { - // 1. Let C be the this value. - const C = this; - - // 2. Let items be ToObject(arrayLike). - const items = Object(arrayLike); - - // 3. ReturnIfAbrupt(items). - if (arrayLike == null) { - throw new TypeError("Array.from requires an array-like object - not null or undefined"); - } - - // 4. If mapfn is undefined, then let mapping be false. - const mapFn = arguments.length > 1 ? arguments[1] : void undefined; - let T; - if (typeof mapFn !== 'undefined') { - // 5. else - // 5. a If IsCallable(mapfn) is false, throw a TypeError exception. - if (!isCallable(mapFn)) { - throw new TypeError('Array.from: when provided, the second argument must be a function'); - } - - // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined. - if (arguments.length > 2) { - T = arguments[2]; - } - } - - // 10. Let lenValue be Get(items, "length"). - // 11. Let len be ToLength(lenValue). - const len = toLength(items.length); - - // 13. If IsConstructor(C) is true, then - // 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len. - // 14. a. Else, Let A be ArrayCreate(len). - const A = isCallable(C) ? Object(new C(len)) : new Array(len); - - // 16. Let k be 0. - let k = 0; - // 17. Repeat, while k < len… (also steps a - h) - let kValue; - while (k < len) { - kValue = items[k]; - if (mapFn) { - A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k); - } else { - A[k] = kValue; - } - k += 1; - } - // 18. Let putStatus be Put(A, "length", len, true). - A.length = len; - // 20. Return A. - return A; - }; - }()); -} - -// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign -if (typeof Object.assign != 'function') { - // Must be writable: true, enumerable: false, configurable: true - Object.defineProperty(Object, "assign", { - value: function assign(target, _varArgs) { // .length of function is 2 - 'use strict'; - if (target == null) { // TypeError if undefined or null - throw new TypeError('Cannot convert undefined or null to object'); - } - - const to = Object(target); - - for (let index = 1; index < arguments.length; index++) { - const nextSource = arguments[index]; - - if (nextSource != null) { // Skip over if undefined or null - for (const nextKey in nextSource) { - // Avoid bugs when hasOwnProperty is shadowed - if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { - to[nextKey] = nextSource[nextKey]; - } - } - } - } - return to; - }, - writable: true, - configurable: true - }); +if (typeof (Dropzone) !== 'undefined') { + Dropzone.autoDiscover = false; } function initCommentPreviewTab($form) { - const $tabMenu = $form.find('.tabular.menu'); - $tabMenu.find('.item').tab(); - $tabMenu.find('.item[data-tab="' + $tabMenu.data('preview') + '"]').click(function () { - const $this = $(this); - $.post($this.data('url'), { - "_csrf": csrf, - "mode": "gfm", - "context": $this.data('context'), - "text": $form.find('.tab.segment[data-tab="' + $tabMenu.data('write') + '"] textarea').val() - }, - function (data) { - const $previewPanel = $form.find('.tab.segment[data-tab="' + $tabMenu.data('preview') + '"]'); - $previewPanel.html(data); - emojify.run($previewPanel[0]); - $('pre code', $previewPanel[0]).each(function () { - hljs.highlightBlock(this); - }); - } - ); + const $tabMenu = $form.find('.tabular.menu'); + $tabMenu.find('.item').tab(); + $tabMenu.find(`.item[data-tab="${$tabMenu.data('preview')}"]`).click(function () { + const $this = $(this); + $.post($this.data('url'), { + _csrf: csrf, + mode: 'gfm', + context: $this.data('context'), + text: $form.find(`.tab.segment[data-tab="${$tabMenu.data('write')}"] textarea`).val() + }, (data) => { + const $previewPanel = $form.find(`.tab.segment[data-tab="${$tabMenu.data('preview')}"]`); + $previewPanel.html(data); + emojify.run($previewPanel[0]); + $('pre code', $previewPanel[0]).each(function () { + hljs.highlightBlock(this); + }); }); + }); - buttonsClickOnEnter(); + 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.click(function () { - const $this = $(this); - $.post($this.data('url'), { - "_csrf": csrf, - "mode": "gfm", - "context": $this.data('context'), - "text": $form.find('.tab.segment[data-tab="' + $tabMenu.data('write') + '"] textarea').val() - }, - function (data) { - const $previewPanel = $form.find('.tab.segment[data-tab="' + $tabMenu.data('preview') + '"]'); - $previewPanel.html(data); - emojify.run($previewPanel[0]); - $('pre code', $previewPanel[0]).each(function () { - hljs.highlightBlock(this); - }); - } - ); + 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.click(function () { + const $this = $(this); + $.post($this.data('url'), { + _csrf: csrf, + mode: 'gfm', + context: $this.data('context'), + text: $form.find(`.tab.segment[data-tab="${$tabMenu.data('write')}"] textarea`).val() + }, (data) => { + const $previewPanel = $form.find(`.tab.segment[data-tab="${$tabMenu.data('preview')}"]`); + $previewPanel.html(data); + emojify.run($previewPanel[0]); + $('pre code', $previewPanel[0]).each(function () { + hljs.highlightBlock(this); }); - } + }); + }); + } } function initEditDiffTab($form) { - const $tabMenu = $form.find('.tabular.menu'); - $tabMenu.find('.item').tab(); - $tabMenu.find('.item[data-tab="' + $tabMenu.data('diff') + '"]').click(function () { - const $this = $(this); - $.post($this.data('url'), { - "_csrf": csrf, - "context": $this.data('context'), - "content": $form.find('.tab.segment[data-tab="' + $tabMenu.data('write') + '"] textarea').val() - }, - function (data) { - const $diffPreviewPanel = $form.find('.tab.segment[data-tab="' + $tabMenu.data('diff') + '"]'); - $diffPreviewPanel.html(data); - emojify.run($diffPreviewPanel[0]); - } - ); + const $tabMenu = $form.find('.tabular.menu'); + $tabMenu.find('.item').tab(); + $tabMenu.find(`.item[data-tab="${$tabMenu.data('diff')}"]`).click(function () { + const $this = $(this); + $.post($this.data('url'), { + _csrf: csrf, + context: $this.data('context'), + content: $form.find(`.tab.segment[data-tab="${$tabMenu.data('write')}"] textarea`).val() + }, (data) => { + const $diffPreviewPanel = $form.find(`.tab.segment[data-tab="${$tabMenu.data('diff')}"]`); + $diffPreviewPanel.html(data); + emojify.run($diffPreviewPanel[0]); }); + }); } function initEditForm() { - if ($('.edit.form').length == 0) { - return; - } + if ($('.edit.form').length === 0) { + return; + } - initEditPreviewTab($('.edit.form')); - initEditDiffTab($('.edit.form')); + initEditPreviewTab($('.edit.form')); + initEditDiffTab($('.edit.form')); } function initBranchSelector() { - const $selectBranch = $('.ui.select-branch') - const $branchMenu = $selectBranch.find('.reference-list-menu'); - $branchMenu.find('.item:not(.no-select)').click(function () { - const selectedValue = $(this).data('id'); - $($(this).data('id-selector')).val(selectedValue); - $selectBranch.find('.ui .branch-name').text(selectedValue); - }); - $selectBranch.find('.reference.column').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; - }); + const $selectBranch = $('.ui.select-branch'); + const $branchMenu = $selectBranch.find('.reference-list-menu'); + $branchMenu.find('.item:not(.no-select)').click(function () { + const selectedValue = $(this).data('id'); + $($(this).data('id-selector')).val(selectedValue); + $selectBranch.find('.ui .branch-name').text(selectedValue); + }); + $selectBranch.find('.reference.column').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 updateIssuesMeta(url, action, issueIds, elementId) { - return new Promise(function(resolve) { - $.ajax({ - type: "POST", - url: url, - data: { - "_csrf": csrf, - "action": action, - "issue_ids": issueIds, - "id": elementId - }, - success: resolve - }) - }) + 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(); - if (migrating) { - const repo_name = migrating.attr('repo'); - if (typeof repo_name === 'undefined') { - return - } - $.ajax({ - type: "GET", - url: suburl +"/"+repo_name+"/status", - data: { - "_csrf": csrf, - }, - complete: function(xhr) { - if (xhr.status == 200) { - if (xhr.responseJSON) { - if (xhr.responseJSON["status"] == 0) { - location.reload(); - return - } - - setTimeout(function () { - initRepoStatusChecker() - }, 2000); - return - } - } - $('#repo_migrating_progress').hide(); - $('#repo_migrating_failed').show(); - } - }) + const migrating = $('#repo_migrating'); + $('#repo_migrating_failed').hide(); + if (migrating) { + const repo_name = migrating.attr('repo'); + if (typeof repo_name === 'undefined') { + return; } + $.ajax({ + type: 'GET', + url: `${suburl}/${repo_name}/status`, + data: { + _csrf: csrf, + }, + complete(xhr) { + if (xhr.status === 200) { + if (xhr.responseJSON) { + if (xhr.responseJSON.status === 0) { + window.location.reload(); + return; + } + + setTimeout(() => { + initRepoStatusChecker(); + }, 2000); + return; + } + } + $('#repo_migrating_progress').hide(); + $('#repo_migrating_failed').show(); + } + }); + } } function initReactionSelector(parent) { - let reactions = ''; - if (!parent) { - parent = $(document); - reactions = '.reactions > '; - } + let reactions = ''; + if (!parent) { + parent = $(document); + reactions = '.reactions > '; + } - parent.find(reactions + 'a.label').popup({'position': 'bottom left', 'metadata': {'content': 'title', 'title': 'none'}}); + 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){ - const vm = this; - e.preventDefault(); + parent.find(`.select-reaction > .menu > .item, ${reactions}a.label`).on('click', function (e) { + const vm = this; + 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: url, - data: { - '_csrf': csrf, - 'content': $(this).data('content') - } - }).done(function(resp) { - if (resp && (resp.html || resp.empty)) { - const content = $(vm).closest('.content'); - let react = content.find('.segment.reactions'); - if (!resp.empty && react.length > 0) { - react.remove(); - } - if (!resp.empty) { - react = $('<div class="ui attached segment reactions"></div>'); - const attachments = content.find('.segment.bottom:first'); - if (attachments.length > 0) { - react.insertBefore(attachments); - } else { - react.appendTo(content); - } - react.html(resp.html); - const hasEmoji = react.find('.has-emoji'); - for (let i = 0; i < hasEmoji.length; i++) { - emojify.run(hasEmoji.get(i)); - } - react.find('.dropdown').dropdown(); - initReactionSelector(react); - } - } - }); - }); + 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 = $(vm).closest('.content'); + let react = content.find('.segment.reactions'); + if (!resp.empty && react.length > 0) { + react.remove(); + } + if (!resp.empty) { + react = $('<div class="ui attached segment reactions"></div>'); + const attachments = content.find('.segment.bottom:first'); + if (attachments.length > 0) { + react.insertBefore(attachments); + } else { + react.appendTo(content); + } + react.html(resp.html); + const hasEmoji = react.find('.has-emoji'); + for (let i = 0; i < hasEmoji.length; i++) { + emojify.run(hasEmoji.get(i)); + } + 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) + 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; - } + 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); - } + 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 retrieveImageFromClipboardAsBlob(pasteEvent, callback){ - if (!pasteEvent.clipboardData) { - return; - } +function retrieveImageFromClipboardAsBlob(pasteEvent, callback) { + if (!pasteEvent.clipboardData) { + return; + } - const items = pasteEvent.clipboardData.items; - if (typeof(items) === "undefined") { - return; - } + const { items } = pasteEvent.clipboardData; + if (typeof items === 'undefined') { + return; + } - for (let i = 0; i < items.length; i++) { - if (items[i].type.indexOf("image") === -1) continue; - const blob = items[i].getAsFile(); + for (let i = 0; i < items.length; i++) { + if (items[i].type.indexOf('image') === -1) continue; + const blob = items[i].getAsFile(); - if (typeof(callback) === "function") { - pasteEvent.preventDefault(); - pasteEvent.stopPropagation(); - callback(blob); - } + if (typeof (callback) === 'function') { + pasteEvent.preventDefault(); + pasteEvent.stopPropagation(); + callback(blob); } + } } function uploadFile(file, callback) { - const xhr = new XMLHttpRequest(); + const xhr = new XMLHttpRequest(); - xhr.onload = function() { - if (xhr.status == 200) { - callback(xhr.responseText); - } - }; + xhr.onload = function () { + if (xhr.status === 200) { + callback(xhr.responseText); + } + }; - xhr.open("post", suburl + "/attachments", true); - xhr.setRequestHeader("X-Csrf-Token", csrf); - const formData = new FormData(); - formData.append('file', file, file.name); - xhr.send(formData); + xhr.open('post', `${suburl}/attachments`, true); + xhr.setRequestHeader('X-Csrf-Token', csrf); + const formData = new FormData(); + formData.append('file', file, file.name); + xhr.send(formData); } function reload() { - window.location.reload(); + window.location.reload(); } function initImagePaste(target) { - target.each(function() { - const field = this; - field.addEventListener('paste', function(event){ - retrieveImageFromClipboardAsBlob(event, function(img) { - const name = img.name.substr(0, img.name.lastIndexOf('.')); - insertAtCursor(field, '![' + name + ']()'); - uploadFile(img, function(res) { - const data = JSON.parse(res); - replaceAndKeepCursor(field, '![' + name + ']()', '![' + name + '](' + suburl + '/attachments/' + data.uuid + ')'); - const input = $('<input id="' + data.uuid + '" name="files" type="hidden">').val(data.uuid); - $('.files').append(input); - }); - }); - }, false); - }); + target.each(function () { + const field = this; + field.addEventListener('paste', (event) => { + retrieveImageFromClipboardAsBlob(event, (img) => { + const name = img.name.substr(0, img.name.lastIndexOf('.')); + insertAtCursor(field, `![${name}]()`); + uploadFile(img, (res) => { + const data = JSON.parse(res); + replaceAndKeepCursor(field, `![${name}]()`, `![${name}](${suburl}/attachments/${data.uuid})`); + const input = $(`<input id="${data.uuid}" name="files" type="hidden">`).val(data.uuid); + $('.files').append(input); + }); + }); + }, false); + }); } function initCommentForm() { - if ($('.comment.form').length == 0) { - return - } - - initBranchSelector(); - initCommentPreviewTab($('.comment.form')); - initImagePaste($('.comment.form textarea')); - - // Listsubmit - function initListSubmits(selector, outerSelector) { - const $list = $('.ui.' + outerSelector + '.list'); - const $noSelect = $list.find('.no-select'); - const $listMenu = $('.' + selector + ' .menu'); - let hasLabelUpdateAction = $listMenu.data('action') == 'update'; - const labels = {}; - - $('.' + selector).dropdown('setting', 'onHide', function(){ - hasLabelUpdateAction = $listMenu.data('action') == 'update'; // Update the var - if (hasLabelUpdateAction) { - const promises = []; - Object.keys(labels).forEach(function(elementId) { - const label = labels[elementId]; - const promise = updateIssuesMeta( - label["update-url"], - label["action"], - label["issue-id"], - elementId - ); - promises.push(promise); - }); - Promise.all(promises).then(reload); - } - }); - - $listMenu.find('.item:not(.no-select)').click(function () { - - // we don't need the action attribute when updating assignees - if (selector == 'select-assignees-modify') { - - // UI magic. We need to do this here, otherwise it would destroy the functionality of - // adding/removing labels - if ($(this).hasClass('checked')) { - $(this).removeClass('checked'); - $(this).find('.octicon').removeClass('octicon-check'); - } else { - $(this).addClass('checked'); - $(this).find('.octicon').addClass('octicon-check'); - } - - updateIssuesMeta( - $listMenu.data('update-url'), - "", - $listMenu.data('issue-id'), - $(this).data('id') - ); - $listMenu.data('action', 'update'); // Update to reload the page when we updated items - return false; - } - - if ($(this).hasClass('checked')) { - $(this).removeClass('checked'); - $(this).find('.octicon').removeClass('octicon-check'); - if (hasLabelUpdateAction) { - if (!($(this).data('id') in labels)) { - labels[$(this).data('id')] = { - "update-url": $listMenu.data('update-url'), - "action": "detach", - "issue-id": $listMenu.data('issue-id'), - }; - } else { - delete labels[$(this).data('id')]; - } - } - } else { - $(this).addClass('checked'); - $(this).find('.octicon').addClass('octicon-check'); - if (hasLabelUpdateAction) { - if (!($(this).data('id') in labels)) { - labels[$(this).data('id')] = { - "update-url": $listMenu.data('update-url'), - "action": "attach", - "issue-id": $listMenu.data('issue-id'), - }; - } else { - delete labels[$(this).data('id')]; - } - } - } - - 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').click(function () { - if (hasLabelUpdateAction || selector == 'select-assignees-modify') { - updateIssuesMeta( - $listMenu.data('update-url'), - "clear", - $listMenu.data('issue-id'), - "" - ).then(reload); - } - - $(this).parent().find('.item').each(function () { - $(this).removeClass('checked'); - $(this).find('.octicon').removeClass('octicon-check'); - }); - - $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'); - - 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)').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); - } - switch (input_id) { - case '#milestone_id': - $list.find('.selected').html('<a class="item" href=' + $(this).data('href') + '>' + - htmlEncode($(this).text()) + '</a>'); - break; - case '#assignee_id': - $list.find('.selected').html('<a class="item" href=' + $(this).data('href') + '>' + - '<img class="ui avatar image" src=' + $(this).data('avatar') + '>' + - htmlEncode($(this).text()) + '</a>'); - } - $('.ui' + select_id + '.list .no-select').addClass('hide'); - $(input_id).val($(this).data('id')); + if ($('.comment.form').length === 0) { + return; + } + + initBranchSelector(); + initCommentPreviewTab($('.comment.form')); + initImagePaste($('.comment.form textarea')); + + // Listsubmit + function initListSubmits(selector, outerSelector) { + const $list = $(`.ui.${outerSelector}.list`); + const $noSelect = $list.find('.no-select'); + const $listMenu = $(`.${selector} .menu`); + let hasLabelUpdateAction = $listMenu.data('action') === 'update'; + const labels = {}; + + $(`.${selector}`).dropdown('setting', 'onHide', () => { + hasLabelUpdateAction = $listMenu.data('action') === 'update'; // Update the var + if (hasLabelUpdateAction) { + const promises = []; + Object.keys(labels).forEach((elementId) => { + const label = labels[elementId]; + const promise = updateIssuesMeta( + label['update-url'], + label.action, + label['issue-id'], + elementId + ); + promises.push(promise); }); - $menu.find('.no-select.item').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); - } + Promise.all(promises).then(reload); + } + }); + + $listMenu.find('.item:not(.no-select)').click(function () { + // we don't need the action attribute when updating assignees + if (selector === 'select-assignees-modify') { + // UI magic. We need to do this here, otherwise it would destroy the functionality of + // adding/removing labels + if ($(this).hasClass('checked')) { + $(this).removeClass('checked'); + $(this).find('.octicon').removeClass('octicon-check'); + } else { + $(this).addClass('checked'); + $(this).find('.octicon').addClass('octicon-check'); + } - $list.find('.selected').html(''); - $list.find('.no-select').removeClass('hide'); - $(input_id).val(''); - }); - } + updateIssuesMeta( + $listMenu.data('update-url'), + '', + $listMenu.data('issue-id'), + $(this).data('id') + ); + $listMenu.data('action', 'update'); // Update to reload the page when we updated items + return false; + } + + if ($(this).hasClass('checked')) { + $(this).removeClass('checked'); + $(this).find('.octicon').removeClass('octicon-check'); + if (hasLabelUpdateAction) { + if (!($(this).data('id') in labels)) { + labels[$(this).data('id')] = { + 'update-url': $listMenu.data('update-url'), + action: 'detach', + 'issue-id': $listMenu.data('issue-id'), + }; + } else { + delete labels[$(this).data('id')]; + } + } + } else { + $(this).addClass('checked'); + $(this).find('.octicon').addClass('octicon-check'); + if (hasLabelUpdateAction) { + if (!($(this).data('id') in labels)) { + labels[$(this).data('id')] = { + 'update-url': $listMenu.data('update-url'), + action: 'attach', + 'issue-id': $listMenu.data('issue-id'), + }; + } else { + delete labels[$(this).data('id')]; + } + } + } - // Milestone and assignee - selectItem('.select-milestone', '#milestone_id'); - selectItem('.select-assignee', '#assignee_id'); + 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').click(function () { + if (hasLabelUpdateAction || selector === 'select-assignees-modify') { + updateIssuesMeta( + $listMenu.data('update-url'), + 'clear', + $listMenu.data('issue-id'), + '' + ).then(reload); + } + + $(this).parent().find('.item').each(function () { + $(this).removeClass('checked'); + $(this).find('.octicon').removeClass('octicon-check'); + }); + + $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'); + + 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)').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); + } + switch (input_id) { + case '#milestone_id': + $list.find('.selected').html(`<a class="item" href=${$(this).data('href')}>${ + htmlEncode($(this).text())}</a>`); + break; + case '#assignee_id': + $list.find('.selected').html(`<a class="item" href=${$(this).data('href')}>` + + `<img class="ui avatar image" src=${$(this).data('avatar')}>${ + htmlEncode($(this).text())}</a>`); + } + $(`.ui${select_id}.list .no-select`).addClass('hide'); + $(input_id).val($(this).data('id')); + }); + $menu.find('.no-select.item').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 and assignee + 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").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(); + 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').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' + }; - $('#pgsql_settings').toggle(dbType === "PostgreSQL"); - $('#mysql_settings').toggle(dbType === "MySQL"); - $.each(dbDefaults, function(_type, defaultHost) { - if ($('#db_host').val() == defaultHost) { - $('#db_host').val(dbDefaults[dbType]); - return false; - } - }); - }); + $('#sqlite_settings').hide(); + $('#sql_settings').show(); - // TODO: better handling of exclusive relations. - $('#offline-mode input').change(function () { - if ($(this).is(':checked')) { - $('#disable-gravatar').checkbox('check'); - $('#federated-avatar-lookup').checkbox('uncheck'); - } - }); - $('#disable-gravatar input').change(function () { - if ($(this).is(':checked')) { - $('#federated-avatar-lookup').checkbox('uncheck'); - } else { - $('#offline-mode').checkbox('uncheck'); - } - }); - $('#federated-avatar-lookup input').change(function () { - if ($(this).is(':checked')) { - $('#disable-gravatar').checkbox('uncheck'); - $('#offline-mode').checkbox('uncheck'); - } - }); - $('#enable-openid-signin input').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').change(function () { - if ($(this).is(':checked')) { - $('#enable-captcha').checkbox('uncheck'); - $('#enable-openid-signup').checkbox('uncheck'); - } else { - $('#enable-openid-signup').checkbox('check'); - } - }); - $('#enable-captcha input').change(function () { - if ($(this).is(':checked')) { - $('#disable-registration').checkbox('uncheck'); - } + $('#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; + } }); -} - -function initRepository() { - if ($('.repository').length == 0) { - return; - } + }); - function initFilterSearchDropdown(selector) { - const $dropdown = $(selector); - $dropdown.dropdown({ - fullTextSearch: true, - selectOnKeydown: false, - onChange: function (_text, _value, $choice) { - if ($choice.data('url')) { - window.location.href = $choice.data('url'); - } - }, - message: {noResults: $dropdown.data('no-results')} - }); + // TODO: better handling of exclusive relations. + $('#offline-mode input').change(function () { + if ($(this).is(':checked')) { + $('#disable-gravatar').checkbox('check'); + $('#federated-avatar-lookup').checkbox('uncheck'); } - - // File list and commits - if ($('.repository.file.list').length > 0 || - ('.repository.commits').length > 0) { - initFilterBranchTagDropdown('.choose.reference .dropdown'); + }); + $('#disable-gravatar input').change(function () { + if ($(this).is(':checked')) { + $('#federated-avatar-lookup').checkbox('uncheck'); + } else { + $('#offline-mode').checkbox('uncheck'); + } + }); + $('#federated-avatar-lookup input').change(function () { + if ($(this).is(':checked')) { + $('#disable-gravatar').checkbox('uncheck'); + $('#offline-mode').checkbox('uncheck'); + } + }); + $('#enable-openid-signin input').change(function () { + if ($(this).is(':checked')) { + if (!$('#disable-registration input').is(':checked')) { + $('#enable-openid-signup').checkbox('check'); + } + } else { + $('#enable-openid-signup').checkbox('uncheck'); } - - // Wiki - if ($('.repository.wiki.view').length > 0) { - initFilterSearchDropdown('.choose.page .dropdown'); + }); + $('#disable-registration input').change(function () { + if ($(this).is(':checked')) { + $('#enable-captcha').checkbox('uncheck'); + $('#enable-openid-signup').checkbox('uncheck'); + } else { + $('#enable-openid-signup').checkbox('check'); } - - // Options - if ($('.repository.settings.options').length > 0) { - $('#repo_name').keyup(function () { - const $prompt = $('#repo-name-change-prompt'); - if ($(this).val().toString().toLowerCase() != $(this).data('name').toString().toLowerCase()) { - $prompt.show(); - } else { - $prompt.hide(); - } - }); - - // Enable or select internal/external wiki system and issue tracker. - $('.enable-system').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').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'); - } - }); + }); + $('#enable-captcha input').change(function () { + if ($(this).is(':checked')) { + $('#disable-registration').checkbox('uncheck'); } + }); +} - // Labels - if ($('.repository.labels').length > 0) { - // Create label - const $newLabelPanel = $('.new-label.segment'); - $('.new-label.button').click(function () { - $newLabelPanel.show(); - }); - $('.new-label.segment .cancel').click(function () { - $newLabelPanel.hide(); - }); - - $('.color-picker').each(function () { - $(this).minicolors(); - }); - $('.precolors .color').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').click(function () { - $('#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')); - $('.minicolors-swatch-color').css("background-color", $(this).data('color')); - $('.edit-label.modal').modal({ - onApprove: function () { - $('.edit-label.form').submit(); +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') } + }); + } + + // File list and commits + if ($('.repository.file.list').length > 0 || ('.repository.commits').length > 0) { + initFilterBranchTagDropdown('.choose.reference .dropdown'); + } + + // Wiki + if ($('.repository.wiki.view').length > 0) { + initFilterSearchDropdown('.choose.page .dropdown'); + } + + // Options + if ($('.repository.settings.options').length > 0) { + $('#repo_name').keyup(function () { + const $prompt = $('#repo-name-change-prompt'); + if ($(this).val().toString().toLowerCase() !== $(this).data('name').toString().toLowerCase()) { + $prompt.show(); + } else { + $prompt.hide(); + } + }); + + // Enable or select internal/external wiki system and issue tracker. + $('.enable-system').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').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) { + // Create label + const $newLabelPanel = $('.new-label.segment'); + $('.new-label.button').click(() => { + $newLabelPanel.show(); + }); + $('.new-label.segment .cancel').click(() => { + $newLabelPanel.hide(); + }); + + $('.color-picker').each(function () { + $(this).minicolors(); + }); + $('.precolors .color').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').click(function () { + $('#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')); + $('.minicolors-swatch-color').css('background-color', $(this).data('color')); + $('.edit-label.modal').modal({ + onApprove() { + $('.edit-label.form').submit(); + } + }).modal('show'); + return false; + }); + } + + // Milestones + if ($('.repository.new.milestone').length > 0) { + const $datepicker = $('.milestone.datepicker'); + $datepicker.datetimepicker({ + lang: $datepicker.data('lang'), + inline: true, + timepicker: false, + startDate: $datepicker.data('start-date'), + formatDate: 'Y-m-d', + onSelectDate(ct) { + $('#deadline').val(ct.dateFormat('Y-m-d')); + } + }); + $('#clear-date').click(() => { + $('#deadline').val(''); + return false; + }); + } + + // 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(); + $('.in-edit').toggle(); + $editInput.focus(); + return false; + }; + $('#edit-title').click(editTitleToggle); + $('#cancel-edit-title').click(editTitleToggle); + $('#save-edit-title').click(editTitleToggle).click(function () { + if ($editInput.val().length === 0 || $editInput.val() === $issueTitle.text()) { + $editInput.val($issueTitle.text()); + return false; + } + + $.post($(this).data('update-url'), { + _csrf: csrf, + title: $editInput.val() + }, + (data) => { + $editInput.val(data.title); + $issueTitle.text(data.title); + reload(); + }); + return false; + }); + + // Edit issue or comment content + $('.edit-content').click(function () { + const $segment = $(this).parent().parent().parent() + .next(); + const $editContentZone = $segment.find('.edit-content-zone'); + const $renderContent = $segment.find('.render-content'); + const $rawContent = $segment.find('.raw-content'); + let $textarea; + + // Setup new form + if ($editContentZone.html().length === 0) { + $editContentZone.html($('#edit-content-form').html()); + $textarea = $editContentZone.find('textarea'); + issuesTribute.attach($textarea.get()); + emojiTribute.attach($textarea.get()); + + const $dropzone = $editContentZone.find('.dropzone'); + $dropzone.data('saved', false); + const $files = $editContentZone.find('.comment-files'); + if ($dropzone.length > 0) { + const filenameDict = {}; + $dropzone.dropzone({ + url: $dropzone.data('upload-url'), + headers: { 'X-Csrf-Token': csrf }, + maxFiles: $dropzone.data('max-file'), + maxFilesize: $dropzone.data('max-size'), + acceptedFiles: ($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'), + init() { + this.on('success', (file, data) => { + filenameDict[file.name] = { + uuid: data.uuid, + submitted: false + }; + const input = $(`<input id="${data.uuid}" name="files" type="hidden">`).val(data.uuid); + $files.append(input); + }); + this.on('removedfile', (file) => { + if (!(file.name in filenameDict)) { + return; } - }).modal('show'); - return false; - }); - } - - // Milestones - if ($('.repository.new.milestone').length > 0) { - const $datepicker = $('.milestone.datepicker'); - $datepicker.datetimepicker({ - lang: $datepicker.data('lang'), - inline: true, - timepicker: false, - startDate: $datepicker.data('start-date'), - formatDate: 'Y-m-d', - onSelectDate: function (ct) { - $('#deadline').val(ct.dateFormat('Y-m-d')); - } - }); - $('#clear-date').click(function () { - $('#deadline').val(''); - return false; - }); - } - - // 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(); - $('.in-edit').toggle(); - $editInput.focus(); - return false; - }; - $('#edit-title').click(editTitleToggle); - $('#cancel-edit-title').click(editTitleToggle); - $('#save-edit-title').click(editTitleToggle).click(function () { - if ($editInput.val().length == 0 || - $editInput.val() == $issueTitle.text()) { - $editInput.val($issueTitle.text()); - return false; - } - - $.post($(this).data('update-url'), { - "_csrf": csrf, - "title": $editInput.val() - }, - function (data) { - $editInput.val(data.title); - $issueTitle.text(data.title); - reload(); - }); - return false; - }); - - // Edit issue or comment content - $('.edit-content').click(function () { - const $segment = $(this).parent().parent().parent().next(); - const $editContentZone = $segment.find('.edit-content-zone'); - const $renderContent = $segment.find('.render-content'); - const $rawContent = $segment.find('.raw-content'); - let $textarea; - - // Setup new form - if ($editContentZone.html().length == 0) { - $editContentZone.html($('#edit-content-form').html()); - $textarea = $editContentZone.find('textarea'); - issuesTribute.attach($textarea.get()); - emojiTribute.attach($textarea.get()); - - const $dropzone = $editContentZone.find('.dropzone'); - $dropzone.data("saved", false); - const $files = $editContentZone.find('.comment-files'); - if ($dropzone.length > 0) { - const filenameDict = {}; - $dropzone.dropzone({ - url: $dropzone.data('upload-url'), - headers: {"X-Csrf-Token": csrf}, - maxFiles: $dropzone.data('max-file'), - maxFilesize: $dropzone.data('max-size'), - acceptedFiles: ($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'), - init: function () { - this.on("success", function (file, data) { - filenameDict[file.name] = { - "uuid": data.uuid, - "submitted": false - } - const input = $('<input id="' + data.uuid + '" name="files" type="hidden">').val(data.uuid); - $files.append(input); - }); - this.on("removedfile", function (file) { - if (!(file.name in filenameDict)) { - return; - } - $('#' + filenameDict[file.name].uuid).remove(); - if ($dropzone.data('remove-url') && $dropzone.data('csrf') && !filenameDict[file.name].submitted) { - $.post($dropzone.data('remove-url'), { - file: filenameDict[file.name].uuid, - _csrf: $dropzone.data('csrf') - }); - } - }); - this.on("submit", function () { - $.each(filenameDict, function(name){ - filenameDict[name].submitted = true; - }); - }); - this.on("reload", function (){ - $.getJSON($editContentZone.data('attachment-url'), function(data){ - const drop = $dropzone.get(0).dropzone; - drop.removeAllFiles(true); - $files.empty(); - $.each(data, function(){ - const imgSrc = $dropzone.data('upload-url') + "/" + this.uuid; - drop.emit("addedfile", this); - drop.emit("thumbnail", this, imgSrc); - drop.emit("complete", this); - drop.files.push(this); - filenameDict[this.name] = { - "submitted": true, - "uuid": this.uuid - } - $dropzone.find("img[src='" + imgSrc + "']").css("max-width", "100%"); - const input = $('<input id="' + this.uuid + '" name="files" type="hidden">').val(this.uuid); - $files.append(input); - }); - }); - }); - } - }); - $dropzone.get(0).dropzone.emit("reload"); + $(`#${filenameDict[file.name].uuid}`).remove(); + if ($dropzone.data('remove-url') && $dropzone.data('csrf') && !filenameDict[file.name].submitted) { + $.post($dropzone.data('remove-url'), { + file: filenameDict[file.name].uuid, + _csrf: $dropzone.data('csrf') + }); } - // 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.segment').attr('data-tab', $editContentZone.data('write')); - $editContentForm.find('.preview.segment').attr('data-tab', $editContentZone.data('preview')); - - initCommentPreviewTab($editContentForm); - - $editContentZone.find('.cancel.button').click(function () { - $renderContent.show(); - $editContentZone.hide(); - $dropzone.get(0).dropzone.emit("reload"); + }); + this.on('submit', () => { + $.each(filenameDict, (name) => { + filenameDict[name].submitted = true; }); - $editContentZone.find('.save.button').click(function () { - $renderContent.show(); - $editContentZone.hide(); - const $attachments = $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 - }, - function (data) { - if (data.length == 0) { - $renderContent.html($('#no-content').html()); - } else { - $renderContent.html(data.content); - emojify.run($renderContent[0]); - $('pre code', $renderContent[0]).each(function () { - hljs.highlightBlock(this); - }); - } - const $content = $segment.parent(); - if(!$content.find(".ui.small.images").length){ - if(data.attachments != ""){ - $content.append( - '<div class="ui bottom attached segment">' + - ' <div class="ui small images">' + - ' </div>' + - '</div>' - ); - $content.find(".ui.small.images").html(data.attachments); - } - } else if (data.attachments == "") { - $content.find(".ui.small.images").parent().remove(); - } else { - $content.find(".ui.small.images").html(data.attachments); - } - $dropzone.get(0).dropzone.emit("submit"); - $dropzone.get(0).dropzone.emit("reload"); - }); + }); + this.on('reload', () => { + $.getJSON($editContentZone.data('attachment-url'), (data) => { + const drop = $dropzone.get(0).dropzone; + drop.removeAllFiles(true); + $files.empty(); + $.each(data, function () { + const imgSrc = `${$dropzone.data('upload-url')}/${this.uuid}`; + drop.emit('addedfile', this); + drop.emit('thumbnail', this, imgSrc); + drop.emit('complete', this); + drop.files.push(this); + filenameDict[this.name] = { + submitted: true, + uuid: this.uuid + }; + $dropzone.find(`img[src='${imgSrc}']`).css('max-width', '100%'); + const input = $(`<input id="${this.uuid}" name="files" type="hidden">`).val(this.uuid); + $files.append(input); + }); }); - } else { - $textarea = $segment.find('textarea'); - } - - // Show write/preview tab and copy raw content as needed - $editContentZone.show(); - $renderContent.hide(); - if ($textarea.val().length == 0) { - $textarea.val($rawContent.text()); - } - $textarea.focus(); - return false; + }); + } + }); + $dropzone.get(0).dropzone.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.segment').attr('data-tab', $editContentZone.data('write')); + $editContentForm.find('.preview.segment').attr('data-tab', $editContentZone.data('preview')); + + initCommentPreviewTab($editContentForm); + + $editContentZone.find('.cancel.button').click(() => { + $renderContent.show(); + $editContentZone.hide(); + $dropzone.get(0).dropzone.emit('reload'); }); - - // Delete comment - $('.delete-comment').click(function () { - const $this = $(this); - if (confirm($this.data('locale'))) { - $.post($this.data('url'), { - "_csrf": csrf - }).success(function () { - $('#' + $this.data('comment-id')).remove(); - }); - } - return false; - }); - - // Change status - const $statusButton = $('#status-button'); - $('#comment-form .edit_area').keyup(function () { - if ($(this).val().length == 0) { - $statusButton.text($statusButton.data('status')) + $editContentZone.find('.save.button').click(() => { + $renderContent.show(); + $editContentZone.hide(); + const $attachments = $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) { + $renderContent.html($('#no-content').html()); } else { - $statusButton.text($statusButton.data('status-and-comment')) - } - }); - $statusButton.click(function () { - $('#status').val($statusButton.data('status-val')); - $('#comment-form').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(); - }); - $('.merge-button > .dropdown').dropdown({ - onChange: function (_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(); - }); - - initReactionSelector(); - } - - // Diff - if ($('.repository.diff').length > 0) { - $('.diff-counter').each(function () { - const $item = $(this); - const addLine = $item.find('span[data-line].add').data("line"); - const delLine = $item.find('span[data-line].del').data("line"); - const addPercent = parseFloat(addLine) / (parseFloat(addLine) + parseFloat(delLine)) * 100; - $item.find(".bar .add").css("width", addPercent + "%"); - }); - } - - // Quick start and repository home - $('#repo-clone-ssh').click(function () { - $('.clone-url').text($(this).data('link')); - $('#repo-clone-url').val($(this).data('link')); - $(this).addClass('blue'); - $('#repo-clone-https').removeClass('blue'); - localStorage.setItem('repo-clone-protocol', 'ssh'); - }); - $('#repo-clone-https').click(function () { - $('.clone-url').text($(this).data('link')); - $('#repo-clone-url').val($(this).data('link')); - $(this).addClass('blue'); - $('#repo-clone-ssh').removeClass('blue'); - localStorage.setItem('repo-clone-protocol', 'https'); - }); - $('#repo-clone-url').click(function () { - $(this).select(); - }); - - // Pull request - const $repoComparePull = $('.repository.compare.pull'); - if ($repoComparePull.length > 0) { - initFilterSearchDropdown('.choose.branch .dropdown'); - // show pull request form - $repoComparePull.find('button.show-form').on('click', function(e) { - e.preventDefault(); - $repoComparePull.find('.pullrequest-form').show(); - $(this).parent().hide(); - }); - } - - // Branches - if ($('.repository.settings.branches').length > 0) { - initFilterSearchDropdown('.protected-branches .dropdown'); - $('.enable-protection, .enable-whitelist').change(function () { - if (this.checked) { - $($(this).data('target')).removeClass('disabled'); + $renderContent.html(data.content); + emojify.run($renderContent[0]); + $('pre code', $renderContent[0]).each(function () { + hljs.highlightBlock(this); + }); + } + const $content = $segment.parent(); + if (!$content.find('.ui.small.images').length) { + if (data.attachments !== '') { + $content.append( + '<div class="ui bottom attached segment"><div class="ui small images"></div></div>' + ); + $content.find('.ui.small.images').html(data.attachments); + } + } else if (data.attachments === '') { + $content.find('.ui.small.images').parent().remove(); } else { - $($(this).data('target')).addClass('disabled'); + $content.find('.ui.small.images').html(data.attachments); } + $dropzone.get(0).dropzone.emit('submit'); + $dropzone.get(0).dropzone.emit('reload'); + }); }); - } + } else { + $textarea = $segment.find('textarea'); + } + + // Show write/preview tab and copy raw content as needed + $editContentZone.show(); + $renderContent.hide(); + if ($textarea.val().length === 0) { + $textarea.val($rawContent.text()); + } + $textarea.focus(); + return false; + }); + + // Delete comment + $('.delete-comment').click(function () { + const $this = $(this); + if (window.confirm($this.data('locale'))) { + $.post($this.data('url'), { + _csrf: csrf + }).success(() => { + $(`#${$this.data('comment-id')}`).remove(); + }); + } + return false; + }); + + // Change status + const $statusButton = $('#status-button'); + $('#comment-form .edit_area').keyup(function () { + if ($(this).val().length === 0) { + $statusButton.text($statusButton.data('status')); + } else { + $statusButton.text($statusButton.data('status-and-comment')); + } + }); + $statusButton.click(() => { + $('#status').val($statusButton.data('status-val')); + $('#comment-form').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(); + }); + $('.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(); + }); + + initReactionSelector(); + } + + // Diff + if ($('.repository.diff').length > 0) { + $('.diff-counter').each(function () { + const $item = $(this); + const addLine = $item.find('span[data-line].add').data('line'); + const delLine = $item.find('span[data-line].del').data('line'); + const addPercent = parseFloat(addLine) / (parseFloat(addLine) + parseFloat(delLine)) * 100; + $item.find('.bar .add').css('width', `${addPercent}%`); + }); + } + + // Quick start and repository home + $('#repo-clone-ssh').click(function () { + $('.clone-url').text($(this).data('link')); + $('#repo-clone-url').val($(this).data('link')); + $(this).addClass('blue'); + $('#repo-clone-https').removeClass('blue'); + localStorage.setItem('repo-clone-protocol', 'ssh'); + }); + $('#repo-clone-https').click(function () { + $('.clone-url').text($(this).data('link')); + $('#repo-clone-url').val($(this).data('link')); + $(this).addClass('blue'); + $('#repo-clone-ssh').removeClass('blue'); + localStorage.setItem('repo-clone-protocol', 'https'); + }); + $('#repo-clone-url').click(function () { + $(this).select(); + }); + + // Pull request + const $repoComparePull = $('.repository.compare.pull'); + if ($repoComparePull.length > 0) { + initFilterSearchDropdown('.choose.branch .dropdown'); + // show pull request form + $repoComparePull.find('button.show-form').on('click', function (e) { + e.preventDefault(); + $repoComparePull.find('.pullrequest-form').show(); + $(this).parent().hide(); + }); + } + + // Branches + if ($('.repository.settings.branches').length > 0) { + initFilterSearchDropdown('.protected-branches .dropdown'); + $('.enable-protection, .enable-whitelist').change(function () { + if (this.checked) { + $($(this).data('target')).removeClass('disabled'); + } else { + $($(this).data('target')).addClass('disabled'); + } + }); + } } function initMigration() { - const toggleMigrations = function() { - const authUserName = $('#auth_username').val(); - const cloneAddr = $('#clone_addr').val(); - if (!$('#mirror').is(":checked") && (authUserName!=undefined && authUserName.length > 0) - && (cloneAddr!=undefined && (cloneAddr.startsWith("https://github.com") || cloneAddr.startsWith("http://github.com")))) { - $('#migrate_items').show(); - } else { - $('#migrate_items').hide(); - } + const toggleMigrations = function () { + const authUserName = $('#auth_username').val(); + const cloneAddr = $('#clone_addr').val(); + if (!$('#mirror').is(':checked') && (authUserName && authUserName.length > 0) + && (cloneAddr !== undefined && (cloneAddr.startsWith('https://github.com') || cloneAddr.startsWith('http://github.com')))) { + $('#migrate_items').show(); + } else { + $('#migrate_items').hide(); } + }; - toggleMigrations(); + toggleMigrations(); - $('#clone_addr').on('input', toggleMigrations) - $('#auth_username').on('input', toggleMigrations) - $('#mirror').on('change', toggleMigrations) + $('#clone_addr').on('input', toggleMigrations); + $('#auth_username').on('input', toggleMigrations); + $('#mirror').on('change', toggleMigrations); } function initPullRequestReview() { - $('.show-outdated').on('click', 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'); - }); - - $('.hide-outdated').on('click', 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'); - }); - - $('button.comment-form-reply').on('click', function (e) { - e.preventDefault(); - $(this).hide(); - const form = $(this).parent().find('.comment-form') - form.removeClass('hide'); - assingMenuAttributes(form.find('.menu')); + $('.show-outdated').on('click', 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'); + }); + + $('.hide-outdated').on('click', 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'); + }); + + $('button.comment-form-reply').on('click', function (e) { + e.preventDefault(); + $(this).hide(); + const form = $(this).parent().find('.comment-form'); + form.removeClass('hide'); + assingMenuAttributes(form.find('.menu')); + }); + // The following part is only for diff views + if ($('.repository.pull.diff').length === 0) { + return; + } + + $('.diff-detail-box.ui.sticky').sticky(); + + $('.btn-review').on('click', function (e) { + e.preventDefault(); + $(this).closest('.dropdown').find('.menu').toggle('visible'); + }).closest('.dropdown').find('.link.close') + .on('click', function (e) { + e.preventDefault(); + $(this).closest('.menu').toggle('visible'); + }); + + $('.code-view .lines-code,.code-view .lines-num') + .on('mouseenter', function () { + const parent = $(this).closest('td'); + $(this).closest('tr').addClass( + parent.hasClass('lines-num-old') || parent.hasClass('lines-code-old') + ? 'focus-lines-old' : 'focus-lines-new' + ); + }) + .on('mouseleave', function () { + $(this).closest('tr').removeClass('focus-lines-new focus-lines-old'); }); - // The following part is only for diff views - if ($('.repository.pull.diff').length == 0) { - return; + $('.add-code-comment').on('click', function (e) { + // https://github.com/go-gitea/gitea/issues/4745 + if ($(e.target).hasClass('btn-add-single')) { + return; } - - $('.diff-detail-box.ui.sticky').sticky(); - - $('.btn-review').on('click', function(e) { - e.preventDefault(); - $(this).closest('.dropdown').find('.menu').toggle('visible'); - }).closest('.dropdown').find('.link.close').on('click', function(e) { - e.preventDefault(); - $(this).closest('.menu').toggle('visible'); - }); - - $('.code-view .lines-code,.code-view .lines-num') - .on('mouseenter', function() { - const parent = $(this).closest('td'); - $(this).closest('tr').addClass( - parent.hasClass('lines-num-old') || parent.hasClass('lines-code-old') - ? 'focus-lines-old' : 'focus-lines-new' - ); - }) - .on('mouseleave', function() { - $(this).closest('tr').removeClass('focus-lines-new focus-lines-old'); - }); - $('.add-code-comment').on('click', function(e) { - // https://github.com/go-gitea/gitea/issues/4745 - if ($(e.target).hasClass('btn-add-single')) { - return; - } - 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 form = $('#pull_review_add_comment').html(); - const tr = $(this).closest('tr'); - let ntr = tr.next(); - if (!ntr.hasClass('add-comment')) { - ntr = $('<tr class="add-comment">' - + (isSplit ? '<td class="lines-num"></td><td class="lines-type-marker"></td><td class="add-comment-left"></td><td class="lines-num"></td><td class="lines-type-marker"></td><td class="add-comment-right"></td>' - : '<td class="lines-num"></td><td class="lines-num"></td><td class="lines-type-marker"></td><td class="add-comment-left add-comment-right"></td>') - + '</tr>'); - tr.after(ntr); - } - const td = ntr.find('.add-comment-' + side); - let commentCloud = td.find('.comment-code-cloud'); - if (commentCloud.length === 0) { - td.html(form); - commentCloud = td.find('.comment-code-cloud'); - assingMenuAttributes(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); - } - commentCloud.find('textarea').focus(); - }); + 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 form = $('#pull_review_add_comment').html(); + const tr = $(this).closest('tr'); + let ntr = tr.next(); + if (!ntr.hasClass('add-comment')) { + ntr = $(`<tr class="add-comment">${ + isSplit ? '<td class="lines-num"></td><td class="lines-type-marker"></td><td class="add-comment-left"></td><td class="lines-num"></td><td class="lines-type-marker"></td><td class="add-comment-right"></td>' + : '<td class="lines-num"></td><td class="lines-num"></td><td class="lines-type-marker"></td><td class="add-comment-left add-comment-right"></td>' + }</tr>`); + tr.after(ntr); + } + const td = ntr.find(`.add-comment-${side}`); + let commentCloud = td.find('.comment-code-cloud'); + if (commentCloud.length === 0) { + td.html(form); + commentCloud = td.find('.comment-code-cloud'); + assingMenuAttributes(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); + } + commentCloud.find('textarea').focus(); + }); } function assingMenuAttributes(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; + 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').click(function () { - const $menu = $(this).parent(); - $.post($menu.data('url'), { - "_csrf": csrf, - "uid": $menu.data('uid'), - "mode": $(this).data('value') - }) + // Change collaborator access mode + $('.access-mode.menu .item').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]').change(function () { - 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(); - } - }); + // Change team access mode + $('.organization.new.team input[name=permission]').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'); - if ($editArea.length > 0) { - const simplemde = new SimpleMDE({ - autoDownloadFontAwesome: false, - element: $editArea[0], - forceSync: true, - previewRender: function (plainText, preview) { // Async method - setTimeout(function () { - // FIXME: still send render request when return back to edit mode - $.post($editArea.data('url'), { - "_csrf": csrf, - "mode": "gfm", - "context": $editArea.data('context'), - "text": plainText - }, - function (data) { - preview.innerHTML = '<div class="markdown ui segment">' + data + '</div>'; - emojify.run($('.editor-preview')[0]); - } - ); - }, 0); - - return "Loading..."; - }, - 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: function(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: function(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: function(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"] - }) - $(simplemde.codemirror.getInputField()).addClass("js-quick-submit"); - } + const $editArea = $('.repository.wiki textarea#edit_area'); + if ($editArea.length > 0) { + const simplemde = new SimpleMDE({ + autoDownloadFontAwesome: false, + element: $editArea[0], + forceSync: true, + previewRender(plainText, preview) { // Async method + setTimeout(() => { + // FIXME: still send render request when return back to edit mode + $.post($editArea.data('url'), { + _csrf: csrf, + mode: 'gfm', + context: $editArea.data('context'), + text: plainText + }, (data) => { + preview.innerHTML = `<div class="markdown ui segment">${data}</div>`; + emojify.run($('.editor-preview')[0]); + }); + }, 0); + + return 'Loading...'; + }, + 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'] + }); + $(simplemde.codemirror.getInputField()).addClass('js-quick-submit'); + } } -// For IE -String.prototype.endsWith = function (pattern) { - const d = this.length - pattern.length; - return d >= 0 && this.lastIndexOf(pattern) === d; -}; - // 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; -} + 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 setSimpleMDE($editArea) { - if (codeMirrorEditor) { - codeMirrorEditor.toTextArea(); - codeMirrorEditor = null; - } + if (codeMirrorEditor) { + codeMirrorEditor.toTextArea(); + codeMirrorEditor = null; + } - if (simpleMDEditor) { - return true; - } - - simpleMDEditor = new SimpleMDE({ - autoDownloadFontAwesome: false, - element: $editArea[0], - forceSync: true, - renderingConfig: { - singleLineBreaks: false + if (simpleMDEditor) { + return true; + } + + simpleMDEditor = new SimpleMDE({ + autoDownloadFontAwesome: false, + element: $editArea[0], + forceSync: true, + renderingConfig: { + singleLineBreaks: false + }, + indentWithTabs: false, + tabSize: 4, + spellChecker: false, + previewRender(plainText, preview) { // Async method + setTimeout(() => { + // FIXME: still send render request when return back to edit mode + $.post($editArea.data('url'), { + _csrf: csrf, + mode: 'gfm', + context: $editArea.data('context'), + text: plainText }, - indentWithTabs: false, - tabSize: 4, - spellChecker: false, - previewRender: function (plainText, preview) { // Async method - setTimeout(function () { - // FIXME: still send render request when return back to edit mode - $.post($editArea.data('url'), { - "_csrf": csrf, - "mode": "gfm", - "context": $editArea.data('context'), - "text": plainText - }, - function (data) { - preview.innerHTML = '<div class="markdown ui segment">' + data + '</div>'; - emojify.run($('.editor-preview')[0]); - } - ); - }, 0); + (data) => { + preview.innerHTML = `<div class="markdown ui segment">${data}</div>`; + emojify.run($('.editor-preview')[0]); + }); + }, 0); - return "Loading..."; - }, - toolbar: ["bold", "italic", "strikethrough", "|", - "heading-1", "heading-2", "heading-3", "heading-bigger", "heading-smaller", "|", - "code", "quote", "|", - "unordered-list", "ordered-list", "|", - "link", "image", "table", "horizontal-rule", "|", - "clean-block", "preview", "fullscreen", "side-by-side"] - }); + return 'Loading...'; + }, + toolbar: ['bold', 'italic', 'strikethrough', '|', + 'heading-1', 'heading-2', 'heading-3', 'heading-bigger', 'heading-smaller', '|', + 'code', 'quote', '|', + 'unordered-list', 'ordered-list', '|', + 'link', 'image', 'table', 'horizontal-rule', '|', + 'clean-block', 'preview', 'fullscreen', 'side-by-side'] + }); - return true; + return true; } function setCodeMirror($editArea) { - if (simpleMDEditor) { - simpleMDEditor.toTextArea(); - simpleMDEditor = null; - } + if (simpleMDEditor) { + simpleMDEditor.toTextArea(); + simpleMDEditor = null; + } - if (codeMirrorEditor) { - return true; - } + if (codeMirrorEditor) { + return true; + } - codeMirrorEditor = CodeMirror.fromTextArea($editArea[0], { - lineNumbers: true - }); - codeMirrorEditor.on("change", function (cm, _change) { - $editArea.val(cm.getValue()); - }); + codeMirrorEditor = CodeMirror.fromTextArea($editArea[0], { + lineNumbers: true + }); + codeMirrorEditor.on('change', (cm, _change) => { + $editArea.val(cm.getValue()); + }); - return true; + return true; } function initEditor() { - $('.js-quick-pull-choice-option').change(function () { - if ($(this).val() == 'commit-to-new-branch') { - $('.quick-pull-branch-name').show(); - $('.quick-pull-branch-name input').prop('required',true); + $('.js-quick-pull-choice-option').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.keyup(function (e) { + const $section = $('.breadcrumb span.section'); + const $divider = $('.breadcrumb div.divider'); + let value; + let parts; + + if (e.keyCode === 8) { + if ($(this).getCursorPosition() === 0) { + if ($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) { + $(`<span class="section"><a href="#">${value}</a></span>`).insertBefore($(this)); + $('<div class="divider"> / </div>').insertBefore($(this)); + } } else { - $('.quick-pull-branch-name').hide(); - $('.quick-pull-branch-name input').prop('required',false); - } - $('#commit-button').text($(this).attr('button_text')); - }); + $(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 markdownFileExts = $editArea.data('markdown-file-exts').split(','); + const lineWrapExtensions = $editArea.data('line-wrap-extensions').split(','); + + $editFilename.on('keyup', () => { + const val = $editFilename.val(); + let mode, spec, extension, extWithDot, dataUrl, apiCall; + + extension = extWithDot = ''; + const m = /.+\.([^.]+)$/.exec(val); + if (m) { + extension = m[1]; + extWithDot = `.${extension}`; + } + + const info = CodeMirror.findModeByExtension(extension); + const previewLink = $('a[data-tab=preview]'); + if (info) { + mode = info.mode; + spec = info.mime; + apiCall = mode; + } else { + apiCall = extension; + } - const $editFilename = $("#file-name"); - $editFilename.keyup(function (e) { - const $section = $('.breadcrumb span.section'); - const $divider = $('.breadcrumb div.divider'); - let value; - let parts; - - if (e.keyCode == 8) { - if ($(this).getCursorPosition() == 0) { - if ($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) { - $('<span class="section"><a href="#">' + value + '</a></span>').insertBefore($(this)); - $('<div class="divider"> / </div>').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'); + if (previewLink.length && apiCall && previewFileModes && previewFileModes.length && previewFileModes.indexOf(apiCall) >= 0) { + dataUrl = previewLink.data('url'); + previewLink.data('url', dataUrl.replace(/(.*)\/.*/i, `$1/${mode}`)); + previewLink.show(); + } else { + previewLink.hide(); + } - const $editArea = $('.repository.editor textarea#edit_area'); - if (!$editArea.length) + // If this file is a Markdown extensions, we will load that editor and return + if (markdownFileExts.indexOf(extWithDot) >= 0) { + if (setSimpleMDE($editArea)) { return; + } + } - const markdownFileExts = $editArea.data("markdown-file-exts").split(","); - const lineWrapExtensions = $editArea.data("line-wrap-extensions").split(","); - - $editFilename.on("keyup", function () { - const val = $editFilename.val(); - let mode, spec, extension, extWithDot, dataUrl, apiCall; - - extension = extWithDot = ""; - const m = /.+\.([^.]+)$/.exec(val); - if (m) { - extension = m[1]; - extWithDot = "." + extension; - } - - const info = CodeMirror.findModeByExtension(extension); - const previewLink = $('a[data-tab=preview]'); - if (info) { - mode = info.mode; - spec = info.mime; - apiCall = mode; - } - else { - apiCall = extension - } - - if (previewLink.length && apiCall && previewFileModes && previewFileModes.length && previewFileModes.indexOf(apiCall) >= 0) { - dataUrl = previewLink.data('url'); - previewLink.data('url', dataUrl.replace(/(.*)\/.*/i, '$1/' + mode)); - previewLink.show(); - } - else { - previewLink.hide(); - } - - // If this file is a Markdown extensions, we will load that editor and return - if (markdownFileExts.indexOf(extWithDot) >= 0) { - if (setSimpleMDE($editArea)) { - return; - } - } + // Else we are going to use CodeMirror + if (!codeMirrorEditor && !setCodeMirror($editArea)) { + return; + } - // Else we are going to use CodeMirror - if (!codeMirrorEditor && !setCodeMirror($editArea)) { - return; - } + if (mode) { + codeMirrorEditor.setOption('mode', spec); + CodeMirror.autoLoadMode(codeMirrorEditor, mode); + } - if (mode) { - codeMirrorEditor.setOption("mode", spec); - CodeMirror.autoLoadMode(codeMirrorEditor, mode); - } + if (lineWrapExtensions.indexOf(extWithDot) >= 0) { + codeMirrorEditor.setOption('lineWrapping', true); + } else { + codeMirrorEditor.setOption('lineWrapping', false); + } + + // get the filename without any folder + let value = $editFilename.val(); + if (value.length === 0) { + return; + } + value = value.split('/'); + value = value[value.length - 1]; + + $.getJSON($editFilename.data('ec-url-prefix') + value, (editorconfig) => { + if (editorconfig.indent_style === 'tab') { + codeMirrorEditor.setOption('indentWithTabs', true); + codeMirrorEditor.setOption('extraKeys', {}); + } else { + codeMirrorEditor.setOption('indentWithTabs', false); + // required because CodeMirror doesn't seems to use spaces correctly for {"indentWithTabs": false}: + // - https://github.com/codemirror/CodeMirror/issues/988 + // - https://codemirror.net/doc/manual.html#keymaps + codeMirrorEditor.setOption('extraKeys', { + Tab(cm) { + const spaces = Array(parseInt(cm.getOption('indentUnit')) + 1).join(' '); + cm.replaceSelection(spaces); + } + }); + } + codeMirrorEditor.setOption('indentUnit', editorconfig.indent_size || 4); + codeMirrorEditor.setOption('tabSize', editorconfig.tab_width || 4); + }); + }).trigger('keyup'); - if (lineWrapExtensions.indexOf(extWithDot) >= 0) { - codeMirrorEditor.setOption("lineWrapping", true); - } - else { - codeMirrorEditor.setOption("lineWrapping", false); - } + // 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'; - // get the filename without any folder - let value = $editFilename.val(); - if (value.length === 0) { - return; - } - value = value.split('/'); - value = value[value.length - 1]; + // Disabling the button at the start + $commitButton.prop('disabled', true); - $.getJSON($editFilename.data('ec-url-prefix')+value, function(editorconfig) { - if (editorconfig.indent_style === 'tab') { - codeMirrorEditor.setOption("indentWithTabs", true); - codeMirrorEditor.setOption('extraKeys', {}); - } else { - codeMirrorEditor.setOption("indentWithTabs", false); - // required because CodeMirror doesn't seems to use spaces correctly for {"indentWithTabs": false}: - // - https://github.com/codemirror/CodeMirror/issues/988 - // - https://codemirror.net/doc/manual.html#keymaps - codeMirrorEditor.setOption('extraKeys', { - Tab: function(cm) { - const spaces = Array(parseInt(cm.getOption("indentUnit")) + 1).join(" "); - cm.replaceSelection(spaces); - } - }); - } - codeMirrorEditor.setOption("indentUnit", editorconfig.indent_size || 4); - codeMirrorEditor.setOption("tabSize", editorconfig.tab_width || 4); - }); - }).trigger('keyup'); - - // 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 - $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: function () { - const dirty = $(this).hasClass(dirtyFileClass); - $commitButton.prop('disabled', !dirty); - } - }); + // 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); + } + }); - $commitButton.click(function (event) { - // A modal which asks if an empty file should be committed - if ($editArea.val().length === 0) { - $('#edit-empty-content-modal').modal({ - onApprove: function () { - $('.edit.form').submit(); - } - }).modal('show'); - event.preventDefault(); + $commitButton.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').submit(); } - }); + }).modal('show'); + event.preventDefault(); + } + }); } function initOrganization() { - if ($('.organization').length == 0) { - return; - } + if ($('.organization').length === 0) { + return; + } - // Options - if ($('.organization.settings.options').length > 0) { - $('#org_name').keyup(function () { - const $prompt = $('#org-name-change-prompt'); - if ($(this).val().toString().toLowerCase() != $(this).data('org-name').toString().toLowerCase()) { - $prompt.show(); - } else { - $prompt.hide(); - } - }); - } + // Options + if ($('.organization.settings.options').length > 0) { + $('#org_name').keyup(function () { + const $prompt = $('#org-name-change-prompt'); + if ($(this).val().toString().toLowerCase() !== $(this).data('org-name').toString().toLowerCase()) { + $prompt.show(); + } else { + $prompt.hide(); + } + }); + } } function initUserSettings() { - // Options - if ($('.user.settings.profile').length > 0) { - $('#username').keyup(function () { - const $prompt = $('#name-change-prompt'); - if ($(this).val().toString().toLowerCase() != $(this).data('name').toString().toLowerCase()) { - $prompt.show(); - } else { - $prompt.hide(); - } - }); - } + // Options + if ($('.user.settings.profile').length > 0) { + $('#username').keyup(function () { + const $prompt = $('#name-change-prompt'); + if ($(this).val().toString().toLowerCase() !== $(this).data('name').toString().toLowerCase()) { + $prompt.show(); + } else { + $prompt.hide(); + } + }); + } } function initWebhook() { - if ($('.new.webhook').length == 0) { - return; - } - - $('.events.checkbox input').change(function () { - if ($(this).is(':checked')) { - $('.events.fields').show(); - } - }); - $('.non-events.checkbox input').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'](); - }; + if ($('.new.webhook').length === 0) { + return; + } + + $('.events.checkbox input').change(function () { + if ($(this).is(':checked')) { + $('.events.fields').show(); + } + }); + $('.non-events.checkbox input').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').change(() => { updateContentType(); - $('#http_method').change(function () { - updateContentType(); - }); + }); - // Test delivery - $('#test-delivery').click(function () { - const $this = $(this); - $this.addClass('loading disabled'); - $.post($this.data('link'), { - "_csrf": csrf - }).done( - setTimeout(function () { - window.location.href = $this.data('redirect'); - }, 5000) - ) - }); + // Test delivery + $('#test-delivery').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').change(function () { - if ($(this).val().substring(0, 1) == '0') { - $('#login_name').removeAttr('required'); - $('.non-local').hide(); - $('.local').show(); - $('#user_name').focus(); - - if ($(this).data('password') == "required") { - $('#password').attr('required', 'required'); - } - - } else { - $('#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() { - $('.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 'github': - case 'gitlab': - case 'gitea': - $('.oauth2_use_custom_url').show(); - break; - case 'openidConnect': - $('.open_id_connect_auto_discovery_url input').attr('required', 'required'); - $('.open_id_connect_auto_discovery_url').show(); - break; - } - onOAuth2UseCustomURLChange(); - } - - function onOAuth2UseCustomURLChange() { - 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')) { - if (!$('#oauth2_token_url').val()) { - $('#oauth2_token_url').val($('#' + provider + '_token_url').val()); - } - if (!$('#oauth2_auth_url').val()) { - $('#oauth2_auth_url').val($('#' + provider + '_auth_url').val()); - } - if (!$('#oauth2_profile_url').val()) { - $('#oauth2_profile_url').val($('#' + provider + '_profile_url').val()); - } - if (!$('#oauth2_email_url').val()) { - $('#oauth2_email_url').val($('#' + provider + '_email_url').val()); - } - switch (provider) { - case 'github': - $('.oauth2_token_url input, .oauth2_auth_url input, .oauth2_profile_url input, .oauth2_email_url input').attr('required', 'required'); - $('.oauth2_token_url, .oauth2_auth_url, .oauth2_profile_url, .oauth2_email_url').show(); - break; - case 'gitea': - case 'gitlab': - $('.oauth2_token_url input, .oauth2_auth_url input, .oauth2_profile_url input').attr('required', 'required'); - $('.oauth2_token_url, .oauth2_auth_url, .oauth2_profile_url').show(); - $('#oauth2_email_url').val(''); - break; - } - } + if ($('.admin').length === 0) { + return; + } + + // New user + if ($('.admin.new.user').length > 0 || $('.admin.edit.user').length > 0) { + $('#login_type').change(function () { + if ($(this).val().substring(0, 1) === '0') { + $('#login_name').removeAttr('required'); + $('.non-local').hide(); + $('.local').show(); + $('#user_name').focus(); + + if ($(this).data('password') === 'required') { + $('#password').attr('required', 'required'); + } + } else { + $('#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(); } + } - // New authentication - if ($('.admin.new.authentication').length > 0) { - $('#auth_type').change(function () { - $('.ldap, .dldap, .smtp, .pam, .oauth2, .has-tls .search-page-size').hide(); - - $('.ldap input[required], .binddnrequired input[required], .dldap input[required], .smtp input[required], .pam input[required], .oauth2 input[required], .has-tls 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(); - break; - } - if (authType == '2' || authType == '5') { - onSecurityProtocolChange() - } - if (authType == '2') { - onUsePagedSearchChange(); - } - }); - $('#auth_type').change(); - $('#security_protocol').change(onSecurityProtocolChange); + 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() { + $('.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 'github': + case 'gitlab': + case 'gitea': + $('.oauth2_use_custom_url').show(); + break; + case 'openidConnect': + $('.open_id_connect_auto_discovery_url input').attr('required', 'required'); + $('.open_id_connect_auto_discovery_url').show(); + break; + } + onOAuth2UseCustomURLChange(); + } + + function onOAuth2UseCustomURLChange() { + 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')) { + if (!$('#oauth2_token_url').val()) { + $('#oauth2_token_url').val($(`#${provider}_token_url`).val()); + } + if (!$('#oauth2_auth_url').val()) { + $('#oauth2_auth_url').val($(`#${provider}_auth_url`).val()); + } + if (!$('#oauth2_profile_url').val()) { + $('#oauth2_profile_url').val($(`#${provider}_profile_url`).val()); + } + if (!$('#oauth2_email_url').val()) { + $('#oauth2_email_url').val($(`#${provider}_email_url`).val()); + } + switch (provider) { + case 'github': + $('.oauth2_token_url input, .oauth2_auth_url input, .oauth2_profile_url input, .oauth2_email_url input').attr('required', 'required'); + $('.oauth2_token_url, .oauth2_auth_url, .oauth2_profile_url, .oauth2_email_url').show(); + break; + case 'gitea': + case 'gitlab': + $('.oauth2_token_url input, .oauth2_auth_url input, .oauth2_profile_url input').attr('required', 'required'); + $('.oauth2_token_url, .oauth2_auth_url, .oauth2_profile_url').show(); + $('#oauth2_email_url').val(''); + break; + } + } + } + + // New authentication + if ($('.admin.new.authentication').length > 0) { + $('#auth_type').change(function () { + $('.ldap, .dldap, .smtp, .pam, .oauth2, .has-tls .search-page-size').hide(); + + $('.ldap input[required], .binddnrequired input[required], .dldap input[required], .smtp input[required], .pam input[required], .oauth2 input[required], .has-tls 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(); + break; + } + if (authType === '2' || authType === '5') { + onSecurityProtocolChange(); + } + if (authType === '2') { + onUsePagedSearchChange(); + } + }); + $('#auth_type').change(); + $('#security_protocol').change(onSecurityProtocolChange); + $('#use_paged_search').change(onUsePagedSearchChange); + $('#oauth2_provider').change(onOAuth2Change); + $('#oauth2_use_custom_url').change(onOAuth2UseCustomURLChange); + } + // Edit authentication + if ($('.admin.edit.authentication').length > 0) { + const authType = $('#auth_type').val(); + if (authType === '2' || authType === '5') { + $('#security_protocol').change(onSecurityProtocolChange); + if (authType === '2') { $('#use_paged_search').change(onUsePagedSearchChange); - $('#oauth2_provider').change(onOAuth2Change); - $('#oauth2_use_custom_url').change(onOAuth2UseCustomURLChange); - } - // Edit authentication - if ($('.admin.edit.authentication').length > 0) { - const authType = $('#auth_type').val(); - if (authType == '2' || authType == '5') { - $('#security_protocol').change(onSecurityProtocolChange); - if (authType == '2') { - $('#use_paged_search').change(onUsePagedSearchChange); - } - } else if (authType == '6') { - $('#oauth2_provider').change(onOAuth2Change); - $('#oauth2_use_custom_url').change(onOAuth2UseCustomURLChange); - onOAuth2Change(); - } - } - - // Notice - if ($('.admin.notice')) { - const $detailModal = $('#detail-modal'); - - // Attach view detail modals - $('.view-detail').click(function () { - $detailModal.find('.content p').text($(this).data('content')); - $detailModal.modal('show'); - return false; - }); - - // Select actions - const $checkboxes = $('.select.table .ui.checkbox'); - $('.select.action').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').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": ids - }).done(function () { - window.location.href = $this.data('redirect'); - }); - }); - } + } + } else if (authType === '6') { + $('#oauth2_provider').change(onOAuth2Change); + $('#oauth2_use_custom_url').change(onOAuth2UseCustomURLChange); + onOAuth2Change(); + } + } + + // Notice + if ($('.admin.notice')) { + const $detailModal = $('#detail-modal'); + + // Attach view detail modals + $('.view-detail').click(function () { + $detailModal.find('.content p').text($(this).data('content')); + $detailModal.modal('show'); + return false; + }); + + // Select actions + const $checkboxes = $('.select.table .ui.checkbox'); + $('.select.action').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').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').keypress(function (e) { - if (e.keyCode == 13 || e.keyCode == 32) // enter key or space bar - $(this).click(); - }); + $('.ui.button').keypress(function (e) { + if (e.keyCode === 13 || e.keyCode === 32) { // enter key or space bar + $(this).click(); + } + }); } function searchUsers() { - const $searchUserBox = $('#search-user-box'); - $searchUserBox.search({ - minCharacters: 2, - apiSettings: { - url: suburl + '/api/v1/users/search?q={query}', - onResponse: function(response) { - const items = []; - $.each(response.data, function (_i, item) { - let title = item.login; - if (item.full_name && item.full_name.length > 0) { - title += ' (' + htmlEncode(item.full_name) + ')'; - } - items.push({ - title: title, - image: item.avatar_url - }) - }); + const $searchUserBox = $('#search-user-box'); + $searchUserBox.search({ + minCharacters: 2, + apiSettings: { + url: `${suburl}/api/v1/users/search?q={query}`, + onResponse(response) { + const items = []; + $.each(response.data, (_i, item) => { + let title = item.login; + if (item.full_name && item.full_name.length > 0) { + title += ` (${htmlEncode(item.full_name)})`; + } + items.push({ + title, + image: item.avatar_url + }); + }); - return { results: items } - } - }, - searchFields: ['login', 'full_name'], - showNoResults: false - }); + return { results: items }; + } + }, + searchFields: ['login', 'full_name'], + showNoResults: false + }); } function searchTeams() { - const $searchTeamBox = $('#search-team-box'); - $searchTeamBox.search({ - minCharacters: 2, - apiSettings: { - url: suburl + '/api/v1/orgs/' + $searchTeamBox.data('org') + '/teams/search?q={query}', - headers: {"X-Csrf-Token": csrf}, - onResponse: function(response) { - const items = []; - $.each(response.data, function (_i, item) { - const title = item.name + ' (' + item.permission + ' access)'; - items.push({ - title: title, - }) - }); + const $searchTeamBox = $('#search-team-box'); + $searchTeamBox.search({ + minCharacters: 2, + apiSettings: { + url: `${suburl}/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 - }); + return { results: items }; + } + }, + searchFields: ['name', 'description'], + showNoResults: false + }); } function searchRepositories() { - const $searchRepoBox = $('#search-repo-box'); - $searchRepoBox.search({ - minCharacters: 2, - apiSettings: { - url: suburl + '/api/v1/repos/search?q={query}&uid=' + $searchRepoBox.data('uid'), - onResponse: function(response) { - const items = []; - $.each(response.data, function (_i, item) { - items.push({ - title: item.full_name.split("/")[1], - description: item.full_name - }) - }); + const $searchRepoBox = $('#search-repo-box'); + $searchRepoBox.search({ + minCharacters: 2, + apiSettings: { + url: `${suburl}/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 - }); + return { results: items }; + } + }, + searchFields: ['full_name'], + showNoResults: false + }); } function initCodeView() { - if ($('.code-view .linenums').length > 0) { - $(document).on('click', '.lines-num span', function (e) { - const $select = $(this); - const $list = $select.parent().siblings('.lines-code').find('ol.linenums > li'); - selectRange($list, $list.filter('[rel=' + $select.attr('id') + ']'), (e.shiftKey ? $list.filter('.active').eq(0) : null)); - deSelect(); - }); - - $(window).on('hashchange', function () { - let m = window.location.hash.match(/^#(L\d+)-(L\d+)$/); - const $list = $('.code-view ol.linenums > li'); - let $first; - if (m) { - $first = $list.filter('.' + m[1]); - selectRange($list, $first, $list.filter('.' + m[2])); - $("html, body").scrollTop($first.offset().top - 200); - return; - } - m = window.location.hash.match(/^#(L|n)(\d+)$/); - if (m) { - $first = $list.filter('.L' + m[2]); - selectRange($list, $first); - $("html, body").scrollTop($first.offset().top - 200); - } - }).trigger('hashchange'); - } + if ($('.code-view .linenums').length > 0) { + $(document).on('click', '.lines-num span', function (e) { + const $select = $(this); + const $list = $select.parent().siblings('.lines-code').find('ol.linenums > li'); + selectRange($list, $list.filter(`[rel=${$select.attr('id')}]`), (e.shiftKey ? $list.filter('.active').eq(0) : null)); + deSelect(); + }); + + $(window).on('hashchange', () => { + let m = window.location.hash.match(/^#(L\d+)-(L\d+)$/); + const $list = $('.code-view ol.linenums > li'); + let $first; + if (m) { + $first = $list.filter(`.${m[1]}`); + selectRange($list, $first, $list.filter(`.${m[2]}`)); + $('html, body').scrollTop($first.offset().top - 200); + return; + } + m = window.location.hash.match(/^#(L|n)(\d+)$/); + if (m) { + $first = $list.filter(`.L${m[2]}`); + selectRange($list, $first); + $('html, body').scrollTop($first.offset().top - 200); + } + }).trigger('hashchange'); + } } function initU2FAuth() { - if($('#wait-for-key').length === 0) { - return - } - u2fApi.ensureSupport() - .then(function () { - $.getJSON(suburl + '/user/u2f/challenge').success(function(req) { - u2fApi.sign(req.appId, req.challenge, req.registeredKeys, 30) - .then(u2fSigned) - .catch(function (err) { - if(err === undefined) { - u2fError(1); - return - } - u2fError(err.metaData.code); - }); - }); - }).catch(function () { - // Fallback in case browser do not support U2F - window.location.href = suburl + "/user/two_factor" - }) + if ($('#wait-for-key').length === 0) { + return; + } + u2fApi.ensureSupport() + .then(() => { + $.getJSON(`${suburl}/user/u2f/challenge`).success((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 = `${suburl}/user/two_factor`; + }); } function u2fSigned(resp) { - $.ajax({ - url: suburl + '/user/u2f/sign', - type: "POST", - headers: {"X-Csrf-Token": csrf}, - data: JSON.stringify(resp), - contentType: "application/json; charset=utf-8", - }).done(function(res){ - window.location.replace(res); - }).fail(function () { - u2fError(1); - }); + $.ajax({ + url: `${suburl}/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: suburl + '/user/settings/security/u2f/register', - type: "POST", - headers: {"X-Csrf-Token": csrf}, - data: JSON.stringify(resp), - contentType: "application/json; charset=utf-8", - success: function(){ - reload(); - }, - fail: function () { - u2fError(1); - } - }); + if (checkError(resp)) { + return; + } + $.ajax({ + url: `${suburl}/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; + 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'); - for(const type in u2fErrors){ - if(type != errorType){ - u2fErrors[type].addClass('hide'); - } + 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'); + }); + $('#u2f-error').modal('show'); } function initU2FRegister() { - $('#register-device').modal({allowMultiple: false}); - $('#u2f-error').modal({allowMultiple: false}); - $('#register-security-key').on('click', function(e) { - e.preventDefault(); - u2fApi.ensureSupport() - .then(u2fRegisterRequest) - .catch(function() { - u2fError('browser'); - }) - }) + $('#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(suburl + "/user/settings/security/u2f/request_register", { - "_csrf": csrf, - "name": $('#nickname').val() - }).success(function(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(function (reason) { - if(reason === undefined) { - u2fError(1); - return - } - u2fError(reason.metaData.code); - }); - }).fail(function(xhr) { - if(xhr.status === 409) { - $("#nickname").closest("div.field").addClass("error"); + $.post(`${suburl}/user/settings/security/u2f/request_register`, { + _csrf: csrf, + name: $('#nickname').val() + }).success((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").click(function (e) { - e.preventDefault(); + $('.title_wip_desc > a').click((e) => { + e.preventDefault(); - const $issueTitle = $("#issue_title"); - $issueTitle.focus(); - const value = $issueTitle.val().trim().toUpperCase(); + const $issueTitle = $('#issue_title'); + $issueTitle.focus(); + const value = $issueTitle.val().trim().toUpperCase(); - for (const i in wipPrefixes) { - if (value.startsWith(wipPrefixes[i].toUpperCase())) { - return; - } - } + for (const i in wipPrefixes) { + if (value.startsWith(wipPrefixes[i].toUpperCase())) { + return; + } + } - $issueTitle.val(wipPrefixes[0] + " " + $issueTitle.val()); - }); + $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() !== "") { - $templateUnits.show(); - $nonTemplate.hide(); - } else { - $templateUnits.hide(); - $nonTemplate.show(); - } - }; - $repoTemplate.change(checkTemplate); - checkTemplate(); - - const changeOwner = function() { - $("#repo_template_search") - .dropdown({ - apiSettings: { - url: suburl + '/api/v1/repos/search?q={query}&template=true&priority_owner_id=' + $("#uid").val(), - onResponse: function(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, function(_r, repo) { - filteredResponse.results.push({ - 'name' : htmlEncode(repo.full_name) , - 'value' : repo.id - }); - }); - return filteredResponse; - }, - cache: false, - }, - - fullTextSearch: true - }); - }; - $("#uid").change(changeOwner); - changeOwner(); -} - -$(document).ready(function () { - csrf = $('meta[name=_csrf]').attr("content"); - suburl = $('meta[name=_suburl]').attr("content"); - - // Show exact time - $('.time-since').each(function () { - $(this).addClass('poping up').attr('data-content', $(this).attr('title')).attr('data-variation', 'inverted tiny').attr('title', ''); - }); - - // Semantic UI modules. - $('.dropdown:not(.custom)').dropdown(); - $('.jump.dropdown').dropdown({ - action: 'hide', - onShow: function () { - $('.poping.up').popup('hide'); - } - }); - $('.slide.up.dropdown').dropdown({ - transition: 'slide up' - }); - $('.upward.dropdown').dropdown({ - direction: 'upward' - }); - $('.ui.accordion').accordion(); - $('.ui.checkbox').checkbox(); - $('.ui.progress').progress({ - showActivity: false - }); - $('.poping.up').popup(); - $('.top.menu .poping.up').popup({ - onShow: function () { - if ($('.top.menu .menu.transition').hasClass('visible')) { - return false; - } - } - }); - $('.tabular.menu .item').tab(); - $('.tabable.menu .item').tab(); - - $('.toggle.button').click(function () { - $($(this).data('target')).slideToggle(100); - }); - - // make table <tr> element clickable like a link - $('tr[data-href]').click(function() { - window.location = $(this).data('href'); - }); - - // Highlight JS - if (typeof hljs != 'undefined') { - const nodes = [].slice.call(document.querySelectorAll('pre code') || []); - for (let i = 0; i < nodes.length; i++) { - hljs.highlightBlock(nodes[i]); - } - } - - // Dropzone - const $dropzone = $('#dropzone'); - if ($dropzone.length > 0) { - const filenameDict = {}; - - new Dropzone("#dropzone", { - url: $dropzone.data('upload-url'), - headers: {"X-Csrf-Token": csrf}, - maxFiles: $dropzone.data('max-file'), - maxFilesize: $dropzone.data('max-size'), - acceptedFiles: ($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'), - init: function () { - this.on("success", function (file, data) { - filenameDict[file.name] = data.uuid; - const input = $('<input id="' + data.uuid + '" name="files" type="hidden">').val(data.uuid); - $('.files').append(input); - }); - this.on("removedfile", function (file) { - if (file.name in filenameDict) { - $('#' + filenameDict[file.name]).remove(); - } - if ($dropzone.data('remove-url') && $dropzone.data('csrf')) { - $.post($dropzone.data('remove-url'), { - file: filenameDict[file.name], - _csrf: $dropzone.data('csrf') - }); - } - }) - }, - }); - } - - // Emojify - emojify.setConfig({ - img_dir: suburl + '/vendor/plugins/emojify/images', - ignore_emoticons: true - }); - const hasEmoji = document.getElementsByClassName('has-emoji'); - for (let i = 0; i < hasEmoji.length; i++) { - emojify.run(hasEmoji[i]); - for (let j = 0; j < hasEmoji[i].childNodes.length; j++) { - if (hasEmoji[i].childNodes[j].nodeName === "A") { - emojify.run(hasEmoji[i].childNodes[j]) - } - } + const $repoTemplate = $('#repo_template'); + const checkTemplate = function () { + const $templateUnits = $('#template_units'); + const $nonTemplate = $('#non_template'); + if ($repoTemplate.val() !== '') { + $templateUnits.show(); + $nonTemplate.hide(); + } else { + $templateUnits.hide(); + $nonTemplate.show(); } + }; + $repoTemplate.change(checkTemplate); + checkTemplate(); - // Clipboard JS - const clipboard = new Clipboard('.clipboard'); - clipboard.on('success', function (e) { - e.clearSelection(); - - $('#' + e.trigger.getAttribute('id')).popup('destroy'); - e.trigger.setAttribute('data-content', e.trigger.getAttribute('data-success')) - $('#' + e.trigger.getAttribute('id')).popup('show'); - e.trigger.setAttribute('data-content', e.trigger.getAttribute('data-original')) - }); - - clipboard.on('error', function (e) { - $('#' + e.trigger.getAttribute('id')).popup('destroy'); - e.trigger.setAttribute('data-content', e.trigger.getAttribute('data-error')) - $('#' + e.trigger.getAttribute('id')).popup('show'); - e.trigger.setAttribute('data-content', e.trigger.getAttribute('data-original')) - }); - - // Helpers. - $('.delete-button').click(showDeletePopup); - $('.add-all-button').click(showAddAllPopup); - - $('.delete-branch-button').click(showDeletePopup); - - $('.undo-button').click(function() { - const $this = $(this); - $.post($this.data('url'), { - "_csrf": csrf, - "id": $this.data("id") - }).done(function(data) { - window.location.href = data.redirect; - }); - }); - $('.show-panel.button').click(function () { - $($(this).data('panel')).show(); - }); - $('.show-modal.button').click(function () { - $($(this).data('modal')).modal('show'); - }); - $('.delete-post.button').click(function () { - const $this = $(this); - $.post($this.data('request-url'), { - "_csrf": csrf - }).done(function () { - window.location.href = $this.data('done-url'); - }); - }); + const changeOwner = function () { + $('#repo_template_search') + .dropdown({ + apiSettings: { + url: `${suburl}/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: htmlEncode(repo.full_name), + value: repo.id + }); + }); + return filteredResponse; + }, + cache: false, + }, - // Set anchor. - $('.markdown').each(function () { - const headers = {}; - $(this).find('h1, h2, h3, h4, h5, h6').each(function () { - let node = $(this); - const val = encodeURIComponent(node.text().toLowerCase().replace(/[^\u00C0-\u1FFF\u2C00-\uD7FF\w\- ]/g, '').replace(/[ ]/g, '-')); - let name = val; - if (headers[val] > 0) { - name = val + '-' + headers[val]; - } - if (headers[val] == undefined) { - headers[val] = 1; - } else { - headers[val] += 1; - } - node = node.wrap('<div id="' + name + '" class="anchor-wrap" ></div>'); - node.append('<a class="anchor" href="#' + name + '"><span class="octicon octicon-link"></span></a>'); + fullTextSearch: true + }); + }; + $('#uid').change(changeOwner); + changeOwner(); +} + +$(document).ready(() => { + csrf = $('meta[name=_csrf]').attr('content'); + suburl = $('meta[name=_suburl]').attr('content'); + + // Show exact time + $('.time-since').each(function () { + $(this) + .addClass('poping up') + .attr('data-content', $(this).attr('title')) + .attr('data-variation', 'inverted tiny') + .attr('title', ''); + }); + + // Semantic UI modules. + $('.dropdown:not(.custom)').dropdown(); + $('.jump.dropdown').dropdown({ + action: 'hide', + onShow() { + $('.poping.up').popup('hide'); + } + }); + $('.slide.up.dropdown').dropdown({ + transition: 'slide up' + }); + $('.upward.dropdown').dropdown({ + direction: 'upward' + }); + $('.ui.accordion').accordion(); + $('.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').click(function () { + $($(this).data('target')).slideToggle(100); + }); + + // make table <tr> element clickable like a link + $('tr[data-href]').click(function () { + window.location = $(this).data('href'); + }); + + // Highlight JS + if (typeof hljs !== 'undefined') { + const nodes = [].slice.call(document.querySelectorAll('pre code') || []); + for (let i = 0; i < nodes.length; i++) { + hljs.highlightBlock(nodes[i]); + } + } + + // Dropzone + const $dropzone = $('#dropzone'); + if ($dropzone.length > 0) { + const filenameDict = {}; + + new Dropzone('#dropzone', { + url: $dropzone.data('upload-url'), + headers: { 'X-Csrf-Token': csrf }, + maxFiles: $dropzone.data('max-file'), + maxFilesize: $dropzone.data('max-size'), + acceptedFiles: ($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'), + init() { + this.on('success', (file, data) => { + filenameDict[file.name] = data.uuid; + const input = $(`<input id="${data.uuid}" name="files" type="hidden">`).val(data.uuid); + $('.files').append(input); }); - }); - - $('.issue-checkbox').click(function() { - 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').click(function () { - let action = this.dataset.action; - let elementId = this.dataset.elementId; - const issueIDs = $('.issue-checkbox').children('input:checked').map(function() { - return this.dataset.issueId; - }).get().join(); - const url = this.dataset.url; - if (elementId === '0' && url.substr(-9) === '/assignee'){ - elementId = ''; - action = 'clear'; - } - updateIssuesMeta(url, action, issueIDs, elementId).then(function() { - // 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(function(_,e){ e.checked = false; }); - } - reload(); + this.on('removedfile', (file) => { + if (file.name in filenameDict) { + $(`#${filenameDict[file.name]}`).remove(); + } + if ($dropzone.data('remove-url') && $dropzone.data('csrf')) { + $.post($dropzone.data('remove-url'), { + file: filenameDict[file.name], + _csrf: $dropzone.data('csrf') + }); + } }); - }); - - // 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(function(_,e) { - e.checked = false; - $(e).click(); - }); - - buttonsClickOnEnter(); - searchUsers(); - searchTeams(); - searchRepositories(); - - initCommentForm(); - initInstall(); - initRepository(); - initMigration(); - initWikiForm(); - initEditForm(); - initEditor(); - initOrganization(); - initWebhook(); - initAdmin(); - initCodeView(); - initVueApp(); - initTeamSettings(); - initCtrlEnterSubmit(); - initNavbarContentToggle(); - initTopicbar(); - initU2FAuth(); - initU2FRegister(); - initIssueList(); - initWipTitle(); - initPullRequestReview(); - initRepoStatusChecker(); - initTemplateSearch(); - - // Repo clone url. - if ($('#repo-clone-url').length > 0) { - switch (localStorage.getItem('repo-clone-protocol')) { - case 'ssh': - if ($('#repo-clone-ssh').click().length === 0) { - $('#repo-clone-https').click(); - } - break; - default: - $('#repo-clone-https').click(); - break; - } - } - - const routes = { - 'div.user.settings': initUserSettings, - 'div.repository.settings.collaboration': initRepositoryCollaboration - }; - - let selector; - for (selector in routes) { - if ($(selector).length > 0) { - routes[selector](); - break; - } - } - - const $cloneAddr = $('#clone_addr'); - $cloneAddr.change(function() { - const $repoName = $('#repo_name'); - if ($cloneAddr.val().length > 0 && $repoName.val().length === 0) { // Only modify if repo_name input is blank - $repoName.val($cloneAddr.val().match(/^(.*\/)?((.+?)(\.git)?)$/)[3]); - } - }); + }, + }); + } + + // Emojify + emojify.setConfig({ + img_dir: `${suburl}/vendor/plugins/emojify/images`, + ignore_emoticons: true + }); + const hasEmoji = document.getElementsByClassName('has-emoji'); + for (let i = 0; i < hasEmoji.length; i++) { + emojify.run(hasEmoji[i]); + for (let j = 0; j < hasEmoji[i].childNodes.length; j++) { + if (hasEmoji[i].childNodes[j].nodeName === 'A') { + emojify.run(hasEmoji[i].childNodes[j]); + } + } + } + + // Clipboard JS + const clipboard = new Clipboard('.clipboard'); + clipboard.on('success', (e) => { + e.clearSelection(); + + $(`#${e.trigger.getAttribute('id')}`).popup('destroy'); + e.trigger.setAttribute('data-content', e.trigger.getAttribute('data-success')); + $(`#${e.trigger.getAttribute('id')}`).popup('show'); + e.trigger.setAttribute('data-content', e.trigger.getAttribute('data-original')); + }); + + clipboard.on('error', (e) => { + $(`#${e.trigger.getAttribute('id')}`).popup('destroy'); + e.trigger.setAttribute('data-content', e.trigger.getAttribute('data-error')); + $(`#${e.trigger.getAttribute('id')}`).popup('show'); + e.trigger.setAttribute('data-content', e.trigger.getAttribute('data-original')); + }); + + // Helpers. + $('.delete-button').click(showDeletePopup); + $('.add-all-button').click(showAddAllPopup); + + $('.delete-branch-button').click(showDeletePopup); + + $('.undo-button').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').click(function () { + $($(this).data('panel')).show(); + }); + $('.show-modal.button').click(function () { + $($(this).data('modal')).modal('show'); + }); + $('.delete-post.button').click(function () { + const $this = $(this); + $.post($this.data('request-url'), { + _csrf: csrf + }).done(() => { + window.location.href = $this.data('done-url'); + }); + }); + + // Set anchor. + $('.markdown').each(function () { + const headers = {}; + $(this).find('h1, h2, h3, h4, h5, h6').each(function () { + let node = $(this); + const val = encodeURIComponent(node.text().toLowerCase().replace(/[^\u00C0-\u1FFF\u2C00-\uD7FF\w\- ]/g, '').replace(/[ ]/g, '-')); + let name = val; + if (headers[val] > 0) { + name = `${val}-${headers[val]}`; + } + if (headers[val] === undefined) { + headers[val] = 1; + } else { + headers[val] += 1; + } + node = node.wrap(`<div id="${name}" class="anchor-wrap" ></div>`); + node.append(`<a class="anchor" href="#${name}"><span class="octicon octicon-link"></span></a>`); + }); + }); + + $('.issue-checkbox').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').click(function () { + let { action } = this.dataset; + let { elementId } = this.dataset; + const issueIDs = $('.issue-checkbox').children('input:checked').map(function () { + return this.dataset.issueId; + }).get().join(); + const { url } = this.dataset; + 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).click(); + }); + + buttonsClickOnEnter(); + searchUsers(); + searchTeams(); + searchRepositories(); + + initCommentForm(); + initInstall(); + initRepository(); + initMigration(); + initWikiForm(); + initEditForm(); + initEditor(); + initOrganization(); + initWebhook(); + initAdmin(); + initCodeView(); + initVueApp(); + initTeamSettings(); + initCtrlEnterSubmit(); + initNavbarContentToggle(); + initTopicbar(); + initU2FAuth(); + initU2FRegister(); + initIssueList(); + initWipTitle(); + initPullRequestReview(); + initRepoStatusChecker(); + initTemplateSearch(); + + // Repo clone url. + if ($('#repo-clone-url').length > 0) { + switch (localStorage.getItem('repo-clone-protocol')) { + case 'ssh': + if ($('#repo-clone-ssh').click().length === 0) { + $('#repo-clone-https').click(); + } + break; + default: + $('#repo-clone-https').click(); + break; + } + } + + const routes = { + 'div.user.settings': initUserSettings, + 'div.repository.settings.collaboration': initRepositoryCollaboration + }; + + let selector; + for (selector in routes) { + if ($(selector).length > 0) { + routes[selector](); + break; + } + } + + const $cloneAddr = $('#clone_addr'); + $cloneAddr.change(() => { + const $repoName = $('#repo_name'); + if ($cloneAddr.val().length > 0 && $repoName.val().length === 0) { // Only modify if repo_name input is blank + $repoName.val($cloneAddr.val().match(/^(.*\/)?((.+?)(\.git)?)$/)[3]); + } + }); }); function changeHash(hash) { - if (history.pushState) { - history.pushState(null, null, hash); - } - else { - location.hash = 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(); - } + if (window.getSelection) { + window.getSelection().removeAllRanges(); + } else { + document.selection.empty(); + } } function selectRange($list, $select, $from) { - $list.removeClass('active'); - 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('.L' + i); - } - $list.filter(classes.join(',')).addClass('active'); - changeHash('#L' + a + '-' + 'L' + b); - return - } - } - $select.addClass('active'); - changeHash('#' + $select.attr('rel')); -} - -$(function () { - // 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]); - } - }); + $list.removeClass('active'); + 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(`.L${i}`); + } + $list.filter(classes.join(',')).addClass('active'); + changeHash(`#L${a}-L${b}`); + return; + } + } + $select.addClass('active'); + changeHash(`#${$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); - let filter = ""; - if ($this.attr("id")) { - filter += "#" + $this.attr("id") - } + const $this = $(this); + let filter = ''; + if ($this.attr('id')) { + filter += `#${$this.attr('id')}`; + } + + const dialog = $(`.delete.modal${filter}`); + dialog.find('.name').text($this.data('name')); + + dialog.modal({ + closable: false, + onApprove() { + if ($this.data('type') === 'form') { + $($this.data('form')).submit(); + return; + } - const dialog = $('.delete.modal' + filter); - dialog.find('.name').text($this.data('name')); + $.post($this.data('url'), { + _csrf: csrf, + id: $this.data('id') + }).done((data) => { + window.location.href = data.redirect; + }); + } + }).modal('show'); + return false; +} - dialog.modal({ - closable: false, - onApprove: function() { - if ($this.data('type') == "form") { - $($this.data('form')).submit(); - return; - } +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')).submit(); + return; + } + + $.post($this.data('url'), { + _csrf: csrf, + id: $this.data('id') + }).done((data) => { + window.location.href = data.redirect; + }); + } + }).modal('show'); + return false; +} + +function initVueComponents() { + const vueDelimeters = ['${', '}']; + + Vue.component('repo-search', { + delimiters: vueDelimeters, + + props: { + searchLimit: { + type: Number, + default: 10 + }, + suburl: { + type: String, + required: true + }, + uid: { + type: Number, + required: true + }, + organizations: { + type: Array, + default: [] + }, + isOrganization: { + type: Boolean, + default: true + }, + canCreateOrganization: { + type: Boolean, + default: false + }, + organizationsTotalCount: { + type: Number, + default: 0 + }, + moreReposLink: { + type: String, + default: '' + } + }, + + data() { + return { + tab: 'repos', + repos: [], + reposTotalCount: 0, + reposFilter: 'all', + searchQuery: '', + isLoading: false, + repoTypes: { + all: { + count: 0, + searchMode: '', + }, + forks: { + count: 0, + searchMode: 'fork', + }, + mirrors: { + count: 0, + searchMode: 'mirror', + }, + sources: { + count: 0, + searchMode: 'source', + }, + collaborative: { + count: 0, + searchMode: 'collaborative', + }, + } + }; + }, + + computed: { + showMoreReposLink() { + return this.repos.length > 0 && this.repos.length < this.repoTypes[this.reposFilter].count; + }, + searchURL() { + return `${this.suburl}/api/v1/repos/search?sort=updated&order=desc&uid=${this.uid}&q=${this.searchQuery + }&limit=${this.searchLimit}&mode=${this.repoTypes[this.reposFilter].searchMode + }${this.reposFilter !== 'all' ? '&exclusive=1' : ''}`; + }, + repoTypeCount() { + return this.repoTypes[this.reposFilter].count; + } + }, + + mounted() { + this.searchRepos(this.reposFilter); + + const self = this; + Vue.nextTick(() => { + self.$refs.search.focus(); + }); + }, + + methods: { + changeTab(t) { + this.tab = t; + }, + + changeReposFilter(filter) { + this.reposFilter = filter; + this.repos = []; + this.repoTypes[filter].count = 0; + this.searchRepos(filter); + }, + + showRepo(repo, filter) { + switch (filter) { + case 'sources': + return repo.owner.id === this.uid && !repo.mirror && !repo.fork; + case 'forks': + return repo.owner.id === this.uid && !repo.mirror && repo.fork; + case 'mirrors': + return repo.mirror; + case 'collaborative': + return repo.owner.id !== this.uid && !repo.mirror; + default: + return true; + } + }, + + searchRepos(reposFilter) { + const self = this; + + this.isLoading = true; + + const searchedMode = this.repoTypes[reposFilter].searchMode; + const searchedURL = this.searchURL; + const searchedQuery = this.searchQuery; + + $.getJSON(searchedURL, (result, _textStatus, request) => { + if (searchedURL === self.searchURL) { + self.repos = result.data; + const count = request.getResponseHeader('X-Total-Count'); + if (searchedQuery === '' && searchedMode === '') { + self.reposTotalCount = count; + } + self.repoTypes[reposFilter].count = count; + } + }).always(() => { + if (searchedURL === self.searchURL) { + self.isLoading = false; + } + }); + }, - $.post($this.data('url'), { - "_csrf": csrf, - "id": $this.data("id") - }).done(function(data) { - window.location.href = data.redirect; - }); + repoClass(repo) { + if (repo.fork) { + return 'octicon octicon-repo-forked'; + } if (repo.mirror) { + return 'octicon octicon-repo-clone'; + } if (repo.private) { + return 'octicon octicon-lock'; } - }).modal('show'); - return false; + return 'octicon octicon-repo'; + } + } + }); } -function showAddAllPopup() { - const $this = $(this); - let filter = ""; - if ($this.attr("id")) { - filter += "#" + $this.attr("id") +function initCtrlEnterSubmit() { + $('.js-quick-submit').keydown(function (e) { + if (((e.ctrlKey && !e.altKey) || e.metaKey) && (e.keyCode === 13 || e.keyCode === 10)) { + $(this).closest('form').submit(); } + }); +} - const dialog = $('.addall.modal' + filter); - dialog.find('.name').text($this.data('name')); - - dialog.modal({ - closable: false, - onApprove: function() { - if ($this.data('type') == "form") { - $($this.data('form')).submit(); - return; - } - - $.post($this.data('url'), { - "_csrf": csrf, - "id": $this.data("id") - }).done(function(data) { - window.location.href = data.redirect; - }); - } +function initVueApp() { + const el = document.getElementById('app'); + if (!el) { + return; + } + + initVueComponents(); + + new Vue({ + delimiters: ['${', '}'], + el, + data: { + searchLimit: document.querySelector('meta[name=_search_limit]').content, + suburl: document.querySelector('meta[name=_suburl]').content, + uid: document.querySelector('meta[name=_context_uid]').content, + }, + }); +} + +window.timeAddManual = function () { + $('.mini.modal') + .modal({ + duration: 200, + onApprove() { + $('#add_time_manual_form').submit(); + } }).modal('show'); - return false; -} +}; -function initVueComponents(){ - const vueDelimeters = ['${', '}']; - - Vue.component('repo-search', { - delimiters: vueDelimeters, - - props: { - searchLimit: { - type: Number, - default: 10 - }, - suburl: { - type: String, - required: true - }, - uid: { - type: Number, - required: true - }, - organizations: { - type: Array, - default: [] - }, - isOrganization: { - type: Boolean, - default: true - }, - canCreateOrganization: { - type: Boolean, - default: false - }, - organizationsTotalCount: { - type: Number, - default: 0 - }, - moreReposLink: { - type: String, - default: '' - } - }, +window.toggleStopwatch = function () { + $('#toggle_stopwatch_form').submit(); +}; +window.cancelStopwatch = function () { + $('#cancel_stopwatch_form').submit(); +}; - data: function() { - return { - tab: 'repos', - repos: [], - reposTotalCount: 0, - reposFilter: 'all', - searchQuery: '', - isLoading: false, - repoTypes: { - 'all': { - count: 0, - searchMode: '', - }, - 'forks': { - count: 0, - searchMode: 'fork', - }, - 'mirrors': { - count: 0, - searchMode: 'mirror', - }, - 'sources': { - count: 0, - searchMode: 'source', - }, - 'collaborative': { - count: 0, - searchMode: 'collaborative', - }, - } - } - }, +window.initHeatmap = function (appElementId, heatmapUser, locale) { + const el = document.getElementById(appElementId); + if (!el) { + return; + } + + locale = locale || {}; + + locale.contributions = locale.contributions || 'contributions'; + locale.no_contributions = locale.no_contributions || 'No contributions'; + + const vueDelimeters = ['${', '}']; + + Vue.component('activity-heatmap', { + delimiters: vueDelimeters, + + props: { + user: { + type: String, + required: true + }, + suburl: { + type: String, + required: true + }, + locale: { + type: Object, + required: true + } + }, + + data() { + return { + isLoading: true, + colorRange: [], + endDate: null, + values: [], + totalContributions: 0, + }; + }, + + mounted() { + this.colorRange = [ + this.getColor(0), + this.getColor(1), + this.getColor(2), + this.getColor(3), + this.getColor(4), + this.getColor(5) + ]; + this.endDate = new Date(); + this.loadHeatmap(this.user); + }, + + methods: { + loadHeatmap(userName) { + const self = this; + $.get(`${this.suburl}/api/v1/users/${userName}/heatmap`, (chartRawData) => { + const chartData = []; + for (let i = 0; i < chartRawData.length; i++) { + self.totalContributions += chartRawData[i].contributions; + chartData[i] = { date: new Date(chartRawData[i].timestamp * 1000), count: chartRawData[i].contributions }; + } + self.values = chartData; + self.isLoading = false; + }); + }, - computed: { - showMoreReposLink: function() { - return this.repos.length > 0 && this.repos.length < this.repoTypes[this.reposFilter].count; - }, - searchURL: function() { - return this.suburl + '/api/v1/repos/search?sort=updated&order=desc&uid=' + this.uid + '&q=' + this.searchQuery - + '&limit=' + this.searchLimit + '&mode=' + this.repoTypes[this.reposFilter].searchMode - + (this.reposFilter !== 'all' ? '&exclusive=1' : ''); - }, - repoTypeCount: function() { - return this.repoTypes[this.reposFilter].count; - } - }, + getColor(idx) { + const el = document.createElement('div'); + el.className = `heatmap-color-${idx}`; + document.body.appendChild(el); - mounted: function() { - this.searchRepos(this.reposFilter); + const color = getComputedStyle(el).backgroundColor; - const self = this; - Vue.nextTick(function() { - self.$refs.search.focus(); - }); - }, + document.body.removeChild(el); - methods: { - changeTab: function(t) { - this.tab = t; - }, - - changeReposFilter: function(filter) { - this.reposFilter = filter; - this.repos = []; - this.repoTypes[filter].count = 0; - this.searchRepos(filter); - }, - - showRepo: function(repo, filter) { - switch (filter) { - case 'sources': - return repo.owner.id == this.uid && !repo.mirror && !repo.fork; - case 'forks': - return repo.owner.id == this.uid && !repo.mirror && repo.fork; - case 'mirrors': - return repo.mirror; - case 'collaborative': - return repo.owner.id != this.uid && !repo.mirror; - default: - return true; - } - }, - - searchRepos: function(reposFilter) { - const self = this; - - this.isLoading = true; - - const searchedMode = this.repoTypes[reposFilter].searchMode; - const searchedURL = this.searchURL; - const searchedQuery = this.searchQuery; - - $.getJSON(searchedURL, function(result, _textStatus, request) { - if (searchedURL == self.searchURL) { - self.repos = result.data; - const count = request.getResponseHeader('X-Total-Count'); - if (searchedQuery === '' && searchedMode === '') { - self.reposTotalCount = count; - } - self.repoTypes[reposFilter].count = count; - } - }).always(function() { - if (searchedURL == self.searchURL) { - self.isLoading = false; - } - }); - }, - - repoClass: function(repo) { - if (repo.fork) { - return 'octicon octicon-repo-forked'; - } else if (repo.mirror) { - return 'octicon octicon-repo-clone'; - } else if (repo.private) { - return 'octicon octicon-lock'; - } else { - return 'octicon octicon-repo'; - } - } - } - }) -} + return color; + } + }, -function initCtrlEnterSubmit() { - $(".js-quick-submit").keydown(function(e) { - if (((e.ctrlKey && !e.altKey) || e.metaKey) && (e.keyCode == 13 || e.keyCode == 10)) { - $(this).closest("form").submit(); - } - }); -} + template: '<div><div v-show="isLoading"><slot name="loading"></slot></div><h4 class="total-contributions" v-if="!isLoading"><span v-html="totalContributions"></span> total contributions in the last 12 months</h4><calendar-heatmap v-show="!isLoading" :locale="locale" :no-data-text="locale.no_contributions" :tooltip-unit="locale.contributions" :end-date="endDate" :values="values" :range-color="colorRange" />' + }); -function initVueApp() { - const el = document.getElementById('app'); - if (!el) { - return; - } + new Vue({ + delimiters: vueDelimeters, + el, - initVueComponents(); + data: { + suburl: document.querySelector('meta[name=_suburl]').content, + heatmapUser, + locale + }, + }); +}; +function initFilterBranchTagDropdown(selector) { + $(selector).each(function () { + const $dropdown = $(this); + const $data = $dropdown.find('.data'); + const data = { + items: [], + mode: $data.data('mode'), + searchTerm: '', + noResults: '', + canCreateBranch: false, + menuVisible: false, + active: 0 + }; + $data.find('.item').each(function () { + data.items.push({ + name: $(this).text(), + url: $(this).data('url'), + branch: $(this).hasClass('branch'), + tag: $(this).hasClass('tag'), + selected: $(this).hasClass('selected') + }); + }); + $data.remove(); new Vue({ - delimiters: ['${', '}'], - el: el, - - data: { - searchLimit: document.querySelector('meta[name=_search_limit]').content, - suburl: document.querySelector('meta[name=_suburl]').content, - uid: document.querySelector('meta[name=_context_uid]').content, - }, - }); -} - -window.timeAddManual = function() { - $('.mini.modal') - .modal({ - duration: 200, - onApprove: function() { - $('#add_time_manual_form').submit(); - } - }).modal('show') - ; -} + delimiters: ['${', '}'], + el: this, + data, -window.toggleStopwatch = function() { - $("#toggle_stopwatch_form").submit(); -} -window.cancelStopwatch = function() { - $("#cancel_stopwatch_form").submit(); -} + beforeMount() { + const vm = this; -window.initHeatmap = function(appElementId, heatmapUser, locale) { - const el = document.getElementById(appElementId); - if (!el) { - return; - } + this.noResults = vm.$el.getAttribute('data-no-results'); + this.canCreateBranch = vm.$el.getAttribute('data-can-create-branch') === 'true'; - locale = locale || {}; + document.body.addEventListener('click', (event) => { + if (vm.$el.contains(event.target)) { + return; + } + if (vm.menuVisible) { + Vue.set(vm, 'menuVisible', false); + } + }); + }, - locale.contributions = locale.contributions || 'contributions'; - locale.no_contributions = locale.no_contributions || 'No contributions'; + watch: { + menuVisible(visible) { + if (visible) { + this.focusSearchField(); + } + } + }, - const vueDelimeters = ['${', '}']; + computed: { + filteredItems() { + const vm = this; - Vue.component('activity-heatmap', { - delimiters: vueDelimeters, + const items = vm.items.filter((item) => { + return ((vm.mode === 'branches' && item.branch) || (vm.mode === 'tags' && item.tag)) + && (!vm.searchTerm || item.name.toLowerCase().indexOf(vm.searchTerm.toLowerCase()) >= 0); + }); - props: { - user: { - type: String, - required: true - }, - suburl: { - type: String, - required: true - }, - locale: { - type: Object, - required: true - } - }, + vm.active = (items.length === 0 && vm.showCreateNewBranch ? 0 : -1); - data: function () { - return { - isLoading: true, - colorRange: [], - endDate: null, - values: [], - totalContributions: 0, - }; + return items; }, - - mounted: function() { - this.colorRange = [ - this.getColor(0), - this.getColor(1), - this.getColor(2), - this.getColor(3), - this.getColor(4), - this.getColor(5) - ]; - this.endDate = new Date(); - this.loadHeatmap(this.user); + showNoResults() { + return this.filteredItems.length === 0 && !this.showCreateNewBranch; }, + showCreateNewBranch() { + const vm = this; + if (!this.canCreateBranch || !vm.searchTerm || vm.mode === 'tags') { + return false; + } - methods: { - loadHeatmap: function(userName) { - const self = this; - $.get(this.suburl + '/api/v1/users/' + userName + '/heatmap', function(chartRawData) { - const chartData = []; - for (let i = 0; i < chartRawData.length; i++) { - self.totalContributions += chartRawData[i].contributions; - chartData[i] = { date: new Date(chartRawData[i].timestamp * 1000), count: chartRawData[i].contributions }; - } - self.values = chartData; - self.isLoading = false; - }); - }, - - getColor: function(idx) { - const el = document.createElement('div'); - el.className = 'heatmap-color-' + idx; - document.body.appendChild(el); - - const color = getComputedStyle(el).backgroundColor; - - document.body.removeChild(el); + return vm.items.filter((item) => item.name.toLowerCase() === vm.searchTerm.toLowerCase()).length === 0; + } + }, - return color; - } + methods: { + selectItem(item) { + const prev = this.getSelected(); + if (prev !== null) { + prev.selected = false; + } + item.selected = true; + window.location.href = item.url; + }, + createNewBranch() { + if (!this.showCreateNewBranch) { + return; + } + this.$refs.newBranchForm.submit(); + }, + focusSearchField() { + const vm = this; + Vue.nextTick(() => { + vm.$refs.searchField.focus(); + }); + }, + getSelected() { + for (let i = 0, j = this.items.length; i < j; ++i) { + if (this.items[i].selected) return this.items[i]; + } + return null; }, + getSelectedIndexInFiltered() { + for (let i = 0, j = this.filteredItems.length; i < j; ++i) { + if (this.filteredItems[i].selected) return i; + } + return -1; + }, + scrollToActive() { + let el = this.$refs[`listItem${this.active}`]; + if (!el || el.length === 0) { + return; + } + if (Array.isArray(el)) { + el = el[0]; + } + + const cont = this.$refs.scrollContainer; + + if (el.offsetTop < cont.scrollTop) { + cont.scrollTop = el.offsetTop; + } else if (el.offsetTop + el.clientHeight > cont.scrollTop + cont.clientHeight) { + cont.scrollTop = el.offsetTop + el.clientHeight - cont.clientHeight; + } + }, + keydown(event) { + const vm = this; + if (event.keyCode === 40) { + // arrow down + event.preventDefault(); - template: '<div><div v-show="isLoading"><slot name="loading"></slot></div><h4 class="total-contributions" v-if="!isLoading"><span v-html="totalContributions"></span> total contributions in the last 12 months</h4><calendar-heatmap v-show="!isLoading" :locale="locale" :no-data-text="locale.no_contributions" :tooltip-unit="locale.contributions" :end-date="endDate" :values="values" :range-color="colorRange" />' - }); + if (vm.active === -1) { + vm.active = vm.getSelectedIndexInFiltered(); + } - new Vue({ - delimiters: vueDelimeters, - el: el, + if (vm.active + (vm.showCreateNewBranch ? 0 : 1) >= vm.filteredItems.length) { + return; + } + vm.active++; + vm.scrollToActive(); + } + if (event.keyCode === 38) { + // arrow up + event.preventDefault(); - data: { - suburl: document.querySelector('meta[name=_suburl]').content, - heatmapUser: heatmapUser, - locale: locale - }, - }); -} + if (vm.active === -1) { + vm.active = vm.getSelectedIndexInFiltered(); + } -function initFilterBranchTagDropdown(selector) { - $(selector).each(function() { - const $dropdown = $(this); - const $data = $dropdown.find('.data'); - const data = { - items: [], - mode: $data.data('mode'), - searchTerm: '', - noResults: '', - canCreateBranch: false, - menuVisible: false, - active: 0 - }; - $data.find('.item').each(function() { - data.items.push({ - name: $(this).text(), - url: $(this).data('url'), - branch: $(this).hasClass('branch'), - tag: $(this).hasClass('tag'), - selected: $(this).hasClass('selected') - }); - }); - $data.remove(); - new Vue({ - delimiters: ['${', '}'], - el: this, - data: data, - - beforeMount: function () { - const vm = this; - - this.noResults = vm.$el.getAttribute('data-no-results'); - this.canCreateBranch = vm.$el.getAttribute('data-can-create-branch') === 'true'; - - document.body.addEventListener('click', function(event) { - if (vm.$el.contains(event.target)) { - return; - } - if (vm.menuVisible) { - Vue.set(vm, 'menuVisible', false); - } - }); - }, + if (vm.active <= 0) { + return; + } + vm.active--; + vm.scrollToActive(); + } + if (event.keyCode === 13) { + // enter + event.preventDefault(); - watch: { - menuVisible: function(visible) { - if (visible) { - this.focusSearchField(); - } - } - }, - - computed: { - filteredItems: function() { - const vm = this; - - const items = vm.items.filter(function (item) { - return ((vm.mode === 'branches' && item.branch) - || (vm.mode === 'tags' && item.tag)) - && (!vm.searchTerm - || item.name.toLowerCase().indexOf(vm.searchTerm.toLowerCase()) >= 0); - }); - - vm.active = (items.length === 0 && vm.showCreateNewBranch ? 0 : -1); - - return items; - }, - showNoResults: function() { - return this.filteredItems.length === 0 - && !this.showCreateNewBranch; - }, - showCreateNewBranch: function() { - const vm = this; - if (!this.canCreateBranch || !vm.searchTerm || vm.mode === 'tags') { - return false; - } - - return vm.items.filter(function (item) { - return item.name.toLowerCase() === vm.searchTerm.toLowerCase() - }).length === 0; - } - }, - - methods: { - selectItem: function(item) { - const prev = this.getSelected(); - if (prev !== null) { - prev.selected = false; - } - item.selected = true; - window.location.href = item.url; - }, - createNewBranch: function() { - if (!this.showCreateNewBranch) { - return; - } - this.$refs.newBranchForm.submit(); - }, - focusSearchField: function() { - const vm = this; - Vue.nextTick(function() { - vm.$refs.searchField.focus(); - }); - }, - getSelected: function() { - for (let i = 0, j = this.items.length; i < j; ++i) { - if (this.items[i].selected) - return this.items[i]; - } - return null; - }, - getSelectedIndexInFiltered: function() { - for (let i = 0, j = this.filteredItems.length; i < j; ++i) { - if (this.filteredItems[i].selected) - return i; - } - return -1; - }, - scrollToActive: function() { - let el = this.$refs['listItem' + this.active]; - if (!el || el.length === 0) { - return; - } - if (Array.isArray(el)) { - el = el[0]; - } - - const cont = this.$refs.scrollContainer; - - if (el.offsetTop < cont.scrollTop) { - cont.scrollTop = el.offsetTop; - } - else if (el.offsetTop + el.clientHeight > cont.scrollTop + cont.clientHeight) { - cont.scrollTop = el.offsetTop + el.clientHeight - cont.clientHeight; - } - }, - keydown: function(event) { - const vm = this; - if (event.keyCode === 40) { - // arrow down - event.preventDefault(); - - if (vm.active === -1) { - vm.active = vm.getSelectedIndexInFiltered(); - } - - if (vm.active + (vm.showCreateNewBranch ? 0 : 1) >= vm.filteredItems.length) { - return; - } - vm.active++; - vm.scrollToActive(); - } - if (event.keyCode === 38) { - // arrow up - event.preventDefault(); - - if (vm.active === -1) { - vm.active = vm.getSelectedIndexInFiltered(); - } - - if (vm.active <= 0) { - return; - } - vm.active--; - vm.scrollToActive(); - } - if (event.keyCode == 13) { - // enter - event.preventDefault(); - - if (vm.active >= vm.filteredItems.length) { - vm.createNewBranch(); - } else if (vm.active >= 0) { - vm.selectItem(vm.filteredItems[vm.active]); - } - } - if (event.keyCode == 27) { - // escape - event.preventDefault(); - vm.menuVisible = false; - } - } + if (vm.active >= vm.filteredItems.length) { + vm.createNewBranch(); + } else if (vm.active >= 0) { + vm.selectItem(vm.filteredItems[vm.active]); } - }); + } + if (event.keyCode === 27) { + // escape + event.preventDefault(); + vm.menuVisible = false; + } + } + } }); + }); } -$(".commit-button").click(function(e) { - e.preventDefault(); - $(this).parent().find('.commit-body').toggle(); +$('.commit-button').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.click(function() { - isExpanded = !isExpanded; - if (isExpanded) { - content.addClass('shown'); - toggle.addClass('active'); - } - else { - content.removeClass('shown'); - toggle.removeClass('active'); - } - }); + const content = $('#navbar'); + const toggle = $('#navbar-expand-toggle'); + let isExpanded = false; + toggle.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.click(function() { - viewDiv.hide(); - editDiv.css('display', ''); // show Semantic UI Grid - }); - - function getPrompts() { - const hidePrompt = $("div.hide#validate_prompt"), - prompts = { - countPrompt: hidePrompt.children('#count_prompt').text(), - formatPrompt: hidePrompt.children('#format_prompt').text() - }; - hidePrompt.remove(); - return prompts; - } - - saveBtn.click(function() { - const topics = $("input[name=topics]").val(); - - $.post(saveBtn.data('link'), { - "_csrf": csrf, - "topics": topics - }, function(_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++) { - $('<div class="ui small label topic" style="cursor:pointer;">'+topicArray[i]+'</div>').insertBefore(last) - } - } - editDiv.css('display', 'none'); - viewDiv.show(); - } - }).fail(function(xhr){ - if (xhr.status === 422) { - if (xhr.responseJSON.invalidTopics.length > 0) { - topicPrompts.formatPrompt = xhr.responseJSON.message; - - const invalidTopics = xhr.responseJSON.invalidTopics, - topicLables = topicDropdown.children('a.ui.label'); - - topics.split(',').forEach(function(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(function() { - topicForm.form('validate form'); - }); - }); - - topicDropdown.dropdown({ - allowAdditions: true, - forceSelection: false, - 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: suburl + '/api/v1/topics/search?q={query}', - throttle: 500, - cache: false, - onResponse: function(res) { - const formattedResponse = { - success: false, - results: [], - }; - const stripTags = function (text) { - return text.replace(/<[^>]*>?/gm, ""); - }; - - const query = stripTags(this.urlData.query.trim()); - let found_query = false; - const current_topics = []; - topicDropdown.find('div.label.visible.topic,a.label.visible').each(function(_,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.indexOf(res.topics[i].topic_name) != -1){ - 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(function(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: function(value) { - value = value.toLowerCase().trim(); - this.attr("data-value", value).contents().first().replaceWith(value); - return $(this); - }, - onAdd: function(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'), - 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; + 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.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.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++) { + $(`<div class="ui small label topic" style="cursor:pointer;">${topicArray[i]}</div>`).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, + 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: `${suburl}/api/v1/topics/search?q={query}`, + throttle: 500, + cache: false, + onResponse(res) { + const formattedResponse = { + success: false, + results: [], + }; + const stripTags = function (text) { + return text.replace(/<[^>]*>?/gm, ''); + }; - topicForm.form({ - on: 'change', - inline : true, - fields: { - topics: { - identifier: 'topics', - rules: [ - { - type: 'validateTopic', - value: /^[a-z0-9][a-z0-9-]{1,35}$/, - prompt: topicPrompts.formatPrompt - }, - { - type: 'maxCount[25]', - prompt: topicPrompts.countPrompt - } - ] - }, - } - }); -} -window.toggleDeadlineForm = function() { - $('#deadlineForm').fadeToggle(150); -} - -window.setDeadline = function() { - const deadline = $('#deadlineDate').val(); - window.updateDeadline(deadline); -} - -window.updateDeadline = function(deadlineString) { - $('#deadline-err-invalid-date').hide(); - $('#deadline-loader').addClass('loading'); + 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.indexOf(res.topics[i].topic_name) !== -1) { + 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-]{1,35}$/, + prompt: topicPrompts.formatPrompt + }, + { + type: 'maxCount[25]', + prompt: topicPrompts.countPrompt + } + ] + }, + } + }); +} + +window.toggleDeadlineForm = function () { + $('#deadlineForm').fadeToggle(150); +}; - let realDeadline = null; - if (deadlineString !== '') { +window.setDeadline = function () { + const deadline = $('#deadlineDate').val(); + window.updateDeadline(deadline); +}; - const newDate = Date.parse(deadlineString) +window.updateDeadline = function (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(); + } + }); +}; - if (isNaN(newDate)) { - $('#deadline-loader').removeClass('loading'); - $('#deadline-err-invalid-date').show(); - return false; - } - realDeadline = new Date(newDate); - } +window.deleteDependencyModal = function (id, type) { + $('.remove-dependency') + .modal({ + closable: false, + duration: 200, + onApprove() { + $('#removeDependencyID').val(id); + $('#dependencyType').val(type); + $('#removeDependencyForm').submit(); + } + }).modal('show'); +}; - $.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: function () { - reload(); +function initIssueList() { + const repolink = $('#repolink').val(); + const repoId = $('#repoId').val(); + const crossRepoSearch = $('#crossRepoSearch').val(); + let issueSearchUrl = `${suburl}/api/v1/repos/${repolink}/issues?q={query}`; + if (crossRepoSearch === 'true') { + issueSearchUrl = `${suburl}/api/v1/repos/issues/search?q={query}&priority_repo_id=${repoId}`; + } + $('#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} ${htmlEncode(issue.title) + }<div class="text small dont-break-out">${htmlEncode(issue.repository.full_name)}</div>`, + value: issue.id + }); + }); + return filteredResponse; }, - error: function () { - $('#deadline-loader').removeClass('loading'); - $('#deadline-err-invalid-date').show(); - } - }); -} - -window.deleteDependencyModal = function(id, type) { - $('.remove-dependency') - .modal({ - closable: false, - duration: 200, - onApprove: function () { - $('#removeDependencyID').val(id); - $('#dependencyType').val(type); - $('#removeDependencyForm').submit(); - } - }).modal('show') - ; -} + cache: false, + }, -function initIssueList() { - const repolink = $('#repolink').val(); - const repoId = $('#repoId').val(); - const crossRepoSearch = $('#crossRepoSearch').val(); - let issueSearchUrl = suburl + '/api/v1/repos/' + repolink + '/issues?q={query}'; - if (crossRepoSearch === 'true') { - issueSearchUrl = suburl + '/api/v1/repos/issues/search?q={query}&priority_repo_id=' + repoId; - } - $('#new-dependency-drop-list') - .dropdown({ - apiSettings: { - url: issueSearchUrl, - onResponse: function(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, function(_i, issue) { - // Don't list current issue in the dependency list. - if(issue.id === currIssueId) { - return; - } - filteredResponse.results.push({ - 'name' : '#' + issue.number + ' ' + htmlEncode(issue.title) + - '<div class="text small dont-break-out">' + htmlEncode(issue.repository.full_name) + '</div>', - 'value' : issue.id - }); - }); - return filteredResponse; - }, - cache: false, - }, - - fullTextSearch: true - }); + fullTextSearch: true + }); - $(".menu a.label-filter-item").each(function() { - $(this).click(function(e) { - if (e.altKey) { - e.preventDefault(); + $('.menu a.label-filter-item').each(function () { + $(this).click(function (e) { + if (e.altKey) { + e.preventDefault(); - const href = $(this).attr("href"); - const id = $(this).data("label-id"); + const href = $(this).attr('href'); + const id = $(this).data('label-id'); - const regStr = "labels=(-?[0-9]+%2c)*(" + id + ")(%2c-?[0-9]+)*&"; - const newStr = "labels=$1-$2$3&"; + const regStr = `labels=(-?[0-9]+%2c)*(${id})(%2c-?[0-9]+)*&`; + const newStr = 'labels=$1-$2$3&'; - window.location = href.replace(new RegExp(regStr), newStr); - } - }); + window.location = href.replace(new RegExp(regStr), newStr); + } }); + }); - $(".menu .ui.dropdown.label-filter").keydown(function(e) { - if (e.altKey && e.keyCode == 13) { - const selectedItems = $(".menu .ui.dropdown.label-filter .menu .item.selected"); + $('.menu .ui.dropdown.label-filter').keydown((e) => { + if (e.altKey && e.keyCode === 13) { + const selectedItems = $('.menu .ui.dropdown.label-filter .menu .item.selected'); - if (selectedItems.length > 0) { - const item = $(selectedItems[0]); + if (selectedItems.length > 0) { + const item = $(selectedItems[0]); - const href = item.attr("href"); - const id = item.data("label-id"); + 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&"; + const regStr = `labels=(-?[0-9]+%2c)*(${id})(%2c-?[0-9]+)*&`; + const newStr = 'labels=$1-$2$3&'; - window.location = href.replace(new RegExp(regStr), newStr); - } - } - }); -} -window.cancelCodeComment = function(btn) { - const form = $(btn).closest("form"); - if(form.length > 0 && form.hasClass('comment-form')) { - form.addClass('hide'); - form.parent().find('button.comment-form-reply').show(); - } else { - form.closest('.comment-code-cloud').remove() + window.location = href.replace(new RegExp(regStr), newStr); + } } + }); } -window.onOAuthLoginClick = function() { - const oauthLoader = $('#oauth2-login-loader'); - const oauthNav = $('#oauth2-login-navigator'); - - oauthNav.hide(); - oauthLoader.removeClass('disabled'); - - setTimeout(function(){ - // recover previous content to let user try again - // usually redirection will be performed before this action - oauthLoader.addClass('disabled'); - oauthNav.show(); - },5000); -} +window.cancelCodeComment = function (btn) { + const form = $(btn).closest('form'); + if (form.length > 0 && form.hasClass('comment-form')) { + form.addClass('hide'); + form.parent().find('button.comment-form-reply').show(); + } else { + form.closest('.comment-code-cloud').remove(); + } +}; +window.onOAuthLoginClick = function () { + 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); +}; |