- // ==UserScript==
- // @name Greasyfork Beautify
- // @namespace https://github.com/kiccer
- // @version 1.6.4
- // @description 优化导航栏样式 / 脚本列表改为卡片布局 / 代码高亮(atom-one-dark + vscode 风格) 等……融入式美化,自然、优雅,没有突兀感,仿佛页面原本就是如此……(更多优化逐步完善中!)
- // @description:en Optimize the navigation bar style / script list to card layout / code highlighting (atom-one-dark + vscode style), etc. Into the style of beautification, more natural, more elegant, no sense of abruptness, as if the page is originally so. (more optimization in progress!)
- // @author kiccer<1072907338@qq.com>
- // @supportURL https://github.com/kiccer/TampermonkeyScripts/issues
- // @license MIT
- // @match https://gf.qytechs.cn/*
- // @match https://sleazyfork.org/*
- // @icon https://gf.qytechs.cn/packs/media/images/blacklogo96-b2384000fca45aa17e45eb417cbcbb59.png
- // @require https://cdn.bootcdn.net/ajax/libs/vue/2.6.12/vue.min.js
- // @require https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.9/index.min.js
- // @require https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js
- // @require https://cdn.bootcdn.net/ajax/libs/javascript-detect-element-resize/0.5.3/jquery.resize.min.js
- // @require https://cdn.bootcdn.net/ajax/libs/less.js/4.1.3/less.min.js
- // @require https://cdn.bootcdn.net/ajax/libs/highlight.js/11.5.1/highlight.min.js
- // @require https://cdn.bootcdn.net/ajax/libs/highlight.js/11.5.1/languages/javascript.min.js
- // @require https://gf.qytechs.cn/scripts/447149-checkversion/code/checkVersion.js?version=1065242
- // @resource normalize.css https://cdn.bootcdn.net/ajax/libs/normalize/8.0.1/normalize.min.css
- // @resource element-ui.css https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.9/theme-chalk/index.min.css
- // @resource element-icons https://cdn.bootcdn.net/ajax/libs/element-ui/2.15.9/theme-chalk/fonts/element-icons.ttf
- // @resource atom-one-dark.css https://cdn.bootcdn.net/ajax/libs/highlight.js/11.5.1/styles/atom-one-dark.min.css
- // @run-at document-start
- // @grant GM_info
- // @grant GM_setValue
- // @grant GM_getValue
- // @grant GM_addStyle
- // @grant GM_getResourceURL
- // @grant GM_getResourceText
- // @grant GM_registerMenuCommand
- // ==/UserScript==
-
- /* globals $ less Vue hljs checkVersion ELEMENT */
-
- Vue.use(ELEMENT)
-
- if (/\(Development\)$/i.test(GM_info.script.name)) {
- Vue.config.devtools = true
- }
-
- // 默认设置
- const defaultSettings = {
- script_list_columns_num: 2,
- show_install_button_in_card: true,
- show_version_info_in_card: true
- }
-
- // 获取设置
- const getSettings = () => {
- return Object.assign(
- {},
- defaultSettings,
- JSON.parse(GM_getValue('formData') || '{}')
- )
- }
-
- const VERSION = GM_info.script.version
- const settings = getSettings()
-
- // 样式注入
- GM_addStyle(GM_getResourceText('normalize.css'))
- GM_addStyle(GM_getResourceText('element-ui.css'))
- GM_addStyle(GM_getResourceText('atom-one-dark.css'))
-
- const lessOptions = {}
-
- const lessInput = `
- // --------------------------------------------- 变量
-
- @nav_height: 60px;
- @user_container_height: 24px;
-
- // --------------------------------------------- 混合宏
-
- .ellipsis (@lines) {
- display: -webkit-box;
- -webkit-box-orient: vertical;
- overflow: hidden;
- line-height: 1.5;
- -webkit-line-clamp: @lines;
- }
-
- // --------------------------------------------- 通用样式
-
- * {
- box-sizing: border-box;
- outline: none;
- }
-
- body {
- line-height: 1.5;
- min-height: 100vh;
- background-color: #f7f7f7;
-
- > .width-constraint {
- min-height: 100vh;
- background-color: #fff;
- padding: 20px;
- padding-top: calc(@nav_height + @user_container_height + 20px);
-
- .text-content {
- border: 0;
- box-shadow: none;
- padding: 0;
- }
- }
- }
-
- a {
- color: rgb(38, 38, 38);
- text-decoration: none;
-
- &:hover {
- text-decoration: underline;
- }
-
- &:visited {
- color: rgb(38, 38, 38);
- }
- }
-
- // --------------------------------------------- element-ui
-
- // 解决 element-icons 图标引用不到问题
- @font-face {
- font-family: element-icons;
- src: url(${GM_getResourceURL('element-icons')}),
- }
-
- // --------------------------------------------- 代码高亮
-
- .code-container {
- background-color: #282c34;
- border-radius: 8px;
- max-height: 100%;
- overflow: visible;
-
- // 定义滚动条
- ::-webkit-scrollbar {
- width: 14px;
- height: 14px;
- background-color: transparent;
- }
-
- // 定义滚动条轨道
- ::-webkit-scrollbar-track {
- background-color: transparent;
- }
-
- // 定义滑块
- ::-webkit-scrollbar-thumb {
- background-color: rgba(78, 86, 102, 0);
- }
-
- // 定义边角
- ::-webkit-scrollbar-corner {
- background-color: transparent;
- }
-
- &:hover {
- ::-webkit-scrollbar-thumb {
- background-color: rgba(78, 86, 102, .5);
- }
- }
-
- ::selection {
- background-color: rgb(51, 56, 66);
- }
-
- pre {
- code {
- padding: 0;
- font-family: Consolas;
- cursor: text;
- overflow: auto;
-
- .marker {
- display: inline-block;
- color: #636d83;
- text-align: right;
- padding-right: 20px;
- user-select: none;
- cursor: auto;
- }
- }
- }
- }
-
- // --------------------------------------------- 页码
-
- .pagination {
- margin-top: 20px !important;
- user-select: none;
-
- > * {
- padding: 0 .5em !important;
- min-width: 2em;
- height: 2em;
- line-height: 2;
- text-align: center;
- text-decoration: none !important;
- }
-
- > a {
- background-color: #f7f7f7 !important;
-
- &:hover {
- background-color: #e1e1e1 !important;
- }
- }
- }
-
- // --------------------------------------------- 输入框
-
- input[type=search] {
- padding: 3px 6px;
- padding-right: 2.4em !important;
- border: 1px solid #bfbfbf;
- border-radius: 4px;
- }
-
- form {
- input.search-submit {
- top: 50% !important;
- transform: translateY(-50%);
- cursor: pointer;
- }
- }
-
- .home-search {
- margin-bottom: 20px;
- }
-
- .sidebar-search {
- margin-bottom: 20px;
-
- input[type="search"] {
- margin: 0;
- }
- }
-
- // --------------------------------------------- header
-
- #main-header {
- background-color: #000;
- background-image: none;
- width: 100%;
- padding: 0;
- position: fixed;
- top: 0;
- z-index: 1;
- user-select: none;
- box-shadow: 0 0 5px 2px rgb(0 0 0 / 50%);
-
- .width-constraint {
- display: flex;
- justify-content: space-between;
- height: 100%;
- padding: 0;
-
- #site-name {
- display: flex;
- align-items: center;
-
- a {
- display: block;
- }
-
- img {
- width: auto;
- height: 50px;
- }
-
- #site-name-text {
- margin-left: 10px;
-
- h1 {
- font-size: 36px;
- }
- }
- }
- }
-
- #user-container {
- width: 100%;
- height: @user_container_height;
- background-color: #343434;
-
- .user-main {
- display: flex;
- justify-content: space-between;
- align-items: center;
- margin: auto;
- max-width: 1200px;
- height: @user_container_height;
- padding-right: 10px;
-
- @media screen and (max-width: 1228px) {
- margin: auto 1.2vw;
- }
-
- .script-version {
- font-size: 12px;
- letter-spacing: 1px;
- font-family: "微软雅黑";
- font-weight: 200;
- color: rgba(255, 255, 255, .3);
-
- .has-new-version {
- color: lime;
- margin-left: 5px;
- }
- }
-
- .login-info {
- font-size: 14px;
- }
- }
- }
- }
-
- #site-nav {
- width: 0;
- height: 0;
- border: 0;
- padding: 0;
- overflow: hidden;
- position: relative;
- }
-
- #site-nav-vue {
- display: flex;
-
- .nav-item {
- line-height: @nav_height;
- padding: 0 10px;
- transition: all .2s ease;
- text-decoration: none;
- position: relative;
- white-space: nowrap;
-
- &:hover {
- background-color: rgba(255, 255, 255, .2);
-
- .sub-nav {
- display: flex;
- }
- }
-
- .sub-nav {
- display: none;
- flex-direction: column;
- position: absolute;
- top: 100%;
- right: 0;
- background-color: rgba(0, 0, 0, .8);
-
- .nav-item {
- line-height: 40px;
- }
- }
- }
- }
-
- // --------------------------------------------- 脚本列表
-
- #user-library-script-list,
- #user-script-list,
- #user-deleted-script-list,
- #browse-script-list {
- display: grid;
- grid-template-columns: repeat(${settings.script_list_columns_num}, 1fr);
- grid-gap: 20px;
- border: 0;
- box-shadow: none;
-
- @media screen and (max-width: 1228px) {
- grid-template-columns: repeat(1, 1fr);
- }
-
- li {
- border: 1px solid #bbb;
- box-shadow: 0 0 5px #ddd;
- border-radius: 5px;
- padding: 10px;
- position: relative;
- word-break: break-all;
-
- a.script-link {
- .ellipsis(2);
- height: calc(3em + 8px);
- font-size: 16px;
- margin: 4px -10px 4px -14px;
- padding: 4px 10px;
- background: linear-gradient(#fff, #eee);
- border-left: 7px solid #800;
- box-shadow: inset 0 1px rgb(0 0 0 / 10%), inset 0 -1px rgb(0 0 0 / 10%);
- }
-
- & > article {
- & > h2 {
- & > .badge,
- & > .name-description-separator,
- & > strong {
- display: none; // 兼容 “大人的Greasyfork”
- }
-
- .script-description {
- .ellipsis(3);
- text-indent: 2em;
- margin: 10px 0 10px;
- height: 4.5em;
- font-size: 14px;
-
- strong,
- #install-area {
- display: none; // 兼容 “大人的Greasyfork”
- }
- }
- }
- }
-
- .inline-script-stats {
- padding: 10px 0;
- // margin-bottom: 10px;
- border-top: 1px solid #ebebeb;
- // border-bottom: 1px solid #ebebeb;
-
- dt {
- // width: 40%;
- }
-
- dd {
- width: 60%;
- }
- }
-
- .install-link {
- float: right;
- font-size: 12px;
-
- &:hover {
- transition: box-shadow .2s;
- box-shadow: 0 8px 16px 0 rgb(0 0 0 / 20%), 0 6px 20px 0 rgb(0 0 0 / 19%);
- }
-
- &.lum-lightbox-loader {
- border-left: 10px solid #005200;
- border-right: 10px solid #005200;
- position: relative;
- min-height: 30px;
- min-width: 70px;
-
- &::before,
- &::after {
- width: 1em;
- height: 1em;
- margin-top: -0.5em;
- border-radius: 1em;
- background: hsla(0, 0%, 100%, .5);
- }
- }
- }
- }
- }
-
- // --------------------------------------------- 列表右侧选项组
-
- .list-option-groups {
- #language-selector {
- + * {
- margin-top: 10px;
- }
-
- #language-selector-locale {
- width: 100%;
- border: 1px solid #bfbfbf;
- border-radius: 4px;
- }
- }
- }
- `
-
- less.render(lessInput, lessOptions).then(output => {
- // output.css = string of css
- // output.map = string of sourcemap
- // output.imports = array of string filenames of the imports referenced
-
- GM_addStyle(output.css)
- }, err => {
- console.error(err)
- })
-
- // 查看代码页面简化,隐藏信息
- if (/https:\/\/greasyfork\.org\/[a-zA-Z-]+\/scripts\/\d+-.+\/code/.test(location.href)) {
- GM_addStyle(`
- #script-info header,
- #install-area,
- #script-feedback-suggestion {
- display: none;
- }
-
- #script-content {
- margin-top: 16px;
- }
-
- .code-container pre code {
- max-height: calc(100vh - 267px);
- }
- `)
- }
-
- // 脚本卡片美化
- function scriptCardBeautify () {
- $(`
- #user-script-list li[data-script-id],
- #user-deleted-script-list li[data-script-id],
- #browse-script-list li[data-script-id]
- `).each((i, n) => {
- const card = $(n)
- const href = card.find('> article a.script-link').attr('href')
-
- // TODO 显示脚本图标 (看情况,如果加了图标不好布局就算了)
-
- // 判断这个卡片是否已经美化过了
- const hasVersionTag = card.find('.script-show-version').length > 0
- const hasDownloadBtn = card.find('.install-link-copy').length > 0
-
- if (!(hasVersionTag && hasDownloadBtn)) {
- // 信息占位
- if (settings.show_version_info_in_card) {
- card.find('.inline-script-stats').append(`
- <dt class="script-show-version"><span>...</span></dt>
- <dd class="script-show-version"><span></span></dd>
- `)
- }
-
- // 下载按钮占位
- if (settings.show_install_button_in_card) {
- card.append(`
- <a class="install-link lum-lightbox-loader"></a>
- `)
- }
-
- // 增加延时,避免请求过多导致 503 错误 (每秒最多 10 个请求)
- setTimeout(() => {
- $.ajax({
- type: 'get',
- url: href,
- success: res => {
- const html = $(res)
-
- if (settings.show_version_info_in_card) {
- // 删除占位元素
- card.find('.script-show-version').remove()
-
- // 版本
- card.find('.inline-script-stats').append(
- html.find('.script-show-version')
- )
- }
-
- if (settings.show_install_button_in_card) {
- // 删除占位元素
- card.find('.install-link.lum-lightbox-loader').remove()
-
- // 下载按钮
- card.append(
- html.find('#install-area .install-link').eq(0).addClass('install-link-copy')
- )
-
- // 下载按钮文案根据已安装的版本号调整
- setTimeout(() => {
- const btn = card.find('.install-link-copy')[0]
- if (btn) checkVersion.checkForUpdatesJS(btn, true)
- })
- }
- }
- })
- }, (i % 5) * 2e3)
- }
- })
- }
-
- // 页面获得焦点时
- window.addEventListener('focus', e => {
- // 自动更新安装状态
- $('.script-list li[data-script-id] a.install-link-copy').each((i, n) => {
- checkVersion.checkForUpdatesJS(n, true)
- })
- })
-
- // 卡片数量记录
- let cardCountRecord = 0
-
- // 兼容无限翻页插件
- function compatibleWithInfiniteScroll () {
- const cardCount = $('.script-list li[data-script-id]').length
-
- if (cardCountRecord !== cardCount) {
- cardCountRecord = cardCount
- scriptCardBeautify()
- }
- }
-
- // 页面加载完成后执行
- $(() => {
- // 导航
- const navContainer = document.createElement('div')
- navContainer.id = 'site-nav-vue'
- document.querySelector('.width-constraint').appendChild(navContainer)
-
- // eslint-disable-next-line no-unused-vars
- const navApp = new Vue({
- el: '#site-nav-vue',
-
- template: `
- <div id="site-nav-vue">
- <a
- class="nav-item"
- v-for="(nav, nav_i) in navList"
- :key="nav_i"
- :href="nav.url"
- >
- <span>{{ nav.label }}</span>
-
- <div class="sub-nav" v-if="nav.list?.length">
- <a
- class="nav-item"
- v-for="(sub, sub_i) in nav.list"
- :key="sub_i"
- :href="sub.url"
- >
- <span>{{ sub.label }}</span>
- </a>
- </div>
- </a>
- </div>
- `,
-
- data () {
- return {
- navList: [...$('#site-nav > nav > li')].map(n => {
- const a = $(n).find('> a')
- const subNav = [...$(n).find('> nav > li')]
-
- return {
- label: a.text() || $(n).text(),
- url: a.attr('href'),
- list: subNav.map(m => {
- const subA = $(m).find('> a')
-
- return {
- label: subA.text(),
- url: subA.attr('href')
- }
- })
- }
- })
- }
- }
- })
-
- // 用户
- const userContainer = document.createElement('div')
- userContainer.id = 'user-container'
- document.querySelector('#main-header').appendChild(userContainer)
-
- // eslint-disable-next-line no-unused-vars
- const userApp = new Vue({
- el: '#user-container',
- template: `
- <div id="user-container">
- <div class="user-main">
- <div class="script-version">
- Greasyfork Beautify v${VERSION}
- <a
- class="has-new-version"
- href="https://gf.qytechs.cn/scripts/446849-greasyfork-beautify/code/Greasyfork%20Beautify.user.js"
- v-if="lastVersion !== '${VERSION}'"
- >Update to v{{ lastVersion }}</a>
- </div>
-
- <div class="login-info">
- <a
- :href="dom.attr('href')"
- >{{ dom.text() }}</a>
-
- <template v-if="isLogin">
- [<a :href="logoutDom.attr('href')">{{ logoutDom.text() }}</a>]
- </template>
- </div>
- </div>
- </div>
- `,
-
- data () {
- return {
- lastVersion: VERSION,
- dom: $('#nav-user-info .user-profile-link a, #nav-user-info .sign-in-link a'),
- logoutDom: $('.sign-out-link a'),
- isLogin: $('.sign-out-link').length > 0 // 存在登出按钮则表示已登录(不可用)
- }
- },
-
- created () {
- this.versionCheck()
- },
-
- methods: {
- versionCheck () {
- $.ajax({
- url: 'https://gf.qytechs.cn/zh-CN/scripts/446849-greasyfork-beautify',
- success: res => {
- const html = $(res)
- this.lastVersion = html.find('dd.script-show-version span').text()
- }
- })
- }
- }
- })
-
- // 代码高亮
- $('pre.lang-js').each((pre_i, pre) => {
- // 调整代码,给一些压缩代码增加换行
- $(pre).find('li').append('\n')
- const code = $('<code class="language-javascript">').html(
- pre.innerHTML
- )
-
- // 清空原始代码容器,放置新容器
- $(pre)
- .removeClass()
- .html('')
- .append(code)
-
- // 高亮
- hljs.highlightElement(pre.querySelector('code'))
-
- // 增加行号
- const html = $(pre).find('code').html()
- const htmlSplit = html.split('\n')
- const totalLines = htmlSplit.length
-
- $(pre).find('code').html(
- htmlSplit.map((n, i) => `<span class="marker" style="width: calc(${String(totalLines).length * 0.5}em + 20px);">${i + 1}</span>${n}`).join('\n')
- )
- })
-
- // 脚本列表页面,卡片
- if (settings.show_install_button_in_card || settings.show_version_info_in_card) {
- compatibleWithInfiniteScroll()
- $('.script-list ').resize(compatibleWithInfiniteScroll)
- }
-
- // 列表右侧选项组
- $('.list-option-groups > *:eq(0)').before(
- // 设置语言
- $('#language-selector')
- )
-
- // 注册(不可用)菜单
- $('body').append($('<div id="greasyfork-beautify-settings">'))
-
- const settingsApp = new Vue({
- el: '#greasyfork-beautify-settings',
-
- template: `
- <el-dialog
- width="600px"
- title="Greasyfork Beautify v${VERSION}"
- :visible.sync="show"
- @closed="onClosed"
- >
- <el-form
- size="mini"
- label-width="120px"
- :model="formData"
- >
- <el-form-item label="脚本列表列数">
- <el-input-number
- label="描述文字"
- v-model="formData.script_list_columns_num"
- :min="1"
- :max="2"
- />
- </el-form-item>
-
- <el-form-item label="显示安装按钮">
- <el-switch
- v-model="formData.show_install_button_in_card"
- />
- </el-form-item>
-
- <el-form-item label="显示版本信息">
- <el-switch
- v-model="formData.show_version_info_in_card"
- />
- </el-form-item>
- </el-form>
-
- <span slot="footer" class="dialog-footer">
- <el-button @click="onReset">重 置</el-button>
- <el-button type="primary" @click="onSubmit">确 定</el-button>
- </span>
- </el-dialog>
- `,
-
- data () {
- return {
- show: false,
- formData: getSettings()
- }
- },
-
- methods: {
- onClosed () {
- Object.assign(this.formData, getSettings())
- },
-
- onReset () {
- Object.assign(this.formData, defaultSettings)
- },
-
- onSubmit () {
- GM_setValue('formData', JSON.stringify(this.formData))
- location.reload()
- }
- }
- })
-
- GM_registerMenuCommand('美化设置', e => {
- settingsApp.show = true
- })
- })