NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name 梦姬贴吧助手 // @name:en Yume Tieba Helper // @name:zh-CN 梦姬贴吧助手 // @name:zh-TW 夢姬貼吧小幫手 // @namespace org.jixun.tieba.ass // @description:en Just Another Tieba Helper // @description:zh-tw 又一個貼吧助手 // @description:zh-cn 又一个贴吧助手 // @description 又一个贴吧助手 // @include http://tieba.baidu.com/* // @version 2.2.73 // @license MIT License; https://raw.githubusercontent.com/JixunMoe/yume-tieba-helper/master/LICENSE /// jQuery 留一份自己用 // @require http://cdn.staticfile.org/jquery/2.1.1/jquery.min.js // @require http://cdn.staticfile.org/jquery-scrollTo/1.4.11/jquery.scrollTo.min.js /// 配置界面 UI // @require http://cdn.staticfile.org/mustache.js/0.8.1/mustache.min.js // @require https://greasyfork.org/scripts/2657/code/tieba_ui.js /// 非同步数组枚举回调 // @require https://greasyfork.org/scripts/3588/code/Interval_Looper.js /// 兼容 GM 1.x, 2.x // @require https://greasyfork.org/scripts/2599/code/gm2_port_v103.js // @supportURL https://github.com/JixunMoe/yume-tieba-helper/issues/new // @grant GM_xmlhttpRequest // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_getValue // @grant GM_listValues // @grant GM_deleteValue // @grant unsafeWindow // ==/UserScript== var w = unsafeWindow, _main; jQuery(function ($) { var iv = setInterval(function () { if (w.jQuery && w.PageData && w.PageData.tbs) { clearInterval(iv); console.log('PageData loaded.'); if (!w.bdShare) { w.bdShare = unsafeObject({ ready: false }); } w.PageData.games = unsafeObject([]); unsafeExec (function () { // 改进自 congxz6688 的 tieba_quote [#147] // 节取自 寂寞的原子 的 悬浮窗脚本 [#116] _.Module.use("common/widget/RichPoster", {}, function (t) { t.init(); t.unbindScrollEvent(); }); }); _main ($, w.PageData); } }, 500); setTimeout (function () { // 15s later force kill waiting. clearInterval (iv); }, 15000); }); var __type_floor = 1, __type_lzl = 2, __type_forum = 4, __type_postact = 8; var __mod_default = 0, __mod_enable = 1, __mod_disable = 2; _main = function ($, wPageData) { // 检查是否在贴吧 if (!wPageData.forum) return ; var isThread = !!wPageData.thread; var _css = $('<style>'); var _cssH = $('<style>').text('.ads{display:none !important;}'); //// Function Helper Object.defineProperty (Function.prototype, 'extract', { value: function () { return this.toString().match(/\/\*([\s\S]+)\*\//)[1]; } }); var _function = function (foo, proto) { foo.prototype = proto; return foo; }; var $conf = new (_function (function () {}, { get: function (m, def) { var val = GM_getValue (m, null); if (!val) return def; try { return JSON.parse (val); } catch (e) { return def; } }, set: function (m, val) { return GM_setValue (m, JSON.stringify (val)); }, rm: function () { [].forEach.call (arguments, GM_deleteValue); }, ls: function () { return GM_listValues (); } })) (); var _hide = function () { _cssH.prepend (Array.prototype.join.call(arguments, ',') + ','); }; var _run = function (foo, name) { // console.groupCollapsed ('[贴吧助手]: ' + (name || '[未知区段]')); for (var args = [], i = 2, ret; i<arguments.length; i++) args.push (arguments[i]); try { ret = foo.apply (this, args); if (ret !== undefined) console.info ('[贴吧助手][返回][%s]: %s', name || '[未知区段]', ret); } catch (err) { console.error ('[贴吧助手][错误][%s]: %s', name || '[未知区段]', err.message); console.error (err); } // console.groupEnd (); return ret; }; $.fn.getField = function () { // var $data = this.attr('data-field'); var $data = this.data ('field'); if ('string' == typeof $data[1]) return JSON.parse($data.replace(/'/g, '"')); return $data; }; $.goToEditor = function () { $('#ueditor_replace').focus (); $.scrollTo($('#tb_rich_poster_container'), 500); }; $.create = function (ele, cls, attr) { var r = $(document.createElement (ele)); if (cls) r.addClass (cls); if (attr) r.attr (attr); return r; }; $.stamp = function () { return + new Date (); }; $.toDateStr = function (d) { return d.toLocaleString(); }; var modules = { "ads_hide": { name: '广告隐藏、屏蔽', desc: '屏蔽无用、广告内容', flag: ~0, _init: function () { var $ads = [ // 贴吧推广 '.spreadad, .game_frs_step1, .BAIDU_CLB_AD, .dasense, .u9_head', '.j_click_stats, .p_postlist>div:not(.l_post):not(.p_postlist)', '[id="pagelet_frs-header/pagelet/head_content_middle"]', '[id="pagelet_encourage-appforum/pagelet/my_app"]', '.life_helper', // 到处插入的广告 '[data-daid]', // 右下角广告 '#game_pop_window', // 直播 '#game_live_list', // 10 年 '#j_ten_years', // 1l 下方的广告 '#sofa_post, .banner_post', // 贴吧顶部广告 '#pb_adbanner', // 图片页面 '.af_head_gamelink', // 左右侧 '.j_couplet', // 右侧 '#encourage_entry', '.platform_aside_tieba_partner', // 客户端发贴 x 倍经验 '.tb_poster_placeholder', // 语音按钮 (需要客户端) '.edui-btn-voice, .j_voice_ad_gif, .lzl_panel_voice', // 发帖请遵守 .... '.poster_head_surveillance', // 不水能死何弃疗! '.lzl_panel_wrapper > tbody > tr > td:first-child > p', // 会员相关广告 '.doupiao_offline, .fMember_cnt', // 右上角 '.u_tshow, .u_tbmall, .u_app, .u_wallet, .u_xiu8', '.u_mytbmall, .u_joinvip, .u_baiduPrivilege, .u_appcenterEntrance', // 右下角 '#pop_frame, #__bdyx_tips, #__bdyx_tips_icon', // 猜拳 '.add_guessing_btn, .guessing_watermark', // 帖子推荐 '.thread_recommend', // 右下角广告 '#__bdyx_tips, #__bdyx_tips_icon, .baidu-tuisong-wrap, .baidutuisong', // 打赏、分享 '.reward_btn, .share_btn_wrapper', // 烟花 '.firework_sender_wrap, .global_notice_wrap', '.tbui_fbar_share, .tbui_fbar_tsukkomi, .tbui_fbar_props, .tbui_fbar_square, .tbui_fbar_home', '#tshow_out_date_warn, #selectsearch-icon', // 贴吧推荐 '#forum_recommend' ].join(', '); $($ads).remove(); $('<style>').text($ads + /* File: ads_hide.css */ (function () { /* { display:none !important; } #com_userbar_message { right: 30px !important; top: 28px !important; } #com_userbar_message > .j_ui_triangle { left: 65px !important; } */}).extract ()).appendTo (document.head); // 只保留 [看帖、图片、精品、视频] 四个选项 $('.j_tbnav_tab').filter (function (i) { return i > 3; }).remove (); // 执行三次, 确保分隔符会消失 for (var i = 3; i--; ) { setTimeout (function () { $('.split_text').next('.split_text').remove(); $('.split').filter(function () { return this.nextElementSibling === null || this.nextElementSibling.className == this.className || !$(this.nextElementSibling).is(':visible'); }).remove(); }, 3000 * i); } this.removePromoteThread(); }, _proc: function (floorType, args) { if (floorType == __type_forum) { if (args.thread.find('.threadlist_rep_num').text() == '推广') args.thread.remove(); } }, removePromoteThread: function () { // 清理帖子列表的推广 var it = document.evaluate('//*[@id="thread_list"]/li/div/div/div[text()="推广"]', document.body, null, XPathResult.ANY_TYPE, null); var thread, threads = []; while (thread = it.iterateNext()) threads.push(thread); $(threads).parents('li').remove(); } } , "ads_thread_list": { name: '屏蔽直播贴等乱七八糟内容 (实验性)', desc: '如题。', flag: ~0, _init: function () { var $ads = [ // 帖子列表顶部, 如直播贴 '#threadListGroupCnt' ].join(', '); $($ads).remove(); $('<style>').text($ads + '{display: none !important}').appendTo('head'); } }, "audio_download": { name: '贴吧语音下载', desc: '下载贴吧语音~ 啦啦啦~', flag: __type_floor | __type_lzl, _proc: function (floorType, args) { var _player = $('.voice_player:not(.parsed)', args._main); if (!_player.size()) return '找不到语音'; var data = _player.parents('[data-field]').getField(), pid = data.spid || data.content.post_id; _player.addClass('parsed').after ( $('<a>').addClass('ui_btn ui_btn_m') .attr({ href: '/voice/index?tid=' + wPageData.thread.thread_id + '&pid=' + pid, download: '语音-' + (data.user_name || data.author.user_name) + '-' + pid + '.mp3' }) .css ({ marginLeft: '1em' }) .append ($('<span>').text('下载')) ).after($('<br>')); } }, "block_post": { name: '贴吧贴子屏蔽', desc: '根据规则屏蔽指定贴子', flag: __type_floor | __type_forum | __type_lzl, // 辅助函数 _match_type: function (_M) { switch (_M) { case this.__M_REGEX: return 'tp_regex'; case this.__M_PLAIN: return 'tp_plain'; } return 'undefined_' + _M; }, // 辅助函数 _range: function (old, min, max) { return Math.min (Math.max (min, old), max); }, // 初始化样式表 _init: function () { _css .append ('ul#jx_post_kword > li {margin-bottom: .2em}') .append ('.jx_word { padding: 0 .5em; width: 8em } span.regex::before, span.regex::after { content: "/"; color: #777 }') .append ('span.regex > .jx_word { border: 0; padding: 0 .2em }') .append ('.jx_modifier { width: 4em; border: 0; padding: 0 0 0 .2em }') .append ('.jx_post_block_stripe::before{content: "共隐藏 " attr(hide-count) " 个数据"}'); $.extend (this, { // Action to take when match __ACT_BAR: 0, __ACT_OPA: 1, __ACT_HIDE: 2, // Keyword match method __M_REGEX: 0, __M_PLAIN: 1 }); this.config = $.extend ({ onmatch: this.__ACT_OPA, opacity: 30, kword: [{ type: this.__M_PLAIN, word: '泽火革' }], user: [ '炮弹56', '炮弹52' ] }, $conf.get (this.id)); this._compileRegex (); this.$tplConfig = /* File: block_post.html */ (function () { /* <div class="jx_autoflow"> <h3>当匹配到时的操作</h3> <p> <select id="jx_post_match"> <option value="0" {{#tp_bar}}selected{{/tp_bar}}>红条</option> <option value="1" {{#tp_opa}}selected{{/tp_opa}}>透明</option> <option value="2" {{#tp_hide}}selected{{/tp_hide}}>隐藏</option> </select> <label title="0 表示完全透明 (占位难看哦); 0~100"{{^tp_opa}} class="hide"{{/tp_opa}}>透明度 <input type="number" id="jx_post_opa" class="text-center" value="{{opacity}}" style="width: 5em" />% </label> </p> <br /> <h3>内容屏蔽规则</h3> <ul id="jx_post_kword"> {{#kword}} <li> <select class="jx_word_type"> <option value="0" {{#tp_regex}}selected{{/tp_regex}}>正则</option> <option value="1" {{#tp_plain}}selected{{/tp_plain}}>文本</option> </select> <span{{#tp_regex}} class="regex"{{/tp_regex}}><input class="jx_word" value="{{word}}" /></span><!-- --><input class="jx_modifier{{^tp_regex}} hide{{/tp_regex}}" value="{{modi}}" /> [ <a class="ptr jx-rm-key" >删除</a> ] </li> {{/kword}} </ul> <p><a class="ui_btn ui_btn_m" data-btn="add"><span><em>添加</em></span></a></p> <br /> <h3>用户屏蔽列表</h3> <p>用户列表,一行一个</p> <!-- Hackish solution --> <div style="padding-right: 10px;"> <textarea id="jx_post_user" row=5 style="width: 100%; padding: .2em">{{user}}</textarea> </div> <br /> <p class="text-center"> <a class="ui_btn ui_btn_m" data-btn="save"><span><em>储存</em></span></a> <a class="ui_btn ui_btn_m" data-btn="close"><span><em>放弃</em></span></a> </p> </div> */}).extract (); this.$tplAddWord = /* File: block_post_kword.html */ (function () { /* <li> <select class="jx_word_type"> <option value="0" {{#tp_regex}}selected{{/tp_regex}}>正则</option> <option value="1" {{#tp_plain}}selected{{/tp_plain}}>文本</option> </select> <span{{#tp_regex}} class="regex"{{/tp_regex}}><input class="jx_word" value="{{word}}" /></span><!-- --><input class="jx_modifier{{^tp_regex}} hide{{/tp_regex}}" value="{{modi}}" /> [ <a class="ptr jx-rm-key" >删除</a> ] </li> */}).extract (); this.css = $('<style>').appendTo (document.head); this._rebuildStyle (); }, // 重构样式表 _rebuildStyle: function () { var sBuilder = '.jx_post_block_act {'; switch (this.config.onmatch) { case this.__ACT_BAR: sBuilder += 'display: none;'; break; case this.__ACT_HIDE: sBuilder += [ 'display: none;', '}', '.jx_post_block_stripe {', 'display: none' // , '}' ].join (''); break; case this.__ACT_OPA: sBuilder += [ 'opacity: ' + (this.config.opacity / 100) + ';', 'transition: opacity .5s;', '}', '.jx_post_block_act:hover {', 'opacity: .9;', '}', '.jx_post_block_stripe {', 'display: none' // , '}' ].join (''); break; } sBuilder += '}'; this.css.text(sBuilder); }, // 编译正则匹配 _compileRegex: function () { var that = this; this.config.kword.forEach (function (e) { try { if (e.type === that.__M_REGEX) e.regex = new RegExp (e.word, e.modi); } catch (err) { console.error ('编译正则表达式时出错!\n表达式: %s, 开关: %s', err.word, err.modi); err.regex = { test: function () { return false; } }; } }); }, // 配置窗口回调 _conf: function () { var $view = $.extend(true, {}, this.config); $view.tp_hide = $view.onmatch === this.__ACT_HIDE; $view.tp_opa = $view.onmatch === this.__ACT_OPA; $view.tp_bar = $view.onmatch === this.__ACT_BAR; for (var i = 0; i < $view.kword.length; i++) $view.kword[i][this._match_type($view.kword[i].type)] = true; $view.user = $view.user.join ('\n'); var $tpl = $(Mustache.render (this.$tplConfig, $view)); var $wndBlocker = $.dialog.open ($tpl, { title: '贴子关键字屏蔽', width: 300, height: 400 }); var that = this; $tpl.on ('click', 'a.jx-rm-key', function () { // 移除那一行 $(this).parent ().remove (); }).on ('change', '.jx_word_type', function () { var isRegex = parseInt (this.value) === that.__M_REGEX; var line = $(this).parent (); line.find ('.jx_word').parent().toggleClass ('regex', isRegex); line.find ('.jx_modifier').toggleClass ('hide', !isRegex); }).on ('change', '#jx_post_match', function () { $('#jx_post_opa', $tpl).parent ().toggleClass ('hide', parseInt (this.value) !== that.__ACT_OPA); }).on ('click', '.ui_btn', function () { switch ($(this).data('btn')) { case 'add': var $tplAdd = $(Mustache.render(that.$tplAddWord, { tp_plain: true })); $('#jx_post_kword', $tpl).append ($tplAdd); $tplAdd.find ('.tg_focus').removeClass ('.tg_focus').focus(); break; case 'save': var newConf = { onmatch: parseInt ($('#jx_post_match', $tpl).val()), opacity: that._range (parseInt ($('#jx_post_opa', $tpl).val()), 0, 100), kword: [], user: $('#jx_post_user', $tpl).val().split ('\n') }; $('#jx_post_kword > li').each (function () { var rule = $(this); newConf.kword.push ({ type: parseInt (rule.find ('select').val ()), word: rule.find ('.jx_word').val (), modi: rule.find ('.jx_modifier').val () }); }); $conf.set (that.id, newConf); that.config = newConf; that._compileRegex (); that._rebuildStyle (); $wndBlocker.close (); break; case 'close': $wndBlocker.close (); break; } }); }, // 标记贴子为隐藏 _hit: function (floor) { floor.addClass ('jx_post_block_act'); if (floor.prev().is('script')) floor.prev().remove (); if (floor.prev().is('.jx_post_block_act')) { // 寻找横条 var prev = floor.prev (); while (!prev.is ('.jx_post_block_stripe')) prev = prev.prev (); prev.attr ('hide-count', parseInt (prev.attr ('hide-count')) + 1); } else { $('<div>').addClass ('jx_post_block_stripe floor-stripe') .attr('hide-count', 1).insertBefore (floor); } }, _getAuthor: function (f) { return f.user_name || f.author_name || f.author.user_name; }, _proc: function (floorType, args) { // 首先检查用户名 if (this.config.user.indexOf (this._getAuthor(args._main.getField ())) !== -1) { this._hit (args._main); return ; } var floorContent; switch (floorType) { case __type_forum: floorContent = $('.threadlist_text', args._main).text(); break; case __type_floor: floorContent = $('.d_post_content', args._main).text(); break; case __type_lzl: floorContent = $('.lzl_content_main', args._main).text(); break; } // 然后循环检查关键字匹配 for (var i = this.config.kword.length; i--; ) { switch (this.config.kword[i].type) { case this.__M_REGEX: if (this.config.kword[i].regex.test (floorContent)) this._hit (args._main); break; case this.__M_PLAIN: if (floorContent.indexOf (this.config.kword[i].word) !== -1) this._hit (args._main); break; } } } }, "hide_loops": { name: '3 天循环隐藏', desc: '3 天循环屏蔽指定用户的帖子, 统一封锁.', flag: __type_postact | __type_forum, _findUser: function (name) { if (0 === this.blockList.author.length) return -1; for (var i = this.blockList.author.length; i--; ) { if (this.blockList.author[i].name == name) return i; } return -1; }, _userExist: function (user) { return -1 !== this._findUser(user); }, _conf: function () { var that = this; var $tpl = $(Mustache.render(this.tplHideAuthor, { author: this.blockList.author.map (function (e, i) { return { name: e.name, time: e.time ? $.toDateStr (new Date(e.time)) : '尚未' }; }) })); var $wndHideUser = $.dialog.open ($tpl, { title: '3天循环隐藏模组配置 - 记得点一次 [全部封禁]', width: 370, height: 400 }); var $inp = $('#jx_new_id', $tpl); var cbAddName = function () { var user = $inp.val ().trim(); that._updList (); if (0 === user.length || that._userExist(user)) return ; $inp.val (''); $(Mustache.render (that.tplNewLine, { name: user, time: '尚未' })).insertBefore($('#jx_last_line_of_3day_block', $tpl)); that.blockList.author.push ({ name: user, time: 0 }); that._saveList (); }; // 绑定事件 $('#jx_add', $tpl).click(cbAddName); $inp.keypress(function (e) { if (e.which === 13) cbAddName (); }); $tpl .on ('click', '.jx_man_hide, .jx_man_rm', function (e) { var $l = $(e.target); if ($l.hasClass ('text-disabled')) return ; $l.addClass ('text-disabled'); var $un = $l.parent().data('name'); that._updList(); switch (true) { case $l.hasClass ('jx_man_hide'): that.blockList.author[that._findUser($un)].time = $.stamp(); that._hide (function () {}, $un); break; case $l.hasClass ('jx_man_rm'): that.blockList.author.splice(that._findUser($un), 1); $l.parent().hide (); break; } that._saveList(); }); $('#jx_close', $tpl).click($wndHideUser.close.bind($wndHideUser)); $('#jx_all', $tpl).click(function () { var hideStatus = $('#jx_hide_info', $tpl).show().text ('正在初始化…'); that.hideQueue.onProgress = function (i, t) { hideStatus.text(Mustache.render('正在隐藏 {{i}} / {{t}}... 请勿关闭该窗口!', {i: i, t: t})); }; that.hideQueue.onComplete = function () { that.hideQueue.onProgress = that.hideQueue.onComplete = null; hideStatus.text ('全部用户已成功隐藏!'); }; that.hideQueue.add.apply ( that.hideQueue, Array.prototype.slice.call($('a.jx.jx_man_hide:not(.text-disabled)').addClass('text-disabled').map(function (i, e) { return $(e).parent().data('name'); })) ); }); return $tpl; }, _hide: function (cb, author) { // 检查是否在列表 this._updList (); if (this._userExist (author)) { // 如果存在, 修正上次隐藏时间 this.blockList.author[this._findUser(author)].time = $.stamp(); this._saveList (); } console.info ('开始隐藏: %s', author); $.ajax ({ url: '/tphide/add', type: 'POST', data: { type: 1, hide_un: author, ie: 'utf-8' }, dataType: 'json' }).success (cb); }, _init: function () { this.tplHideAuthor = /* File: hide_loops_config.html */ (function () { /* <div class="jx_autoflow"> <h2>3 天循环隐藏的列表</h2> <p class="text-center">请注意: 封禁时间不会自动刷新, 请关闭后重新开启该对话框。</p> <ol> {{#author}} <li data-name="{{name}}"><b>{{name}}</b> [ 上次隐藏: <span class="text-red">{{time}}</span> | <a class="jx jx_man_hide">手动</a> | <a class="jx jx_man_rm">移除</a> ]</li> {{/author}} <li id="jx_last_line_of_3day_block"> <input id="jx_new_id" placeholder="请输入新的需要自动封禁的 id" style="width: 20em;" /> <br /><a class="ui_btn ui_btn_m" id="jx_add"><span><em>添加</em></span></a> </li> </ol> <p class="hide" id="jx_hide_info"></p> <div class="text-center"> <a class="ui_btn ui_btn_m" id="jx_all"><span><em>全部封禁</em></span></a> <a class="ui_btn ui_btn_m" id="jx_close"><span><em>关闭</em></span></a> </div> </div> */}).extract (); this.tplNewLine = /* File: hide_loops_author.html */ (function () { /* <li data-name="{{name}}"><b>{{name}}</b> [ 上次隐藏: <span class="text-red">{{time}}</span> | <a class="jx jx_man_hide">手动</a> | <a class="jx jx_man_rm">移除</a> ]</li> */}).extract (); this._updList (); var _hide = this._hide.bind (this); this.hideQueue = new IntervalLoop ([], _hide, 400).loop (); var curTime = $.stamp (); var t3Days = 3 * 24 * 60 * 60; var that = this; this.blockList.author.forEach (function (e) { if (curTime - e.time > t3Days) that.hideQueue.add (e.name); }); }, _updList: function () { this.blockList = $.extend ({ author: [ // 格式如下 //{ // name: '炮弹56', // lastHide: 0 //} ] }, $conf.get (this.id, {})); }, _saveList: function () { $conf.set (this.id, this.blockList); }, _findNameAndHide: function (e) { var floorData = $(e.target).parents('.lzl_single_post,.l_post') .first().getField(); var author = floorData.user_name || floorData.author.user_name; if (this._userExist(author)) { $.dialog.alert (Mustache.render(/* File: hide_loops_already_in_list.html */ (function () { /* 用户 [<b>{{name}}</b>] 已存在于屏蔽列表! */}).extract (), {name: author}), { title: '3 天循环隐藏' }); return ; } this._updList (); this.blockList.author.push ({ name: author, time: $.stamp() }); this._saveList (); this._hide (function (r) { $.dialog.alert (Mustache.render(/* File: hide_loop_result.html */ (function () { /* 对 <b>{{name}}</b> 的隐藏处理结果: {{msg}}({{no}}) */}).extract (), $.extend ({name: author}, r)), { title: '3 天循环隐藏 (楼中楼无效)' }); }, author); }, _menu: function (floorType, args) { var $act = $('.user-hide-post-action', args._main); var $actHidePost = $.create('a', 'jx jx-post-action'); $actHidePost .text ('加入 3 天循环隐藏列表') .appendTo ($act) .data ('jx', this.id) .data ('eve', args._main.getField().author.user_name) .click(this._findNameAndHide.bind(this)); } }, "icon_hide": { name: '隐藏用户图标', desc: '将用户名下方、右方的图标集藏起来。', def: false, flag: ~0, _init: function () { _hide ('.icon_wrap'); } }, "no_text_link": { name: '屏蔽帖子内文字推广链接', desc: '将帖子内的文字推广搜索链接替换为普通文本', flag: __type_lzl | __type_floor, _proc: function (floorType, args) { this.rmLinkText (args._main); }, _init: function () { this.rmLinkText (); }, rmLinkText: function (_p) { $(_p || 'body').find ('a.ps_cb').each(function () { $(this).after (document.createTextNode (this.textContent)); }).remove(); } }, "orange": { name: '移除会员彩名', desc: '全部变成变成默认链接颜色。', flag: __type_floor | __type_lzl | __type_forum, clsList: ['sign_highlight', 'vip_red', 'fiesta_member', 'fiesta_member_red', 'member_thread_title_frs', 'sign_highlight'], rmOrange: function (target) { var $target = $(target); for (var i = 1; i < this.clsList.length; i++) $('.' + this.clsList[i], $target.removeClass (this.clsList[i])).removeClass(this.clsList[i]); }, _init: function () { // 标题红名移除 this.rmOrange ('body'); }, _proc: function (floorType, args) { this.rmOrange (args._main); } }, "quote": { name: '引用楼层', desc: '引用某一层的内容', flag: __type_floor, _proc: function (floorType, args) { var $quote = $('<li>').addClass('pad-left').append( $('<a>').text('#引用').addClass('jx') .data('jx', 'quote').data('floor', args.floorNum) ).prependTo($('.p_tail', args._main)); }, _click: function ($ele, $eve) { var $floor = $ele.parents('.l_post'); var $editor = $('#ueditor_replace'); var $quote = $('<p>').appendTo($editor); $quote .append ('引用 ' + $ele.data('floor') + '楼 @' + $('.p_author_name', $floor).first().text() + ' 的发言:') .append ('<br>') .append ('——————————') .append ('<br>'); $('.j_d_post_content', $floor).contents().each(function (i, ele) { if (ele.nodeType == 3) { if (ele.nodeValue.trim() !== '') $quote.append (ele.nodeValue); return ; } var $ele = $(ele); if ($ele.is('a')) { if ($ele.find('img').size()) { $quote.append ('[#图片]'); } else { $quote.append ($ele.text()); } } else if ($ele.is ('img')) { $quote.append ('[#表情]'); } else if ($ele.is ('object,embed')) { $quote.append ('[#视频]'); } else { $quote.append ($ele.clone()); } }); $quote.append ('<br>> '); $.goToEditor(); } }, "quote_lzl": { name: '楼中楼帖子引用', desc: '引用楼中楼的回复', flag: __type_lzl, _proc: function (floorType, args) { $('<a>').text('引用').addClass('jx d_tail') .insertBefore($('.lzl_time', args._main)) .after($('<span>').addClass('d_tail').text(' | ')) .data('jx', 'quote_lzl'); }, _click: function ($ele, $eve) { var $editor = $('#ueditor_replace'); var $cnt = $ele.parents('.lzl_cnt'); $('<p>').appendTo($editor) .append ('引用 @' + $cnt.find('.j_user_card').attr('username') + ' 在楼中楼的发言:<br>') .append ($ele.parents('.lzl_cnt').find('.lzl_content_main').text()) .append ('<br>') .append ('——————————') .append ('<br> ><br>'); $.goToEditor(); } }, "real_url": { name: '贴吧跳转链解除', desc: '将百度所谓安全链接改成直链。', flag: __type_floor | __type_lzl, _proc: function (floorType, args) { var $floor = $(args._main); $floor.find('a[href*="jump.bdimg.com/safecheck"]').each(function (i, ele) { var $ele = $(ele), $url = $ele.text(); if ($url.indexOf('@') === 0) { // Do nothing. } else if (/^https?:\/\//.test($url)) { $ele.attr('href', $url); } else { // HEAD 请求会变成 error ..? GM_xmlhttpRequest ({ method: 'GET', url: ele.href, headers: { // 去你的百度 Referer: 'http://tieba.baidu.com/p/123456789', Range: 'bytes=0-0' }, onload: function (response) { if (response.finalUrl.indexOf('http') === 0) { $ele.attr('href', response.finalUrl); } } }); } }); } }, "rmImgFav": { name: '移除图片的收藏工具栏', desc: '鼠标悬浮图片时出现的工具栏。', flag: 0, _init: function () { $('.fav-wrapper').remove(); } }, "rmSaveFace": { name: '隐藏挽尊卡提示', desc: '隐藏会员发帖的使用挽尊卡提示。', flag: 0, _init: function () { _hide ('.save_face_bg'); } }, "rm_img_view": { name: '看图模式屏蔽', desc: '还原旧版贴吧点图看大图功能', flag: __type_floor, rmImg: function ($root) { $('img.BDE_Image', $root).each(function () { var m = this.src.match(/\/sign=[a-z0-9]+\/(.+)/i); if (!m) return ; var imgLink = '//imgsrc.baidu.com/forum/pic/item/' + m[1]; $('<a>') .attr('href', imgLink) .attr('target', '_blank') .append($('<img>').attr('src', imgLink).addClass('jx_no_overflow')) .insertAfter (this); $(this).remove(); }); }, _init: function () { _css.append ('.jx_no_overflow { max-width: 100%; }'); this.rmImg (document); }, _proc: function (floorType, args) { this.rmImg (args._main); } }, "save_face": { name: '挽尊卡隐藏', desc: '屏蔽挽尊卡,留下一个横条提示。', flag: __type_floor, _init: function () { _css.append ('.save_lz_face::before{content:attr(who) " 使用了挽尊卡"}'); }, _proc: function (floorType, args) { if ($('.save_face_post', args._main).size()) { // 发现挽尊卡 $('<div>').addClass('floor-stripe save_lz_face') .attr ('who', $('.p_author_name', args._main).text()) .insertBefore (args._main); args._main.addClass('savedFace').hide(); } } } }; var _menu = (function () { var $template = /* File: main_config.html */ (function () { /* <div style="height: 100%; overflow-y: auto"> <h2>启用的模组</h2> <div id="jx_conf_modules"> {{#modules}} <label title="{{desc}}"> <input type="checkbox" data-module="{{id}}" {{#enable}}checked{{/enable}}/> {{name}} </label>{{#config}}[ <a data-config="{{id}}" class="jx_conf ptr">配置</a> ]{{/config}} <br /> {{/modules}} </div> <br /> <!-- 按钮区 --> <div class="text-center"> <a class="ui_btn ui_btn_m" id="jx_save"><span><em>储存</em></span></a> <a class="ui_btn ui_btn_m" id="jx_close"><span><em>放弃</em></span></a> </div> </div> */}).extract (); return _run.bind ({}, function () { var $view = { modules: [] }; for (var x in modules) { if (modules.hasOwnProperty(x)) { var isEnable = lMods.hasOwnProperty(x); $view.modules.push ({ id: x, name: modules[x].name, desc: modules[x].desc, enable: isEnable, config: isEnable && !!modules[x]._conf }); } } var $tpl = $(Mustache.render ($template, $view)); var $wndConfig = $.dialog.open ($tpl, { title: '贴吧助手 - 配置窗口 (刷新后生效)', height: 200 }); $('.jx_conf', $tpl).click(function () { var x = $(this).data('config'); if (lMods.hasOwnProperty(x)) _run (lMods[x]._conf.bind (lMods[x]), '模组配置 [' + lMods[x].name + ' (' + x + ')]'); }); $('#jx_save', $tpl).click(function () { var newStatus = {}; $('#jx_conf_modules>label>input', $tpl).each(function (i, inp) { newStatus[$(inp).data('module')] = inp.checked ? __mod_enable : __mod_disable; }); $conf.set ('modules', newStatus); $wndConfig.close (); }); $('#jx_close', $tpl).click($wndConfig.close.bind($wndConfig)); }, '助手设定界面'); })(); // 未登录用户可以通过 GM 菜单激活配置项 GM_registerMenuCommand ('梦姬贴吧助手模块配置', _menu); if (unsafeWindow.__YUME_DEBUG__) { GM_registerMenuCommand ('打印模组配置', function () { console.info ('梦姬模组配置: '); console.info ($conf.get ('modules')); }); } _run (function () { var _callMenu = function ($parent) { console.info ('成功捕捉到菜单元素,传递至回调…'); _run (function () { var $menuItem = $('<li>'), $menuLink = $('<a>' ).appendTo ($menuItem).addClass('jx').text('助手设置'); $parent.find ('.u_tb_profile').before($menuItem); $menuLink.click (_menu); }, '菜单召唤'); }; var ma = new MutationObserver (function ($q) { try { $($q).each(function (i, $eve) { $($eve.addedNodes).each(function (i, $ele) { if ($ele.nodeType != 3 && $ele.className == 'u_ddl') { throw {ele: $($ele), name: 's'}; } }); }); } catch (err) { if (err.ele) { ma.disconnect(); _callMenu (err.ele); return ; } throw err; } }); setTimeout(function () { var _m = $('.u_setting>.u_ddl'); if (_m.length) { _callMenu (_m); } else { ma.observe($('.u_setting')[0], { childList: true, subtree: true }); } }, 1500); }, '捕捉设定'); // console.log ($('li.u_setting .u_tb_profile')); var lMods = {}; _run (function () { _css = $('<style>').appendTo(document.head); _css.append (/* File: tieba.css */ (function () { /* .pull-right { float: right } a.jx, .ptr { cursor: pointer } .pad-left { padding-left: 0.5em } .floor-stripe { background-image: linear-gradient(45deg,rgba(255,255,255,.15) 25%, transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent); background-color: #d9534f; background-size: 40px 40px; text-align: center; border: 1px solid #ccc; margin: -1px;color: #fff; text-shadow: #000 0 0 .5em; padding: .5em 0 } .hide { display: none } .text-red { color: red } .text-center { text-align: center } .text-disabled { color: #666; text-decoration: line-through } .user-hide-post-action > a.jx-post-action { display: block; padding: 3px 5px 5px; cursor: pointer; color: #222; } .user-hide-post-action a.jx-post-action:hover { background: #f2f2f2; } .jx_autoflow { height: 100%; overflow-y: auto; } */}).extract ()); _cssH.insertAfter(_css); // 配置项更新 switch ($conf.get ('confVer', [0])[0]) { case 0: var $disabledMods = $conf.get ('modules', []); var $modsList = {}; $disabledMods.forEach (function (e) { $modsList[e] = __mod_disable; }); $conf.set ('modules', $modsList); break; } $conf.set ('confVer', [1]); var $mods = $conf.get ('modules', {}); $.each (modules, function (mId, fMod) { if ($mods[mId] == __mod_disable || ( ($mods[mId] == __mod_default || !$mods.hasOwnProperty(mId)) && fMod.def === false ) ) return ; lMods[mId] = fMod; lMods[mId].id = mId; if (lMods[mId]._init) { console.info ('初始化模组: %s[%s]', mId, lMods[mId].name); lMods[mId]._init.call (lMods[mId]); } }); }, 'Init. modules'); var _event = function (floorType, otherInfo, _proc) { var fooCB = _proc || '_proc'; $.each (lMods, function (mId, m) { if (!m[fooCB] || !(m.flag & floorType)) return; _run (m[fooCB].bind(m, floorType, otherInfo), m.name); }); }; var _procLzlContainer = function (i, tailer) { var $tailer = $(tailer), _main = $tailer.parents('.l_post'); // console.log ($tailer, _main); _event (__type_floor, { _main: _main, floor: _main, // 「'」is not standard, convert to 「"」 first. floorNum: parseInt($tailer.getField().floor_num), tail: $('.p_tail', _main) }); // 处理解析 lzl 帖子(… // $tailer.find('.lzl_single_post').each(_procLzlPost); return _main; }; var _procThreadList = function (i, threadlist) { var $thread = $(threadlist); _event (__type_forum, { _main: $thread, thread: $thread }); return $thread; }; var _procLzlPost = function (i, lzlPost) { var $lzl = $(lzlPost); _event (__type_lzl, { _main: $lzl, lzl: $lzl }); return $lzl; }; if (isThread) { $('.j_lzl_container').each(_run.bind ({}, _procLzlContainer, '初始化帖子搜索')); $('.lzl_single_post').each(_run.bind ({}, _procLzlPost, '初始化楼中楼搜索')); } else { $('.j_thread_list').each(_run.bind ({}, _procThreadList , '初始化贴吧页帖子搜索')); } var mo = new MutationObserver (function (eve) { _run (function () { $(eve).each(function (i, eve) { if (!eve.addedNodes.length) return ; $(eve.addedNodes).each(function (i, ele) { // Text node. if (ele.nodeType == 3) return ; var $ele = $(ele), _type = 0, $tmp; // 单贴处理 if ($ele.hasClass ('j_lzl_container')) { // _type = __type_floor; $tmp = _procLzlContainer (i, $ele); $tmp.find('.lzl_single_post').each(_procLzlPost); } else if ($ele.hasClass ('j_thread_list')) { // 贴吧主页面 _procThreadList (i, $ele); } else if ($ele.hasClass ('lzl_single_post')) { // 仅限翻页时触发 _procLzlPost (i, $ele); } else if ($ele.hasClass ('user-hide-post-action') && !$ele.hasClass('jx_post')) { $ele.addClass('jx_post'); _event (__type_postact, { _main: $ele.parents('.l_post'), _menu: $ele }, '_menu'); } }); }); }, '页面元素插入'); }); $(document.body).on ('click', '.jx', function (eve) { var $eve = $(eve.target); var $data = $eve.data ('jx'); if (!$data || !lMods[$data] || !lMods[$data]._click) return ; _run.call (lMods[$data], lMods[$data]._click, '>> 单击助手功能: ' + $data, $eve, $eve.data('eve')); }); mo.observe($('#j_p_postlist,#thread_list').get(0), { childList: true, subtree: true }); };