-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathdrupal-textarea-paste.js
More file actions
147 lines (132 loc) · 4.59 KB
/
drupal-textarea-paste.js
File metadata and controls
147 lines (132 loc) · 4.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/**
* Plugin: Drupal issue URL → [#ISSUE_NUMBER] on paste
*
* When a Drupal.org issue URL is pasted into a textarea it is converted to
* the [#ISSUE_NUMBER] shorthand format. If the issue is not already listed
* in the "Related issues links" field and not the parent issue, the user is
* offered the option to add it there.
*/
const pastePlugin = {
DRUPAL_ISSUE_URL:
/https?:\/\/www\.drupal\.org\/(?:project\/[^/]+\/issues|node|i)\/(\d+)[^\s]*/g,
/**
* Returns all current values from the Related Issues Links URL inputs.
*/
getRelatedLinkInputs: function () {
return Array.from(
document.querySelectorAll(
'#edit-field-issue-related-links input[id*="-url"], ' +
'#field-issue-related-links-add-more-wrapper input[id*="-url"], ' +
'input[id^="edit-field-issue-related-links-und-"][id*="-url"]'
)
);
},
/**
* Returns true if the given issue ID already appears in any related link.
*/
isInRelatedLinks: function (issueId) {
return this.getRelatedLinkInputs().some((input) =>
input.value.includes(issueId)
);
},
/**
* Sets the value of the first empty related link URL input to the given URL.
* Returns true if successful.
*/
setEmptyRelatedLink: function (url) {
const empty = this.getRelatedLinkInputs().find(
(input) => input.value.trim() === ""
);
if (empty) {
empty.value = url;
empty.dispatchEvent(new Event("input", { bubbles: true }));
empty.dispatchEvent(new Event("change", { bubbles: true }));
return true;
}
return false;
},
/**
* Clicks "Add another item", polls until a new URL input appears, then sets it.
*/
addViaButton: function (url) {
const btn = document.getElementById(
"edit-field-issue-related-links-und-add-more"
);
if (!btn) return;
const before = this.getRelatedLinkInputs().length;
chrome.runtime.sendMessage({
call: "triggerClick",
elementId: "edit-field-issue-related-links-und-add-more",
});
// Poll until count increases (Drupal AJAX replaces the wrapper div, so
// MutationObserver on the wrapper misses the replacement; polling the whole
// document is more reliable).
let attempts = 0;
const poll = setInterval(() => {
attempts++;
const inputs = this.getRelatedLinkInputs();
if (inputs.length > before) {
clearInterval(poll);
const newest = inputs[inputs.length - 1];
if (newest.value.trim() === "") {
newest.value = url;
newest.dispatchEvent(new Event("input", { bubbles: true }));
newest.dispatchEvent(new Event("change", { bubbles: true }));
newest.scrollIntoView({ behavior: "smooth", block: "center" });
}
} else if (attempts > 30) {
clearInterval(poll);
}
}, 300);
},
/**
* Offers to add url (with issueId) to the Related Issues links field.
*/
offerRelatedLink: function (issueId, originalUrl) {
if (this.isInRelatedLinks(issueId)) return;
// Do not offer to add if this is the parent link field
const parentInput = document.querySelector(
'input[id^="edit-field-issue-parent-link"][id$="-url"]'
);
if (parentInput && parentInput.value && parentInput.value.includes(issueId))
return;
if (!window.confirm(`Add issue #${issueId} to \"Related issues links\"?`))
return;
if (!this.setEmptyRelatedLink(originalUrl)) {
this.addViaButton(originalUrl);
}
},
attach: function (textarea) {
textarea.addEventListener("paste", (e) => {
const pasted = e.clipboardData.getData("text");
const replacements = [];
const replaced = pasted.replace(
new RegExp(this.DRUPAL_ISSUE_URL.source, "g"),
(match, issueId) => {
replacements.push({ issueId, originalUrl: match });
return `[#${issueId}]`;
}
);
if (replaced === pasted) return;
e.preventDefault();
const start = textarea.selectionStart;
const end = textarea.selectionEnd;
textarea.value =
textarea.value.slice(0, start) + replaced + textarea.value.slice(end);
const newPos = start + replaced.length;
textarea.setSelectionRange(newPos, newPos);
// Only offer to add to related links if the field exists on this page
if (
document.getElementById(
"edit-field-issue-related-links-und-add-more"
) ||
this.getRelatedLinkInputs().length > 0
) {
replacements.forEach(({ issueId, originalUrl }) => {
this.offerRelatedLink(issueId, originalUrl);
});
}
});
},
};
export { pastePlugin };