Compare commits
3 Commits
6e7f3d4b4c
...
f748d0b771
Author | SHA1 | Date | |
---|---|---|---|
f748d0b771 | |||
d1de5350e1 | |||
826c7ccb73 |
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
node_modules
|
||||||
|
react
|
||||||
|
package.json
|
||||||
|
package-lock.json
|
@ -17,7 +17,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<h1>Add emoji</h1>
|
<h1>Add emoji</h1>
|
||||||
<input type="file" id="fileinput" accept="image/*" multiple /><input type="submit" id="submit" />
|
<input type="file" id="fileinput" accept="image/png,image/jpeg" multiple /><input type="submit" id="submit" />
|
||||||
|
|
||||||
<script src="edit.js" charset="UTF-8"></script>
|
<script src="edit.js" charset="UTF-8"></script>
|
||||||
</body></html>
|
</body></html>
|
17
edit.js
17
edit.js
@ -45,7 +45,7 @@ async function instantiateRow(url, internalId, animationStyle) {
|
|||||||
c0.appendChild(img);
|
c0.appendChild(img);
|
||||||
|
|
||||||
let select = document.createElement("select");
|
let select = document.createElement("select");
|
||||||
animationStyles.forEach(a => {
|
ANIMATION_STYLES.forEach(a => {
|
||||||
let option = document.createElement("option");
|
let option = document.createElement("option");
|
||||||
option.innerText = a;
|
option.innerText = a;
|
||||||
option.value = a.toLowerCase();
|
option.value = a.toLowerCase();
|
||||||
@ -59,18 +59,14 @@ async function instantiateRow(url, internalId, animationStyle) {
|
|||||||
|
|
||||||
let button = document.createElement("button");
|
let button = document.createElement("button");
|
||||||
button.innerText = "🗑️";
|
button.innerText = "🗑️";
|
||||||
button.onclick = () => {
|
button.onclick = async () => {
|
||||||
deleteEmoji(internalId);
|
//if (!confirm("Delete emoji from storage. Are you sure?")) return;
|
||||||
row.remove();
|
|
||||||
};
|
|
||||||
c2.appendChild(button);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteEmoji(internalId) {
|
|
||||||
if (!confirm("Delete emoji from storage. Are you sure?")) return;
|
|
||||||
var {emoji} = await chrome.storage.local.get("emoji");
|
var {emoji} = await chrome.storage.local.get("emoji");
|
||||||
emoji = emoji.filter(e => e.internalId != internalId);
|
emoji = emoji.filter(e => e.internalId != internalId);
|
||||||
await chrome.storage.local.set({emoji});
|
await chrome.storage.local.set({emoji});
|
||||||
|
row.remove();
|
||||||
|
};
|
||||||
|
c2.appendChild(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setAnimationStyle(internalId, animationStyle) {
|
async function setAnimationStyle(internalId, animationStyle) {
|
||||||
@ -98,6 +94,7 @@ submit.onclick = async () => {
|
|||||||
var {emoji} = await chrome.storage.local.get("emoji");
|
var {emoji} = await chrome.storage.local.get("emoji");
|
||||||
emoji = emoji.concat(newEmoji);
|
emoji = emoji.concat(newEmoji);
|
||||||
await chrome.storage.local.set({emoji});
|
await chrome.storage.local.set({emoji});
|
||||||
|
fileinput.value = "";
|
||||||
submit.disabled = false;
|
submit.disabled = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -25,8 +25,5 @@
|
|||||||
"background": {
|
"background": {
|
||||||
"service_worker": "background.js",
|
"service_worker": "background.js",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
},
|
|
||||||
"action": {
|
|
||||||
"default_popup": "toggle.html"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
36
toggle.html
36
toggle.html
@ -1,39 +1,51 @@
|
|||||||
<!DOCTYPE html><html><head>
|
<!DOCTYPE html><html><head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
<style>
|
<style>
|
||||||
|
|
||||||
#emojigrid {
|
#emojigrid {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
width: 269px;
|
|
||||||
}
|
}
|
||||||
.emojisquare {
|
.emojisquare {
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
border: 1px solid gray;
|
border: 1px solid gray;
|
||||||
margin: 1px;
|
margin: 1px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
.emojisquare .imgcontainer {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
margin: auto;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
.emojisquare img {
|
.emojisquare .imgcontainer img {
|
||||||
width: 48px;
|
max-width: 100%;
|
||||||
height: 48px;
|
max-height: 100%;
|
||||||
margin: auto;
|
object-fit: contain;
|
||||||
}
|
}
|
||||||
.enabled {
|
|
||||||
|
[data-state=enabled] {
|
||||||
background-color:rgba(0, 255, 0, 0.5);
|
background-color:rgba(0, 255, 0, 0.5);
|
||||||
}
|
}
|
||||||
.disabled {
|
[data-state=disabled] {
|
||||||
background-color:rgba(255, 0, 0, 0.5);
|
background-color:rgba(255, 0, 0, 0.5);
|
||||||
}
|
}
|
||||||
.pending {
|
[data-state=pending] {
|
||||||
background-color:rgba(255, 255, 0, 0.5);
|
background-color:rgba(255, 255, 0, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#emojiadd {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
#errorDiv {
|
#errorDiv {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head><body>
|
</head><body>
|
||||||
|
<h1>VRChat Emoji Manager</h1>
|
||||||
<div id="emojigrid"></div>
|
<div id="emojigrid"></div>
|
||||||
<div id="errorDiv"></div>
|
<div id="errorDiv"></div>
|
||||||
|
<div id="emojiadd">Add more!!! <input type="file" id="fileinput" accept="image/png,image/jpeg" multiple /> <select id="default_animation_style_select" value="aura"><option value="aura">Aura</option><option value="bats">Bats</option><option value="bees">Bees</option><option value="bounce">Bounce</option><option value="cloud">Cloud</option><option value="confetti">Confetti</option><option value="crying">Crying</option><option value="dislike">Dislike</option><option value="fire">Fire</option><option value="idea">Idea</option><option value="lasers">Lasers</option><option value="like">Like</option><option value="magnet">Magnet</option><option value="mistletoe">Mistletoe</option><option value="money">Money</option><option value="noise">Noise</option><option value="orbit">Orbit</option><option value="pizza">Pizza</option><option value="rain">Rain</option><option value="rotate">Rotate</option><option value="shake">Shake</option><option value="snow">Snow</option><option value="snowball">Snowball</option><option value="spin">Spin</option><option value="splash">Splash</option><option value="stop">Stop</option><option value="zzz">ZZZ</option></select> <input type="submit" id="submit" value="Add" /></div>
|
||||||
|
<div><button id="deletemodebtn">Delete Emojis</button></div>
|
||||||
<script src="toggle.js"></script>
|
<script src="toggle.js"></script>
|
||||||
</body></html>
|
</body></html>
|
182
toggle.js
182
toggle.js
@ -1,3 +1,92 @@
|
|||||||
|
const ANIMATION_STYLES = `Aura
|
||||||
|
Bats
|
||||||
|
Bees
|
||||||
|
Bounce
|
||||||
|
Cloud
|
||||||
|
Confetti
|
||||||
|
Crying
|
||||||
|
Dislike
|
||||||
|
Fire
|
||||||
|
Idea
|
||||||
|
Lasers
|
||||||
|
Like
|
||||||
|
Magnet
|
||||||
|
Mistletoe
|
||||||
|
Money
|
||||||
|
Noise
|
||||||
|
Orbit
|
||||||
|
Pizza
|
||||||
|
Rain
|
||||||
|
Rotate
|
||||||
|
Shake
|
||||||
|
Snow
|
||||||
|
Snowball
|
||||||
|
Spin
|
||||||
|
Splash
|
||||||
|
Stop
|
||||||
|
ZZZ`.split('\n');
|
||||||
|
|
||||||
|
function createAnimationStyleSelect() {
|
||||||
|
let select = document.createElement("select");
|
||||||
|
ANIMATION_STYLES.forEach(a => {
|
||||||
|
let option = document.createElement("option");
|
||||||
|
option.innerText = a;
|
||||||
|
option.value = a.toLowerCase();
|
||||||
|
select.appendChild(option);
|
||||||
|
});
|
||||||
|
return select;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
submit.onclick = async () => {
|
||||||
|
submit.disabled = true;
|
||||||
|
var newEmoji = [];
|
||||||
|
for (let file of fileinput.files) {
|
||||||
|
let e = {
|
||||||
|
internalId: randomId(),
|
||||||
|
animationStyle: default_animation_style_select.value,
|
||||||
|
};
|
||||||
|
let data = await fileToDataURL(file);
|
||||||
|
//todo convert/resize
|
||||||
|
await chrome.storage.local.set({["data-"+e.internalId]: data});
|
||||||
|
newEmoji.push(e);
|
||||||
|
instantiateRow(data, e.internalId, e.animationStyle);
|
||||||
|
}
|
||||||
|
var {emoji} = await chrome.storage.local.get("emoji");
|
||||||
|
emoji = emoji.concat(newEmoji);
|
||||||
|
await chrome.storage.local.set({emoji});
|
||||||
|
fileinput.value = "";
|
||||||
|
submit.disabled = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var deleteMode = false;
|
||||||
|
deletemodebtn.onclick = () => {
|
||||||
|
if (deleteMode) {
|
||||||
|
deleteMode = false;
|
||||||
|
deletemodebtn.innerText = "Delete Emojis";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
alert("Delete mode activated, click emojis to delete.");
|
||||||
|
deleteMode = true;
|
||||||
|
deletemodebtn.innerText = "CANCEL DELETE MODE";
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
loadEmojis().catch(displayError);
|
loadEmojis().catch(displayError);
|
||||||
|
|
||||||
async function loadEmojis() {
|
async function loadEmojis() {
|
||||||
@ -6,15 +95,26 @@ async function loadEmojis() {
|
|||||||
if (!store.emoji) throw new Error("No emoji.");
|
if (!store.emoji) throw new Error("No emoji.");
|
||||||
for (let emoji of store.emoji) {
|
for (let emoji of store.emoji) {
|
||||||
let div = document.createElement("div");
|
let div = document.createElement("div");
|
||||||
|
div.className = "emojisquare";
|
||||||
div.dataset.animationStyle = emoji.animationStyle;
|
div.dataset.animationStyle = emoji.animationStyle;
|
||||||
div.dataset.internalId = emoji.internalId;
|
div.dataset.internalId = emoji.internalId;
|
||||||
div.dataset.currentId = emoji.currentId;
|
div.dataset.currentId = emoji.currentId;
|
||||||
div.classList.add("emojisquare");
|
|
||||||
div.onclick = toggleEmoji;
|
div.onclick = toggleEmoji;
|
||||||
|
|
||||||
|
let imgdiv = document.createElement("div");
|
||||||
|
imgdiv.className = "imgcontainer";
|
||||||
let img = document.createElement("img");
|
let img = document.createElement("img");
|
||||||
img.src = store["data-" + emoji.internalId];
|
img.src = store["data-" + emoji.internalId];
|
||||||
div.appendChild(img);
|
imgdiv.appendChild(img);
|
||||||
|
div.appendChild(imgdiv);
|
||||||
|
|
||||||
|
let select = createAnimationStyleSelect();
|
||||||
|
select.value = emoji.animationStyle;
|
||||||
|
select.onclick = event => event.stopPropagation();
|
||||||
|
select.onchange = event => {
|
||||||
|
setAnimationStyle(emoji.internalId, event.target.value);
|
||||||
|
};
|
||||||
|
div.appendChild(select);
|
||||||
|
|
||||||
emojigrid.appendChild(div);
|
emojigrid.appendChild(div);
|
||||||
}
|
}
|
||||||
@ -22,14 +122,13 @@ async function loadEmojis() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadToggleState() {
|
async function loadToggleState() {
|
||||||
|
console.debug("loadToggleState");
|
||||||
var elements = document.querySelectorAll(".emojisquare");
|
var elements = document.querySelectorAll(".emojisquare");
|
||||||
if (elements.length === 0) return;
|
if (elements.length === 0) return;
|
||||||
var currentEmojis = await callContentScript("getEmojis");
|
var currentEmojis = await callContentScript("getEmojis");
|
||||||
var active = currentEmojis?.map(e => e.id);
|
var active = currentEmojis?.map(e => e.id);
|
||||||
elements.forEach(e => {
|
elements.forEach(e => {
|
||||||
var yes = active.includes(e.dataset.currentId);
|
e.dataset.state = active.includes(e.dataset.currentId) ? "enabled" : "disabled";
|
||||||
e.classList.add(yes ? "enabled" : "disabled");
|
|
||||||
e.classList.remove(yes ? "disabled" : "enabled");
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,44 +160,87 @@ async function callContentScript(method, ...args) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function toggleEmoji(event) {
|
async function toggleEmoji(event) {
|
||||||
if (this.classList.contains("pending")) return;
|
if (this.dataset.state == "pending") return;
|
||||||
this.classList.add("pending");
|
var selectedState = this.dataset.state;
|
||||||
|
this.dataset.state = "pending";
|
||||||
errorDiv.innerText = "";
|
errorDiv.innerText = "";
|
||||||
|
|
||||||
if (document.querySelectorAll(".enabled").length + document.querySelectorAll(".disabled").length < document.querySelectorAll(".emojisquare").length) {
|
if (emojigrid.querySelector(".emojisquare:not([data-state])")) {
|
||||||
try {
|
try {
|
||||||
await loadToggleState();
|
await loadToggleState();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
displayError(error);
|
displayError(error);
|
||||||
this.classList.remove("pending");
|
this.dataset.state = selectedState;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.classList.contains("enabled")) {
|
if (selectedState == "enabled") {
|
||||||
// disable
|
// disable
|
||||||
callContentScript("deleteEmoji", this.dataset.currentId).then(() => {
|
callContentScript("deleteEmoji", this.dataset.currentId).then(() => {
|
||||||
this.classList.remove("pending");
|
this.dataset.state = "disabled";
|
||||||
this.classList.remove("enabled");
|
|
||||||
this.classList.add("disabled");
|
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
this.classList.remove("pending");
|
this.dataset.state = selectedState;
|
||||||
displayError(error);
|
displayError(error);
|
||||||
});
|
});
|
||||||
} else {
|
if (!deleteMode) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (deleteMode) {
|
||||||
|
let {emoji} = await chrome.storage.local.get("emoji");
|
||||||
|
emoji = emoji.filter(e => e.internalId != this.dataset.internalId);
|
||||||
|
await chrome.storage.local.set({emoji});
|
||||||
|
this.remove();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// enable
|
// enable
|
||||||
callContentScript("createEmoji", this.querySelector("img").src, this.dataset.animationStyle).then(newEmoji => {
|
callContentScript("createEmoji", this.querySelector("img").src, this.dataset.animationStyle).then(newEmoji => {
|
||||||
chrome.storage.local.get("emoji").then(({emoji}) => {
|
chrome.storage.local.get("emoji").then(({emoji}) => {
|
||||||
emoji.find(e => e.internalId == this.dataset.internalId).currentId = newEmoji.id;
|
emoji.find(e => e.internalId == this.dataset.internalId).currentId = newEmoji.id;
|
||||||
chrome.storage.local.set({emoji});
|
chrome.storage.local.set({emoji});
|
||||||
this.dataset.currentId = newEmoji.id;
|
this.dataset.currentId = newEmoji.id;
|
||||||
this.classList.remove("pending");
|
this.dataset.state = "enabled";
|
||||||
this.classList.remove("disabled");
|
|
||||||
this.classList.add("enabled");
|
|
||||||
});
|
});
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
this.classList.remove("pending");
|
this.dataset.state = selectedState;
|
||||||
displayError(error);
|
displayError(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function setAnimationStyle(internalId, animationStyle) {
|
||||||
|
var {emoji} = await chrome.storage.local.get("emoji");
|
||||||
|
var e = emoji.find(e => e.internalId == internalId);
|
||||||
|
e.animationStyle = animationStyle;
|
||||||
|
await chrome.storage.local.set({emoji});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function fileToDataURL(file) {
|
||||||
|
return new Promise(function(resolve, reject){
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
reader.onload = () => {
|
||||||
|
resolve(reader.result);
|
||||||
|
};
|
||||||
|
reader.onerror = reject;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function randomId() {
|
||||||
|
let id = "";
|
||||||
|
const CHARS = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||||
|
for (var i = 0; i < 16; i++) {
|
||||||
|
id += CHARS[Math.floor(Math.random() * CHARS.length)];
|
||||||
|
}
|
||||||
|
return id;
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user