JavBus论坛移动端界面适配

使司机社(JavBus)的论坛适应移动端界面

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください。
// ==UserScript==
// @name         JavBus论坛移动端界面适配
// @namespace    https://sleazyfork.org/zh-CN/users/1140711-steven-fake
// @homepageURL  https://github.com/Steven-Fake/JavBus-forum-mobile-layout
// @version      1.3.1
// @license      MIT
// @description  使司机社(JavBus)的论坛适应移动端界面
// @author       Steven-Fake
// @run-at       document-idle
// @icon         https://www.javbus.com/favicon.ico
// @match        https://www.javbus.com/forum*
// @match        https://www.buscdn.cfd/forum*
// @match        https://www.buscdn.lol/forum*
// @match        https://www.busfan.cfd/forum*
// @match        https://www.busfan.lol/forum*
// @match        https://www.cdnbus.cfd/forum*
// @match        https://www.cdnbus.lol/forum*
// @match        https://www.dmmbus.cfd/forum*
// @match        https://www.dmmbus.lol/forum*
// @match        https://www.dmmsee.cfd/forum*
// @match        https://www.dmmsee.lol/forum*
// @match        https://www.javsee.cfd/forum*
// @match        https://www.javsee.lol/forum*
// @match        https://www.seejav.cfd/forum*
// @match        https://www.seejav.lol/forum*
// @grant        none
// ==/UserScript==

PAGE = {//页面枚举类型
    Index: 0, // 首页
    SubForum: 1, // 子版块页面
    Post: 2, // 帖子页面
    Search: 3, // 搜索页面
    Favorite: 4, // 收藏页面
    Profile: 5, // 个人信息页面
    Login: 6, // 登录页面
    Unknown: -1 // 未知,或未适配的页面
}
const VERSION = "1.3.1"
const SLEAZYFORK = "https://sleazyfork.org/zh-CN/scripts/472169-javbus论坛移动端界面适配"; // 脚本主页

/**
 * 是否启用移动端布局
 * @returns {boolean}
 */
const ifEnable = () => {
    const status = window.innerHeight > window.innerWidth && window.innerWidth <= 1600;
    if (!status) {
        const info = `JavBus论坛移动端界面适配 v${VERSION}: 未启用移动端布局,原因: `;
        if (window.innerWidth > 1600) {
            if (window.innerWidth >= window.innerHeight) console.log(info + "当前屏幕宽度大于1600px, 且不为竖屏。");
            else console.log(info + "当前屏幕宽度大于1600px。");
        } else {
            if (window.innerWidth >= window.innerHeight) console.log(info + "当前屏幕不为竖屏。");
            else console.log(info + "未知原因。");
        }
        console.log("width:", window.innerWidth, "height:", window.innerHeight);
    } else {
        console.log(`JavBus论坛移动端界面适配 v${VERSION}: 启用移动端布局`);
    }
    return status;
}

/**
 * 获取当前页面的类型
 * @returns {PAGE}
 */
function getPageType() {
    const url = window.location.href;
    const indexRegex = /^.+\/forum(\.php)?([\/?])?$/; // 相当于只匹配['/forum', '/forum/', '/forum?'],不允许子路径和任何get参数
    if (url.match(indexRegex)) return PAGE.Index;
    else if (url.includes("gid=")) return PAGE.Index;
    else if (url.includes("tid=")) return PAGE.Post;
    else if (url.includes("fid=")) return PAGE.SubForum;
    else if (url.includes("uid=")) return PAGE.Profile;
    else if (url.includes("action=login")) return PAGE.Login;
    else if (url.includes("search.php")) return PAGE.Search;
    else if (url.includes("do=favorite")) return PAGE.Favorite;
    else return PAGE.Unknown;
}


function getFeedbackInfo(e) {
    if (e) {
        return `
        Version: ${VERSION}
        URL    : ${window.location.href}
        UA     : ${navigator.userAgent}
        Error  : 
            ${e.name} - ${e.message}
        `
    } else {
        return `
        Version: ${VERSION}
        URL    : ${window.location.href}
        UA     : ${navigator.userAgent}
        `
    }
}

/**
 * 对于出错的页面是否进行反馈
 */
function copyFeedbackToClipboard(e) {
    try {
        window.focus();
        navigator.clipboard.writeText(getFeedbackInfo(e));
        return true;
    } catch (error) {
        return false;
    }
}

