Compare commits
8 Commits
faec1140e7
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 90a7f95fd2 | |||
| b4f0b1838e | |||
| 73d9d380f5 | |||
| 5d6a0d7d41 | |||
| bde00f27d3 | |||
| 893fe47ace | |||
| d078339288 | |||
| ac8e88fe66 |
82
Chaturbate/README.md
Normal file
82
Chaturbate/README.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Chaturbate 缩略图自定义缩放
|
||||
|
||||
这个油猴脚本用于自定义调整 Chaturbate 页面中的缩略图和房间卡片尺寸,让首页、关注页、发现页、频道页相关推荐、关注下拉和悬停预览在大屏上更容易浏览。
|
||||
|
||||
## 安装地址
|
||||
|
||||
[https://gitea.jackadam.top/jack/Tampermonkey_scripts/raw/branch/main/Chaturbate/chaturbate-thumbnails-custom.user.js](https://gitea.jackadam.top/jack/Tampermonkey_scripts/raw/branch/main/Chaturbate/chaturbate-thumbnails-custom.user.js)
|
||||
|
||||
脚本元信息中的 `@updateURL` 和 `@downloadURL` 都指向上面的 Gitea raw 地址,因此安装后可以通过 Tampermonkey 自动更新。
|
||||
|
||||
## 主要功能
|
||||
|
||||
- 自定义放大首页和关注页房间列表。
|
||||
- 自定义放大发现页横向轮播区域。
|
||||
- 自定义放大频道页“更多这样的直播间”。
|
||||
- 自定义放大顶部“关注”下拉弹层中的预览卡片。
|
||||
- 修正关注下拉弹层放大后可能超出屏幕左侧的问题。
|
||||
- 翻译 `tags` 页面中以 `#` 开头的主题标签。
|
||||
- 在页面右下角提供缩放控制面板。
|
||||
- 可选自动隐藏频道页直播画面上方的小聊天浮窗。
|
||||
|
||||
## 页面控制项
|
||||
|
||||
脚本加载后会在页面右下角显示控制面板:
|
||||
|
||||
| 控制项 | 作用 |
|
||||
| --- | --- |
|
||||
| 响应式 | 用于首页、关注页、频道页相关推荐等需要跟随浏览器宽度自动换列的区域。 |
|
||||
| 固定式 | 用于关注下拉、悬停预览等固定尺寸弹层。 |
|
||||
| 隐藏聊天窗 | 只隐藏频道页直播画面上方的小聊天浮窗,不隐藏主直播画面。 |
|
||||
| 恢复原始尺寸 | 将响应式和固定式倍率恢复为 `1.0`。 |
|
||||
|
||||
倍率支持小数点后一位。设置会保存到浏览器 `localStorage`,下次打开页面继续生效。
|
||||
|
||||
## 放大逻辑
|
||||
|
||||
脚本尽量保留网站原有响应式布局,只改变尺寸倍率:
|
||||
|
||||
- 响应式列表不固定列数,而是提高卡片的最小宽度,让浏览器按窗口宽度自动决定列数。
|
||||
- 缩略图宽高按倍率放大,避免只改宽度导致比例失衡。
|
||||
- 卡片整体高度使用自动布局,避免文字区被强行拉高或压扁。
|
||||
- 固定弹层只在必要时调整位置,弹层放大后如果超出屏幕左侧,才向右移动到可见范围内。
|
||||
|
||||
## 适用页面
|
||||
|
||||
- `https://chaturbate.com/*`
|
||||
- `https://*.chaturbate.com/*`
|
||||
|
||||
常用中文页面包括:
|
||||
|
||||
- `https://zh-hans.chaturbate.com/`
|
||||
- `https://zh-hans.chaturbate.com/followed-cams/`
|
||||
- `https://zh-hans.chaturbate.com/discover/`
|
||||
- `https://zh-hans.chaturbate.com/tags/`
|
||||
- `https://zh-hans.chaturbate.com/tags/female/`
|
||||
- 各主播频道页,例如 `https://zh-hans.chaturbate.com/example_room/`
|
||||
|
||||
## 调试
|
||||
|
||||
脚本保留控制台调试入口:
|
||||
|
||||
```js
|
||||
tmThumbScaleDebug()
|
||||
tmThumbScaleInspectRecommendations()
|
||||
tmThumbScaleInspectRoomChat()
|
||||
tmThumbScaleSetScales(1.8, 1.5)
|
||||
```
|
||||
|
||||
如果页面布局异常,可以先在控制台执行 `tmThumbScaleDebug()`,查看当前页面识别到的区域、CSS 变量和脚本版本。
|
||||
|
||||
## 本地验证
|
||||
|
||||
```powershell
|
||||
node --check .\Chaturbate\chaturbate-thumbnails-custom.user.js
|
||||
node .\tests\followed-dropdown-css.test.js
|
||||
```
|
||||
|
||||
## 维护注意
|
||||
|
||||
- 修改脚本名或文件名时,同步更新 `@updateURL`、`@downloadURL`、根目录 README 和测试文件路径。
|
||||
- 不要把本机 `127.0.0.1` 调试地址写进 `@updateURL` 或 `@downloadURL`。
|
||||
- 若 Chaturbate 页面结构变更,应优先在浏览器中检查真实 DOM,再调整选择器。
|
||||
File diff suppressed because it is too large
Load Diff
2975
Chaturbate/chaturbate-thumbnails-custom.user.js
Normal file
2975
Chaturbate/chaturbate-thumbnails-custom.user.js
Normal file
File diff suppressed because it is too large
Load Diff
31
README.md
Normal file
31
README.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# Tampermonkey Scripts
|
||||
|
||||
个人维护的油猴脚本集合。当前主要脚本用于改造 Chaturbate 页面展示,方便在较大屏幕上浏览房间列表、发现页轮播、关注下拉和频道页相关推荐。
|
||||
|
||||
## 脚本列表
|
||||
|
||||
| 脚本 | 说明 | 安装 / 更新 |
|
||||
| --- | --- | --- |
|
||||
| [Chaturbate 缩略图自定义缩放](./Chaturbate/README.md) | 自定义放大 Chaturbate 缩略图和房间卡片,支持页面控制面板、标签中文化、频道页小聊天窗隐藏。 | [安装地址](https://gitea.jackadam.top/jack/Tampermonkey_scripts/raw/branch/main/Chaturbate/chaturbate-thumbnails-custom.user.js) |
|
||||
|
||||
## 使用方式
|
||||
|
||||
1. 安装 Tampermonkey 或兼容的用户脚本管理器。
|
||||
2. 打开上表中的安装地址。
|
||||
3. 在 Tampermonkey 安装页确认安装。
|
||||
4. 后续脚本会通过 `@updateURL` 从本仓库自动检查更新。
|
||||
|
||||
## 开发与测试
|
||||
|
||||
当前脚本为纯 JavaScript userscript,不需要构建步骤。
|
||||
|
||||
```powershell
|
||||
node --check .\Chaturbate\chaturbate-thumbnails-custom.user.js
|
||||
node .\tests\followed-dropdown-css.test.js
|
||||
```
|
||||
|
||||
## 维护约定
|
||||
|
||||
- 脚本文件放在对应站点目录下。
|
||||
- 每个脚本目录应包含自己的 `README.md`,说明功能、安装地址和主要行为。
|
||||
- 发布前确认 `@updateURL` 和 `@downloadURL` 指向可公开访问的 raw 地址,而不是本机调试地址。
|
||||
@@ -2,7 +2,7 @@ const assert = require("node:assert/strict");
|
||||
const fs = require("node:fs");
|
||||
const vm = require("node:vm");
|
||||
|
||||
const source = fs.readFileSync("Chaturbate/chaturbate-thumbnails-2x.user.js", "utf8");
|
||||
const source = fs.readFileSync("Chaturbate/chaturbate-thumbnails-custom.user.js", "utf8");
|
||||
let capturedCss = "";
|
||||
let gmAddStyleCalls = 0;
|
||||
let menuCommandName = "";
|
||||
@@ -86,38 +86,43 @@ assert.match(
|
||||
);
|
||||
assert.match(
|
||||
capturedCss,
|
||||
/data-tm-thumb-scale-following-list="1"[\s\S]*--tm-thumb-following-list-width/,
|
||||
"followed-cams list should be separated from homepage list sizing",
|
||||
/data-tm-thumb-scale-following-list="1"[\s\S]*--tm-thumb-following-list-min-width/,
|
||||
"followed-cams list should use its own detected minimum column width",
|
||||
);
|
||||
assert.match(
|
||||
capturedCss,
|
||||
/data-tm-thumb-scale-following-list="1"[\s\S]*grid-template-columns:\s*repeat\(auto-fill,\s*minmax\(var\(--tm-thumb-following-list-width\),\s*1fr\)\)\s*!important/,
|
||||
"followed-cams list should keep the site's stretching grid alignment while using detected minimum width",
|
||||
/data-tm-thumb-scale-following-list="1"[\s\S]*grid-template-columns:\s*repeat\(auto-fill,\s*minmax\(var\(--tm-thumb-following-list-min-width\),\s*1fr\)\)\s*!important/,
|
||||
"followed-cams list should reduce columns responsively using scaled minimum width",
|
||||
);
|
||||
assert.match(
|
||||
capturedCss,
|
||||
/#main\.roomPage\s+ul\.list:has\(li\.roomCard\)\s+\.room_thumbnail_container[\s\S]*width:\s*var\(--tm-thumb-related-thumb-width\)\s*!important[\s\S]*height:\s*var\(--tm-thumb-related-height\)\s*!important/,
|
||||
"room page related rooms thumbnail containers should use detected thumb width and height",
|
||||
/#main\.roomPage\s+ul\.list:has\(li\.roomCard\)\s+\.room_thumbnail_container[\s\S]*width:\s*100%\s*!important[\s\S]*height:\s*auto\s*!important/,
|
||||
"room page related rooms thumbnail containers should follow card width and automatic height",
|
||||
);
|
||||
assert.match(
|
||||
capturedCss,
|
||||
/\.BaseRoomContents\s+ul\.list:has\(li\.roomCard\)\s+\.room_thumbnail[\s\S]*width:\s*var\(--tm-thumb-related-thumb-width\)\s*!important[\s\S]*height:\s*var\(--tm-thumb-related-height\)\s*!important/,
|
||||
"base room related thumbnails should use detected thumb width and height",
|
||||
/\.BaseRoomContents\s+ul\.list:has\(li\.roomCard\)\s+\.room_thumbnail[\s\S]*width:\s*100%\s*!important[\s\S]*height:\s*auto\s*!important/,
|
||||
"base room related thumbnails should follow card width and automatic height",
|
||||
);
|
||||
assert.match(
|
||||
capturedCss,
|
||||
/\.BaseRoomContents\s+ul\.RoomCardGrid[\s\S]*grid-template-columns:\s*repeat\(auto-fill,\s*minmax\(var\(--tm-thumb-related-width\),\s*1fr\)\)\s*!important/,
|
||||
"base room RoomCardGrid related rooms should use detected width",
|
||||
"base room RoomCardGrid related rooms should fill the row using detected scaled minimum width",
|
||||
);
|
||||
assert.match(
|
||||
capturedCss,
|
||||
/\.BaseRoomContents\s+ul\.RoomCardGrid\s+\.RoomCardThumbnail[\s\S]*width:\s*var\(--tm-thumb-related-thumb-width\)\s*!important[\s\S]*height:\s*var\(--tm-thumb-related-height\)\s*!important/,
|
||||
"base room RoomCardGrid thumbnails should use detected thumb width and height",
|
||||
/\.BaseRoomContents\s+ul\.RoomCardGrid\s+\.RoomCardThumbnail[\s\S]*width:\s*100%\s*!important[\s\S]*height:\s*auto\s*!important/,
|
||||
"base room RoomCardGrid thumbnails should follow card width and automatic height",
|
||||
);
|
||||
assert.match(
|
||||
capturedCss,
|
||||
/\.BaseRoomContents\s+ul\.RoomCardGrid\s*>\s*li\.RoomCard[\s\S]*height:\s*var\(--tm-thumb-related-card-height\)\s*!important/,
|
||||
"base room RoomCardGrid related cards should use detected card height",
|
||||
/\.BaseRoomContents\s+ul\.RoomCardGrid\s*>\s*li\.RoomCard[\s\S]*height:\s*auto\s*!important[\s\S]*max-height:\s*none\s*!important/,
|
||||
"base room RoomCardGrid related cards should use automatic height",
|
||||
);
|
||||
assert.match(
|
||||
capturedCss,
|
||||
/\.BaseRoomContents\s+ul\.RoomCardGrid\s*>\s*li\.RoomCard[\s\S]*width:\s*auto\s*!important[\s\S]*justify-self:\s*stretch\s*!important[\s\S]*max-width:\s*none\s*!important/,
|
||||
"base room RoomCardGrid related cards should stretch to fill responsive columns",
|
||||
);
|
||||
assert.doesNotMatch(capturedCss, /--tm-thumb-min-root:\s*\d+px;/, "script should not hard-code a root thumbnail fallback width");
|
||||
assert.doesNotMatch(capturedCss, /--tm-thumb-height-root:\s*\d+px;/, "script should not hard-code a root thumbnail fallback height");
|
||||
@@ -131,6 +136,11 @@ assert.match(
|
||||
/#discover_root\s+\.room-list-carousel\s+\.room_thumbnail_container\s+img/,
|
||||
"discover carousel thumbnail images should fill enlarged items",
|
||||
);
|
||||
assert.match(
|
||||
capturedCss,
|
||||
/#discover_root\s+\.room-list-carousel\s+\.room_thumbnail_container[\s\S]*height:\s*var\(--tm-thumb-discover-height\)\s*!important/,
|
||||
"discover carousel thumbnail containers should use detected scaled height",
|
||||
);
|
||||
assert.match(
|
||||
capturedCss,
|
||||
/height:\s*var\(--tm-thumb-discover-triple-ul\)\s*!important/,
|
||||
@@ -141,6 +151,36 @@ assert.match(
|
||||
/height:\s*var\(--tm-thumb-discover-double-arrow\)\s*!important/,
|
||||
"discover carousel arrow heights should use detected original heights",
|
||||
);
|
||||
assert.match(
|
||||
capturedCss,
|
||||
/data-tm-thumb-scale-followed-page="1"[\s\S]*\.carousel-root\s+\.single-row\s+ul\.list[\s\S]*height:\s*auto\s*!important[\s\S]*max-height:\s*none\s*!important[\s\S]*overflow:\s*visible\s*!important/,
|
||||
"followed-cams recommendations should release carousel list height after scaling",
|
||||
);
|
||||
assert.match(
|
||||
capturedCss,
|
||||
/data-tm-thumb-scale-followed-page="1"[\s\S]*grid-template-columns:\s*repeat\(auto-fill,\s*minmax\(var\(--tm-thumb-discover-width\),\s*1fr\)\)\s*!important/,
|
||||
"followed-cams legacy recommendations should use responsive scaled minimum width",
|
||||
);
|
||||
assert.doesNotMatch(
|
||||
capturedCss,
|
||||
/data-tm-thumb-scale-followed-page="1"[\s\S]*grid-template-columns:\s*repeat\(2,/,
|
||||
"followed-cams recommendations should not force a hard-coded two-column layout",
|
||||
);
|
||||
assert.match(
|
||||
capturedCss,
|
||||
/data-tm-thumb-scale-followed-recommendations="1"[\s\S]*data-tm-thumb-followed-recommendations-list="1"[\s\S]*grid-template-columns:\s*repeat\(auto-fill,\s*minmax\(var\(--tm-thumb-followed-recommendations-width\),\s*1fr\)\)\s*!important[\s\S]*grid-auto-rows:\s*auto\s*!important/,
|
||||
"title-detected followed recommendations should use responsive scaled minimum columns and automatic row height",
|
||||
);
|
||||
assert.match(
|
||||
capturedCss,
|
||||
/data-tm-thumb-scale-followed-recommendations="1"[\s\S]*data-tm-thumb-followed-recommendations-card="1"[\s\S]*width:\s*auto\s*!important[\s\S]*max-width:\s*none\s*!important[\s\S]*height:\s*auto\s*!important[\s\S]*max-height:\s*none\s*!important/,
|
||||
"title-detected followed recommendation cards should stretch responsively and grow naturally",
|
||||
);
|
||||
assert.match(
|
||||
capturedCss,
|
||||
/data-tm-thumb-scale-followed-recommendations="1"[\s\S]*data-tm-thumb-followed-recommendations-thumb="1"[\s\S]*height:\s*auto\s*!important/,
|
||||
"title-detected followed recommendation thumbnails should keep automatic height",
|
||||
);
|
||||
assert.match(
|
||||
capturedCss,
|
||||
/#discover_root\s+\.room-list-carousel\s+\.room_thumbnail[\s\S]*height:\s*var\(--tm-thumb-discover-height\)\s*!important/,
|
||||
@@ -153,9 +193,12 @@ assert.match(
|
||||
);
|
||||
assert.match(
|
||||
capturedCss,
|
||||
/\.FollowedDropdown__room-image[\s\S]*width:\s*var\(--tm-thumb-follow-thumb-width\)\s*!important[\s\S]*height:\s*var\(--tm-thumb-follow-thumb-height\)\s*!important/,
|
||||
"follow dropdown images should use detected original thumb width and height",
|
||||
/\.FollowedDropdown__room-image[\s\S]*width:\s*100%\s*!important[\s\S]*height:\s*var\(--tm-thumb-follow-thumb-height\)\s*!important/,
|
||||
"follow dropdown images should follow fixed card width and use detected height",
|
||||
);
|
||||
assert.doesNotMatch(capturedCss, /translateX\(/, "follow dropdown should not use a fixed horizontal offset");
|
||||
assert.match(source, /adjustFollowDropdownPosition/, "script should adjust follow dropdown position only when it overflows");
|
||||
assert.match(source, /viewportPadding - rect\.left/, "follow dropdown adjustment should be based on left viewport overflow");
|
||||
assert.match(
|
||||
capturedCss,
|
||||
/react-tooltip[\s\S]*width:\s*var\(--tm-thumb-tooltip-width\)\s*!important[\s\S]*height:\s*var\(--tm-thumb-tooltip-height\)\s*!important/,
|
||||
@@ -169,11 +212,21 @@ assert.match(
|
||||
assert.doesNotMatch(source, /withOwnStyleDisabled/, "script should not disable its own style while measuring");
|
||||
assert.match(source, /detectAndApplySizes/, "script should detect original sizes before applying scaled variables");
|
||||
assert.match(source, /moduleReady/, "script should lock each module after detecting its original size");
|
||||
assert.match(source, /CACHE_KEY/, "script should cache detected original sizes");
|
||||
assert.match(source, /CARD_HEIGHT_SCALE/, "script should use a separate scale for card heights");
|
||||
assert.match(source, /setCardHeightVar/, "script should apply card height scaling separately from thumbnail scaling");
|
||||
assert.match(source, /localStorage\.setItem\(CACHE_KEY/, "script should save detected sizes in localStorage");
|
||||
assert.match(source, /localStorage\.removeItem\(CACHE_KEY/, "script should clear cached sizes from the menu command");
|
||||
assert.match(source, /DEFAULT_RESPONSIVE_SCALE/, "script should expose a default responsive scale");
|
||||
assert.match(source, /DEFAULT_FIXED_SCALE/, "script should expose a default fixed scale");
|
||||
assert.match(source, /tmThumbScaleSetScales/, "script should expose a debug API for changing both scales");
|
||||
assert.match(capturedCss, /#tm-thumb-scale-controls[\s\S]*position:\s*fixed\s*!important/, "script should render a fixed page control panel");
|
||||
assert.match(capturedCss, /data-tm-thumb-scale-auto-hide-room-chat="1"[\s\S]*data-tm-thumb-room-chat-overlay="1"[\s\S]*display:\s*none\s*!important/, "script should hide marked room chat overlays when enabled");
|
||||
assert.match(source, /hideRoomChat/, "script should persist the room chat auto-hide setting");
|
||||
assert.match(source, /applyRoomChatAutoHide/, "script should apply automatic room chat hiding");
|
||||
assert.match(source, /setDiscoverStackHeightVars/, "discover carousel stack heights should be derived from scaled card rows");
|
||||
assert.match(source, /setCardHeightFromThumbVar\("--tm-thumb-discover-card-height"/, "discover card height should scale the thumbnail area and keep metadata height");
|
||||
assert.match(source, /stackHeight == null\) return true/, "discover should not require every carousel row type to exist before enabling");
|
||||
assert.match(source, /TAG_TRANSLATIONS/, "script should include a centralized tag translation table");
|
||||
assert.match(source, /translateTagsPage/, "script should translate the Chaturbate tags page");
|
||||
assert.doesNotMatch(source, /localStorage\.setItem\([^,]*LEGACY_CACHE_PREFIX/, "script should not write detected sizes to localStorage");
|
||||
assert.match(source, /SETTINGS_KEY/, "script may persist user-adjustable scale settings");
|
||||
assert.match(source, /LEGACY_CACHE_PREFIX/, "script should only keep legacy cache cleanup support");
|
||||
assert.match(source, /scheduleMeasure\("after-800ms"\)/, "script should delay the first page measurement until original layout can render");
|
||||
assert.doesNotMatch(
|
||||
capturedCss,
|
||||
@@ -226,23 +279,87 @@ assert.equal(earlyAppendedTarget, null, "GM_addStyle should avoid manual documen
|
||||
assert.equal(typeof earlyDomContentLoadedHandler, "function", "script should reinject after head becomes available");
|
||||
assert.equal(earlyObservedTarget, null, "script should not observe a missing head");
|
||||
|
||||
const styleVars = new Map();
|
||||
const attrs = new Map();
|
||||
const cacheFakeDocument = {
|
||||
const tagAnchors = [
|
||||
{
|
||||
href: "https://zh-hans.chaturbate.com/tag/asian/",
|
||||
textContent: "#asian",
|
||||
dataset: {},
|
||||
title: "",
|
||||
getAttribute(name) {
|
||||
return name === "href" ? this.href : "";
|
||||
},
|
||||
},
|
||||
{
|
||||
href: "https://zh-hans.chaturbate.com/tag/bigboobs/",
|
||||
textContent: "#bigboobs",
|
||||
dataset: {},
|
||||
title: "",
|
||||
getAttribute(name) {
|
||||
return name === "href" ? this.href : "";
|
||||
},
|
||||
},
|
||||
{
|
||||
href: "https://zh-hans.chaturbate.com/tag/ebony/female/",
|
||||
textContent: "#ebony",
|
||||
dataset: {},
|
||||
title: "",
|
||||
getAttribute(name) {
|
||||
return name === "href" ? this.href : "";
|
||||
},
|
||||
},
|
||||
{
|
||||
href: "https://zh-hans.chaturbate.com/tag/not-in-table/",
|
||||
textContent: "Not In Table",
|
||||
dataset: {},
|
||||
title: "",
|
||||
getAttribute(name) {
|
||||
return name === "href" ? this.href : "";
|
||||
},
|
||||
},
|
||||
{
|
||||
href: "https://zh-hans.chaturbate.com/tag/new-custom-tag/",
|
||||
textContent: "#new-custom-tag",
|
||||
dataset: {},
|
||||
title: "",
|
||||
getAttribute(name) {
|
||||
return name === "href" ? this.href : "";
|
||||
},
|
||||
},
|
||||
{
|
||||
href: "https://zh-hans.chaturbate.com/tag/colombiana/female/",
|
||||
textContent: "#colombiana",
|
||||
dataset: {},
|
||||
title: "",
|
||||
getAttribute(name) {
|
||||
return name === "href" ? this.href : "";
|
||||
},
|
||||
},
|
||||
{
|
||||
href: "https://zh-hans.chaturbate.com/tag/untranslated-example/",
|
||||
textContent: "#untranslated-example",
|
||||
dataset: {},
|
||||
title: "",
|
||||
getAttribute(name) {
|
||||
return name === "href" ? this.href : "";
|
||||
},
|
||||
},
|
||||
{
|
||||
href: "https://zh-hans.chaturbate.com/tags/female/?page=2",
|
||||
textContent: "2",
|
||||
dataset: {},
|
||||
title: "",
|
||||
getAttribute(name) {
|
||||
return name === "href" ? this.href : "";
|
||||
},
|
||||
},
|
||||
];
|
||||
const tagFakeDocument = {
|
||||
documentElement: {
|
||||
style: {
|
||||
setProperty(name, value) {
|
||||
styleVars.set(name, value);
|
||||
setAttribute() {},
|
||||
},
|
||||
head: {
|
||||
appendChild() {},
|
||||
},
|
||||
setAttribute(name, value) {
|
||||
attrs.set(name, value);
|
||||
},
|
||||
removeAttribute(name) {
|
||||
attrs.delete(name);
|
||||
},
|
||||
},
|
||||
head: {},
|
||||
body: {},
|
||||
createElement() {
|
||||
return { id: "", textContent: "", setAttribute() {} };
|
||||
@@ -250,52 +367,20 @@ const cacheFakeDocument = {
|
||||
getElementById() {
|
||||
return null;
|
||||
},
|
||||
querySelectorAll() {
|
||||
return [];
|
||||
querySelectorAll(selector) {
|
||||
return selector.includes("/tag") ? tagAnchors : [];
|
||||
},
|
||||
addEventListener() {},
|
||||
};
|
||||
const cacheStore = new Map([
|
||||
["tm-thumb-scale:size-cache:v9", JSON.stringify({
|
||||
schema: 9,
|
||||
scale: 2,
|
||||
cardHeightScale: 1.55,
|
||||
modules: {
|
||||
discover: {
|
||||
discoverCard: { width: 182, height: 176 },
|
||||
discoverThumb: { width: 180, height: 101 },
|
||||
discoverTripleUl: { width: 1904, height: 550 },
|
||||
discoverTripleArrow: { width: 35, height: 534 },
|
||||
discoverDoubleUl: { width: 1904, height: 366 },
|
||||
discoverDoubleArrow: { width: 35, height: 350 },
|
||||
discoverSingleUl: { width: 2127, height: 182 },
|
||||
discoverSingleArrow: { width: 35, height: 166 },
|
||||
},
|
||||
},
|
||||
})],
|
||||
]);
|
||||
|
||||
vm.runInNewContext(source, {
|
||||
const runTagsPageTranslationTest = (pathname = "/tags/") => vm.runInNewContext(source, {
|
||||
console,
|
||||
document: cacheFakeDocument,
|
||||
localStorage: {
|
||||
getItem(key) {
|
||||
return cacheStore.get(key) || null;
|
||||
},
|
||||
setItem(key, value) {
|
||||
cacheStore.set(key, value);
|
||||
},
|
||||
removeItem(key) {
|
||||
cacheStore.delete(key);
|
||||
},
|
||||
},
|
||||
getComputedStyle() {
|
||||
return {
|
||||
getPropertyValue(name) {
|
||||
return styleVars.get(name) || "";
|
||||
},
|
||||
};
|
||||
document: tagFakeDocument,
|
||||
location: {
|
||||
href: `https://zh-hans.chaturbate.com${pathname}`,
|
||||
pathname,
|
||||
},
|
||||
URL,
|
||||
GM_addStyle() {
|
||||
return { id: "", textContent: "", setAttribute() {} };
|
||||
},
|
||||
@@ -305,13 +390,32 @@ vm.runInNewContext(source, {
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(attrs.get("data-tm-thumb-scale-discover"), "1", "discover should be enabled directly from cached original sizes");
|
||||
assert.equal(styleVars.get("--tm-thumb-discover-width"), "364px");
|
||||
assert.equal(styleVars.get("--tm-thumb-discover-card-height"), "273px");
|
||||
assert.equal(styleVars.get("--tm-thumb-discover-thumb-width"), "360px");
|
||||
assert.equal(styleVars.get("--tm-thumb-discover-height"), "202px");
|
||||
assert.equal(styleVars.get("--tm-thumb-discover-single-ul"), "282px");
|
||||
assert.equal(styleVars.get("--tm-thumb-discover-triple-ul"), "853px");
|
||||
runTagsPageTranslationTest();
|
||||
|
||||
assert.equal(tagAnchors[0].textContent, "亚洲", "tags page should translate known tag link text");
|
||||
assert.equal(tagAnchors[0].dataset.tmTagOriginal, "#asian", "translated tags should keep the original label in dataset");
|
||||
assert.equal(tagAnchors[0].title, "#asian", "translated tags should expose the original label in title");
|
||||
assert.equal(tagAnchors[1].textContent, "大胸", "tags page should translate compact multi-word tag slugs");
|
||||
assert.equal(tagAnchors[2].textContent, "黑人", "gender-scoped tag links should translate by the first slug segment");
|
||||
assert.equal(tagAnchors[3].textContent, "Not In Table", "unknown tags should remain unchanged");
|
||||
assert.equal(tagAnchors[4].textContent, "新自定义标签", "untranslated hashtag-style tags should use slug word translation");
|
||||
assert.equal(tagAnchors[5].textContent, "哥伦比亚女性", "known gendered nationality tags should be translated");
|
||||
assert.equal(tagAnchors[6].textContent, "#untranslated-example", "untranslated hashtag-style tags should keep the original slug");
|
||||
assert.equal(tagAnchors[7].textContent, "2", "tags pagination links should not be translated");
|
||||
|
||||
tagAnchors.forEach((anchor, index) => {
|
||||
anchor.textContent = ["#asian", "#bigboobs", "#ebony", "Not In Table", "#new-custom-tag", "#colombiana", "#untranslated-example", "2"][index];
|
||||
anchor.dataset = {};
|
||||
anchor.title = "";
|
||||
});
|
||||
|
||||
runTagsPageTranslationTest("/tags/female/");
|
||||
assert.equal(tagAnchors[0].textContent, "亚洲", "tag category pages under /tags/ should also translate links");
|
||||
assert.equal(tagAnchors[1].textContent, "大胸", "tag category pages should translate compact slugs too");
|
||||
assert.equal(tagAnchors[2].textContent, "黑人", "tag category pages should translate /tag/slug/gender/ links");
|
||||
assert.equal(tagAnchors[5].textContent, "哥伦比亚女性", "tag category pages should translate gendered nationality links");
|
||||
assert.equal(tagAnchors[6].textContent, "#untranslated-example", "tag category pages should preserve unknown hashtag-style tags");
|
||||
assert.equal(tagAnchors[7].textContent, "2", "tag category page pagination should keep page numbers");
|
||||
|
||||
const followingVars = new Map();
|
||||
const followingAttrs = new Map();
|
||||
@@ -350,15 +454,19 @@ vm.runInNewContext(source, {
|
||||
localStorage: {
|
||||
getItem() {
|
||||
return JSON.stringify({
|
||||
schema: 9,
|
||||
schema: 15,
|
||||
scale: 2,
|
||||
cardHeightScale: 1.55,
|
||||
modules: {
|
||||
home: {
|
||||
homeMinWidth: 190,
|
||||
homeCard: { width: 174, height: 120 },
|
||||
homeThumb: { width: 174, height: 98 },
|
||||
},
|
||||
followingList: {
|
||||
followingListMinWidth: 190,
|
||||
followingListCard: { width: 190, height: 120 },
|
||||
followingListThumb: { width: 188, height: 106 },
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -382,10 +490,10 @@ vm.runInNewContext(source, {
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(followingAttrs.get("data-tm-thumb-scale-following-list"), "1", "followed-cams should use its own cached list module");
|
||||
assert.equal(followingAttrs.get("data-tm-thumb-scale-home"), undefined, "followed-cams should not apply homepage cache");
|
||||
assert.equal(followingVars.get("--tm-thumb-following-list-width"), "380px");
|
||||
assert.equal(followingAttrs.get("data-tm-thumb-scale-following-list"), undefined, "followed-cams should not apply legacy cached list module");
|
||||
assert.equal(followingAttrs.get("data-tm-thumb-scale-home"), undefined, "followed-cams should not apply legacy homepage cache");
|
||||
assert.equal(followingVars.get("--tm-thumb-following-list-min-width"), undefined);
|
||||
assert.equal(followingVars.get("--tm-thumb-following-list-card-height"), undefined);
|
||||
assert.equal(followingVars.get("--tm-thumb-following-list-thumb-width"), undefined);
|
||||
assert.equal(followingVars.get("--tm-thumb-following-list-thumb-height"), undefined);
|
||||
assert.equal(followingVars.get("--tm-thumb-home-width"), undefined);
|
||||
assert.equal(followingVars.get("--tm-thumb-home-min-width"), undefined);
|
||||
|
||||
Reference in New Issue
Block a user