]> source.dussan.org Git - gitea.git/commitdiff
Improve async/await usage, and sort init calls in `index.js` (#17386)
authorwxiaoguang <wxiaoguang@gmail.com>
Tue, 9 Nov 2021 09:27:25 +0000 (17:27 +0800)
committerGitHub <noreply@github.com>
Tue, 9 Nov 2021 09:27:25 +0000 (17:27 +0800)
* clean up async/await, and sort init calls in `index.js
* use `const _promise` to indicate that we do not need await an async function

25 files changed:
web_src/js/features/common-global.js
web_src/js/features/comp/SearchUserBox.js
web_src/js/features/comp/WebHookEditor.js
web_src/js/features/diff.js [deleted file]
web_src/js/features/dropzone.js
web_src/js/features/gitgraph.js [deleted file]
web_src/js/features/heatmap.js
web_src/js/features/imagediff.js
web_src/js/features/issue-content-history.js [deleted file]
web_src/js/features/lastcommitloader.js [deleted file]
web_src/js/features/migration.js [deleted file]
web_src/js/features/notification.js
web_src/js/features/projects.js [deleted file]
web_src/js/features/repo-commit.js
web_src/js/features/repo-diff.js
web_src/js/features/repo-editor.js
web_src/js/features/repo-graph.js [new file with mode: 0644]
web_src/js/features/repo-issue-content.js [new file with mode: 0644]
web_src/js/features/repo-legacy.js
web_src/js/features/repo-migration.js [new file with mode: 0644]
web_src/js/features/repo-projects.js [new file with mode: 0644]
web_src/js/features/repo-settings.js
web_src/js/features/stopwatch.js
web_src/js/index.js
web_src/js/markup/content.js

index 04d44d8142abbbe78e8d4457891a245728ab15f9..da3fb9d1e3836fec05fbf84a4df69e66afdff99a 100644 (file)
@@ -135,11 +135,11 @@ export function initGlobalCommon() {
   });
 }
 
-export async function initGlobalDropzone() {
+export function initGlobalDropzone() {
   // Dropzone
   for (const el of document.querySelectorAll('.dropzone')) {
     const $dropzone = $(el);
-    await createDropzone(el, {
+    const _promise = createDropzone(el, {
       url: $dropzone.data('upload-url'),
       headers: {'X-Csrf-Token': csrfToken},
       maxFiles: $dropzone.data('max-file'),
index 0e7d122391a881e8d5123aa906a2dd99a1322993..1382c7060de4ea07b50151f0f51ce9bface2fa80 100644 (file)
@@ -2,7 +2,7 @@ import {htmlEscape} from 'escape-goat';
 
 const {appSubUrl} = window.config;
 
-export function initSearchUserBox() {
+export function initCompSearchUserBox() {
   const $searchUserBox = $('#search-user-box');
   $searchUserBox.search({
     minCharacters: 2,
index ddff73ee377083df0700cccbb369844505e73c67..efef10ad6406c0b802e67128698db68a28673c1e 100644 (file)
@@ -1,6 +1,6 @@
 const {csrfToken} = window.config;
 
-export function initWebHookEditor() {
+export function initCompWebHookEditor() {
   if ($('.new.webhook').length === 0) {
     return;
   }
diff --git a/web_src/js/features/diff.js b/web_src/js/features/diff.js
deleted file mode 100644 (file)
index ef0aace..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-export function initDiffShowMore() {
-  $('#diff-files, #diff-file-boxes').on('click', '#diff-show-more-files, #diff-show-more-files-stats', (e) => {
-    e.preventDefault();
-
-    if ($(e.target).hasClass('disabled')) {
-      return;
-    }
-    $('#diff-show-more-files, #diff-show-more-files-stats').addClass('disabled');
-
-    const url = $('#diff-show-more-files, #diff-show-more-files-stats').data('href');
-    $.ajax({
-      type: 'GET',
-      url,
-    }).done((resp) => {
-      if (!resp || resp.html === '' || resp.empty) {
-        $('#diff-show-more-files, #diff-show-more-files-stats').removeClass('disabled');
-        return;
-      }
-      $('#diff-too-many-files-stats').remove();
-      $('#diff-files').append($(resp).find('#diff-files li'));
-      $('#diff-incomplete').replaceWith($(resp).find('#diff-file-boxes').children());
-    });
-  });
-}
index 6a4f7e17e49ab71580117743c10df050d927cf97..f3c3a1415fb44d75ff9e23913b57ac3fa8129565 100644 (file)
@@ -3,7 +3,6 @@ export default async function createDropzone(el, opts) {
     import(/* webpackChunkName: "dropzone" */'dropzone'),
     import(/* webpackChunkName: "dropzone" */'dropzone/dist/dropzone.css'),
   ]);
-
   Dropzone.autoDiscover = false;
   return new Dropzone(el, opts);
 }
diff --git a/web_src/js/features/gitgraph.js b/web_src/js/features/gitgraph.js
deleted file mode 100644 (file)
index cd2668a..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-export default async function initGitGraph() {
-  const graphContainer = document.getElementById('git-graph-container');
-  if (!graphContainer) return;
-
-  $('#flow-color-monochrome').on('click', () => {
-    $('#flow-color-monochrome').addClass('active');
-    $('#flow-color-colored').removeClass('active');
-    $('#git-graph-container').removeClass('colored').addClass('monochrome');
-    const params = new URLSearchParams(window.location.search);
-    params.set('mode', 'monochrome');
-    const queryString = params.toString();
-    if (queryString) {
-      window.history.replaceState({}, '', `?${queryString}`);
-    } else {
-      window.history.replaceState({}, '', window.location.pathname);
-    }
-    $('.pagination a').each((_, that) => {
-      const href = $(that).attr('href');
-      if (!href) return;
-      const url = new URL(href, window.location);
-      const params = url.searchParams;
-      params.set('mode', 'monochrome');
-      url.search = `?${params.toString()}`;
-      $(that).attr('href', url.href);
-    });
-  });
-  $('#flow-color-colored').on('click', () => {
-    $('#flow-color-colored').addClass('active');
-    $('#flow-color-monochrome').removeClass('active');
-    $('#git-graph-container').addClass('colored').removeClass('monochrome');
-    $('.pagination a').each((_, that) => {
-      const href = $(that).attr('href');
-      if (!href) return;
-      const url = new URL(href, window.location);
-      const params = url.searchParams;
-      params.delete('mode');
-      url.search = `?${params.toString()}`;
-      $(that).attr('href', url.href);
-    });
-    const params = new URLSearchParams(window.location.search);
-    params.delete('mode');
-    const queryString = params.toString();
-    if (queryString) {
-      window.history.replaceState({}, '', `?${queryString}`);
-    } else {
-      window.history.replaceState({}, '', window.location.pathname);
-    }
-  });
-  const url = new URL(window.location);
-  const params = url.searchParams;
-  const updateGraph = async () => {
-    const queryString = params.toString();
-    const ajaxUrl = new URL(url);
-    ajaxUrl.searchParams.set('div-only', 'true');
-    window.history.replaceState({}, '', queryString ? `?${queryString}` : window.location.pathname);
-    $('#pagination').empty();
-    $('#rel-container').addClass('hide');
-    $('#rev-container').addClass('hide');
-    $('#loading-indicator').removeClass('hide');
-
-    const div = $(await $.ajax(String(ajaxUrl)));
-    $('#pagination').html(div.find('#pagination').html());
-    $('#rel-container').html(div.find('#rel-container').html());
-    $('#rev-container').html(div.find('#rev-container').html());
-    $('#loading-indicator').addClass('hide');
-    $('#rel-container').removeClass('hide');
-    $('#rev-container').removeClass('hide');
-  };
-  const dropdownSelected = params.getAll('branch');
-  if (params.has('hide-pr-refs') && params.get('hide-pr-refs') === 'true') {
-    dropdownSelected.splice(0, 0, '...flow-hide-pr-refs');
-  }
-
-  $('#flow-select-refs-dropdown').dropdown('set selected', dropdownSelected);
-  $('#flow-select-refs-dropdown').dropdown({
-    clearable: true,
-    fullTextSeach: 'exact',
-    onRemove(toRemove) {
-      if (toRemove === '...flow-hide-pr-refs') {
-        params.delete('hide-pr-refs');
-      } else {
-        const branches = params.getAll('branch');
-        params.delete('branch');
-        for (const branch of branches) {
-          if (branch !== toRemove) {
-            params.append('branch', branch);
-          }
-        }
-      }
-      updateGraph();
-    },
-    onAdd(toAdd) {
-      if (toAdd === '...flow-hide-pr-refs') {
-        params.set('hide-pr-refs', true);
-      } else {
-        params.append('branch', toAdd);
-      }
-      updateGraph();
-    },
-  });
-  $('#git-graph-container').on('mouseenter', '#rev-list li', (e) => {
-    const flow = $(e.currentTarget).data('flow');
-    if (flow === 0) return;
-    $(`#flow-${flow}`).addClass('highlight');
-    $(e.currentTarget).addClass('hover');
-    $(`#rev-list li[data-flow='${flow}']`).addClass('highlight');
-  });
-  $('#git-graph-container').on('mouseleave', '#rev-list li', (e) => {
-    const flow = $(e.currentTarget).data('flow');
-    if (flow === 0) return;
-    $(`#flow-${flow}`).removeClass('highlight');
-    $(e.currentTarget).removeClass('hover');
-    $(`#rev-list li[data-flow='${flow}']`).removeClass('highlight');
-  });
-  $('#git-graph-container').on('mouseenter', '#rel-container .flow-group', (e) => {
-    $(e.currentTarget).addClass('highlight');
-    const flow = $(e.currentTarget).data('flow');
-    $(`#rev-list li[data-flow='${flow}']`).addClass('highlight');
-  });
-  $('#git-graph-container').on('mouseleave', '#rel-container .flow-group', (e) => {
-    $(e.currentTarget).removeClass('highlight');
-    const flow = $(e.currentTarget).data('flow');
-    $(`#rev-list li[data-flow='${flow}']`).removeClass('highlight');
-  });
-  $('#git-graph-container').on('mouseenter', '#rel-container .flow-commit', (e) => {
-    const rev = $(e.currentTarget).data('rev');
-    $(`#rev-list li#commit-${rev}`).addClass('hover');
-  });
-  $('#git-graph-container').on('mouseleave', '#rel-container .flow-commit', (e) => {
-    const rev = $(e.currentTarget).data('rev');
-    $(`#rev-list li#commit-${rev}`).removeClass('hover');
-  });
-}
index 07ecaee4616700f2dbbccee565139af40f19ca6c..52b7517c1ac646916f19a7eab9f16ce79804e2f0 100644 (file)
@@ -2,7 +2,7 @@ import Vue from 'vue';
 
 import ActivityHeatmap from '../components/ActivityHeatmap.vue';
 
