Skip to content

Commit 0f93008

Browse files
authored
[tagCopyPaste] add native copy/paste support (#571)
Co-authored-by: feederbox826 <[email protected]>
1 parent 4ccbf2f commit 0f93008

File tree

2 files changed

+65
-94
lines changed

2 files changed

+65
-94
lines changed

plugins/tagCopyPaste/tagCopyPaste.js

Lines changed: 64 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
requireConfirmation: false,
66
};
77

8+
// helper function to get the innerText of all elements matching a selector
9+
const getAllInnerText = (selector) => Array.from(document.querySelectorAll(selector))
10+
.map((el) => el.innerText)
11+
.filter((text) => text.trim() !== "");
12+
813
// On image page, get data about gallery (image's position within gallery, next/prev image IDs),
914
// add arrow buttons to page, and register arrow keypress handlers,
1015
async function setupTagCopyPaste(objType) {
@@ -24,21 +29,35 @@
2429
}
2530

2631
function insertCopyPasteButtons(objID, objType) {
32+
// listen for copy and paste events within tag input box
33+
// find tag input box
34+
const tagInputBox = document.querySelector("label[for='tag_ids'] + div .react-select__value-container");
35+
if (tagInputBox) {
36+
tagInputBox.addEventListener("copy", (event) => {
37+
event.preventDefault();
38+
handleCopyClick();
39+
});
40+
tagInputBox.addEventListener("paste", (event) => {
41+
event.preventDefault();
42+
handlePasteClick(objID, objType);
43+
});
44+
}
45+
2746
var copyButton = document.createElement("button");
2847
copyButton.className = "imageGalleryNav-copyButton btn btn-secondary";
2948
copyButton.innerText = "Copy";
30-
copyButton.addEventListener("click", (e) => {
31-
e.preventDefault();
49+
copyButton.onclick = (event) => {
50+
event.preventDefault();
3251
handleCopyClick();
33-
});
52+
}
3453

3554
var pasteButton = document.createElement("button");
3655
pasteButton.className = "imageGalleryNav-pasteButton btn btn-secondary";
3756
pasteButton.innerText = "Paste";
38-
pasteButton.addEventListener("click", (e) => {
39-
e.preventDefault();
57+
pasteButton.onclick = (event) => {
58+
event.preventDefault();
4059
handlePasteClick(objID, objType);
41-
});
60+
}
4261

4362
if (document.querySelector("button.imageGalleryNav-pasteButton") == null) {
4463
document.querySelector("label[for='tag_ids']").append(pasteButton);
@@ -50,99 +69,66 @@
5069

5170
// Handle copy click. Return delimited list of current tags.
5271
async function handleCopyClick() {
53-
// Get tags from input box.
54-
var tagList = [];
55-
document
56-
.querySelectorAll(
57-
"label[for='tag_ids'] + div .react-select__multi-value__label"
58-
)
59-
.forEach((item) => {
60-
tagList.push(item.innerText);
61-
});
62-
63-
// Join tags as comma delimited list and write to clipboard.
64-
await navigator.clipboard.writeText(tagList.join(","));
72+
// Get tags from input box
73+
// join as comma delimited list
74+
const tagList = getAllInnerText("label[for='tag_ids'] + div .react-select__multi-value__label").join(",")
75+
// write to clipboard.
76+
navigator.clipboard.writeText(tagList);
6577
}
6678

6779
// Handle paste click.
6880
async function handlePasteClick(objID, objType) {
69-
var inputTagList = [];
70-
7181
// Parse tag list from comma delimited string.
72-
var tagInput = await navigator.clipboard.readText();
73-
tagInput.split(",").forEach((item) => {
74-
if (!inputTagList.includes(item)) {
75-
inputTagList.push(item);
76-
}
77-
});
82+
const tagInput = await navigator.clipboard.readText();
83+
var inputTagList = tagInput.split(",") // do de-duplication later
7884

7985
// Get tags from input box and also add to tag list.
80-
document
81-
.querySelectorAll(
82-
"label[for='tag_ids'] + div .react-select__multi-value__label"
83-
)
84-
.forEach((item) => {
85-
if (!inputTagList.includes(item)) {
86-
inputTagList.push(item.innerText);
87-
}
88-
});
86+
const existingTagList = getAllInnerText("label[for='tag_ids'] + div .react-select__multi-value__label")
8987

90-
inputTagList.sort();
88+
inputTagList = [...new Set([...inputTagList, ...existingTagList])].sort();
9189

9290
var missingTags = [];
9391
var existingTags = [];
9492
var tagUpdateList = [];
9593

9694
// Search for tag ID for each tag. If exists, add to tag ID list. If not exists, create new tag and add to tag ID list.
97-
for (let i = 0; i < inputTagList.length; i++) {
98-
var inputTag = inputTagList[i];
99-
var tagID = await getTagByName(inputTag);
100-
if (tagID != null && tagID.length) {
95+
for (const inputTag of inputTagList) {
96+
const tagID = await getTagByName(inputTag.trim());
97+
if (tagID && tagID.length) {
10198
existingTags.push(inputTag);
10299
tagUpdateList.push(tagID[0]);
103100
} else {
104101
missingTags.push(inputTag);
105102
}
106103
}
107104

105+
108106
if (pluginSettings.requireConfirmation) {
109-
var msg = "";
110-
if (pluginSettings.createIfNotExists) {
111-
msg = `Missing Tags that will be created:\n${missingTags.join(
112-
", "
113-
)}\n\nExisting Tags that will be saved: \n${existingTags.join(
114-
", "
115-
)}\n\nContinue?`;
116-
} else {
117-
msg = `Missing Tags that will be skipped:\n${missingTags.join(
118-
", "
119-
)}\n\nExisting Tags that will be saved: \n${existingTags.join(
120-
", "
121-
)}\n\nContinue?`;
122-
}
123107

124-
var userConfirmed = confirm(msg);
125-
if (!userConfirmed) {
108+
const missingTagsStr = missingTags.join(", ");
109+
const existingTagsStr = existingTags.join(", ");
110+
const msg = pluginSettings.createIfNotExists
111+
? `Missing Tags that will be created:\n${missingTagsStr}\n\nExisting Tags that will be saved: \n${existingTagsStr}\n\nContinue?`
112+
: `Missing Tags that will be skipped:\n${missingTagsStr}\n\nExisting Tags that will be saved: \n${existingTagsStr}\n\nContinue?`;
113+
114+
if (!confirm(msg)) {
126115
return;
127116
}
128117
}
129118

130119
if (pluginSettings.createIfNotExists && missingTags.length) {
131-
for (let i = 0; i < missingTags.length; i++) {
132-
var newTagName = missingTags[i];
133-
var newTagID = await createNewTag(newTagName);
134-
if (newTagID != null) {
135-
tagUpdateList.push(newTagID);
136-
}
120+
for (const missingTag of missingTags) {
121+
const newTagID = await createNewTag(missingTag);
122+
if (newTagID != null) tagUpdateList.push(newTagID);
137123
}
138124
}
139125

140126
// Update tags on object with new tag ID list.
141127
await updateObjTags(
142128
objID,
143129
tagUpdateList,
144-
objType.toLowerCase() + "Update",
145-
objType + "UpdateInput"
130+
`${objType.toLowerCase()}Update`,
131+
`${objType}UpdateInput`
146132
);
147133

148134
window.location.reload();
@@ -183,33 +169,18 @@
183169
.then((data) => data.findTags.tags.map((item) => item.id));
184170
}
185171

186-
// Wait for scenes page.
187-
csLib.PathElementListener("/scenes/", "[id*='-edit-details']", () => {
188-
setupTagCopyPaste("Scene");
189-
}); // PathElementListener is from cs-ui-lib.js
190-
191-
// Wait for studios page.
192-
csLib.PathElementListener("/studios/", "[id='studio-edit']", () => {
193-
setupTagCopyPaste("Studio");
194-
}); // PathElementListener is from cs-ui-lib.js
195-
196-
// Wait for groups page.
197-
csLib.PathElementListener("/groups/", "[id='group-edit']", () => {
198-
setupTagCopyPaste("Group");
199-
}); // PathElementListener is from cs-ui-lib.js
200-
201-
// Wait for performers page.
202-
csLib.PathElementListener("/performers/", "[id='performer-edit']", () => {
203-
setupTagCopyPaste("Performer");
204-
}); // PathElementListener is from cs-ui-lib.js
205-
206-
// Wait for galleries page.
207-
csLib.PathElementListener("/galleries/", "[id*='-edit-details']", () => {
208-
setupTagCopyPaste("Gallery");
209-
}); // PathElementListener is from cs-ui-lib.js
210-
211-
// Wait for images page.
212-
csLib.PathElementListener("/images/", "[id*='-edit-details']", () => {
213-
setupTagCopyPaste("Image");
214-
}); // PathElementListener is from cs-ui-lib.js
172+
// listener arrays
173+
[
174+
[ "/scenes/", "[id*='-edit-details']", "Scene" ],
175+
[ "/studios/", "[id='studio-edit']", "Studio" ],
176+
[ "/groups/", "[id='group-edit']", "Group" ],
177+
[ "/performers/", "[id='performer-edit']", "Performer" ],
178+
[ "/galleries/", "[id*='-edit-details']", "Gallery" ],
179+
[ "/images/", "[id*='-edit-details']", "Image" ]
180+
].forEach(([path, selector, objType]) => {
181+
// Wait for the page to load and the element to be present.
182+
csLib.PathElementListener(path, selector, () => {
183+
setupTagCopyPaste(objType);
184+
}); // PathElementListener is from cs-ui-lib.js
185+
});
215186
})();

plugins/tagCopyPaste/tagCopyPaste.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: tagCopyPaste
22
# requires: CommunityScriptsUILibrary
33
description: Adds Copy/Paste buttons to Tags field.
4-
version: 0.1
4+
version: 0.2
55
settings:
66
createIfNotExists:
77
displayName: Create If Not Exists

0 commit comments

Comments
 (0)