├── .editorconfig ├── .github ├── ISSUE_TEMPLATE │ └── bug.md └── workflows │ └── build.yml ├── .gitignore ├── 163_Music_HTML5_Player ├── 163_Music_HTML5_Player.user.js ├── README.md └── meta.yml ├── 163_Music_High_Quality_Support ├── 163.Music.HQ.user.js ├── README.md └── meta.yml ├── 163_Music_Media_Metadata ├── 163_Music_Media_Metadata.user.js ├── README.md └── meta.yml ├── Akina ├── Akina.user.js ├── README.md └── meta.yml ├── Automatically_Pause_Video ├── Automatically_Pause_Video.user.js ├── README.md └── meta.yml ├── Cache_Manager_for_Tieba_Ueditor ├── Cache_Manager_for_Tieba_Ueditor.user.js ├── README.md └── meta.yml ├── Copy_all_links ├── Copy_all_links.user.js ├── README.md └── meta.yml ├── Fxxk_CSDN ├── Fxxk_CSDN.user.js ├── README.md └── meta.yml ├── GitHub_with_FastGit ├── GitHub_with_FastGit.user.js ├── README.md └── meta.yml ├── LoadBT_batch_download ├── LoadBT_batch_download.user.js ├── README.md └── meta.yml ├── Post_Del_Robot ├── PostDelRobot.user.js ├── README.md └── meta.yml ├── Putian_Warning ├── .gitignore ├── README.md ├── fetchList.js ├── meta.yml ├── package.json └── src │ ├── index.css │ ├── index.ts │ └── list.json ├── README.md ├── Reading_Mode ├── .gitignore ├── README.md ├── meta.yml ├── package.json └── src │ ├── app.tsx │ ├── components │ ├── bar.tsx │ └── rm.tsx │ ├── index.css │ ├── index.ts │ ├── sites │ ├── chinaunix.ts │ ├── cnblogs.ts │ ├── csdn.ts │ ├── index.ts │ ├── juejin.ts │ ├── netease.ts │ ├── sciencenet.ts │ ├── sina.ts │ ├── sohu.ts │ ├── tianya.ts │ ├── var.ts │ └── zhihu.ts │ ├── styles.ts │ └── utils.ts ├── Short_URL ├── README.md ├── Short_URL.user.js └── meta.yml ├── Steam_Charge ├── Steam_Charge.user.js └── meta.yml ├── Straight_Baidu ├── README.md ├── Straight_Baidu.user.js └── meta.yml ├── Tieba_Blocked_Detect ├── .eslintrc.json ├── README.md ├── Tieba_Blocked_Detect.user.js └── meta.yml ├── Tieba_Client ├── README.md ├── Tieba_Client.user.js └── meta.yml ├── Tieba_Kuso ├── README.md ├── Tieba_Kuso.user.js └── meta.yml ├── Tieba_Lzl_Dialogue ├── README.md ├── Tieba_Lzl_Dialogue.user.js └── meta.yml ├── Tieba_Private ├── README.md ├── Tieba_Private.user.js └── meta.yml ├── Tieba_Quick_Reply ├── README.md ├── Tieba_Quick_Reply.user.js └── meta.yml ├── global.d.ts ├── package.json ├── scripts ├── build-site.js ├── build.js ├── copy-master.js ├── dev.js ├── master │ └── README.md ├── utils │ ├── build-one.js │ ├── copy-one.js │ ├── generate-meta.js │ ├── index.js │ ├── loader │ │ └── gm-style-loader.js │ ├── terminal.js │ └── webpack.config.js └── www │ ├── CNAME │ └── index.html ├── tsconfig.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = false 9 | insert_final_newline = false -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: BUG 3 | about: 报告脚本BUG 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | 【涉及脚本】 11 | 例如:Reading Mode 12 | 13 | 【所使用和浏览器和版本】 14 | 例如:Firefox 79.0a1 15 | 16 | 【所使用的扩展和版本】 17 | 例如:Tampermonkey v4.10 18 | 19 | 【测试地址】 20 | 例如:http://tieba.baidu.com/f?kw=firefox 21 | 22 | 【重现步骤】 23 | 1.打开 xxx 网站 24 | 2.点击 xxx 按钮 25 | 3.出现 xxx 情况 26 | 27 | 【相关截图(如果有)】 28 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | - name: Setup node 14 | uses: actions/setup-node@v2 15 | with: 16 | node-version: 12 17 | - name: Install Yarn 18 | run: npm i -g yarn 19 | - name: Get yarn cache directory path 20 | id: yarn-cache-dir-path 21 | run: echo "::set-output name=dir::$(yarn cache dir)" 22 | - uses: actions/cache@v1 23 | id: yarn-cache 24 | with: 25 | path: ${{ steps.yarn-cache-dir-path.outputs.dir }} 26 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 27 | restore-keys: | 28 | ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 29 | - name: Install and build 30 | run: | 31 | yarn install --frozen-lockfile 32 | npm run build-site 33 | node ./scripts/copy-master.js 34 | - name: Deploy master 35 | uses: JamesIves/github-pages-deploy-action@4.1.4 36 | with: 37 | branch: master 38 | folder: dist/master 39 | - name: Deploy pages 40 | uses: JamesIves/github-pages-deploy-action@4.1.4 41 | with: 42 | branch: gh-pages 43 | folder: dist/pages -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | .idea/ 4 | .vscode/ 5 | ~* 6 | 7 | # Packages # 8 | ############ 9 | # it's better to unpack these files and commit the raw source 10 | # git has its own built in compression methods 11 | *.7z 12 | *.dmg 13 | *.gz 14 | *.iso 15 | *.jar 16 | *.rar 17 | *.tar 18 | *.zip 19 | # Optional npm cache directory 20 | .npm 21 | 22 | # dist 23 | lib/ 24 | lib/* 25 | dist/ 26 | dist/* 27 | 28 | # Logs and databases # 29 | ###################### 30 | *.log 31 | *.sql 32 | *.sqlite 33 | 34 | # tests and coverages 35 | .nyc_output/ 36 | coverage/ 37 | coverage/* 38 | mochawesome-report/ 39 | 40 | # OS generated files # 41 | ###################### 42 | .DS_Store 43 | *.swp 44 | .DS_Store? 45 | ._* 46 | .Spotlight-V100 47 | .Trashes 48 | ehthumbs.db 49 | Thumbs.db 50 | 51 | # NPM 52 | package-lock.json -------------------------------------------------------------------------------- /163_Music_HTML5_Player/163_Music_HTML5_Player.user.js: -------------------------------------------------------------------------------- 1 | Object.defineProperty(navigator,'plugins',{}); -------------------------------------------------------------------------------- /163_Music_HTML5_Player/README.md: -------------------------------------------------------------------------------- 1 | # 163 Music HTML5 Player # 2 | 3 | * [【点击安装】](https://userscript.firefoxcn.net/js/163_Music_HTML5_Player.user.js) 4 | * author: 柏拉 5 | * 网易云音乐 HTML5 播放。 6 | * version: 2 7 | -------------------------------------------------------------------------------- /163_Music_HTML5_Player/meta.yml: -------------------------------------------------------------------------------- 1 | name: 163 Music HTML5 Player 2 | version: 2 3 | description: 网易云音乐 HTML5 播放器 4 | match: "*://music.163.com/*" 5 | include: "*://music.163.com/*" 6 | author: Palatoo Simple 7 | run-at: document-start 8 | namespace: http://firefoxbar.github.io/#userscripts -------------------------------------------------------------------------------- /163_Music_High_Quality_Support/163.Music.HQ.user.js: -------------------------------------------------------------------------------- 1 | // getTrackURL 源码来自 Chrome 扩展程序 网易云音乐增强器(Netease Music Enhancement) by wanmingtom@gmail.com 2 | // 菊苣这个加密算法你是怎么知道的 _(:3 3 | var getTrackURL = function getTrackURL (dfsId) { 4 | var byte1 = '3' + 'g' + 'o' + '8' + '&' + '$' + '8' + '*' + '3' + 5 | '*' + '3' + 'h' + '0' + 'k' + '(' + '2' + ')' + '2'; 6 | var byte1Length = byte1.length; 7 | var byte2 = dfsId + ''; 8 | var byte2Length = byte2.length; 9 | var byte3 = []; 10 | for (var i = 0; i < byte2Length; i++) { 11 | byte3[i] = byte2.charCodeAt(i) ^ byte1.charCodeAt(i % byte1Length); 12 | } 13 | 14 | byte3 = byte3.map(function(i) { 15 | return String.fromCharCode(i); 16 | }).join(''); 17 | 18 | results = CryptoJS.MD5(byte3).toString(CryptoJS.enc.Base64); 19 | results = results.replace(/\//g, '_').replace(/\+/g, '-'); 20 | 21 | // 如果需要修改使用的 cdn,请修改下面的地址 22 | // 可用的服务器有 ['m1', 'm2', 'p1', 'p2'] 23 | // 使用 p1 或 p2 的 cdn 可解决境外用户无法播放的问题 24 | var url = 'http://m1.music.126.net/' + results + '/' + byte2 + '.mp3'; 25 | return url; 26 | }; 27 | 28 | var modifyURL = function modifyURL(data, parentKey) { 29 | console.log('API 返回了 ' + data.length + ' 首曲目'); 30 | console.log('施放魔法!变变变!'); 31 | data.forEach(function(elem){ 32 | // 部分音乐没有高音质 33 | if (!parentKey) elem.mp3Url = getTrackURL((elem.hMusic || elem.mMusic || elem.lMusic).dfsId); 34 | else elem[parentKey].mp3Url = getTrackURL((elem[parentKey].hMusic || elem[parentKey].mMusic || elem.lMusic[parentKey]).dfsId); 35 | }); 36 | return data; 37 | }; 38 | 39 | var cachedURL = {}; 40 | var qualityNode = null; 41 | 42 | // 重新编写脚本,改用 hook xhr 的形式替换 URL 链接 43 | var originalXMLHttpRequest = window.XMLHttpRequest; 44 | var fakeXMLHttpRequest = function(){ 45 | var __this__ = this; 46 | var xhr = new originalXMLHttpRequest(); 47 | var xhrProto = xhr.constructor.prototype; 48 | 49 | Object.keys(xhrProto).forEach(function(elem){ 50 | if (elem in __this__) return; 51 | if (elem === 'responseText') return; 52 | 53 | if (typeof xhr[elem] === 'function') { 54 | if (elem === 'open') { // add requestURL support 55 | __this__[elem] = function(){ 56 | if (arguments[1].indexOf('/enhance/player/') >= 0) { 57 | // 对新版 api 请求旧的 api 接口 58 | __this__.ping = new fakeXMLHttpRequest(); 59 | __this__.ping.open(arguments[0], arguments[1].replace('/enhance/player/url', '/detail'), false); // 不使用异步 xhr 以阻断原 xhr 请求 60 | __this__.ping.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 61 | } 62 | xhr[elem].apply(xhr, arguments); 63 | __this__.requestURL = arguments[1]; 64 | }; 65 | } 66 | else if (elem === 'send') { // add requestURL support 67 | __this__[elem] = function(){ 68 | if (__this__.requestURL.indexOf('/enhance/player/') >= 0 && __this__.ping) { 69 | //__this__.ping.send(arguments[0]); 70 | __this__.ping.sendData = arguments[0]; 71 | } 72 | xhr[elem].apply(xhr, arguments); 73 | }; 74 | } 75 | else { 76 | __this__[elem] = function(){ 77 | xhr[elem].apply(xhr, arguments); 78 | }; 79 | } 80 | } 81 | else { 82 | var property = {}; 83 | var originalProperty = Object.getOwnPropertyDescriptor(xhrProto, elem); 84 | property.get = function(){ /*console.log(elem);*/ return xhr[elem]; }; 85 | if (originalProperty.set) property.set = function(val){ return xhr[elem] = val; }; 86 | Object.defineProperty(__this__, elem, property); 87 | } 88 | }); 89 | 90 | Object.defineProperty(__this__, 'responseText', { 91 | get: function(){ 92 | try { 93 | if (__this__.requestURL.indexOf('/weapi/') < 0 && __this__.requestURL.indexOf('/api/') < 0) { 94 | return xhr.responseText; 95 | } 96 | var action = __this__.requestURL.split(/\/(?:we)?api\//)[1].split('?')[0].split('/'); 97 | var res = JSON.parse(xhr.responseText); 98 | 99 | switch (action[0]) { 100 | case 'album': 101 | modifyURL(res.album.songs); 102 | return JSON.stringify(res); 103 | 104 | case 'song': 105 | if (action[1] !== 'detail') { 106 | if (action[1] !== 'enhance' && action[2] !== 'player' && action[3] !== 'url') { 107 | return xhr.responseText; 108 | } 109 | 110 | // 给黄易节省带宽,优先调用缓存的 URL 111 | if (cachedURL[res.data[0].id] && new Date() < cachedURL[res.data[0].id].expires) { 112 | res.data[0].url = cachedURL[res.data[0].id].url; 113 | delete __this__.ping; 114 | if (qualityNode) { 115 | qualityNode.textContent = cachedURL[res.data[0].id].quality / 1000 + 'K'; 116 | } 117 | return JSON.stringify(res); 118 | } 119 | 120 | // 缓存超时了再用新的 URL 121 | // 其实实际 CDN 超时实践并不是那个 `expi`,而是 URL 的 path 的第一个字段 122 | // 不过不知道后期会不会改变,以及这个 path 的时间戳可能是基于 GMT+8 123 | // 为了减少复杂度,就用 `expi` 了,不过实际上真正的可用时间是远高于 `expi` 的 124 | 125 | // 因为新版 API 已经返回的是高音质版本,所以不需要再请求旧的 API 126 | // 如果返回地址为 null 再尝试获取旧版 API 127 | if (res.data[0].url) { 128 | cachedURL[res.data[0].id] = { 129 | url: res.data[0].url, 130 | quality: res.data[0].br, 131 | expires: res.data[0].expi * 1000 + new Date().getTime() 132 | }; 133 | delete __this__.ping; 134 | if (qualityNode) { 135 | qualityNode.textContent = (res.data[0].br) / 1000 + 'K'; 136 | } 137 | return xhr.responseText; 138 | } 139 | 140 | if (!cachedURL[res.data[0].id]) { // 若未缓存,且新 API 没有音质再请求原始 api 141 | console.log('新版 API 未返回 URL,fallback 至旧版 API'); 142 | __this__.ping.send(__this__.ping.sendData); 143 | // 因为使用了同步 xhr 所以请求会被阻塞,下面的代码相当于回调 144 | // 其实获取到 pingRes 时已经是对本函数的一次执行了,qualityNode 不需要再更改 145 | var pingRes = JSON.parse(__this__.ping.responseText); 146 | var pingResSong = pingRes.songs[0]; 147 | cachedURL[res.data[0].id] = { 148 | url: pingRes.songs[0].mp3Url, 149 | quality: (pingResSong.hMusic || pingResSong.h || pingResSong.mMusic || pingResSong.m || pingResSong.lMusic || pingResSong.l).bitrate, 150 | expires: Infinity // 旧版 API URL 永不超时 151 | }; 152 | } 153 | res.data[0].url = cachedURL[res.data[0].id].url; 154 | return JSON.stringify(res); 155 | } 156 | 157 | // 这里是处理旧版 API 的部分 158 | modifyURL(res.songs); 159 | if (qualityNode) { 160 | qualityNode.textContent = (res.songs[0].hMusic || res.songs[0].h || res.songs[0].mMusic || res.songs[0].m || res.songs[0].lMusic || res.songs[0].l).bitrate / 1000 + 'K'; 161 | } 162 | return JSON.stringify(res); 163 | 164 | case 'playlist': 165 | if (action[1] !== 'detail') { 166 | return xhr.responseText; 167 | } 168 | 169 | modifyURL(res.result.tracks); 170 | return JSON.stringify(res); 171 | 172 | case 'dj': 173 | if (action[2] === 'byradio') { 174 | modifyURL(res.programs, 'mainSong'); 175 | return JSON.stringify(res); 176 | } 177 | if (action[2] === 'detail') { 178 | res.program = modifyURL([res.program], 'mainSong')[0]; 179 | return JSON.stringify(res); 180 | } 181 | return xhr.responseText; 182 | 183 | case 'radio': 184 | if (action[1] === 'get') { 185 | modifyURL(res.data); 186 | return JSON.stringify(res); 187 | } 188 | return xhr.responseText; 189 | 190 | case 'v3': 191 | switch (action[1]){ 192 | // http://music.163.com/weapi/v3/playlist/detail 193 | case 'playlist': 194 | if (action[2] !== 'detail') { 195 | return xhr.responseText; 196 | } 197 | 198 | res.privileges.forEach(function(elem){ 199 | var q = elem.pl || elem.dl || elem.fl || Math.min(elem.maxbr, 320000) || 320000; 200 | elem.st = 0; 201 | elem.pl = q; 202 | elem.dl = q; 203 | elem.fl = q; 204 | }); 205 | if (res.privileges.length < res.playlist.trackIds.length && res.playlist.trackIds.length === res.playlist.tracks.length) { 206 | // 对超过 1000 的播放列表补充播放信息(需魔改 core.js) 207 | for (var i = res.privileges.length; i < res.playlist.trackIds.length; i++) { 208 | var q = (res.playlist.tracks.h || res.playlist.tracks.m || res.playlist.tracks.l || res.playlist.tracks.a).br || 320000; 209 | res.privileges.push({ 210 | cp: 1, 211 | cs: false, 212 | dl: q, 213 | fee: 0, 214 | fl: q, 215 | id: res.playlist.trackIds[i].id, 216 | maxbr: q, 217 | payed: 0, 218 | pl: q, 219 | sp: 7, 220 | st: 0, 221 | subp: 1 222 | }); 223 | } 224 | } 225 | return JSON.stringify(res); 226 | 227 | case 'song': 228 | if (action[2] !== 'detail') { 229 | return xhr.responseText; 230 | } 231 | 232 | res.privileges.forEach(function(elem){ 233 | var q = elem.pl || elem.dl || elem.fl || Math.min(elem.maxbr, 320000) || 320000; 234 | elem.st = 0; 235 | elem.pl = q; 236 | elem.dl = q; 237 | elem.fl = q; 238 | }); 239 | return JSON.stringify(res); 240 | 241 | default: 242 | return xhr.responseText; 243 | } 244 | break; 245 | default: 246 | return xhr.responseText; 247 | } 248 | } 249 | catch (error) { 250 | // 以防 api 转换失败也能正常返回数据 251 | console.error('转换出错!', error); 252 | return xhr.responseText; 253 | } 254 | } 255 | }); 256 | 257 | // 轮询当前对象的 prototype,以解决无法获取更高原型链的属性的问题 258 | var curPrototype = xhrProto; 259 | while (curPrototype = Object.getPrototypeOf(curPrototype)) { 260 | Object.keys(curPrototype).forEach(function(elem){ 261 | var property = {}; 262 | var originalProperty = Object.getOwnPropertyDescriptor(curPrototype, elem); 263 | property.get = function(){ /*console.log(elem);*/ return xhr[elem]; }; 264 | if (originalProperty.set) property.set = function(val){ return xhr[elem] = val; }; 265 | Object.defineProperty(__this__, elem, property); 266 | }); 267 | } 268 | 269 | this.originalXMLHttpRequest = xhr; 270 | }; 271 | window.XMLHttpRequest = fakeXMLHttpRequest; 272 | 273 | // 旧版 API 大部分曲目失效,故 hook 加密函数以获取新版 API 的高音质版本 274 | var original_asrsea; 275 | var fake_asrsea = function(){ 276 | //console.log(arguments) 277 | var data = JSON.parse(arguments[0]); 278 | if (data.br && data.br === 128000) { 279 | data.br = 320000; 280 | arguments[0] = JSON.stringify(data); 281 | } 282 | //console.log(arguments); 283 | return original_asrsea.apply(window, arguments); 284 | }; 285 | if (window.asrsea) { 286 | original_asrsea = window.asrsea; 287 | window.asrsea = fake_asrsea; 288 | } 289 | else { 290 | Object.defineProperty(window, 'asrsea', { 291 | get: function(){ 292 | return fake_asrsea; 293 | }, 294 | set: function(val) { 295 | original_asrsea = val; 296 | } 297 | }); 298 | } 299 | 300 | 301 | var quailtyInsertHandler = function() { 302 | var target = document.querySelector('.m-pbar'); 303 | if (target) { 304 | qualityNode = document.createElement('span'); 305 | qualityNode.style.cssText = 'color: #797979; position: absolute; top: 0px; right: 31px; text-shadow: 0 1px 0 #171717; line-height: 28px;'; 306 | target.parentElement.insertBefore(qualityNode, target); 307 | } 308 | 309 | document.removeEventListener('DOMContentLoaded', quailtyInsertHandler, false); 310 | }; 311 | 312 | document.addEventListener('DOMContentLoaded', quailtyInsertHandler, false); -------------------------------------------------------------------------------- /163_Music_High_Quality_Support/README.md: -------------------------------------------------------------------------------- 1 | # 网易云音乐高音质支持 2 | 3 | **WARNING: 此脚本已经不再维护,且本脚本对标记为【128K免费】的曲目可能没有效果** 4 | 5 | ## 安装 6 | - [Install From GitHub](https://userscript.firefoxcn.net/js/163_Music_HTML5_Player.user.js) 7 | - [Install From GreasyFork](https://greasyfork.org/zh-CN/scripts/10582/) 8 | 9 | ## 功能 10 | 去除网页版网易云音乐仅可播放低音质(96/128Kbps)的限制,强制播放高音质版本 11 | 12 | ## 使用说明 13 | 安装脚本后无需进行设置,播放任意歌曲,即可自动转换为高音质版本 14 | 15 | \* 自 2.0 起不再需要对播放列表作出更改即可切换至高音质版本 16 | 17 | \* 由于旧版 API 大部分曲目失效,自 3.0 起使用新版 API 返回高音质版本(注意:新版 API 曲目重复播放时可能会重复缓冲,虽然 3.2 自行增强了缓存机制,可是网宿 CDN 自己又改了 URL 不读缓存,这就不能怪我了) 18 | 19 | \* 此脚本使用了 Chrome 扩展程序 网易云音乐增强器(Netease Music Enhancement) by Tom Wan 的源码 20 | 21 | \* 如果不出意外,3.2 将是本脚本的最后一个版本 22 | 23 | ![Preview](https://i.minus.com/iGjRe2lrYTQWl.png) 24 | 25 | ## 脚本信息 26 | - Author: 864907600cc (@ccloli) 27 | - License: GPLv3 -------------------------------------------------------------------------------- /163_Music_High_Quality_Support/meta.yml: -------------------------------------------------------------------------------- 1 | name: 网易云音乐高音质支持 2 | version: 3.3 3 | description: 去除网页版网易云音乐仅可播放低音质(96Kbps)的限制,强制播放高音质版本 4 | match: "*://music.163.com/*" 5 | include: "*://music.163.com/*" 6 | author: 864907600cc 7 | icon: https://secure.gravatar.com/avatar/147834caf9ccb0a66b2505c753747867 8 | run-at: document-start 9 | grant: none 10 | namespace: http://ext.ccloli.com -------------------------------------------------------------------------------- /163_Music_Media_Metadata/163_Music_Media_Metadata.user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* globals CryptoJS, MediaMetadata */ 4 | 5 | (() => { 6 | if (!navigator.mediaSession || typeof MediaMetadata === 'undefined') { 7 | console.log('The browser doesn\'t support MediaSession'); 8 | return; 9 | } 10 | 11 | /** 12 | * document.querySelector 13 | * 14 | * @param {string} selector - 选择器 15 | * @returns {Node|null} 节点 16 | */ 17 | const $ = (selector) => document.querySelector(selector); 18 | 19 | /** 20 | * 获取缩放后的图片 URL 21 | * 22 | * @param {string} url - 图片 URL 23 | * @param {number} [size] - 图片缩放尺寸 24 | * @returns {string} 缩放后的图片 URL 25 | */ 26 | const getResizedPicUrl = (url, size) => { 27 | return `${url}${size ? `?param=${size}y${size}` : ''}`; 28 | }; 29 | 30 | /** 31 | * 获取图片完整 URL 32 | * 33 | * @param {number|string} id - 文件 ID 34 | * @returns {string} 图片 URL 35 | */ 36 | const getPicUrl = (id) => { 37 | const key = '3go8&$8*3*3h0k(2)2'; 38 | const idStr = `${id}`; 39 | const idStrLen = idStr.length; 40 | const token = idStr.split('').map((e, i) => { 41 | return String.fromCharCode(e.charCodeAt(0) ^ key.charCodeAt(i % idStrLen)); 42 | }).join(''); 43 | const result = CryptoJS.MD5(token).toString(CryptoJS.enc.Base64) 44 | .replace(/\/|\+/g, match => ({ 45 | '/': '_', 46 | '+': '-' 47 | })[match]); 48 | return `https://p1.music.126.net/${result}/${idStr}.jpg`; 49 | }; 50 | 51 | /** 52 | * 从播放列表中生成对应曲目的 metadata 53 | * 54 | * @param {number} id - 歌曲 ID 55 | * @returns {object} metadata 数据内容 56 | */ 57 | const getSongMetadata = (id) => { 58 | let result; 59 | try { 60 | const playlist = JSON.parse(localStorage.getItem('track-queue')); 61 | const item = playlist.find(e => e.id === +id); 62 | if (item) { 63 | const album = item.album || {}; 64 | result = { 65 | title: item.name, 66 | artist: (item.artists || []).map(e => e.name).join('/'), 67 | album: album.name, 68 | artwork: [{ 69 | src: getResizedPicUrl(album.picUrl || getPicUrl(album.pic_str || album.pic), 160), 70 | sizes: '160x160', 71 | type: 'image/jpeg' 72 | }, { 73 | src: getResizedPicUrl(album.picUrl || getPicUrl(album.pic_str || album.pic), 320), 74 | sizes: '320x320', 75 | type: 'image/jpeg' 76 | }, { 77 | src: getResizedPicUrl(album.picUrl || getPicUrl(album.pic_str || album.pic), 480), 78 | sizes: '480x480', 79 | type: 'image/jpeg' 80 | }] 81 | }; 82 | } 83 | } catch (err) { 84 | console.log(err); 85 | } 86 | 87 | return result; 88 | }; 89 | 90 | /** 91 | * 从页面内 DOM 生成 metadata(不含专辑名) 92 | * 93 | * @returns {object} metadata 数据内容 94 | */ 95 | const generateCurrentMetadata = () => { 96 | const coverUrl = $('.m-playbar .head > img').getAttribute('src').split('?').shift(); 97 | return { 98 | title: $('.m-playbar .words .name').getAttribute('title'), 99 | artist: $('.m-playbar .words .by > span').getAttribute('title'), 100 | artwork: [{ 101 | src: getResizedPicUrl(coverUrl), 102 | sizes: '160x160', 103 | type: 'image/jpeg' 104 | }, { 105 | src: getResizedPicUrl(coverUrl), 106 | sizes: '320x320', 107 | type: 'image/jpeg' 108 | }, { 109 | src: getResizedPicUrl(coverUrl), 110 | sizes: '480x480', 111 | type: 'image/jpeg' 112 | }] 113 | }; 114 | }; 115 | 116 | /** 117 | * 设置 metadata 到 mediaSession 上 118 | * 119 | * @param {object} [data] - metadata 数据 120 | */ 121 | const setMetadata = (data) => { 122 | if (data) { 123 | navigator.mediaSession.metadata = new MediaMetadata(data); 124 | } 125 | else { 126 | navigator.mediaSession.metadata = null; 127 | } 128 | }; 129 | 130 | /** 131 | * 更新当前曲目的 media metadata 132 | * 133 | */ 134 | const updateCurrent = () => { 135 | const id = ($('.m-playbar .words .name').getAttribute('href').match(/\?id=(\d+)/) || [])[1]; 136 | if (id) { 137 | const data = getSongMetadata(id) || generateCurrentMetadata(); 138 | setMetadata(data); 139 | } 140 | }; 141 | 142 | /** 143 | * 处理客户端播放面板操作回调 144 | * 145 | */ 146 | const setActionHandler = () => { 147 | navigator.mediaSession.setActionHandler('previoustrack', () => { 148 | $('.m-playbar .btns .prv').click(); 149 | }); 150 | navigator.mediaSession.setActionHandler('nexttrack', () => { 151 | $('.m-playbar .btns .nxt').click(); 152 | }); 153 | }; 154 | 155 | /** 156 | * 初始化函数 157 | * 158 | */ 159 | const init = () => { 160 | if ($('.m-playbar')) { 161 | // 使用 animation 事件监听可能会覆盖其他使用相同方法处理的脚本,改用 MutationObserver 162 | const observer = new MutationObserver((mutations) => { 163 | if (mutations && mutations[0]) { 164 | updateCurrent(); 165 | } 166 | }); 167 | 168 | observer.observe($('.m-playbar .words'), { 169 | attributes: true, childList: true, subtree: true 170 | }); 171 | 172 | setActionHandler(); 173 | updateCurrent(); 174 | } 175 | }; 176 | 177 | init(); 178 | })(); 179 | -------------------------------------------------------------------------------- /163_Music_Media_Metadata/README.md: -------------------------------------------------------------------------------- 1 | # 163 Music MediaMetadata 2 | 3 | > 此功能即将由官方支持,此脚本可能将不再需要,继续使用有可能会出现状态冲突等问题 4 | 5 | 在网易云音乐 Web 版上启用 MediaSession 支持 6 | 7 | ![](https://user-images.githubusercontent.com/8115912/72663337-81ddf300-3a2c-11ea-9299-6106bb75d3e0.png) 8 | 9 | 10 | ## 安装 11 | 12 | - [Install From GitHub](https://userscript.firefoxcn.net/js/163_Music_Media_Metadata.user.js) 13 | - [Install From GreasyFork](https://greasyfork.org/zh-CN/scripts/395376) 14 | 15 | 16 | ## 功能 17 | 18 | - 在支持的浏览器和系统的播放控件上展示播放状态 19 | 20 | - 支持切换上一曲与下一曲播放 21 | 22 | 23 | ## 使用说明 24 | 25 | 无 26 | 27 | 28 | ## 注意事项 29 | 30 | - 目前可能仅 Chrome 浏览器支持 31 | 32 | - 据 MDN 兼容性列表,Firefox 浏览器理论上是支持的,但是在 Windows 上似乎不起作用,即便在 `about:config` 上开启 `dom.media.mediasession.enabled` 也不会调用系统 API,建议关注 [bugzilla](https://bugzilla.mozilla.org/show_bug.cgi?id=1112032) 了解支持进度 33 | 34 | - 若在多个页面切换播放,在 Chrome 浏览器上可能会显示多个播放状态 35 | 36 | 37 | ## 脚本信息 38 | - Author: 864907600cc (@ccloli) 39 | - License: GPLv3 40 | -------------------------------------------------------------------------------- /163_Music_Media_Metadata/meta.yml: -------------------------------------------------------------------------------- 1 | name: 163 Music MediaMetadata 2 | version: '1.0' 3 | description: 在网易云音乐 Web 版上启用 MediaMetadata 支持 4 | match: '*://music.163.com/*' 5 | include: '*://music.163.com/*' 6 | author: 864907600cc 7 | icon: 'https://secure.gravatar.com/avatar/147834caf9ccb0a66b2505c753747867' 8 | grant: none 9 | namespace: 'http://ext.ccloli.com' 10 | -------------------------------------------------------------------------------- /Akina/Akina.user.js: -------------------------------------------------------------------------------- 1 | (function(cssText,dialogHtml){ 2 | 3 | var style = document.createElement('style'); 4 | style.innerHTML = cssText; 5 | document.head.appendChild(style); 6 | 7 | var postList = document.querySelector('#j_p_postlist'); 8 | postList.addEventListener('click',function(e){ 9 | var target = e.target; 10 | 'l_badge' === target.className && depart(e,'disk'); 11 | }); 12 | 13 | var pageData = { 14 | un: '', 15 | jump: 'http://yun.baidu.com/share/home?uk=', 16 | apiUrl: 'http://pan.baidu.com/api/user/search?user_list=[%22$un%22]', 17 | oldDriver: {}, 18 | timeOut: 0 19 | } 20 | 21 | function depart(e,r){ 22 | if(document.querySelector('#akina-pop')) return; 23 | if(r === 'disk'){ 24 | var parentNode = e.target.parentNode; 25 | } 26 | pageData.un = JSON.parse(parentNode.querySelector('.p_author_name').dataset.field).un; 27 | pageData.oldDriver = parentNode.querySelector('.p_author_face img'); 28 | 29 | driving(); 30 | } 31 | 32 | function driving(){ 33 | GM_xmlhttpRequest({ 34 | method: 'GET', 35 | url: pageData.apiUrl.replace('$un',pageData.un), 36 | onload: function(res){ 37 | getUK(res,pageData.oldDriver); 38 | } 39 | }); 40 | } 41 | 42 | function dialog(callback){ 43 | 44 | var dialog = document.createElement('div'); 45 | 46 | dialog.className = 'dialogJ dialogJfix dialogJshadow'; 47 | 48 | dialog.style.cssText = 'z-index:5000;width:500px;left:' + 49 | (document.documentElement.clientWidth / 2 - 250) + 'px;top:' + 50 | (document.documentElement.clientHeight / 2 - 272) + 'px'; 51 | 52 | dialog.innerHTML = dialogHtml; 53 | document.body.appendChild(dialog); 54 | 55 | callback.call(this,dialog); 56 | } 57 | 58 | function getUK(res,oldDriver) { 59 | var res = JSON.parse(res.response); 60 | console.dir(res); 61 | if( 0 === res.errno){ 62 | return location.href = pageData.jump + res.records[0].uk; 63 | } 64 | dialog(function(dialog){ 65 | var ticket = res.img; 66 | var akinaCode = dialog.querySelector('#akina-code'); 67 | akinaCode.setAttribute('src',ticket); 68 | var vcode = res.vcode; 69 | dialog.querySelector('#old-driver').src = oldDriver.src; 70 | 71 | var changeCode = dialog.querySelector('#akina-change'); 72 | var tip = dialog.querySelector('#driver-tip'); 73 | var vcodeTxt = dialog.querySelector('#akina-text'); 74 | vcodeTxt.focus(); 75 | 76 | var submitBtn = dialog.querySelector('#akina-submit'); 77 | 78 | dialog.querySelector('.dialogJclose').addEventListener('click',function(e){ 79 | document.body.removeChild(dialog); 80 | },false); 81 | 82 | dialog.querySelector('.ui_btn_disable').addEventListener('click',function(e){ 83 | document.body.removeChild(dialog); 84 | },false); 85 | 86 | changeCode.addEventListener('click',function(){ 87 | akinaCode.src = ticket + "&" + Date.now(); 88 | },false); 89 | 90 | submitBtn.addEventListener('click',function(){ 91 | 92 | var vcodeText = dialog.querySelector('#akina-text').value; 93 | var url = 'http://pan.baidu.com/api/user/search?user_list=[%22$un%22]'; 94 | 95 | if(vcodeText === ''){ 96 | return tip.textContent = '你不输入车票的话,我是不会发车的哦。'; 97 | } 98 | if(vcodeText.length < 4){ 99 | return tip.textContent = '车票码长度是四位数哦。'; 100 | } 101 | 102 | GM_xmlhttpRequest ({ 103 | method: 'GET', 104 | url: pageData.apiUrl.replace('$un',pageData.un) +"&input=" +vcodeText+ "&vcode=" + vcode, 105 | onload:function(res){ 106 | var res = JSON.parse(res.response); 107 | if( 0 === res.errno){ 108 | tip.textContent = '检票成功,准备发车,请坐好扶稳。'; 109 | location.href = pageData.jump + res.records[0].uk; 110 | } 111 | else{ 112 | vcode = res.vcode; 113 | ticket = res.img; 114 | akinaCode.setAttribute("src",ticket); 115 | if(2157 === res.errno_captcha){ 116 | return tip.textContent = '车票不正确哦~'; 117 | } 118 | tip.textContent = '好像出错了,请重新输入车票~'; 119 | } 120 | } 121 | }); 122 | },false); 123 | 124 | if(-6 === res.errno){ 125 | pageData.timeOut++; 126 | tip.textContent = '司机正在上车,请稍等……'; 127 | dialog.querySelector("#akina-wrap").style.display = "none"; 128 | dialog.querySelector("#akina-btns").style.display = "none"; 129 | if(pageData.timeOut >= 2){ 130 | return tip.textContent = '车辆检修,无法发车(可能是你未登陆)'; 131 | } 132 | var iframe = document.createElement("iframe"); 133 | iframe.src = "http://pan.baidu.com/disk/home#list/path=%2F"; 134 | iframe.width = 0; 135 | iframe.height = 0; 136 | iframe.onload = function(){ 137 | document.body.removeChild(iframe); 138 | document.body.removeChild(dialog); 139 | driving(); 140 | } 141 | document.body.appendChild(iframe); 142 | } 143 | 144 | if(-80 === res.errno){ 145 | tip.textContent = '今天不发车了,明天再来吧。'; 146 | dialog.querySelector("#akina-wrap").style.display = "none"; 147 | dialog.querySelector("#akina-btns").style.display = "none"; 148 | } 149 | }); 150 | } 151 | 152 | })('.l_badge::before {\ 153 | content: "";\ 154 | background: url(\"http://pan.baidu.com/box-static/disk-system/images/favicon.ico");\ 155 | background-size:cover;\ 156 | position: absolute;\ 157 | top: -56px;\ 158 | left: 24px;\ 159 | cursor: pointer;\ 160 | height: 15px;\ 161 | width: 15px;\ 162 | }\ 163 | #akina-text{\ 164 | width: 100px;\ 165 | height: 30px;\ 166 | text-indent: 5px;\ 167 | }\ 168 | #driver-tip{\ 169 | font-size: 20px;\ 170 | }\ 171 | #akina-change{\ 172 | cursor: pointer;\ 173 | }\ 174 | #old-driver{\ 175 | width: 80px;\ 176 | height: 80px;\ 177 | border-radius: 50%;\ 178 | }\ 179 | #old-driver,#akina-code{\ 180 | vertical-align: middle;\ 181 | padding-right: 10px;\ 182 | }','
\ 183 |
\ 184 |
 \ 185 |
