Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85804498
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
21 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/instance/pleroma-mods/pleroma-mod-catify/config.json b/instance/pleroma-mods/pleroma-mod-catify/config.json
index 2aac73625c..31e19ede13 100644
--- a/instance/pleroma-mods/pleroma-mod-catify/config.json
+++ b/instance/pleroma-mods/pleroma-mod-catify/config.json
@@ -1,58 +1,78 @@
{
"nya": {
"enabled": true,
"matcher": "(^|\\s|>)な+(\\s|<|$)",
"replacer": {
"source": "な",
"dest": "にゃ"
}
},
"triggers": {
- "displayName": [
- "🐱",
- "😺",
- "🐈",
- "😼",
- "😹",
- "にゃ",
- "cat",
- "mew",
- "meow",
- "nya",
- "miaou",
- "miau",
- "kitten",
- "kitn",
- "ktn",
- "kadse",
- "catte",
- "makemeneko",
- "🔒",
- "waifu",
- "zonk",
- "neko",
- "anime",
- "red",
- "bot",
- "moe",
- "jason",
- "admiral",
- "corona",
- "lumeinshin",
- "coomer",
- "famardy",
- "matrix",
- "iji",
- "lewd",
- "deviant",
- "disabled"
- ],
- "instances": [
- "misskey.io",
- "fedi.absturztau.be",
- "catgirl.life",
- "freespeechextremist.com",
- "blob.cat"
- ]
+ "cat": {
+ "displayName": [
+ "🐱",
+ "😺",
+ "🐈",
+ "😼",
+ "😹",
+ "にゃ",
+ "cat",
+ "mew",
+ "meow",
+ "nya",
+ "miaou",
+ "miau",
+ "kitten",
+ "kitn",
+ "ktn",
+ "kadse",
+ "catte",
+ "makemeneko",
+ "🔒",
+ "waifu",
+ "zonk",
+ "neko",
+ "anime",
+ "red",
+ "bot",
+ "moe",
+ "jason",
+ "admiral",
+ "corona",
+ "lumeinshin",
+ "coomer",
+ "famardy",
+ "matrix",
+ "iji",
+ "lewd",
+ "deviant",
+ "disabled"
+ ],
+ "instances": [
+ "misskey.io",
+ "fedi.absturztau.be",
+ "catgirl.life",
+ "freespeechextremist.com",
+ "blob.cat"
+ ]
+ },
+ "bear": {
+ "displayName": [
+ "🐻"
+ ],
+ "instances": []
+ },
+ "rabbit": {
+ "displayName": [
+ "🐰",
+ "🐇",
+ "rabbit",
+ "bun",
+ "hase",
+ "häschen",
+ "kaninchen"
+ ],
+ "instances": []
+ }
}
}
diff --git a/instance/pleroma-mods/pleroma-mod-catify/mod.js b/instance/pleroma-mods/pleroma-mod-catify/mod.js
index 3f34191c1a..1f549aa9fd 100644
--- a/instance/pleroma-mods/pleroma-mod-catify/mod.js
+++ b/instance/pleroma-mods/pleroma-mod-catify/mod.js
@@ -1,350 +1,389 @@
function ColorDetector (image) {
this.color = { r: 0, g: 0, b: 0 };
this.image = image;
}
[
function componentToHex (c) {
var hex = Math.max(Math.min(c, 255), 0).toString(16);
return hex.length === 1 ? "0" + hex : hex;
},
function getHexColor (offset) {
if (!offset) {
offset = { r: 0, g: 0, b: 0 };
}
return "#" +
this.componentToHex(this.color.r + offset.r) +
this.componentToHex(this.color.g + offset.g) +
this.componentToHex(this.color.b + offset.b);
},
function detect () {
return new Promise((resolve) => {
let blockSize = 5;
const canvas = document.createElement("canvas");
const context = canvas.getContext && canvas.getContext("2d");
let data; let width; let height;
let i = -4;
let rgb = { r: 0, g: 0, b: 0 };
let length;
let count = 0;
if (!context) {
console.warn("can't get context of avatar");
resolve(this.color);
return;
}
height = canvas.height = this.image.naturalHeight || this.image.offsetHeight || this.image.height;
width = canvas.width = this.image.naturalWidth || this.image.offsetWidth || this.image.width;
context.drawImage(this.image, 0, 0);
try {
data = context.getImageData(0, 0, width, height);
} catch (e) {
console.error("can't get image data");
console.error(e);
resolve(this.color);
return;
}
length = data.data.length;
while ((i += blockSize * 4) < length) {
++count;
rgb.r += data.data[i];
rgb.g += data.data[i + 1];
rgb.b += data.data[i + 2];
}
this.color.r = ~~(rgb.r / count);
this.color.g = ~~(rgb.g / count);
this.color.b = ~~(rgb.b / count);
resolve(this.color);
});
}
].forEach((fn) => { ColorDetector.prototype[fn.name] = fn; });
-function PleromaCat (handle) {
+function PleromaCat (handle, type) {
+ this.type = type || "cat";
this.handle = handle;
this.colors = {
backgroundColor: "#000000",
borderColor: "#000000"
};
this.config = {
"nya": {
enabled: true,
matcher: "(^|\s|>)な+(\s|<|$)", // eslint-disable-line no-useless-escape
replacer: {
source: "な",
dest: "にゃ"
}
}
};
this.loadConfig();
}
[
function loadConfig () {
const json = PleromaModCatify.config;
if (!json.nya) {
return;
}
this.config.nya.enabled = json.nya.enabled;
if (this.config.nya.enabled) {
this.config.nya.matcher = json.nya.matcher || this.config.nya.matcher;
this.config.nya.replacer.source = json.nya.replacer.source || this.config.nya.replacer.source;
this.config.nya.replacer.dest = json.nya.replacer.dest || this.config.nya.replacer.dest;
}
},
function getClassName () {
return "USER____" + this.handle.replace(/@/g, "_AT_").replace(/\./g, "_");
},
function makeCat (element) {
if (!element) {
element = document;
}
if (element.querySelectorAll) {
var posts = element.querySelectorAll("." + this.getClassName());
this.makeCatByClassName("user-info");
this.makeCatByClassName("basic-user-card", "basic-user-card-screen-name");
for (const currentPost of posts) {
this.makeCatByElement(currentPost);
this.nyaByPost(currentPost);
}
}
},
function makeCatByClassName (className, usernameClass) {
if (!className) {
className = "user-info";
}
if (!usernameClass) {
usernameClass = "user-screen-name";
}
const userinfos = document.querySelectorAll("." + className);
for (const infoIndex in userinfos) {
if (userinfos[infoIndex].querySelector && !/cat$/.test(userinfos[infoIndex].innerText)) {
const handle = userinfos[infoIndex].querySelector("." + usernameClass);
const regexHandle = new RegExp(this.handle, "i");
if (handle) {
if (regexHandle.test(handle.innerText)) {
this.makeCatByElement(userinfos[infoIndex]);
}
}
}
}
},
function makeCatByElement (element) {
if (element.querySelectorAll) {
- if (!/cat$/.test(element.className)) {
- element.className += " cat";
- }
- const avatars = element.querySelectorAll(".avatar");
+ element.classList.add("catified");
+ element.classList.add(this.type);
+ const avatars = element.querySelectorAll(".Avatar");
for (const avatarIndex in avatars) {
const currentAvatar = avatars[avatarIndex];
if (currentAvatar.style) {
if (this.colors.borderColor === "#000000") {
this.detectColors(currentAvatar);
}
currentAvatar.style.backgroundColor = this.colors.backgroundColor;
currentAvatar.style.borderColor = this.colors.borderColor;
}
}
}
},
function nyaByPost (element) {
- if (element.querySelectorAll && this.config.nya.enabled) {
+ if (element.querySelectorAll && this.config.nya.enabled && element.classList.contains("cat")) {
const contents = element.querySelectorAll(".status-content");
for (const content of contents) {
if (content.innerHTML) {
const regex = new RegExp(this.config.nya.matcher, "g");
let match;
while ((match = regex.exec(content.innerHTML)) !== null) {
const source = match[0];
const dest = source.replace(
new RegExp(this.config.nya.replacer.source, "g"),
this.config.nya.replacer.dest
);
content.innerHTML = content.innerHTML.replace(source, dest);
}
}
}
}
},
function detectColors (avatarElement) {
const images = avatarElement.querySelectorAll("img");
for (const imageIndex in images) {
images[imageIndex].crossOrigin = "anonymous";
const colorAvatar = () => {
const detector = new ColorDetector(images[imageIndex]);
detector.detect().then((color) => {
this.colors.backgroundColor = detector.getHexColor();
this.colors.borderColor = detector.getHexColor({ r: -40, g: -40, b: -40 });
avatarElement.style.backgroundColor = this.colors.backgroundColor;
avatarElement.style.borderColor = this.colors.borderColor;
});
};
if (images[imageIndex].complete) {
colorAvatar();
} else {
images[imageIndex].onload = colorAvatar;
}
return;
}
}
].forEach((fn) => { PleromaCat.prototype[fn.name] = fn; });
function PleromaModCatify () {
- this.cats = {};
+ this.animals = {};
this.config = {
stylesheet: "style.css",
triggers: {
- displayName: [
- "🐱",
- "😺",
- "🐈",
- "😼",
- "😹",
- "にゃ",
- "cat",
- "mew",
- "meow",
- "nya",
- "miaou",
- "kitten",
- "kitn",
- "ktn",
- "kadse",
- "catte"
- ],
- instances: [
- "misskey.io"
- ]
+ cat: {
+ displayName: [
+ "🐱",
+ "😺",
+ "🐈",
+ "😼",
+ "😹",
+ "にゃ",
+ "cat",
+ "mew",
+ "meow",
+ "nya",
+ "miaou",
+ "kitten",
+ "kitn",
+ "ktn",
+ "kadse",
+ "catte"
+ ],
+ instances: [
+ "misskey.io"
+ ]
+ },
+ bear: {
+ displayName: [
+ "🐻"
+ ],
+ instances: []
+ },
+ rabbit: {
+ displayName: [
+ "🐰",
+ "🐇",
+ "rabbit",
+ "bunny",
+ "hase",
+ "häschen",
+ "kaninchen"
+ ],
+ instances: []
+ }
},
filter: [
"user-info",
"timeline",
+ "Conversation",
"panel-body",
"main",
"active",
"status-body"
]
};
this.loadConfig();
}
[
function loadConfig () {
window.fetch(PleromaModLoader.getModDir() + "pleroma-mod-catify/config.json").then((response) => {
if (response.ok) {
response.json().then((json) => {
PleromaModCatify.config = json;
- this.config.triggers.displayName = json.triggers.displayName;
- this.config.triggers.instances = json.triggers.instances;
+ for (const type in json.triggers) {
+ this.config.triggers[type] = {};
+ this.config.triggers[type].displayName = json.triggers[type].displayName || [];
+ this.config.triggers[type].instances = json.triggers[type].instances || [];
+ }
}).catch((error) => {
console.error("can't parse catify config");
console.error(error);
});
}
}).catch((error) => {
console.warn("can't load catify config");
console.warn(error);
});
},
function onMutation (mutation, observer) {
if (mutation.target.classList.contains("user-info")) {
- mutation.target.classList.remove("cat");
+ mutation.target.classList.remove("catified");
+ for (const type in this.config.triggers) {
+ mutation.target.classList.remove(type);
+ }
}
this.detectCats();
for (const element of mutation.addedNodes) {
this.catify(element);
}
},
function onReady () {
this.areYouACat();
this.detectCats();
this.catify();
},
function onDestroy () {
- const allCats = document.querySelectorAll(".cat");
+ const allCats = document.querySelectorAll(".catified");
for (const cat of allCats) {
- cat.classList.remove("cat");
+ cat.classList.remove("catified");
+ for (const type in this.config.triggers) {
+ cat.classList.remove(type);
+ }
}
},
function run () {
PleromaModLoader.includeModCss("pleroma-mod-catify/" + this.config.stylesheet);
},
- function addCat (handle) {
+ function addCat (handle, type) {
+ if (type == null) {
+ type = "cat";
+ }
handle = handle.trim();
- if (!this.cats.handle) {
- this.cats[handle] = new PleromaCat(handle);
+ if (!this.animals[handle]) {
+ this.animals[handle] = new PleromaCat(handle, type);
}
},
function areYouACat () {
const profile = document.querySelector(".user-card");
- const pattern = this.config.triggers.displayName.join("|");
- const regex = new RegExp(pattern, "i");
- if (profile) {
- const username = profile.querySelector(".user-name");
- if (username) {
- if (regex.test(username.innerText)) {
- const accountName = profile.querySelector(".user-screen-name");
- if (accountName) {
- this.addCat(accountName.innerText.substring(1), true);
+ for (const type in this.config.triggers) {
+ const pattern = this.config.triggers[type].displayName.join("|");
+ const regex = new RegExp(pattern, "i");
+ if (profile) {
+ const username = profile.querySelector(".user-name");
+ if (username) {
+ if (regex.test(username.innerText)) {
+ const accountName = profile.querySelector(".user-screen-name");
+ if (accountName) {
+ this.addCat(accountName.innerText.substring(1), type);
+ }
}
}
}
}
},
function detectCatsByClassName (classname, usernameClass, accountnameClass) {
- classname = classname || "name-and-account-name";
- usernameClass = usernameClass || "user-name";
+ classname = classname || "status-container";
+ usernameClass = usernameClass || "status-username";
accountnameClass = accountnameClass || "account-name";
const nameAndAccountNames = document.querySelectorAll("." + classname);
- const regexName = new RegExp(this.config.triggers.displayName.join("|"), "i");
- const regexInstance = new RegExp(this.config.triggers.instances.join("|"), "i");
- for (const currentAccount of nameAndAccountNames) {
- if (currentAccount.querySelector) {
- let isCat = false;
- const username = currentAccount.querySelector("." + usernameClass);
- if (username) {
- isCat = regexName.test(username.innerText);
- }
- const account = currentAccount.querySelector("." + accountnameClass);
- if (account) {
- const handle = account.innerText;
- isCat = isCat || regexInstance.test(handle);
- if (isCat) {
- this.addCat(handle);
+ for (const type in this.config.triggers) {
+ const regexName = new RegExp(this.config.triggers[type].displayName.join("|"), "i");
+ const regexInstance = new RegExp(this.config.triggers[type].instances.join("|"), "i");
+ for (const currentAccount of nameAndAccountNames) {
+ if (currentAccount.querySelector) {
+ let isCat = false;
+ const username = currentAccount.querySelector("." + usernameClass);
+ if (username && this.config.triggers[type].displayName.length > 0) {
+ isCat = regexName.test(username.innerText);
+ }
+ const account = currentAccount.querySelector("." + accountnameClass);
+ if (account) {
+ const handle = account.innerText;
+ if (this.config.triggers[type].instances.length > 0) {
+ isCat = isCat || regexInstance.test(handle);
+ }
+ if (isCat) {
+ this.addCat(handle, type);
+ }
}
}
}
}
},
function detectCats () {
- this.detectCatsByClassName("name-and-account-name");
+ this.detectCatsByClassName("status-container");
this.detectCatsByClassName("basic-user-card", "basic-user-card-user-name-value", "basic-user-card-screen-name");
},
function catify (element) {
- for (const catKey in this.cats) {
- this.cats[catKey].makeCat(element);
+ for (const catKey in this.animals) {
+ this.animals[catKey].makeCat(element);
}
}
].forEach((fn) => { PleromaModCatify.prototype[fn.name] = fn; });
PleromaModLoader.registerMod(PleromaModCatify);
diff --git a/instance/pleroma-mods/pleroma-mod-catify/style.css b/instance/pleroma-mods/pleroma-mod-catify/style.css
index 08ce64b72b..7cfea2f276 100644
--- a/instance/pleroma-mods/pleroma-mod-catify/style.css
+++ b/instance/pleroma-mods/pleroma-mod-catify/style.css
@@ -1,73 +1,123 @@
-.cat>:not(.status-body) .avatar::before, .cat>:not(.status-body) .avatar::after {
+.catified>:not(.status-container) .Avatar::before, .catified>:not(.status-container) .Avatar::after {
content: "";
display: inline-block;
border: 4px solid;
box-sizing: border-box;
width: 50%;
height: 50%;
background-color: inherit;
border-color: inherit;
position: absolute;
z-index: 0;
}
-.cat>:not(.status-body) .avatar {
+.catified>:not(.status-container) .Avatar {
overflow: visible;
border-radius: 100%!important;
}
-.cat .user-info-avatar-link-overlay {
+.catified .user-info-avatar-link-overlay {
border-radius: 100%!important;
z-index: 1;
}
-.cat>:not(.status-body) .avatar img, .cat>:not(.status-body) .avatar canvas {
+.catified>:not(.status-container) .Avatar img, .catified>:not(.status-container) .Avatar canvas {
border-radius: 100%;
top: 0;
bottom: 0;
right: 0;
left: 0;
z-index: 1;
position: absolute;
}
-.cat>:not(.status-body) .avatar::before {
- border-radius: 75% 0 75% 75%;
+.catified>:not(.status-container) .Avatar::before {
transform: rotate(-37.6deg) skew(-30deg);
top: 0;
bottom: 0;
right: 0;
}
-.cat .avatar:hover::before {
+.catified.cat>:not(.status-container) .Avatar::before {
+ border-radius: 75% 0 75% 75%;
+}
+
+.catified.bear>:not(.status-container) .Avatar::before {
+ border-radius: 75%;
+ top: -3px;
+}
+
+.catified .Avatar:hover::before {
animation: earwiggleright 1s infinite;
}
-.cat>:not(.status-body) .avatar::after {
- border-radius: 0 75% 75%;
+.catified>:not(.status-container) .Avatar::after {
transform: rotate(37.6deg) skew(30deg);
top: 0;
bottom: 0;
left: 0;
right: 0;
}
-.cat .avatar:hover::after {
+.catified.cat>:not(.status-container) .Avatar::after {
+ border-radius: 0 75% 75%;
+}
+
+.catified.bear>:not(.status-container) .Avatar::after {
+ border-radius: 75%;
+ top: -3px;
+}
+
+.catified .Avatar:hover::after {
animation: earwiggleleft 1s infinite;
}
@keyframes earwiggleleft {
from { transform: rotate(37.6deg) skew(30deg); }
25% { transform: rotate(10deg) skew(30deg); }
50% { transform: rotate(20deg) skew(30deg); }
75% { transform: rotate(0deg) skew(30deg); }
to { transform: rotate(37.6deg) skew(30deg); }
}
@keyframes earwiggleright {
from { transform: rotate(-37.6deg) skew(-30deg); }
30% { transform: rotate(-10deg) skew(-30deg); }
55% { transform: rotate(-20deg) skew(-30deg); }
75% { transform: rotate(-0deg) skew(-30deg); }
to { transform: rotate(-37.6deg) skew(-30deg); }
}
+
+.catified.rabbit>:not(.status-container) .Avatar::before,
+.catified.rabbit>:not(.status-container) .Avatar::after {
+ /* 100px makes that it's actually just rounding and not a curve between the top and the sides */
+ border-radius: 100px 100px 0 0;
+ border-width: 3px;
+ height: 100%;
+}
+.catified.rabbit>:not(.status-container) .Avatar::before {
+ transform: rotate(10deg);
+ top: -.5rem;
+ right: 13.25%;
+ width: 18.5%;
+}
+.catified.rabbit>:not(.status-container) .Avatar::after {
+ transform: none;
+ top: -.7rem;
+ left: 13.25%;
+ width: 20%;
+}
+.catified.rabbit .Avatar:hover::before { animation: earwiggleright-rabbit 0.65s infinite; }
+.catified.rabbit .Avatar:hover::after { animation: earwiggleleft-rabbit 0.65s infinite; }
+
+@keyframes earwiggleleft-rabbit {
+ from { transform: rotate(0deg); }
+ 75% { transform: rotate(-25deg); }
+ to { transform: rotate(0deg); }
+}
+
+@keyframes earwiggleright-rabbit {
+ from { transform: rotate(10deg); }
+ 75% { transform: rotate(25deg); }
+ to { transform: rotate(10deg); }
+}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Oct 10, 7:34 AM (1 d, 15 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1785106
Default Alt Text
(21 KB)
Attached To
Mode
rPUFE pleroma-fe-upstream
Attached
Detach File
Event Timeline
Log In to Comment