/**
 * 页面布局控制器
 */
class Layout {
    constructor() {
        // 注入自适应调整的meta标签
        const meta = document.createElement("meta");
        meta.name = "viewport";
        meta.content = "width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no";
        document.head.appendChild(meta);

        this.pageType = getPageType();

        if (this.pageType === PAGE.Unknown) { // 未适配的页面
            if ("Notification" in window) { // 当前环境存在Notification API
                Notification.requestPermission().then(permission => {
                    if (permission === "granted") {
                        const notification = new Notification("JavBus论坛移动端界面适配 v" + VERSION, {
                            body: copyFeedbackToClipboard() ? "尚未适配当前页面,已复制相关信息到剪贴板,点击可进行反馈。" : "尚未适配当前页面,点击可进行反馈。",
                            icon: "https://www.javbus.com/favicon.ico"
                        });
                        notification.onclick = () => {
                            window.open(`${SLEAZYFORK}/feedback`)
                        };
                    } // 用户拒绝了通知权限请求
                });
            } else {
                console.log("JavBus论坛移动端界面适配 v" + VERSION + ": 未适配当前页面")
            }
            return;
        } else if (this.pageType !== PAGE.Search) { // 执行通用规则,但搜索页面不适用
            this.mainWrapper = document.querySelector("#wp");
            this.mainContainer = document.querySelector(".mn");
            this.basic(); // 应用页面的全局调整
            this.topBar(); // 调整顶栏
        }
        // 各页面的规则
        if (this.pageType === PAGE.Index) {
            this.index(); // 调整首页
        } else if (this.pageType === PAGE.SubForum) {
            this.subForum(); // 调整子版块页面
        } else if (this.pageType === PAGE.Search) {
            this.search(); // 调整搜索页面
        } else if (this.pageType === PAGE.Profile) {
            this.profile(); // 调整个人信息页面
        } else if (this.pageType === PAGE.Post) {
            this.post(); // 调整帖子页面
        } else if (this.pageType === PAGE.Login) {
            this.login(); // 调整登录页面
        } else if (this.pageType === PAGE.Favorite) {
            this.favorite(); // 调整收藏页面
        }
    }

    /**
     * 页面的基本调整
     */
    basic() {
        // 1.移除顶部广告
        document.querySelectorAll(".bcpic2").forEach(ad => ad.remove());

        // 2.删除本就隐藏的元素
        document.querySelector("div.wp.cl")?.remove();

        // 3.调整层级导航栏的样式
        document.querySelectorAll("div.z").forEach(e => e.style.cssText = "width: 100%; padding: 0;");

        // 4.调整回到页首按钮的位置
        const backToTop = document.getElementsByClassName("biaoqi-fix-area");
        if (backToTop.length > 0) {
            backToTop[0].style.cssText = "left: 0; margin-left: 80%;"
            backToTop[0].firstElementChild.style.bottom = "10%";
        }

        // 5.调整页脚样式
        const footer = document.getElementsByClassName("jav-footer");
        if (footer.length > 0) {
            footer[0].style.cssText = "width: 100%; min-width: 0; padding: 0;";
            footer[0].firstElementChild.style.padding = "10px";
        }

        // 6.调整页面中wrapper的布局
        if (this.mainWrapper)
            this.mainWrapper.style.cssText = "width: 100%; min-width: 0; padding: 0;";
        if (this.mainContainer)
            this.mainContainer.style.cssText = "width: 100%; min-width: 0; padding: 0;";

        // 调整侧边栏的背景色(若有)
        const sider_ct = document.querySelector("div#ct")
        if (sider_ct) sider_ct.style.background = "none";
        //调整侧边栏的宽度(若有)
        const sider_appleft = document.querySelector("div.appl");
        if (sider_appleft) sider_appleft.style.cssText = "width: 100%; min-width: 0; padding: 0;";
    }