\ 186 | 同学,请检票上车\ 187 |
\ 188 |
\ 189 |

要上我的车,先检票哦~

\ 190 |

换一张\ 191 |

\ 192 | \ 195 |
\ 196 |
'); 197 | -------------------------------------------------------------------------------- /Akina/README.md: -------------------------------------------------------------------------------- 1 | # Akina # 2 | 3 | * [【点击安装】](https://userscript.firefoxcn.net/js/Akina.user.js) 4 | * author: Paltoo Young 5 | * 直接访问指定用户网盘 6 | * version: 2.0.4 7 | -------------------------------------------------------------------------------- /Akina/meta.yml: -------------------------------------------------------------------------------- 1 | name: Akina 2 | version: 2.0.4 3 | description: 访问指定用户的百度网盘 4 | include: 5 | - /https?:\/\/tieba\.baidu\.com\/p.*/ 6 | - /https?:\/\/tieba\.baidu\.com\/f.*/ 7 | author: Paltoo Young 8 | grant: 9 | - GM_xmlhttpRequest 10 | namespace: http://tieba.baidu.com/f?kw=firefox 11 | -------------------------------------------------------------------------------- /Automatically_Pause_Video/Automatically_Pause_Video.user.js: -------------------------------------------------------------------------------- 1 | ;(function(UW){ 2 | var sites = { 3 | tudou : function(h){ 4 | h ? UW.playerEx.pause() : 5 | UW.playerEx.play(); 6 | }, 7 | bilibili : function(h){ 8 | h ? UW.bilibili-player.pause() : 9 | UW.bilibili-player.play(); 10 | }, 11 | iqiyi : function(h){ 12 | h ? UW._player.pause() : 13 | UW._player.resume(); 14 | }, 15 | qq : function(h){ 16 | h ? UW.txv.playdata.player.pause() : 17 | UW.txv.playdata.player.play(); 18 | }, 19 | letv : function(h){ 20 | h ? UW.__PLAYER__.pause() : 21 | UW.__PLAYER__.play(); 22 | }, 23 | youku : function(h){ 24 | h ? UW.PlayerPause(1) : 25 | UW.PlayerPause(0); 26 | }, 27 | sohu : function(h){ 28 | h ? UW.sohuHD.getElem('player').pauseVideo() : 29 | UW.sohuHD.getElem('player').playVideo(); 30 | }, 31 | xunlei : function(h){ 32 | h ? UW.G_PLAYER_INSTANCE.pause() : 33 | UW.G_PLAYER_INSTANCE.play(); 34 | }, 35 | kankan : function(h){ 36 | h ? UW.G_PLAYER_INSTANCE.pause() : 37 | UW.G_PLAYER_INSTANCE.play(); 38 | }, 39 | baidu : function(h){ 40 | h ? UW.disk.ui.VideoPlayer.myFlashPlayer.pause() : 41 | UW.disk.ui.VideoPlayer.myFlashPlayer.play(); 42 | }, 43 | "56" : function(h){ 44 | h ? UW.video_player.j2s_setVideoPauseAll() : 45 | UW.video_player.j2s_setVideoResumeAll(); 46 | } 47 | }, 48 | current, 49 | url = window.location.host, 50 | autoPause = { 51 | handleEvent: function(){ 52 | sites[current](document.mozHidden || document.webkitHidden); 53 | }, 54 | init: function(){ 55 | for(var i in sites){ 56 | (url.search(i) !== -1) && (current = i); 57 | } 58 | var change = typeof document.mozHidden !== "undefined" ? 59 | "mozvisibilitychange" : "webkitvisibilitychange"; 60 | current && document.addEventListener(change,this,false); 61 | } 62 | }; 63 | autoPause.init(); 64 | })(unsafeWindow); 65 | -------------------------------------------------------------------------------- /Automatically_Pause_Video/README.md: -------------------------------------------------------------------------------- 1 | # Automatically Pause Video # 2 | 3 | ## 安装 ## 4 | * [【点击安装】](https://userscript.firefoxcn.net/js/Automatically_Pause_Video.user.js) 5 | 6 | ## 功能 ## 7 | * 切换标签页或者最小化浏览器窗口时,自动暂停/播放视频。 8 | 9 | ## 支持列表 ## 10 | * 优酷 11 | * 土豆 12 | * 爱奇艺 13 | * 腾讯视频 14 | * 乐视 15 | * 搜狐 16 | * 迅雷云点播 17 | * 迅雷看看 18 | * 百度云 19 | * 56 20 | * 哔哩哔哩 21 | 22 | ## 脚本信息 ## 23 | * 作者: 网络中二行客 24 | * 版本: 0.2 25 | -------------------------------------------------------------------------------- /Automatically_Pause_Video/meta.yml: -------------------------------------------------------------------------------- 1 | name: Automatically Pause Video 2 | version: 0.2 3 | description: 视频自动播放/暂停 4 | include: 5 | - http://www.tudou.com/* 6 | - http://www.iqiyi.com/* 7 | - http://v.qq.com/* 8 | - http://www.letv.com/* 9 | - http://v.youku.com/* 10 | - http://tv.sohu.com/* 11 | - http://vod.xunlei.com/* 12 | - http://vod.kankan.com/* 13 | - http://pan.baidu.com/play/* 14 | - http://www.56.com/* 15 | - http://www.bilibili.com/* 16 | author: 网络中二行客 17 | grant: none 18 | namespace: http://tieba.baidu.com/f?kw=firefox 19 | -------------------------------------------------------------------------------- /Cache_Manager_for_Tieba_Ueditor/Cache_Manager_for_Tieba_Ueditor.user.js: -------------------------------------------------------------------------------- 1 | // localStorage_key: localStorage_value 2 | // draft-66891597-635137-0: 1449481471315|


3 | // draft-{{user_id}}-{{forum_id}}-{{thread_id}}: {{備份時間,1969年1月1日00:00:00起算的毫秒數}}|{{備份內容}} 4 | 5 | ;(function(w, $, _, reg){ 6 | 7 | 8 | if(w.top !== w.self)return; 9 | // Not in a frame 10 | 11 | var opacity = 0.85; 12 | var opacityImg = 0.4; 13 | var previewTextLength = 36; 14 | 15 | var bBgImg; 16 | var fOption = function(){ 17 | var arr = [['', Number.MAX_VALUE+'|

(请选择一个备份)

']]; 18 | var str = ''; 19 | var key; 20 | for(key in localStorage){ 21 | reg.test(key) && arr.push([key, localStorage.getItem(key)]); 22 | } 23 | arr.sort(function(a, b){ 24 | return Number(b[1].split('|')[0]) - Number(a[1].split('|')[0]); 25 | }); 26 | arr.forEach(function(item){ 27 | str += ''; 30 | }); 31 | return str; 32 | }; 33 | var fCurrentDraftKey = function(){ 34 | var el = $('#cm4tu-ui-select')[0]; 35 | return el.getElementsByTagName('option')[el.selectedIndex].value; 36 | }; 37 | var fInsertBackupText = function(selector){ 38 | var key = fCurrentDraftKey(); 39 | var sHTML = key?localStorage.getItem(key).split('|')[1]:''; 40 | $(selector).html(sHTML); 41 | }; 42 | var fUIupdate = function(){ 43 | $('#cm4tu-ui-select').replaceWith(''); 44 | $("#cm4tu-ui-select").change(function(){fInsertBackupText('#cm4tu-ui-textarea');}); 45 | fInsertBackupText('#cm4tu-ui-textarea'); 46 | }; 47 | var fMain = function($toolbar){ 48 | $('
▤ 暂存
') 49 | .css({ 50 | 'cursor': 'pointer', 51 | 'margin': '0 0 0 8px', 52 | 'color': '#3163B6'}) 53 | .appendTo($toolbar) 54 | .click(function(){ 55 | var $ui = $('#cm4tu-ui'); 56 | if($ui.length === 0){ 57 | $('
'+ 58 | '
Cache Manager for Tieba Ueditor
'+ 59 | '
'+ 60 | '
'+ 61 | '
插入
删除此笔
全部删除
背景图片(夏語遙)
出品
'+ 62 | '
') 63 | .css({ 64 | 'z-index': 60006, 65 | 'cursor': 'move', 66 | 'bottom': '500px', 67 | 'left': '10%', 68 | 'width': '650px', 69 | 'height': '300px', 70 | 'padding': '15px 25px 35px 25px', 71 | 'background': 'rgba(156, 214, 174, '+opacity+')'}) 72 | .appendTo(document.body) 73 | .draggable({ 74 | // 讓可能有捲軸的#cm4tu-ui-textarea元素不可拖動,使捲軸正常工作 75 | start: function(e, ui){ 76 | if ($(e.originalEvent.target).is('#cm4tu-ui-textarea')) 77 | e.preventDefault(); 78 | } 79 | }); 80 | $('#cm4tu-ui-select').change(function(){fInsertBackupText('#cm4tu-ui-textarea');}); 81 | $('#cm4tu-checkbox-bgImg').click(function(){ 82 | localforage.setItem('bBgImg', this.checked, function(value){ 83 | $('#cm4tu-ui').toggleClass('cm4tu-ui-bgImg'); 84 | bBgImg = value; 85 | }); 86 | }); 87 | fInsertBackupText('#cm4tu-ui-textarea'); 88 | $('#cm4tu-btn-insert').click(function(){ 89 | fInsertBackupText('#ueditor_replace'); 90 | $('#cm4tu-ui').hide(); 91 | }); 92 | $('#cm4tu-btn-delete').click(function(){ 93 | if(fCurrentDraftKey()==='')return; 94 | localStorage.removeItem(fCurrentDraftKey()); 95 | fUIupdate(); 96 | }); 97 | $('#cm4tu-btn-clear').click(function(){ 98 | var key; 99 | for(key in localStorage){ 100 | reg.test(key) && localStorage.removeItem(key); 101 | } 102 | fUIupdate(); 103 | $('#cm4tu-ui').hide(); 104 | }); 105 | $('#cm4tu-ui-close').click(function(){ 106 | $('#cm4tu-ui').hide(); 107 | return false; 108 | }); 109 | }else{ 110 | fUIupdate(); 111 | $ui.show(); 112 | } 113 | }); 114 | }; 115 | 116 | 117 | $('head').append($('')); 148 | 149 | localforage.getItem('bBgImg', function(err, value){ 150 | if(null === value){ 151 | bBgImg = true; 152 | localforage.setItem('bBgImg', bBgImg); 153 | }else{ 154 | bBgImg = value; 155 | } 156 | }); 157 | 158 | var observer = new MutationObserver(function(mutations){ 159 | mutations.forEach(function(mutation){ 160 | if($('#cm4tu-tbbtn').length !== 0)return; 161 | var addedNodes = mutation.addedNodes; 162 | var ii = addedNodes.length; 163 | while(ii--){ 164 | var $toolbar = $(addedNodes[ii]).find('.edui-btn-toolbar'); 165 | if($toolbar.length === 0)continue; 166 | observer.disconnect(); 167 | fMain($toolbar); 168 | break; 169 | } 170 | }); 171 | }); 172 | observer.observe($('#tb_rich_poster_container')[0], {childList: true, subtree: true}); 173 | 174 | var $toolbar = $('.edui-btn-toolbar'); 175 | if($('#cm4tu-tbbtn').length === 0 && $toolbar.length !== 0){ 176 | fMain($toolbar); 177 | } 178 | 179 | 180 | })(window, window.$, window._, /^draft/); -------------------------------------------------------------------------------- /Cache_Manager_for_Tieba_Ueditor/README.md: -------------------------------------------------------------------------------- 1 | # Cache Manager for Tieba Ueditor # 2 | 3 | ## 安装 ## 4 | * [【点击安装】](https://userscript.firefoxcn.net/js/Cache_Manager_for_Tieba_Ueditor.user.js) 5 | 6 | ## 功能 ## 7 | * 基於百度贴吧内建之文字备份系统的管理器。 8 | 9 | ## 预览 ## 10 | ![preview picture](https://i.imgur.com/SszCfOo.png) 11 | 12 | ## 脚本信息 ## 13 | * 作者: shyangs 14 | * 版本: 0.7 15 | -------------------------------------------------------------------------------- /Cache_Manager_for_Tieba_Ueditor/meta.yml: -------------------------------------------------------------------------------- 1 | name: Cache Manager for Tieba Ueditor 2 | version: 0.7 3 | description: 基於貼吧內建之文字備份系統的管理器 4 | include: http://tieba.baidu.com/* 5 | author: Shyangs 6 | grant: none 7 | require: http://cdn.jsdelivr.net/localforage/1.2.0/localforage.min.js 8 | icon: http://tb.himg.baidu.com/sys/portrait/item/4daf736879616e6773fc03 9 | namespace: https://github.com/shyangs#cm4tu 10 | license: GPLv3; http://opensource.org/licenses/gpl-3.0.html -------------------------------------------------------------------------------- /Copy_all_links/Copy_all_links.user.js: -------------------------------------------------------------------------------- 1 | GM_registerMenuCommand('复制所有链接', function() { 2 | let result = ''; 3 | Array.prototype.forEach.call(document.querySelectorAll('a'), function(it) { 4 | if (typeof(it.href) === "string" && it.href != "" && !it.href.startsWith('#')) { 5 | result += it.href + "\n"; 6 | } 7 | }); 8 | GM_setClipboard(result, 'text'); 9 | alert('已复制'); 10 | }); -------------------------------------------------------------------------------- /Copy_all_links/README.md: -------------------------------------------------------------------------------- 1 | # Copy all links 2 | 3 | 复制所有链接 4 | 5 | [【点击安装】](https://userscript.firefoxcn.net/js/Copy_all_links.user.js) 6 | 7 | author: 泷涯 8 | -------------------------------------------------------------------------------- /Copy_all_links/meta.yml: -------------------------------------------------------------------------------- 1 | name: Copy all links 2 | version: 1 3 | description: 复制所有链接 4 | include: 5 | - http://*/* 6 | - https://*/* 7 | author: ShuangYa 8 | run-at: document-end 9 | grant: 10 | - GM_setClipboard 11 | - GM_registerMenuCommand 12 | namespace: http://blog.sylingd.com 13 | -------------------------------------------------------------------------------- /Fxxk_CSDN/Fxxk_CSDN.user.js: -------------------------------------------------------------------------------- 1 | GM_addStyle([ 2 | '#csdn-redpack, .toolbar-advert, #overTheScreen, .hljs-button.signin, #articleSearchTip', 3 | '{ display: none !important }', 4 | '#content_views pre, #content_views pre code {', 5 | '-webkit-touch-callout: default !important;', 6 | '-webkit-user-select: auto !important;', 7 | '-khtml-user-select: auto !important;', 8 | '-moz-user-select: auto !important;', 9 | '-ms-user-select: auto !important;', 10 | 'user-select: auto !important;', 11 | '}' 12 | ].join('')); 13 | 14 | const hide = document.querySelector('.hide-article-box'); 15 | if (hide) { 16 | Array.prototype.forEach.call(document.querySelectorAll('.article_content'), it => it.style.height = "auto"); 17 | hide.remove(); 18 | } 19 | 20 | const observer = new MutationObserver(() => { 21 | const loginBox = document.querySelector('.passport-login-container'); 22 | if (loginBox) { 23 | loginBox.remove(); 24 | } 25 | }); 26 | 27 | observer.observe(document.body, { 28 | childList: true, 29 | subtree: true 30 | }); -------------------------------------------------------------------------------- /Fxxk_CSDN/README.md: -------------------------------------------------------------------------------- 1 | # Fxxk CSDN 2 | 3 | CSDN自动展开 4 | 5 | [【点击安装】](https://userscript.firefoxcn.net/js/Fxxk_CSDN.user.js) 6 | 7 | author: 泷涯 8 | -------------------------------------------------------------------------------- /Fxxk_CSDN/meta.yml: -------------------------------------------------------------------------------- 1 | name: Fxxk CSDN 2 | version: 4 3 | description: CSDN辅助工具 4 | include: 5 | - https://blog.csdn.net/* 6 | grant: 7 | - GM_addStyle 8 | author: ShuangYa 9 | run-at: document-end 10 | namespace: http://blog.sylingd.com -------------------------------------------------------------------------------- /GitHub_with_FastGit/GitHub_with_FastGit.user.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | const isDotGit = (url) => 3 | url.indexOf("https://github.com/") === 0 && url.substr(-4) === ".git"; 4 | 5 | const replaceUrl = (url) => { 6 | if (url.indexOf(".fastgit.org") !== -1) return; 7 | // 下载 Release 8 | if (url.indexOf("/releases/download/") !== -1) { 9 | return url.replace("github.com", "download.fastgit.org"); 10 | } 11 | // 下载某分支 12 | if (url.indexOf("/archive/refs/") !== -1) { 13 | return url.replace("github.com", "archive.fastgit.org"); 14 | } 15 | // .git,只识别 https 协议 16 | if (isDotGit(url)) { 17 | return url.replace("github.com", "hub.fastgit.xyz"); 18 | } 19 | // RAW 20 | const rawRegex = /https?:\/\/(.*?)\/(.*?)\/(.*?)\/raw\//; 21 | if (rawRegex.test(url)) { 22 | return url.replace(rawRegex, "https://raw.fastgit.org/$2/$3/"); 23 | } 24 | }; 25 | 26 | const replaceAnchor = (anchor) => { 27 | const newUrl = replaceUrl(anchor.href); 28 | if (newUrl) { 29 | console.log( 30 | "[GitHub with FastGit] 将 " + anchor.href + " 替换为 " + newUrl 31 | ); 32 | anchor.href = newUrl; 33 | } 34 | }; 35 | 36 | const replaceAnchors = (anchors) => 37 | Array.prototype.forEach.call(anchors, replaceAnchor); 38 | 39 | const checkTasks = [ 40 | () => { 41 | // Download ZIP 42 | const modal = document.querySelector( 43 | "div[data-target='get-repo.modal']:not(.x_hacked)" 44 | ); 45 | if (!modal) return; 46 | modal.classList.add("x_hacked"); 47 | replaceAnchors(modal.querySelectorAll("a")); 48 | // HTTPS Clone 49 | const e = Array.from(modal.querySelectorAll("input")).find((x) => 50 | isDotGit(x.value) 51 | ); 52 | if (e) { 53 | const inputGroup = e.parentElement; 54 | inputGroup.classList.add("mt-2"); 55 | const newGroup = inputGroup.cloneNode(true); 56 | const newInput = newGroup.querySelector("input"); 57 | newInput.value = replaceUrl(newInput.value); 58 | const newCopy = newGroup.querySelector("clipboard-copy"); 59 | newCopy.value = replaceUrl(newCopy.value); 60 | inputGroup.parentElement.insertBefore(newGroup, inputGroup); 61 | } 62 | }, 63 | () => { 64 | // Release 下载页面 65 | const cards = document.querySelectorAll( 66 | "div[data-test-selector='release-card']:not(.x_hacked)" 67 | ); 68 | if (cards.length === 0) return; 69 | Array.prototype.forEach.call(cards, (card) => { 70 | card.classList.add("x_hacked"); 71 | replaceAnchors(card.querySelectorAll(".Box-footer ul li a")); 72 | }); 73 | }, 74 | () => { 75 | // Tags 页面 76 | const cards = document.querySelectorAll( 77 | "div[data-test-selector='tag-info-container']:not(.x_hacked)" 78 | ); 79 | if (cards.length === 0) return; 80 | Array.prototype.forEach.call(cards, (card) => { 81 | card.classList.add("x_hacked"); 82 | replaceAnchors(card.querySelectorAll("ul li a")); 83 | }); 84 | }, 85 | () => { 86 | // Raw 按钮 87 | const raw = document.querySelector("#raw-url"); 88 | if (!raw) return; 89 | replaceAnchor(raw); 90 | }, 91 | ]; 92 | 93 | const doCheck = () => checkTasks.forEach((x) => x()); 94 | 95 | function main() { 96 | const observer = new MutationObserver(doCheck); 97 | observer.observe(document.body, { 98 | childList: true, 99 | attributes: true, 100 | subtree: true, 101 | }); 102 | doCheck(); 103 | } 104 | 105 | main(); 106 | })(); 107 | -------------------------------------------------------------------------------- /GitHub_with_FastGit/README.md: -------------------------------------------------------------------------------- 1 | # GitHub with FastGit 2 | 3 | 自动替换 GitHub 页面上的相关链接为 FastGit。 4 | 5 | 注意:此脚本仅替换链接,[FastGit 服务](https://doc.fastgit.org/zh-cn/)非脚本作者维护。 6 | 7 | [【点击安装】](https://userscript.firefoxcn.net/js/GitHub_with_FastGit.user.js) 8 | 9 | author: 泷涯 10 | -------------------------------------------------------------------------------- /GitHub_with_FastGit/meta.yml: -------------------------------------------------------------------------------- 1 | name: GitHub with FastGit 2 | version: 2 3 | description: 自动替换 GitHub 页面上的相关链接为 FastGit 4 | include: 5 | - https://github.com/* 6 | author: ShuangYa 7 | run-at: document-end 8 | icon: https://github.githubassets.com/favicons/favicon.png 9 | grant: 10 | - unsafeWindow 11 | namespace: blog.sylingd.com 12 | -------------------------------------------------------------------------------- /LoadBT_batch_download/LoadBT_batch_download.user.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | let _fetchQueueCount = 0; 3 | const _fetchQueueArr = []; 4 | const _fetchQueueRun = async () => { 5 | const item = _fetchQueueArr.shift(); 6 | if (!item) return; 7 | fetch(item.url) 8 | .then((x) => x.json()) 9 | .then(item.resolve) 10 | .catch(item.reject) 11 | .finally(() => { 12 | _fetchQueueCount--; 13 | _fetchQueueRun(); 14 | }); 15 | }; 16 | const fetchQueue = (url) => { 17 | return new Promise((resolve, reject) => { 18 | _fetchQueueArr.push({ url, resolve, reject }); 19 | if (_fetchQueueCount < 3) { 20 | _fetchQueueCount++; 21 | _fetchQueueRun(); 22 | } 23 | }); 24 | }; 25 | 26 | const waitUntil = (cb) => { 27 | return new Promise((resolve) => { 28 | const check = () => { 29 | setTimeout(() => { 30 | if (cb()) resolve(); 31 | else check(); 32 | }, 300); 33 | }; 34 | check(); 35 | }); 36 | }; 37 | 38 | let tipContainer = null; 39 | let tipProgressDisplay = ""; 40 | function tip(text, progress) { 41 | if (!tipContainer) { 42 | tipContainer = document.createElement("div"); 43 | tipContainer.style.position = "fixed"; 44 | tipContainer.style.zIndex = "99999"; 45 | tipContainer.style.textAlign = "center"; 46 | tipContainer.style.bottom = "20px"; 47 | tipContainer.style.left = "50%"; 48 | tipContainer.style.width = "400px"; 49 | tipContainer.style.maxWidth = "100%"; 50 | tipContainer.style.transform = "translateX(-50%)"; 51 | tipContainer.style.border = "1px solid #00000026"; 52 | tipContainer.style.borderRadius = "0.25rem"; 53 | tipContainer.style.padding = "12px"; 54 | tipContainer.style.background = "#fff"; 55 | tipContainer.innerHTML = "
"; 56 | document.body.appendChild(tipContainer); 57 | tipProgressDisplay = getComputedStyle(tipContainer.querySelector(".progress")).display; 58 | } 59 | tipContainer.querySelector(".progress").style.display = typeof progress === "undefined" ? "none" : tipProgressDisplay; 60 | tipContainer.querySelector(".desc").innerText = text; 61 | if (typeof progress !== "undefined") { 62 | tipContainer.querySelector(".progress-bar").style.width = progress * 100 + "%"; 63 | } 64 | } 65 | 66 | function hideTip() { 67 | tipContainer.remove(); 68 | tipContainer = null; 69 | } 70 | 71 | async function onItemClick(rootId) { 72 | if (rootId == -1) return; 73 | tip("正在获取文件列表"); 74 | const result = []; 75 | let totalCount = 0; 76 | // 更新进度 77 | const updateProgress = () => tip("获取下载地址 " + result.length + " / " + totalCount, result.length / totalCount); 78 | const addFolder = async (folderId) => { 79 | const { files } = await fetchQueue("/files/" + folderId); 80 | totalCount += files.filter((x) => !x.is_directory).length; 81 | for (const file of files) { 82 | // 处理多级文件夹 83 | if (file.is_directory) { 84 | await addFolder(file.id); 85 | } else { 86 | const u = await fetchQueue("/download/" + file.id); 87 | result.push(u.url); 88 | } 89 | updateProgress(); 90 | } 91 | }; 92 | await addFolder(rootId); 93 | 94 | hideTip(); 95 | GM_setClipboard(result.join("\n")); 96 | setTimeout(() => alert("下载地址已复制到剪贴板")); 97 | } 98 | 99 | async function run() { 100 | tip("正在获取文件夹"); 101 | // 加载文件夹列表 102 | const folders = (await fetchQueue("/files/0")).files; 103 | hideTip(); 104 | if (folders.length === 0) { 105 | alert("没有可下载的文件夹"); 106 | return; 107 | } 108 | 109 | folders.push({ 110 | id: -1, 111 | name: "取消" 112 | }); 113 | 114 | const folderList = document.createElement("div"); 115 | folderList.className = "dropdown-menu dropdown-menu-right show"; 116 | folderList.style.position = "fixed"; 117 | folderList.style.zIndex = "99999"; 118 | folderList.style.top = "50%"; 119 | folderList.style.left = "50%"; 120 | folderList.style.width = "400px"; 121 | folderList.style.maxWidth = "100%"; 122 | folderList.style.transform = "translate(-50%, -50%)"; 123 | 124 | folders.forEach((folder) => { 125 | const item = document.createElement("span"); 126 | item.classList = "dropdown-item"; 127 | item.innerText = folder.name; 128 | item.style.cursor = "pointer"; 129 | item.setAttribute("data-id", folder.id); 130 | 131 | item.addEventListener("click", () => { 132 | onItemClick(folder.id); 133 | folderList.remove(); 134 | }); 135 | 136 | folderList.appendChild(item); 137 | }); 138 | 139 | document.body.appendChild(folderList); 140 | } 141 | 142 | async function main() { 143 | await waitUntil(() => !!document.getElementById("torrent-file")); 144 | const container = document.getElementById("torrent-file").parentElement; 145 | const b = document.createElement("button"); 146 | b.type = "button"; 147 | b.className = "btn btn-primary mb-2 ml-2 d-none d-sm-block"; 148 | b.innerHTML = "批量下载"; 149 | b.addEventListener("click", run); 150 | container.appendChild(b); 151 | } 152 | 153 | main(); 154 | })(); 155 | -------------------------------------------------------------------------------- /LoadBT_batch_download/README.md: -------------------------------------------------------------------------------- 1 | # LoadBT batch download 2 | 3 | LoadBT 批量复制下载链接,适用网站:https://www.loadbt.com/ 4 | 5 | [【点击安装】](https://userscript.firefoxcn.net/js/LoadBT_batch_download.user.js) 6 | 7 | author: 泷涯 8 | -------------------------------------------------------------------------------- /LoadBT_batch_download/meta.yml: -------------------------------------------------------------------------------- 1 | name: LoadBT 批量下载 2 | version: 5 3 | description: LoadBT 批量复制下载链接 4 | include: 5 | - https://www.loadbt.com/files* 6 | author: ShuangYa 7 | run-at: document-end 8 | icon: https://www.loadbt.com/images/icons/favicon.ico 9 | grant: 10 | - unsafeWindow 11 | - GM_setClipboard 12 | namespace: http://blog.sylingd.com 13 | -------------------------------------------------------------------------------- /Post_Del_Robot/PostDelRobot.user.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | //贴子列表 3 | var tielist = [], 4 | meinput = '', 5 | /* 6 | * 检查一些运行脚本的必要条件 7 | * @return boolean 8 | */ 9 | checkMe = function() { 10 | //检查是否在贴子列表页 11 | if (typeof(unsafeWindow.PageData) === 'undefined') { 12 | alert('PageData未定义,脚本无法运行'); 13 | return false; 14 | } 15 | //检查是否为吧务,通过右侧“进入吧务后台”的按钮检查 16 | if ($('.tbAdminManage').length <= 0 && $('.manager_btn') <= 0) { 17 | alert('您不是本吧吧务'); 18 | return false; 19 | } 20 | return true; 21 | }, 22 | /* 23 | * 添加到删帖列表 24 | * @param string text 搜索结果页的HTML 25 | * @return boolean 26 | */ 27 | addToList = function(text) { 28 | //楼中楼部分 29 | //回贴部分 30 | var reply = text.match(/\/p\/(\d+)\?pid=(\d+)/gi); 31 | if (reply === null) reply = []; 32 | //替换已匹配内容,避免重复匹配 33 | text = text.replace(/\/p\/(\d+)\?pid=(\d+)/gi, ''); 34 | var i, one; 35 | for (i = 0; i < reply.length; i++) { 36 | one = reply[i].match(/\/p\/(\d+)\?pid=(\d+)/i); 37 | tielist.push({ 38 | "type": 1, 39 | "tid": one[1], 40 | "pid": one[2] 41 | }); 42 | } 43 | //主题部分 44 | var post = text.match(/\/p\/(\d+)/gi); 45 | if (post === null) post = []; 46 | for (i = 0; i < post.length; i++) { 47 | one = post[i].match(/\/p\/(\d+)/i); 48 | tielist.push({ 49 | "type": 2, 50 | "tid": one[1] 51 | }); 52 | } 53 | //返回是否继续抓取下一页 54 | return (reply.length > 0 || post.length > 0); 55 | }, 56 | /* 57 | * 通过用户ID进行删帖 58 | * @param string user 用户ID 59 | * @param int page 页码 60 | */ 61 | delByUser = function(user, page) { 62 | logResult('正在获取贴子列表(第' + page + '页)'); 63 | GM_xmlhttpRequest({ 64 | "method": 'GET', 65 | "url": 'http://tieba.baidu.com/f/search/ures?ie=utf-8&kw=' + encodeURIComponent(unsafeWindow.PageData.forum.name) + '&qw=&rn=10&un=' + encodeURIComponent(user) + '&only_thread=&sm=1&sd=&ed=&pn=' + page, 66 | "onload": function(response) { 67 | //匹配出搜索结果的所有贴子 68 | var result = response.responseText; 69 | if (addToList(result) && page < 5) { //防止因为数量过大造成的卡顿 70 | //奇葩百度 71 | setTimeout(function() { 72 | delByUser(user, page + 1); //自调用,继续获取 73 | }, 2500); 74 | } else { //获取完毕,开始删帖 75 | logResult('获取贴子列表完成!'); 76 | if (tielist.length > 0) { 77 | delByList(0); 78 | } else { 79 | logResult('没有任何贴子!'); 80 | Stop(); 81 | } 82 | } 83 | } 84 | }); 85 | }, 86 | /* 87 | * 通过关键字进行删帖 88 | * @param string keyword 关键字 89 | * @param int page 页码 90 | */ 91 | delByKeyword = function(keyword, page) { 92 | logResult('正在获取贴子列表(第' + page + '页)'); 93 | GM_xmlhttpRequest({ 94 | "method": 'GET', 95 | "url": 'http://tieba.baidu.com/f/search/res?kw=' + encodeURIComponent(unsafeWindow.PageData.forum.name) + '&qw=' + encodeURIComponent(keyword) + '&rn=10&un=&sm=1&pn=' + page, 96 | "onload": function(response) { 97 | //匹配出搜索结果的所有贴子 98 | var result = response.responseText; 99 | if (addToList(result) && page < 5) { //防止因为数量过大造成的卡顿 100 | //奇葩百度 101 | setTimeout(function() { 102 | delByKeyword(keyword, page + 1); //自调用,继续获取 103 | }, 2500); 104 | } else { //获取完毕,开始删帖 105 | logResult('获取贴子列表完成!'); 106 | if (tielist.length > 0) { 107 | delByList(0); 108 | } else { 109 | logResult('没有任何贴子!'); 110 | Stop(); 111 | } 112 | } 113 | } 114 | }); 115 | }, 116 | /* 117 | * 按已建立的列表删帖 118 | * @param int num 对应列表中的Key 119 | */ 120 | delByList = function(num) { 121 | var fid = unsafeWindow.PageData.forum.id, 122 | kw = unsafeWindow.PageData.forum.name, 123 | leng = tielist.length - 1, 124 | me = tielist[num], 125 | postdata = { 126 | "commit_fr": "pb", 127 | "ie": "utf-8", 128 | "kw": kw, 129 | "fid": fid, 130 | "tid": me.tid, 131 | "tbs": unsafeWindow.PageData.tbs 132 | }; 133 | if (me.type == 1) { //回贴 134 | postdata.pid = me.pid; 135 | } else if (me.type == 2) { //主题 136 | } 137 | //GM的ajax不知道为什么不能用,就用jQuery的吧 138 | $.ajax({ 139 | "type": 'POST', 140 | "data": postdata, 141 | "url": '/f/commit/post/delete', 142 | "success": function(result) { 143 | // 确认删帖结果 144 | if (typeof(result) === 'string') { 145 | result = JSON.parse(result); 146 | } 147 | if (result.err_code == 0) { 148 | if (me.type == 1) { //回贴 149 | logResult('删除贴子成功!主题ID:' + me.tid + ',贴子ID:' + me.pid); 150 | } else if (me.type == 2) { //主题 151 | logResult('删除贴子成功!主题ID:' + me.tid); 152 | } 153 | if (num != leng) { //调用自身 154 | delByList(num + 1); 155 | } 156 | } else { 157 | if (me.type == 1) { //回贴 158 | logResult('删除贴子失败!查看'); 159 | } else if (me.type == 2) { //主题 160 | logResult('删除贴子失败!查看'); 161 | } 162 | if (result.err_code == '224011') { 163 | logResult('触发验证码,停止删帖(解决办法:刷新当前网页,或手动删除一个贴子)'); 164 | GM_setClipboard(meinput); 165 | logResult('本次用户ID/关键词已经复制到剪贴板'); 166 | Stop(); 167 | $('#sy_del_reload').show(); 168 | } else { 169 | if (num != leng) { //调用自身 170 | delByList(num + 1); 171 | } else { 172 | Stop(); 173 | } 174 | } 175 | } 176 | } 177 | }); 178 | }, 179 | /* 180 | * 记录结果 181 | * @param string info 信息 182 | */ 183 | logResult = function(info) { 184 | var e = $('#sy_del_info'); 185 | e.append('

