// ==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
});
};