    /**
     * 调整顶栏
     */
    topBar() {
        const nav = document.getElementById("toptb");
        if (!nav) return;
        // 1. 删除logo
        nav.querySelector('a.jav-logo')?.remove();
        // 2. 调整导航栏样式
        nav.style.cssText = "min-width: unset; width: 100%; padding: 0;";
        // 3. 移除原有搜索框和导航栏
        nav.querySelector('div.wp')?.remove(); // 移除搜索框和导航栏
        // 4. 调整个人信息部分
        const member = nav.querySelector('div.login-wrap.y');
        // 4.1 移除ID以节省空间
        member.querySelector('span.member-name')?.remove();
        // 4.2 个人头像改为左对齐
        member.style.cssText = "float: left;";
        // 4.3 将下拉菜单搬出来
        const menu = document.createElement('ul');
        menu.style.cssText = "display: flex; justify-content: left; align-items: center;"
        member.querySelectorAll('div.menu-body div.item a').forEach(e => {
            const li = document.createElement('li');
            li.style.cssText = "float: none; display: inline-block; margin: 0 5px;";
            e.style.cssText = "font-size: 14px; padding: 10px 0; text-align: center;";
            li.appendChild(e);
            menu.appendChild(li);
        });
        nav.appendChild(menu);
        // 4.4 删掉menu的第一个元素(快捷导航)
        menu.firstElementChild?.remove();
        // 4.5 删掉原本的下拉按钮和下拉菜单
        member.querySelector('span.angle')?.remove();
        member.querySelector('div.menu-body')?.remove();
    }


    /**
     * 首页(javbus.com/forum/ 或 javbus.com/forum/forum.php?gid=1)
     */
    index() {
        if (!this.mainContainer) return;

        // 第1部分,包括轮播图片和帖子推荐列表
        const part1 = this.mainContainer.firstElementChild;
        part1.style.cssText = "width: 100%; min-width: 0; padding: 0; height: auto;"; // 调整样式

        const carouselContainer = part1.children[0], postListContainer = part1.children[1];
        carouselContainer.style.cssText = "float: none; width: 100%; height: auto;"; // 调整轮播图片框的样式
        postListContainer.style.cssText = "margin: 0; margin-top: 10px; width: 100%; height: auto;"; // 调整帖子列表框的样式
        // 轮播图片框
        carouselContainer.querySelectorAll("img").forEach(e => e.style.width = "100%");
        // 帖子推荐列表
        const postLists = postListContainer.querySelectorAll("div.sideMenu");
        for (let sideMenu of postLists) {
            for (let item of sideMenu.children) {
                item.style.width = "100%";
                item.firstElementChild?.remove(); // 删除发帖用户的名称
            }
        }

        // 第2部分,“综合交流区”和“福利讨论分类”
        this.mainContainer.querySelectorAll("table.fl_tb").forEach(e => {
            const tbody = e.firstElementChild;
            for (let tr of tbody.children) {
                for (let col = 0; col < tr.childElementCount; col++) {
                    if (col !== 0 && col !== 1) { // 第0列是图标,第1列是标题
                        tr.children[1].innerHTML += tr.children[col].outerHTML;
                        tr.children[col].style.display = "none";
                    }
                }
            }
        });

        // 3.“热门主题”和“精选内容”
        const sidebar = document.getElementById("sd");
        sidebar.style.cssText = "width: 100%; min-width: 0;";
        const hotContainer = sidebar.children[0], choiceContainer = sidebar.children[1];
        hotContainer.style.cssText = "width: 100%; min-width: 0;";
        choiceContainer.style.cssText = "width: 100%; min-width: 0;";
        const hotList = hotContainer.querySelector("div.main-right-zuixin");
        for (let item of hotList.children) {
            item.querySelector("table").style.width = "100%"; // 调整每个帖子的宽度为100%
            const title = item.querySelector("p.comment-post"); // 显示完整标题
            title.firstElementChild.textContent = title.firstElementChild.title;
        }
        const choiceList = choiceContainer.querySelector("div.main-right-tw");
        for (let item of choiceList.querySelectorAll("dl")) {
            item.querySelectorAll("dd").forEach(e => e.style.width = "100%"); // 调整帖子宽度为100%
            item.querySelectorAll("img").forEach(e => {
                e.style.width = "100%";
                e.style.height = "auto";
            }) // 调整图片宽度为100%
            const title = item.querySelector("dt"); //显示完整标题
            title.firstElementChild.textContent = title.firstElementChild.title;
        }
    }

