├── img └── icon.png ├── src ├── .babelrc ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ ├── glyphicons-halflings-regular.woff2 │ └── glyphicons-halflings-regular.svg ├── popup.tmpl.html └── js │ ├── main.js │ ├── component │ └── app.vue │ ├── utils.js │ └── popup.js ├── image ├── preview-1.png └── preview-2.jpg ├── .gitignore ├── manifest.json ├── README.md ├── package.json ├── webpack.config.js └── LICENSE /img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanFengSan/BilibiliCleaner/HEAD/img/icon.png -------------------------------------------------------------------------------- /src/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0"], 3 | "plugins": ["transform-runtime"] 4 | } -------------------------------------------------------------------------------- /image/preview-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanFengSan/BilibiliCleaner/HEAD/image/preview-1.png -------------------------------------------------------------------------------- /image/preview-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanFengSan/BilibiliCleaner/HEAD/image/preview-2.jpg -------------------------------------------------------------------------------- /src/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanFengSan/BilibiliCleaner/HEAD/src/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /src/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanFengSan/BilibiliCleaner/HEAD/src/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /src/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanFengSan/BilibiliCleaner/HEAD/src/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /src/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hanFengSan/BilibiliCleaner/HEAD/src/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /src/popup.tmpl.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Home 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | .DS_Store -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "Bilibili 清洁者", 4 | "description": "为Bilibili的环境清洁做点微小的工作,主要功能:拉黑UP主、屏蔽特定用户的评论", 5 | "version": "1.1.1", 6 | "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'", 7 | "browser_action": { 8 | "default_popup": "./build/popup.html", 9 | "default_title": "Bilibili 清洁者" 10 | }, 11 | "icons": { 12 | "16": "./img/icon.png", 13 | "24": "./img/icon.png", 14 | "128": "./img/icon.png" 15 | }, 16 | "permissions": [ 17 | "activeTab", 18 | "https://ajax.googleapis.com/", 19 | "storage" 20 | ], 21 | "content_scripts": [{ 22 | "matches": [ 23 | "*://www.bilibili.com/*", 24 | "*://bangumi.bilibili.com/*" 25 | ], 26 | "js": [ 27 | "./src/js/utils.js", 28 | "./build/main.js" 29 | ] 30 | }] 31 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BilibiliCleaner / Bilibili 清洁者 2 | chrome扩展,主要功能:屏蔽up主,屏蔽评论。屏蔽列表自动同步云端,可多设备共享。 3 | 4 | ## 技术相关 5 | 这个我没有怎么按照chrome官方的例子套路来,上了``webpack+vue+sass``,当然,在此只是杀鸡用牛刀,不过我就是喜欢hhh。里面的color什么的,太少啦,我也就不抽出来了。 6 | 7 | 核心代码``main.js``是我初学前端时候写的了,所以挺难看的233。那时候苦于没有信用卡,注册不了开发者账号,也就搁浅了,今天想了起来,就顺手填了填坑。 8 | 9 | ## 界面/效果预览 10 | 11 | 12 | 13 | **以上UP主名单仅是测试,毫无恶性:)** 14 | 15 | ## 用户使用说明 16 | [chrome webstore 下载地址](https://chrome.google.com/webstore/detail/bilibili-%E6%B8%85%E6%B4%81%E8%80%85/ihadnfkejmlnpohmlccdmgeikafohamb/related) 17 | 18 | 直接在输入框中输入用户名称然后点击添加或回车即可,屏蔽列表会自动同步到云端,不用担心丢失。添加是立即见效的,而删除需要重新刷新页面。 19 | 20 | ## 使用 21 | ``` 22 | npm install 23 | ``` 24 | 先解决依赖 25 | ``` 26 | webpack 27 | ``` 28 | 如上,即可完成编译,然后在chrome://extensions中勾选``开发者模式``,再选择``已解压的扩展程序``,选择此根目录,即加载成功。 29 | 30 | 31 | 32 | 33 | # License 34 | 35 | Copyright (C) 2016 hanFengSan 36 | 37 | Licensed under the Apache License, Version 2.0 (the "License"); 38 | you may not use this file except in compliance with the License. 39 | You may obtain a copy of the License at 40 | 41 | http://www.apache.org/licenses/LICENSE-2.0 42 | 43 | Unless required by applicable law or agreed to in writing, software 44 | distributed under the License is distributed on an "AS IS" BASIS, 45 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 46 | See the License for the specific language governing permissions and 47 | limitations under the License. 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bilibili-cleaner", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "jquery.min.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --progress", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "", 11 | "license": "ISC", 12 | "devDependencies": { 13 | "webpack": "^1.13.2", 14 | "autoprefixer": "^6.4.1", 15 | "babel-core": "^6.14.0", 16 | "babel-loader": "^6.2.5", 17 | "babel-plugin-react-transform": "^2.0.2", 18 | "babel-plugin-transform-runtime": "^6.1.2", 19 | "babel-preset-es2015": "^6.14.0", 20 | "babel-preset-react": "^6.11.1", 21 | "babel-preset-stage-0": "^6.1.2", 22 | "babel-runtime": "^5.8.0", 23 | "bootstrap": "^3.3.7", 24 | "css-loader": "^0.24.0", 25 | "expose-loader": "^0.7.1", 26 | "extract-text-webpack-plugin": "^1.0.1", 27 | "favicons-webpack-plugin": "0.0.7", 28 | "file-loader": "^0.9.0", 29 | "html-webpack-plugin": "^2.22.0", 30 | "image-loader": "0.0.1", 31 | "image-webpack-loader": "^2.0.0", 32 | "jquery": "^1.10.2", 33 | "json-loader": "^0.5.4", 34 | "node-sass": "^3.9.3", 35 | "postcss-loader": "^0.11.1", 36 | "react-transform-hmr": "^1.0.4", 37 | "sass-loader": "^4.0.1", 38 | "style-loader": "^0.13.1", 39 | "url-loader": "^0.5.7", 40 | "vue": "^1.0.26", 41 | "vue-html-loader": "^1.0.0", 42 | "vue-loader": "^7.3.0", 43 | "vue-strap": "^1.1.14", 44 | "vuestrap-base-components": "^0.8.10", 45 | "vuex": "^1.0.0-rc.2", 46 | "webpack-dev-server": "^1.15.1" 47 | }, 48 | "dependencies": { 49 | "vue": "^1.0.26", 50 | "vuex": "^1.0.0-rc.2" 51 | } 52 | } -------------------------------------------------------------------------------- /src/js/main.js: -------------------------------------------------------------------------------- 1 | var blockList = [] // 屏蔽列表 2 | var html = document.documentElement.innerHTML // 用于检查是否需要刷新 3 | 4 | // 读取数据 5 | chrome.storage.sync.get('list', function (items) { 6 | for (let item of items.list) { 7 | blockList.push(item) 8 | } 9 | }) 10 | 11 | // 监听屏蔽列表的变化 12 | chrome.storage.onChanged.addListener(function (changes, namespace) { 13 | for (let key in changes) { 14 | if (key == 'list') { 15 | blockList = changes[key].newValue 16 | } 17 | } 18 | }) 19 | 20 | const clean = function () { 21 | // console.log('clean') 22 | // 音乐区推荐列表 23 | let hasRecommand = ($('.top-list').length != 0) 24 | // 评论列表 25 | let hasCommList = ($('.comm_list').length != 0) 26 | 27 | blockList.forEach(function (item) { 28 | // /清潔一般項 29 | $("[data-up='{0}']".format(item)).remove() 30 | // 清潔排行榜 31 | $(".l-item .up-info [title='{0}']".format(item)).parents('li').remove() 32 | // 清潔動態 33 | $(".vl-dyn-cnt [up='{0}']".format(item)).parents('li').remove() 34 | 35 | // 清洁推荐 36 | if (hasRecommand) { 37 | for (let recommandItem of $('.v-item')) { 38 | if (recommandItem.innerHTML.indexOf(item) != -1) { 39 | let node = recommandItem.parentNode 40 | node.parentNode.removeChild(node) 41 | } 42 | } 43 | } 44 | 45 | // 清洁评论 46 | if (hasCommList) { 47 | for (let commItem of $('.t')) { 48 | console.log(commItem.innerHTML) 49 | if (commItem.innerHTML.indexOf('card="{0}"'.format(item)) != -1) { 50 | let node = commItem.parentNode 51 | node.parentNode.removeChild(node) 52 | } 53 | } 54 | } 55 | }) 56 | } 57 | 58 | const DOMModificationHandler = function () { 59 | $(this).unbind('DOMSubtreeModified') 60 | setTimeout(function () { 61 | // 判断document是否有变化 62 | if (html != document.documentElement.innerHTML) { 63 | clean() 64 | html = document.documentElement.innerHTML 65 | } 66 | $(document).bind('DOMSubtreeModified', DOMModificationHandler) 67 | }, 250) 68 | } 69 | 70 | // Checking page title 71 | if (document.title.indexOf('哔哩哔哩') != -1) { 72 | clean() 73 | DOMModificationHandler() 74 | } 75 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack') 2 | const HtmlWebpackPlugin = require('html-webpack-plugin') 3 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 4 | const path = require('path') 5 | const srcPath = './src' 6 | const buildPath = '/build' 7 | 8 | module.exports = { 9 | devtool: 'eval-source-map', 10 | 11 | entry: { 12 | main: __dirname + '/src/js/main.js', 13 | popup: __dirname + '/src/js/popup.js' 14 | }, 15 | output: { 16 | path: __dirname + buildPath, 17 | filename: '[name].js' 18 | }, 19 | 20 | module: { 21 | loaders: [{ 22 | test: /\.json$/, 23 | loader: 'json' 24 | }, 25 | { 26 | test: /\.js$/, 27 | exclude: /node_modules/, 28 | loader: 'babel', 29 | query: { 30 | presets: ['es2015'] 31 | } 32 | }, 33 | { 34 | test: /\.scss$/, 35 | loaders: ['style', 'css', 'sass'] 36 | }, 37 | { 38 | test: /\.css$/, 39 | loader: 'style-loader!css-loader' 40 | }, 41 | { 42 | test: /\.(png|jpg)$/, 43 | loader: 'url-loader?limit=8192' 44 | }, 45 | { 46 | test: /\.vue$/, 47 | loader: 'vue' 48 | }, 49 | { 50 | test: /\.(woff|woff2|eot|ttf|svg)$/, 51 | loader: 'url-loader?limit=100000' 52 | }, 53 | { 54 | test: require.resolve('jquery'), 55 | loader: 'expose?jQuery!expose?$' 56 | }, 57 | { 58 | test: /\.(jpe?g|png|gif|svg)$/i, 59 | loaders: [ 60 | 'image-webpack' 61 | ] 62 | } 63 | ] 64 | }, 65 | 66 | vue: { 67 | loaders: { 68 | js: 'babel' 69 | } 70 | }, 71 | 72 | postcss: [], 73 | 74 | sassLoader: { 75 | includePaths: [path.resolve(__dirname, srcPath)] 76 | }, 77 | 78 | imageWebpackLoader: { 79 | pngquant: { 80 | quality: '65-90', 81 | speed: 4 82 | }, 83 | jpegtran: { 84 | progressive: false 85 | } 86 | }, 87 | 88 | plugins: [ 89 | new HtmlWebpackPlugin({ 90 | filename: 'popup.html', 91 | template: __dirname + '/src/popup.tmpl.html', 92 | excludeChunks: ['main'] 93 | }), 94 | new ExtractTextPlugin('[name].css'), 95 | new webpack.ProvidePlugin({ 96 | $: 'jquery', 97 | jQuery: 'jquery' 98 | }) 99 | ] 100 | 101 | } -------------------------------------------------------------------------------- /src/js/component/app.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 89 | 90 | -------------------------------------------------------------------------------- /src/js/utils.js: -------------------------------------------------------------------------------- 1 | //仿python的range函数,生成指定范围的数组 2 | function range(start, stop, step) { 3 | if (typeof stop == 'undefined') { 4 | // one param defined 5 | stop = start; 6 | start = 0; 7 | } 8 | 9 | if (typeof step == 'undefined') { 10 | step = 1; 11 | } 12 | 13 | if ((step > 0 && start >= stop) || (step < 0 && start <= stop)) { 14 | return []; 15 | } 16 | 17 | var result = []; 18 | for (var i = start; step > 0 ? i < stop : i > stop; i += step) { 19 | result.push(i); 20 | } 21 | 22 | return result; 23 | } 24 | 25 | 26 | //string格式化 27 | String.prototype.format = function (args) { 28 | var result = this; 29 | if (arguments.length > 0) { 30 | if (arguments.length == 1 && typeof (args) == "object") { 31 | for (var key in args) { 32 | if (args[key] != undefined) { 33 | var reg = new RegExp("({" + key + "})", "g"); 34 | result = result.replace(reg, args[key]); 35 | } 36 | } 37 | } 38 | else { 39 | for (var i = 0; i < arguments.length; i++) { 40 | if (arguments[i] != undefined) { 41 | //var reg = new RegExp("({[" + i + "]})", "g");//这个在索引大于9时会有问题,谢谢何以笙箫的指出 42 | var reg = new RegExp("({)" + i + "(})", "g"); 43 | result = result.replace(reg, arguments[i]); 44 | } 45 | } 46 | } 47 | } 48 | return result; 49 | } 50 | 51 | 52 | //为这破js打补丁,foreach循环,非标准js,chrome和firefox支持,ie不支持,下面是为ie添加支持 53 | if (!Array.prototype.forEach) { 54 | Array.prototype.forEach = function (callback, thisArg) { 55 | var T, k; 56 | if (this == null) { 57 | throw new TypeError(" this is null or not defined"); 58 | } 59 | var O = Object(this); 60 | var len = O.length >>> 0; // Hack to convert O.length to a UInt32 61 | if ({}.toString.call(callback) != "[object Function]") { 62 | throw new TypeError(callback + " is not a function"); 63 | } 64 | if (thisArg) { 65 | T = thisArg; 66 | } 67 | k = 0; 68 | while (k < len) { 69 | var kValue; 70 | if (k in O) { 71 | kValue = O[k]; 72 | callback.call(T, kValue, k, O); 73 | } 74 | k++; 75 | } 76 | }; 77 | } 78 | 79 | //数组的remove函数,dx为所需删除项的下标 80 | Array.prototype.remove = function (dx) { 81 | if (isNaN(dx) || dx >= this.length) { return false; } 82 | for (var i = 0, n = 0; i < this.length; i++) { 83 | if (this[i] != this[dx]) { 84 | this[n++] = this[i] 85 | } 86 | } 87 | this.length -= 1; 88 | }; 89 | 90 | //检查数组是否为空,为空则返回空数组,不为空则返回元素为1的一个数组 91 | function checkArray(arr) { 92 | if (arr.length > 0) { 93 | return [1] 94 | } else { 95 | return [] 96 | } 97 | } 98 | 99 | //clone对象 100 | var clone = (function(){ 101 | // classify object 102 | var classof = function(o){ 103 | if (o === null) { return "null"; } 104 | if (o === undefined) { return "undefined"; } 105 | // I suppose Object.prototype.toString use obj.constructor.name 106 | // to generate string 107 | var className = Object.prototype.toString.call(o).slice(8,-1); 108 | return className; 109 | }; 110 | 111 | var references = null; 112 | 113 | var handlers = { 114 | // Handle regexp and date even in shallow. 115 | 'RegExp': function(reg) { 116 | var flags = ''; 117 | flags += reg.global ? 'g' : ''; 118 | flags += reg.multiline ? 'm' : ''; 119 | flags += reg.ignoreCase ? 'i' : ''; 120 | return new RegExp(reg.source, flags); 121 | }, 122 | 'Date': function(date) { 123 | return new Date(+date); 124 | }, 125 | 'Array': function(arr, shallow) { 126 | var newArr = [], i; 127 | for (i=0; i 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | --------------------------------------------------------------------------------