-export default async function initHeatmap() {
+export default function initHeatmap() {
   const el = document.getElementById('user-heatmap');
   if (!el) return;
 
@@ -24,7 +24,7 @@ export default async function initHeatmap() {
 
     new View().$mount(el);
   } catch (err) {
-    console.error(err);
+    console.error('Heatmap failed to load', err);
     el.textContent = 'Heatmap failed to load';
   }
 }
index 67e9548596ee522da9d80d960c5f55ed4d957df0..d3f90b6260e07643c3afbc3047ca5a3faeb61675 100644 (file)
@@ -29,7 +29,7 @@ function getDefaultSvgBoundsIfUndefined(svgXml, src) {
   }
 }
 
-export default async function initImageDiff() {
+export default function initImageDiff() {
   function createContext(image1, image2) {
     const size1 = {
       width: image1 && image1.width || 0,
diff --git a/web_src/js/features/issue-content-history.js b/web_src/js/features/issue-content-history.js
deleted file mode 100644 (file)
index c71d378..0000000
+++ /dev/null
@@ -1,135 +0,0 @@
-import {svg} from '../svg.js';
-
-const {appSubUrl, csrfToken} = window.config;
-
-let i18nTextEdited;
-let i18nTextOptions;
-let i18nTextDeleteFromHistory;
-let i18nTextDeleteFromHistoryConfirm;
-
-function showContentHistoryDetail(issueBaseUrl, commentId, historyId, itemTitleHtml) {
-  let $dialog = $('.content-history-detail-dialog');
-  if ($dialog.length) return;
-
-  $dialog = $(`
-<div class="ui modal content-history-detail-dialog">
-  <i class="close icon inside"></i>
-  <div class="header">
-    ${itemTitleHtml}
-    <div class="ui dropdown right dialog-header-options" style="display: none; margin-right: 50px;">
-      ${i18nTextOptions} <i class="dropdown icon"></i>
-      <div class="menu">
-        <div class="item red text" data-option-item="delete">${i18nTextDeleteFromHistory}</div>
-      </div>
-    </div>
-  </div>
-  <!-- ".modal .content" style was polluted in "_base.less": "&.modal > .content"  -->
-  <div class="scrolling content" style="text-align: left; min-height: 30vh;">
-      <div class="ui loader active"></div>
-  </div>
-</div>`);
-  $dialog.appendTo($('body'));
-  $dialog.find('.dialog-header-options').dropdown({
-    showOnFocus: false,
-    allowReselection: true,
-    onChange(_value, _text, $item) {
-      const optionItem = $item.data('option-item');
-      if (optionItem === 'delete') {
-        if (window.confirm(i18nTextDeleteFromHistoryConfirm)) {
-          $.post(`${issueBaseUrl}/content-history/soft-delete?comment_id=${commentId}&history_id=${historyId}`, {
-            _csrf: csrfToken,
-          }).done((resp) => {
-            if (resp.ok) {
-              $dialog.modal('hide');
-            } else {
-              alert(resp.message);
-            }
-          });
-        }
-      } else { // required by eslint
-        window.alert(`unknown option item: ${optionItem}`);
-      }
-    },
-    onHide() {
-      $(this).dropdown('clear', true);
-    }
-  });
-  $dialog.modal({
-    onShow() {
-      $.ajax({
-        url: `${issueBaseUrl}/content-history/detail?comment_id=${commentId}&history_id=${historyId}`,
-        data: {
-          _csrf: csrfToken,
-        },
-      }).done((resp) => {
-        $dialog.find('.content').html(resp.diffHtml);
-        // there is only one option "item[data-option-item=delete]", so the dropdown can be entirely shown/hidden.
-        if (resp.canSoftDelete) {
-          $dialog.find('.dialog-header-options').show();
-        }
-      });
-    },
-    onHidden() {
-      $dialog.remove();
-    },
-  }).modal('show');
-}
-
-function showContentHistoryMenu(issueBaseUrl, $item, commentId) {
-  const $headerLeft = $item.find('.comment-header-left');
-  const menuHtml = `
-  <div class="ui pointing dropdown top left content-history-menu" data-comment-id="${commentId}">
-    <a>&bull; ${i18nTextEdited} ${svg('octicon-triangle-down', 17)}</a>
-    <div class="menu">
-    </div>
-  </div>`;
-
-  $headerLeft.find(`.content-history-menu`).remove();
-  $headerLeft.append($(menuHtml));
-  $headerLeft.find('.dropdown').dropdown({
-    action: 'hide',
-    apiSettings: {
-      cache: false,
-      url: `${issueBaseUrl}/content-history/list?comment_id=${commentId}`,
-    },
-    saveRemoteData: false,
-    onHide() {
-      $(this).dropdown('change values', null);
-    },
-    onChange(value, itemHtml, $item) {
-      if (value && !$item.find('[data-history-is-deleted=1]').length) {
-        showContentHistoryDetail(issueBaseUrl, commentId, value, itemHtml);
-      }
-    },
-  });
-}
-
-export function initIssueContentHistory() {
-  const issueIndex = $('#issueIndex').val();
-  const $itemIssue = $('.timeline-item.comment.first');
-  if (!issueIndex || !$itemIssue.length) return;
-
-  const repoLink = $('#repolink').val();
-  const issueBaseUrl = `${appSubUrl}/${repoLink}/issues/${issueIndex}`;
-
-  $.ajax({
-    url: `${issueBaseUrl}/content-history/overview`,
-    data: {
-      _csrf: csrfToken,
-    },
-  }).done((resp) => {
-    i18nTextEdited = resp.i18n.textEdited;
-    i18nTextDeleteFromHistory = resp.i18n.textDeleteFromHistory;
-    i18nTextDeleteFromHistoryConfirm = resp.i18n.textDeleteFromHistoryConfirm;
-    i18nTextOptions = resp.i18n.textOptions;
-
-    if (resp.editedHistoryCountMap[0]) {
-      showContentHistoryMenu(issueBaseUrl, $itemIssue, '0');
-    }
-    for (const [commentId, _editedCount] of Object.entries(resp.editedHistoryCountMap)) {
-      if (commentId === '0') continue;
-      const $itemComment = $(`#issuecomment-${commentId}`);
-      showContentHistoryMenu(issueBaseUrl, $itemComment, commentId);
-    }
-  });
-}
diff --git a/web_src/js/features/lastcommitloader.js b/web_src/js/features/lastcommitloader.js
deleted file mode 100644 (file)
index 04e1e45..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-const {csrfToken} = window.config;
-
-export async function initLastCommitLoader() {
-  const entryMap = {};
-
-  const entries = $('table#repo-files-table tr.notready')
-    .map((_, v) => {
-      entryMap[$(v).attr('data-entryname')] = $(v);
-      return $(v).attr('data-entryname');
-    })
-    .get();
-
-  if (entries.length === 0) {
-    return;
-  }
-
-  const lastCommitLoaderURL = $('table#repo-files-table').data('lastCommitLoaderUrl');
-
-  if (entries.length > 200) {
-    $.post(lastCommitLoaderURL, {
-      _csrf: csrfToken,
-    }, (data) => {
-      $('table#repo-files-table').replaceWith(data);
-    });
-    return;
-  }
-
-  $.post(lastCommitLoaderURL, {
-    _csrf: csrfToken,
-    'f': entries,
-  }, (data) => {
-    $(data).find('tr').each((_, row) => {
-      if (row.className === 'commit-list') {
-        $('table#repo-files-table .commit-list').replaceWith(row);
-        return;
-      }
-      entryMap[$(row).attr('data-entryname')].replaceWith(row);
-    });
-  });
-}
diff --git a/web_src/js/features/migration.js b/web_src/js/features/migration.js
deleted file mode 100644 (file)
index d41dc3c..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-const $service = $('#service_type');
-const $user = $('#auth_username');
-const $pass = $('#auth_password');
-const $token = $('#auth_token');
-const $mirror = $('#mirror');
-const $lfs = $('#lfs');
-const $lfsSettings = $('#lfs_settings');
-const $lfsEndpoint = $('#lfs_endpoint');
-const $items = $('#migrate_items').find('input[type=checkbox]');
-
-export default function initMigration() {
-  checkAuth();
-  setLFSSettingsVisibility();
-
-  $user.on('keyup', () => {checkItems(false)});
-  $pass.on('keyup', () => {checkItems(false)});
-  $token.on('keyup', () => {checkItems(true)});
-  $mirror.on('change', () => {checkItems(true)});
-  $('#lfs_settings_show').on('click', () => { $lfsEndpoint.show(); return false });
-  $lfs.on('change', setLFSSettingsVisibility);
-
-  const $cloneAddr = $('#clone_addr');
-  $cloneAddr.on('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 checkAuth() {
-  const serviceType = $service.val();
-
-  checkItems(serviceType !== 1);
-}
-
-function checkItems(tokenAuth) {
-  let enableItems;
-  if (tokenAuth) {
-    enableItems = $token.val() !== '';
-  } else {
-    enableItems = $user.val() !== '' || $pass.val() !== '';
-  }
-  if (enableItems && $service.val() > 1) {
-    if ($mirror.is(':checked')) {
-      $items.not('[name="wiki"]').attr('disabled', true);
-      $items.filter('[name="wiki"]').attr('disabled', false);
-      return;
-    }
-    $items.attr('disabled', false);
-  } else {
-    $items.attr('disabled', true);
-  }
-}
-
-function setLFSSettingsVisibility() {
-  const visible = $lfs.is(':checked');
-  $lfsSettings.toggle(visible);
-  $lfsEndpoint.hide();
-}
index 71ebb538d4af0c17bc131592cd356980980317d1..f4c31c5ede9218bf606e372e885cf678eff65422 100644 (file)
@@ -40,7 +40,7 @@ async function receiveUpdateCount(event) {
   }
 }
 
-export async function initNotificationCount() {
+export function initNotificationCount() {
   const notificationCount = $('.notification_count');
 
   if (!notificationCount.length) {
@@ -66,7 +66,7 @@ export async function initNotificationCount() {
         return;
       }
       if (event.data.type === 'notification-count') {
-        receiveUpdateCount(event.data);
+        const _promise = receiveUpdateCount(event.data);
       } else if (event.data.type === 'error') {
         console.error(event.data);
       } else if (event.data.type === 'logout') {
diff --git a/web_src/js/features/projects.js b/web_src/js/features/projects.js
deleted file mode 100644 (file)
index 736c096..0000000
+++ /dev/null
@@ -1,188 +0,0 @@
-const {csrfToken} = window.config;
-
-export default async function initProject() {
-  if (!$('.repository.projects').length) {
-    return;
-  }
-
-  const {Sortable} = await import(/* webpackChunkName: "sortable" */'sortablejs');
-  const boardColumns = document.getElementsByClassName('board-column');
-
-  new Sortable(
-    document.getElementsByClassName('board')[0],
-    {
-      group: 'board-column',
-      draggable: '.board-column',
-      animation: 150,
-      ghostClass: 'card-ghost',
-      onSort: () => {
-        const board = document.getElementsByClassName('board')[0];
-        const boardColumns = board.getElementsByClassName('board-column');
-
-        boardColumns.forEach((column, i) => {
-          if (parseInt($(column).data('sorting')) !== i) {
-            $.ajax({
-              url: $(column).data('url'),
-              data: JSON.stringify({sorting: i, color: rgbToHex($(column).css('backgroundColor'))}),
-              headers: {
-                'X-Csrf-Token': csrfToken,
-                'X-Remote': true,
-              },
-              contentType: 'application/json',
-              method: 'PUT',
-            });
-          }
-        });
-      },
-    },
-  );
-
-  for (const column of boardColumns) {
-    new Sortable(
-      column.getElementsByClassName('board')[0],
-      {
-        group: 'shared',
-        animation: 150,
-        ghostClass: 'card-ghost',
-        onAdd: (e) => {
-          $.ajax(`${e.to.dataset.url}/${e.item.dataset.issue}`, {
-            headers: {
-              'X-Csrf-Token': csrfToken,
-              'X-Remote': true,
-            },
-            contentType: 'application/json',
-            type: 'POST',
-            error: () => {
-              e.from.insertBefore(e.item, e.from.children[e.oldIndex]);
-            },
-          });
-        },
-      },
-    );
-  }
-
-  $('.edit-project-board').each(function () {
-    const projectHeader = $(this).closest('.board-column-header');
-    const projectTitleLabel = projectHeader.find('.board-label');
-    const projectTitleInput = $(this).find(
-      '.content > .form > .field > .project-board-title',
-    );
-    const projectColorInput = $(this).find('.content > .form > .field  #new_board_color');
-    const boardColumn = $(this).closest('.board-column');
-
-    if (boardColumn.css('backgroundColor')) {
-      setLabelColor(projectHeader, rgbToHex(boardColumn.css('backgroundColor')));
-    }
-
-    $(this)
-      .find('.content > .form > .actions > .red')
-      .on('click', function (e) {
-        e.preventDefault();
-
-        $.ajax({
-          url: $(this).data('url'),
-          data: JSON.stringify({title: projectTitleInput.val(), color: projectColorInput.val()}),
-          headers: {
-            'X-Csrf-Token': csrfToken,
-            'X-Remote': true,
-          },
-          contentType: 'application/json',
-          method: 'PUT',
-        }).done(() => {
-          projectTitleLabel.text(projectTitleInput.val());
-          projectTitleInput.closest('form').removeClass('dirty');
-          if (projectColorInput.val()) {
-            setLabelColor(projectHeader, projectColorInput.val());
-          }
-          boardColumn.attr('style', `background: ${projectColorInput.val()}!important`);
-          $('.ui.modal').modal('hide');
-        });
-      });
-  });
-
-  $(document).on('click', '.set-default-project-board', async function (e) {
-    e.preventDefault();
-
-    await $.ajax({
-      method: 'POST',
-      url: $(this).data('url'),
-      headers: {
-        'X-Csrf-Token': csrfToken,
-        'X-Remote': true,
-      },
-      contentType: 'application/json',
-    });
-
-    window.location.reload();
-  });
-
-  $('.delete-project-board').each(function () {
-    $(this).click(function (e) {
-      e.preventDefault();
-
-      $.ajax({
-        url: $(this).data('url'),
-        headers: {
-          'X-Csrf-Token': csrfToken,
-          'X-Remote': true,
-        },
-        contentType: 'application/json',
-        method: 'DELETE',
-      }).done(() => {
-        window.location.reload();
-      });
-    });
-  });
-
-  $('#new_board_submit').click(function (e) {
-    e.preventDefault();
-
-    const boardTitle = $('#new_board');
-    const projectColorInput = $('#new_board_color_picker');
-
-    $.ajax({
-      url: $(this).data('url'),
-      data: JSON.stringify({title: boardTitle.val(), color: projectColorInput.val()}),
-      headers: {
-        'X-Csrf-Token': csrfToken,
-        'X-Remote': true,
-      },
-      contentType: 'application/json',
-      method: 'POST',
-    }).done(() => {
-      boardTitle.closest('form').removeClass('dirty');
-      window.location.reload();
-    });
-  });
-}
-
-function setLabelColor(label, color) {
-  const red = getRelativeColor(parseInt(color.substr(1, 2), 16));
-  const green = getRelativeColor(parseInt(color.substr(3, 2), 16));
-  const blue = getRelativeColor(parseInt(color.substr(5, 2), 16));
-  const luminance = 0.2126 * red + 0.7152 * green + 0.0722 * blue;
-
-  if (luminance > 0.179) {
-    label.removeClass('light-label').addClass('dark-label');
-  } else {
-    label.removeClass('dark-label').addClass('light-label');
-  }
-}
-
-/**
- * Inspired by W3C recommandation https://www.w3.org/TR/WCAG20/#relativeluminancedef
- */
-function getRelativeColor(color) {
-  color /= 255;
-  return color <= 0.03928 ? color / 12.92 : ((color + 0.055) / 1.055) ** 2.4;
-}
-
-function rgbToHex(rgb) {
-  rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
-  return `#${hex(rgb[1])}${hex(rgb[2])}${hex(rgb[3])}`;
-}
-
-function hex(x) {
-  const hexDigits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
-  return Number.isNaN(x) ? '00' : hexDigits[(x - x % 16) / 16] + hexDigits[x % 16];
-}
index 336a37d6547251ab33478b1639a1a66fb0b51679..847fed3f1d77936e945df975e59c767577d64ddb 100644 (file)
@@ -1,6 +1,47 @@
+const {csrfToken} = window.config;
+
 export function initRepoCommitButton() {
   $('.commit-button').on('click', function (e) {
     e.preventDefault();
     $(this).parent().find('.commit-body').toggle();
   });
 }
+
+export function initRepoCommitLastCommitLoader() {
+  const entryMap = {};
+
+  const entries = $('table#repo-files-table tr.notready')
+    .map((_, v) => {
+      entryMap[$(v).attr('data-entryname')] = $(v);
+      return $(v).attr('data-entryname');
+    })
+    .get();
+
+  if (entries.length === 0) {
+    return;
+  }
+
+  const lastCommitLoaderURL = $('table#repo-files-table').data('lastCommitLoaderUrl');
+
+  if (entries.length > 200) {
+    $.post(lastCommitLoaderURL, {
+      _csrf: csrfToken,
+    }, (data) => {
+      $('table#repo-files-table').replaceWith(data);
+    });
+    return;
+  }
+
+  $.post(lastCommitLoaderURL, {
+    _csrf: csrfToken,
+    'f': entries,
+  }, (data) => {
+    $(data).find('tr').each((_, row) => {
+      if (row.className === 'commit-list') {
+        $('table#repo-files-table .commit-list').replaceWith(row);
+        return;
+      }
+      entryMap[$(row).attr('data-entryname')].replaceWith(row);
+    });
+  });
+}
index c3fb78a683ec97b1cee4287c695dc8373f9e03c8..76355615c646161f5a0294f4e9a02e5764806b91 100644 (file)
@@ -79,3 +79,28 @@ export function initRepoDiffConversationNav() {
     window.location.href = `#${anchor}`;
   });
 }
+
+export function initRepoDiffShowMore() {
+  $('#diff-files, #diff-file-boxes').on('click', '#diff-show-more-files, #diff-show-more-files-stats', (e) => {
+    e.preventDefault();
+
+    if ($(e.target).hasClass('disabled')) {
+      return;
+    }
+    $('#diff-show-more-files, #diff-show-more-files-stats').addClass('disabled');
+
+    const url = $('#diff-show-more-files, #diff-show-more-files-stats').data('href');
+    $.ajax({
+      type: 'GET',
+      url,
+    }).done((resp) => {
+      if (!resp || resp.html === '' || resp.empty) {
+        $('#diff-show-more-files, #diff-show-more-files-stats').removeClass('disabled');
+        return;
+      }
+      $('#diff-too-many-files-stats').remove();
+      $('#diff-files').append($(resp).find('#diff-files li'));
+      $('#diff-incomplete').replaceWith($(resp).find('#diff-file-boxes').children());
+    });
+  });
+}
index e2acaafd12a6d7ea5dd1ad9b8ca02c111a51c06b..7bf401207ad139c110c256223958e74195a60eb6 100644 (file)
@@ -24,7 +24,7 @@ function initEditPreviewTab($form) {
         _csrf: csrfToken,
         mode,
         context,
-        text: $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val()
+        text: $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val(),
       }, (data) => {
         const $previewPanel = $form.find(`.tab[data-tab="${$tabMenu.data('preview')}"]`);
         $previewPanel.html(data);
@@ -42,7 +42,7 @@ function initEditDiffTab($form) {
     $.post($this.data('url'), {
       _csrf: csrfToken,
       context: $this.data('context'),
-      content: $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val()
+      content: $form.find(`.tab[data-tab="${$tabMenu.data('write')}"] textarea`).val(),
     }, (data) => {
       const $diffPreviewPanel = $form.find(`.tab[data-tab="${$tabMenu.data('diff')}"]`);
       $diffPreviewPanel.html(data);
@@ -75,7 +75,7 @@ function getCursorPosition($e) {
   return pos;
 }
 
-export async function initRepoEditor() {
+export function initRepoEditor() {
   initEditorForm();
 
   $('.js-quick-pull-choice-option').on('change', function () {
@@ -134,47 +134,49 @@ export async function initRepoEditor() {
   const $editArea = $('.repository.editor textarea#edit_area');
   if (!$editArea.length) return;
 
-  const editor = await createCodeEditor($editArea[0], $editFilename[0], previewFileModes);
+  (async () => {
+    const editor = await createCodeEditor($editArea[0], $editFilename[0], previewFileModes);
 
-  // Using events from https://github.com/codedance/jquery.AreYouSure#advanced-usage
-  // to enable or disable the commit button
-  const $commitButton = $('#commit-button');
-  const $editForm = $('.ui.edit.form');
-  const dirtyFileClass = 'dirty-file';
+    // Using events from https://github.com/codedance/jquery.AreYouSure#advanced-usage
+    // to enable or disable the commit button
+    const $commitButton = $('#commit-button');
+    const $editForm = $('.ui.edit.form');
+    const dirtyFileClass = 'dirty-file';
 
-  // Disabling the button at the start
-  if ($('input[name="page_has_posted"]').val() !== 'true') {
-    $commitButton.prop('disabled', true);
-  }
-
-  // Registering a custom listener for the file path and the file content
-  $editForm.areYouSure({
-    silent: true,
-    dirtyClass: dirtyFileClass,
-    fieldSelector: ':input:not(.commit-form-wrapper :input)',
-    change() {
-      const dirty = $(this).hasClass(dirtyFileClass);
-      $commitButton.prop('disabled', !dirty);
+    // Disabling the button at the start
+    if ($('input[name="page_has_posted"]').val() !== 'true') {
+      $commitButton.prop('disabled', true);
     }
-  });
 
-  // Update the editor from query params, if available,
-  // only after the dirtyFileClass initialization
-  const params = new URLSearchParams(window.location.search);
-  const value = params.get('value');
-  if (value) {
-    editor.setValue(value);
-  }
+    // 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.on('click', (event) => {
-    // A modal which asks if an empty file should be committed
-    if ($editArea.val().length === 0) {
-      $('#edit-empty-content-modal').modal({
-        onApprove() {
-          $('.edit.form').trigger('submit');
-        }
-      }).modal('show');
-      event.preventDefault();
+    // Update the editor from query params, if available,
+    // only after the dirtyFileClass initialization
+    const params = new URLSearchParams(window.location.search);
+    const value = params.get('value');
+    if (value) {
+      editor.setValue(value);
     }
-  });
+
+    $commitButton.on('click', (event) => {
+      // A modal which asks if an empty file should be committed
+      if ($editArea.val().length === 0) {
+        $('#edit-empty-content-modal').modal({
+          onApprove() {
+            $('.edit.form').trigger('submit');
+          },
+        }).modal('show');
+        event.preventDefault();
+      }
+    });
+  })();
 }
diff --git a/web_src/js/features/repo-graph.js b/web_src/js/features/repo-graph.js
new file mode 100644 (file)
index 0000000..007cf9b
--- /dev/null
@@ -0,0 +1,133 @@
+export default function initRepoGraphGit() {
+  const graphContainer = document.getElementById('git-graph-container');
+  if (!graphContainer) return;
+
+  $('#flow-color-monochrome').on('click', () => {
+    $('#flow-color-monochrome').addClass('active');
+    $('#flow-color-colored').removeClass('active');
+    $('#git-graph-container').removeClass('colored').addClass('monochrome');
+    const params = new URLSearchParams(window.location.search);
+    params.set('mode', 'monochrome');
+    const queryString = params.toString();
+    if (queryString) {
+      window.history.replaceState({}, '', `?${queryString}`);
+    } else {
+      window.history.replaceState({}, '', window.location.pathname);
+    }
+    $('.pagination a').each((_, that) => {
+      const href = $(that).attr('href');
+      if (!href) return;
+      const url = new URL(href, window.location);
+      const params = url.searchParams;
+      params.set('mode', 'monochrome');
+      url.search = `?${params.toString()}`;
+      $(that).attr('href', url.href);
+    });
+  });
+  $('#flow-color-colored').on('click', () => {
+    $('#flow-color-colored').addClass('active');
+    $('#flow-color-monochrome').removeClass('active');
+    $('#git-graph-container').addClass('colored').removeClass('monochrome');
+    $('.pagination a').each((_, that) => {
+      const href = $(that).attr('href');
+      if (!href) return;
+      const url = new URL(href, window.location);
+      const params = url.searchParams;
+      params.delete('mode');
+      url.search = `?${params.toString()}`;
+      $(that).attr('href', url.href);
+    });
+    const params = new URLSearchParams(window.location.search);
+    params.delete('mode');
+    const queryString = params.toString();
+    if (queryString) {
+      window.history.replaceState({}, '', `?${queryString}`);
+    } else {
+      window.history.replaceState({}, '', window.location.pathname);
+    }
+  });
+  const url = new URL(window.location);
+  const params = url.searchParams;
+  const updateGraph = async () => {
+    const queryString = params.toString();
+    const ajaxUrl = new URL(url);
+    ajaxUrl.searchParams.set('div-only', 'true');
+    window.history.replaceState({}, '', queryString ? `?${queryString}` : window.location.pathname);
+    $('#pagination').empty();
+    $('#rel-container').addClass('hide');
+    $('#rev-container').addClass('hide');
+    $('#loading-indicator').removeClass('hide');
+
+    const div = $(await $.ajax(String(ajaxUrl)));
+    $('#pagination').html(div.find('#pagination').html());
+    $('#rel-container').html(div.find('#rel-container').html());
+    $('#rev-container').html(div.find('#rev-container').html());
+    $('#loading-indicator').addClass('hide');
+    $('#rel-container').removeClass('hide');
+    $('#rev-container').removeClass('hide');
+  };
+  const dropdownSelected = params.getAll('branch');
+  if (params.has('hide-pr-refs') && params.get('hide-pr-refs') === 'true') {
+    dropdownSelected.splice(0, 0, '...flow-hide-pr-refs');
+  }
+
+  $('#flow-select-refs-dropdown').dropdown('set selected', dropdownSelected);
+  $('#flow-select-refs-dropdown').dropdown({
+    clearable: true,
+    fullTextSeach: 'exact',
+    onRemove(toRemove) {
+      if (toRemove === '...flow-hide-pr-refs') {
+        params.delete('hide-pr-refs');
+      } else {
+        const branches = params.getAll('branch');
+        params.delete('branch');
+        for (const branch of branches) {
+          if (branch !== toRemove) {
+            params.append('branch', branch);
+          }
+        }
+      }
+      updateGraph();
+    },
+    onAdd(toAdd) {
+      if (toAdd === '...flow-hide-pr-refs') {
+        params.set('hide-pr-refs', true);
+      } else {
+        params.append('branch', toAdd);
+      }
+      updateGraph();
+    },
+  });
+  $('#git-graph-container').on('mouseenter', '#rev-list li', (e) => {
+    const flow = $(e.currentTarget).data('flow');
+    if (flow === 0) return;
+    $(`#flow-${flow}`).addClass('highlight');
+    $(e.currentTarget).addClass('hover');
+    $(`#rev-list li[data-flow='${flow}']`).addClass('highlight');
+  });
+  $('#git-graph-container').on('mouseleave', '#rev-list li', (e) => {
+    const flow = $(e.currentTarget).data('flow');
+    if (flow === 0) return;
+    $(`#flow-${flow}`).removeClass('highlight');
+    $(e.currentTarget).removeClass('hover');
+    $(`#rev-list li[data-flow='${flow}']`).removeClass('highlight');
+  });
+  $('#git-graph-container').on('mouseenter', '#rel-container .flow-group', (e) => {
+    $(e.currentTarget).addClass('highlight');
+    const flow = $(e.currentTarget).data('flow');
+    $(`#rev-list li[data-flow='${flow}']`).addClass('highlight');
+  });
+  $('#git-graph-container').on('mouseleave', '#rel-container .flow-group', (e) => {
+    $(e.currentTarget).removeClass('highlight');
+    const flow = $(e.currentTarget).data('flow');
+    $(`#rev-list li[data-flow='${flow}']`).removeClass('highlight');
+  });
+  $('#git-graph-container').on('mouseenter', '#rel-container .flow-commit', (e) => {
+    const rev = $(e.currentTarget).data('rev');
+    $(`#rev-list li#commit-${rev}`).addClass('hover');
+  });
+  $('#git-graph-container').on('mouseleave', '#rel-container .flow-commit', (e) => {
+    const rev = $(e.currentTarget).data('rev');
+    $(`#rev-list li#commit-${rev}`).removeClass('hover');
+  });
+}
diff --git a/web_src/js/features/repo-issue-content.js b/web_src/js/features/repo-issue-content.js
new file mode 100644 (file)
index 0000000..a2fc6c3
--- /dev/null
@@ -0,0 +1,135 @@
+import {svg} from '../svg.js';
+
+const {appSubUrl, csrfToken} = window.config;
+
+let i18nTextEdited;
+let i18nTextOptions;
+let i18nTextDeleteFromHistory;
+let i18nTextDeleteFromHistoryConfirm;
+
+function showContentHistoryDetail(issueBaseUrl, commentId, historyId, itemTitleHtml) {
+  let $dialog = $('.content-history-detail-dialog');
+  if ($dialog.length) return;
+
+  $dialog = $(`
+<div class="ui modal content-history-detail-dialog">
+  <i class="close icon inside"></i>
+  <div class="header">
+    ${itemTitleHtml}
+    <div class="ui dropdown right dialog-header-options" style="display: none; margin-right: 50px;">
+      ${i18nTextOptions} <i class="dropdown icon"></i>
+      <div class="menu">
+        <div class="item red text" data-option-item="delete">${i18nTextDeleteFromHistory}</div>
+      </div>
+    </div>
+  </div>
+  <!-- ".modal .content" style was polluted in "_base.less": "&.modal > .content"  -->
+  <div class="scrolling content" style="text-align: left; min-height: 30vh;">
+      <div class="ui loader active"></div>
+  </div>
+</div>`);
+  $dialog.appendTo($('body'));
+  $dialog.find('.dialog-header-options').dropdown({
+    showOnFocus: false,
+    allowReselection: true,
+    onChange(_value, _text, $item) {
+      const optionItem = $item.data('option-item');
+      if (optionItem === 'delete') {
+        if (window.confirm(i18nTextDeleteFromHistoryConfirm)) {
+          $.post(`${issueBaseUrl}/content-history/soft-delete?comment_id=${commentId}&history_id=${historyId}`, {
+            _csrf: csrfToken,
+          }).done((resp) => {
+            if (resp.ok) {
+              $dialog.modal('hide');
+            } else {
+              alert(resp.message);
+            }
+          });
+        }
+      } else { // required by eslint
+        window.alert(`unknown option item: ${optionItem}`);
+      }
+    },
+    onHide() {
+      $(this).dropdown('clear', true);
+    }
+  });
+  $dialog.modal({
+    onShow() {
+      $.ajax({
+        url: `${issueBaseUrl}/content-history/detail?comment_id=${commentId}&history_id=${historyId}`,
+        data: {
+          _csrf: csrfToken,
+        },
+      }).done((resp) => {
+        $dialog.find('.content').html(resp.diffHtml);
+        // there is only one option "item[data-option-item=delete]", so the dropdown can be entirely shown/hidden.
+        if (resp.canSoftDelete) {
+          $dialog.find('.dialog-header-options').show();
+        }
+      });
+    },
+    onHidden() {
+      $dialog.remove();
+    },
+  }).modal('show');
+}
+
+function showContentHistoryMenu(issueBaseUrl, $item, commentId) {
+  const $headerLeft = $item.find('.comment-header-left');
+  const menuHtml = `
+  <div class="ui pointing dropdown top left content-history-menu" data-comment-id="${commentId}">
+    <a>&bull; ${i18nTextEdited} ${svg('octicon-triangle-down', 17)}</a>
+    <div class="menu">
+    </div>
+  </div>`;
+
+  $headerLeft.find(`.content-history-menu`).remove();
+  $headerLeft.append($(menuHtml));
+  $headerLeft.find('.dropdown').dropdown({
+    action: 'hide',
+    apiSettings: {
+      cache: false,
+      url: `${issueBaseUrl}/content-history/list?comment_id=${commentId}`,
+    },
+    saveRemoteData: false,
+    onHide() {
+      $(this).dropdown('change values', null);
+    },
+    onChange(value, itemHtml, $item) {
+      if (value && !$item.find('[data-history-is-deleted=1]').length) {
+        showContentHistoryDetail(issueBaseUrl, commentId, value, itemHtml);
+      }
+    },
+  });
+}
+
+export function initRepoIssueContentHistory() {
+  const issueIndex = $('#issueIndex').val();
+  const $itemIssue = $('.timeline-item.comment.first');
+  if (!issueIndex || !$itemIssue.length) return;
+
+  const repoLink = $('#repolink').val();
+  const issueBaseUrl = `${appSubUrl}/${repoLink}/issues/${issueIndex}`;
+
+  $.ajax({
+    url: `${issueBaseUrl}/content-history/overview`,
+    data: {
+      _csrf: csrfToken,
+    },
+  }).done((resp) => {
+    i18nTextEdited = resp.i18n.textEdited;
+    i18nTextDeleteFromHistory = resp.i18n.textDeleteFromHistory;
+    i18nTextDeleteFromHistoryConfirm = resp.i18n.textDeleteFromHistoryConfirm;
+    i18nTextOptions = resp.i18n.textOptions;
+
+    if (resp.editedHistoryCountMap[0]) {
+      showContentHistoryMenu(issueBaseUrl, $itemIssue, '0');
+    }
+    for (const [commentId, _editedCount] of Object.entries(resp.editedHistoryCountMap)) {
+      if (commentId === '0') continue;
+      const $itemComment = $(`#issuecomment-${commentId}`);
+      showContentHistoryMenu(issueBaseUrl, $itemComment, commentId);
+    }
+  });
+}
index 6692d1902c9332c5ce705c538c87c045cfb84eed..f4a8c0cf3e9fee1ddeb819187121399f5ad9e0dc 100644 (file)
@@ -259,7 +259,7 @@ export function initRepoCommentForm() {
 }
 
 
-export async function initRepository() {
+export function initRepository() {
   if ($('.repository').length === 0) {
     return;
   }
@@ -363,7 +363,7 @@ export async function initRepository() {
       if ($editContentZone.html().length === 0) {
         $editContentZone.html($('#edit-content-form').html());
         $textarea = $editContentZone.find('textarea');
-        attachTribute($textarea.get(), {mentions: true, emoji: true});
+        await attachTribute($textarea.get(), {mentions: true, emoji: true});
 
         let dz;
         const $dropzone = $editContentZone.find('.dropzone');
diff --git a/web_src/js/features/repo-migration.js b/web_src/js/features/repo-migration.js
new file mode 100644 (file)
index 0000000..6e59d65
--- /dev/null
@@ -0,0 +1,60 @@
+const $service = $('#service_type');
+const $user = $('#auth_username');
+const $pass = $('#auth_password');
+const $token = $('#auth_token');
+const $mirror = $('#mirror');
+const $lfs = $('#lfs');
+const $lfsSettings = $('#lfs_settings');
+const $lfsEndpoint = $('#lfs_endpoint');
+const $items = $('#migrate_items').find('input[type=checkbox]');
+
+export default function initRepoMigration() {
+  checkAuth();
+  setLFSSettingsVisibility();
+
+  $user.on('keyup', () => {checkItems(false)});
+  $pass.on('keyup', () => {checkItems(false)});
+  $token.on('keyup', () => {checkItems(true)});
+  $mirror.on('change', () => {checkItems(true)});
+  $('#lfs_settings_show').on('click', () => { $lfsEndpoint.show(); return false });
+  $lfs.on('change', setLFSSettingsVisibility);
+
+  const $cloneAddr = $('#clone_addr');
+  $cloneAddr.on('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 checkAuth() {
+  const serviceType = $service.val();
+
+  checkItems(serviceType !== 1);
+}
+
+function checkItems(tokenAuth) {
+  let enableItems;
+  if (tokenAuth) {
+    enableItems = $token.val() !== '';
+  } else {
+    enableItems = $user.val() !== '' || $pass.val() !== '';
+  }
+  if (enableItems && $service.val() > 1) {
+    if ($mirror.is(':checked')) {
+      $items.not('[name="wiki"]').attr('disabled', true);
+      $items.filter('[name="wiki"]').attr('disabled', false);
+      return;
+    }
+    $items.attr('disabled', false);
+  } else {
+    $items.attr('disabled', true);
+  }
+}
+
+function setLFSSettingsVisibility() {
+  const visible = $lfs.is(':checked');
+  $lfsSettings.toggle(visible);
+  $lfsEndpoint.hide();
+}
diff --git a/web_src/js/features/repo-projects.js b/web_src/js/features/repo-projects.js
new file mode 100644 (file)
index 0000000..995b971
--- /dev/null
@@ -0,0 +1,194 @@
+const {csrfToken} = window.config;
+
+async function initRepoProjectSortable() {
+  const {Sortable} = await import(/* webpackChunkName: "sortable" */'sortablejs');
+  const boardColumns = document.getElementsByClassName('board-column');
+
+  new Sortable(
+    document.getElementsByClassName('board')[0],
+    {
+      group: 'board-column',
+      draggable: '.board-column',
+      animation: 150,
+      ghostClass: 'card-ghost',
+      onSort: () => {
+        const board = document.getElementsByClassName('board')[0];
+        const boardColumns = board.getElementsByClassName('board-column');
+
+        boardColumns.forEach((column, i) => {
+          if (parseInt($(column).data('sorting')) !== i) {
+            $.ajax({
+              url: $(column).data('url'),
+              data: JSON.stringify({sorting: i, color: rgbToHex($(column).css('backgroundColor'))}),
+              headers: {
+                'X-Csrf-Token': csrfToken,
+                'X-Remote': true,
+              },
+              contentType: 'application/json',
+              method: 'PUT',
+            });
+          }
+        });
+      },
+    },
+  );
+
+  for (const column of boardColumns) {
+    new Sortable(
+      column.getElementsByClassName('board')[0],
+      {
+        group: 'shared',
+        animation: 150,
+        ghostClass: 'card-ghost',
+        onAdd: (e) => {
+          $.ajax(`${e.to.dataset.url}/${e.item.dataset.issue}`, {
+            headers: {
+              'X-Csrf-Token': csrfToken,
+              'X-Remote': true,
+            },
+            contentType: 'application/json',
+            type: 'POST',
+            error: () => {
+              e.from.insertBefore(e.item, e.from.children[e.oldIndex]);
+            },
+          });
+        },
+      },
+    );
+  }
+}
+
+export default function initRepoProject() {
+  if (!$('.repository.projects').length) {
+    return;
+  }
+
+  (async () => {
+    await initRepoProjectSortable();
+  })();
+
+  $('.edit-project-board').each(function () {
+    const projectHeader = $(this).closest('.board-column-header');
+    const projectTitleLabel = projectHeader.find('.board-label');
+    const projectTitleInput = $(this).find(
+      '.content > .form > .field > .project-board-title',
+    );
+    const projectColorInput = $(this).find('.content > .form > .field  #new_board_color');
+    const boardColumn = $(this).closest('.board-column');
+
+    if (boardColumn.css('backgroundColor')) {
+      setLabelColor(projectHeader, rgbToHex(boardColumn.css('backgroundColor')));
+    }
+
+    $(this)
+      .find('.content > .form > .actions > .red')
+      .on('click', function (e) {
+        e.preventDefault();
+
+        $.ajax({
+          url: $(this).data('url'),
+          data: JSON.stringify({title: projectTitleInput.val(), color: projectColorInput.val()}),
+          headers: {
+            'X-Csrf-Token': csrfToken,
+            'X-Remote': true,
+          },
+          contentType: 'application/json',
+          method: 'PUT',
+        }).done(() => {
+          projectTitleLabel.text(projectTitleInput.val());
+          projectTitleInput.closest('form').removeClass('dirty');
+          if (projectColorInput.val()) {
+            setLabelColor(projectHeader, projectColorInput.val());
+          }
+          boardColumn.attr('style', `background: ${projectColorInput.val()}!important`);
+          $('.ui.modal').modal('hide');
+        });
+      });
+  });
+
+  $(document).on('click', '.set-default-project-board', async function (e) {
+    e.preventDefault();
+
+    await $.ajax({
+      method: 'POST',
+      url: $(this).data('url'),
+      headers: {
+        'X-Csrf-Token': csrfToken,
+        'X-Remote': true,
+      },
+      contentType: 'application/json',
+    });
+
+    window.location.reload();
+  });
+
+  $('.delete-project-board').each(function () {
+    $(this).click(function (e) {
+      e.preventDefault();
+
+      $.ajax({
+        url: $(this).data('url'),
+        headers: {
+          'X-Csrf-Token': csrfToken,
+          'X-Remote': true,
+        },
+        contentType: 'application/json',
+        method: 'DELETE',
+      }).done(() => {
+        window.location.reload();
+      });
+    });
+  });
+
+  $('#new_board_submit').click(function (e) {
+    e.preventDefault();
+
+    const boardTitle = $('#new_board');
+    const projectColorInput = $('#new_board_color_picker');
+
+    $.ajax({
+      url: $(this).data('url'),
+      data: JSON.stringify({title: boardTitle.val(), color: projectColorInput.val()}),
+      headers: {
+        'X-Csrf-Token': csrfToken,
+        'X-Remote': true,
+      },
+      contentType: 'application/json',
+      method: 'POST',
+    }).done(() => {
+      boardTitle.closest('form').removeClass('dirty');
+      window.location.reload();
+    });
+  });
+}
+
+function setLabelColor(label, color) {
+  const red = getRelativeColor(parseInt(color.substr(1, 2), 16));
+  const green = getRelativeColor(parseInt(color.substr(3, 2), 16));
+  const blue = getRelativeColor(parseInt(color.substr(5, 2), 16));
+  const luminance = 0.2126 * red + 0.7152 * green + 0.0722 * blue;
+
+  if (luminance > 0.179) {
+    label.removeClass('light-label').addClass('dark-label');
+  } else {
+    label.removeClass('dark-label').addClass('light-label');
+  }
+}
+
+/**
+ * Inspired by W3C recommandation https://www.w3.org/TR/WCAG20/#relativeluminancedef
+ */
+function getRelativeColor(color) {
+  color /= 255;
+  return color <= 0.03928 ? color / 12.92 : ((color + 0.055) / 1.055) ** 2.4;
+}
+
+function rgbToHex(rgb) {
+  rgb = rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
+  return `#${hex(rgb[1])}${hex(rgb[2])}${hex(rgb[3])}`;
+}
+
+function hex(x) {
+  const hexDigits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
+  return Number.isNaN(x) ? '00' : hexDigits[(x - x % 16) / 16] + hexDigits[x % 16];
+}
index bd42bcb441fe3644959d42ddf20b3ade67c6a330..b0d43bd48722f2ef96d1cc6c537388b271af493d 100644 (file)
@@ -40,10 +40,10 @@ export function initRepoSettingSearchTeamBox() {
 }
 
 
-export async function initRepoSettingGitHook() {
+export function initRepoSettingGitHook() {
   if ($('.edit.githook').length === 0) return;
   const filename = document.querySelector('.hook-filename').textContent;
-  await createMonaco($('#content')[0], filename, {language: 'shell'});
+  const _promise = createMonaco($('#content')[0], filename, {language: 'shell'});
 }
 
 export function initRepoSettingBranches() {
index 8364569892a1145a9a23164017a799a62497e0c7..ff3edaf8cc8ad02fc6c1f7fdd041e385a705ad93 100644 (file)
@@ -3,7 +3,7 @@ const {appSubUrl, csrfToken, notificationSettings, enableTimeTracking} = window.
 
 let updateTimeInterval = null; // holds setInterval id when active
 
-export async function initStopwatch() {
+export function initStopwatch() {
   if (!enableTimeTracking) {
     return;
   }
@@ -135,7 +135,7 @@ async function updateStopwatchData(data) {
     $('.stopwatch-cancel').attr('action', `${issueUrl}/times/stopwatch/cancel`);
     $('.stopwatch-issue').text(`${repo_owner_name}/${repo_name}#${issue_index}`);
     $('.stopwatch-time').text(prettyMilliseconds(seconds * 1000));
-    updateStopwatchTime(seconds);
+    await updateStopwatchTime(seconds);
     btnEl.removeClass('hidden');
   }
 
index a3bd35175e5a811a3f0f15fb5e9064a373fe35f7..957a0d9e8a7b8fe9a3aadc5dafb15cb278227c02 100644 (file)
@@ -7,27 +7,25 @@ import {initDashboardRepoList} from './components/DashboardRepoList.js';
 import attachTribute from './features/tribute.js';
 import initGlobalCopyToClipboardListener from './features/clipboard.js';
 import initContextPopups from './features/contextpopup.js';
-import initGitGraph from './features/gitgraph.js';
+import initRepoGraphGit from './features/repo-graph.js';
 import initHeatmap from './features/heatmap.js';
 import initImageDiff from './features/imagediff.js';
-import initMigration from './features/migration.js';
-import initProject from './features/projects.js';
+import initRepoMigration from './features/repo-migration.js';
+import initRepoProject from './features/repo-projects.js';
 import initServiceWorker from './features/serviceworker.js';
 import initTableSort from './features/tablesort.js';
 import {initAdminUserListSearchForm} from './features/admin-users.js';
 import {initMarkupAnchors} from './markup/anchors.js';
 import {initNotificationCount, initNotificationsTable} from './features/notification.js';
-import {initLastCommitLoader} from './features/lastcommitloader.js';
-import {initIssueContentHistory} from './features/issue-content-history.js';
+import {initRepoIssueContentHistory} from './features/repo-issue-content.js';
 import {initStopwatch} from './features/stopwatch.js';
-import {initDiffShowMore} from './features/diff.js';
 import {initCommentContent, initMarkupContent} from './markup/content.js';
 
 import {initUserAuthLinkAccountView, initUserAuthOauth2} from './features/user-auth.js';
 import {
   initRepoDiffConversationForm,
   initRepoDiffFileViewToggle,
-  initRepoDiffReviewButton,
+  initRepoDiffReviewButton, initRepoDiffShowMore,
 } from './features/repo-diff.js';
 import {
   initRepoIssueDue,
@@ -38,7 +36,7 @@ import {
   initRepoPullRequestMergeInstruction,
   initRepoPullRequestReview,
 } from './features/repo-issue.js';
-import {initRepoCommitButton} from './features/repo-commit.js';
+import {initRepoCommitButton, initRepoCommitLastCommitLoader} from './features/repo-commit.js';
 import {
   initFootLanguageMenu,
   initGlobalButtonClickOnEnter,
@@ -68,9 +66,9 @@ import {initOrgTeamSearchRepoBox, initOrgTeamSettings} from './features/org-team
 import {initUserAuthU2fAuth, initUserAuthU2fRegister} from './features/user-auth-u2f.js';
 import {initRepoRelease, initRepoReleaseEditor} from './features/repo-release.js';
 import {initRepoEditor} from './features/repo-editor.js';
-import {initSearchUserBox} from './features/comp/SearchUserBox.js';
+import {initCompSearchUserBox} from './features/comp/SearchUserBox.js';
 import {initInstall} from './features/install.js';
-import {initWebHookEditor} from './features/comp/WebHookEditor.js';
+import {initCompWebHookEditor} from './features/comp/WebHookEditor.js';
 import {initCommonIssue} from './features/common-issue.js';
 import {initRepoBranchButton} from './features/repo-branch.js';
 import {initCommonOrganization} from './features/common-organization.js';
@@ -82,85 +80,90 @@ $.fn.tab.settings.silent = true;
 
 initVueEnv();
 
-$(document).ready(async () => {
+$(document).ready(() => {
   initGlobalCommon();
+
+  initGlobalButtonClickOnEnter();
+  initGlobalButtons();
+  initGlobalCopyToClipboardListener();
   initGlobalDropzone();
+  initGlobalEnterQuickSubmit();
+  initGlobalFormDirtyLeaveConfirm();
   initGlobalLinkActions();
-  initGlobalButtons();
-  initRepoBranchButton();
+
+  attachTribute(document.querySelectorAll('#content, .emoji-input'));
 
   initCommonIssue();
+  initCommonOrganization();
 
-  initSearchUserBox();
-  initRepoSettingSearchTeamBox();
-  initOrgTeamSearchRepoBox();
+  initCompSearchUserBox();
+  initCompWebHookEditor();
 
-  initGlobalButtonClickOnEnter();
-  initMarkupAnchors();
-  initCommentContent();
-  initRepoCommentForm();
   initInstall();
-  initRepoArchiveLinks();
-  initRepository();
-  initMigration();
-  initRepoWikiForm();
-  initRepoEditor();
-  initCommonOrganization();
-  initWebHookEditor();
+
+  initHeadNavbarContentToggle();
+  initFootLanguageMenu();
+
+  initCommentContent();
+  initContextPopups();
+  initHeatmap();
+  initImageDiff();
+  initMarkupAnchors();
+  initMarkupContent();
+  initServiceWorker();
+  initSshKeyFormParser();
+  initStopwatch();
+  initTableSort();
+
   initAdminCommon();
-  initRepoCodeView();
-  initRepoActivityTopAuthorsChart();
+  initAdminEmails();
+  initAdminUserListSearchForm();
+
   initDashboardRepoList();
+
+  initNotificationCount();
+  initNotificationsTable();
+
+  initOrgTeamSearchRepoBox();
   initOrgTeamSettings();
-  initGlobalEnterQuickSubmit();
-  initHeadNavbarContentToggle();
-  initFootLanguageMenu();
-  initRepoTopicBar();
-  initUserAuthU2fAuth();
-  initUserAuthU2fRegister();
+
+  initRepoActivityTopAuthorsChart();
+  initRepoArchiveLinks();
+  initRepoBranchButton();
+  initRepoCodeView();
+  initRepoCommentForm();
+  initRepoCommitButton();
+  initRepoCommitLastCommitLoader();
+  initRepoDiffConversationForm();
+  initRepoDiffFileViewToggle();
+  initRepoDiffReviewButton();
+  initRepoDiffShowMore();
+  initRepoEditor();
+  initRepoGraphGit();
+  initRepoIssueContentHistory();
+  initRepoIssueDue();
   initRepoIssueList();
+  initRepoIssueReferenceRepositorySearch();
   initRepoIssueTimeTracking();
-  initRepoIssueDue();
   initRepoIssueWipTitle();
-  initRepoPullRequestReview();
+  initRepoMigration();
   initRepoMigrationStatusChecker();
-  initRepoTemplateSearch();
-  initRepoIssueReferenceRepositorySearch();
-  initContextPopups();
-  initTableSort();
-  initNotificationsTable();
-  initLastCommitLoader();
+  initRepoProject();
   initRepoPullRequestMergeInstruction();
-  initRepoDiffFileViewToggle();
-  initRepoReleaseEditor();
+  initRepoPullRequestReview();
   initRepoRelease();
-  initDiffShowMore();
-  initIssueContentHistory();
-  initAdminUserListSearchForm();
-  initGlobalCopyToClipboardListener();
-  initUserAuthOauth2();
-  initRepoDiffReviewButton();
-  initRepoCommitButton();
-  initAdminEmails();
-  initGlobalEnterQuickSubmit();
-  initSshKeyFormParser();
-  initGlobalFormDirtyLeaveConfirm();
-  initUserSettings();
+  initRepoReleaseEditor();
+  initRepoSettingGitHook();
+  initRepoSettingSearchTeamBox();
   initRepoSettingsCollaboration();
-  initUserAuthLinkAccountView();
-  initRepoDiffConversationForm();
+  initRepoTemplateSearch();
+  initRepoTopicBar();
+  initRepoWikiForm();
+  initRepository();
 
-  // parallel init of async loaded features
-  await Promise.all([
-    attachTribute(document.querySelectorAll('#content, .emoji-input')),
-    initGitGraph(),
-    initHeatmap(),
-    initProject(),
-    initServiceWorker(),
-    initNotificationCount(),
-    initStopwatch(),
-    initMarkupContent(),
-    initRepoSettingGitHook(),
-    initImageDiff(),
-  ]);
+  initUserAuthLinkAccountView();
+  initUserAuthOauth2();
+  initUserAuthU2fAuth();
+  initUserAuthU2fRegister();
+  initUserSettings();
 });
index 19b749aaabfc346498f3cbaa0517e547b2cebf99..0564199bbffabdb2c8a3cdf2b18964c8df81b77f 100644 (file)
@@ -2,8 +2,8 @@ import {renderMermaid} from './mermaid.js';
 import {initMarkupTasklist} from './tasklist.js';
 
 // code that runs for all markup content
-export async function initMarkupContent() {
-  await renderMermaid(document.querySelectorAll('code.language-mermaid'));
+export function initMarkupContent() {
+  const _promise = renderMermaid(document.querySelectorAll('code.language-mermaid'));
 }
 
 // code that only runs for comments