    /**
     * 子版块页面(javbus.com/forum.php?mod=forumdisplay&fid=)
     */
    subForum() {
        // 1. 调整子版块标题的样式
        const titleBlock = document.querySelector("div.forumbky");
        if (titleBlock) titleBlock.style.cssText = "width: 100%; padding: 0;";

        const main = document.querySelector("div#threadlist");
        if (!main) return;

        // 2. 调整顶栏的筛选框和排序框
        main.querySelector("div.th table").style.cssText = "display: flex; justify-content: space-between; width: 100%;";

        // 3. 调整各条目的宽度
        const itemTable = main.querySelector("table#threadlisttableid");
        if (itemTable) {
            itemTable.style.cssText = "width: 100%"
            itemTable.querySelectorAll("tbody").forEach(
                tbody => {
                    tbody.style.cssText = "width: 100%; min-width: 0; padding: 0;"
                    tbody.querySelectorAll("th").forEach(e => e.style.cssText = "width: 100%;")
                    tbody.querySelectorAll("div.post_inforight").forEach(e => e.style.cssText = "display:flex; flex-direction: column; width: 100%")
                    tbody.querySelectorAll("div.post_infolist").forEach(e => e.style.cssText = "width: 100%;")
                    tbody.querySelectorAll("div.post_infolist_other").forEach(e => e.style.cssText = "display:flex;flex-direction: column;")
                }
            )
        }

        // 4.“精选内容”与“精选主题”
        const sidebar = this.mainWrapper.querySelector("div.sd.sd_allbox");
        sidebar.style.cssText = "width: 100%; min-width: 0;";
        sidebar.querySelectorAll("div.main-right-box.cl").forEach(e => e.style.cssText = "width: 100%; min-width: 0; padding: 0;"); // 调整每个帖子的样式
        // “精选内容”
        const choiceContent = sidebar.querySelector("ul.main-right-kuaixu.cl");
        if (choiceContent) {
            choiceContent.querySelectorAll("li").forEach(e => {
                // 调整“精选内容”每个li的宽度
                e.style.width = "100%";
                // 调整图片和标题的宽度
                e.querySelectorAll("div").forEach(e => e.style.width = "50%");
            });
        }
        // “精选主题”
        const choiceTheme = sidebar.querySelector("div.main-right-zuixin");
        if (choiceTheme) {
            choiceTheme.querySelectorAll("li").forEach(e => {
                // 调整“精选内容”每个li的宽度
                e.style.width = "100%";
                // 显示完整标题
                const title = e.querySelector("p.comment-post a");
                title.textContent = title.title;
                // 调整表格宽度
                e.querySelector("table").style.width = "100%";
            });
        }
    }

    /**
     * 帖子页面(javbus.com/forum.php?mod=viewthread&tid=)
     */
    post() {
        // 1.调整帖子和回复中的图片尺寸
        let post_td = document.getElementsByClassName("t_f");
        for (let i = 0; i < post_td.length; i++) {
            let imgs = post_td[i].getElementsByTagName("img");
            for (let j = 0; j < imgs.length; j++) {
                if (!imgs[j].src.includes("/static/image/smiley/")) {//不调整表情图片
                    imgs[j].style.cssText = "width: 100%; height: auto;";
                }
            }
        }

        // 2.“精选内容”与“精选主题”
        const sidebar = this.mainWrapper.querySelector("div.sd.sd_allbox");
        sidebar.style.cssText = "width: 100%; min-width: 0;";
        sidebar.querySelectorAll("div.main-right-box.cl").forEach(e => e.style.cssText = "width: 100%; min-width: 0; padding: 0;"); // 调整每个帖子的样式
        // “精选内容”
        const choiceContent = sidebar.querySelector("ul.main-right-kuaixu.cl");
        if (choiceContent) {
            choiceContent.querySelectorAll("li").forEach(e => {
                // 调整“精选内容”每个li的宽度
                e.style.width = "100%";
                // 调整图片和标题的宽度
                e.querySelectorAll("div").forEach(e => e.style.width = "50%");
            });
        }
        // “精选主题”
        const choiceTheme = sidebar.querySelector("div.main-right-zuixin");
        if (choiceTheme) {
            choiceTheme.querySelectorAll("li").forEach(e => {
                // 调整“精选内容”每个li的宽度
                e.style.width = "100%";
                // 显示完整标题
                const title = e.querySelector("p.comment-post a");
                title.textContent = title.title;
                // 调整表格宽度
                e.querySelector("table").style.width = "100%";
            });
        }
    }