' + info + '

'); 186 | }, 187 | /* 188 | * 结束函数 189 | */ 190 | Stop = function() { 191 | var btn = $('#sy_del_close'); 192 | btn.bind('click', 193 | function() { 194 | $('#sy_del_dialog').hide(); 195 | $('#sy_del_full').hide(); 196 | }); 197 | btn.find('em').html('关闭'); 198 | tielist = []; 199 | logResult('已结束运行'); 200 | }, 201 | /* 202 | * 初始函数 203 | * @param int type 类型,1为按用户ID 204 | */ 205 | Start = function(type) { 206 | var input; 207 | if (!checkMe()) return; 208 | //显示输入框 209 | if (type == 1) input = window.prompt('请输入用户ID'); 210 | else if (type == 2) input = window.prompt('请输入关键词(不支持通配符、正则表达式)'); 211 | else return; 212 | if (input) { 213 | //霸屏 214 | $('#sy_del_close').unbind('click'); 215 | $('#sy_del_close').find('em').html('请稍候'); 216 | $('#sy_del_reload').hide(); 217 | $('#sy_del_dialog').show(); 218 | $('#sy_del_full').show(); 219 | meinput = input; 220 | if (type == 1) delByUser(input, 1); 221 | else if (type == 2) delByKeyword(input, 1); 222 | else return; 223 | } 224 | }, 225 | Start_user = function() { 226 | Start(1); 227 | }, 228 | Start_keyword = function() { 229 | Start(2); 230 | }; 231 | //注册菜单 232 | if (window.location.href.match(/f\?(.*?)kw=/) !== null) { //确认是在贴子列表页 233 | GM_addStyle('#sy_del_full{position:fixed;background:rgba(0,0,0,0.6);width:100%;height:100%;top:0;left:0;color:white;z-index:99998;}#sy_del_dialog{position:fixed;z-index:99999;width:460px;height:400px;top:10px;left:50%;margin-left:-200px;}#sy_del_info{height:295px;padding:10px;overflow-y:scroll;}'); 234 | $('body').append('
删帖机器人
'); 235 | $('#sy_del_dialog').hide(); 236 | $('#sy_del_full').hide(); 237 | GM_registerMenuCommand('批量删帖(按用户ID)', Start_user); 238 | GM_registerMenuCommand('批量删帖(按关键词)', Start_keyword); 239 | } 240 | })(); -------------------------------------------------------------------------------- /Post_Del_Robot/README.md: -------------------------------------------------------------------------------- 1 | # Post Del Robot 2 | 3 | **删帖机器人** 4 | 5 | ## 安装 6 | 7 | * [【点击安装】](https://userscript.firefoxcn.net/js/PostDelRobot.user.js) 8 | 9 | ## 功能 10 | 11 | * 按用户ID或关键词批量删帖 12 | 13 | * 注意:一次性删除有最大数量限制,可分多次使用 14 | 15 | ## 脚本信息 16 | 17 | * 作者: 泷涯 18 | 19 | * 版本: 9 20 | 21 | ## 更新记录 22 | 23 | ### 11 24 | 25 | * 修复由于jQuery自动解析JSON造成的语法错误 26 | 27 | * 支持HTTPS页面 28 | 29 | ### 10 30 | 31 | * 修复几处语法问题 32 | 33 | ### 9 34 | 35 | * 修复由于百度缓存导致获取列表不正常的BUG 36 | 37 | * 修复按钮样式BUG 38 | 39 | ### 8 40 | 41 | * 修复由于贴吧改版导致脚本无法使用的问题 42 | 43 | ### 7 44 | 45 | * 增加对新版贴吧的检测机制 46 | 47 | ### 6 48 | 49 | * 修复一些Bug 50 | 51 | * 优化一点功能 52 | 53 | ### 5 54 | 55 | * 修复关键字删帖的BUG 56 | 57 | * 美化界面 58 | 59 | ### 4 60 | 61 | * 修复不能使用关键字删帖的BUG 62 | 63 | * 新增触发验证码的提示 64 | 65 | ### 3 66 | 67 | * 修复了GM的兼容 68 | 69 | ### 2 70 | 71 | * 修改了一些判断机制(吧务判断和贴子列表页的判断) 72 | 73 | * 增加对多次运行的判断 74 | -------------------------------------------------------------------------------- /Post_Del_Robot/meta.yml: -------------------------------------------------------------------------------- 1 | name: Post Del Robot 2 | version: 11 3 | description: 贴吧删帖机器人 4 | include: 5 | - http://tieba.baidu.com/f?* 6 | - https://tieba.baidu.com/f?* 7 | author: ShuangYa 8 | run-at: document-end 9 | grant: 10 | - GM_xmlhttpRequest 11 | - GM_registerMenuCommand 12 | - GM_addStyle 13 | - GM_setClipboard 14 | run-at: document-end 15 | require: http://libs.baidu.com/jquery/1.11.1/jquery.min.js 16 | namespace: http://blog.sylingd.com 17 | -------------------------------------------------------------------------------- /Putian_Warning/.gitignore: -------------------------------------------------------------------------------- 1 | *.user.js 2 | *.meta.js -------------------------------------------------------------------------------- /Putian_Warning/README.md: -------------------------------------------------------------------------------- 1 | # Putian Warning 2 | 3 | **莆田系医院提醒脚本** 4 | 5 | ## 安装 6 | 7 | * [【点击安装】](https://userscript.firefoxcn.net/js/PostDelRobot.user.js) 8 | 9 | ## 功能 10 | 11 | * 在访问莆田系医院的网站时,增加醒目的提醒 12 | 13 | ## 脚本信息 14 | 15 | * 作者: 泷涯 16 | 17 | * 版本: 3 18 | 19 | ## 更新记录 20 | 21 | ### 3 22 | 23 | 更新列表 24 | 25 | ### 2 26 | 27 | * 修复一些已知BUG 28 | -------------------------------------------------------------------------------- /Putian_Warning/fetchList.js: -------------------------------------------------------------------------------- 1 | const fetch = require('node-fetch'); 2 | const { writeFile } = require('fs-extra'); 3 | const { resolve } = require('path'); 4 | const url = 'https://raw.githubusercontent.com/open-power-workgroup/Hospital/master/resource/API_resource/hospital_list.json'; 5 | 6 | const getDomain = (url) => { 7 | let result = url; 8 | result = result.replace(/^https?:\/\//, ''); 9 | if (result.includes('/')) { 10 | result = result.substr(0, result.indexOf('/')); 11 | } 12 | return result; 13 | } 14 | 15 | const main = async function() { 16 | const res = await fetch(url); 17 | const json = await res.json(); 18 | 19 | const result = {}; 20 | 21 | Object.values(json).forEach(city => { 22 | Object.entries(city).forEach(hospitalArr => { 23 | const name = hospitalArr[0]; 24 | const hospital = hospitalArr[1]; 25 | if (typeof hospital['网址'] === 'undefined') { 26 | return; 27 | } 28 | 29 | const urls = hospital['网址']; 30 | delete hospital['网址']; 31 | // 先转换其他部分内容 32 | const text = [ 33 | "名称:" + name 34 | ]; 35 | Object.entries(hospital).forEach(it => { 36 | text.push(`${it[0]}: ${it[1].join("\n")}`); 37 | }); 38 | 39 | // 网址 40 | urls.forEach(url => { 41 | result[getDomain(url)] = text; 42 | }); 43 | }); 44 | }); 45 | 46 | await writeFile(resolve(__dirname, 'src/list.json'), JSON.stringify(result, undefined, 2), { 47 | encoding: 'UTF-8' 48 | }); 49 | } 50 | 51 | main(); -------------------------------------------------------------------------------- /Putian_Warning/meta.yml: -------------------------------------------------------------------------------- 1 | name: Putian Warning 2 | description: 莆田系医院网站提醒 3 | include: 4 | - http://*/* 5 | - https://*/* 6 | author: ShuangYa 7 | run-at: document-end 8 | grant: 9 | - GM_addStyle 10 | namespace: http://blog.sylingd.com 11 | -------------------------------------------------------------------------------- /Putian_Warning/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fxb/userscript-putian-warning", 3 | "version": "6", 4 | "main": "index.js", 5 | "author": "ShuangYa", 6 | "license": "MIT" 7 | } 8 | -------------------------------------------------------------------------------- /Putian_Warning/src/index.css: -------------------------------------------------------------------------------- 1 | .banner { 2 | display: block !important; 3 | width: 100%; 4 | height: 100px; 5 | line-height: 100px; 6 | position: fixed; 7 | top: 0; 8 | left: 0; 9 | z-index: 99999; 10 | font-size: 50px; 11 | text-align: center; 12 | color: white; 13 | background: red; 14 | } 15 | .banner > * { 16 | vertical-align: middle; 17 | } 18 | .banner > button { 19 | font-size: 30px; 20 | } 21 | .info { 22 | width: 400px; 23 | position: fixed; 24 | display: none; 25 | top: 150px; 26 | left: 50%; 27 | z-index: 99999; 28 | font-size: 15px; 29 | margin: 0 -200px; 30 | background: #FFF; 31 | color: #000; 32 | border: 1px solid #000; 33 | padding: 20px; 34 | } 35 | .info p { 36 | margin-bottom: 5px; 37 | } 38 | .info a { 39 | color: #337ab7; 40 | text-decoration: underline; 41 | } -------------------------------------------------------------------------------- /Putian_Warning/src/index.ts: -------------------------------------------------------------------------------- 1 | import { locals } from './index.css'; 2 | 3 | const list: { [x: string]: [] } = require('./list.json'); 4 | 5 | const domain = unsafeWindow.location.hostname; 6 | 7 | 8 | if (unsafeWindow.location.href !== '' && typeof list[domain] !== 'undefined') { 9 | //顶部banner 10 | const banner = document.createElement('div'); 11 | banner.className = locals.banner; 12 | const banner_btn = document.createElement('button'); 13 | banner_btn.innerHTML = '查看详情'; 14 | const banner_text = document.createElement('span'); 15 | banner_text.innerHTML = '此网站可能为莆田系医院'; 16 | banner.appendChild(banner_text); 17 | banner.appendChild(banner_btn); 18 | document.body.appendChild(banner); 19 | //事件 20 | banner_btn.addEventListener('click', () => { 21 | info.style.display = 'block'; 22 | }); 23 | 24 | //详情框 25 | const info = document.createElement('div'); 26 | info.className = locals.info; 27 | for (const k in list[domain]) { 28 | const childEle = document.createElement('p'); 29 | childEle.innerText = list[domain][k]; 30 | info.appendChild(childEle); 31 | } 32 | //详情框的一些按钮 33 | const info_text = document.createElement('p'); 34 | info_text.innerHTML = '信息来自Hospital项目,您可以参与或者提出异议'; 35 | info.appendChild(info_text); 36 | const info_close = document.createElement('p'); 37 | const info_close_btn = document.createElement('button'); 38 | info_close_btn.innerHTML = '关闭'; 39 | info_close.appendChild(info_close_btn); 40 | info.appendChild(info_close); 41 | document.body.appendChild(info); 42 | //事件 43 | info_close_btn.addEventListener('click', () => { 44 | info.style.display = 'none'; 45 | }); 46 | } -------------------------------------------------------------------------------- /Putian_Warning/src/list.json: -------------------------------------------------------------------------------- 1 | { 2 | "www.lztlyy.com": [ 3 | "名称:兰州天伦不孕症医院" 4 | ], 5 | "www.scmylike.com": [ 6 | "名称:成都美莱医学美容医院", 7 | "证据: 参见文末美莱医疗美容连锁医院集团条目", 8 | "电话: 02868268888", 9 | "地址: 成都市青华路31号杜甫草堂北大门旁" 10 | ], 11 | "www.whablnk.com": [ 12 | "名称:武汉阿波罗男科医院", 13 | "电话: 02785888669" 14 | ], 15 | "www.xdmnyy.com": [ 16 | "名称:武汉现代泌尿外科医院", 17 | "电话: 02782886888" 18 | ], 19 | "www.whnanke.net": [ 20 | "名称:武汉博大男科医院", 21 | "电话: 02785896666" 22 | ], 23 | "www.whmsnk.com": [ 24 | "名称:武汉名仕泌尿外科医院", 25 | "电话: 02788328080" 26 | ], 27 | "www.snvzi.com": [ 28 | "名称:湖州阳光女子医院" 29 | ], 30 | "www.hznvzi.com": [ 31 | "名称:湖州阳光女子医院" 32 | ], 33 | "www.fzhmzx.com": [ 34 | "名称:福州华美美莱整形医院", 35 | "证据: 参见文末美莱医疗美容连锁医院集团条目", 36 | "电话: 059122966666", 37 | "地址: 福州市晋安区塔头路177号" 38 | ], 39 | "www.chinesefamousdoctor.org": [ 40 | "名称:中华名医协会未完待续", 41 | "相关资料:: 工信部备案信息主办单位名称北京世华天地网络技术服务有限公司主办单位性质企业网站名称名医协会网站负责人姓名周建网站备案许可号京ICP证000045号23", 42 | "电话: 15911030069\n香港8522915.7428", 43 | "地址: 香港铜锣湾轩尼诗道488号轩尼诗大厦10M永久地址" 44 | ], 45 | "www.cnzhongliu.com": [ 46 | "名称:中国肿瘤网未完待续" 47 | ], 48 | "shmdy2015.tecenet.com": [ 49 | "名称:上海美迪亚医院投资集团有限公司", 50 | "证据: 上海工商行政管理局企业注册登记信息公开(http://www.sgs.gov.cn/lz/etpsInfo.do?method=index)查询显示上海美迪亚医院投资集团有限公司的法定代表人陈国兴\n陈国兴相关信息莆田中国健康产业总会(http://www.99169916.com/content/899171989538.html)\n公司主页显示美迪亚医院集团目前投资的主要项目有中美合资上海美迪西生物医药有限公司北京中研东方国际医学研究院上海杭州深圳武汉长沙无锡昆山温州宁波河南新郑等地设立大型医疗机构拥有上海虹桥医院上海健桥医院上海新虹桥医院昆山虹桥医院无锡虹桥医院杭州虹桥医院杭州天目山医院深圳建国泌尿外科医院等三十多家医院可依据以上名单确认各地区医院为莆田系", 51 | "地址: 上海市闵行区吴中路686弄D座10楼" 52 | ], 53 | "www.mylike.com": [ 54 | "名称:美莱医疗美容连锁医院集团", 55 | "证据: 工信部备案信息:上海美莱投资管理有限公司网站负责人陈国付备案号沪ICP备12022412号6\n全国企业信息公示系统(https://m.imgur.com/I3rbbxh)显示上海美莱投资管理有限公司法定代表人陈金秀自然人股东为陈金秀和詹建昇\n谷歌缓存页面(https://webcache.googleusercontent.com/search?q=cache:D5otmZNewMJ:www.ptshsh.com/index.php%3Fm%3Dcms%26q%3Dview%26id%3D33cd=1hl=enct=clnklr=langen%7ClangzhCN%7ClangzhTW)显示这是谷歌对上海市莆田商会(http://www.ptshsh.com/)网页的缓存陈金秀是上海市莆田商会名誉会长籍贯秀屿区东庄镇\n以上证据确认完毕可按照美莱医疗美容医院集团的官网确认其下属各地医院属于莆田系无需继续查证", 56 | "电话: 4008980000" 57 | ], 58 | "www.songhongen.com.cn": [ 59 | "名称:中华肿瘤康复网(中医肿瘤康复咨询网)未完待续", 60 | "相关资料: 工信部备案信息主办单位个人并非企业网站负责人宋洪恩备案许可号豫ICP备09017762号1网站名称健康康复网和显示名称中医肿瘤康复咨询网明显不符而百度搜索显示为中华肿瘤康复网\n网站显示汇款信息(http://www.songhongen.com.cn/order/index.htm)", 61 | "电话: 037168717218\n037168715173\n13803893403\n13803893402", 62 | "地址: 郑州市航海中路95号(台胞小区24号)" 63 | ], 64 | "www.whdsfk.com": [ 65 | "名称:武汉都市妇产医院", 66 | "电话: 02785268003" 67 | ], 68 | "www.87189999.com": [ 69 | "名称:武汉真爱妇产医院", 70 | "电话: 02787189999" 71 | ], 72 | "www.whbjyy.com": [ 73 | "名称:武汉百佳医院", 74 | "电话: 02766666333" 75 | ], 76 | "www.mlyfc.net": [ 77 | "名称:武汉玛利亚妇产科医院", 78 | "电话: 02788858965" 79 | ], 80 | "www.lr120.com": [ 81 | "名称:合肥丽人妇科医院", 82 | "电话: 055163653311", 83 | "地址: 安徽省合肥市包河区屯溪路349号" 84 | ], 85 | "3612999.com": [ 86 | "名称:合肥丽人妇科医院", 87 | "电话: 055163653311", 88 | "地址: 安徽省合肥市包河区屯溪路349号" 89 | ], 90 | "www.hfneweye.com": [ 91 | "名称:合肥新视界眼科医院", 92 | "电话: 4006651616", 93 | "地址: 合肥包河区徽州大道689号(徽州大道与九华山路交口往南300米)" 94 | ], 95 | "www.qdszyy.com": [ 96 | "名称:启东市中医院", 97 | "电话: 051383212540", 98 | "地址: 江苏省启东市紫薇中路458号" 99 | ], 100 | "www.fuke120.cn": [ 101 | "名称:北京艾丽斯妇科医院", 102 | "用户反馈: 亲测医院收费不透明治疗过程中各种乱开检查的单子手术价格为普通医院价格10倍左右最后医院连检查的结果都不会给你有类似的受害者http://money.163.com/16/0329/09/BJAIJCRK00253B0H.html(http://money.163.com/16/0329/09/BJAIJCRK00253B0H.html)http://ask.yaolan.com/question/15112409310913796432.html(http://ask.yaolan.com/question/15112409310913796432.html)" 103 | ], 104 | "www.bjmylike.com": [ 105 | "名称:北京美莱医疗美容医院(所属为北京美莱医疗美容医院有限公司美莱医疗美容(连锁)医院集团http://www.mylike.com/)", 106 | "电话: 01056935111", 107 | "地址: 北京市朝阳区朝阳门外大街227号" 108 | ], 109 | "www.bjjsyy.com": [ 110 | "名称:北京京顺医院", 111 | "电话: 01069423999" 112 | ], 113 | "www.whmdgc.com": [ 114 | "名称:武汉明德肛肠医院", 115 | "电话: 02787585666" 116 | ], 117 | "www.39bsw.com": [ 118 | "名称:武汉博仕肛肠肛肠医院", 119 | "电话: 02788855999" 120 | ], 121 | "www.hbjieshi.com": [ 122 | "名称:武汉京都结石病医院", 123 | "电话: 02788616718" 124 | ], 125 | "www.whrenan.com": [ 126 | "名称:武汉仁安眼耳鼻喉医院", 127 | "电话: 4006027669" 128 | ], 129 | "www.gytsm.com": [ 130 | "名称:武汉国医堂", 131 | "电话: 02787639188" 132 | ], 133 | "www.hzjl120.com": [ 134 | "名称:杭州萧山九龙男科医院" 135 | ], 136 | "www.nkxsjl.com": [ 137 | "名称:杭州萧山九龙男科医院" 138 | ], 139 | "www.0571tongji.com": [ 140 | "名称:杭州同济医院" 141 | ], 142 | "www.hztjh.com": [ 143 | "名称:杭州同济医院" 144 | ], 145 | "www.hzgryy.com": [ 146 | "名称:杭州广仁医院" 147 | ], 148 | "www.zjbybyw.com": [ 149 | "名称:杭州广仁医院" 150 | ], 151 | "www.hzmylike.com": [ 152 | "名称:杭州美莱医疗美容医院", 153 | "证据: 参见文末美莱医疗美容连锁医院集团条目", 154 | "电话: 4000085188", 155 | "地址: 杭州市西湖区莫干山路333号" 156 | ], 157 | "www.hzhqyy.com": [ 158 | "名称:杭州虹桥医院" 159 | ], 160 | "www.hzabl.com": [ 161 | "名称:杭州阿波罗男子医院" 162 | ], 163 | "www.hzabl.cn": [ 164 | "名称:杭州阿波罗男子医院" 165 | ], 166 | "www.hzmly.com": [ 167 | "名称:杭州玛莉亚妇女医院" 168 | ], 169 | "www.97120.cn": [ 170 | "名称:杭州天目山妇产医院(杭州天目山医院)", 171 | "证据: 确认莆田系参见文末上海美迪亚医院投资集团有限公司条目\n天涯帖子:杭州天目山医院你到底黑不黑(http://bbs.tianya.cn/postfree19291961.shtml)", 172 | "地址: 杭州市天目山路319号" 173 | ], 174 | "www.tmsfk.com": [ 175 | "名称:杭州天目山妇产医院(杭州天目山医院)", 176 | "证据: 确认莆田系参见文末上海美迪亚医院投资集团有限公司条目\n天涯帖子:杭州天目山医院你到底黑不黑(http://bbs.tianya.cn/postfree19291961.shtml)", 177 | "地址: 杭州市天目山路319号" 178 | ], 179 | "www.hm120.cn": [ 180 | "名称:杭州和睦医院" 181 | ], 182 | "www.zsxdfk.com": [ 183 | "名称:舟山现代妇科医院" 184 | ], 185 | "www.tjcg120.com": [ 186 | "名称:天津长庚耳鼻喉医院", 187 | "电话: 4006703120" 188 | ], 189 | "www.huaxiahp.com": [ 190 | "名称:天津华厦医院", 191 | "电话: 02228230188" 192 | ], 193 | "暂缺": [ 194 | "名称:天津怡泰生殖专科医院", 195 | "电话: 02227113333" 196 | ], 197 | "www.yafrlaowuw.com": [ 198 | "名称:天津254医院/解放军254医院部分科室", 199 | "电话: 02226220266", 200 | "地址: 天津市河北区黄纬路60号" 201 | ], 202 | "www.28278888.com": [ 203 | "名称:天津乐园医院", 204 | "电话: 02228278888" 205 | ], 206 | "www.lr16.com": [ 207 | "名称:天津丽人女子医院", 208 | "电话: 02224156655" 209 | ], 210 | "www.tjlrfk.com": [ 211 | "名称:天津丽人女子医院", 212 | "电话: 02224156655" 213 | ], 214 | "lr16.tj.aimeicity.com": [ 215 | "名称:天津丽人女子医院", 216 | "电话: 02224156655" 217 | ], 218 | "www.tjhsfk.com": [ 219 | "名称:天津华山医院", 220 | "电话: 02258815889" 221 | ], 222 | "www.bevall.com": [ 223 | "名称:天津华山医院", 224 | "电话: 02258815889" 225 | ], 226 | "www.nkyy022.com": [ 227 | "名称:天津阿波罗医院", 228 | "电话: 02228111999", 229 | "地址: 天津市南开区黄河道38号" 230 | ], 231 | "www.huabeihp.com": [ 232 | "名称:天津华北医院", 233 | "电话: 02224418755" 234 | ], 235 | "www.tjkrml.com": [ 236 | "名称:天津坤如玛丽医院", 237 | "电话: 02228285031" 238 | ], 239 | "www.022nz.com": [ 240 | "名称:天津现代女子医院", 241 | "电话: 02224459999" 242 | ], 243 | "www.dgmaria.cn": [ 244 | "名称:东莞玛丽亚妇产医院", 245 | "电话: 076922993333", 246 | "地址: 东莞南城莞太大道(广彩城酒店旁)" 247 | ], 248 | "www.39568888.com": [ 249 | "名称:大连长城妇科医院", 250 | "电话: 4000078120" 251 | ], 252 | "zqnkyy120.com": [ 253 | "名称:肇庆现代男科医院", 254 | "电话: 07582221120", 255 | "地址: 肇庆市端州区二塔路中段市二人民医院前行20米左转" 256 | ], 257 | "www.nccharm.com.cn": [ 258 | "名称:南昌佳美美容医院", 259 | "微信公众号: nccharm", 260 | "电话: 4006796066", 261 | "地址: 江西省南昌市青云谱区洪都南大道273号" 262 | ], 263 | "www.btsdhjsj.com": [ 264 | "名称:南昌博爱泌尿专科医院", 265 | "QQ: 542099677\n3237581371", 266 | "电话: 079186658922\n079187958517", 267 | "地址: 江西省南昌市东湖区胜利路355号(近八一桥)" 268 | ], 269 | "www.ncboai.cn": [ 270 | "名称:南昌博爱泌尿专科医院", 271 | "QQ: 542099677\n3237581371", 272 | "电话: 079186658922\n079187958517", 273 | "地址: 江西省南昌市东湖区胜利路355号(近八一桥)" 274 | ], 275 | "www.ncboai.net": [ 276 | "名称:南昌博爱泌尿专科医院", 277 | "QQ: 542099677\n3237581371", 278 | "电话: 079186658922\n079187958517", 279 | "地址: 江西省南昌市东湖区胜利路355号(近八一桥)" 280 | ], 281 | "www.ot8.cn": [ 282 | "名称:南昌博爱泌尿专科医院", 283 | "QQ: 542099677\n3237581371", 284 | "电话: 079186658922\n079187958517", 285 | "地址: 江西省南昌市东湖区胜利路355号(近八一桥)" 286 | ], 287 | "www.xngxw.com": [ 288 | "名称:南昌博爱泌尿专科医院", 289 | "QQ: 542099677\n3237581371", 290 | "电话: 079186658922\n079187958517", 291 | "地址: 江西省南昌市东湖区胜利路355号(近八一桥)" 292 | ], 293 | "www.ncebh.com": [ 294 | "名称:南昌博大耳鼻咽喉专科医院", 295 | "电话: 079186658908", 296 | "地址: 江西省南昌市青云谱区洪都南大道237号" 297 | ], 298 | "www.hand120.com": [ 299 | "名称:南昌曙光手足外科医院", 300 | "微信公众号: shuguanggongyi", 301 | "电话: 079188230000", 302 | "地址: 江西省南昌市解放西路99号" 303 | ], 304 | "www.nz91.com": [ 305 | "名称:南昌仁爱女子医院", 306 | "电话: 079185207758\n079186573333", 307 | "地址: 江西省南昌市洪城路636号(洪城大市场正门往西300米路南)" 308 | ], 309 | "www.wy120.com": [ 310 | "名称:南昌市第五医院", 311 | "微信公众号: ncdwrmyy", 312 | "电话: 4001606199\n079186658901", 313 | "地址: 江西省南昌市青云谱区井冈山大道239号(家乐福超市对面)" 314 | ], 315 | "www.hsbyby.cn": [ 316 | "名称:南昌华山不孕不育医院", 317 | "电话: 4006013130\n079186658913\n18507005120", 318 | "地址: 江西省南昌市迎宾北大道285号" 319 | ], 320 | "www.nch3yy.com": [ 321 | "名称:南昌华山不孕不育医院", 322 | "电话: 4006013130\n079186658913\n18507005120", 323 | "地址: 江西省南昌市迎宾北大道285号" 324 | ], 325 | "www.0791gc.com": [ 326 | "名称:南昌东大肛肠专科医院", 327 | "电话: 079186822072", 328 | "微信: ddgc0791", 329 | "地址: 江西省南昌市胜利路298号(八一桥旁)" 330 | ], 331 | "www.kh91.com": [ 332 | "名称:马鞍山康豪泌尿专科医院", 333 | "电话: 05552402821" 334 | ], 335 | "www.120mas.com": [ 336 | "名称:马鞍山金陵医院", 337 | "电话: 05552161555" 338 | ], 339 | "www.hszyy.cn": [ 340 | "名称:马鞍山红十字医院", 341 | "电话: 05552345519" 342 | ], 343 | "www.whhxyy.com": [ 344 | "名称:武汉华夏医院", 345 | "电话: 02787275566" 346 | ], 347 | "www.hbsrjyy.com": [ 348 | "名称:湖北省荣军医院", 349 | "电话: 02762084891" 350 | ], 351 | "www.boaifk.com": [ 352 | "名称:武汉博爱医院", 353 | "电话: 02786722223" 354 | ], 355 | "www.whrenai.com": [ 356 | "名称:武汉仁爱医院", 357 | "电话: 02783798531" 358 | ], 359 | "www.whzyyy.com": [ 360 | "名称:武汉中原医院", 361 | "电话: 02785733999" 362 | ], 363 | "www.hqsmk.com": [ 364 | "名称:武汉虎泉医院", 365 | "电话: 02788089120" 366 | ], 367 | "www.szn027.com": [ 368 | "名称:武汉送子鸟不孕不育医院", 369 | "电话: 02783771313" 370 | ], 371 | "www.ctbyw.com": [ 372 | "名称:武汉黄浦中西医结合医院", 373 | "电话: 4009961009" 374 | ], 375 | "www.lyys120.com": [ 376 | "名称:耒阳云森医院" 377 | ], 378 | "www.njgtyy.com": [ 379 | "名称:南京肛泰中医医院", 380 | "地址: 建邺区黄山路2号" 381 | ], 382 | "www.4000303027.com": [ 383 | "名称:武汉韩辰整形医院", 384 | "电话: 4000303027" 385 | ], 386 | "www.whyestar.com": [ 387 | "名称:武汉艺星医疗美容医院", 388 | "电话: 4000601992" 389 | ], 390 | "www.zswzx.com": [ 391 | "名称:武汉华美整形医院", 392 | "电话: 4000070606" 393 | ], 394 | "www.88077777.com": [ 395 | "名称:武汉五洲美莱整形美容医院", 396 | "电话: 02786785799" 397 | ], 398 | "www.bbrmyy.com": [ 399 | "名称:信阳博士医院", 400 | "地址:信阳市申城大道(原大庆路)沁园春旁: ", 401 | "电话: 03763222555" 402 | ], 403 | "www.shmylike.com": [ 404 | "名称:上海美莱医疗美容医院", 405 | "电话: 02122235555", 406 | "证据:: 所属为美莱医疗美容(连锁)医院集团http://www.mylike.com/详见文末美莱医疗美容(连锁)医院集团为莆田系信息", 407 | "地址: 上海长宁区延安西路789号美莱大厦曹家堰路88号" 408 | ], 409 | "www.tcmmh.com": [ 410 | "名称:上海市闵行区中医医院原上海莱茵医院", 411 | "电话: 02151876888", 412 | "证据:: 该院页面(http://www.tcmmh.com/entry/149)信息显示上海市闵行区卫生局上海中医药大学附属龙华医院西红柿投资控股有限公司合作共建闵行区中医医院其中上海西红柿投资控股集团董事执行总裁为林一平\n全国企业信用信息系统(https://m.imgur.com/ADiokDn)显示上海西红柿投资控股有限公司的法定代表人是陈金秀自然人股东包括李爱兰和陈金秀\n谷歌缓存页面(https://webcache.googleusercontent.com/search?q=cache:D5otmZNewMJ:www.ptshsh.com/index.php%3Fm%3Dcms%26q%3Dview%26id%3D33cd=1hl=enct=clnklr=langen%7ClangzhCN%7ClangzhTW)显示这是谷歌对上海市莆田商会(http://www.ptshsh.com/)网页的缓存陈金秀是上海市莆田商会名誉会长籍贯秀屿区东庄镇\n通过上海市企业注册登记信息公开(http://www.sgs.gov.cn/lz/etpsInfo.do?method=index)地址查询显示(https://imgur.com/wUqEpMU)上海莱茵医院有限公司于今年四月二十八日注销该公司法定代表人为林一平", 413 | "地址: 上海市闵行区合川路3071号" 414 | ], 415 | "www.sh411wgk.com": [ 416 | "名称:解放军411医院", 417 | "电话: 4000789411" 418 | ], 419 | "www.mary120.cn": [ 420 | "名称:上海玛丽女子医院", 421 | "电话: 02169726299", 422 | "地址: 上海市青浦区公园路380号" 423 | ], 424 | "www.shmary.cn": [ 425 | "名称:上海玛丽女子医院", 426 | "电话: 02169726299", 427 | "地址: 上海市青浦区公园路380号" 428 | ], 429 | "www.wz120.cc": [ 430 | "名称:上海万众医院", 431 | "证据: 全国企业信用信息公示系统显示上海万众医院有限公司法定代表人(http://gsxt.sh.gov.cn/notice/notice/view?uuid=PS0hIGEWVK63gZt7AqIcxkQZ5Db8QeQZtab=01)占阳珊\n占阳珊为莆田健康产业总商会常务副会长上海市福建商会理事确认参考新华网微博(http://weibo.com/ttarticle/p/show?id=2309351000123970977124987204)上海市莆田商会网页谷歌缓存(https://webcache.googleusercontent.com/search?q=cache:aEauriVoQOEJ:www.shptsh.com/index.php%3Fm%3Dcms%26q%3Dview%26id%3D94cd=2hl=enct=clnklr=langen%7ClangzhCN%7ClangzhTW)新闻同济大学与莆系医疗合作共建附属医院(http://t1.cnhealthcare.com/article/20150401/content472036.html)\n用户反馈患者家属(http://health.dahe.cn/yypj/yhb/201207/t20120716397644.html)\n用户反馈Google缓存(http://webcache.googleusercontent.com/search?q=cache%3A%2F%2Fhealth.dahe.cn%2Fyypj%2Fyhb%2F201207%2Ft20120716397644.htmlrlz=1C5CHFAenUS659US659oq=cache%3A%2F%2Fhealth.dahe.cn%2Fyypj%2Fyhb%2F201207%2Ft20120716397644.htmlaqs=chrome..69i57j69i58.4789j0j4sourceid=chromeie=UTF8)\n用户反馈天涯网友(http://bbs.tianya.cn/post419430301.shtml)\n用户反馈Google缓存(http://webcache.googleusercontent.com/search?q=cache%3A%2F%2Fbbs.tianya.cn%2Fpost419430301.shtmlrlz=1C5CHFAenUS659US659oq=cache%3A%2F%2Fbbs.tianya.cn%2Fpost419430301.shtmlaqs=chrome..69i57j69i58.2551j0j4sourceid=chromeie=UTF8)", 432 | "电话: 4008892218", 433 | "地址: 上海市徐汇区吴中路2号" 434 | ], 435 | "www.shzhenai.com": [ 436 | "名称:上海真爱(整形美容)医院", 437 | "电话: 02162269000", 438 | "用户反馈: 患者1(http://www.kobeei.com/lc/31713.html)\nGoogle缓存(http://webcache.googleusercontent.com/search?sourceid=chromepsyapi2ion=1espv=2ie=UTF8q=cache%3A%2F%2Fwww.kobeei.com%2Flc%2F31713.htmloq=cache%3A%2F%2Fwww.kobeei.com%2Flc%2F31713.htmlrlz=1C5CHFAenUS659US659aqs=chrome..69i57j69i58.4678j0j4)" 439 | ], 440 | "www.jqbyby.com": [ 441 | "名称:上海健桥医院", 442 | "电话: 02156659999\n02165022556", 443 | "上海长江医院: " 444 | ], 445 | "www.cjhospital.com": [ 446 | "名称:上海健桥医院", 447 | "电话: 02156659999\n02165022556", 448 | "上海长江医院: " 449 | ], 450 | "www.xinhongqiao.cn": [ 451 | "名称:上海新虹桥医院(号称上海男科医院)", 452 | "电话: 02162092255", 453 | "地址: 上海长宁区虹古路85号(虹桥开发区西侧)" 454 | ], 455 | "23759.zhaopin.job120.com": [ 456 | "名称:上海国正医院(注:隶属上海明爱医疗集团)没有找到", 457 | "电话: 02158009999\n18801856118", 458 | "地址: 上海市南汇区惠南镇人民西路99号" 459 | ], 460 | "shgzyy.cn.b2b168.com": [ 461 | "名称:上海国正医院(注:隶属上海明爱医疗集团)没有找到", 462 | "电话: 02158009999\n18801856118", 463 | "地址: 上海市南汇区惠南镇人民西路99号" 464 | ], 465 | "www.nanpuyy.com": [ 466 | "名称:上海南浦妇科医院", 467 | "相关资料:: 这是百度和谷歌发现网址最多的医院\n最后一个网址http://yyk.qqyy.com/h14964/所属全球医院网http://www.qqyy.com/工信部备案信息(https://m.imgur.com/VqVuGnj)为闽ICP备09014062号20141106福州网健天下网络科技有限公司\n谷歌搜索关键词医院site:.qqyy.com可以发现很多在这个名单上出现的医院可以为进一步搜索参考\n天涯帖子上海南浦妇科医院的受害者该怎么维权(http://bbs.tianya.cn/post8287848591.shtml)\n网址http://www.pmref.com/将自己称为上海松江区妇科医院而电话仍然是02158891199并且在这个网页http://www.pmref.com/yyjs/51.html显示为南浦妇科医院", 468 | "电话: 02158891199\n15821608827", 469 | "地址: 上海市浦东新区浦东南路2250号南浦大桥下" 470 | ], 471 | "www.brandv.org": [ 472 | "名称:上海南浦妇科医院", 473 | "相关资料:: 这是百度和谷歌发现网址最多的医院\n最后一个网址http://yyk.qqyy.com/h14964/所属全球医院网http://www.qqyy.com/工信部备案信息(https://m.imgur.com/VqVuGnj)为闽ICP备09014062号20141106福州网健天下网络科技有限公司\n谷歌搜索关键词医院site:.qqyy.com可以发现很多在这个名单上出现的医院可以为进一步搜索参考\n天涯帖子上海南浦妇科医院的受害者该怎么维权(http://bbs.tianya.cn/post8287848591.shtml)\n网址http://www.pmref.com/将自己称为上海松江区妇科医院而电话仍然是02158891199并且在这个网页http://www.pmref.com/yyjs/51.html显示为南浦妇科医院", 474 | "电话: 02158891199\n15821608827", 475 | "地址: 上海市浦东新区浦东南路2250号南浦大桥下" 476 | ], 477 | "www.fyjx.org": [ 478 | "名称:上海南浦妇科医院", 479 | "相关资料:: 这是百度和谷歌发现网址最多的医院\n最后一个网址http://yyk.qqyy.com/h14964/所属全球医院网http://www.qqyy.com/工信部备案信息(https://m.imgur.com/VqVuGnj)为闽ICP备09014062号20141106福州网健天下网络科技有限公司\n谷歌搜索关键词医院site:.qqyy.com可以发现很多在这个名单上出现的医院可以为进一步搜索参考\n天涯帖子上海南浦妇科医院的受害者该怎么维权(http://bbs.tianya.cn/post8287848591.shtml)\n网址http://www.pmref.com/将自己称为上海松江区妇科医院而电话仍然是02158891199并且在这个网页http://www.pmref.com/yyjs/51.html显示为南浦妇科医院", 480 | "电话: 02158891199\n15821608827", 481 | "地址: 上海市浦东新区浦东南路2250号南浦大桥下" 482 | ], 483 | "www.fdfzxy.org": [ 484 | "名称:上海南浦妇科医院", 485 | "相关资料:: 这是百度和谷歌发现网址最多的医院\n最后一个网址http://yyk.qqyy.com/h14964/所属全球医院网http://www.qqyy.com/工信部备案信息(https://m.imgur.com/VqVuGnj)为闽ICP备09014062号20141106福州网健天下网络科技有限公司\n谷歌搜索关键词医院site:.qqyy.com可以发现很多在这个名单上出现的医院可以为进一步搜索参考\n天涯帖子上海南浦妇科医院的受害者该怎么维权(http://bbs.tianya.cn/post8287848591.shtml)\n网址http://www.pmref.com/将自己称为上海松江区妇科医院而电话仍然是02158891199并且在这个网页http://www.pmref.com/yyjs/51.html显示为南浦妇科医院", 486 | "电话: 02158891199\n15821608827", 487 | "地址: 上海市浦东新区浦东南路2250号南浦大桥下" 488 | ], 489 | "www.acgene.org": [ 490 | "名称:上海南浦妇科医院", 491 | "相关资料:: 这是百度和谷歌发现网址最多的医院\n最后一个网址http://yyk.qqyy.com/h14964/所属全球医院网http://www.qqyy.com/工信部备案信息(https://m.imgur.com/VqVuGnj)为闽ICP备09014062号20141106福州网健天下网络科技有限公司\n谷歌搜索关键词医院site:.qqyy.com可以发现很多在这个名单上出现的医院可以为进一步搜索参考\n天涯帖子上海南浦妇科医院的受害者该怎么维权(http://bbs.tianya.cn/post8287848591.shtml)\n网址http://www.pmref.com/将自己称为上海松江区妇科医院而电话仍然是02158891199并且在这个网页http://www.pmref.com/yyjs/51.html显示为南浦妇科医院", 492 | "电话: 02158891199\n15821608827", 493 | "地址: 上海市浦东新区浦东南路2250号南浦大桥下" 494 | ], 495 | "www.nanpu120.com": [ 496 | "名称:上海南浦妇科医院", 497 | "相关资料:: 这是百度和谷歌发现网址最多的医院\n最后一个网址http://yyk.qqyy.com/h14964/所属全球医院网http://www.qqyy.com/工信部备案信息(https://m.imgur.com/VqVuGnj)为闽ICP备09014062号20141106福州网健天下网络科技有限公司\n谷歌搜索关键词医院site:.qqyy.com可以发现很多在这个名单上出现的医院可以为进一步搜索参考\n天涯帖子上海南浦妇科医院的受害者该怎么维权(http://bbs.tianya.cn/post8287848591.shtml)\n网址http://www.pmref.com/将自己称为上海松江区妇科医院而电话仍然是02158891199并且在这个网页http://www.pmref.com/yyjs/51.html显示为南浦妇科医院", 498 | "电话: 02158891199\n15821608827", 499 | "地址: 上海市浦东新区浦东南路2250号南浦大桥下" 500 | ], 501 | "m.up54.com": [ 502 | "名称:上海南浦妇科医院", 503 | "相关资料:: 这是百度和谷歌发现网址最多的医院\n最后一个网址http://yyk.qqyy.com/h14964/所属全球医院网http://www.qqyy.com/工信部备案信息(https://m.imgur.com/VqVuGnj)为闽ICP备09014062号20141106福州网健天下网络科技有限公司\n谷歌搜索关键词医院site:.qqyy.com可以发现很多在这个名单上出现的医院可以为进一步搜索参考\n天涯帖子上海南浦妇科医院的受害者该怎么维权(http://bbs.tianya.cn/post8287848591.shtml)\n网址http://www.pmref.com/将自己称为上海松江区妇科医院而电话仍然是02158891199并且在这个网页http://www.pmref.com/yyjs/51.html显示为南浦妇科医院", 504 | "电话: 02158891199\n15821608827", 505 | "地址: 上海市浦东新区浦东南路2250号南浦大桥下" 506 | ], 507 | "www.rzjfk.com": [ 508 | "名称:上海南浦妇科医院", 509 | "相关资料:: 这是百度和谷歌发现网址最多的医院\n最后一个网址http://yyk.qqyy.com/h14964/所属全球医院网http://www.qqyy.com/工信部备案信息(https://m.imgur.com/VqVuGnj)为闽ICP备09014062号20141106福州网健天下网络科技有限公司\n谷歌搜索关键词医院site:.qqyy.com可以发现很多在这个名单上出现的医院可以为进一步搜索参考\n天涯帖子上海南浦妇科医院的受害者该怎么维权(http://bbs.tianya.cn/post8287848591.shtml)\n网址http://www.pmref.com/将自己称为上海松江区妇科医院而电话仍然是02158891199并且在这个网页http://www.pmref.com/yyjs/51.html显示为南浦妇科医院", 510 | "电话: 02158891199\n15821608827", 511 | "地址: 上海市浦东新区浦东南路2250号南浦大桥下" 512 | ], 513 | "yyk.qqyy.com": [ 514 | "名称:上海南浦妇科医院", 515 | "相关资料:: 这是百度和谷歌发现网址最多的医院\n最后一个网址http://yyk.qqyy.com/h14964/所属全球医院网http://www.qqyy.com/工信部备案信息(https://m.imgur.com/VqVuGnj)为闽ICP备09014062号20141106福州网健天下网络科技有限公司\n谷歌搜索关键词医院site:.qqyy.com可以发现很多在这个名单上出现的医院可以为进一步搜索参考\n天涯帖子上海南浦妇科医院的受害者该怎么维权(http://bbs.tianya.cn/post8287848591.shtml)\n网址http://www.pmref.com/将自己称为上海松江区妇科医院而电话仍然是02158891199并且在这个网页http://www.pmref.com/yyjs/51.html显示为南浦妇科医院", 516 | "电话: 02158891199\n15821608827", 517 | "地址: 上海市浦东新区浦东南路2250号南浦大桥下" 518 | ], 519 | "www.28567.hos.999120.net": [ 520 | "名称:上海城市女子医院", 521 | "电话: 02151092999", 522 | "地址: 上海市普陀区曹安路1352号" 523 | ], 524 | "www.long120.cn": [ 525 | "名称:上海九龙男子医院", 526 | "电话: 02152739067\n02152732699", 527 | "地址: 上海市长宁区中山西路333号近中山公园" 528 | ], 529 | "www.shjlnzyy.com": [ 530 | "名称:上海九龙男子医院", 531 | "电话: 02152739067\n02152732699", 532 | "地址: 上海市长宁区中山西路333号近中山公园" 533 | ], 534 | "www.shhuamei.cn": [ 535 | "名称:上海华美医疗美容医院", 536 | "相关资料: 工信部备案信息主办单位上海华美医疗美容医院有限公司网站负责人蔡智俊备案许可证号沪ICP备13030768号1\n上海企业信息登记注册公开查询信息上海华美医疗美容医院有限公司法定代表人雷启龙自然人股东洪玲燕黄志佳\n黄志佳相关信息莆田青年发展促进会珠海第三次交流会顺利召开(http://zonghui.hxyjw.com/show146921)中国健康产业总会上海分会筹备大会在沪胜利召开上海市莆田商会(https://www.google.com/search?hl=enq=%E9%BB%84%E5%BF%97%E4%BD%B3site%3Awww.ptshsh.comnewwindow=1hl=enq=%E9%BB%84%E5%BF%97%E4%BD%B3site:ptshsh.com)请进一步求证", 537 | "电话: 02158856655\n4008806580", 538 | "地址: 上海市市辖区浦东新区源深路155号" 539 | ], 540 | "boaihospital.com": [ 541 | "名称:上海博爱医院是否属于莆田系存在争议请继续求证", 542 | "电话: 02164333999\n02164312600", 543 | "证据:: 这个网页(http://www.gotoread.com/s/e/?vo=3803p=43)是瞭望东方周刊2007年第4期(电子杂志)的报道莆田系败走上海博爱医院报道记者为李蔚和朱国栋文章提到上海博爱医院是上海第一家民营医院2004年莆田系曾入股上海博爱医院当时莆田人的股份是中骏20%美迪亚20%澳信20%这三家医疗投资集团的掌门人分别为莆田游医大佬詹玉鹏詹国团和林宗金2006年3月上海市卫生局会同市工商局药监物价等部门对全市医疗机构的专项检查中上海博爱医院成为问题最多的医院违规项目包括使用毁灭癌症细胞提高存活率等明显违规用于门口则擅自挂有瑞士羊胚胎素注射亚洲服务中心等招牌擅自实行医保自费病人收费双轨制部分收费项目自费病人要高出医保病人达三四倍且未明码标价王家屏告诉瞭望东方周刊记者不久之后博爱医院就被取消了医保资格过了一段时间莆田系多数股份黯然推出博爱医院", 544 | "地址: 上海市淮海中路1590号" 545 | ], 546 | "www.giivi.com": [ 547 | "名称:上海真美妇科医院", 548 | "电话: 02155099999", 549 | "地址: 上海市杨浦区宁国路313弄4号" 550 | ], 551 | "www.zdia.net": [ 552 | "名称:上海真美妇科医院", 553 | "电话: 02155099999", 554 | "地址: 上海市杨浦区宁国路313弄4号" 555 | ], 556 | "www.yodak.net": [ 557 | "名称:上海远大心胸医院(号称上海心脏病医院)", 558 | "电话: 02164829999", 559 | "地址: 上海市徐汇区龙漕路218号" 560 | ], 561 | "www.4006306997.com": [ 562 | "名称:上海东方丽人医疗美容医院", 563 | "相关资料:: 工信部备案信息闽ICP备15019526号主办单位性质个人主办单位名称/网站负责人胡森森网站名称麦凯乐网(和网站显示名称严重不符)", 564 | "电话: 4006306997\n1379444015", 565 | "地址: 上海市长宁区安龙路835号" 566 | ], 567 | "www.shtl120.com": [ 568 | "名称:上海天伦医院(注:隶属上海明爱医疗集团)done", 569 | "电话: 02165667633\n02165259999\n9864006660102\n02155158829", 570 | "地址: 上海市虹口区凉城路545号" 571 | ], 572 | "www.shbyby.org": [ 573 | "名称:上海天伦医院(注:隶属上海明爱医疗集团)done", 574 | "电话: 02165667633\n02165259999\n9864006660102\n02155158829", 575 | "地址: 上海市虹口区凉城路545号" 576 | ], 577 | "www.shtlyy.com": [ 578 | "名称:上海天伦医院(注:隶属上海明爱医疗集团)done", 579 | "电话: 02165667633\n02165259999\n9864006660102\n02155158829", 580 | "地址: 上海市虹口区凉城路545号" 581 | ], 582 | "www.shtianlun.org": [ 583 | "名称:上海天伦医院(注:隶属上海明爱医疗集团)done", 584 | "电话: 02165667633\n02165259999\n9864006660102\n02155158829", 585 | "地址: 上海市虹口区凉城路545号" 586 | ], 587 | "www.tianlun100.com": [ 588 | "名称:上海天伦医院(注:隶属上海明爱医疗集团)done", 589 | "电话: 02165667633\n02165259999\n9864006660102\n02155158829", 590 | "地址: 上海市虹口区凉城路545号" 591 | ], 592 | "www.tida.sh.cn": [ 593 | "名称:上海天大医疗美容医院", 594 | "相关资料: 百度搜索关键词上海五官科医院上海沪申五官科医院可以直接看到搜索结果把两者混淆", 595 | "电话: 02164080808", 596 | "上海五官科医院(注:这个隶属深圳博爱医疗集团: 非汾阳路的复旦大学附属眼耳鼻喉科医院/上海市五官科医院)复旦大学附属眼耳鼻喉科医院/上海市五官科医院是卫计委直属公立三甲医院此条目仅一字之差容易混淆目前百度搜索上海五官科医院也是导向到复旦大学附属眼耳鼻喉科医院/上海市五官科医院院方申明www.fdeent.org/content/4408" 597 | ], 598 | "www.tida120.com": [ 599 | "名称:上海天大医疗美容医院", 600 | "相关资料: 百度搜索关键词上海五官科医院上海沪申五官科医院可以直接看到搜索结果把两者混淆", 601 | "电话: 02164080808", 602 | "上海五官科医院(注:这个隶属深圳博爱医疗集团: 非汾阳路的复旦大学附属眼耳鼻喉科医院/上海市五官科医院)复旦大学附属眼耳鼻喉科医院/上海市五官科医院是卫计委直属公立三甲医院此条目仅一字之差容易混淆目前百度搜索上海五官科医院也是导向到复旦大学附属眼耳鼻喉科医院/上海市五官科医院院方申明www.fdeent.org/content/4408" 603 | ], 604 | "www.er021.com": [ 605 | "名称:上海天大医疗美容医院", 606 | "相关资料: 百度搜索关键词上海五官科医院上海沪申五官科医院可以直接看到搜索结果把两者混淆", 607 | "电话: 02164080808", 608 | "上海五官科医院(注:这个隶属深圳博爱医疗集团: 非汾阳路的复旦大学附属眼耳鼻喉科医院/上海市五官科医院)复旦大学附属眼耳鼻喉科医院/上海市五官科医院是卫计委直属公立三甲医院此条目仅一字之差容易混淆目前百度搜索上海五官科医院也是导向到复旦大学附属眼耳鼻喉科医院/上海市五官科医院院方申明www.fdeent.org/content/4408" 609 | ], 610 | "www.renai.cn": [ 611 | "名称:上海仁爱医院", 612 | "电话: 02164688888", 613 | "地址: 上海市徐汇区漕溪路133号(近万体馆)" 614 | ], 615 | "www.shhqyy.com": [ 616 | "名称:上海虹桥医院所属上海闵行虹桥医院有限公司(http://weibo.com/shhqyy02164659999)", 617 | "电话: 02164659999", 618 | "地址: 上海市闵行区虹梅路2181号近吴中路" 619 | ], 620 | "www.hs5g.com": [ 621 | "名称:上海沪申五官科医院", 622 | "电话: 02154305338" 623 | ], 624 | "www.sdaj.net": [ 625 | "名称:上海西郊骨科医院", 626 | "电话: 02152208866\n13651724016", 627 | "地址: 上海市长宁区剑河路595号4号楼" 628 | ], 629 | "www.gb5u.com": [ 630 | "名称:上海西郊骨科医院", 631 | "电话: 02152208866\n13651724016", 632 | "地址: 上海市长宁区剑河路595号4号楼" 633 | ], 634 | "www.gkyy120.com": [ 635 | "名称:上海西郊骨科医院", 636 | "电话: 02152208866\n13651724016", 637 | "地址: 上海市长宁区剑河路595号4号楼" 638 | ], 639 | "www.cqgkzj.com": [ 640 | "名称:上海西郊骨科医院", 641 | "电话: 02152208866\n13651724016", 642 | "地址: 上海市长宁区剑河路595号4号楼" 643 | ], 644 | "www.wzjg120.com": [ 645 | "名称:温州建国医院" 646 | ], 647 | "www.wzcwk120.com": [ 648 | "名称:温州建国医院" 649 | ], 650 | "www.120cz.com": [ 651 | "名称:温州长征医院" 652 | ], 653 | "www.xczshp.com": [ 654 | "名称:许昌中山医院", 655 | "电话: 03742158889", 656 | "地址: 许昌市西大街280号" 657 | ], 658 | "www.xcyyhp.com": [ 659 | "名称:许昌中山医院", 660 | "电话: 03742158889", 661 | "地址: 许昌市西大街280号" 662 | ], 663 | "www.2158888.com": [ 664 | "名称:许昌中山医院", 665 | "电话: 03742158889", 666 | "地址: 许昌市西大街280号" 667 | ], 668 | "www.xczsnk.com": [ 669 | "名称:许昌中山医院", 670 | "电话: 03742158889", 671 | "地址: 许昌市西大街280号" 672 | ], 673 | "xczsnk.com": [ 674 | "名称:许昌中山医院", 675 | "电话: 03742158889", 676 | "地址: 许昌市西大街280号" 677 | ], 678 | "www.82056999.com": [ 679 | "名称:嘉兴博爱医院" 680 | ], 681 | "www.jxjk.cn": [ 682 | "名称:嘉兴曙光中西医结合医院" 683 | ], 684 | "www.sian.cc": [ 685 | "名称:浙江新安国际医院" 686 | ], 687 | "www.qlxzhuanke.com": [ 688 | "名称:贵阳结石病医院", 689 | "电话: 085188546001\n085188546002" 690 | ], 691 | "www.gyjsbyy.com": [ 692 | "名称:贵阳结石病医院", 693 | "电话: 085188546001\n085188546002" 694 | ], 695 | "www.owei.com": [ 696 | "名称:贵州省第二人民医院耳鼻喉科(贵阳耳鼻喉科医院)", 697 | "电话: 4000851962" 698 | ], 699 | "www.ygzhu8.com": [ 700 | "名称:贵州省第二人民医院耳鼻喉科(贵阳耳鼻喉科医院)", 701 | "电话: 4000851962" 702 | ], 703 | "www.lsjlyy.com": [ 704 | "名称:丽水九龙男科医院", 705 | "电话: 05782119011", 706 | "QQ:3082311948: ", 707 | "相关信息: 全国企业信用信息公示系统浙江(http://gsxt.zjaic.gov.cn/appbasicinfo/doViewAppBasicInfo.do?corpid=780CFC6E907B4DB490707687D41A927EA2D082CB21CFD8173A93D6CF9ADA5299)显示法定代表人为陈元华", 708 | "地址: 浙江省丽水市莲都区解放街88号", 709 | "工商注册名称: 丽水九龙医院有限公司" 710 | ], 711 | "www.2119011.com": [ 712 | "名称:丽水九龙男科医院", 713 | "电话: 05782119011", 714 | "QQ:3082311948: ", 715 | "相关信息: 全国企业信用信息公示系统浙江(http://gsxt.zjaic.gov.cn/appbasicinfo/doViewAppBasicInfo.do?corpid=780CFC6E907B4DB490707687D41A927EA2D082CB21CFD8173A93D6CF9ADA5299)显示法定代表人为陈元华", 716 | "地址: 浙江省丽水市莲都区解放街88号", 717 | "工商注册名称: 丽水九龙医院有限公司" 718 | ], 719 | "www1.2119011.com": [ 720 | "名称:丽水九龙男科医院", 721 | "电话: 05782119011", 722 | "QQ:3082311948: ", 723 | "相关信息: 全国企业信用信息公示系统浙江(http://gsxt.zjaic.gov.cn/appbasicinfo/doViewAppBasicInfo.do?corpid=780CFC6E907B4DB490707687D41A927EA2D082CB21CFD8173A93D6CF9ADA5299)显示法定代表人为陈元华", 724 | "地址: 浙江省丽水市莲都区解放街88号", 725 | "工商注册名称: 丽水九龙医院有限公司" 726 | ], 727 | "www.lsjlnkyy.com": [ 728 | "名称:丽水九龙男科医院", 729 | "电话: 05782119011", 730 | "QQ:3082311948: ", 731 | "相关信息: 全国企业信用信息公示系统浙江(http://gsxt.zjaic.gov.cn/appbasicinfo/doViewAppBasicInfo.do?corpid=780CFC6E907B4DB490707687D41A927EA2D082CB21CFD8173A93D6CF9ADA5299)显示法定代表人为陈元华", 732 | "地址: 浙江省丽水市莲都区解放街88号", 733 | "工商注册名称: 丽水九龙医院有限公司" 734 | ], 735 | "m.lsjlnkyy.com": [ 736 | "名称:丽水九龙男科医院", 737 | "电话: 05782119011", 738 | "QQ:3082311948: ", 739 | "相关信息: 全国企业信用信息公示系统浙江(http://gsxt.zjaic.gov.cn/appbasicinfo/doViewAppBasicInfo.do?corpid=780CFC6E907B4DB490707687D41A927EA2D082CB21CFD8173A93D6CF9ADA5299)显示法定代表人为陈元华", 740 | "地址: 浙江省丽水市莲都区解放街88号", 741 | "工商注册名称: 丽水九龙医院有限公司" 742 | ], 743 | "www.qzmylike.com": [ 744 | "名称:泉州华美美莱整形美容医院", 745 | "电话: 059528266666", 746 | "负责人: 龚英格" 747 | ], 748 | "www.qzhmzx.com": [ 749 | "名称:泉州华美美莱整形美容医院", 750 | "电话: 059528266666", 751 | "负责人: 龚英格" 752 | ], 753 | "www.0595fk.com": [ 754 | "名称:泉州新阳光女子医院", 755 | "电话: 059528966788", 756 | "负责人: 郑海滨" 757 | ], 758 | "www.xyg120.net": [ 759 | "名称:泉州新阳光女子医院", 760 | "电话: 059528966788", 761 | "负责人: 郑海滨" 762 | ], 763 | "www.xygfkyy.com": [ 764 | "名称:泉州新阳光女子医院", 765 | "电话: 059528966788", 766 | "负责人: 郑海滨" 767 | ], 768 | "www.xygfkyy.net": [ 769 | "名称:泉州新阳光女子医院", 770 | "电话: 059528966788", 771 | "负责人: 郑海滨" 772 | ], 773 | "www.qzsznyy.net": [ 774 | "名称:泉州新阳光女子医院", 775 | "电话: 059528966788", 776 | "负责人: 郑海滨" 777 | ], 778 | "www.qzsznyy.com": [ 779 | "名称:泉州新阳光女子医院", 780 | "电话: 059528966788", 781 | "负责人: 郑海滨" 782 | ], 783 | "www.0595ml.com": [ 784 | "名称:泉州坤如玛丽医院", 785 | "负责人: 徐金源" 786 | ], 787 | "www.szsayy.com": [ 788 | "名称:苏州圣爱医院", 789 | "电话: 051268078047" 790 | ], 791 | "www.mlmryy.com": [ 792 | "名称:苏州华美美莱整形医院", 793 | "电话: 4008816499" 794 | ], 795 | "www.szdwyy.com": [ 796 | "名称:苏州东吴医院", 797 | "电话: 051268120120" 798 | ], 799 | "www.cy5g.com": [ 800 | "名称:张家港朝阳五官科医院", 801 | "电话: 051258983999" 802 | ], 803 | "www.ksyy120.com": [ 804 | "名称:昆山虹桥医院", 805 | "电话: 051257399999" 806 | ], 807 | "www.csfk.net": [ 808 | "名称:常熟东方妇科医院", 809 | "电话: 051251531111" 810 | ], 811 | "www.nbhqyy.com": [ 812 | "名称:宁波虹桥医院" 813 | ], 814 | "www.nbnkyy.com": [ 815 | "名称:宁波欧亚男科医院" 816 | ], 817 | "www.nbmylike.com": [ 818 | "名称:宁波美莱整形美容医院" 819 | ], 820 | "www.cxsayy.net": [ 821 | "名称:慈溪圣爱医院" 822 | ] 823 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 用户脚本列表 2 | 3 | * [点击查看](https://team.firefoxcn.net/#userscripts) 4 | 5 | ## 开发说明 6 | 7 | * 代码修改请在main分支上进行。 8 | * GM头信息请写在各个脚本下的meta.yml文件中。Yaml语法可参考[这里](https://www.ruanyifeng.com/blog/2016/07/yaml.html) 9 | * 可以使用[这个工具](https://userscript.firefoxcn.net/)将原有的GM头信息转为Yaml格式。 10 | * 提交后CI会自动build并发布。 11 | 12 | ## 使用NPM开发 13 | 14 | ### 基本说明 15 | * 进入你的脚本目录,使用npm初始化。各类参数可自由指定。 16 | * 如果需要,可以在脚本目录下进一步安装npm依赖。 17 | * 新建入口文件,在其中编写脚本代码。 18 | * 如果你想要使用TypeScript,则新建`src/index.ts`。 19 | * 如果你想要使用普通JS,则新建`src/index.js`。 20 | * 编写过程中,运行`npm run dev 你的目录名称`,例如`npm run dev Putian_Warning`,即可启动自动编译,在你的目录下生成脚本代码,并会随着你的修改自动重新编译。 21 | * 编写完成后,运行编译命令,即可在你的目录下生成脚本代码。 22 | * 如果需要未经压缩的,请运行`npm run build-dev 你的目录名称`,例如`npm run build-dev Putian_Warning` 23 | * 如果需要优化后的,请运行`npm run build 你的目录名称`,例如`npm run build Putian_Warning`。这个版本会进行[tree shaking](https://www.webpackjs.com/guides/tree-shaking/),减小脚本体积。 24 | 25 | ### 使用样式 26 | 编译工具内置了CSS Module支持。因此,你可以直接编写CSS文件,并且不需要担心样式冲突的问题。但注意在meta.yml中申请GM_addStyle权限。 27 | 28 | 首先新建一个CSS,例如`src/index.css`,写入以下内容: 29 | ```css 30 | .my-button { 31 | color: red; 32 | } 33 | ``` 34 | 35 | 在你的脚本中,可以这样使用: 36 | ```js 37 | import { locals } from './index.css'; 38 | 39 | const button = document.createElement('button'); 40 | button.className = locals['my-button']; 41 | ``` 42 | -------------------------------------------------------------------------------- /Reading_Mode/.gitignore: -------------------------------------------------------------------------------- 1 | *.user.js 2 | *.meta.js -------------------------------------------------------------------------------- /Reading_Mode/README.md: -------------------------------------------------------------------------------- 1 | # Reading Mode 2 | 3 | 给一些网站添加阅读模式的功能 4 | 5 | 支持网站: 6 | 7 | * CSDN博客 8 | * 新浪博客 9 | * 网易博客 10 | * 搜狐博客 11 | * 天涯博客 12 | * 科学网博客 13 | * ChinaUnix 14 | * cnblogs 15 | * 掘金 16 | * 知乎专栏 17 | 18 | [【点击安装】](https://userscript.firefoxcn.net/js/Reading_Mode.user.js) 19 | 20 | author: 泷涯 21 | 22 | ## changelog 23 | 24 | **13** 25 | 26 | 支持掘金、知乎专栏,重构代码 27 | 28 | **12** 29 | 30 | 修复设置字体颜色对控制UI无效的问题 31 | 32 | **11** 33 | 34 | 优化标题文字的显示 35 | 36 | 修复部分CSDN页面无法进入阅读模式 37 | 38 | **10** 39 | 40 | 修复部分网站的HTTPS支持 41 | 42 | **9** 43 | 44 | 继续解决防抓取乱码 45 | 46 | **8** 47 | 48 | 部分网站有防机器抓取的随机乱码,此次更新可以屏蔽掉其中一部分 49 | 50 | **7** 51 | 52 | 修复对cnblogs的HTTPS支持 53 | 54 | **6** 55 | 56 | 修复细字体无效 57 | 58 | **5** 59 | 60 | 修复某些博客没有去掉默认文字颜色的问题 61 | 62 | 增加字体粗细选项 63 | 64 | **4** 65 | 66 | 修复部分CSDN页面无法进入阅读模式 67 | 68 | **3** 69 | 70 | 添加ChinaUnix支持 71 | 72 | **2** 73 | 74 | 修复部分cnblogs页面无法进入阅读模式 75 | -------------------------------------------------------------------------------- /Reading_Mode/meta.yml: -------------------------------------------------------------------------------- 1 | name: Reading Mode 2 | namespace: blog.sylingd.com 3 | description: 阅读模式 4 | author: ShuangYa 5 | include: 6 | - 'http://blog.csdn.net/*/article/details/*' 7 | - 'https://blog.csdn.net/*/article/details/*' 8 | - 'http://*.cnblogs.com/*/p/*' 9 | - 'http://*.cnblogs.com/*/archive/*' 10 | - 'http://*.cnblogs.com/*/articles/*' 11 | - 'https://*.cnblogs.com/*/p/*' 12 | - 'https://*.cnblogs.com/*/archive/*' 13 | - 'https://*.cnblogs.com/*/articles/*' 14 | - 'http://blog.sina.com.cn/s/blog_*.html' 15 | - 'http://*.blog.163.com/blog/static/*' 16 | - 'http://*.blog.sohu.com/*' 17 | - 'https://juejin.im/entry/*' 18 | - 'https://zhuanlan.zhihu.com/p/*' 19 | - 'http://blog.tianya.cn/post-*' 20 | - 'https://blog.tianya.cn/post-*' 21 | - 'http://blog.sciencenet.cn/blog-*' 22 | - 'http://blog.chinaunix.net/uid-*' 23 | grant: 24 | - GM_addStyle 25 | - GM_setValue 26 | - GM_getValue 27 | -------------------------------------------------------------------------------- /Reading_Mode/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fxb/userscript-reading-mode", 3 | "version": "14", 4 | "main": "index.js", 5 | "author": "ShuangYa", 6 | "license": "MIT" 7 | } 8 | -------------------------------------------------------------------------------- /Reading_Mode/src/app.tsx: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import { useState } from 'preact/hooks'; 3 | import Bar from './components/bar'; 4 | import Rm from './components/rm'; 5 | 6 | const App = () => { 7 | const [show, setShow] = useState(false); 8 | const [bar, setBar] = useState(true); 9 | 10 | return ( 11 |
12 | {bar && setShow(true)} onClose={() => setBar(false)} />} 13 | setShow(false)} /> 14 |
15 | ) 16 | } 17 | 18 | export default App; -------------------------------------------------------------------------------- /Reading_Mode/src/components/bar.tsx: -------------------------------------------------------------------------------- 1 | import { locals } from '../index.css'; 2 | import { h } from 'preact'; 3 | 4 | interface BarProps { 5 | onEnter: () => void; 6 | onClose: () => void; 7 | } 8 | 9 | const Bar = (props: BarProps) => { 10 | return ( 11 |
12 | 13 | 14 |
15 | ) 16 | } 17 | 18 | export default Bar; -------------------------------------------------------------------------------- /Reading_Mode/src/components/rm.tsx: -------------------------------------------------------------------------------- 1 | import { h } from 'preact'; 2 | import { useEffect, useState } from 'preact/hooks'; 3 | import { locals } from '../index.css'; 4 | import { get } from '../sites'; 5 | import * as Styles from '../styles'; 6 | 7 | interface RmProps { 8 | show: boolean; 9 | onExit: () => void; 10 | } 11 | 12 | const Rm = (props: RmProps) => { 13 | const [title, setTitle] = useState(""); 14 | const [content, setContent] = useState(""); 15 | const [, forceUpdate] = useState(0); 16 | 17 | useEffect(() => { 18 | if (props.show && title === "") { 19 | const article = get(); 20 | setTitle(article.title); 21 | setContent(article.content) 22 | } 23 | }); 24 | 25 | useEffect(() => { 26 | const doForceUpdate = () => forceUpdate(x => x + 1); 27 | Styles.on(doForceUpdate); 28 | return () => { 29 | Styles.off(doForceUpdate); 30 | } 31 | }, []); 32 | 33 | const currentFontWeight = Styles.get('font_weight'); 34 | 35 | const handleSave = () => { 36 | const name = window.prompt('请输入预设名称'); 37 | if (name) { 38 | Styles.preset.add(name, { 39 | ...Styles.get() 40 | }); 41 | } 42 | } 43 | 44 | return ( 45 |
48 |
49 | 50 | 51 | 52 | 53 | 54 | 55 | 63 | 67 | 71 | 83 | 84 | 85 |
86 |
{title}
87 |
90 |
91 | ) 92 | } 93 | 94 | export default Rm; -------------------------------------------------------------------------------- /Reading_Mode/src/index.css: -------------------------------------------------------------------------------- 1 | :global(body.show-reading) { 2 | overflow: hidden; 3 | } 4 | .bar { 5 | position: fixed; 6 | bottom: 0; 7 | left: 50%; 8 | height: 46px; 9 | width: 190px; 10 | margin-left: -90px; 11 | z-index: 99999998; 12 | background-color: #FFF; 13 | padding: 5px; 14 | box-sizing: border-box; 15 | box-shadow: 0 2px 2px 0 rgba(0,0,0,.14) ,0 3px 1px -2px rgba(0,0,0,.2) ,0 1px 5px 0 rgba(0,0,0,.12); 16 | } 17 | .bar button, 18 | .rm .option button { 19 | background: 0 0; 20 | border: none; 21 | border-radius: 2px; 22 | color: #000; 23 | position: relative; 24 | height: 36px; 25 | margin: 0; 26 | min-width: 64px; 27 | padding: 0 16px; 28 | display: inline-block; 29 | font-size: 14px; 30 | font-weight: 500; 31 | letter-spacing: 0; 32 | overflow: hidden; 33 | outline: none; 34 | cursor: pointer; 35 | text-align: center; 36 | line-height: 36px; 37 | transition: background-color .2s cubic-bezier(.4,0,.2,1); 38 | } 39 | .bar button:hover, 40 | .rm .option button:hover { 41 | background-color: rgba(158,158,158,.2); 42 | } 43 | .bar button:active, 44 | .rm .option button:active { 45 | background-color: rgba(158,158,158,.4); 46 | } 47 | .rm { 48 | position: fixed; 49 | top: 0; 50 | left: 0; 51 | height: 100%; 52 | width: 100%; 53 | z-index: 99999999; 54 | overflow: auto; 55 | box-sizing: border-box; 56 | font-family: "Microsoft YaHei", "PingFang SC", "Lantinghei SC", "WenQuanYi Zen Hei", sans-serif !important; 57 | text-align: left !important; 58 | } 59 | .rm .option { 60 | margin-bottom: 20px; 61 | text-align: center; 62 | } 63 | .rm .option > * { 64 | vertical-align: middle; 65 | margin-left: 10px; 66 | margin-right: 10px; 67 | } 68 | .rm .option input[type="color"] { 69 | padding: 0; 70 | border: 0; 71 | outline: none; 72 | cursor: pointer; 73 | } 74 | .rm .title { 75 | padding-bottom: 20px; 76 | margin-bottom: 20px; 77 | } -------------------------------------------------------------------------------- /Reading_Mode/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as Styles from './styles'; 2 | import { has } from './sites'; 3 | import { h, render } from 'preact'; 4 | import App from './app'; 5 | 6 | if (has()) { 7 | Styles.update(); 8 | 9 | const el = document.createElement('div'); 10 | document.body.appendChild(el); 11 | render(h(App, null), el); 12 | } 13 | -------------------------------------------------------------------------------- /Reading_Mode/src/sites/chinaunix.ts: -------------------------------------------------------------------------------- 1 | import { getContent, getHtml } from "../utils"; 2 | import { Site } from "./var"; 3 | 4 | const chinaunix: Site = { 5 | getTitle() { 6 | return getHtml('.Blog_tit4 a'); 7 | }, 8 | getContent() { 9 | return getContent('.Blog_wz1'); 10 | } 11 | } 12 | 13 | export default chinaunix; -------------------------------------------------------------------------------- /Reading_Mode/src/sites/cnblogs.ts: -------------------------------------------------------------------------------- 1 | import { getContent } from "../utils"; 2 | import { Site } from "./var"; 3 | 4 | const cnblogs: Site = { 5 | getTitle() { 6 | const a = document.querySelector(".postTitle a"); 7 | const b = document.getElementById('cb_post_title_url'); 8 | if (a) return a.innerHTML; 9 | if (b) return b.innerHTML; 10 | return ''; 11 | }, 12 | getContent() { 13 | return getContent('#cnblogs_post_body'); 14 | } 15 | } 16 | 17 | export default cnblogs; -------------------------------------------------------------------------------- /Reading_Mode/src/sites/csdn.ts: -------------------------------------------------------------------------------- 1 | import { getContent, trimNewLines } from "../utils"; 2 | import { Site } from "./var"; 3 | 4 | const csdn: Site = { 5 | getTitle() { 6 | const c = document.querySelector(".article_title h1"); 7 | if (c) { 8 | const a = c.querySelector('a'); 9 | if (a) { 10 | return trimNewLines(a.innerHTML); 11 | } else { 12 | return trimNewLines(c.children[0].innerHTML); 13 | } 14 | } else { 15 | const top = document.querySelector(".csdn_top"); 16 | const article = document.querySelector(".title-article"); 17 | if (top) return trimNewLines(top.innerHTML); 18 | if (article) return trimNewLines(article.innerHTML); 19 | } 20 | return ''; 21 | }, 22 | getContent() { 23 | return getContent('.article_content') || getContent('.markdown_views'); 24 | } 25 | } 26 | 27 | export default csdn; -------------------------------------------------------------------------------- /Reading_Mode/src/sites/index.ts: -------------------------------------------------------------------------------- 1 | import { Site } from "./var"; 2 | import csdn from "./csdn"; 3 | import cnblogs from "./cnblogs"; 4 | import sina from "./sina"; 5 | import netease from "./netease"; 6 | import sohu from "./sohu"; 7 | import tianya from "./tianya"; 8 | import sciencenet from "./sciencenet"; 9 | import chinaunix from "./chinaunix"; 10 | import juejin from "./juejin"; 11 | import zhihu from "./zhihu"; 12 | 13 | const sites: { [x: string]: Site } = { 14 | 'blog.csdn.net': csdn, 15 | 'www.cnblogs.com': cnblogs, 16 | 'blog.sina.com.cn': sina, 17 | 'blog.163.com': netease, 18 | 'blog.sohu.com': sohu, 19 | 'blog.tianya.cn': tianya, 20 | 'blog.sciencenet.cn': sciencenet, 21 | 'blog.chinaunix.net': chinaunix, 22 | 'juejin.im': juejin, 23 | 'zhuanlan.zhihu.com': zhihu 24 | } 25 | 26 | function getHost() { 27 | const hostname = window.location.hostname; 28 | if (typeof sites[hostname] !== 'undefined') { 29 | return hostname; 30 | } 31 | const domains = Object.values(sites); 32 | for (const it in domains) { 33 | if (hostname.includes(it)) { 34 | return it; 35 | } 36 | } 37 | return ''; 38 | } 39 | 40 | const host = getHost(); 41 | 42 | export function has() { 43 | return typeof sites[host] !== 'undefined'; 44 | } 45 | 46 | export function get() { 47 | const domain = host; 48 | const result = { 49 | title: '', 50 | content: '' 51 | }; 52 | if (has()) { 53 | result.title = sites[domain].getTitle(); 54 | result.content = sites[domain].getContent(); 55 | } 56 | return result; 57 | } -------------------------------------------------------------------------------- /Reading_Mode/src/sites/juejin.ts: -------------------------------------------------------------------------------- 1 | import { getHtml } from "../utils"; 2 | import { Site } from "./var"; 3 | 4 | const juejin: Site = { 5 | getTitle() { 6 | return getHtml('.entry-content-box > h1'); 7 | }, 8 | getContent() { 9 | const html = getHtml('.article-content'); 10 | return html.replace(/data\-src="(.*?)"(.*?)src="(.*?)"/g, 'data-src="$1"$2src="$1"'); 11 | } 12 | } 13 | 14 | export default juejin; -------------------------------------------------------------------------------- /Reading_Mode/src/sites/netease.ts: -------------------------------------------------------------------------------- 1 | import { getContent, getHtml } from "../utils"; 2 | import { Site } from "./var"; 3 | 4 | const netease: Site = { 5 | getTitle() { 6 | return getHtml('.title .tcnt'); 7 | }, 8 | getContent() { 9 | return getContent('.nbw-blog'); 10 | } 11 | } 12 | 13 | export default netease; -------------------------------------------------------------------------------- /Reading_Mode/src/sites/sciencenet.ts: -------------------------------------------------------------------------------- 1 | import { getContent, getHtml } from "../utils"; 2 | import { Site } from "./var"; 3 | 4 | const sciencenet: Site = { 5 | getTitle() { 6 | return getHtml('.vw h1'); 7 | }, 8 | getContent() { 9 | return getContent('#blog_article'); 10 | } 11 | } 12 | 13 | export default sciencenet; -------------------------------------------------------------------------------- /Reading_Mode/src/sites/sina.ts: -------------------------------------------------------------------------------- 1 | import { getContent, getHtml } from "../utils"; 2 | import { Site } from "./var"; 3 | 4 | const sina: Site = { 5 | getTitle() { 6 | return getHtml('.articalTitle .titName'); 7 | }, 8 | getContent() { 9 | return getContent('.articalContent'); 10 | } 11 | } 12 | 13 | export default sina; -------------------------------------------------------------------------------- /Reading_Mode/src/sites/sohu.ts: -------------------------------------------------------------------------------- 1 | import { getContent, getHtml } from "../utils"; 2 | import { Site } from "./var"; 3 | 4 | const sohu: Site = { 5 | getTitle() { 6 | return getHtml('.newBlog-title h2 span'); 7 | }, 8 | getContent() { 9 | return getContent('#main-content'); 10 | } 11 | } 12 | 13 | export default sohu; -------------------------------------------------------------------------------- /Reading_Mode/src/sites/tianya.ts: -------------------------------------------------------------------------------- 1 | import { getContent, getHtml } from "../utils"; 2 | import { Site } from "./var"; 3 | 4 | const tianya: Site = { 5 | getTitle() { 6 | return getHtml('.article a'); 7 | }, 8 | getContent() { 9 | return getContent('.articletext'); 10 | } 11 | } 12 | 13 | export default tianya; -------------------------------------------------------------------------------- /Reading_Mode/src/sites/var.ts: -------------------------------------------------------------------------------- 1 | export interface Site { 2 | getTitle(): string; 3 | getContent(): string; 4 | } -------------------------------------------------------------------------------- /Reading_Mode/src/sites/zhihu.ts: -------------------------------------------------------------------------------- 1 | import { getContent, getHtml } from "../utils"; 2 | import { Site } from "./var"; 3 | 4 | const zhihu: Site = { 5 | getTitle() { 6 | return getHtml('.Post-Title'); 7 | }, 8 | getContent() { 9 | return getContent('.Post-RichTextContainer'); 10 | } 11 | } 12 | 13 | export default zhihu; -------------------------------------------------------------------------------- /Reading_Mode/src/styles.ts: -------------------------------------------------------------------------------- 1 | import { locals } from './index.css'; 2 | 3 | export interface Style { 4 | [x: string]: any; 5 | name?: string; 6 | box_bg: string; 7 | text_color: string; 8 | font_size: number; 9 | box_padding: number; 10 | box_line_height: number; 11 | font_weight: string; 12 | } 13 | 14 | let curStyle: Style = { 15 | box_bg: GM_getValue('box_bg') || '#ffffff', 16 | text_color: GM_getValue('text_color') || '#000000', 17 | font_size: GM_getValue('font_size') || 18, 18 | box_padding: GM_getValue('box_padding') || 30, 19 | box_line_height: GM_getValue('box_line_height') || 100, 20 | font_weight: GM_getValue('font_weight') || 'normal' 21 | }; 22 | 23 | function getText() { 24 | const title_font_size = Math.ceil(curStyle.font_size * 1.6); 25 | let text = `.${locals.rm} .option button { 26 | color: ${curStyle.text_color}; 27 | } 28 | .${locals.rm} { 29 | background-color: ${curStyle.box_bg}; 30 | padding: 15px ${curStyle.box_padding}px; 31 | } 32 | .${locals.rm} .option * { 33 | color: ${curStyle.text_color}; 34 | } 35 | .${locals.rm} .title { 36 | font-size: ${title_font_size}px !important; 37 | border-bottom: 1px solid ${curStyle.text_color}; 38 | color: ${curStyle.text_color} !important; 39 | } 40 | .${locals.rm} .content { 41 | font-size: ${curStyle.font_size}px !important; 42 | line-height: ${curStyle.box_line_height}%; 43 | color: ${curStyle.text_color} !important; 44 | font-weight: ${curStyle.font_weight} !important; 45 | } 46 | .${locals.rm} h1, 47 | .${locals.rm} h2, 48 | .${locals.rm} h3, 49 | .${locals.rm} h4, 50 | .${locals.rm} h5, 51 | .${locals.rm} h6 { 52 | font-weight: ${curStyle.font_weight} !important; 53 | }`; 54 | //添加h1-h6的字号 55 | for (let i = 6; i >= 1; i--) { 56 | text += `.${locals.rm} h${i} { font-size: ${curStyle.font_size + (7 - i) * 2}px; }`; 57 | } 58 | return text; 59 | } 60 | 61 | let element: HTMLElement | undefined = undefined; 62 | export function update() { 63 | if (typeof element === 'undefined') { 64 | element = document.createElement('style'); 65 | document.body.appendChild(element); 66 | } 67 | element.innerHTML = getText(); 68 | } 69 | 70 | export function set(name: string, value: any) { 71 | GM_setValue(name, value); 72 | curStyle[name] = value; 73 | update(); 74 | handlers.forEach(it => it()); 75 | } 76 | 77 | export function get(name?: string) { 78 | if (!name) return curStyle; 79 | return curStyle[name]; 80 | } 81 | 82 | const handlers: any[] = []; 83 | export function off(handler: any) { 84 | if (handlers.includes(handler)) { 85 | handlers.splice(handlers.indexOf(handler), 1); 86 | } 87 | } 88 | export function on(handler: any) { 89 | handlers.push(handler); 90 | } 91 | 92 | const presets: Style[] = JSON.parse(GM_getValue('preset') || '[]'); 93 | export const preset = { 94 | get: () => { 95 | return presets; 96 | }, 97 | add: (name: string, style: Style) => { 98 | presets.push({ 99 | ...style, 100 | name 101 | }); 102 | GM_setValue("preset", JSON.stringify(presets)); 103 | handlers.forEach(it => it()); 104 | }, 105 | use: (index: number) => { 106 | if (presets[index]) { 107 | curStyle = presets[index]; 108 | Object.keys(curStyle).forEach(k => { 109 | GM_getValue(k, curStyle[k]); 110 | }); 111 | update(); 112 | handlers.forEach(it => it()); 113 | } 114 | } 115 | } -------------------------------------------------------------------------------- /Reading_Mode/src/utils.ts: -------------------------------------------------------------------------------- 1 | export function trimNewLines(s: string) { 2 | return s.replace(/^[\s\n]+/, "").replace(/[\s\n]+$/, ""); 3 | }; 4 | 5 | export function removeAllStyle(el: HTMLElement) { 6 | Array.prototype.forEach.call(el.children, child => removeAllStyle(child)); 7 | //部分网站有防抓取的代码 8 | if (el.style.display === 'none' || el.style.fontSize === '0px' || el.style.visibility === 'hidden') { 9 | el.remove(); 10 | } else { 11 | el.removeAttribute('id'); 12 | el.removeAttribute('color'); 13 | el.removeAttribute('class'); 14 | el.removeAttribute('style'); 15 | } 16 | }; 17 | 18 | export function getHtml(selector: string) { 19 | const el = document.querySelector(selector); 20 | if (el) { 21 | return el.innerHTML; 22 | } else { 23 | return ''; 24 | } 25 | } 26 | 27 | export function getContent(selector: string) { 28 | const el = document.querySelector(selector); 29 | if (!el) return ''; 30 | const n = el.cloneNode(true) as HTMLElement; 31 | removeAllStyle(n); 32 | return trimNewLines(n.innerHTML); 33 | } -------------------------------------------------------------------------------- /Short_URL/README.md: -------------------------------------------------------------------------------- 1 | # Short URL 2 | 3 | 为网页快速生成多个短网址 4 | 5 | 使用方式: 6 | 7 | * 所有浏览器都可以通过GM菜单生成当前页面的短网址 8 | 9 | * Firefox右键单击页面空白处可以生成当前页面的短网址 10 | 11 | * Firefox右键单击页面中的链接可以生成此链接的短网址(不支持动态添加的链接) 12 | 13 | * [【点击安装】](https://userscript.firefoxcn.net/js/Short_URL.user.js) 14 | 15 | * author: 泷涯 16 | 17 | ## changelog 18 | 19 | **12** 20 | 21 | 修复偶尔会出现的小错误 22 | 23 | **11** 24 | 25 | 修复一处BUG 26 | 27 | **10** 28 | 29 | 支持为网页中的链接快捷生成短网址 30 | 31 | 移除了两个生成接口 32 | 33 | **9** 34 | 35 | 不再向iframe中添加右键菜单 36 | 37 | **8** 38 | 39 | 修复几处语法错误 40 | 41 | **7** 42 | 43 | 兼容TamperMonkey 44 | 45 | **6** 46 | 47 | 增加网页右键菜单的支持 48 | 49 | **5** 50 | 51 | 修复界面BUG 52 | 53 | **4** 54 | 55 | 添加API 56 | 57 | **3** 58 | 59 | 修复界面BUG 60 | 61 | **2** 62 | 63 | 添加API -------------------------------------------------------------------------------- /Short_URL/Short_URL.user.js: -------------------------------------------------------------------------------- 1 | //forEach兼容 2 | if (!NodeList.prototype.forEach) { 3 | NodeList.prototype.forEach = function(callback, thisArg) { 4 | [].forEach.call(this, callback); 5 | }; 6 | } 7 | //API 8 | var apis = { 9 | "sina": function(url, rawParam, callback) { 10 | GM_xmlhttpRequest({ 11 | "url": 'http://api.t.sina.com.cn/short_url/shorten.json?source=1681459862&url_long=' + encodeURIComponent(url), 12 | "method": 'GET', 13 | "timeout": 2000, 14 | "onload": function(response) { 15 | response = JSON.parse(response.responseText); 16 | callback(rawParam, response[0].url_short); 17 | }, 18 | "ontimeout": function() { 19 | callback(rawParam, ''); 20 | } 21 | }); 22 | }, 23 | "baidu": function(url, rawParam, callback) { 24 | GM_xmlhttpRequest({ 25 | "url": 'http://dwz.cn/create.php', 26 | "method": 'POST', 27 | "data": 'url=' + encodeURIComponent(url), 28 | "headers": { 29 | "Content-Type": "application/x-www-form-urlencoded" 30 | }, 31 | "timeout": 2000, 32 | "onload": function(response) { 33 | response = JSON.parse(response.responseText); 34 | callback(rawParam, response.tinyurl); 35 | }, 36 | "ontimeout": function() { 37 | callback(rawParam, ''); 38 | } 39 | }); 40 | }, 41 | "ni2": function(url, rawParam, callback) { 42 | GM_xmlhttpRequest({ 43 | "url": 'http://ni2.org/api/create.json', 44 | "method": 'POST', 45 | "data": 'url=' + encodeURIComponent(url), 46 | "headers": { 47 | "Content-Type": "application/x-www-form-urlencoded" 48 | }, 49 | "timeout": 2000, 50 | "onload": function(response) { 51 | response = JSON.parse(response.responseText); 52 | callback(rawParam, response.url); 53 | }, 54 | "ontimeout": function() { 55 | callback(rawParam, ''); 56 | } 57 | }); 58 | }, 59 | "suoim": function(url, rawParam, callback) { 60 | GM_xmlhttpRequest({ 61 | "url": 'http://suo.im/api.php?format=json&url=' + encodeURIComponent(url), 62 | "method": 'GET', 63 | "timeout": 2000, 64 | "onload": function(response) { 65 | response = JSON.parse(response.responseText); 66 | callback(rawParam, response.url); 67 | }, 68 | "ontimeout": function() { 69 | callback(rawParam, ''); 70 | } 71 | }); 72 | }, 73 | "fiftyr": function(url, rawParam, callback) { 74 | GM_xmlhttpRequest({ 75 | "url": 'http://50r.cn/urls/add.json?url=' + encodeURIComponent(url), 76 | "method": 'GET', 77 | "timeout": 2000, 78 | "onload": function(response) { 79 | response = JSON.parse(response.responseText); 80 | callback(rawParam, response.url); 81 | }, 82 | "ontimeout": function() { 83 | callback(rawParam, ''); 84 | } 85 | }); 86 | }, 87 | "sixdu": function(url, rawParam, callback) { 88 | GM_xmlhttpRequest({ 89 | "url": 'http://6du.in/?is_api=1&lurl=' + encodeURIComponent(url), 90 | "method": 'GET', 91 | "timeout": 2000, 92 | "onload": function(response) { 93 | callback(rawParam, response.responseText); 94 | }, 95 | "ontimeout": function() { 96 | callback(rawParam, ''); 97 | } 98 | }); 99 | } 100 | }; 101 | var apiList = {'sina': '新浪', 'baidu': '百度', 'ni2': 'NI2', 'suoim': '缩我', 'fiftyr': '50r', 'sixdu': '六度'}; 102 | //界面 103 | var css = '\ 104 | .sy_shorturl_main {\ 105 | position: fixed;\ 106 | top: 50%;\ 107 | left: 50%;\ 108 | background-color: #FFF;\ 109 | border: 1px solid #CCC;\ 110 | border-radius: 5px;\ 111 | height: 200px;\ 112 | width: 660px;\ 113 | margin: -100px -330px;\ 114 | box-shadow: 0 0 10px #CCC;\ 115 | padding: 15px;\ 116 | box-sizing: border-box;\ 117 | display: none;\ 118 | z-index: 9999;\ 119 | color: #000;\ 120 | }\ 121 | .sy_shorturl_main .sy_item {\ 122 | width: 314px;\ 123 | margin-bottom: 10px;\ 124 | font-size: 15px;\ 125 | display: inline-block;\ 126 | }\ 127 | .sy_shorturl_main .sy_name {\ 128 | display: inline-block;\ 129 | width: 40px;\ 130 | }\ 131 | .sy_shorturl_main .sy_btn {\ 132 | display:inline-block;\ 133 | padding:6px 12px;\ 134 | font-size:14px;\ 135 | vertical-align:middle;\ 136 | touch-action:manipulation;\ 137 | cursor:pointer;\ 138 | user-select:none;\ 139 | border:1px solid transparent;\ 140 | border-radius:4px;\ 141 | color:#fff;\ 142 | background-color:#428bca;\ 143 | border-color:#357ebd;\ 144 | text-align: center;\ 145 | background-image: none;\ 146 | box-sizing: border-box;\ 147 | }\ 148 | .sy_shorturl_main .sy_btn:hover,\ 149 | .sy_shorturl_main .sy_btn:active {\ 150 | color:#fff;\ 151 | background-color:#3071a9;\ 152 | border-color:#285e8e;\ 153 | }\ 154 | .sy_shorturl_main .sy_btn:active {\ 155 | box-shadow:inset 0 3px 5px rgba(0,0,0,.125)\ 156 | }\ 157 | .sy_shorturl_main .sy_btn[disabled],\ 158 | .sy_shorturl_main .sy_btn[disabled]:hover,\ 159 | .sy_shorturl_main .sy_btn[disabled].active {\ 160 | background-color:#428bca;\ 161 | border-color:#357ebd;\ 162 | pointer-events:none;\ 163 | cursor:not-allowed;\ 164 | box-shadow:none;\ 165 | opacity:.65;\ 166 | }\ 167 | .sy_btn_block {\ 168 | display: block;\ 169 | width: 100%;\ 170 | }\ 171 | .sy_shorturl_main .input {\ 172 | display: inline-block;\ 173 | width: 200px;\ 174 | height:34px;\ 175 | padding:6px 12px;\ 176 | font-size:14px;\ 177 | line-height:1.42857143;\ 178 | color:#555;\ 179 | background-color:#fff;\ 180 | background-image:none;\ 181 | border:1px solid #ccc;\ 182 | border-radius:4px;\ 183 | box-shadow:inset 0 1px 1px rgba(0,0,0,.075);\ 184 | transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;\ 185 | box-sizing: border-box;\ 186 | vertical-align:middle;\ 187 | margin-left: 5px;\ 188 | margin-right: 5px;\ 189 | }\ 190 | .sy_shorturl_main .input:focus {\ 191 | border-color:#66afe9;\ 192 | outline:0;\ 193 | box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)\ 194 | }'; 195 | GM_addStyle(css); 196 | var body = document.getElementsByTagName('body')[0]; 197 | var main = document.createElement('div'); 198 | main.setAttribute('id', 'sy_shorturl_main'); 199 | main.setAttribute('class', 'sy_shorturl_main'); 200 | body.appendChild(main); 201 | //主要事件调用 202 | var createUrl = function(url) { 203 | if (typeof(url) !== 'string') { 204 | url = window.location.href; 205 | } 206 | var mainDiv = document.getElementById('sy_shorturl_main'); 207 | if (main.getAttribute('data-url') === url) { 208 | mainDiv.style.display = "block"; 209 | return; 210 | } 211 | mainDiv.innerHTML = ''; 212 | var el = {}; 213 | for (var i in apiList) { 214 | el[i] = document.createElement('div'); 215 | el[i].setAttribute('class', 'sy_item'); 216 | el[i].innerHTML = '' + apiList[i] + ''; 217 | mainDiv.appendChild(el[i]); 218 | apis[i](url, el[i], function(rawParam, result) { 219 | if (result === undefined || result === '') { 220 | rawParam.querySelector('input').value = "生成失败"; 221 | } else { 222 | rawParam.querySelector('input').value = result; 223 | rawParam.querySelector('input').addEventListener('focus', function() { 224 | this.setSelectionRange(0, this.value.length); 225 | }, false); 226 | rawParam.querySelector('button').removeAttribute('disabled'); 227 | rawParam.querySelector('button').addEventListener('click', function() { 228 | GM_setClipboard(this.parentElement.querySelector('input').value); 229 | }, false); 230 | } 231 | }); 232 | } 233 | var close = document.createElement('div'); 234 | close.innerHTML = ''; 235 | close.querySelector('button').addEventListener('click', function() { 236 | this.parentElement.parentElement.style.display = "none"; 237 | }); 238 | mainDiv.appendChild(close); 239 | mainDiv.setAttribute('data-url', url); 240 | mainDiv.style.display = "block"; 241 | }; 242 | //绑定按钮啥的 243 | GM_registerMenuCommand('生成短网址', createUrl); 244 | //HTML5添加网页右键菜单 245 | //检查浏览器是否支持HTML5右键菜单,目前只有Firefox支持此属性 246 | var support_rclick = function() { 247 | if (!/Firefox\/(\d+)/.test(navigator.userAgent)) { 248 | return false; 249 | } 250 | return parseInt(navigator.userAgent.match(/Firefox\/(\d+)/)[1]) > 9; 251 | }; 252 | if (window.top === window.self && window.location.href !== '' && support_rclick()) { 253 | var rclickMenu; 254 | if (body.getAttribute('contextmenu') === null) { 255 | body.setAttribute('contextmenu','popup-menu'); 256 | rclickMenu = document.createElement('menu'); 257 | rclickMenu.setAttribute('type','context'); 258 | rclickMenu.setAttribute('id', 'popup-menu'); 259 | body.appendChild(rclickMenu); 260 | } else { 261 | rclickMenu = document.getElementById(body.getAttribute('contextmenu')); 262 | } 263 | var imenu = document.createElement('menuitem'); 264 | imenu.setAttribute("id", 'sy_shorturl'); 265 | imenu.setAttribute('label', '生成短网址'); 266 | imenu.innerHTML = '生成短网址'; 267 | rclickMenu.appendChild(imenu); 268 | imenu.addEventListener("click", createUrl, false); 269 | //为a标签添加快速生成的功能 270 | var aRclickMenu = document.createElement('menu'); 271 | aRclickMenu.setAttribute('type','context'); 272 | aRclickMenu.setAttribute('id', 'a-popup-menu'); 273 | body.appendChild(aRclickMenu); 274 | var aImenu = document.createElement('menuitem'); 275 | aImenu.setAttribute("id", 'sy_shorturl_a'); 276 | aImenu.setAttribute('label', '为此链接生成短网址'); 277 | aImenu.innerHTML = '为此链接生成短网址'; 278 | aRclickMenu.appendChild(aImenu); 279 | aImenu.addEventListener("click", function(){ 280 | var url = this.getAttribute('data-url'); 281 | //对于相对路径,先拼接完整路径 282 | if (url.indexOf('/') === 0) { 283 | url = window.location.href.match(/(.*?):\/\/(.*?)\//)[0] + url.substr(1); 284 | } else { 285 | url = [window.location.protocol, '//', window.location.hostname, window.location.pathname.match(/(.*)\//)[0], url].join(''); 286 | } 287 | createUrl(url); 288 | }, false); 289 | document.querySelectorAll('a').forEach(function(element) { 290 | var url = element.getAttribute('href'); 291 | if (url === null) { 292 | return; 293 | } 294 | if (/^(.*?):/.test(url)) { 295 | //部分特殊链接(例如磁力链接、应用链接等)不进行生成 296 | if (url.substr(0, 7) !== 'http://' && url.substr(0, 8) !== 'https://' && url.substr(0, 6) !== 'ftp://') { 297 | return; 298 | } 299 | } 300 | element.setAttribute('contextmenu','a-popup-menu'); 301 | element.addEventListener('contextmenu', function() { 302 | aImenu.setAttribute('data-url', this.getAttribute('href')); 303 | }, false); 304 | }); 305 | } -------------------------------------------------------------------------------- /Short_URL/meta.yml: -------------------------------------------------------------------------------- 1 | name: Short URL 2 | namespace: blog.sylingd.com 3 | description: 短网址生成器 4 | author: ShuangYa 5 | include: 6 | - 'http://*' 7 | - 'https://*' 8 | grant: 9 | - GM_xmlhttpRequest 10 | - GM_addStyle 11 | - GM_setClipboard 12 | - GM_registerMenuCommand 13 | connect: '*' 14 | version: '12' 15 | -------------------------------------------------------------------------------- /Steam_Charge/Steam_Charge.user.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | const container = document.getElementById("prices_user"); 3 | 4 | // 获取转换率 5 | const firstItem = container.querySelector(".addfunds_area_purchase_game"); 6 | const newItem = firstItem.cloneNode(true); 7 | const displayAmount = newItem 8 | .querySelector(".game_purchase_price") 9 | .innerText.trim(); 10 | newItem.querySelector(".game_purchase_price").remove(); 11 | const submit = newItem.querySelector("[data-currency]"); 12 | const realAmount = submit.getAttribute("data-amount"); 13 | 14 | // 填入输入框 15 | const t = Array.from(newItem.children).find((x) => x.tagName === "H1"); 16 | const tips = Array.from(newItem.children).find((x) => x.tagName === "P"); 17 | 18 | t.innerHTML = ""; 19 | tips.innerHTML = realAmount + " = " + displayAmount; 20 | 21 | // 提交 22 | submit.removeAttribute("onclick"); 23 | submit.addEventListener("click", () => { 24 | document.getElementById("input_amount").value = t.querySelector("input").value; 25 | document.getElementById("input_currency").value = submit.getAttribute("data-currency"); 26 | document.getElementById("form_addfunds").submit(); 27 | }); 28 | 29 | container.insertBefore(newItem, container.children[0]); 30 | })(); 31 | -------------------------------------------------------------------------------- /Steam_Charge/meta.yml: -------------------------------------------------------------------------------- 1 | name: Steam 自定义充值 2 | namespace: 'https://blog.sylingd.com' 3 | description: 自行输入一次性往 Steam 钱包充值数量 4 | author: ShuangYa 5 | version: '1.0.1' 6 | match: 'https://store.steampowered.com/steamaccount/addfunds*' 7 | -------------------------------------------------------------------------------- /Straight_Baidu/README.md: -------------------------------------------------------------------------------- 1 | # Straight Baidu # 2 | 3 | * [【点击安装】](https://userscript.firefoxcn.net/js/Straight_Baidu.user.js) 4 | * author: 柏拉图样图森破 5 | * 去掉百度重定向 6 | * version: 1 -------------------------------------------------------------------------------- /Straight_Baidu/Straight_Baidu.user.js: -------------------------------------------------------------------------------- 1 | ;(function(selector, MutationObserver){ 2 | 3 | function replaceUrl(){ 4 | Array.prototype.forEach.call( 5 | document.querySelectorAll(selector), 6 | function(i){ 7 | GM_xmlhttpRequest({ 8 | method: "GET", 9 | url: i.href, 10 | onload: function(r){i.href = r.finalUrl;} 11 | }); 12 | }); 13 | } 14 | 15 | new MutationObserver(function(m) { 16 | m.forEach(function(i){ 17 | (i.addedNodes.length === 2) 18 | && replaceUrl(); 19 | }); 20 | }).observe(document.body, {childList: true}); 21 | 22 | replaceUrl(); 23 | 24 | })(".t a,.c-abstract a,.c-offset a", 25 | MutationObserver || WebKitMutationObserver || MozMutationObserver); -------------------------------------------------------------------------------- /Straight_Baidu/meta.yml: -------------------------------------------------------------------------------- 1 | name: Straight Baidu 2 | namespace: 'http://firefoxbar.github.io/#userscripts' 3 | include: 'https://www.baidu.com/*' 4 | description: 去掉百度搜索结果重定向 5 | version: '1' 6 | grant: GM_xmlhttpRequest 7 | author: Paltoo Young 8 | -------------------------------------------------------------------------------- /Tieba_Blocked_Detect/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true 5 | }, 6 | "extends": "eslint:recommended", 7 | "rules": { 8 | "indent": [ 9 | "error", 10 | "tab", 11 | { 12 | "SwitchCase": 1 13 | } 14 | ], 15 | "linebreak-style": [ 16 | "error", 17 | "windows" 18 | ], 19 | "quotes": [ 20 | "error", 21 | "single" 22 | ], 23 | "semi": [ 24 | "error", 25 | "always" 26 | ], 27 | "no-console": "warn", 28 | "no-unused-vars": "warn", 29 | "no-unreachable": "warn", 30 | "no-param-reassign": "warn" 31 | } 32 | } -------------------------------------------------------------------------------- /Tieba_Blocked_Detect/README.md: -------------------------------------------------------------------------------- 1 | # 贴吧贴子屏蔽检测 2 | 3 | 检测自己发表的贴子是否被百度屏蔽为仅自己可见 4 | 5 | 6 | ## 安装 7 | 8 | - [Install From GitHub](https://userscript.firefoxcn.net/js/Tieba_Blocked_Detect.user.js) 9 | - [Install From GreasyFork](https://greasyfork.org/zh-CN/scripts/383981) 10 | 11 | ## 使用说明 12 | 13 | 无 14 | 15 | 如果要说些什么的话,想问问为什么你还在用贴吧呢? 16 | 17 | 18 | ## 注意事项 19 | 20 | - 回复贴子(含楼中楼)时直接显示的回复没有包含 pid,所以此时无法检测回复是否被屏蔽 21 | 22 | - 回复贴子(含楼中楼)后,至少在 5~10 秒内贴子 ~~不会被屏蔽~~ 会被默认屏蔽,在此时间内刷新时的结果可能不可靠(脚本会延迟 5 秒后再请求结果),且可能会被脚本缓存屏蔽状态 23 | 24 | - 超过 1 页的楼中楼不会被检测,因为楼中楼翻页时不会显示被屏蔽的楼中楼 25 | 26 | ![](https://greasyfork.org/system/screenshots/screenshots/000/015/611/original/QQ%E6%88%AA%E5%9B%BE20190601001053.png?1559319741) 27 | _贴吧页被屏蔽贴子效果_ 28 | 29 | ![](https://greasyfork.org/system/screenshots/screenshots/000/015/612/original/QQ%E6%88%AA%E5%9B%BE20190601001026.png?1559319741) 30 | _贴子页被屏蔽贴子效果_ 31 | 32 | ![](https://greasyfork.org/system/screenshots/screenshots/000/015/613/original/QQ%E6%88%AA%E5%9B%BE20190601001012.png?1559319741) 33 | _贴子页被屏蔽回复与楼中楼效果_ 34 | 35 | 36 | ## 脚本信息 37 | - Author: 864907600cc (@ccloli) 38 | - License: GPLv3 -------------------------------------------------------------------------------- /Tieba_Blocked_Detect/Tieba_Blocked_Detect.user.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | let threadCache = {}; 4 | let replyCache = {}; 5 | 6 | /** 7 | * 精简封装 fetch 请求,自带请求 + 通用配置 + 自动 .text() 8 | * 9 | * @param {string} url - 请求 URL 10 | * @param {object} [options={}] - fetch Request 配置 11 | * @returns {Promise} fetch 请求 12 | */ 13 | const request = (url, options = {}) => fetch(url, Object.assign({ 14 | credentials: 'omit', 15 | // 部分贴吧(如 firefox 吧)会强制跳转回 http 16 | redirect: 'follow', 17 | // 阻止浏览器发出 CORS 检测的 HEAD 请求头 18 | mode: 'same-origin', 19 | headers: { 20 | 'X-Requested-With': 'XMLHttpRequest' 21 | } 22 | }, options)).then(res => res.text()); 23 | 24 | /** 25 | * 延迟执行 26 | * 27 | * @param {number} time - 延迟毫秒数 28 | */ 29 | const sleep = time => new Promise(resolve => setTimeout(resolve, time)); 30 | 31 | /** 32 | * 获取当前用户是否登录 33 | * 34 | * @returns {number|boolean} 是否登录,若已登录,贴吧页为 1,贴子页为 true 35 | */ 36 | const getIsLogin = () => window.PageData.user.is_login; 37 | 38 | /** 39 | * 获取当前用户的用户名 40 | * 41 | * @returns {string} 用户名 42 | */ 43 | const getUsername = () => window.PageData.user.name || window.PageData.user.user_name; 44 | 45 | /** 46 | * 获取当前用户的 portrait(适用于无用户名) 47 | * 48 | * @returns {string} portrait 49 | */ 50 | const getPortrait = () => window.PageData.user.portrait.split('?').shift(); 51 | 52 | /** 53 | * 获取 \u 形式的 unicode 字符串 54 | * 55 | * @param {string} str - 需要转码的字符串 56 | * @returns {string} 转码后的字符串 57 | */ 58 | const getEscapeString = str => escape(str).replace(/%/g, '\\').toLowerCase(); 59 | 60 | /** 61 | * 获取主题贴的移动端地址 62 | * 63 | * @param {number} tid - 贴子 id 64 | * @returns {string} URL 65 | */ 66 | const getThreadMoUrl = tid => `//tieba.baidu.com/mo/q-----1-1-0----/m?kz=${tid}`; 67 | 68 | /** 69 | * 获取回复贴的移动端地址 70 | * 71 | * @param {number} tid - 贴子 id 72 | * @param {number} pid - 回复 id 73 | * @param {number} [pn=0] - 页码 74 | * @returns {string} URL 75 | */ 76 | const getReplyMoUrl = (tid, pid, pn = 0) => `//tieba.baidu.com/mo/q-----1-1-0----/flr?pid=${pid}&kz=${tid}&pn=${pn}`; 77 | 78 | /** 79 | * 获取回复贴的 ajax 地址 80 | * 81 | * @param {number} tid - 贴子 id 82 | * @param {number} pid - 主回复 id 83 | * @param {number} spid - 楼中楼回复 id 84 | * @param {number} [pn=0] - 页码 85 | * @returns {string} URL 86 | */ 87 | const getReplyUrl = (tid, pid, pn = 0) => `//tieba.baidu.com/p/comment?tid=${tid}&pid=${pid}&pn=${pn}&t=${Date.now()}`; 88 | 89 | /** 90 | * 从页面内容判断贴子是否直接消失 91 | * 92 | * @param {string} res - 页面内容 93 | * @returns {boolean} 是否被屏蔽 94 | */ 95 | const threadIsNotExist = res => res.indexOf('您要浏览的贴子不存在') >= 0 || res.indexOf('(共0贴)') >= 0; 96 | 97 | /** 98 | * 获取主题贴是否被屏蔽 99 | * 100 | * @param {number} tid - 贴子 id 101 | * @returns {Promise} 是否被屏蔽 102 | */ 103 | const getThreadBlocked = tid => request(getThreadMoUrl(tid)) 104 | .then(threadIsNotExist); 105 | 106 | /** 107 | * 获取回复贴是否被屏蔽 108 | * 109 | * @param {number} tid - 贴子 id 110 | * @param {number} pid - 回复 id 111 | * @returns {Promise} 是否被屏蔽 112 | */ 113 | const getReplyBlocked = (tid, pid) => request(getReplyMoUrl(tid, pid)) 114 | .then(res => threadIsNotExist(res) || res.indexOf('刷新
楼. 
') >= 0); 115 | 116 | /** 117 | * 获取楼中楼是否被屏蔽 118 | * 119 | * @param {number} tid - 贴子 id 120 | * @param {number} pid - 主回复 id 121 | * @param {number} spid - 楼中楼回复 id 122 | * @returns {Promise} 是否被屏蔽 123 | */ 124 | const getLzlBlocked = (tid, pid, spid) => request(getReplyUrl(tid, pid)) 125 | // 楼中楼 ajax 翻页后被屏蔽的楼中楼不会展示,所以不需要考虑 pn,同理不需要考虑不在第一页的楼中楼 126 | .then(res => threadIsNotExist(res) || res.indexOf(``) < 0); 127 | 128 | /** 129 | * 获取触发 CSS 样式 130 | * 131 | * @param {string} username - 用户名 132 | * @returns {string} 样式表 133 | */ 134 | const getTriggerStyle = ({ username, portrait }) => { 135 | const escapedUsername = getEscapeString(username).replace(/\\/g, '\\\\'); 136 | 137 | return ` 138 | /* 使用 animation 监测 DOM 变化 */ 139 | @-webkit-keyframes __tieba_blocked_detect__ {} 140 | @-moz-keyframes __tieba_blocked_detect__ {} 141 | @keyframes __tieba_blocked_detect__ {} 142 | 143 | /* 主题贴 */ 144 | #thread_list .j_thread_list[data-field*='"author_name":"${escapedUsername}"'], 145 | #thread_list .j_thread_list[data-field*='"author_portrait":"${portrait}"'], 146 | /* 回复贴 */ 147 | #j_p_postlist .l_post[data-field*='"user_name":"${escapedUsername}"'], 148 | #j_p_postlist .l_post[data-field*='"portrait":"${portrait}"'], 149 | /* 楼中楼 */ 150 | .j_lzl_m_w .lzl_single_post[data-field*="'user_name':'${username}'"], 151 | .j_lzl_m_w .lzl_single_post[data-field*="'portrait':'${portrait}'"] { 152 | -webkit-animation: __tieba_blocked_detect__; 153 | -moz-animation: __tieba_blocked_detect__; 154 | animation: __tieba_blocked_detect__; 155 | } 156 | 157 | /* 被屏蔽样式 */ 158 | .__tieba_blocked__, 159 | .__tieba_blocked__ .d_post_content_main { 160 | background: rgba(255, 0, 0, 0.05); 161 | position: relative; 162 | } 163 | .__tieba_blocked__.core_title { 164 | background: #fae2e3; 165 | } 166 | .__tieba_blocked__::before { 167 | background: #f22737; 168 | position: absolute; 169 | padding: 5px 10px; 170 | color: #ffffff; 171 | font-size: 14px; 172 | line-height: 1.5em; 173 | z-index: 399; 174 | } 175 | .__tieba_blocked__.lzl_single_post { 176 | margin-left: -15px; 177 | margin-right: -15px; 178 | margin-bottom: -6px; 179 | padding-left: 15px; 180 | padding-right: 15px; 181 | padding-bottom: 6px; 182 | } 183 | 184 | .__tieba_blocked__.j_thread_list::before, 185 | .__tieba_blocked__.core_title::before { 186 | content: '该贴已被屏蔽'; 187 | right: 0; 188 | top: 0; 189 | } 190 | .__tieba_blocked__.l_post::before { 191 | content: '该楼层已被屏蔽'; 192 | right: 0; 193 | top: 0; 194 | } 195 | .__tieba_blocked__.lzl_single_post::before { 196 | content: '该楼中楼已被屏蔽'; 197 | left: 0; 198 | bottom: 0; 199 | } 200 | `; 201 | }; 202 | 203 | /** 204 | * 检测贴子/回复屏蔽回调函数 205 | * 206 | * @param {AnimationEvent} event - 触发的事件对象 207 | */ 208 | const detectBlocked = (event) => { 209 | if (event.animationName !== '__tieba_blocked_detect__') { 210 | return; 211 | } 212 | 213 | const { target } = event; 214 | const { classList } = target; 215 | let checker; 216 | 217 | if (classList.contains('j_thread_list')) { 218 | const tid = target.dataset.tid; 219 | if (threadCache[tid] !== undefined) { 220 | checker = threadCache[tid]; 221 | } 222 | else { 223 | checker = getThreadBlocked(tid).then(result => { 224 | threadCache[tid] = result; 225 | // saveCache('thread'); 226 | 227 | return result; 228 | }); 229 | } 230 | } 231 | else if (classList.contains('l_post')) { 232 | const tid = window.PageData.thread.thread_id; 233 | const pid = target.dataset.pid || ''; 234 | if (!pid) { 235 | // 新回复可能没有 pid 236 | return; 237 | } 238 | 239 | if (replyCache[pid] !== undefined) { 240 | checker = replyCache[pid]; 241 | } 242 | else { 243 | // 回复时直接取值结果不准确,延迟 5 秒后请求 244 | checker = sleep(5000).then(() => getReplyBlocked(tid, pid).then(result => { 245 | replyCache[pid] = result; 246 | // saveCache('reply'); 247 | try { 248 | if (result && JSON.parse(target.dataset.field).content.post_no === 1) { 249 | document.querySelector('.core_title').classList.add('__tieba_blocked__'); 250 | } 251 | } 252 | catch (err) { 253 | // pass through 254 | } 255 | 256 | return result; 257 | })); 258 | } 259 | } 260 | else if (classList.contains('lzl_single_post')) { 261 | const field = target.dataset.field || ''; 262 | const parent = target.parentElement; 263 | const pageNumber = parent.querySelector('.tP'); 264 | if (pageNumber && pageNumber.textContent.trim() !== '1') { 265 | // 翻页后的楼中楼不会显示屏蔽的楼中楼,所以命中的楼中楼一定是不会屏蔽的,不需要处理 266 | return; 267 | } 268 | 269 | const tid = window.PageData.thread.thread_id; 270 | const pid = (field.match(/'pid':'?(\d+)'?/) || [])[1]; 271 | const spid = (field.match(/'spid':'?(\d+)'?/) || [])[1]; 272 | if (!spid) { 273 | // 新回复没有 spid 274 | return; 275 | } 276 | 277 | if (replyCache[spid] !== undefined) { 278 | checker = replyCache[spid]; 279 | } 280 | else { 281 | checker = getLzlBlocked(tid, pid, spid).then(result => { 282 | replyCache[spid] = result; 283 | // saveCache('reply'); 284 | 285 | return result; 286 | }); 287 | } 288 | } 289 | 290 | if (checker) { 291 | Promise.resolve(checker).then(result => { 292 | if (result) { 293 | classList.add('__tieba_blocked__'); 294 | } 295 | }); 296 | } 297 | }; 298 | 299 | /** 300 | * 初始化样式 301 | * 302 | * @param {object} param - 用户参数 303 | */ 304 | const initStyle = (param) => { 305 | const style = document.createElement('style'); 306 | style.textContent = getTriggerStyle(param); 307 | document.head.appendChild(style); 308 | }; 309 | 310 | /** 311 | * 初始化事件监听 312 | * 313 | */ 314 | const initListener = () => { 315 | document.addEventListener('webkitAnimationStart', detectBlocked, false); 316 | document.addEventListener('MSAnimationStart', detectBlocked, false); 317 | document.addEventListener('animationstart', detectBlocked, false); 318 | }; 319 | 320 | /** 321 | * 加载并没有什么卵用的缓存 322 | * 323 | */ 324 | const loadCache = () => { 325 | const thread = sessionStorage.getItem('tieba-blocked-cache-thread'); 326 | const reply = sessionStorage.getItem('tieba-blocked-cache-reply'); 327 | if (thread) { 328 | try { 329 | threadCache = JSON.parse(thread); 330 | } 331 | catch (error) { 332 | // pass through 333 | } 334 | } 335 | if (reply) { 336 | try { 337 | replyCache = JSON.parse(reply); 338 | } 339 | catch (error) { 340 | // pass through 341 | } 342 | } 343 | }; 344 | 345 | /** 346 | * 保存并没有什么卵用的缓存 347 | * 348 | * @param {string} key - 缓存 key 349 | */ 350 | const saveCache = (key) => { 351 | if (key === 'thread') { 352 | sessionStorage.setItem('tieba-blocked-cache-thread', JSON.stringify(threadCache)); 353 | } 354 | else if (key === 'reply') { 355 | sessionStorage.setItem('tieba-blocked-cache-reply', JSON.stringify(replyCache)); 356 | } 357 | }; 358 | 359 | /** 360 | * 初始化执行 361 | * 362 | */ 363 | const init = () => { 364 | if (getIsLogin()) { 365 | const username = getUsername(); 366 | const portrait = getPortrait(); 367 | // loadCache(); 368 | initListener(); 369 | initStyle({ username, portrait }); 370 | } 371 | }; 372 | 373 | init(); -------------------------------------------------------------------------------- /Tieba_Blocked_Detect/meta.yml: -------------------------------------------------------------------------------- 1 | name: 贴吧贴子屏蔽检测 2 | version: 1.1.2 3 | description: 贴吧都快凉了,过去的痕迹都没了,你为什么还在刷贴吧呢?你们建个群不好吗? 4 | match: '*://tieba.baidu.com/*' 5 | include: '*://tieba.baidu.com/*' 6 | grant: none 7 | author: 864907600cc 8 | icon: 'https://secure.gravatar.com/avatar/147834caf9ccb0a66b2505c753747867' 9 | namespace: 'http://ext.ccloli.com' 10 | license: GPL-3.0 11 | -------------------------------------------------------------------------------- /Tieba_Client/README.md: -------------------------------------------------------------------------------- 1 | # Tieba Client # 2 | 3 | * [【点击安装】](https://userscript.firefoxcn.net/js/Tieba_Client.user.js) 4 | * author: 网络中二行客 5 | * 模拟手机客户端发帖/回帖 6 | * version: 1.0.1 7 | -------------------------------------------------------------------------------- /Tieba_Client/meta.yml: -------------------------------------------------------------------------------- 1 | name: Tieba Client 2 | namespace: 'http://tieba.baidu.com' 3 | include: 'http://tieba.baidu.com/*' 4 | icon: 'http://imgsrc.baidu.com/forum/pic/item/8a65d04bd11373f0075bca22a60f4bfbfaed04c9.jpg' 5 | require: 'http://libs.baidu.com/jquery/2.0.0/jquery.min.js' 6 | grant: GM_xmlhttpRequest 7 | author: 网络中二行客 8 | version: 1.0.2 9 | description: 模拟贴吧客户端发帖。 -------------------------------------------------------------------------------- /Tieba_Kuso/README.md: -------------------------------------------------------------------------------- 1 | # Tieba Kuso # 2 | 3 | * [【点击安装】](https://userscript.firefoxcn.net/js/Tieba_Kuso.user.js) 4 | * author: Paltoo Young 5 | * 贴吧头像恶搞 6 | * version: 0.1 7 | -------------------------------------------------------------------------------- /Tieba_Kuso/Tieba_Kuso.user.js: -------------------------------------------------------------------------------- 1 | (function(config){ 2 | 3 | var canvas,ctx,avatar,userName,postNo,tip,postTip,render,selected; 4 | var editor = document.querySelector('#ueditor_replace'), 5 | fName = document.head.querySelectorAll('meta')[1].getAttribute('fname'), 6 | cssText = '.d_name:after{color: #b0b0b0;content: "\u2764";\ 7 | cursor: pointer;display: inline-block;padding-left: 5px;}\ 8 | #canvas-wrap{background:#e2e2e2;float:right;width:533px;height:410px;position:relative;}\ 9 | #ks-tip{text-align:center;line-height:30px;}\ 10 | #canvas{position:absolute;margin:auto;left:0;right:0;top:0;bottom:0;}\ 11 | #kuso-cate{float:left;max-width:364px;}\ 12 | #kuso-cate-hd a{display:inline-block;text-align:center;}\ 13 | #kuso-cate-hd .cu{font-weight:bold;background-color:#ededed;color:#000000;}\ 14 | #kuso-cate-bd {height:380px;overflow-y:auto;}\ 15 | #kuso-cate-bd>div{display:none;}\ 16 | #kuso-cate-bd>div:first-child{display:block;}\ 17 | #kuso-cate-bd li{float:left;margin:10px;}\ 18 | #kuso-cate-bd img{max-height:150px;max-width:140px;}\ 19 | #kuso-cate a{line-height:30px;padding:0 10px;}\ 20 | #kuso-cate-bd li a{display:inline-block;}\ 21 | #kuso-cate-bd li a:hover img {box-shadow: 2px 2px 2px #999999;}\ 22 | .dialogJshadow a{cursor:pointer;}', 23 | html = '
\ 24 |
\ 25 |
 \ 26 |
\ 27 | 请点击选择要使用的效果
\ 28 |
\ 29 |
\ 30 |
\ 31 |

\ 32 |
\ 33 |
\ 34 | \ 35 | 确 定\ 36 | 取 消

'; 37 | var style = document.createElement('style'); 38 | style.innerHTML = cssText; 39 | 40 | var _ = { 41 | subStr: function(s,i){ 42 | return s.replace(/[^\x00-\xff]/gi, '--').length > i 43 | ? s.substr(0,i/2-1)+"…" : s; 44 | }, 45 | setCvsSize: function(w,h){ 46 | canvas.width = w; 47 | canvas.height = h; 48 | canvas.style.width = w + 'px'; 49 | canvas.style.height = h + 'px'; 50 | }, 51 | preDraw: function(src,callback){ 52 | var img = new Image(); 53 | img.src = src; 54 | if(img.complete){ 55 | return callback.call(img); 56 | } 57 | img.onload = function(){ 58 | callback.call(img); 59 | } 60 | }, 61 | ajax: function (method,url,callback){ 62 | var xhr = new XMLHttpRequest; 63 | xhr.open(method || 'GET',url); 64 | xhr.send(); 65 | xhr.onreadystatechange = function(e){ 66 | if (xhr.readyState === 4){ 67 | callback(e); 68 | } 69 | } 70 | }, 71 | dataUrlToBlob: function (dataURL) { 72 | var mimeString = dataURL.split(',')[0].split(':')[1].split(';')[0]; 73 | var byteString = atob(dataURL.split(',')[1]); 74 | var ab = new ArrayBuffer(byteString.length); 75 | var ia = new Uint8Array(ab); 76 | for (var i = 0; i < byteString.length; i++) { 77 | ia[i] = byteString.charCodeAt(i); 78 | } 79 | return new Blob([ab], { type: mimeString }); 80 | }, 81 | getDataURL: function (imgUrl,callback){ 82 | GM_xmlhttpRequest ( { 83 | method:'GET', 84 | url:imgUrl, 85 | onload:function (respDetails) { 86 | var binResp = _.customBase64Encode (respDetails.responseText); 87 | dataURL = 'data:image/png;base64,'+ binResp; 88 | // console.log(dataURL); 89 | callback.call(dataURL); 90 | }, 91 | overrideMimeType: 'text/plain; charset=x-user-defined' 92 | } ); 93 | }, 94 | customBase64Encode: function (inputStr) { 95 | var 96 | bbLen = 3, 97 | enCharLen = 4, 98 | inpLen = inputStr.length, 99 | inx = 0, 100 | jnx, 101 | keyStr = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' 102 | + '0123456789+/=', 103 | output = '', 104 | paddingBytes = 0; 105 | var 106 | bytebuffer = new Array (bbLen), 107 | encodedCharIndexes = new Array (enCharLen); 108 | 109 | while (inx < inpLen) { 110 | for (jnx = 0; jnx < bbLen; ++jnx) { 111 | if (inx < inpLen) { 112 | bytebuffer[jnx] = inputStr.charCodeAt (inx++) & 0xff; 113 | } 114 | else { 115 | bytebuffer[jnx] = 0; 116 | } 117 | } 118 | 119 | encodedCharIndexes[0] = bytebuffer[0] >> 2; 120 | encodedCharIndexes[1] = ( (bytebuffer[0] & 0x3) << 4) | (bytebuffer[1] >> 4); 121 | encodedCharIndexes[2] = ( (bytebuffer[1] & 0x0f) << 2) | (bytebuffer[2] >> 6); 122 | encodedCharIndexes[3] = bytebuffer[2] & 0x3f; 123 | 124 | paddingBytes = inx - (inpLen - 1); 125 | switch (paddingBytes) { 126 | case 1: 127 | encodedCharIndexes[3] = 64; 128 | break; 129 | case 2: 130 | encodedCharIndexes[3] = 64; 131 | encodedCharIndexes[2] = 64; 132 | break; 133 | default: 134 | break; 135 | } 136 | 137 | for (jnx = 0; jnx < enCharLen; ++jnx) 138 | output += keyStr.charAt ( encodedCharIndexes[jnx] ); 139 | } 140 | return output; 141 | } 142 | }; 143 | 144 | /* Callback Hell */ 145 | 146 | var effect = { 147 | wanted : function(img){ 148 | _.setCvsSize(216,313); 149 | _.getDataURL(img.base,function(){ 150 | _.preDraw(this,function(){ 151 | ctx.drawImage(this,0,0); 152 | _.getDataURL(avatar,function(){ 153 | _.preDraw(this,function(){ 154 | ctx.drawImage(this,53,76,110,110); 155 | _.getDataURL(img.compound[0],function(){ 156 | _.preDraw(this,function(){ 157 | ctx.drawImage(this,25,50); 158 | ctx.textAlign = 'center' 159 | ctx.font = '20px 微软雅黑'; 160 | ctx.fillStyle = '#3b2c27'; 161 | ctx.fillText(_.subStr(userName,18),108,246); 162 | ctx.font = '15px 微软雅黑'; 163 | ctx.fillText(_.subStr(fName,20)+'吧',108,270); 164 | }); 165 | }); 166 | }); 167 | }); 168 | }); 169 | }) 170 | }, 171 | pork: function(img){ 172 | _.setCvsSize(189,288); 173 | _.getDataURL(img.base,function(){ 174 | _.preDraw(this,function(){ 175 | ctx.drawImage(this,0,0); 176 | _.getDataURL(avatar,function(){ 177 | _.preDraw(this,function(){ 178 | ctx.drawImage(this,40,78,110,110); 179 | ctx.font = '15px 微软雅黑'; 180 | ctx.fillStyle = '#3b2c27'; 181 | //ctx.fillText("性别:"+"男",40,225); 182 | ctx.textAlign = 'center' 183 | ctx.fillText(_.subStr(userName,18),95,208); 184 | 185 | _.getDataURL(img.compound[0],function(){ 186 | _.preDraw(this,function(){ 187 | ctx.drawImage(this,0,0); 188 | }); 189 | }); 190 | }); 191 | }); 192 | }); 193 | }); 194 | }, 195 | game : function(img){ 196 | _.setCvsSize(154,219); 197 | _.getDataURL(img.base,function(){ 198 | _.preDraw(this,function(){ 199 | ctx.drawImage(this,0,0); 200 | _.getDataURL(avatar,function(){ 201 | _.preDraw(this,function(){ 202 | ctx.drawImage(this,22,47,110,110); 203 | ctx.font = '15px 楷体'; 204 | ctx.fillStyle = '#3b2c27'; 205 | ctx.fillText(_.subStr(userName,18),15,27); 206 | ctx.font = '11px 宋体'; 207 | ctx.fillText('【大水笔·效果】',14,180); 208 | ctx.fillText('经验:+233',18,195); 209 | }); 210 | }); 211 | }); 212 | }) 213 | }, 214 | hs : function(img){ 215 | _.setCvsSize(190,255); 216 | _.getDataURL(avatar,function(){ 217 | _.preDraw(this,function(){ 218 | ctx.drawImage(this,40,16,110,110); 219 | _.getDataURL(img.base,function(){ 220 | _.preDraw(this,function(){ 221 | ctx.drawImage(this,0,0); 222 | }); 223 | }); 224 | }); 225 | }) 226 | }, 227 | hentai: function(img){ 228 | _.setCvsSize(315,405); 229 | _.getDataURL(img.base,function(){ 230 | _.preDraw(this,function(){ 231 | ctx.drawImage(this,0,0); 232 | _.getDataURL(avatar,function(){ 233 | _.preDraw(this,function(){ 234 | ctx.drawImage(this,105,236,110,110); 235 | _.getDataURL(img.compound[0],function(){ 236 | _.preDraw(this,function(){ 237 | ctx.drawImage(this,0,0); 238 | ctx.font = '15px 微软雅黑'; 239 | ctx.fillStyle = '#3b2c27'; 240 | ctx.textAlign = 'center' 241 | ctx.fillText('变态',155,365); 242 | }); 243 | }); 244 | }); 245 | }); 246 | }); 247 | }) 248 | }, 249 | police : function(img){ 250 | _.setCvsSize(533,300); 251 | _.getDataURL(img.base,function(){ 252 | _.preDraw(this,function(){ 253 | ctx.drawImage(this,0,0); 254 | _.getDataURL(avatar,function(){ 255 | _.preDraw(this,function(){ 256 | ctx.rotate(Math.PI / 100 * 3); 257 | ctx.drawImage(this,264,148,48,48); 258 | _.getDataURL(img.compound[0],function(){ 259 | ctx.rotate(-Math.PI / 100 * 3); 260 | _.preDraw(this,function(){ 261 | ctx.drawImage(this,0,0); 262 | ctx.textAlign = 'center' 263 | ctx.font = 'bold 25px 微软雅黑'; 264 | ctx.fillStyle = '#ffffff'; 265 | ctx.strokeStyle = '#000000'; 266 | ctx.strokeText('警察叔叔,就是 @'+userName+' 这个人!',266,287); 267 | ctx.fillText('警察叔叔,就是 @'+userName+' 这个人!',266,287); 268 | }); 269 | }); 270 | }); 271 | }); 272 | }); 273 | }) 274 | } 275 | } 276 | 277 | document.head.appendChild(style); 278 | 279 | var postList = document.querySelector('#j_p_postlist'); 280 | postList.addEventListener('click',function(e){ 281 | var target = e.target; 282 | 'd_name' === target.className && (function(){ 283 | 284 | render = effect.wanted; 285 | selected = config.wanted; 286 | 287 | var face = target.parentNode.querySelector('.p_author_face>IMG'); 288 | //头像中图 289 | avatar = face.src.replace('portrait','portraitm'); 290 | var d = target.parentNode.parentNode.parentNode, 291 | field = JSON.parse(d.dataset.field); 292 | postNo = field.content.post_no, 293 | userName = field.author.user_name; 294 | var dialog = document.createElement('div'); 295 | dialog.className = 'dialogJ dialogJfix dialogJshadow'; 296 | dialog.style.zIndex = '50000'; 297 | dialog.style.width = '900px'; 298 | dialog.style.left = (document.documentElement.clientWidth <= 900 299 | ? 0 : document.documentElement.clientWidth / 2 - 450) + 'px'; 300 | dialog.style.top = (document.documentElement.clientHeight <= 544 301 | ? 0 : document.documentElement.clientHeight / 2 - 272) + 'px'; 302 | dialog.innerHTML = html; 303 | document.body.appendChild(dialog); 304 | 305 | dialog.addEventListener('click',function(e){ 306 | var target = e.target; 307 | if(target.className === 'dialogJclose'){ 308 | document.body.removeChild(this); 309 | } 310 | if(target.className === 'kuso-draw'){ 311 | postTip.innerHTML = selected.description.replace('$1',postNo).replace('$2',userName); 312 | render({ 313 | 'base' : target.src, 314 | 'compound' : selected.compound 315 | }); 316 | } 317 | },false); 318 | 319 | dialog.querySelector('.j_post_setting_submit').addEventListener('click',function(){ 320 | _.ajax('GET','http://tieba.baidu.com/dc/common/imgtbs?t=' + new Date().getTime(),function(e){ 321 | var json = JSON.parse(e.target.responseText); 322 | upload(json.data.tbs); 323 | }); 324 | },false); 325 | dialog.querySelector('.ui_btn_disable').addEventListener('click',function(){ 326 | document.body.removeChild(dialog); 327 | }); 328 | 329 | var cateHd = '', 330 | cateBd = '', 331 | count = 1; 332 | 333 | for (var i in config){ 334 | var h = ""; 335 | config[i].base.forEach(function(i,j){ 336 | h += '
  • '; 337 | }); 338 | cateHd += '' +config[i].name+ ''; 339 | cateBd += '
      ' +h+ '
    '; 340 | } 341 | 342 | dialog.querySelector('#kuso-cate-bd').innerHTML = cateBd; 343 | var tab = dialog.querySelectorAll('#kuso-cate-bd >div'); 344 | var hd = dialog.querySelector('#kuso-cate-hd'); 345 | hd.innerHTML = '
      ' +cateHd + '
    '; 346 | var a = hd.querySelectorAll("a"); 347 | a[0].className = 'cu'; 348 | 349 | hd.addEventListener('click',function(e){ 350 | var target = e.target; 351 | if(target.nodeName === 'A'){ 352 | var dataSet = target.dataset.e; 353 | render = effect[dataSet]; 354 | selected = config[dataSet]; 355 | Array.prototype.forEach.call(a,function(i){ 356 | i.classList.contains('cu') && 357 | i.classList.remove('cu'); 358 | }); 359 | if(!target.classList.contains('cu')){ 360 | target.classList.add('cu'); 361 | } 362 | var c = target.dataset.c; 363 | Array.prototype.forEach.call(tab,function(i){ 364 | i.style.display = 'none'; 365 | if(i.dataset.c === c){ 366 | i.style.display = 'block'; 367 | } 368 | }); 369 | } 370 | }); 371 | 372 | canvas = document.getElementById('canvas'); 373 | ctx = canvas.getContext('2d'); 374 | ctx.globalCompositeOperation = 'source-over'; 375 | tip = document.querySelector('#kuso-tip'); 376 | postTip = document.querySelector('#ks-tip'); 377 | 378 | })(); 379 | },false); 380 | 381 | function upload(tbs) { 382 | var dataURL = canvas.toDataURL() 383 | var blob = _.dataUrlToBlob(dataURL); 384 | 385 | var data = new FormData(); 386 | data.append('file', blob); 387 | data.append('tbs', tbs); 388 | 389 | var fileXHR = new XMLHttpRequest(); 390 | fileXHR.withCredentials = true; 391 | fileXHR.upload.onprogress = progressHandler; 392 | fileXHR.onload = onloadHandler; 393 | fileXHR.open('post','http://upload.tieba.baidu.com/upload/pic',true); 394 | fileXHR.send(data); 395 | } 396 | 397 | function onloadHandler (r) { 398 | try{ 399 | var 400 | r = JSON.parse(r.target.responseText), 401 | fullWidth = r.info.fullpic_width, 402 | fullHeight = r.info.fullpic_height, 403 | picId = r.info.pic_id_encode; 404 | pic = 'http://imgsrc.baidu.com/forum/pic/item/' +picId+ '.jpg'; 405 | document.body.removeChild(document.querySelector('.dialogJshadow')); 406 | window.scrollTo(0,2333333); 407 | var at = document.createElement('p'); 408 | at.innerHTML = selected.description.replace('$1',postNo).replace('$2',userName); 409 | var p = document.createElement('p'); 410 | p.innerHTML = ''; 411 | setTimeout(function(){ 412 | editor.appendChild(at); 413 | editor.appendChild(p); 414 | },500); 415 | } 416 | catch(e){ 417 | tip.innerHTML = '上传出现异常,请重试。'; 418 | } 419 | } 420 | 421 | function progressHandler(e){ 422 | if(e.lengthComputable){ 423 | var howmuch = (e.loaded / e.total) * 100; 424 | tip.innerHTML = '正在上传: ' +Math.ceil(howmuch)+ '%'; 425 | } 426 | } 427 | 428 | })({ 429 | wanted : { 430 | name : "海贼王", 431 | description : "活捉 $1 楼 @$2 大水笔
    ", 432 | base : ["http://imgsrc.baidu.com/forum/pic/item/c657f1899e510fb350bf99f8da33c895d0430c51.jpg"], 433 | compound : ["http://imgsrc.baidu.com/forum/pic/item/5b1314510fb30f24223c026ccb95d143ac4b0351.jpg"], 434 | }, 435 | pork : { 436 | name : "扑克牌", 437 | description : "对 $1 楼 @$2 使用:
    ", 438 | base : [ 439 | "http://imgsrc.baidu.com/forum/pic/item/1432d4df8db1cb132a129943de54564e93584b85.jpg", 440 | "http://imgsrc.baidu.com/forum/pic/item/7e4507b1cb1349546d654d34554e9258d0094a85.jpg", 441 | "http://imgsrc.baidu.com/forum/pic/item/05b75e2a2834349b1b7dcbd5caea15ce37d3be85.jpg", 442 | "http://imgsrc.baidu.com/forum/pic/item/a2bc45fc1e178a826d2ec8f1f503738da877e8e8.jpg", 443 | "http://imgsrc.baidu.com/forum/pic/item/81110dd6277f9e2fedf7a9591c30e924b999f338.jpg", 444 | "http://imgsrc.baidu.com/forum/pic/item/f189f9f082025aaf663ec99af8edab64024f1ae9.jpg" 445 | ], 446 | compound : ["http://imgsrc.baidu.com/forum/pic/item/6b6171edab64034fc1b66756acc379310b551d85.jpg"] 447 | }, 448 | game : { 449 | name : "游戏王", 450 | description : "对 $1 楼 @$2 使用:
    ", 451 | base :["http://imgsrc.baidu.com/forum/pic/item/bfa772dcd100baa14472fdeb4410b912c9fc2e9a.jpg"], 452 | compound : [] 453 | }, 454 | hs : { 455 | name : "炉石传说", 456 | description : "对 $1 楼 @$2 使用:
    ", 457 | base: [ 458 | "http://imgsrc.baidu.com/forum/pic/item/a2bc45fc1e178a827f2edaf0f503738da877e8ef.jpg", 459 | "http://imgsrc.baidu.com/forum/pic/item/f4a50d94a4c27d1e44349e9418d5ad6edcc438ee.jpg", 460 | "http://imgsrc.baidu.com/forum/pic/item/23a1d1b5c9ea15ce2a944559b5003af33b87b286.jpg", 461 | "http://imgsrc.baidu.com/forum/pic/item/2860db3d269759ee3ef9d19ab1fb43166c22df54.jpg" 462 | ], 463 | compound : [] 464 | }, 465 | hentai : { 466 | name : "变态", 467 | description : "$1 楼 @$2 你个大变态!
    ", 468 | base : ["http://imgsrc.baidu.com/forum/pic/item/a2112c1ea8d3fd1f3672076a334e251f94ca5f85.jpg"], 469 | compound: ["http://imgsrc.baidu.com/forum/pic/item/8849771f4134970a4961b57f96cad1c8a6865dee.jpg"] 470 | }, 471 | police : { 472 | name: "警察叔叔", 473 | description : "警察叔叔,就是 $1 楼 @$2 这个人
    ", 474 | base : [ 475 | "http://imgsrc.baidu.com/forum/pic/item/05851fcad1c8a78663e71b5f6409c93d71cf5028.jpg", 476 | "http://imgsrc.baidu.com/forum/pic/item/b075fcc6a7efce1be35d9e9bac51f3deb58f6583.jpg", 477 | "http://imgsrc.baidu.com/forum/pic/item/bbcac5c2d56285356065b17093ef76c6a6ef6370.jpg" 478 | ], 479 | compound: ["http://imgsrc.baidu.com/forum/pic/item/5b6e80d162d9f2d34b0f4fbeaaec8a136227ccdb.jpg"] 480 | } 481 | }); -------------------------------------------------------------------------------- /Tieba_Kuso/meta.yml: -------------------------------------------------------------------------------- 1 | name: Tieba Kuso 2 | namespace: 'http://tieba.baidu.com/f?kw=firefox' 3 | include: 4 | - 'http://tieba.baidu.com/p/*' 5 | - 'http://tieba.baidu.com/f?*' 6 | version: '0.1' 7 | grant: GM_xmlhttpRequest 8 | author: Paltoo Young 9 | description: 贴吧头像恶搞。 -------------------------------------------------------------------------------- /Tieba_Lzl_Dialogue/README.md: -------------------------------------------------------------------------------- 1 | # Tieba Lzl Dialogue # 2 | ![教程1](http://imgsrc.baidu.com/forum/w%3D580/sign=802e239335d3d539c13d0fcb0a86e927/e46cee1190ef76c6ee2bf8469e16fdfaae516716.jpg) 3 | ![教程2](http://imgsrc.baidu.com/forum/w%3D580/sign=5f0a008ed639b6004dce0fbfd9513526/e6a099ef76c6a7ef84dfab4bfefaaf51f2de6616.jpg) 4 | * [【点击安装】](https://userscript.firefoxcn.net/js/Tieba_Lzl_Dialogue.user.js) 5 | * author: 网络中二行客 6 | * 贴吧楼中楼查看回复。 7 | * version: 0.5 -------------------------------------------------------------------------------- /Tieba_Lzl_Dialogue/Tieba_Lzl_Dialogue.user.js: -------------------------------------------------------------------------------- 1 | ;(function(uw) { 2 | 3 | var 4 | $ = uw.jQuery, 5 | PageData = uw.PageData, 6 | cssText ='#dialogJbody .lzl_single_post{\ 7 | border-bottom: 1px dotted #d7d7d7;\ 8 | list-style: outside none none;\ 9 | margin-top: 6px;\ 10 | padding-top: 10px;\ 11 | min-width: 570px;}\ 12 | #dialogJbody .dialogue,#dialogJbody .lzl_s_r,\ 13 | #dialogJbody .lzl_li_pager,#dialogJbody .j_pager,\ 14 | #dialogJbody .user-hide-post-down{\ 15 | display: none !important;}\ 16 | #dialogJbody{\ 17 | -moz-box-orient: vertical;\ 18 | display: -moz-box;}\ 19 | .dialogue{\ 20 | color: #666666;\ 21 | cursor: pointer;\ 22 | padding-right: 5px;}\ 23 | .tb_alert_wrapper{\ 24 | -moz-box-ordinal-group: 1000;}\ 25 | #user_visit_card{\ 26 | z-index: 50010 !important;\ 27 | }'; 28 | $(document.head).append(""); 29 | 30 | function getUserName(s) { 31 | try {return s.match(/回复 .*? /)[0].substring(2).trim();} 32 | catch(err) {} 33 | } 34 | 35 | function getTalk(a,b) { 36 | var str = ""; 37 | b.find(".lzl_single_post") 38 | .each(function() { 39 | var u = $(this).find(".j_user_card").attr("username"); 40 | if (a.indexOf(u) != -1){ 41 | var s = $(this).find(".lzl_content_main").text(); 42 | var name = getUserName(s); 43 | if (a.indexOf(name) != -1){ 44 | str += "
  • "+$(this).html()+"
  • "; 45 | } 46 | } 47 | }); 48 | if (str){ 49 | $.tb.alert({ 50 | title: '查看对话', 51 | message: "", 52 | buttons:[{text: '关闭'}] 53 | }); 54 | var w = $(window).width(); 55 | var h = $(window).height(); 56 | $(".dialogJ").css({ 57 | "width":"600px", 58 | "top": h <= 555 ? 0 : (h - 555) / 2, 59 | "left": w <= 600 ? 0 : (w - 600) / 2 60 | }) 61 | .find(".tb_alert_wrapper") 62 | .before("
    "+str+"
    ") 63 | .parents(".dialogJcontent") 64 | .css({"max-height":"500px","overflow":"auto"}); 65 | } 66 | } 67 | 68 | function popTalk(a,b) { 69 | $.tb.alert({ 70 | title: '查看对话', 71 | message: "", 72 | buttons:[{text: '关闭'}] 73 | }); 74 | 75 | var last = a.find(".j_pager").children().last(); 76 | 77 | if(last.hasClass("tP")) { 78 | var max = last.text(); 79 | } 80 | else { 81 | var pages = []; 82 | a.find(".j_pager a").each(function() { 83 | var index = $(this).attr("index"); 84 | if (index){ 85 | return pages.push(index); 86 | } 87 | var page = $(this).attr("href"); 88 | pages.push(page.substring(1)); 89 | }) 90 | var max = Math.max.apply(null, pages); 91 | } 92 | 93 | for (i = 1; i <= max; i++) { 94 | pageTurning(i,b); 95 | } 96 | 97 | function pageTurning(p,b) { 98 | var l = new Date; 99 | l = l.getTime(); 100 | $.ajax({ 101 | url: '/p/comment', 102 | data: { 103 | tid: PageData.thread.thread_id, 104 | pid: a.getData().pid, 105 | pn: p, 106 | t: l 107 | }, 108 | dataType: 'html', 109 | success: function (e) { 110 | dialog(e,p,b); 111 | }, 112 | error: function (e) { 113 | console.log(e); 114 | } 115 | }) 116 | } 117 | } 118 | 119 | function dialog(msg,p,a) { 120 | var w = $(window).width(); 121 | var h = $(window).height(); 122 | $(".dialogJ").css({ 123 | "width":"600px", 124 | "top": h <= 555 ? 0 : (h - 555) / 2, 125 | "left": w <= 600 ? 0 : (w - 600) / 2 126 | }) 127 | .find(".tb_alert_wrapper") 128 | .before("
    "+msg+"
    ") 129 | .parents(".dialogJcontent") 130 | .css({"max-height":"500px","overflow":"auto"}) 131 | .find(".lzl_single_post") 132 | .each(function(){ 133 | var uc = $(this).find(".j_user_card"); 134 | var u = uc.attr("username"); 135 | if (-1 === a.indexOf(u)) { 136 | uc.parent(".lzl_single_post").remove(); 137 | } 138 | else{ 139 | var sv = $(this).find(".lzl_content_main"); 140 | var s = sv.text().trim(); 141 | var name = getUserName(s); 142 | if (-1 === a.indexOf(name)){ 143 | uc.parent(".lzl_single_post").remove(); 144 | } 145 | } 146 | }); 147 | } 148 | 149 | $("#j_p_postlist").on("mouseover",".core_reply_content",function(e) { 150 | $(this).find(".lzl_single_post") 151 | .each(function() { 152 | if (!$(this).find(".dialogue").length){ 153 | var s = $(this).find(".lzl_content_main").text().trim(); 154 | var name = getUserName(s); 155 | if (name){ 156 | var u = $(this).find(".j_user_card").attr("username"); 157 | $(this).find(".lzl_content_reply") 158 | .prepend("查看对话"); 159 | } 160 | } 161 | }); 162 | }) 163 | .on("click",".dialogue",function(e) { 164 | var t = $(e.target); 165 | var p = t.parents(".j_lzl_container"); 166 | if (p.find(".j_pager a").length){ 167 | return popTalk(p,t.data("s")); 168 | } 169 | getTalk(t.data("s"),t.parents(".core_reply_content")); 170 | }); 171 | 172 | })((function(){ 173 | return "undefined" === typeof unsafeWindow 174 | ? { 175 | jQuery:window.jQuery, 176 | PageData:window.PageData 177 | } 178 | : { 179 | jQuery:unsafeWindow.jQuery, 180 | PageData:unsafeWindow.PageData 181 | }; 182 | })()); -------------------------------------------------------------------------------- /Tieba_Lzl_Dialogue/meta.yml: -------------------------------------------------------------------------------- 1 | name: Tieba Lzl Dialogue 2 | namespace: 'http://tieba.baidu.com/' 3 | include: 4 | - 'http://tieba.baidu.com/p/*' 5 | - 'http://tieba.baidu.com/f?ct*' 6 | version: '0.5' 7 | grant: none 8 | description: 贴吧楼中楼弹出框查看回复。 -------------------------------------------------------------------------------- /Tieba_Private/README.md: -------------------------------------------------------------------------------- 1 | # Tieba Private # 2 | 3 | * [【点击安装】](https://userscript.firefoxcn.net/js/Tieba_Private.user.js) 4 | * author: 网络中二行客 5 | * 尾行新体验 6 | * version: 1 7 | -------------------------------------------------------------------------------- /Tieba_Private/Tieba_Private.user.js: -------------------------------------------------------------------------------- 1 | ;(function ($) { 2 | $.ajax({ 3 | type: 'post', 4 | url: '/home/post/delvisite', 5 | data: { 6 | ie: 'utf-8', 7 | un: $('.userinfo_username').text(), 8 | tbs: unsafeWindow.PageData.tbs 9 | }, 10 | dataType: 'json' 11 | }).success(function (t) { 12 | 0 === t.no && $('.visitor_card:first').animate({ 13 | width: 0 14 | }, 200, function () { 15 | $('.visitor_card:first').remove(), 16 | (function () { 17 | var t = $('#visitor_card_wrap') .find('.visitor_card').length; 18 | 0 === t && $('#visitor_card_wrap') .closest('.ihome_visitor').slideUp(200) 19 | }) (); 20 | }) 21 | }) 22 | })(unsafeWindow.$); -------------------------------------------------------------------------------- /Tieba_Private/meta.yml: -------------------------------------------------------------------------------- 1 | name: Tieba Private 2 | namespace: 'http://tieba.baidu.com' 3 | include: 'http://tieba.baidu.com/home/*' 4 | version: '1' 5 | grant: none 6 | author: 网络中二行客 7 | description: 自动清除贴吧访问记录。 -------------------------------------------------------------------------------- /Tieba_Quick_Reply/README.md: -------------------------------------------------------------------------------- 1 | # Tieba Quick Reply # 2 | ![使用方法](http://imgsrc.baidu.com/forum/w%3D580/sign=1e6b296c56fbb2fb342b581a7f4a2043/7db124ee3d6d55fbf57e57176e224f4a20a4dd69.jpg) 3 | * [【点击安装】](https://userscript.firefoxcn.net/js/Tieba_Quick_Reply.user.js) 4 | * 点击帖子列表左侧的回复数量,可弹出回复窗口。 5 | * author: 网络中二行客 6 | * version: 0.4 7 | -------------------------------------------------------------------------------- /Tieba_Quick_Reply/Tieba_Quick_Reply.user.js: -------------------------------------------------------------------------------- 1 | ;(function($,PageData){ 2 | const 3 | kw = PageData.forum.name, 4 | fid = PageData.forum.id, 5 | url = "http://tieba.baidu.com/f/commit/post/add", 6 | tbs = PageData.tbs, 7 | replyContent = ["文科大法好!"], 8 | cssText ="#home_reply li a{display:block;padding-left:10px;\ 9 | cursor:pointer;line-height:30px;font-size:15px;}\ 10 | #home_reply{max-height:380px;overflow-y:auto;}\ 11 | #home_reply li{border-bottom:1px dashed #e2e2e2;}\ 12 | #home_reply li a:hover{background:#e2e2e2;}\ 13 | #home_reply li a:hover span{display:inline-block;}\ 14 | .dialogJtxt b{color:red;}\ 15 | #home_reply li span{float:right;display:none;\ 16 | background:url('http://tb2.bdstatic.com/tb/static-common/img/tb_ui/tb_dialog_close_3478e87.png');\ 17 | width:14px;height:13px;margin:8px;}\ 18 | #home_reply li span:hover{background-position:0 13px;}\ 19 | .threadlist_rep_num{cursor:pointer}\ 20 | #add_reply{background:#e2e2e2;color: #fff;display: block;cursor:pointer;\ 21 | font-size: 20px;line-height: 30px;text-align: center;}\ 22 | #add_reply:hover{background:#e6e6e6;}\ 23 | .reply_text{width:550px;line-height:30px;border:1px solid #e2e2e2;}\ 24 | #reply_btn{margin-right: 20px;cursor:pointer;}\ 25 | "; 26 | 27 | var tid, 28 | replyStorage = { 29 | set handler(ls){ 30 | return localStorage["quickreply"] = JSON.stringify(ls); 31 | }, 32 | setStorage : function(value){ 33 | var reply = quickReply.reply; 34 | reply.push(value); 35 | this.handler = quickReply; 36 | }, 37 | delStorage : function(key){ 38 | quickReply.reply.splice(key, 1); 39 | this.handler = quickReply; 40 | } 41 | }, 42 | quickReply = localStorage["quickreply"] ? 43 | JSON.parse(localStorage["quickreply"]) : 44 | {"reply":replyContent}; 45 | 46 | function reply(content){ 47 | var postData = { 48 | __type__ : "reply", 49 | content : content, 50 | fid : fid, 51 | files : "[]", 52 | floor_num : 16, 53 | ie : "utf-8", 54 | kw : kw, 55 | rich_text : 1, 56 | tbs : tbs, 57 | tid : tid, 58 | vcode_md5 : "" 59 | } 60 | 61 | $.post(url,postData,function(r){ 62 | r = JSON.parse(r); 63 | !(r.err_code) ? ($("#home_reply").html("回复成功"), 64 | setTimeout(function(){$(".dialogJclose").click()},1000)) 65 | : $(".dialogJtxt").html("回复失败!!"); 66 | }); 67 | } 68 | 69 | $("#content_leftList").on("click",".threadlist_rep_num",function(e){ 70 | tid = $(e.target).parents(".j_thread_list").data("field").id; 71 | var html = ""; 72 | quickReply["reply"].forEach(function(i){ 73 | html += "
  • "+i+"
  • "; 74 | }); 75 | 76 | $.tb.alert({ 77 | title: '快捷回复', 78 | message: "", 79 | buttons:[{text: '关闭'}] 80 | }); 81 | var w = $(window).width(); 82 | var h = $(window).height(); 83 | $(".dialogJ").css({ 84 | "width":"600px", 85 | "top": h <= 555 ? 0 : (h - 555) / 2, 86 | "left": w <= 600 ? 0 : (w - 600) / 2 87 | }) 88 | .find(".tb_alert_wrapper") 89 | .before("
      "+html+"
    ") 90 | .parents(".dialogJcontent") 91 | .find(".tb_alert_message") 92 | .before("+") 93 | .next() 94 | .prepend('发表'); 95 | }); 96 | 97 | $(document.body).on("click","#home_reply li a",function(e){ 98 | if(e.target.nodeName === "SPAN"){ 99 | var index = $(e.target).parent().parent().index(); 100 | replyStorage.delStorage(index); 101 | return $(e.target).parent().parent().remove(); 102 | } 103 | reply($(e.target).text()); 104 | }).on("click","#add_reply",function(){ 105 | $(this).before("") 106 | .prev().focus().blur(function(){ 107 | $(this).remove(); 108 | if ($(this).val() === "") return; 109 | replyStorage.setStorage($(this).val()); 110 | $("#home_reply ul").append("
  • "+$(this).val()+"
  • "); 111 | }); 112 | }).on("click","#reply_btn",function(){ 113 | var text = $("#reply_text").val(); 114 | text !== "" && reply(text); 115 | }); 116 | 117 | $(document.head).append(""); 118 | })(unsafeWindow.$,unsafeWindow.PageData,undefined); -------------------------------------------------------------------------------- /Tieba_Quick_Reply/meta.yml: -------------------------------------------------------------------------------- 1 | name: Tieba Quick Reply 2 | namespace: 'http://tieba.baidu.com' 3 | include: 'http://tieba.baidu.com/f?*' 4 | grant: none 5 | version: '0.4' 6 | author: 网络中二行客 7 | description: 贴吧列表页快速回复。 -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.css' { 2 | const locals: any; 3 | export { 4 | locals 5 | }; 6 | } 7 | 8 | declare const GM_info: { 9 | script: { 10 | name: string; 11 | namespace?: string; 12 | description: string; 13 | 'run-at': 'document-start' | 'document.end'; 14 | version?: string; 15 | includes: string[]; 16 | excludes: string[]; 17 | matches: string[]; 18 | /* 包含所有资源和其对应的地址的对象 (自 GM 1.2)。 */ 19 | resources: any[]; 20 | unwrap: boolean; 21 | } 22 | scriptMetaStr: string; 23 | scriptWillUpdate: boolean; 24 | version: string; 25 | scriptHandler?: string; 26 | } 27 | 28 | /** 29 | * 插入样式 30 | * 该函数用于插入一段 CSS 到当前页面 31 | * @param {string} css CSS样式 32 | */ 33 | declare function GM_addStyle(css: string): void; 34 | /** 35 | * XHR请求 36 | * 该函数主要用于实现跨域传输信息调用,如从 www.example.com 请求 upload.example.com 的内容 37 | * @param {GM.Request} details 请求详情 38 | */ 39 | declare function GM_xmlhttpRequest(details: GM.Request): void; 40 | 41 | /** 42 | * 注册菜单 43 | * @param {string} caption 标题 44 | * @param {() => void} commandFunc 回调 45 | * @param {string} accessKey 菜单界面的热键;一个单子节符号,一般为项目标题中的一个字 46 | */ 47 | declare function GM_registerMenuCommand(caption: string, commandFunc: () => void, accessKey?: string): void; 48 | 49 | /** 50 | * 读取数据 51 | * 该函数用于获取脚本之前使用 GM_setValue 赋值储存的数据,可以为 String、Boolean 等类型 52 | * @param {string} name 名称 53 | * @param {string | boolean | number} defaultValue 默认值 54 | */ 55 | declare function GM_getValue(name: string, defaultValue?: T): T; 56 | /** 57 | * 写入数据 58 | * 该函数用于写入一些数据并储存,可使用 GM_getValue 获取储存的数据。String、Boolean 等类型 59 | * @param {string} name 名称 60 | * @param {string | boolean | number} value 内容 61 | */ 62 | declare function GM_setValue(name: string, value: string | boolean | number); 63 | /** 64 | * 删除数据 65 | * @param {string} name 名称 66 | */ 67 | declare function GM_deleteValue(name: string); 68 | /** 69 | * 获取所有已写入数据的名称 70 | * @returns string[] 71 | */ 72 | declare function GM_listValues(): string[]; 73 | 74 | /** 75 | * 获取资源(文本) 76 | * 该函数用于获取定义的 @resource 的元属性值 77 | * @param {string} resourceName 资源名称 78 | */ 79 | declare function GM_getResourceText(resourceName: string): string; 80 | /** 81 | * 获取资源(文本) 82 | * 该函数用于获取定义的 @resource 所指向的内容 83 | * @param {string} resourceName 资源名称 84 | */ 85 | declare function GM_getResourceURL(resourceName: string): string; 86 | 87 | 88 | /** 89 | * 打开新标签页 90 | * @param {string} url 在新标签页开启的地址 91 | * @param {boolean} loadInBackground 是否后台开启目标标签页; 默认为 true,即后台开启 92 | */ 93 | declare function GM_openInTab(url: string, loadInBackground?: boolean): Window; 94 | 95 | /** 96 | * 设置剪贴板 97 | * @param {string} text 任意文本 98 | */ 99 | declare function GM_setClipboard(text: string); 100 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@fxb/userscript", 3 | "version": "1.0.0", 4 | "description": "FirefoxBar user script", 5 | "scripts": { 6 | "build": "node scripts/build.js", 7 | "build-dev": "node scripts/build.js -D", 8 | "dev": "node scripts/dev.js", 9 | "build-site": "node scripts/build-site.js" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/FirefoxBar/userscript.git" 14 | }, 15 | "author": "FirefoxBar", 16 | "bugs": { 17 | "url": "https://github.com/FirefoxBar/userscript/issues" 18 | }, 19 | "homepage": "https://github.com/FirefoxBar/userscript#readme", 20 | "devDependencies": { 21 | "@babel/core": "^7.10.4", 22 | "@babel/preset-env": "^7.10.4", 23 | "@babel/preset-react": "^7.10.4", 24 | "@babel/preset-typescript": "^7.10.4", 25 | "@types/greasemonkey": "^4.0.0", 26 | "@types/node": "^14.0.14", 27 | "@types/react": "^16.9.41", 28 | "babel-loader": "^8.1.0", 29 | "chalk": "^4.1.0", 30 | "css-loader": "^3.6.0", 31 | "cssnano": "^4.1.10", 32 | "fs-extra": "^9.0.1", 33 | "node-fetch": "^2.6.0", 34 | "postcss-loader": "^3.0.0", 35 | "preact": "^10.4.4", 36 | "progress-bar-webpack-plugin": "^2.1.0", 37 | "terser-webpack-plugin": "^3.0.6", 38 | "typescript": "^3.9.5", 39 | "webpack": "^4.43.0", 40 | "yaml": "^1.10.0" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /scripts/build-site.js: -------------------------------------------------------------------------------- 1 | const { existsSync, readdirSync, mkdirSync, statSync } = require("fs"); 2 | const { copySync, writeFile } = require("fs-extra"); 3 | const path = require("path"); 4 | const buildOne = require("./utils/build-one"); 5 | const copyOne = require("./utils/copy-one"); 6 | const chalk = require("chalk"); 7 | 8 | const root = path.join(__dirname, ".."); 9 | const dirs = readdirSync(root); 10 | 11 | if (!existsSync(path.join(root, "dist"))) { 12 | mkdirSync(path.join(root, "dist")); 13 | } 14 | if (!existsSync(path.join(root, "dist/master"))) { 15 | mkdirSync(path.join(root, "dist/master")); 16 | } 17 | if (!existsSync(path.join(root, "dist/pages"))) { 18 | mkdirSync(path.join(root, "dist/pages")); 19 | } 20 | 21 | // 生成列表 22 | async function main() { 23 | // 编译脚本 24 | const distJs = path.join(root, "dist/pages/js"); 25 | if (!existsSync(distJs)) { 26 | mkdirSync(distJs); 27 | } 28 | const queue = dirs.map(async (it) => { 29 | const fullPath = path.join(root, it); 30 | if (!statSync(fullPath).isDirectory()) { 31 | return; 32 | } 33 | // meta.yml 34 | if (existsSync(path.join(fullPath, "package.json"))) { 35 | // 需要编译的 36 | return await buildOne(it, distJs); 37 | } else if (existsSync(path.join(root, it, it + ".user.js"))) { 38 | // 纯复制,但仍然需要解析meta 39 | return copyOne(it, distJs); 40 | } 41 | }); 42 | 43 | try { 44 | const result = await Promise.all(queue); 45 | const list = result 46 | .map((it) => { 47 | if (!it) return; 48 | console.log(chalk.green("✔") + it.name + ": 已" + (it.stats ? "编译" : "复制")); 49 | const meta = it.meta.meta; 50 | const res = { 51 | name: meta.name, 52 | version: meta.version, 53 | installURL: meta.downloadURL, 54 | homepageURL: "https://github.com/FirefoxBar/userscript/tree/main/" + it.name, 55 | description: meta.description, 56 | }; 57 | if (meta.icon) { 58 | res.icon = meta.icon; 59 | } 60 | return res; 61 | }) 62 | .filter((x) => !!x) 63 | .sort((a, b) => a.name.localeCompare(b.name)); 64 | 65 | if (!existsSync(path.join(root, "dist/pages/api"))) { 66 | mkdirSync(path.join(root, "dist/pages/api")); 67 | } 68 | await writeFile( 69 | path.join(root, "dist/pages/api/list.json"), 70 | JSON.stringify(list) 71 | ); 72 | await writeFile( 73 | path.join(root, "dist/pages/api/list.js"), 74 | "onGetList(" + JSON.stringify(list) + ")" 75 | ); 76 | // 复制站点文件 77 | copySync(path.join(__dirname, "www"), path.join(root, "dist/pages")); 78 | } catch (err) { 79 | if (Array.isArray(err)) { 80 | err.forEach((error) => { 81 | console.error(error.message); 82 | }); 83 | } else { 84 | console.error(err); 85 | } 86 | process.exit(1); 87 | } 88 | } 89 | 90 | main(); 91 | -------------------------------------------------------------------------------- /scripts/build.js: -------------------------------------------------------------------------------- 1 | const build = require('./utils/build-one'); 2 | const terminal = require('./utils/terminal'); 3 | 4 | const isDev = process.argv.includes('-D'); 5 | const name = process.argv[process.argv.length - 1]; 6 | 7 | build(name, undefined, isDev) 8 | .then(res => { 9 | terminal.assets(res.stats.compilation.assets); 10 | }) 11 | .catch(err => { 12 | if (Array.isArray(err)) { 13 | err.forEach(error => { 14 | console.error(error.message); 15 | }); 16 | } else { 17 | console.error(err); 18 | } 19 | process.exit(1); 20 | }) -------------------------------------------------------------------------------- /scripts/copy-master.js: -------------------------------------------------------------------------------- 1 | // 从pages目录,复制文件到master分支 2 | const { existsSync, readdirSync, mkdirSync } = require('fs'); 3 | const { copySync } = require('fs-extra'); 4 | const { resolve } = require('path'); 5 | const dist = resolve(__dirname, '../dist'); 6 | const pages = resolve(dist, 'pages/js'); 7 | const master = resolve(dist, 'master'); 8 | 9 | readdirSync(pages).forEach(it => { 10 | if (!it.includes('.user.js') && !it.includes('.meta.js')) { 11 | return; 12 | } 13 | 14 | const name = it.substr(0, it.indexOf('.')); 15 | 16 | if (!existsSync(resolve(master, name))) { 17 | mkdirSync(resolve(master, name)); 18 | } 19 | copySync(resolve(pages, it), resolve(master, name, it)); 20 | }); 21 | 22 | copySync(resolve(__dirname, 'master'), master); -------------------------------------------------------------------------------- /scripts/dev.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const config = require('./utils/webpack.config'); 3 | const generateMeta = require('./utils/generate-meta'); 4 | const { readFileSync, existsSync } = require('fs'); 5 | const path = require('path'); 6 | const { exec } = require('./utils'); 7 | const terminal = require('./utils/terminal'); 8 | 9 | const name = process.argv[process.argv.length - 1]; 10 | 11 | const main = async function() { 12 | const root = path.resolve(__dirname, '..', name); 13 | 14 | // 检查有没有安装依赖 15 | if (!existsSync(path.resolve(root, 'node_modules'))) { 16 | await exec("cd " + root + " && yarn install --frozen-lockfile"); 17 | } 18 | 19 | // 读取版本号 20 | const package = JSON.parse(readFileSync(path.resolve(root, 'package.json'))); 21 | // 读取配置,生成注释 22 | const meta = generateMeta(path.resolve(root, 'meta.yml'), { 23 | version: package.version, 24 | updateURL: `https://userscript.firefoxcn.net/js/${name}.meta.js`, 25 | downloadURL: `https://userscript.firefoxcn.net/js/${name}.user.js` 26 | }); 27 | 28 | const webpackConfig = config({ 29 | name, 30 | meta: meta.text, 31 | output: root, 32 | isDev: true 33 | }); 34 | 35 | if (existsSync(path.resolve(root, 'webpack.overwrite.js'))) { 36 | require(path.resolve(root, 'webpack.overwrite.js'))(webpackConfig); 37 | } 38 | 39 | const complier = webpack(webpackConfig); 40 | 41 | complier.watch({}, (err, stats) => { 42 | if (err) { 43 | console.error(err); 44 | } else { 45 | // 遍历结果,输出错误 46 | for (const module of stats.compilation.modules) { 47 | if (module.errors && module.errors.length > 0) { 48 | module.errors.forEach(error => { 49 | console.error(error.message); 50 | }); 51 | return; 52 | } 53 | } 54 | terminal.assets(stats.compilation.assets); 55 | } 56 | }); 57 | } 58 | 59 | main(); -------------------------------------------------------------------------------- /scripts/master/README.md: -------------------------------------------------------------------------------- 1 | # 请不要在此分支提交文件 2 | 3 | 此分支会被CI强制覆盖,用作兼容旧版本的发布 4 | 5 | # 说明 6 | 7 | * 代码修改请在main分支上进行。 8 | * GM头信息请写在各个脚本下的meta.yml文件中。Yaml语法可参考[这里](https://www.ruanyifeng.com/blog/2016/07/yaml.html) 9 | * 可以使用[这个工具](https://userscript.firefoxcn.net/)将原有的GM头信息转为Yaml格式。 10 | * 提交后CI会自动build并发布。 11 | -------------------------------------------------------------------------------- /scripts/utils/build-one.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const config = require('./webpack.config'); 3 | const generateMeta = require('./generate-meta'); 4 | const { readFileSync, existsSync } = require('fs'); 5 | const path = require('path'); 6 | const { exec } = require('./index'); 7 | 8 | module.exports = function(name, output, isDev = false) { 9 | return new Promise(async (resolve, reject) => { 10 | const root = path.resolve(__dirname, '../..', name); 11 | 12 | // 检查有没有安装依赖 13 | if (!existsSync(path.resolve(root, 'node_modules'))) { 14 | await exec("cd " + root + " && yarn install --frozen-lockfile"); 15 | } 16 | 17 | const outputDir = output || root; 18 | // 读取版本号 19 | const package = JSON.parse(readFileSync(path.resolve(root, 'package.json'))); 20 | // 读取配置,生成注释 21 | const meta = generateMeta(path.resolve(root, 'meta.yml'), { 22 | version: package.version, 23 | updateURL: `https://userscript.firefoxcn.net/js/${name}.meta.js`, 24 | downloadURL: `https://userscript.firefoxcn.net/js/${name}.user.js` 25 | }); 26 | 27 | const webpackConfig = config({ 28 | name, 29 | meta: meta.text, 30 | output: outputDir, 31 | isDev 32 | }); 33 | 34 | if (existsSync(path.resolve(root, 'webpack.overwrite.js'))) { 35 | require(path.resolve(root, 'webpack.overwrite.js'))(webpackConfig); 36 | } 37 | 38 | const complier = webpack(webpackConfig); 39 | 40 | complier.run((err, stats) => { 41 | if (err) { 42 | reject(err); 43 | return; 44 | } 45 | // 遍历stats,检查有没有错误 46 | for (const module of stats.compilation.modules) { 47 | if (module.errors && module.errors.length > 0) { 48 | reject(module.errors); 49 | return; 50 | } 51 | } 52 | resolve({ name, meta, stats }); 53 | }); 54 | }); 55 | } 56 | -------------------------------------------------------------------------------- /scripts/utils/copy-one.js: -------------------------------------------------------------------------------- 1 | const generateMeta = require('./generate-meta'); 2 | const path = require('path'); 3 | const { readFileSync, writeFileSync } = require('fs'); 4 | 5 | module.exports = function(name, output) { 6 | const root = path.resolve(__dirname, '../..', name); 7 | // 读取配置,生成注释 8 | const meta = generateMeta(path.resolve(root, 'meta.yml'), { 9 | updateURL: `https://userscript.firefoxcn.net/js/${name}.meta.js`, 10 | downloadURL: `https://userscript.firefoxcn.net/js/${name}.user.js` 11 | }); 12 | 13 | // 生成meta.js 14 | writeFileSync(path.resolve(output, name + '.meta.js'), meta.text.trim(), { 15 | encoding: 'UTF-8' 16 | }); 17 | 18 | // 复制user.js 19 | const content = readFileSync(path.resolve(root, name + '.user.js'), { 20 | encoding: 'UTF-8' 21 | }); 22 | writeFileSync(path.resolve(output, name + '.user.js'), meta.text + content, { 23 | encoding: 'UTF-8' 24 | }); 25 | 26 | return { name, meta }; 27 | } -------------------------------------------------------------------------------- /scripts/utils/generate-meta.js: -------------------------------------------------------------------------------- 1 | const { readFileSync } = require('fs'); 2 | const yaml = require('yaml'); 3 | 4 | module.exports = function(file, merge = {}) { 5 | const text = readFileSync(file, { 6 | encoding: 'UTF-8' 7 | }); 8 | const meta = { 9 | ...yaml.parse(text), 10 | ...merge 11 | }; 12 | 13 | const result = ['// ==UserScript==']; 14 | for (const k in meta) { 15 | if (Array.isArray(meta[k])) { 16 | meta[k].forEach(v => { 17 | result.push(`// @${k} ${v}`); 18 | }); 19 | } else { 20 | result.push(`// @${k} ${meta[k]}`); 21 | } 22 | } 23 | result.push('// ==/UserScript=='); 24 | 25 | return { 26 | meta: meta, 27 | text: result.join("\n") + "\n" 28 | }; 29 | } -------------------------------------------------------------------------------- /scripts/utils/index.js: -------------------------------------------------------------------------------- 1 | const childProcess = require('child_process'); 2 | 3 | module.exports = { 4 | exec: function(command) { 5 | return new Promise((resolve, reject) => { 6 | childProcess.exec(command, (error, stdout, stderr) => { 7 | if (error) { 8 | reject(error); 9 | return; 10 | } 11 | resolve(stdout); 12 | }); 13 | }); 14 | } 15 | } -------------------------------------------------------------------------------- /scripts/utils/loader/gm-style-loader.js: -------------------------------------------------------------------------------- 1 | const loader = function(source) { 2 | return source + "\nGM_addStyle(exports[0][1])"; 3 | }; 4 | 5 | module.exports = loader; -------------------------------------------------------------------------------- /scripts/utils/terminal.js: -------------------------------------------------------------------------------- 1 | const chalk = require('chalk'); 2 | 3 | module.exports = { 4 | assets: assets => { 5 | Object.entries(assets).forEach(it => { 6 | const name = it[0]; 7 | const asset = it[1]; 8 | console.log((asset.emitted ? chalk.green('[emitted]') : chalk.red('[not emitted]')) + ' ' + name); 9 | }); 10 | } 11 | } -------------------------------------------------------------------------------- /scripts/utils/webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require("webpack"); 2 | const TerserPlugin = require('terser-webpack-plugin'); 3 | const ProgressBarPlugin = require('progress-bar-webpack-plugin'); 4 | const path = require('path'); 5 | const { writeFileSync } = require('fs'); 6 | 7 | module.exports = function(options) { 8 | const { isDev, name, meta, output } = options; 9 | const root = path.resolve(__dirname, '../..', name); 10 | const minimizer = [ 11 | new webpack.BannerPlugin({ 12 | banner: meta, 13 | raw: true 14 | }) 15 | ] 16 | if (!isDev) { 17 | minimizer.unshift(new TerserPlugin()); 18 | } 19 | const cssLoader = [ 20 | 'gm-style-loader', 21 | { 22 | loader: 'css-loader', 23 | options: { 24 | modules: true 25 | } 26 | } 27 | ]; 28 | if (!isDev) { 29 | cssLoader.push({ 30 | loader: 'postcss-loader', 31 | options: { 32 | plugins: [ 33 | require('cssnano') 34 | ] 35 | } 36 | }); 37 | } 38 | const babelLoader = { 39 | loader: 'babel-loader', 40 | options: { 41 | presets: [ 42 | // '@babel/preset-env', 43 | ['@babel/preset-react', { 44 | pragma: 'h' 45 | }] 46 | ], 47 | babelrc: false, 48 | overrides: [ 49 | { 50 | exclude: /(^|\/|\\)node_modules(\/|\/)/, 51 | presets: [ 52 | [require.resolve('@babel/preset-typescript'), { jsxPragma: 'h' }], 53 | ], 54 | }, 55 | ], 56 | } 57 | }; 58 | return { 59 | context: root, 60 | entry: { 61 | [name + '.user']: path.resolve(root, 'src/index') 62 | }, 63 | mode: 'production', 64 | // mode: 'development', 65 | module: { 66 | rules: [ 67 | { 68 | test: /\.jsx?$/, 69 | include: path.resolve(root, 'src'), 70 | use: babelLoader 71 | }, 72 | { 73 | test: /\.tsx?$/, 74 | include: path.resolve(root, 'src'), 75 | use: [ 76 | babelLoader 77 | ] 78 | }, 79 | { 80 | test: /\.css$/, 81 | use: cssLoader 82 | } 83 | ], 84 | }, 85 | resolve: { 86 | extensions: [ 87 | '.tsx', 88 | '.ts', 89 | '.jsx', 90 | '.js' 91 | ], 92 | }, 93 | output: { 94 | filename: '[name].js', 95 | path: output, 96 | }, 97 | plugins: [ 98 | new ProgressBarPlugin({ 99 | summary: false 100 | }), 101 | { 102 | apply: compiler => { 103 | compiler.hooks.afterEmit.tap('Generate meta.js', () => { 104 | writeFileSync(path.resolve(output, name + '.meta.js'), meta.trim(), { 105 | encoding: 'UTF-8' 106 | }); 107 | }); 108 | } 109 | } 110 | ], 111 | optimization: { 112 | minimize: true, 113 | minimizer: minimizer 114 | }, 115 | resolveLoader: { 116 | modules: [ 117 | 'node_modules', 118 | path.resolve(__dirname, 'loader') 119 | ] 120 | }, 121 | stats: "verbose" 122 | }; 123 | } -------------------------------------------------------------------------------- /scripts/www/CNAME: -------------------------------------------------------------------------------- 1 | userscript.firefoxcn.net -------------------------------------------------------------------------------- /scripts/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 转换metajs至meta 7 | 8 | 24 | 25 | 26 |
    27 | 28 | 29 |
    30 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "experimentalDecorators": true, 4 | "allowSyntheticDefaultImports": true, 5 | "lib": [ 6 | "dom", 7 | "es2017" 8 | ], 9 | "module": "commonjs", 10 | "moduleResolution": "node", 11 | "noEmit": false, 12 | "preserveConstEnums": true, 13 | "removeComments": false, 14 | "skipLibCheck": true, 15 | "sourceMap": false, 16 | "strict": true, 17 | "target": "esnext", 18 | "declaration": false, 19 | "resolveJsonModule": true, 20 | "jsx": "preserve" 21 | }, 22 | "exclude": [ 23 | "./scripts" 24 | ] 25 | } --------------------------------------------------------------------------------