Skip to content

Commit

Permalink
Remove jQuery .text() (#30506)
Browse files Browse the repository at this point in the history
Remove and forbid [.text()](https://api.jquery.com/text/). Tested some,
but not all functionality, but I think these are pretty safe
replacements.

---------

Co-authored-by: wxiaoguang <[email protected]>
  • Loading branch information
silverwind and wxiaoguang authored Jun 10, 2024
1 parent 4f7d6fe commit a2304cb
Show file tree
Hide file tree
Showing 12 changed files with 160 additions and 168 deletions.
4 changes: 2 additions & 2 deletions .eslintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ rules:
jquery/no-sizzle: [2]
jquery/no-slide: [2]
jquery/no-submit: [2]
jquery/no-text: [0]
jquery/no-text: [2]
jquery/no-toggle: [2]
jquery/no-trigger: [0]
jquery/no-trim: [2]
Expand Down Expand Up @@ -477,7 +477,7 @@ rules:
no-jquery/no-slide: [2]
no-jquery/no-sub: [2]
no-jquery/no-support: [2]
no-jquery/no-text: [0]
no-jquery/no-text: [2]
no-jquery/no-trigger: [0]
no-jquery/no-trim: [2]
no-jquery/no-type: [2]
Expand Down
6 changes: 3 additions & 3 deletions templates/repo/editor/commit_form.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<div class="quick-pull-choice js-quick-pull-choice">
<div class="field">
<div class="ui radio checkbox {{if not .CanCommitToBranch.CanCommitToBranch}}disabled{{end}}">
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="direct" button_text="{{ctx.Locale.Tr "repo.editor.commit_changes"}}" {{if eq .commit_choice "direct"}}checked{{end}}>
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="direct" data-button-text="{{ctx.Locale.Tr "repo.editor.commit_changes"}}" {{if eq .commit_choice "direct"}}checked{{end}}>
<label>
{{svg "octicon-git-commit"}}
{{ctx.Locale.Tr "repo.editor.commit_directly_to_this_branch" .BranchName}}
Expand All @@ -43,9 +43,9 @@
<div class="field">
<div class="ui radio checkbox">
{{if .CanCreatePullRequest}}
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="commit-to-new-branch" button_text="{{ctx.Locale.Tr "repo.editor.propose_file_change"}}" {{if eq .commit_choice "commit-to-new-branch"}}checked{{end}}>
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="commit-to-new-branch" data-button-text="{{ctx.Locale.Tr "repo.editor.propose_file_change"}}" {{if eq .commit_choice "commit-to-new-branch"}}checked{{end}}>
{{else}}
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="commit-to-new-branch" button_text="{{ctx.Locale.Tr "repo.editor.commit_changes"}}" {{if eq .commit_choice "commit-to-new-branch"}}checked{{end}}>
<input type="radio" class="js-quick-pull-choice-option" name="commit_choice" value="commit-to-new-branch" data-button-text="{{ctx.Locale.Tr "repo.editor.commit_changes"}}" {{if eq .commit_choice "commit-to-new-branch"}}checked{{end}}>
{{end}}
<label>
{{svg "octicon-git-pull-request"}}
Expand Down
8 changes: 4 additions & 4 deletions templates/repo/settings/collaboration.tmpl
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
<div class="flex-item-trailing">
<div class="flex-text-block">
{{svg "octicon-shield-lock"}}
<div class="ui inline dropdown access-mode" data-url="{{$.Link}}/access_mode" data-uid="{{.ID}}" data-last-value="{{printf "%d" .Collaboration.Mode}}">
<div class="ui dropdown custom access-mode" data-url="{{$.Link}}/access_mode" data-uid="{{.ID}}" data-last-value="{{.Collaboration.Mode}}">
<div class="text">{{if eq .Collaboration.Mode 1}}{{ctx.Locale.Tr "repo.settings.collaboration.read"}}{{else if eq .Collaboration.Mode 2}}{{ctx.Locale.Tr "repo.settings.collaboration.write"}}{{else if eq .Collaboration.Mode 3}}{{ctx.Locale.Tr "repo.settings.collaboration.admin"}}{{else}}{{ctx.Locale.Tr "repo.settings.collaboration.undefined"}}{{end}}</div>
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu">
<div class="item" data-text="{{ctx.Locale.Tr "repo.settings.collaboration.admin"}}" data-value="3">{{ctx.Locale.Tr "repo.settings.collaboration.admin"}}</div>
<div class="item" data-text="{{ctx.Locale.Tr "repo.settings.collaboration.write"}}" data-value="2">{{ctx.Locale.Tr "repo.settings.collaboration.write"}}</div>
<div class="item" data-text="{{ctx.Locale.Tr "repo.settings.collaboration.read"}}" data-value="1">{{ctx.Locale.Tr "repo.settings.collaboration.read"}}</div>
<div class="item" data-value="3">{{ctx.Locale.Tr "repo.settings.collaboration.admin"}}</div>
<div class="item" data-value="2">{{ctx.Locale.Tr "repo.settings.collaboration.write"}}</div>
<div class="item" data-value="1">{{ctx.Locale.Tr "repo.settings.collaboration.read"}}</div>
</div>
</div>
</div>
Expand Down
93 changes: 53 additions & 40 deletions web_src/js/features/common-global.js
Original file line number Diff line number Diff line change
Expand Up @@ -301,52 +301,65 @@ async function linkAction(e) {
}
}

export function initGlobalLinkActions() {
function showDeletePopup(e) {
e.preventDefault();
const $this = $(this);
const dataArray = $this.data();
let filter = '';
if (this.getAttribute('data-modal-id')) {
filter += `#${this.getAttribute('data-modal-id')}`;
}
export function initGlobalDeleteButton() {
// ".delete-button" shows a confirmation modal defined by `data-modal-id` attribute.
// Some model/form elements will be filled by `data-id` / `data-name` / `data-data-xxx` attributes.
// If there is a form defined by `data-form`, then the form will be submitted as-is (without any modification).
// If there is no form, then the data will be posted to `data-url`.
// TODO: it's not encouraged to use this method. `show-modal` does far better than this.
for (const btn of document.querySelectorAll('.delete-button')) {
btn.addEventListener('click', (e) => {
e.preventDefault();

const $dialog = $(`.delete.modal${filter}`);
$dialog.find('.name').text($this.data('name'));
for (const [key, value] of Object.entries(dataArray)) {
if (key && key.startsWith('data')) {
$dialog.find(`.${key}`).text(value);
}
}
// eslint-disable-next-line github/no-dataset -- code depends on the camel-casing
const dataObj = btn.dataset;

const modalId = btn.getAttribute('data-modal-id');
const modal = document.querySelector(`.delete.modal${modalId ? `#${modalId}` : ''}`);

$dialog.modal({
closable: false,
onApprove: async () => {
if ($this.data('type') === 'form') {
$($this.data('form')).trigger('submit');
return;
// set the modal "display name" by `data-name`
const modalNameEl = modal.querySelector('.name');
if (modalNameEl) modalNameEl.textContent = btn.getAttribute('data-name');

// fill the modal elements with data-xxx attributes: `data-data-organization-name="..."` => `<span class="dataOrganizationName">...</span>`
for (const [key, value] of Object.entries(dataObj)) {
if (key.startsWith('data')) {
const textEl = modal.querySelector(`.${key}`);
if (textEl) textEl.textContent = value;
}
const postData = new FormData();
for (const [key, value] of Object.entries(dataArray)) {
if (key && key.startsWith('data')) {
postData.append(key.slice(4), value);
}

$(modal).modal({
closable: false,
onApprove: async () => {
// if `data-type="form"` exists, then submit the form by the selector provided by `data-form="..."`
if (btn.getAttribute('data-type') === 'form') {
const formSelector = btn.getAttribute('data-form');
const form = document.querySelector(formSelector);
if (!form) throw new Error(`no form named ${formSelector} found`);
form.submit();
}
if (key === 'id') {
postData.append('id', value);

// prepare an AJAX form by data attributes
const postData = new FormData();
for (const [key, value] of Object.entries(dataObj)) {
if (key.startsWith('data')) { // for data-data-xxx (HTML) -> dataXxx (form)
postData.append(key.slice(4), value);
}
if (key === 'id') { // for data-id="..."
postData.append('id', value);
}
}
}

const response = await POST($this.data('url'), {data: postData});
if (response.ok) {
const data = await response.json();
window.location.href = data.redirect;
}
},
}).modal('show');
const response = await POST(btn.getAttribute('data-url'), {data: postData});
if (response.ok) {
const data = await response.json();
window.location.href = data.redirect;
}
},
}).modal('show');
});
}

// Helpers.
$('.delete-button').on('click', showDeletePopup);
}

function initGlobalShowModal() {
Expand Down Expand Up @@ -382,7 +395,7 @@ function initGlobalShowModal() {
} else if ($attrTarget[0].matches('input, textarea')) {
$attrTarget.val(attrib.value); // FIXME: add more supports like checkbox
} else {
$attrTarget.text(attrib.value); // FIXME: it should be more strict here, only handle div/span/p
$attrTarget[0].textContent = attrib.value; // FIXME: it should be more strict here, only handle div/span/p
}
}

Expand Down
10 changes: 5 additions & 5 deletions web_src/js/features/imagediff.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,20 @@ export function initImageDiff() {
path: this.getAttribute('data-path-after'),
mime: this.getAttribute('data-mime-after'),
$images: $container.find('img.image-after'), // matches 3 <img>
$boundsInfo: $container.find('.bounds-info-after'),
boundsInfo: this.querySelector('.bounds-info-after'),
}, {
path: this.getAttribute('data-path-before'),
mime: this.getAttribute('data-mime-before'),
$images: $container.find('img.image-before'), // matches 3 <img>
$boundsInfo: $container.find('.bounds-info-before'),
boundsInfo: this.querySelector('.bounds-info-before'),
}];

await Promise.all(imageInfos.map(async (info) => {
const [success] = await Promise.all(Array.from(info.$images, (img) => {
return loadElem(img, info.path);
}));
// only the first images is associated with $boundsInfo
if (!success) info.$boundsInfo.text('(image error)');
// only the first images is associated with boundsInfo
if (!success && info.boundsInfo) info.boundsInfo.textContent = '(image error)';
if (info.mime === 'image/svg+xml') {
const resp = await GET(info.path);
const text = await resp.text();
Expand All @@ -102,7 +102,7 @@ export function initImageDiff() {
this.setAttribute('width', bounds.width);
this.setAttribute('height', bounds.height);
});
hideElem(info.$boundsInfo);
hideElem(info.boundsInfo);
}
}
}));
Expand Down
14 changes: 7 additions & 7 deletions web_src/js/features/notification.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,13 @@ async function receiveUpdateCount(event) {
}

export function initNotificationCount() {
const $notificationCount = $('.notification_count');

if (!$notificationCount.length) {
return;
}
if (!document.querySelector('.notification_count')) return;

let usingPeriodicPoller = false;
const startPeriodicPoller = (timeout, lastCount) => {
if (timeout <= 0 || !Number.isFinite(timeout)) return;
usingPeriodicPoller = true;
lastCount = lastCount ?? $notificationCount.text();
lastCount = lastCount ?? getCurrentCount();
setTimeout(async () => {
await updateNotificationCountWithCallback(startPeriodicPoller, timeout, lastCount);
}, timeout);
Expand Down Expand Up @@ -121,8 +117,12 @@ export function initNotificationCount() {
startPeriodicPoller(notificationSettings.MinTimeout);
}

function getCurrentCount() {
return document.querySelector('.notification_count').textContent;
}

async function updateNotificationCountWithCallback(callback, timeout, lastCount) {
const currentCount = $('.notification_count').text();
const currentCount = getCurrentCount();
if (lastCount !== currentCount) {
callback(notificationSettings.MinTimeout, currentCount);
return;
Expand Down
111 changes: 44 additions & 67 deletions web_src/js/features/repo-editor.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import $ from 'jquery';
import {htmlEscape} from 'escape-goat';
import {createCodeEditor} from './codeeditor.js';
import {hideElem, showElem} from '../utils/dom.js';
import {hideElem, queryElems, showElem} from '../utils/dom.js';
import {initMarkupContent} from '../markup/content.js';
import {attachRefIssueContextPopup} from './contextpopup.js';
import {POST} from '../modules/fetch.js';
Expand Down Expand Up @@ -40,98 +40,75 @@ function initEditPreviewTab($form) {
}
}

function initEditorForm() {
const $form = $('.repository .edit.form');
if (!$form) return;
initEditPreviewTab($form);
}

function getCursorPosition($e) {
const el = $e.get(0);
let pos = 0;
if ('selectionStart' in el) {
pos = el.selectionStart;
} else if ('selection' in document) {
el.focus();
const Sel = document.selection.createRange();
const SelLength = document.selection.createRange().text.length;
Sel.moveStart('character', -el.value.length);
pos = Sel.text.length - SelLength;
}
return pos;
}

export function initRepoEditor() {
initEditorForm();

$('.js-quick-pull-choice-option').on('change', function () {
if ($(this).val() === 'commit-to-new-branch') {
showElem('.quick-pull-branch-name');
document.querySelector('.quick-pull-branch-name input').required = true;
} else {
hideElem('.quick-pull-branch-name');
document.querySelector('.quick-pull-branch-name input').required = false;
}
$('#commit-button').text(this.getAttribute('button_text'));
});
const $editArea = $('.repository.editor textarea#edit_area');
if (!$editArea.length) return;

const joinTreePath = ($fileNameEl) => {
const parts = [];
$('.breadcrumb span.section').each(function () {
const $element = $(this);
if ($element.find('a').length) {
parts.push($element.find('a').text());
for (const el of queryElems('.js-quick-pull-choice-option')) {
el.addEventListener('input', () => {
if (el.value === 'commit-to-new-branch') {
showElem('.quick-pull-branch-name');
document.querySelector('.quick-pull-branch-name input').required = true;
} else {
parts.push($element.text());
hideElem('.quick-pull-branch-name');
document.querySelector('.quick-pull-branch-name input').required = false;
}
document.querySelector('#commit-button').textContent = el.getAttribute('data-button-text');
});
if ($fileNameEl.val()) parts.push($fileNameEl.val());
$('#tree_path').val(parts.join('/'));
};

const $editFilename = $('#file-name');
$editFilename.on('input', function () {
const parts = $(this).val().split('/');
}

const filenameInput = document.querySelector('#file-name');
function joinTreePath() {
const parts = [];
for (const el of document.querySelectorAll('.breadcrumb span.section')) {
const link = el.querySelector('a');
parts.push(link ? link.textContent : el.textContent);
}
if (filenameInput.value) {
parts.push(filenameInput.value);
}
document.querySelector('#tree_path').value = parts.join('/');
}
filenameInput.addEventListener('input', function () {
const parts = filenameInput.value.split('/');
if (parts.length > 1) {
for (let i = 0; i < parts.length; ++i) {
const value = parts[i];
if (i < parts.length - 1) {
if (value.length) {
$(`<span class="section"><a href="#">${htmlEscape(value)}</a></span>`).insertBefore($(this));
$('<div class="breadcrumb-divider">/</div>').insertBefore($(this));
$(`<span class="section"><a href="#">${htmlEscape(value)}</a></span>`).insertBefore($(filenameInput));
$('<div class="breadcrumb-divider">/</div>').insertBefore($(filenameInput));
}
} else {
$(this).val(value);
filenameInput.value = value;
}
this.setSelectionRange(0, 0);
}
}

joinTreePath($(this));
joinTreePath();
});

$editFilename.on('keydown', function (e) {
const $section = $('.breadcrumb span.section');

filenameInput.addEventListener('keydown', function (e) {
const sections = queryElems('.breadcrumb span.section');
const dividers = queryElems('.breadcrumb .breadcrumb-divider');
// Jump back to last directory once the filename is empty
if (e.code === 'Backspace' && getCursorPosition($(this)) === 0 && $section.length > 0) {
if (e.code === 'Backspace' && filenameInput.selectionStart === 0 && sections.length > 0) {
e.preventDefault();
const $divider = $('.breadcrumb .breadcrumb-divider');
const value = $section.last().find('a').text();
$(this).val(value + $(this).val());
const lastSection = sections[sections.length - 1];
const lastDivider = dividers.length ? dividers[dividers.length - 1] : null;
const value = lastSection.querySelector('a').textContent;
filenameInput.value = value + filenameInput.value;
this.setSelectionRange(value.length, value.length);
$section.last().remove();
$divider.last().remove();
joinTreePath($(this));
lastDivider?.remove();
lastSection.remove();
joinTreePath();
}
});

const $editArea = $('.repository.editor textarea#edit_area');
if (!$editArea.length) return;
const $form = $('.repository.editor .edit.form');
initEditPreviewTab($form);

(async () => {
const editor = await createCodeEditor($editArea[0], $editFilename[0]);
const editor = await createCodeEditor($editArea[0], filenameInput);

// Using events from https://github.com/codedance/jquery.AreYouSure#advanced-usage
// to enable or disable the commit button
Expand Down
Loading

0 comments on commit a2304cb

Please sign in to comment.