    /**
     * 搜索页面(javbus.com/forum/search.php)
     */
    search() {
        const searchForm = document.getElementById("scform");
        if (searchForm) {
            searchForm.style.cssText += "width: 100%; min-width: 0;";//调整搜索框的宽度
            searchForm.querySelector("input#scform_srchtxt").style.width = "100%";
            searchForm.querySelector("button#scform_submit").style.cssText = "width: 30%; min-width: 100px;";
        }
        document.getElementById("toptb").style.cssText = "width: 100%; min-width: 0;padding: 0; border: 0;";//调整顶栏的宽度
        document.getElementById("ft").remove();//删除页脚
        //调整搜索结果的布局
        try {
            document.getElementById("threadlist").style.width = "100%";
        } catch (e) {
        }
    }

    /**
     * 个人信息页面(javbus.com/forum/home.php?mod=space&uid=)
     */
    profile() {
        document.querySelectorAll('.z').forEach(e => e.style.cssText = "width: 100%; padding: 0;"); // 调整层级导航栏的样式

        // 调整上部容器的样式
        const upperContainer = document.getElementById("uhd");
        upperContainer.style.cssText = "width: 100%; padding: 0";
        // 调整头像的样式
        const avatar = upperContainer.querySelector("div.icn.avt");
        if (avatar) avatar.style.padding = "5px";

        // 调整底部容器的样式
        const bottomContainer = document.querySelector('div.bm_c');
        if (bottomContainer) bottomContainer.style.padding = 0;

        // 调整个人资料页面的排版
        this.mainContainer.querySelectorAll("h2.mbn").forEach(e => e.style.marginLeft = "5px");
        const profileContainer = this.mainContainer.querySelector("div.bm_c.u_profile");
        if (profileContainer)
            profileContainer.querySelectorAll("li").forEach(e => e.style.cssText = "text-align: left; margin-left: 10px; width: 100%;");

        // 调整帖子表格的样式
        if (document.getElementsByClassName("th").length > 0) {
            const tbody = document.querySelector('tbody');
            tbody.querySelectorAll('td.icn').forEach(e => e.remove()); // 删除图标列
            tbody.querySelectorAll('td.xg1 img').forEach(e => e.remove()); // 删除回复时的图标列
            tbody.style.cssText = "th {padding: 5px; border: 1px;} td {padding: 5px; border: 1px;}"; // 调整表格的样式
            const thead = tbody.querySelector('tr.th');
            thead.children[0].style.width = "40%"; // 帖子标题列宽50%
            thead.children[1].style.width = "25%"; // 帖子板块列宽20%
            thead.children[2].style.width = "10%"; // 帖子回复/查看列宽10%
            thead.children[3].style.display = "25%"; // 帖子最后回复列宽20%
        }

        // “添加好友”按钮
        const add_friend = document.querySelector("div.mn ul li.addf");
        if (add_friend) add_friend.style.textAlign = "right";
        // “发送消息”按钮
        const send_message = document.querySelector("div.mn ul li.pm2")
        if (send_message) send_message.style.textAlign = "right";
    }

    /**
     * 登录页面(javbus.com/forum/member.php?mod=logging&action=login)
     */
    login() {
        document.getElementById("ct").style.width = "100%"; // 调整整个窗口的宽度
        document.querySelectorAll("div.biaoqi_dl").forEach(dialog => {
            dialog.style.cssText = "margin: 0; width: 100%; div.rfm {width: 100%; padding: 0 5%; }"; // 调整浮窗的边距、宽度和子元素的宽度
        });//调整浮窗的边距和宽度

        // 移除因界面调整而会触发界面bug的“找回密码”
        const temp = document.querySelectorAll("td.tipcol");
        temp[temp.length - 1].remove();
        document.querySelector(".f_c.altw").style.padding = "0"; //跳转显示框

    }

    /**
     * 收藏页面(javbus.com/forum/home.php?mod=space&do=favorite&view=me)
     */
    favorite() {
        this.mainContainer.style.margin = "0"; // 调整边距

        // 调整收藏列表的样式
        const favoriteList = document.querySelector("ul#favorite_ul");
        if (favoriteList) {
            favoriteList.querySelectorAll("li").forEach(e => e.style.padding = "10px");
        }

        // 调整全选按钮的样式
        this.mainContainer.querySelector("p.mtm.pns").style.padding = "10px";
    }
}


(function () {
    'use strict';
    if (!ifEnable()) return;

    try {
        new Layout(); // 开始调整布局
    } catch (e) {
        console.error("JavBus论坛移动端界面适配 v" + VERSION + ": 页面适配时出现错误", e);
        // feedback(e);
    }
})();