// ==UserScript==
// @name 论坛文章页宽屏
// @version 1.1.0
// @description 适配了微信公众号、知乎、掘金、简书,贴吧,百度搜索
// @author sakura-flutter
// @namespace https://github.com/sakura-flutter
// @compatible chrome >= 80
// @compatible firefox >= 75
// @run-at document-start
// @match https://mp.weixin.qq.com/s*
// @match https://zhuanlan.zhihu.com/p/*
// @match https://www.zhihu.com/question/*
// @match https://juejin.im/post/*
// @match https://www.jianshu.com/p/*
// @match https://www.baidu.com/s?*
// @match https://www.baidu.com/
// @match https://tieba.baidu.com/p/*
// @grant unsafeWindow
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @require https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js
// @require https://greasyfork.org/scripts/411093-toast/code/Toast.js?version=847261
// ==/UserScript==
/* global Vue Toast */
(function() {
'use strict'
const isDebug = false
const $ = document.querySelector.bind(document)
const $$ = document.querySelectorAll.bind(document)
function log(...args) {
if (!isDebug) return
console.log(...args)
}
// 主函数
function main() {
const sites = checkWebsites()
sites.forEach(site => {
const hanlder = handlers.get(site)
log(site)
hanlder && hanlder()
})
}
// 检查网站
function checkWebsites() {
const { origin, pathname } = location
const url = origin + pathname
// 格式[ ['xx', true|false], ]
const sites = [
['mpWeixin', /mp.weixin.qq.com\/s/.test(url)],
['zhihu', /zhuanlan.zhihu.com\/p\//.test(url) || /zhihu.com\/question\//.test(url)],
['juejin', /juejin.im\/post\//.test(url)],
['jianshu', /jianshu.com\/p\//.test(url)],
['baidu', /www.baidu.com\/s?/.test(url)],
['tieba', /tieba.baidu.com\/p\//.test(url)],
]
// 返回匹配的页面
return sites
.filter(item => item[1])
.map(item => item[0])
}
// 对应网页要执行的操作操作
const handlers = new Map()
/* ===微信文章===start */
handlers.set('mpWeixin', function() {
const store = createStore('mpWeixin')
function execute() {
GM_addStyle(`
/* 文章宽屏 */
.rich_media_area_primary_inner { max-width: 100vw !important; }
/* 二维码位置 */
#js_pc_qr_code .qr_code_pc { position: fixed; top: 25vh; right: 3vw; opacity: .4;}
#js_pc_qr_code .qr_code_pc:hover { opacity: 1;}
@media screen and (min-width: 1024px) {
.rich_media_area_primary_inner { max-width: 75vw !important; }
#js_pc_qr_code .qr_code_pc { position: fixed; top: 25vh; right: 3vw; }
}
`)
window.addEventListener('DOMContentLoaded', () => {
// 文章图片宽高(仅对大图处理)
const imgEls = $$('.rich_media_area_primary_inner img')
imgEls.forEach(img => {
img.addEventListener('load', () => {
// 页面本身对图片有宽高处理,延时后再处理
setTimeout(() => {
const width = parseFloat(getComputedStyle(img).width)
if (width >= 400) {
img.style.cssText += 'width: auto !important; height: auto !important;'
}
},16)
})
})
})
}
createWidescreenControl({ store, execute })
})
/* ===微信文章===end */
/* ===知乎===start */
handlers.set('zhihu', function() {
const store = createStore('zhihu')
function execute() {
GM_addStyle(`
/* 知乎专栏 */
.Post-NormalMain .Post-Header, .Post-NormalMain>div, .Post-NormalSub>div {
width: 65vw;
min-width: 690px;
}
.Post-SideActions { left: calc((100vw - 82vw)/2); }
/* 知乎问答 */
.QuestionHeader-content, .QuestionHeader-footer {
width: 75vw;
min-width: 1000px;
margin-left: auto;
margin-right: auto;
}
.QuestionHeader-footer-inner {
width: auto;
}
.QuestionHeader-footer-main {
padding-left: 0;
}
.QuestionHeader-main {
width: auto;
flex: 1;
}
.Question-main {
width: 75vw;
min-width: 1000px;
}
.Question-main .ListShortcut {
flex: 1;
}
.Question-mainColumn {
flex: 1;
width: auto;
padding-right: 10px;
}
`)
}
createWidescreenControl({ store, execute })
})
/* ===知乎===end */
/* ===掘金===start */
handlers.set('juejin', function() {
const store = createStore('juejin')
function execute() {
GM_addStyle(`
/* 掘金文章 */
@media screen and (min-width: 1300px) {
.main-container {
max-width: 75vw;
}
.main-container .main-area {
width: calc(100% - 21rem);
}
}
`)
}
createWidescreenControl({ store, execute })
})
/* ===掘金===end */
/* ===简书===start */
handlers.set('jianshu', function() {
const store = createStore('jianshu')
function execute() {
GM_addStyle(`
/* 简书文章 */
@media screen and (min-width: 1250px) {
[role=main] > div:first-child {
flex: 1;
width: auto;
}
}
@media screen and (min-width: 1250px) {
[role=main] {
width: 85vw;
}
#__next > div:last-child {
left: 30px;
}
}
@media screen and (min-width: 1450px) {
[role=main] {
width: 75vw;
}
#__next > div:last-child {
left: 7vw;
}
}
`)
}
createWidescreenControl({ store, execute })
})
/* ===简书===end */
/* ===百度搜索===start */
handlers.set('baidu', function() {
const store = createStore('baidu')
function execute() {
const styleEl = GM_addStyle(`
:root {
--inject-page-width: 70vw;
}
@media screen and (min-width: 1490px) {
/* 顶部搜索 */
.head_wrapper .s_form {
margin-left: auto;
margin-right: auto;
width: fit-content;
width: -moz-fit-content;
}
/* 搜索tab */
.s_tab {
padding-left: 0 !important;
}
.s_tab {
margin-left: auto;
margin-right: auto;
width: fit-content;
width: -moz-fit-content;
}
/* 搜索内容 */
#container {
margin-left: auto !important;
margin-right: auto !important;
}
/* 仅对新闻流处理宽屏 */
.container_new {
width: var(--inject-page-width) !important;
}
/* 左侧搜索结果 */
.container_new #content_left {
width: calc(var(--inject-page-width) - 450px) !important;
}
.container_new #content_left > div {
width: 100% !important;
}
.container_new #content_left .new-pmd .c-span9 {
width: 70%;
}
.container_new #content_left .c-group-wrapper .c-group {
width: 95% !important;
}
/* 分页 */
.page-inner {
margin-left: auto;
margin-right: auto;
padding-left: 0 !important;
width: var(--inject-page-width);
}
/* 页脚 */
.foot-inner {
margin-left: auto;
margin-right: auto;
width: var(--inject-page-width);
}
#foot .foot-inner #help {
padding-left: 0 !important;
}
}
`)
// 搜索时百度会清除文档这里需要将样式重新插入
function redo() {
if (document.head.contains(styleEl)) return
document.head.appendChild(styleEl)
}
window.addEventListener('DOMContentLoaded', () => {
const { jQuery } = unsafeWindow
jQuery(document).ajaxSuccess((event, xhr, settings) => {
if (!settings.url.startsWith('/s?')) return
redo()
})
})
window.addEventListener('popstate', redo)
}
createWidescreenControl({ store, execute })
})
/* ===百度搜索===end */
/* ===贴吧===start */
handlers.set('tieba', function() {
const store = createStore('tieba')
function execute() {
GM_addStyle(`
/* 帖子 */
@media screen and (min-width: 1390px) {
#container {
width: 70vw;
}
#container > .content {
width: 100%;
}
.nav_wrap, .p_thread, .pb_content, .core_title_wrap_bright, .l_post_bright, .core_reply_wrapper, .l_post_bright .core_reply_wrapper, .pb_footer {
width: 100%;
}
.core_title_absolute_bright {
width: calc(70vw - 240px);
}
/* 内容区域 */
.pb_content {
display: flex;
background-size: 100%;
}
.pb_content::after {
content: none;
}
/* 楼区域 */
.left_section {
flex: 1;
border-right: 2px solid #e4e6eb;
}
/* 楼层 */
.l_post_bright {
display: flex;
}
.l_post_bright .d_post_content_main{
width: auto;
flex: 1;
}
/* 右侧悬浮按钮 */
.tbui_aside_float_bar {
left: auto;
right: 11vw;
margin-left: 0;
}
}
`)
}
createWidescreenControl({ store, execute })
})
/* ===贴吧===end */
// 存储 以网站作为模块
function createStore(sitename) {
if (!sitename) throw new TypeError('缺少sitename,期望<string>')
const getRealProp = property => `${sitename}_${property}`
const target = {}
const handler = {
get(target, property) {
const realProp = getRealProp(property)
let value = target[realProp]
if (value == null) {
value = GM_getValue(realProp)
target[realProp] = value
}
return value
},
set(target, property, value) {
const realProp = getRealProp(property)
target[realProp] = value
GM_setValue(realProp, value)
return true
},
deleteProperty(target, property) {
const realProp = getRealProp(property)
const deleted = delete target[realProp]
GM_deleteValue(realProp)
return deleted
},
}
const store = new Proxy(target, handler)
return store
}
// 添加按钮样式
GM_addStyle(`
.inject-widescreen-js {
position: fixed;
z-index: 99;
top: 150px;
right: 8vw;
opacity: .5;
border: none;
color :#fff;
padding: 6px 12px;
font-size: 14px;
background: #3385ff;
box-shadow: 0 1px 6px rgba(0,0,0,.2);
transition: opacity .3s;
}
.inject-widescreen-js:hover {
opacity: 1;
}
`)
// 宽屏开关 options: store<store>, execute要执行的函数
function createWidescreenControl(options) {
const { store, execute } = options
const buttonComponent = new Vue({
template: `
<button
class="inject-widescreen-js"
title="注意:页面会被刷新"
@click="toggle"
>
{{ isOpen ? '已开启' : '关闭' }}
</button>
`,
data() {
return {
isOpen: store.is_open || false,
}
},
beforeCreate() {
store.is_open && (execute(), Toast('已宽屏处理'))
},
methods: {
async toggle() {
store.is_open = !this.isOpen
location.reload()
}
},
}).$mount()
window.addEventListener('DOMContentLoaded', () => {
document.body.appendChild(buttonComponent.$el)
})
}
main()
})();