Yet Another Weibo Filter

新浪微博根据关键词、作者、话题、来源等过滤微博;修改版面。 新浪微博根據關鍵字、作者、話題、來源等篩選微博;修改版面。 filter Sina Weibo by keywords, original, topic, source, etc.; modify layout

当前为 2014-08-04 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Yet Another Weibo Filter
  3. // @namespace https://github.com/tiansh
  4. // @description 新浪微博根据关键词、作者、话题、来源等过滤微博;修改版面。 新浪微博根據關鍵字、作者、話題、來源等篩選微博;修改版面。 filter Sina Weibo by keywords, original, topic, source, etc.; modify layout
  5. // @include http://weibo.com/*
  6. // @include http://www.weibo.com/*
  7. // @version 0.2.37 alpha
  8. // @author 田生
  9. // @copyright 2013+, 田生
  10. // @license The MIT License (MIT); http://opensource.org/licenses/MIT
  11. // @grant GM_xmlhttpRequest
  12. // @grant GM_setValue
  13. // @grant GM_getValue
  14. // @grant GM_addStyle
  15. // @grant GM_registerMenuCommand
  16. // @grant GM_info
  17. // @grant unsafeWindow
  18. // @run-at document-start
  19. // ==/UserScript==
  20.  
  21. // 图片
  22. var images = {
  23. 'filter': 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAFEUExURUxpcZ6eno6OjpmZmf////v7+/j4+Pr6+vr6+uzs7MvLy8rKyuzs7Ly8vLy8vO7u7r29vb29ve7u7unp6by8vOrq6ufn5+/v776+vry8vO3t7ejo6Ojo6Ovr6+/v7729ve7u7ry8vO/v7+vr6+jo6L6+vu7u7ry8vO/v7+rq6ujo6L6+vu3t7e/v77e3t+vr6+jo6Jubm5qampqamp+fn7m5uezs7MXFxe7u7p2dnZubm5+fn5ycnOzs7L6+vsDAwMLCwsHBwevr6/Ly8vr6+sPDw+3t7e/v78TExMHBwcnJycXFxff39+np6e7u7vPz8/f39/j4+O/v7/T09Ovr6/////Dw8PT09P///9zc3Pn5+fv7+////+3t7f///9ra2v///8DAwOPj49PT0////8LCwtHR0b+/v+Pj48bGxsHBwcfHx0ifhqUAAABsdFJOUwA/Skoauru8u8K7u40sLY8yMYeULoqRijIxjIiNkI42jTWQko86nDaeoJ08sLRKuLQ1Nj8yUL2twEc9Nzq6op6lqbbGn6K/wKymsqmExsO6foDKuMV+wchsm3qmF8l6l2ZWC7oWtrpSCLq2ugo9naEAAAClSURBVBjTY2DAANGsbOzsHEDAzs7G6gIUCI9Ny8rm4ubmykxNDnIGCniFxKczMjEzMzEmRvqaAwUsvYMTMkB6kyL8HYxBDCuPsLgUBgaWUD9HI4ixNp6cUTEsgZx2hjCLrF3dA3zc7E0QVptaONma6SG5RVvXQF9HHUlAVUNLU00RSUBeSUVZQRJJQEJORlaKD0lAQFxaVEgESUCQX0yYh5cBKwAASvgW88X18swAAAAASUVORK5CYII=',
  24. };
  25.  
  26. // 多行字符串
  27. var funcStr = function (f) {
  28. var s = f.toString().split(/\r\n|\r|\n/g).slice(1, -1).join('\n');
  29. return s;
  30. };
  31.  
  32. // 快速创建一段文档元素
  33. var cewih = function (tag, inner) {
  34. var d = document.createElement(tag);
  35. d.innerHTML = inner;
  36. return d;
  37. };
  38.  
  39. // 文本常量
  40. // 请以简体中文为原文,参考这些资料翻译
  41. // http://zh.wikipedia.org/wiki/Template:CGroup/IT
  42. // http://www.microsoft.com/Language/zh-cn/Search.aspx
  43. var text = {
  44. // 基本按钮
  45. 'okButtonTitle': { 'zh-cn': '确定', 'zh-hk': '確定', 'zh-tw': '確定', 'en': 'Confirm' },
  46. 'cancelButtonTitle': { 'zh-cn': '取消', 'zh-hk': '取消', 'zh-tw': '取消', 'en': 'Cancel' },
  47. 'closeButtonTitle': { 'zh-cn': '关闭', 'zh-hk': '關閉', 'zh-tw': '關閉', 'en': 'Close' },
  48. 'configStringsAdd': { 'zh-cn': '添加', 'zh-hk': '新增', 'zh-tw': '新增', 'en': 'Add' },
  49. 'configUsersAdd': { 'zh-cn': '添加', 'zh-hk': '新增', 'zh-tw': '新增', 'en': 'Add' },
  50. 'foldedWeiboText': {
  51. 'zh-cn': '"来自 @" attr(yawf-author) " 的一条微博被折叠,请点击查看"',
  52. 'zh-hk': '"來自 @" attr(yawf-author) " 的一條微博被折疊,請點擊查看"',
  53. 'zh-tw': '"來自 @" attr(yawf-author) " 的一條微博被折疊,請點擊查看"',
  54. 'en': '"A Weibo from @" attr(yawf-author) " was folded, click to view."'
  55. },
  56. 'disabledKey': { 'zh-cn': '(已禁用)', 'zh-hk': '(已停用)', 'zh-tw': '(已停用)', 'en': '(Disabled)' },
  57. // 设置框
  58. 'filter': { 'zh-cn': '过滤器', 'zh-hk': '篩選器', 'zh-tw': '篩選器', 'en': 'Filter' },
  59. 'configDialogTitle': { 'zh-cn': '过滤器设置', 'zh-hk': '篩選器設定', 'zh-tw': '篩選器設定', 'en': 'Filter Settings' },
  60. 'whitelistFilterDesc': { 'zh-cn': '总是显示{{{typed}}}', 'zh-hk': '總是顯示{{{typed}}}', 'zh-tw': '總是顯示{{{typed}}}', 'en': 'Always show {{{typed}}}' },
  61. 'blacklistFilterDesc': { 'zh-cn': '隐藏{{{typed}}}', 'zh-hk': '隱藏{{{typed}}}', 'zh-tw': '隱藏{{{typed}}}', 'en': 'Hide {{{typed}}}' },
  62. 'foldlistFilterDesc': { 'zh-cn': '折叠{{{typed}}}', 'zh-hk': '折疊{{{typed}}}', 'zh-tw': '折疊{{{typed}}}', 'en': 'Fold {{{typed}}}' },
  63. 'whitelistActionDesc': { 'zh-cn': '显示', 'zh-hk': '顯示', 'zh-tw': '顯示', 'en': 'Show' },
  64. 'blacklistActionDesc': { 'zh-cn': '隐藏', 'zh-hk': '隱藏', 'zh-tw': '隱藏', 'en': 'Hide' },
  65. 'foldlistActionDesc': { 'zh-cn': '折叠', 'zh-hk': '折疊', 'zh-tw': '折疊', 'en': 'Fold' },
  66. // 关键词
  67. 'keywordFilterGroupTitle': { 'zh-cn': '内容', 'zh-hk': '內容', 'zh-tw': '內容', 'en': 'Content' },
  68. 'keywordFilterDesc': { 'zh-cn': '关键词', 'zh-hk': '關鍵字', 'zh-tw': '關鍵字', 'en': 'Keyword' },
  69. 'keywordFilterDetails': { 'zh-cn': '包含以下关键词的微博', 'zh-hk': '包含以下關鍵字的微博', 'zh-tw': '包含以下關鍵字的微博', 'en': 'Weibo with these keywords' },
  70. 'keywordFilterFast': { 'zh-cn': '包含关键词“{{text}}”的微博', 'zh-hk': '包含關鍵字「{{text}}」的微博', 'zh-tw': '包含關鍵字「{{text}}」的微博', 'en': 'Weibo contains keyword "{{text}}"' },
  71. // 正则表达式
  72. 'regexpFilterGroupTitle': { 'zh-cn': '正则', 'zh-hk': '正則', 'zh-tw': '正規', 'en': 'Regexp' },
  73. 'regexpFilterDesc': { 'zh-cn': '正则式', 'zh-hk': '正則式', 'zh-tw': '正規式', 'en': 'Regexp' },
  74. 'regexpFilterDetails': { 'zh-cn': '匹配以下正则表达式的微博', 'zh-hk': '匹配以下正則表達式的微博', 'zh-tw': '匹配以下正規表示式的微博', 'en': 'Weibo matches these regular expressions' },
  75. 'regexpFilterRemark': {
  76. 'zh-cn': '正则表达式的过滤方式提供了基于微博内容的高级过滤方式,如果只是需要简单地关键词过滤请使用内容标签页的关键词以获得更好的效率。正则表达式书写时不需要对“/”字符转义。',
  77. 'zh-hk': '正則表達式的過濾方式提供了基於微博內容的高級過濾方式,如果只是需要簡單地關鍵字過濾請使用內容標籤頁的關鍵字以獲得更好的效率。正則表達式書寫時不需要對「/」字符轉義。',
  78. 'zh-tw': '正規表示式的過濾方式提供了基於微博內容的高級過濾方式,如果只是需要簡單地關鍵字過濾請使用內容標籤頁的關鍵字以獲得更好的效率。正規表示式書寫時不需要對「/」字符轉義。',
  79. 'en': 'Regular expression provide advanced filter settings based on content. You may set keywords in Content tab for better performance. You do not need to escape "/" in your regexp.'
  80. },
  81. 'regexpBadFormedTitle': { 'zh-cn': '非法的正则表达式', 'zh-hk': '不合法的正則表達式', 'zh-tw': '不合法的正規表示式', 'en': 'Illegal Regexp' },
  82. 'regexpBadFormed': {
  83. 'zh-cn': '您输入的/{{regexp}}/不能被正确地解析为正则表达式,请检查您的输入。如需关键词屏蔽请到内容标签页设置。',
  84. 'zh-hk': '您输入的/{{regexp}}/不能被正确地解析为正则表达式,请检查您的输入。如需关键词屏蔽请到内容标签页面设置。',
  85. 'zh-tw': '您輸入的/{{regexp}}/不能被正確地解析為正規表示式,請檢查您的輸入。如需關鍵詞屏蔽請到內容標籤頁面設置。',
  86. 'en': 'Cannot parse /{{regexp}}/ as regexp. Please check your input. You may hide Weibo by keywords in Content tab page.'
  87. },
  88. // 帐号
  89. 'accountFilterGroupTitle': { 'zh-cn': '帐号', 'zh-hk': '帳號', 'zh-tw': '帳號', 'en': 'Account' },
  90. 'accountFilterDesc': { 'zh-cn': '帐号', 'zh-hk': '帳號', 'zh-tw': '帳號', 'en': 'Account' },
  91. 'accountFilterDetails': { 'zh-cn': '来自以下帐号的微博', 'zh-hk': '來自以下帳號的微博', 'zh-tw': '來自以下帳號的微博', 'en': 'Weibo from these accounts' },
  92. 'accountFilterFast': { 'zh-cn': '作者是“@{{name}}”的微博', 'zh-hk': '作者是「@{{name}}」的微博', 'zh-tw': '作者是「@{{name}}」的微博', 'en': 'Weibo from "@{{name}}"', },
  93. 'accountFilterRemark': {
  94. 'zh-cn': '推荐您到<a target="_blank" href="http://account.weibo.com/set/privacy#open=privacy_feeduser">隐私设置 - 屏蔽账号</a>屏蔽您关注了但不想在首页看到的账号。',
  95. 'zh-hk': '推薦您到<a target="_blank" href="http://account.weibo.com/set/privacy#open=privacy_feeduser">隱私設置 - 屏蔽帐号</a>封鎖您關注了但不想在首頁看到的帳號。',
  96. 'zh-tw': '推薦您到<a target="_blank" href="http://account.weibo.com/set/privacy#open=privacy_feeduser">隱私設置 - 屏蔽帐号</a>封鎖您關注了但不想在首頁看到的帳號。',
  97. 'en': 'You can block Weibo from accounts you followed in the page <a target="_blank" href="http://account.weibo.com/set/privacy#open=privacy_feeduser">Privacy - Block account</a>.'
  98. },
  99. 'accountNotExistErrorTitle': { 'zh-cn': '帐号不存在', 'zh-hk': '帳號不存在', 'zh-tw': '帳號不存在', 'en': 'Account does not exist' },
  100. 'accountNotExistError': { 'zh-cn': '不存在名为{{name}}的账号', 'zh-hk': '不存在名為{{name}}的賬號', 'zh-tw': '不存在名為{{name}}的賬號', 'en': 'Account named {{name}} does not exist' },
  101. // 原创
  102. 'originalFilterGroupTitle': { 'zh-cn': '原创', 'zh-hk': '原創', 'zh-tw': '原創', 'en': 'Original' },
  103. 'originalFilterDesc': { 'zh-cn': '帐号', 'zh-hk': '帳號', 'zh-tw': '帳號', 'en': 'Account' },
  104. 'originalFilterDetails': { 'zh-cn': '转发自以下账号的微博', 'zh-hk': '隱藏轉發自以下帳號的微博', 'zh-tw': '隱藏轉發自以下帳號的微博', 'en': 'Hide Weibo forwarded from these accounts' },
  105. 'originalFilterFast': { 'zh-cn': '转发自“@{{name}}”的微博', 'zh-hk': '轉發自「@{{name}}」的微博', 'zh-tw': '轉發自「@{{name}}」的微博', 'en': 'Weibo forwarded from "@{{name}}"' },
  106. // 提到
  107. 'mentionFilterGroupTitle': { 'zh-cn': '提到', 'zh-hk': '提到', 'zh-tw': '提到', 'en': 'Mention' },
  108. 'mentionFilterDesc': { 'zh-cn': '帐号', 'zh-hk': '帳號', 'zh-tw': '帳號', 'en': 'Account' },
  109. 'mentionFilterDetails': { 'zh-cn': '提到以下账号的微博', 'zh-hk': '提到以下帳號的微博', 'zh-tw': '提到以下帳號的微博', 'en': 'Weibo mentioned these accounts' },
  110. 'mentionFilterFast': { 'zh-cn': '提到了“@{{name}}”的微博', 'zh-hk': '提到了「@{{name}}」的微博', 'zh-tw': '提到了「@{{name}}」的微博', 'en': 'Weibo mentioned "@{{name}}"' },
  111. // 话题
  112. 'topicFilterGroupTitle': { 'zh-cn': '话题', 'zh-hk': '話題', 'zh-tw': '話題', 'en': 'Topic' },
  113. 'topicFilterDesc': { 'zh-cn': '话题', 'zh-hk': '話題', 'zh-tw': '話題', 'en': 'Topic' },
  114. 'topicFilterDetails': { 'zh-cn': '包含以下话题的微博', 'zh-hk': '包含以下話題的微博', 'zh-tw': '包含以下話題的微博', 'en': 'Weibo with these topics' },
  115. 'topicFilterFast': { 'zh-cn': '包含“#{{topic}}#”话题的微博', 'zh-hk': '包含「#{{topic}}#」話題的微博', 'zh-tw': '包含「#{{topic}}#」話題的微博', 'en': 'Weibo contains topic "#{{topic}}#"' },
  116. // 来源
  117. 'sourceFilterGroupTitle': { 'zh-cn': '来源', 'zh-hk': '來源', 'zh-tw': '來源', 'en': 'Source' },
  118. 'sourceFilterDesc': { 'zh-cn': '来自', 'zh-hk': '來自', 'zh-tw': '來自', 'en': 'Via' },
  119. 'sourceFilterDetails': { 'zh-cn': '以下来源的微博', 'zh-hk': '以下來源的微博', 'zh-tw': '以下來源的微博', 'en': 'Weibo from these sources' },
  120. 'sourceFilterFast': { 'zh-cn': '来自“{{source}}”的微博', 'zh-hk': '來自「{{source}}」的微博', 'zh-tw': '來自「{{source}}」的微博', 'en': 'Weibo via "{{source}}"' },
  121. 'sourceFilterWarningTitle': { 'zh-cn': '默认来源', 'zh-hk': '預設來源', 'zh-tw': '預設來源', 'en': 'Default Source' },
  122. 'sourceFilterWarning': { 'zh-cn': '不能添加默认来源', 'zh-hk': '不能新增預設來源', 'zh-tw': '不能新增預設來源', 'en': 'You cannot add default source' },
  123. // 超链接
  124. 'hyperlinkFilterGroupTitle': { 'zh-cn': '链接', 'zh-hk': '連結', 'zh-tw': '連結', 'en': 'Link' },
  125. 'hyperlinkFilterDesc': { 'zh-cn': '超链接', 'zh-hk': '超連結', 'zh-tw': '超連結', 'en': 'Hyperlink' },
  126. 'hyperlinkFilterDetails': { 'zh-cn': '包含指向以下网站的超链接的微博', 'zh-hk': '包含指向以下站點的超連結的微博', 'zh-tw': '包含指向以下站點的超連結的微博', 'en': 'Weibo with hyperlink to these website' },
  127. 'hyperlinkFilterFast': { 'zh-cn': '包含指向包含“{{host}}”地址链接的微博', 'zh-hk': '包含指向包含「{{host}}」位址連結的微博', 'zh-tw': '包含指向包含「{{host}}」位址連結的微博', 'en': 'Weibo contains hyperlink to "{{host}}"' },
  128. // 更多
  129. 'otherFilterGroupTitle': { 'zh-cn': '更多', 'zh-hk': '其他', 'zh-tw': '其他', 'en': 'More' },
  130. // 显示
  131. 'otherWhitelistTitle': { 'zh-cn': '显示以下内容(不计入白名单)', 'zh-hk': '顯示以下內容(不計入白名單)', 'zh-tw': '顯示以下內容(不計入白名單)', 'en': 'Show following content (not regard as whitelist)' },
  132. 'showMyWeiboDesc': { 'zh-cn': '自己的微博', 'zh-hk': '自己的微博', 'zh-tw': '自己的微博', 'en': 'Weibo by myself' },
  133. 'showMyOriginalDesc': { 'zh-cn': '自己微博的转发', 'zh-hk': '自己微博的轉發', 'zh-tw': '自己微博的轉發', 'en': 'Forward of my Weibo' },
  134. 'showMentionMeDesc': { 'zh-cn': '提到自己的微博', 'zh-hk': '提到自己的微博', 'zh-tw': '提到自己的微博', 'en': 'Weibo mentioned me' },
  135. // 隐藏
  136. 'otherBlacklistTitle': { 'zh-cn': '隐藏以下内容', 'zh-hk': '以下內容', 'zh-tw': '隱藏以下內容', 'en': 'Hide following content' },
  137. 'adfeedFilterDesc': { 'zh-cn': '推广微博', 'zh-hk': '推廣微博', 'zh-tw': '推廣微博', 'en': 'Ad Weibo' },
  138. 'recommandFeedDesc': { 'zh-cn': '推荐微博', 'zh-hk': '建議微博', 'zh-tw': '建議微博', 'en': 'Recommand Weibo' },
  139. 'fakeWeiboFilterDesc': { 'zh-cn': '混入微博列表的其它内容', 'zh-hk': '混入微博列表的其它內容', 'zh-tw': '混入微博列表的其它內容', 'en': 'Other contents in Weibo list' },
  140. 'deletedForwardFilterDesc': { 'zh-cn': '已删除微博的转发', 'zh-hk': '已刪除微博的轉發', 'zh-tw': '已刪除微博的轉發', 'en': 'Forward of deleted Weibo' },
  141. 'voteWeiboFilterDesc': { 'zh-cn': '投票微博', 'zh-hk': '投票微博', 'zh-tw': '投票微博', 'en': 'Vote weibo' },
  142. 'taobaoTianmaoWeibo': { 'zh-cn': '带有淘宝或天猫商品的微博', 'zh-hk': '帶有淘寶或天貓商品的微博', 'zh-tw': '帶有淘寶或天貓商品的微博', 'en': 'Weibo with Taobao / Tmall commodity' },
  143. // 刷屏与版聊
  144. 'otherSpammingTitle': { 'zh-cn': '刷屏与版聊', 'zh-hk': '洗版與版聊', 'zh-tw': '洗版與版聊', 'en': 'Spamming &amp; Chating' },
  145. 'sameAccountFilterDesc': { 'zh-cn': '相同作者的微博:|超过{{<number>}}条|时{{<action>}}', 'zh-hk': '相同作者的微博:|超過{{<number>}}條|時{{<action>}}', 'zh-tw': '相同作者的微博:|超過{{<number>}}條|時{{<action>}}', 'en': 'Weibo from same account: |{{<action>}} the part | which exceeds {{<number>}} Weibo' },
  146. 'sameForwardFilterDesc': { 'zh-cn': '相同微博的转发:|超过{{<number>}}条|时{{<action>}}', 'zh-hk': '相同微博的轉發:|超過{{<number>}}條|時{{<action>}}', 'zh-tw': '相同微博的轉發:|超過{{<number>}}條|時{{<action>}}', 'en': 'Forward from same Weibo: |{{<action>}} the part | which exceeds {{<number>}} Weibo' },
  147. // 分组浏览
  148. 'otherGroupTitle': { 'zh-cn': '分组浏览', 'zh-hk': '分組流覽', 'zh-tw': '分組流覽', 'en': 'Browse by Group' },
  149. 'accountByGroup': { 'zh-cn': '分组浏览时禁用按账号隐藏', 'zh-hk': '分組流覽時禁用按帳號隱藏', 'zh-tw': '分組流覽時禁用按帳號隱藏', 'en': 'Disable hide by account filter when browsing by group' },
  150. 'sameAccountByGroup': { 'zh-cn': '浏览分组时禁用相同作者数量限制', 'zh-hk': '流覽分組時禁用相同作者數量限制', 'zh-tw': '流覽分組時禁用相同作者數量限制', 'en': 'Disable hide too many Weibo from same account filter when browsing by group' },
  151. // 模块
  152. 'layoutFilterGroupTitle': { 'zh-cn': '模块', 'zh-hk': '模組', 'zh-tw': '模組', 'en': 'Module' },
  153. 'layoutFilterGroupDesc': { 'zh-cn': '隐藏以下模块', 'zh-hk': '隱藏以下模組', 'zh-tw': '隱藏以下模組', 'en': 'Hide following modules' },
  154. // 标识图标
  155. 'layoutHideIcon': { 'zh-cn': '标识/图标', 'zh-hk': '標誌/圖示', 'zh-tw': '標誌/圖示', 'en': 'Logo / Icon' },
  156. 'layoutHideIconLevel': { 'zh-cn': '等级', 'zh-hk': 'Level', 'zh-tw': '等級', 'en': 'Level' },
  157. 'layoutHideIconMember': { 'zh-cn': '微博会员', 'zh-hk': '微博會員', 'zh-tw': '微博會員', 'en': 'Weibo VIP / Member' },
  158. 'layoutHideIconApprove': { 'zh-cn': '个人认证', 'zh-hk': '個人認證', 'zh-tw': '個人認證', 'en': 'Personal Authentication / 個人認證' },
  159. 'layoutHideIconApproveCo': { 'zh-cn': '机构认证', 'zh-hk': '企業認證', 'zh-tw': '企業認證', 'en': 'Weibo Verification / 企業認證' },
  160. 'layoutHideIconClub': { 'zh-cn': '微博达人', 'zh-hk': '微博達人', 'zh-tw': '微博達人', 'en': 'Pioneer' },
  161. 'layoutHideIconVGirl': { 'zh-cn': '微博女郎', 'zh-hk': '微博女郎', 'zh-tw': '微博女郎', 'en': 'Weibo girl' },
  162. 'layoutHideIconTaobao': { 'zh-cn': '淘宝商户', 'zh-hk': '淘寶商戶', 'zh-tw': '淘寶商戶', 'en': 'Taobao Merchant' },
  163. 'layoutHideIconZongyika': { 'zh-cn': '我是综艺咖', 'zh-hk': '我是综艺咖'/* as is */, 'zh-tw': '我是综艺咖', 'en': '我是综艺咖 (Variety Wack)' },
  164. 'layoutHideIconYouji': { 'zh-cn': '邂逅有机', 'zh-hk': '邂逅有机'/* as is */, 'zh-tw': '邂逅有机', 'en': '邂逅有机 (Travel Notes)' },
  165. // 导航栏
  166. 'layoutHideNav': { 'zh-cn': '导航栏', 'zh-hk': '導覽列', 'zh-tw': '導覽列', 'en': 'Navigation Bar' },
  167. 'layoutHideNavMain': { 'zh-cn': '首页', 'zh-hk': '首頁', 'zh-tw': '首頁', 'en': 'Home' },
  168. 'layoutHideNavHot': { 'zh-cn': '热门', 'zh-hk': '熱門', 'zh-tw': '熱門', 'en': 'Hot' },
  169. 'layoutHideNavApp': { 'zh-cn': '应用', 'zh-hk': '應用', 'zh-tw': '應用', 'en': 'Apps' },
  170. 'layoutHideNavGame': { 'zh-cn': '游戏', 'zh-hk': '遊戲', 'zh-tw': '遊戲', 'en': 'Game' },
  171. 'layoutHideNavMember': { 'zh-cn': '会员菜单', 'zh-hk': '會員功能表', 'zh-tw': '會員功能表', 'en': 'VIP Menu' },
  172. // 左栏
  173. 'layoutHideLeft': { 'zh-cn': '左栏', 'zh-hk': '左欄', 'zh-tw': '左欄', 'en': 'Left Column' },
  174. 'layoutHideLeftToMe': { 'zh-cn': '发给我的', 'zh-hk': '發給我的', 'zh-tw': '發給我的', 'en': 'Send to me' },
  175. 'layoutHideLeftFriends': { 'zh-cn': '好友圈', 'zh-hk': '好友圈', 'zh-tw': '好友圈', 'en': 'Friends' },
  176. 'layoutHideLeftApp': { 'zh-cn': '应用', 'zh-hk': '应用', 'zh-tw': '应用'/* as is */, 'en': 'Apps' },
  177. // 中栏
  178. 'layoutHideMiddle': { 'zh-cn': '中栏', 'zh-hk': '中欄', 'zh-tw': '中欄', 'en': 'Middle Column' },
  179. 'layoutHideMiddleRecommendedTopic': { 'zh-cn': '热门微博(发布框上方)', 'zh-hk': '热门微博(發布框上方)', 'zh-tw': '热门微博(發布框上方)'/* as is */, 'en': '热门微博 (Hot Weibo), top of publisher' },
  180. 'layoutHideMiddleFeedRecommand': { 'zh-cn': '微博兴趣推荐(顶部)', 'zh-hk': '微博興趣推薦(頂部)', 'zh-tw': '微博興趣推薦(頂部)', 'en': 'Feed Recommand, top' },
  181. 'layoutHideMiddleMemberTip': { 'zh-cn': '开通会员提示(底部)', 'zh-hk': '開通會員提示(底部)', 'zh-tw': '開通會員提示(底部)', 'en': 'Tip of Join Weibo VIP, bottom' },
  182. // 右栏
  183. 'layoutHideRight': { 'zh-cn': '右栏', 'zh-hk': '右欄', 'zh-tw': '右欄', 'en': 'Right Column' },
  184. 'layoutHideRightTemplate': { 'zh-cn': '设置模板', 'zh-hk': '背景設定', 'zh-tw': '背景設定', 'en': 'Template Settings' },
  185. 'layoutHideRightInfo': { 'zh-cn': '头像', 'zh-hk': '頭像', 'zh-tw': '頭像', 'en': 'Avatar' },
  186. 'layoutHideRightTrial': { 'zh-cn': '登录赢会员', 'zh-hk': '登录赢会员', 'zh-tw': '登录赢会员'/* as is */, 'en': '登录赢会员 (Login for VIP Trial)' },
  187. 'layoutHideRightAtten': { 'zh-cn': '关注/粉丝/微博数', 'zh-hk': '關注/粉絲/微博數', 'zh-tw': '關注/粉絲/微博數', 'en': 'Numbers of Following/Followers/Weibo' },
  188. 'layoutHideRightInterest': { 'zh-cn': '可能感兴趣的人', 'zh-hk': '可能感興趣的人', 'zh-tw': '可能感興趣的人', 'en': 'You may know' },
  189. 'layoutHideRightHotTopic': { 'zh-cn': '热门话题', 'zh-hk': '熱門話題', 'zh-tw': '熱門話題', 'en': 'Hot Topic' },
  190. 'layoutHideRightMember': { 'zh-cn': '会员专区', 'zh-hk': '會員專區', 'zh-tw': '會員專區', 'en': 'Weibo VIP' },
  191. 'layoutHideRightWeibo': { 'zh-cn': '热门微博', 'zh-hk': '熱門微博', 'zh-tw': '熱門微博', 'en': 'Hot Weibo' },
  192. 'layoutHideRightLocation': { 'zh-cn': '地点推荐', 'zh-hk': '地點推薦', 'zh-tw': '地點推薦', 'en': 'Location' },
  193. 'layoutHideRightMusic': { 'zh-cn': ' 热门歌曲', 'zh-hk': '熱門歌曲', 'zh-tw': '熱門歌曲', 'en': 'Hot Music' },
  194. 'layoutHideRightMovie': { 'zh-cn': '最新电影', 'zh-hk': '最新電影', 'zh-tw': '最新電影', 'en': 'Hot Movie' },
  195. 'layoutHideRightBook': { 'zh-cn': '人气图书', 'zh-hk': '人氣圖書', 'zh-tw': '人氣圖書', 'en': 'Hot Book' },
  196. 'layoutHideRightNotice': { 'zh-cn': '公告栏', 'zh-hk': '公告欄', 'zh-tw': '公告欄', 'en': 'Bulletin Board' },
  197. // 微博内
  198. 'layoutHideWeibo': { 'zh-cn': '微博内', 'zh-hk': '微博內', 'zh-tw': '微博內', 'en': 'In Weibo' },
  199. 'layoutHideWeiboRecomFeed': { 'zh-cn': '精彩微博推荐', 'zh-hk': '精彩微博推薦', 'zh-tw': '精彩微博推薦', 'en': '精彩微博推荐 (Weibo you may interested in)' },
  200. 'layoutHideWeiboTopicCard': { 'zh-cn': '话题卡片', 'zh-hk': '話題卡片', 'zh-tw': '話題卡片', 'en': 'Topic Cards' },
  201. 'layoutHideWeiboFeedTip': { 'zh-cn': '评论框提示横幅', 'zh-hk': '評論框提示橫幅', 'zh-tw': '評論框提示橫幅', 'en': 'Tips for Comment' },
  202. 'layoutHideWeiboSonTitle': { 'zh-cn': '同源转发合并提示', 'zh-hk': '同源转发合并提示', 'zh-tw': '同源转发合并提示', 'en': '同源转发合并 (Merge forwards from same origin)' },
  203. 'layoutHideWeiboLocationCard': { 'zh-cn': '位置卡片', 'zh-hk': '位置卡片', 'zh-tw': '位置卡片', 'en': 'Location Cards' },
  204. 'layoutHideWeiboSource': { 'zh-cn': '来源', 'zh-hk': '來源', 'zh-tw': '來源', 'en': 'Source' },
  205. 'layoutHideWeiboReport': { 'zh-cn': '举报', 'zh-hk': '檢舉', 'zh-tw': '檢舉', 'en': 'Report' },
  206. 'layoutHideWeiboLike': { 'zh-cn': '赞', 'zh-hk': '讚', 'zh-tw': '讚', 'en': 'Like' },
  207. 'layoutHideWeiboForward': { 'zh-cn': '转发', 'zh-hk': '轉發', 'zh-tw': '轉發', 'en': 'Forward' },
  208. 'layoutHideWeiboFavourite': { 'zh-cn': '收藏', 'zh-hk': '收藏', 'zh-tw': '收藏', 'en': 'Favourite' },
  209. 'layoutHideWeiboBlockBySource': { 'zh-cn': '屏蔽来源', 'zh-hk': '屏蔽來源', 'zh-tw': '屏蔽來源', 'en': 'Block Source' },
  210. 'layoutHideWeiboBlockByKeyword': { 'zh-cn': '屏蔽关键词', 'zh-hk': '屏蔽關鍵詞', 'zh-tw': '屏蔽關鍵詞', 'en': 'Block Keywords' },
  211. // 个人主页
  212. 'layoutHidePerson': { 'zh-cn': '个人主页', 'zh-hk': '個人主頁', 'zh-tw': '個人主頁', 'en': 'Ones home page' },
  213. 'layoutHidePersonCover': { 'zh-cn': '封面图', 'zh-hk': '封面圖', 'zh-tw': '封面圖', 'en': 'Cover Picture' },
  214. 'layoutHidePersonTemplate': { 'zh-cn': '模板设置', 'zh-hk': '模板設置', 'zh-tw': '模板設置', 'en': 'Template Settings' },
  215. 'layoutHidePersonBadgeIcon': { 'zh-cn': '勋章', 'zh-hk': '勳章', 'zh-tw': '勳章', 'en': 'Badges' },
  216. 'layoutHidePersonStats': { 'zh-cn': '关注/粉丝/微博数', 'zh-hk': '關注/粉絲/微博數', 'zh-tw': '關注/粉絲/微博數', 'en': 'Numbers of Following/Followers/Weibo' },
  217. 'layoutHidePersonMyData': { 'zh-cn': '我的微博人气', 'zh-hk': '我的微博人气', 'zh-tw': '我的微博人气', 'en': '我的微博人气 (My Micro World)' },
  218. 'layoutHidePersonSuggestUser': { 'zh-cn': '可能感兴趣的人', 'zh-hk': '可能感兴趣的人', 'zh-tw': '可能感兴趣的人'/* as is */, 'en': 'Suggested' },
  219. 'layoutHidePersonGroup': { 'zh-cn': '推荐的人', 'zh-hk': '推荐的人', 'zh-tw': '推荐的人'/* as is */, 'en': '推荐的人 (Suggested Group)' },
  220. 'layoutHidePersonRelation': { 'zh-cn': '微关系', 'zh-hk': '微关系', 'zh-tw': '微关系'/* as is */, 'en': 'Relationship' },
  221. 'layoutHidePersonAlbum': { 'zh-cn': '图片', 'zh-hk': '相冊', 'zh-tw': '相冊', 'en': 'Album' },
  222. 'layoutHidePersonHotTopic': { 'zh-cn': '话题', 'zh-hk': '話題', 'zh-tw': '話題', 'en': 'Topic' },
  223. 'layoutHidePersonHotWeibo': { 'zh-cn': '热门微博', 'zh-hk': '熱門微博', 'zh-tw': '熱門微博', 'en': 'Hot Weibo' },
  224. // 杂项
  225. 'layoutHideOther': { 'zh-cn': '杂项', 'zh-hk': '雜項', 'zh-tw': '雜項', 'en': 'Others' },
  226. 'layoutHideOtherAds': { 'zh-cn': '广告', 'zh-hk': '廣告', 'zh-tw': '廣告', 'en': 'Advertisement' },
  227. 'layoutHideOtherFeedRecom': { 'zh-cn': '相关微博推荐', 'zh-hk': '相关推荐', 'zh-tw': '相关推荐', 'en': '相关推荐 (Related Weibo Suggestion)' },
  228. 'layoutHideOtherFooter': { 'zh-cn': '页面底部', 'zh-hk': '頁面底部', 'zh-tw': '頁面底部', 'en': 'Footer' },
  229. 'layoutHideOtherWbIm': { 'zh-cn': '微博桌面推荐(右下)', 'zh-hk': '微博桌面推薦(右下)', 'zh-tw': '微博桌面推薦(右下)', 'en': '微博桌面2014 (Desktop Weibo), bottom right' },
  230. 'layoutHideOtherTip': { 'zh-cn': '功能提示框', 'zh-hk': '功能提示框', 'zh-tw': '功能提示框', 'en': 'Function Tips' },
  231. // 工具
  232. 'toolFilterGroupTitle': { 'zh-cn': '工具', 'zh-hk': '工具', 'zh-tw': '工具', 'en': 'Tool' },
  233. // 边栏
  234. 'sideColumnToolsTitle': { 'zh-cn': '边栏', 'zh-hk': '邊欄', 'zh-tw': '邊欄', 'en': 'Side Column' },
  235. 'showAllGroupDesc': { 'zh-cn': '展开左栏分组', 'zh-hk': '展開左欄分組', 'zh-tw': '展開左欄分組', 'en': 'Unfold groups in left column' },
  236. 'showAllMsgNavDesc': { 'zh-cn': '展开左栏消息', 'zh-hk': '展開左欄消息', 'zh-tw': '展開左欄消息', 'en': 'Unfold news in left column' },
  237. 'mergeLeftRight': { 'zh-cn': '合并左右边栏|到{{<side>}}', 'zh-hk': '合併左右邊欄|到{{<side>}}', 'zh-tw': '合併左右邊欄|到{{<side>}}', 'en': 'Merge left &amp; right column | to {{<side>}}' },
  238. 'mergeLeftRightLeft': { 'zh-cn': '左侧', 'zh-hk': '左側', 'zh-tw': '左側', 'en': 'left side' },
  239. 'mergeLeftRightRight': { 'zh-cn': '右侧', 'zh-hk': '右側', 'zh-tw': '右側', 'en': 'right side' },
  240. 'fixedLeft': { 'zh-cn': '浮动左边栏|{{<items>}}', 'zh-hk': '浮動左邊欄|{{<items>}}', 'zh-tw': '浮動左邊欄|{{<items>}}', 'en': 'Float left column | {{<items>}}' },
  241. 'fixedLeftDefault': { 'zh-cn': '默认元素', 'zh-hk': '預設元素', 'zh-tw': '預設元素', 'en': 'default elements' },
  242. 'fixedLeftWhole': { 'zh-cn': '整个左栏', 'zh-hk': '整個左欄', 'zh-tw': '整個左欄', 'en': 'whole column' },
  243. // 微博
  244. 'weiboToolsTitle': { 'zh-cn': '微博', 'zh-hk': '微博', 'zh-tw': '微博', 'en': 'Weibo' },
  245. 'clearDefTopicDesc': { 'zh-cn': '清除发布框中的默认话题', 'zh-hk': '清除發布框中的預設話題', 'zh-tw': '清除發布框中的預設話題', 'en': 'Remove default topic in Publisher' },
  246. 'unwrapTextDesc': { 'zh-cn': '微博作者和正文同行', 'zh-hk': '微博作者和正文同行', 'zh-tw': '微博作者和正文同行', 'en': 'No line break after author' },
  247. 'personalRedirectWeibo': { 'zh-cn': '访问账号主页显示微博页面', 'zh-hk': '訪問帳號主頁顯示微博頁面', 'zh-tw': '訪問帳號主頁顯示微博頁面', 'en': 'Show Weibo page instead of personal mainpage by default' },
  248. 'viewOriginalDesc': { 'zh-cn': '添加“查看原图”链接', 'zh-hk': '添加「查看原圖」連結', 'zh-tw': '添加「查看原圖」連結', 'en': 'add "Original Picture" link' },
  249. 'viewOriginalText': { 'zh-cn': '查看原图', 'zh-hk': '查看原圖', 'zh-tw': '查看原圖', 'en': 'Original Picture' },
  250. // 脚本
  251. 'scriptToolsTitle': { 'zh-cn': '脚本', 'zh-hk': '腳本', 'zh-tw': '腳本', 'en': 'Script' },
  252. 'useFastCreator': { 'zh-cn': '使用拖放快速创建过滤器', 'zh-hk': '使用拖放快速創建篩選器', 'zh-tw': '使用拖放快速創建篩選器', 'en': 'Use drag and drop to create filters' },
  253. 'blockHiddenWeiboDesc': { 'zh-cn': '告知服务器被隐藏的微博以避免再次加载', 'zh-hk': '告知伺服器被隱藏的微博以避免再次載入', 'zh-tw': '告知伺服器被隱藏的微博以避免再次載入', 'en': 'Send blocked Weibo to server to avoid load it again' },
  254. // 样式
  255. 'styleToolsTitle': { 'zh-cn': '外观', 'zh-hk': '外觀', 'zh-tw': '外觀', 'en': 'Appearance' },
  256. 'userstyleEditDesc': { 'zh-cn': '编辑微博自定义CSS', 'zh-hk': '編輯微博自訂CSS', 'zh-tw': '編輯微博自訂CSS', 'en': 'Edit Weibo Customize CSS' },
  257. 'userstyleEditDetails': { 'zh-cn': 'YAWF CSS: ', 'zh-hk': 'YAWF CSS: ', 'zh-tw': 'YAWF CSS: ', 'en': 'YAWF CSS: ' },
  258. 'whitelistHighlightDesc': { 'zh-cn': '高亮显示白名单的微博|背景色{{<color>}}|透明度{{<transparency>}}%', 'zh-hk': '高亮顯示白名單的微博|背景色{{<color>}}|透明度{{<transparency>}}%', 'zh-tw': '高亮顯示白名單的微博|背景色{{<color>}}|透明度{{<transparency>}}%', 'en': 'Highlight Weibo in whitelist with | background color {{<color>}} | transparency {{<transparency>}}%' },
  259. 'mainBackgroundColorOverride': { 'zh-cn': '首页背景|颜色{{<color>}}|透明度{{<transparency>}}%', 'zh-hk': '首頁背景|色彩{{<color>}}|透明度{{<transparency>}}%', 'zh-tw': '首頁背景|色彩{{<color>}}|透明度{{<transparency>}}%', 'en': 'Background color for home page | {{<color>}} | transparency {{<transparency>}}%' },
  260. 'profileBackgroundColorOverride': { 'zh-cn': '个人主页背景|颜色{{<color>}}|透明度{{<transparency>}}%', 'zh-hk': '個人主頁背景|色彩{{<color>}}|透明度{{<transparency>}}%', 'zh-tw': '個人主頁背景|色彩{{<color>}}|透明度{{<transparency>}}%', 'en': 'Background color for personal home page | {{<color>}} | transparency {{<transparency>}}%' },
  261. 'weiboOnly': {
  262. 'zh-cn': '阅读视图|宽度{{<width>}}px|快捷键{{<key>}}||背景颜色{{<color>}}|透明度{{<transparency>}}%||{{<switch>}}在微博列表顶部显示快捷开关按钮',
  263. 'zh-hk': '閱讀視圖|寬度{{<width>}}px|快速鍵{{<key>}}||背景色彩{{<color>}}|透明度{{<transparency>}}%||{{<switch>}}在微博清單頂部顯示快速開關按鈕',
  264. 'zh-tw': '閱讀視圖|寬度{{<width>}}px|快速鍵{{<key>}}||背景色彩{{<color>}}|透明度{{<transparency>}}%||{{<switch>}}在微博清單頂部顯示快速開關按鈕',
  265. 'en': 'Reading View | width {{<width>}}px | shortcut {{<key>}} || backgroundcolor {{<color>}} | transparency {{<transparency>}} || {{<switch>}} show switch button at top of Weibo list'
  266. },
  267. 'weiboOnlyButton': { 'zh-cn': '切换视图', 'zh-hk': '切換視圖', 'zh-tw': '切換視圖', 'en': 'Switch View' },
  268. 'userstyleTitle': {
  269. 'zh-cn': '<span>自定义CSS<a class="yawf-userstyles-tip" href="https://userstyles.org/styles/browse/weibo" target="_blank">在 userstyles.org 上搜索样式</a></span>{{}}',
  270. 'zh-hk': '<span>自訂CSS<a class="yawf-userstyles-tip" href="https://userstyles.org/styles/browse/weibo" target="_blank">在 userstyles.org 上搜尋樣式</a></span>{{}}',
  271. 'zh-tw': '<span>自訂CSS<a class="yawf-userstyles-tip" href="https://userstyles.org/styles/browse/weibo" target="_blank">在 userstyles.org 上搜尋樣式</a></span>{{}}',
  272. 'en': '<span>Customize CSS<a class="yawf-userstyles-tip" href="https://userstyles.org/styles/browse/weibo" target="_blank">Search styles on userstyles.org</a></span>{{}}'
  273. },
  274. // 脚本
  275. 'scriptFilterGroupTitle': { 'zh-cn': '脚本', 'zh-hk': '腳本', 'zh-tw': '腳本', 'en': 'Script' },
  276. // 导入导出
  277. 'configImportAndExport': { 'zh-cn': '设置', 'zh-hk': '設定', 'zh-tw': '設定', 'en': 'Setting' },
  278. 'configImportButton': { 'zh-cn': '导入', 'zh-hk': '匯入', 'zh-tw': '匯入', 'en': 'Import' },
  279. 'configImportWarningTitle': { 'zh-cn': '设置导入', 'zh-hk': '設定匯入', 'zh-tw': '設定匯入', 'en': 'Setting Import' },
  280. 'configImportWarning': {
  281. 'zh-cn': '导入的设置会覆盖您当前已有的设置,确实要导入设置吗?',
  282. 'zh-hk': '匯入的設定會覆蓋您當前已有的設定,您確定要匯入設定嗎?',
  283. 'zh-tw': '匯入的設定會覆蓋您當前已有的設定,您確定要匯入設定嗎?',
  284. 'en': 'The imported settings may replace your current settings. Are you sure you want to import this file?'
  285. },
  286. 'configImportSuccessTitle': { 'zh-cn': '设置导入完成', 'zh-hk': '設定匯入完成', 'zh-tw': '設定匯入完成', 'en': 'Import settings' },
  287. 'configImportSuccess': { 'zh-cn': '已经成功地导入了设置', 'zh-hk': '已经成功地匯入了設定', 'zh-tw': '已经成功地匯入了設定', 'en': 'Successfully imported settings' },
  288. 'configImportFailTitle': { 'zh-cn': '设置导入失败', 'zh-hk': '設定匯入失败', 'zh-tw': '設定匯入失败', 'en': 'Import settings' },
  289. 'configImportFail': {
  290. 'zh-cn': '导入设置文件时出现错误,可能是使用了错误的文件,文件已损坏或文件的版本不支持',
  291. 'zh-hk': '匯入設定檔案時出現錯誤,可能是使用了錯誤的檔案,檔案已損壞或為不支援的版本',
  292. 'zh-tw': '匯入設定檔案時出現錯誤,可能是使用了錯誤的檔案,檔案已損壞或為不支援的版本',
  293. 'en': 'Error occurred during importing process. Wrong file may be used, the file may be broken, or the version of setting file is not supported.'
  294. },
  295. 'configExportButton': { 'zh-cn': '导出', 'zh-hk': '匯出', 'zh-tw': '匯出', 'en': 'Export' },
  296. 'configResetButton': { 'zh-cn': '重置', 'zh-hk': '重設', 'zh-tw': '重設', 'en': 'Reset' },
  297. 'configResetWarningTitle': { 'zh-cn': '设置重置', 'zh-hk': '設定重設', 'zh-tw': '設定重設', 'en': 'Setting Reset' },
  298. 'configResetWarning': { 'zh-cn': '这将会清空您当前的所有配置,确实要重置设置吗?', 'zh-hk': '這將會清空您當前的所有設定,您確定要重置設定嗎?', 'zh-tw': '這將會清空您當前的所有設定,您確定要重置設定嗎?', 'en': 'You are deleting all your settings. Are you sure you want to reset your settings?' },
  299. // 调试
  300. 'scriptDebugTitle': { 'zh-cn': '调试', 'zh-hk': '偵錯', 'zh-tw': '偵錯', 'en': 'Debug' },
  301. 'scriptDebug': { 'zh-cn': '在控制台打印调试信息', 'zh-hk': '將偵錯訊息列印到主控台', 'zh-tw': '將偵錯訊息列印到主控台', 'en': 'Print debug info to console' },
  302. // 关于
  303. 'scriptAboutTitle': { 'zh-cn': '关于', 'zh-hk': '關於', 'zh-tw': '關於', 'en': 'About' },
  304. 'scriptAbout': {
  305. 'zh-cn': '<p>Yet Another Weibo Filter (YAWF) {{version}}</p><p>YAWF 使用 MIT 协议授权。您可以访问<a target="_blank" href="https://tiansh.github.io/yawf/">脚本主页</a>获取详细信息。<br />如果您在使用过程中遇到任何脚本的错误,或对脚本有任何建议,您可以到<a target="_blank" href="https://github.com/tiansh/yawf/issues">反馈页面</a>提供报告,或直接<a target="_blank" href="http://weibo.com/tsh90/weibo">私信作者</a>。</p><p>本脚本参考并使用了<a target="_blank" href="https://code.google.com/p/weibo-content-filter/">眼不见心不烦</a>脚本的部分代码。</p>',
  306. 'zh-hk': '<p>Yet Another Weibo Filter (YAWF) {{version}}</p><p>YAWF 使用 MIT 協定授權。您可以訪問<a target="_blank" href="https://tiansh.github.io/yawf/">腳本主頁</a>獲取詳細資訊。<br />如果您在使用過程中遇到任何腳本的錯誤,或對腳本有任何建議,您可以到<a target="_blank" href="https://github.com/tiansh/yawf/issues">回饋頁面</a>提供報告,或直接<a target="_blank" href="http://weibo.com/tsh90/weibo">私信作者</a>。</p><p>本腳本參考並使用了<a target="_blank" href="https://code.google.com/p/weibo-content-filter/">眼不見心不煩</a>腳本的部分原始碼。</p>',
  307. 'zh-tw': '<p>Yet Another Weibo Filter (YAWF) {{version}}</p><p>YAWF 使用 MIT 協定授權。您可以訪問<a target="_blank" href="https://tiansh.github.io/yawf/">腳本主頁</a>獲取詳細資訊。<br />如果您在使用過程中遇到任何腳本的錯誤,或對腳本有任何建議,您可以到<a target="_blank" href="https://github.com/tiansh/yawf/issues">回饋頁面</a>提供報告,或直接<a target="_blank" href="http://weibo.com/tsh90/weibo">私信作者</a>。</p><p>本腳本參考並使用了<a target="_blank" href="https://code.google.com/p/weibo-content-filter/">眼不見心不煩</a>腳本的部分原始碼。</p>',
  308. 'en': '<p>Yet Another Weibo Filter (YAWF) {{version}}</p><p>YAWF is under the MIT License. You may want to visit <a target="_blank" href="https://tiansh.github.io/yawf/">project homepage</a> for more information.<br />If you find any bugs or have feature request, please report them in the <a target="_blank" href="https://github.com/tiansh/yawf/issues">feedback page</a>, or <a target="_blank" href="http://weibo.com/tsh90/weibo">send message to author</a>. </p><p>Some codes of this script come from <a target="_blank" href="https://code.google.com/p/weibo-content-filter/"><span lang="zh-cn">眼不见心不烦</span> (Weibo Content Filter)</a> script.</p>',
  309. },
  310. // 拖拽
  311. 'dropAreaTitle': { 'zh-cn': '拖放至此<br />快速创建过滤器', 'zh-hk': '拖放至此<br />快速創建篩選器', 'zh-tw': '拖放至此<br />快速創建篩選器', 'en': 'Drop Here to Create Filter' },
  312. 'dropAreaText': { 'zh-cn': '将文字或链接拖放至此,快速创建过滤器。', 'zh-hk': '將文字或連結拖放至此,快速創建篩選器。', 'zh-tw': '將文字或連結拖放至此,快速創建篩選器。', 'en': 'Drop text or link here to create filter.' },
  313. 'fastCreateChoseTitle': { 'zh-cn': '创建过滤器', 'zh-hk': '創建篩篩器', 'zh-tw': '創建篩篩器', 'en': 'Create Filter' },
  314. 'fastFilterChoseText': { 'zh-cn': '请选择要创建的过滤器:', 'zh-hk': '請選擇要創建的篩選器:', 'zh-tw': '請選擇要創建的篩選器:', 'en': 'Chose the filter(s) you want:' },
  315. };
  316.  
  317. // 页面常量
  318. var html = {
  319. '|': '</label><label>', // 先关闭前面的label,再从后面开一个,所以这里没写反
  320. '||': '</label><br /><label>',
  321. 'select': '<select>{{options}}</select>',
  322. 'option': '<option value="{{value}}">{{{text}}}</option>',
  323. // 对话框
  324. 'cover': '<div node-type="outer" style="position: fixed; top: 0px; left: 0px; width: 100%; height: 100%; background: #000; opacity: 0.3; z-index: 10001;"></div>',
  325. 'dialog': '<div style="position: absolute; z-index: 10001;" node-type="outer" class="W_layer yawf-Layer" id="{{id}}"><div class="bg"><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td><div node-type="layoutContent" class="content"><div node-type="title" class="title"><span node-type="title_content">{{title}}</span></div><a node-type="close" title="{{closeButtonTitle}}" class="W_close" href="javascript:void(0);"></a><div node-type="inner"></div></div></td></tr></tbody></table></div></div>',
  326. 'alert': '<div style="position: absolute; z-index: 10001;" node-type="outer" class="W_layer yawf-Layer" id="{{id}}"><div class="bg"><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td><div node-type="layoutContent" class="content"><div node-type="title" class="title" style=""><span node-type="title_content">{{title}}</span></div><a node-type="close" title="{{closeButtonTitle}}" class="W_close" href="javascript:void(0);"></a><div node-type="inner"><div class="layer_point" node-type="outer"><dl class="point clearfix"><dt><span node-type="icon" class="icon_{{icon}}M"></span></dt><dd node-type="inner"><p node-type="textLarge" class="S_txt1">{{text}}</p><p node-type="textSmall" class="S_txt2"></p></dd></dl><div class="btn"><a node-type="OK" class="W_btn_a" href="javascript:void(0)"><span class="btn_30px W_f14">{{okButtonTitle}}</span></a></div></div></div></div></td></tr></tbody></table></div></div>',
  327. 'confirm': '<div style="position: absolute; z-index: 10001;" node-type="outer" class="W_layer yawf-Layer" id="{{id}}"><div class="bg"><table cellspacing="0" cellpadding="0" border="0"><tbody><tr><td><div node-type="layoutContent" class="content"><div node-type="title" class="title" style=""><span node-type="title_content">{{title}}</span></div><a node-type="close" title="{{closeButtonTitle}}" class="W_close" href="javascript:void(0);"></a><div node-type="inner"><div class="layer_point" node-type="outer"><dl class="point clearfix"><dt><span node-type="icon" class="icon_{{icon}}M"></span></dt><dd node-type="inner"><p node-type="textLarge" class="S_txt1">{{text}}</p><p node-type="textComplex" class="S_txt2" style="display: none;"></p><p node-type="textSmall" class="S_txt2" style="display: none;"></p></dd></dl><div class="btn"><a node-type="OK" class="W_btn_a" href="javascript:void(0)"><span class="btn_30px W_f14">{{okButtonTitle}}</span></a><a node-type="cancel" class="W_btn_b" href="javascript:void(0)"><span class="btn_30px W_f14">{{cancelButtonTitle}}</span></a></div></div></div></div></td></tr></tbody></table></div></div>',
  328. // 漏斗图标
  329. 'icon': '<div class="gn_setting" node-type="filter"><i><a class="gn_tab gn_filter" href="#"><span class="ico">{{filter}}</span></a></i></div>',
  330. // 设置窗口
  331. 'configHeaderTop': '<div class="profile_tab S_line5 yawf-config-header" node-type="yawf-config-header"><ul class="pftb_ul S_line1">',
  332. 'configHeaderItem': '<li class="pftb_itm S_line1 {{liclass}}"><a class="pftb_lk S_line5 S_txt1 {{aclass}}" action-type="tab_item" onclick="return false;" href="javascript:void(0);">{{name}}</a>',
  333. 'configHeaderBottom': '</ul></div>',
  334. 'configLayerTop': '<div node-type="yawf-config-body" class="yawf-config-body">',
  335. 'configLayerItem': '<div class="{{name}} yawf-config-layer" node-type="{{name}}" style="display: none;"></div>',
  336. 'configLayerBottom': '</div>',
  337. 'configFooter': '',
  338. // 设置窗口内文字
  339. 'configSubtitle': '<div class="yawf-groupSubtitle">{{{text}}}</div>',
  340. 'configText': '<div class="yawf-groupText">{{{text}}}</div>',
  341. 'configRemark': '<div class="yawf-groupRemark">{{{text}}}</div>',
  342. // 设置项
  343. 'configBoolean': '<div class="yawf-configBoolean yawf-configItem"><label>{{text}}</label></div>',
  344. 'configBooleanInput': '<div class="yawf-configInput yawf-configBooleanInput"><input id="yawf-{{key}}" class="W_checkbox yawf-configBooleanInput" type="checkbox" name="yawf-{{key}}"></div>',
  345. 'configSelect': '<div class="yawf-configSelect yawf-configItem"><label>{{text}}</label></div>',
  346. 'configSelectInput': '<div class="yawf-configInput yawf-configSelectInput"><select class="yawf-configSelectSelect" name="yawf-{{key}}"></select></div>',
  347. 'configString': '<div class="yawf-configString yawf-configItem"><label>{{text}}</label></div>',
  348. 'configStringInput': '<div class="yawf-configInput yawf-configStringInput"><textarea id="yawf-{{key}}" class="W_input yawf-configStringInput" name="yawf-{{key}}"></textarea></div>',
  349. 'configColor': '<div class="yawf-configColor yawf-configItem"><label>{{text}}</label></div>',
  350. 'configColorInput': '<div class="yawf-configInput yawf-configColorInput"><input type="color" class="W_f14" style="width: 40px;" /></div>',
  351. 'configNumber': '<div class="yawf-configNumber yawf-configItem"><label>{{text}}</label></div>',
  352. 'configNumberInput': '<div class="yawf-configInput yawf-configNumberInput"><input type="number" class="W_f14" style="width: 60px;" /></div>',
  353. 'configRange': '<div class="yawf-configRange yawf-configItem"><label>{{text}}</label></div>',
  354. 'configRangeInput': '<div class="yawf-configInput yawf-configRangeInput"><input type="number" min="0" max="100" maxlength="3" class="W_f14" style="width: 45px; text-align: right;" /><div class="yawf-range-container"><input type="range" style="height: 1em; width: 66px; margin-left: 7px; margin-right: 7px; " tabindex="-1" /></div></div>',
  355. 'configKey': '<div class="yawf-configKey yawf-configItem"><label>{{text}}</label></div>',
  356. 'configKeyInput': '<div class="yawf-configInput yawf-configKeyInput"><button class="W_f14 yawf-configKeyName"></button><input type="hidden" /></div>',
  357. 'configStrings': '<div class="yawf-configStrings yawf-configItem"><form action="#"><label><span class="yawf-configDesc yawf-configStringsDesc">{{{text}}}</span><input id="yawf-{{key}}" class="W_input yawf-configStringsInput" type="text" name="yawf-{{key}}"></label><button id="yawf-add-{{key}}" class="W_btn_a yawf-configAdd" type="submit"><span>{{configStringsAdd}}</span></button></form><ul class="yawf-configStringsItems"></ul></div>',
  358. 'configStringsItem': '<li class="W_btn_arrow tag yawf-configStringsItem"><span>{{[item]}}<a class="W_ico12 icon_close" href="javascript:void(0);"></a></span></li>',
  359. 'configUsers': '<div class="yawf-configUsers yawf-configItem"><form action="#"><label><span class="yawf-configDesc yawf-configUsersDesc">{{{text}}}</span><input id="yawf-{{key}}" class="W_input yawf-configUsersInput" type="text" name="yawf-{{key}}"></label><button id="yawf-add-{{key}}" class="W_btn_a yawf-configAdd" type="submit"><span>{{configUsersAdd}}</span></button></form><ul class="yawf-configUsersItems"></ul></div>',
  360. 'configUsersItem': '<li class="yawf-configUsersItem"><div class="shield_object_card"><div class="card_bg clearfix"><div class="card_pic"><span class="pic"><img class="W_face_radius" width="50" height="50" alt="" src="{{avatar}}"></span></div><div class="card_content"><div class="object_info clearfix"><p class="W_fl"><span class="object_name" uid="{{id}}" title="{{name}}">{{name}}</span></p><p class="W_fr"><a class="W_ico12 icon_close" action-data="uid={{id}}" href="javascript:void(0);"></a></p></div><div class="other_info"></div></div></div></div></li>',
  361. 'configPrefill': '<span class="yawf-configPrefill" id="{{id}}"></span>',
  362. // 导入导出
  363. 'configImportExport': '<div class="yawf-configImportExport yawf-configItem"><label><input type="file" style=" width: 1px; height: 1px; margin: 0 -1px 0 0; opacity: 0;" /><span node-type="import" class="W_btn_b" action-type="import"><span class="W_f14">{{configImportButton}}</span></span></label><a node-type="export" class="W_btn_b" action-type="export" href="javascript:;"><span class="W_f14">{{configExportButton}}</span></a><a node-type="reset" class="W_btn_b" action-type="reset" href="javascript:;"><span class="W_f14">{{configResetButton}}</span></a></div>',
  364. // 查看原图
  365. 'viewOriginalLink': '<a target="_blank" class="show_big" suda-data="key=tblog_newimage_feed&value=view_original" action-type="maximum" href="javascript:;"><em class="W_ico12 ico_showbig"></em>{{viewOriginalText}}</a><i class="W_vline">|</i>',
  366. // 拖拽
  367. 'dropArea': '<div id="yawf-drop-area" class="display: none;"><div class="yawf-drop-area-desc"><div class="yawf-drop-area-title">{{dropAreaTitle}}</div><div class="yawf-drop-area-text">{{dropAreaText}}</div></div><div contenteditable="true" id="yawf-drop-area-content"></div></div>',
  368. 'fastFilterHeader': '<div id="yawf-fast-filter-chose"><div class="yawf-fast-filter-option"><span class="yawf-fast-filter-text">{{fastFilterChoseText}}</span><ul id="yawf-fast-filter-chose">',
  369. 'fastFilterItem': '<li class="yawf-fast-filter-item"><label><input class="W_checkbox yawf-configBooleanInput" type="checkbox"><span>{{inner}}</span></label><select value="blacklist"><option value="whitelist">{{whitelistActionDesc}}</option><option value="blacklist">{{blacklistActionDesc}}</option><option value="foldlist">{{foldlistActionDesc}}</option></select></li>',
  370. 'fastFilterFooter': '</ul></div><div class="btn clearfix"><a node-type="ok" class="W_btn_a" action-type="ok" href="javascript:;"><span class="btn_30px W_f14">{{okButtonTitle}}</span></a><a node-type="cancel" class="W_btn_b" action-type="cancel" href="javascript:;"><span class="btn_30px W_f14">{{cancelButtonTitle}}</span></a></div></div>',
  371. // 只看微博列表
  372. 'weiboOnlyButton': '<div class="right_item"><div><a class="W_btn_round2" href="javascript:void(0);" title="{{text}}{{shortcut}}"><span>{{text}}</span></a></div></div></div>',
  373. // 分组或特别关注的未读提示
  374. 'noticeContainer': '<div class="WB_feed_type SW_fun S_line2" action-type="feed_list_item" yawf-display="notice"></div>',
  375. };
  376.  
  377. var url = {
  378. 'newcard': '/aj/user/newcard?type=1&{{query}}&_t=1&callback={{callback}}',
  379. 'view_ori': 'http://photo.weibo.com/{{uid}}/wbphotos/large/mid/{{mid}}/pid/{{pid}}',
  380. 'block_wb': '/aj/user/block?_wv=5&__rnd={{rnd}}',
  381. };
  382.  
  383. // 将按键编号或将显示编号对应名称
  384. var keys = (function () {
  385. var ctrl = 1 << 8, shift = 1 << 9, alt = 1 << 10, meta = 1 << 11, key = ctrl - 1;
  386. var namelist = '#0;#1;#2;Cancel;#4;#5;Help;#7;BackSpace;TAB;#10;#11;Clear;Enter;EnterSpecial;#15;;;;Pause;CapsLock;Kana;Eisu;Junja;Final;Hanja;#26;Esc;Convert;Nonconvert;Accept;ModeChange;Space;PageUp;PageDown;End;Home;Left;Up;Right;Down;Select;Print;Execute;PrintScreen;Insert;Delete;#47;0;1;2;3;4;5;6;7;8;9;Colon;Semicolon;LessThan;Equals;GreaterThan;QuestionMark;At;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;W;X;Y;Z;Win;#92;ContextMenu;#94;Sleep;NumPad0;NumPad1;NumPad2;NumPad3;NumPad4;NumPad5;NumPad6;NumPad7;NumPad8;NumPad9;Multiply;Add;Separator;Subtract;Decimal;Divide;F1;F2;F3;F4;F5;F6;F7;F8;F9;F10;F11;F12;F13;F14;F15;F16;F17;F18;F19;F20;F21;F22;F23;F24;#136;#137;#138;#139;#140;#141;#142;#143;NumLock;ScrollLocK;WIN_OEM_FJ_JISHO;WIN_OEM_FJ_MASSHOU;WIN_OEM_FJ_TOUROKU;WIN_OEM_FJ_LOYA;WIN_OEM_FJ_ROYA;#151;#152;#153;#154;#155;#156;#157;#158;#159;Circumflex;Exclamation;DoubleQuote;Hash;Dollar;Percent;Ampersand;Underscore;OpenParen;CloseParen;Asterisk;Plus;Pipe;HyphenMinus;OpenCurlyBracket;CloseCurlyBracket;Tilde;#177;#178;#179;#180;VolumeMute;VolumeDown;VolumeUp;#184;#185;#186;#187;Comma;#189;Period;Slash;BackQuote;#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;OpenBracket;BackSlash;CloseBracket;Quote;#223;;AltGr;#226;WIN_ICO_HELP;WIN_ICO_00;#229;WIN_ICO_CLEAR;#231;#232;WIN_OEM_RESET;WIN_OEM_JUMP;WIN_OEM_PA1;WIN_OEM_PA2;WIN_OEM_PA3;WIN_OEM_WSCTRL;WIN_OEM_CUSEL;WIN_OEM_ATTN;WIN_OEM_FINISH;WIN_OEM_COPY;WIN_OEM_AUTO;WIN_OEM_ENLW;WIN_OEM_BACKTAB;Attn;Crsel;Exsel;Ereof;Play;Zoom;#252;PA1;WIN_OEM_CLEAR;#255'.split(';');
  387. // 对一个按键事件做编号
  388. var get = function (e) {
  389. var code = e.keyCode & key;
  390. if (e.ctrlKey) code |= ctrl;
  391. if (e.shiftKey) code |= shift;
  392. if (e.altKey) code |= alt;
  393. if (e.metaKey) code |= meta;
  394. return code;
  395. };
  396. // 给一个编号,转换为键名
  397. var name = function (n) {
  398. if (n === 0) return text.disabledKey;
  399. var ret = '';
  400. if (n & ctrl) ret += 'Ctrl-';
  401. if (n & shift) ret += 'Shift-';
  402. if (n & alt) ret += 'Alt-';
  403. if (n & meta) ret += 'Meta-';
  404. ret += namelist[n & key];
  405. if (ret.slice(-1) === '-') ret = ret.slice(0, -1);
  406. return ret;
  407. };
  408. // 注册全局监听按键
  409. var triggers = {};
  410. var reg = function (key, callback) {
  411. triggers[key] = triggers[key] || [];
  412. triggers[key].push(withTry(callback));
  413. };
  414. // 监听按键
  415. var baseEvent = function (e) {
  416. var code = get(e);
  417. if (!triggers[code]) return [];
  418. e.stopPropagation(); e.preventDefault();
  419. return triggers[code];
  420. };
  421. document.addEventListener('keydown', function (e) {
  422. baseEvent(e).forEach(function (f) { f(); })
  423. });
  424. document.addEventListener('keyup', baseEvent);
  425. return {
  426. 'get': get,
  427. 'name': name,
  428. 'reg': reg,
  429. };
  430. }());
  431.  
  432.  
  433. // 根据用户界面上的语言做不同调整
  434. var i18n = (function () {
  435. var defaultLang = 'zh-cn';
  436. var lang = null;
  437. var pending = [];
  438. var chose = function (langObj) {
  439. langObj.local = langObj[lang] || langObj[defaultLang];
  440. };
  441. return function (l) {
  442. lang = l;
  443. pending.map(chose);
  444. pending = [];
  445. i18n = chose;
  446. i18n.lang = l;
  447. };
  448. }());
  449.  
  450. // 将字符串用&#dd的形式转义
  451. var escapeXml = function (s) {
  452. return s.replace(/./g, function (c) { return '&#' + c.charCodeAt(0); });
  453. };
  454.  
  455. // 以参数填充字符串
  456. var fillStr = function (base, func) {
  457. var argdatas = Array.apply(Array, arguments).slice(1);
  458. var datas = argdatas.concat([text]);
  459. var parseFunction;
  460. if (typeof func === 'function') parseFunction = func;
  461. else parseFunction = function (text) {
  462. var ret = null;
  463. datas.some(function (data) {
  464. if (typeof data === 'object' && text in data) ret = '' + data[text];
  465. return ret !== null;
  466. });
  467. return ret;
  468. };
  469. return base.replace(/{{([\[{]?([a-zA-Z0-9_-]*)[\]}]?)}}/g, function (o, i, p) {
  470. var ret = parseFunction(p);
  471. if (ret == null) return o;
  472. if (i[0] === '{') return ret = fillStr(ret, parseFunction);
  473. if (i[0] === '[') return escapeXml(ret);
  474. return ret;
  475. });
  476. };
  477.  
  478. // 设置项
  479. var config = function (uid) {
  480. var config = {}, keys = [], onputs = [], storageKey = 'user' + uid + 'config';
  481. var tonputs = function (key, value, oldValue) {
  482. onputs.map(function (f) { f(key, value, oldValue); });
  483. };
  484. // 读取到内存
  485. var read = function () {
  486. try { config = JSON.parse(GM_getValue(storageKey, '{}')); }
  487. catch (e) { config = {}; }
  488. };
  489. // 从内存写出
  490. var write = function () {
  491. GM_setValue(storageKey, JSON.stringify(config));
  492. };
  493. // 写入到内存
  494. var put = function (key, value) {
  495. if (keys.indexOf(key) === -1) return;
  496. tonputs(key, value, config[key]);
  497. config[key] = value;
  498. write();
  499. return value;
  500. };
  501. // 从内存读取
  502. var get = function (key, value, type) {
  503. read();
  504. if (!(key in config)) return value;
  505. var val = config[key];
  506. if (typeof val === 'undefined') return value;
  507. if (type && (val === null || val.constructor !== type)) return value;
  508. return val;
  509. };
  510. // 当内存配置被修改时调用
  511. var onput = function (f) {
  512. onputs.push(f);
  513. };
  514. // 从字串导入
  515. var import_ = function (s) {
  516. try {
  517. clear();
  518. s = JSON.parse(s).conf;
  519. Object.keys(s).forEach(function (key) {
  520. put(key, s[key]);
  521. });
  522. write();
  523. return true;
  524. } catch (e) { }
  525. return false;
  526. };
  527. // 导出成为字串
  528. var export_ = function () {
  529. var info = GM_info || {}, script = info.script || {};
  530. var conf = {};
  531. Object.keys(config)
  532. .filter(function (x) { return x.indexOf('._') === -1; })
  533. .forEach(function (x) { conf[x] = config[x]; })
  534. return JSON.stringify({
  535. 'ua': navigator.userAgent,
  536. 'yawf': script.name,
  537. 'ver': script.version,
  538. 'gm': (info.scriptHandler || '') + info.version,
  539. 'conf': conf,
  540. }, null, 2);
  541. };
  542. // 清空设置
  543. var clear = function () {
  544. config = {};
  545. tonputs();
  546. write();
  547. };
  548. // 注册键
  549. var reg = function (key) { keys.push(key); };
  550. // 初始化
  551. return {
  552. 'uid': uid,
  553. 'put': put, 'get': get, 'onput': onput,
  554. 'read': read, 'write': write,
  555. 'import': import_, 'export': export_,
  556. 'clear': clear,
  557. 'reg': reg,
  558. };
  559. };
  560.  
  561.  
  562. // 微博过滤规则
  563. var rules = (function () {
  564. var list = [];
  565. var add = function (priority, rule) {
  566. list.push({ 'priority': priority, 'rule': rule });
  567. list.sort(function (x, y) { return y.priority - x.priority; });
  568. };
  569. var parse = function (feed) {
  570. var result = null;
  571. list.some(function (item) {
  572. try { result = item.rule(feed) || result; }
  573. catch (e) { debug('error while parsing rule %o: %o', item.rule, e); }
  574. if (result) debug('%o(%o) -> %s', item.rule, feed, result);
  575. return result;
  576. });
  577. return result;
  578. };
  579. return {
  580. 'add': add,
  581. 'parse': parse,
  582. };
  583. }());
  584.  
  585. var debug = GM_getValue('debug', false) &&
  586. console && console.log && console.log.bind(console) ||
  587. function () { };
  588.  
  589. // 显示右上角过滤器图标
  590. var showIcon = function () {
  591. var p = document.querySelector('.WB_global_nav .gn_person');
  592. if (!p) return setTimeout(showIcon, 100);
  593. var d = cewih('div', html.icon).firstChild;
  594. p.appendChild(d);
  595. var f = document.querySelector('.gn_filter');
  596. f.addEventListener('click', function (e) {
  597. filters.showDialog();
  598. e.preventDefault();
  599. });
  600. };
  601.  
  602. // 对话框
  603. var Form = function (dom, display, details) {
  604. var ok = dom.querySelector('[node-type="OK"]');
  605. var cancel = dom.querySelector('[node-type="cancel"]');
  606. var close = dom.querySelector('[node-type="close"]');
  607. var title = dom.querySelector('.title');
  608. var mouse = null, pos;
  609. // 定位对话框的位置
  610. var setPos = function (pos) {
  611. var left = pos[0], top = pos[1];
  612. left = Math.min(Math.max(0, left), document.body.clientWidth - dom.clientWidth - 2);
  613. top = Math.min(Math.max(pageYOffset, top), pageYOffset + window.innerHeight - dom.clientHeight - 2);
  614. dom.style.left = left + 'px';
  615. dom.style.top = top + 'px';
  616. return [left, top];
  617. };
  618. // 开始拖拽
  619. var dragMoveStart = function (e) {
  620. mouse = [e.clientX, e.clientY];
  621. document.addEventListener('mousemove', dragMove);
  622. document.addEventListener('mouseup', dragMoveDone);
  623. dom.classList.add('yawf-drag');
  624. if (dom.setCapture) { dom.setCapture(); }
  625. };
  626. // 拖拽移动
  627. var dragMove = function (e) {
  628. var mouse_new = [e.clientX, e.clientY];
  629. pos[0] += mouse_new[0] - mouse[0];
  630. pos[1] += mouse_new[1] - mouse[1];
  631. setPos(pos);
  632. mouse = mouse_new;
  633. };
  634. // 拖拽结束
  635. var dragMoveDone = function () {
  636. document.removeEventListener('mousemove', dragMove);
  637. document.removeEventListener('mouseup', dragMoveDone);
  638. dom.classList.remove('yawf-drag');
  639. if (dom.releaseCapture) { dom.releaseCapture(); }
  640. pos = setPos(pos);
  641. mouse = null;
  642. };
  643. // 标题栏可以拖拽
  644. if (title) {
  645. title.style.cursor = 'move';
  646. title.addEventListener('mousedown', dragMoveStart);
  647. }
  648. // 确定取消等按钮
  649. if (details.onOk) if (ok) ok.addEventListener('click', details.onOk);
  650. if (details.onCancel) {
  651. if (cancel) cancel.addEventListener('click', details.onCancel);
  652. if (close) close.addEventListener('click', details.onCancel);
  653. }
  654. // 背景遮罩
  655. var cover = cewih('div', html.cover).firstChild;
  656. // 响应按键
  657. var keys = function (e) {
  658. if (e.keyCode === 13) if (ok) ok.click(); // Enter
  659. if (e.keyCode === 27) if (close) close.click(); // Esc
  660. };
  661. // 关闭对话框
  662. var hide = function () {
  663. document.body.removeChild(dom);
  664. document.body.removeChild(cover);
  665. document.removeEventListener('keypress', keys);
  666. };
  667. // 显示对话框
  668. var show = function (top, left) {
  669. document.body.appendChild(cover);
  670. document.body.appendChild(dom);
  671. if (top == null) top = (window.innerHeight - dom.clientHeight) / 2;
  672. if (left == null) left = (window.innerWidth - dom.clientWidth) / 2;
  673. pos = [left, top + pageYOffset];
  674. setPos(pos);
  675. document.addEventListener('keypress', keys);
  676. document.activeElement.blur();
  677. };
  678. if (display) show();
  679. if (ok) ok.addEventListener('click', hide);
  680. if (cancel) cancel.addEventListener('click', hide);
  681. if (close) close.addEventListener('click', hide);
  682. return { 'hide': hide, 'show': show };
  683. };
  684.  
  685. // 显示一个对话框
  686. var Dialog = function (id, title, fillFun) {
  687. var dom = cewih('div', fillStr(html.dialog, { 'id': id, 'title': fillStr(title) })).firstChild;
  688. var form = Form(dom, false, {});
  689. fillFun(dom.querySelector('[node-type="inner"]'));
  690. return form;
  691. };
  692.  
  693. // 显示一个提示框
  694. var Alert = function (id, details) {
  695. var dom = cewih('div', fillStr(html.alert, { 'id': id, 'title': details.title, 'text': details.text, 'icon': details.icon || 'warn' })).firstChild;
  696. var form = Form(dom, true, details);
  697. return form;
  698. };
  699.  
  700. // 显示一个确定框
  701. var Confirm = function (id, details) {
  702. var dom = cewih('div', fillStr(html.confirm, { 'id': id, 'title': details.title, 'text': details.text, 'icon': details.icon || 'question' })).firstChild;
  703. var form = Form(dom, true, details);
  704. return form;
  705. };
  706.  
  707. // 延迟调用函数
  708. var call = function (f) {
  709. setTimeout.bind(this, f, 0).apply(null, Array.apply(Array, arguments).slice(1));
  710. };
  711.  
  712. // 套上try-catch
  713. var withTry = function (f, fc) {
  714. return function () {
  715. try { f.apply(this, arguments); }
  716. catch (e) {
  717. debug('Exception while run %o: %o (%o)', f, e, e.stack);
  718. if (fc) fc(e);
  719. }
  720. };
  721. };
  722.  
  723. // 管理样式
  724. var css = (function () {
  725. var styleText = '';
  726. var fun = function (css) { return fun.add.bind(fun, css); };
  727. fun.init = function () { GM_addStyle(styleText); };
  728. fun.add = function (css) { styleText += css + '\n'; };
  729. return fun;
  730. }());
  731.  
  732. // 产生一个假的回调函数
  733. var dateStr = (function () {
  734. var last = 0;
  735. return function () {
  736. return '' + (last = Math.max(last + 1, Number(new Date())));
  737. };
  738. }());
  739.  
  740. // 维护账号信息,用于显示
  741. var account = (function () {
  742. var idCache = {}, nameCache = {}, working = {};
  743. var request = function (queryStr, onsucc, onerror) {
  744. // 如果同时已经有了同类请求则等待那个请求的返回
  745. if (working[queryStr]) return working[queryStr].push([onsucc, onerror]);
  746. working[queryStr] = [[onsucc, onerror]];
  747. var done = function (success, data) {
  748. working[queryStr].forEach(function (w) { w[success ? 0 : 1](data); });
  749. delete working[queryStr];
  750. };
  751. // 请求获取
  752. GM_xmlhttpRequest({
  753. 'method': 'GET',
  754. 'url': fillStr(url.newcard, { 'query': queryStr, 'callback': 'STK_' + dateStr() }),
  755. 'onload': withTry(function (resp) {
  756. var respJson = JSON.parse(resp.responseText.replace(/^try{[^{]*\(/, '').replace(/\)}catch\(e\){};$/, ''));
  757. var namecard = cewih('div', respJson.data);
  758. var avatar = namecard.querySelector('.name dt img').getAttribute('src');
  759. var name = namecard.querySelector('.name dd a[uid]').getAttribute('title');
  760. var uid = namecard.querySelector('.name dd a[uid]').getAttribute('uid');
  761. var data = { 'avatar': avatar, 'id': uid, 'name': name };
  762. nameCache[name] = idCache[uid] = data;
  763. done(true, data);
  764. }, done.bind(this, false)),
  765. 'onerror': function () { done(false); },
  766. });
  767. return queryStr;
  768. };
  769. var byId = function (id, onsucc, onerror) {
  770. if (idCache[id]) onsucc(idCache[id]);
  771. else request('id=' + id, onsucc, onerror);
  772. };
  773. var byName = function (name, onsucc, onerror) {
  774. if (nameCache[name]) onsucc(nameCache[name]);
  775. else request('name=' + encodeURIComponent(name), onsucc, onerror);
  776. };
  777. return { 'id': byId, 'name': byName };
  778. }());
  779.  
  780. // 过滤器管理器
  781. var filters = (function () {
  782. var list = [], preinit = true;
  783. // 添加到表格中
  784. var add = function (details) {
  785. list.push(details);
  786. if (!preinit) details.init();
  787. return details;
  788. };
  789. // 网页加载完毕时初始化
  790. var init = function () {
  791. list.forEach(function (x) { x.init(); });
  792. preinit = false;
  793. };
  794. var dialog = null;
  795. var lastTab = 0;
  796. var dialogTabs = function (list, inner, page) {
  797. var alist = Array.apply(Array, inner.querySelectorAll('.yawf-config-header a'));
  798. var llist = Array.apply(Array, inner.querySelectorAll('.yawf-config-body .yawf-config-layer'));
  799. var choseLList = function (i) {
  800. llist.forEach(function (l) { l.style.display = 'none'; l.innerHTML = ''; });
  801. alist.forEach(function (a) { a.classList.remove('current'); a.classList.remove('S_bg5'); a.classList.add('S_bg1'); });
  802. llist[i].innerHTML = ''; list[i].show(llist[i]); llist[i].style.display = 'block';
  803. alist[i].classList.add('current'); alist[i].classList.remove('S_bg1'); alist[i].classList.add('S_bg5');
  804. lastTab = i;
  805. };
  806. list.map(function (filter, i) {
  807. var a = alist[i];
  808. a.addEventListener('mousedown', function () { choseLList(i); });
  809. a.addEventListener('keydown', function () { choseLList(i); });
  810. });
  811. choseLList(page);
  812. };
  813. var showDialog = function (page, count) {
  814. var showDialogInner = function (inner) {
  815. inner.innerHTML = [html.configHeaderTop,
  816. list.map(function (filter, index) {
  817. return fillStr(html.configHeaderItem, {
  818. 'name': text[filter.name + 'Title'],
  819. 'aclass': index === 0 ? 'S_bg5 current' : 'S_bg1',
  820. 'liclass': index === list.length - 1 ? 'pftb_itm_lst' : ' ',
  821. });
  822. }).join(''),
  823. html.configHeaderBottom,
  824. html.configLayerTop,
  825. list.map(function (filter, index) {
  826. return fillStr(html.configLayerItem, {
  827. 'name': filter.name + 'Layer',
  828. });
  829. }).join(''),
  830. html.configLayerBottom,
  831. html.configFooter,
  832. ].join('');
  833. call(function () { dialog.show(0); });
  834. if (list.indexOf(page) === -1) dialogTabs(list, inner, lastTab);
  835. else dialogTabs(list, inner, list.indexOf(page));
  836. };
  837. if (!(dialog = Dialog('yawf-config', '{{configDialogTitle}}', showDialogInner))) {
  838. if (!count || count < 100) setTimeout(showDialog, 100, page, (count || 0) + 1);
  839. }
  840. };
  841. return {
  842. 'add': add,
  843. 'init': init,
  844. 'showDialog': showDialog,
  845. };
  846. }());
  847.  
  848. // 快速创建过滤器的对话框
  849. var fastFilterDialog = function (chose) {
  850. var dialogInner = function (inner) {
  851. inner.innerHTML = [html.fastFilterHeader,
  852. chose.map(function (c) {
  853. return fillStr(html.fastFilterItem, { 'inner': c.filter.desc(c.val) });
  854. }).join(''),
  855. html.fastFilterFooter].join('');
  856. var checkboxList = Array.apply(Array, inner.querySelectorAll('input[type="checkbox"]'));
  857. var selectList = Array.apply(Array, inner.querySelectorAll('select'));
  858. selectList.forEach(function (select) { select.value = 'blacklist'; });
  859. // 找到所有选择了的过滤器
  860. var allChecked = function () {
  861. var active = [];
  862. checkboxList.forEach(function (checkbox, i) {
  863. if (checkbox.checked)
  864. active.push({ 'chose': chose[i], 'action': selectList[i].value });
  865. });
  866. return active;
  867. };
  868. checkboxList.forEach(function (checkbox) { checkbox.checked = true; });
  869. var updateOkButton = function () {
  870. if (allChecked().length) ok.classList.remove('W_btn_a_disable');
  871. else ok.classList.add('W_btn_a_disable');
  872. };
  873. var ok = inner.querySelector('[action-type="ok"]');
  874. ok.addEventListener('click', function () {
  875. var active = allChecked();
  876. if (!active.length) return null;
  877. active.forEach(function (act) {
  878. act.chose.filter.add(act.chose.val, act.action);
  879. });
  880. dialog.hide();
  881. });
  882. checkboxList.forEach(function (checkbox) {
  883. checkbox.addEventListener('change', updateOkButton);
  884. });
  885. updateOkButton();
  886. var cancel = inner.querySelector('[action-type="cancel"]');
  887. cancel.addEventListener('click', function () { dialog.hide(); });
  888. };
  889. var dialog = Dialog('yawf-drop-select', '{{fastCreateChoseTitle}}', dialogInner);
  890. return dialog;
  891. };
  892.  
  893. // 将文本、链接等拖拽到框内,快速创建过滤器
  894. var dropdown = (function () {
  895. var dropArea, content;
  896. var dropdownFilters = [];
  897. var got = function (element) {
  898. if (!element) return;
  899. // 忽略最外面套的em/strong
  900. if (element.tagName && element.firstChild === element.lastChild &&
  901. ['strong', 'em'].indexOf(element.tagName.toLowerCase()) !== -1)
  902. element = element.firstChild;
  903. debug('got %o', element);
  904. var chose = [];
  905. // 问所有过滤器是不是需要
  906. dropdownFilters.forEach(function (filter, i) { chose[i] = null; });
  907. dropdownFilters.forEach(function (filter, i) {
  908. filter.valid(element, function (val) {
  909. if (!val) chose[i] = [];
  910. else if (val.constructor !== Array) chose[i] = [{ 'filter': filter, 'val': val }];
  911. else chose[i] = val.map(function (val) { return { 'filter': filter, 'val': val }; });
  912. if (chose.filter(function (x) { return !x; }).length) return;
  913. var chosen = chose.reduce(function (x, y) { return x.concat(y); });
  914. if (chosen.length) {
  915. var dialog = fastFilterDialog(chosen);
  916. call(function () { dialog.show(); });
  917. }
  918. });
  919. });
  920. };
  921. var init = function () {
  922. // 拖放到的框
  923. dropArea = cewih('div', html.dropArea).firstChild;
  924. var active = false, target = null;
  925. content = dropArea.querySelector('#yawf-drop-area-content');
  926. document.body.appendChild(dropArea);
  927. var hideDropArea = function () {
  928. if (target && target.hover) target.hover(); target = null;
  929. dropArea.style.display = 'none';
  930. };
  931. // 只接受从微博拽过来的东西
  932. var checkTarget = function () {
  933. var checker = target;
  934. for (; checker && checker !== document; checker = checker.parentNode) {
  935. if (!checker.classList || !checker.classList.contains || !checker.tagName) continue;
  936. if (checker.classList.contains('WB_feed')) return true;
  937. if (checker.classList.contains('W_miniblog')) return false;
  938. if (checker.tagName.toLowerCase() === 'body') return true;
  939. }
  940. return false;
  941. };
  942. // 开始拽
  943. document.addEventListener('dragstart', function (e) {
  944. var cover = document.querySelector('body>div[node-type="outer"]');
  945. if (cover && cover.clientHeight) return;
  946. target = e.explicitOriginalTarget; if (!target || !checkTarget()) return;
  947. active = true;
  948. dropArea.style.display = 'block';
  949. }, false);
  950. // 拽完了
  951. document.addEventListener('dragend', function (e) {
  952. if (!active) return; active = false;
  953. e.preventDefault(); e.stopPropagation();
  954. if (content.firstChild === content.lastChild) got(content.firstChild);
  955. else got(content);
  956. content.innerHTML = '';
  957. hideDropArea();
  958. }, false);
  959. // 拽出去了
  960. document.addEventListener('mouseout', function (e) {
  961. if (!active) return; active = false;
  962. hideDropArea();
  963. });
  964. };
  965. var add = function (details) {
  966. dropdownFilters.push(details);
  967. };
  968. return {
  969. 'init': init,
  970. 'add': add,
  971. };
  972. }());
  973.  
  974. // 告诉服务器屏蔽被隐藏的微博
  975. var blockWeibo = (function () {
  976. var buffer = [], busy = false;
  977. var delay = function () { return 3000 + Math.round(20 * Math.random()) * 100; };
  978. var block = function (mid, callback) {
  979. var done = function () { setTimeout(callback, delay()); };
  980. debug('blocking weibo %s', mid);
  981. GM_xmlhttpRequest({
  982. 'method': 'POST',
  983. 'url': fillStr(url.block_wb, { 'rnd': dateStr() }),
  984. 'headers': {
  985. 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  986. 'Cache-Control': 'no-cache',
  987. 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
  988. 'Cookie': document.cookie,
  989. 'Referer': location.href,
  990. 'X-Requested-With': 'XMLHttpRequest',
  991. },
  992. 'data': 'filter_type=0&mid=' + mid + '&justhide=0&location=home&_t=0',
  993. 'onload': function (resp) { debug('block %s response: %s', mid, resp.responseText); done(); },
  994. 'onerror': function () { debug('block %s network error', mid); done(); },
  995. });
  996. };
  997. var active = function () {
  998. if (!(busy = !!buffer.length)) return;
  999. var mid = buffer.shift();
  1000. block(mid, active);
  1001. };
  1002. return function (mid) {
  1003. buffer.push(mid);
  1004. if (!busy) active();
  1005. return mid;
  1006. };
  1007. }());
  1008.  
  1009. // 有新节点时分发事件监听
  1010. var newNode = (function () {
  1011. var callbacks = [], actived = false;
  1012. var callAll = function (mutation) {
  1013. callbacks.forEach(function (c) { c(mutation); });
  1014. };
  1015. var observe = function () {
  1016. callAll(); // 初始化
  1017. (new MutationObserver(function (mutations) {
  1018. mutations.forEach(function (mutation) { callAll(mutation); });
  1019. })).observe(document.body, { 'childList': true, 'subtree': true });
  1020. };
  1021. var add = function (callback) {
  1022. callbacks.push(withTry(callback));
  1023. return callback;
  1024. };
  1025. var remove = function (callback) {
  1026. var found = false;
  1027. callbacks = callbacks.filter(function (x) {
  1028. var same = x === callback;
  1029. if (same) found = true;
  1030. return !same;
  1031. });
  1032. return found;
  1033. };
  1034. var active = function () {
  1035. if (actived) return;
  1036. actived = true;
  1037. observe();
  1038. };
  1039. return {
  1040. 'add': add,
  1041. 'remove': remove,
  1042. 'active': active,
  1043. };
  1044. }());
  1045.  
  1046. // 添加点击后展开折叠消息的事件
  1047. var fixFoldWeibo = (function () {
  1048. var fixOne = withTry(function (feed) {
  1049. var display = feed.getAttribute('yawf-display').replace(/-fold$/g, '-unfold');
  1050. var showFeed = function () {
  1051. feed.setAttribute('yawf-display', display);
  1052. feed.removeEventListener('click', showFeed);
  1053. };
  1054. var author = feed.querySelector('.WB_name[usercard]').getAttribute('title');
  1055. feed.setAttribute('yawf-author', author);
  1056. feed.addEventListener('click', showFeed);
  1057. });
  1058. var fix = function (feed) {
  1059. var feeds = [feed].concat(Array.apply(Array, feed.querySelectorAll('.WB_feed_type')));
  1060. feeds.forEach(function (feed) {
  1061. if (feed.getAttribute('yawf-display').lastIndexOf('-fold') === -1) return;
  1062. if (feed.getAttribute('yawf-author')) return;
  1063. fixOne(feed);
  1064. });
  1065. };
  1066. fix.init = function () {
  1067. css.add(fillStr('[node-type="feed_list"] .WB_feed_type[yawf-display$="-fold"]::before { content: {{foldedWeiboText}}; }'));
  1068. };
  1069. return fix;
  1070. }());
  1071.  
  1072. // 将父微博和下面的子微博交换
  1073. var swapParentSonWeibo = function (parent, son) {
  1074. var x = cewih('div', '');
  1075. ['.WB_face', '.WB_info', '.WB_text', '.WB_func'].map(function (q) {
  1076. (function (a, b) {
  1077. b.parentNode.replaceChild(x, b);
  1078. a.parentNode.replaceChild(b, a);
  1079. x.parentNode.replaceChild(a, x);
  1080. }(parent.querySelector(q), son.querySelector(q)));
  1081. });
  1082. var mid = parent.getAttribute('mid');
  1083. parent.setAttribute('mid', son.getAttribute('mid'));
  1084. son.setAttribute('mid', mid);
  1085. // 各种细节的修补(原网站做的太乱了……)
  1086. var pf = parent.querySelector('.WB_face'), sf = son.querySelector('.WB_face');
  1087. var pfa = pf.querySelector('a'), pfi = pf.querySelector('img');
  1088. var sfa = sf.querySelector('a'), sfi = sf.querySelector('img');
  1089. if (pfa.href.indexOf('?') === -1) pfa.href += '?from=feed&loc=avatar';
  1090. if (sfa.href.indexOf('?') !== -1) sfa.href = sfa.href.slice(0, sfa.href.indexOf('?'));
  1091. if (!pfa.title) pfa.title = pfi.title;
  1092. if (sfa.title) sfa.removeAttribute('title');
  1093. sfi.width = sfi.height = '30'; pfi.width = pfi.height = '50';
  1094. return son;
  1095. };
  1096.  
  1097. // 如果有一个微博的子微博都隐藏了,那么就隐藏这个微博的子微博框
  1098. var fixSonWeiboDisplay = function (feed) {
  1099. if (!feed.querySelector('.WB_feed_together')) return fixFoldWeibo(feed);
  1100. var sonList = Array.apply(Array, feed.querySelectorAll('.WB_feed_together .WB_sonFeed .WB_feed_type[yawf-display]'));
  1101. // 交换两个子微博
  1102. var swapSon = function (p, q) {
  1103. var x = sonList[p], y = sonList[q];
  1104. var fakeNode = document.createElement('div');
  1105. x.parentNode.insertBefore(fakeNode, x);
  1106. y.parentNode.insertBefore(x, y);
  1107. fakeNode.parentNode.insertBefore(y, fakeNode);
  1108. fakeNode.parentNode.removeChild(fakeNode);
  1109. sonList[p] = y; sonList[q] = x;
  1110. };
  1111. // 把子微博重新排序一下,按照从想看到不想看的顺序排列,排序算法和一趟快排差不多
  1112. var p = 0, q;
  1113. ['show', 'unset', 'unfold', 'fold', 'hidden'].forEach(function (display) {
  1114. q = sonList.length - 1;
  1115. while (p < q) {
  1116. if (sonList[p].getAttribute('yawf-display').lastIndexOf(display) !== -1) p++;
  1117. else if (sonList[q].getAttribute('yawf-display').lastIndexOf(display) === -1) q--;
  1118. else swapSon(p, q);
  1119. }
  1120. });
  1121. // 看看还有多少显示出来的子微博,更新一下子微博的计数
  1122. var sonCount = feed.querySelectorAll('.WB_feed_together .WB_sonFeed .WB_feed_type:not([yawf-display$="-hidden"])').length;
  1123. if (sonCount === 0) feed.querySelector('.WB_feed_together').setAttribute('yawf-display', 'display-hidden');
  1124. else feed.querySelector('[node-type="followNum"]').textContent = sonCount;
  1125. // 如果下面更多的按钮已经没用了,就藏起来吧
  1126. var foldSonCount = feed.querySelectorAll('[node-type="feed_list_wrapForward"] .WB_feed_type:not([yawf-display$="-hidden"])').length;
  1127. if (foldSonCount === 0 && feed.querySelector('[node-type="feed_list_wrapForward"]')) {
  1128. feed.querySelector('.WB_feed_together').setAttribute('yawf-sonfold', 'display');
  1129. }
  1130. // 把原始微博和子微博拆开
  1131. var another = feed.cloneNode(true);
  1132. feed.parentNode.insertBefore(another, feed.nextSibling);
  1133. feed.setAttribute('yawf-withson', 'son');
  1134. another.setAttribute('yawf-display', 'display-son');
  1135. fixFoldWeibo(feed);
  1136. fixFoldWeibo(another);
  1137. };
  1138.  
  1139. // 真正微博过滤的核心模块
  1140. var weiboFilter = function (feed) {
  1141. // 同源合并的微博
  1142. var sonFeeds = Array.apply(Array, feed.querySelectorAll('[node-type="feed_list"] .WB_feed_type:not([yawf-display])'));
  1143. var action = null, parentAction = null;
  1144. var needSwap = function (action) {
  1145. if (!sonFeeds.length) return false;
  1146. if (['hidden', 'fold'].indexOf(action) === -1) return false;
  1147. return true;
  1148. };
  1149. var setAction = function (feed, action) {
  1150. feed.setAttribute('yawf-display', 'display-' + action);
  1151. };
  1152. while (true) {
  1153. action = rules.parse(feed) || 'unset';
  1154. // 如果父微博被屏蔽或折叠,那么就把下面一条没被屏蔽的拉上来换个位置
  1155. if (!needSwap(action)) break;
  1156. setAction(swapParentSonWeibo(feed, sonFeeds.pop()), action);
  1157. }
  1158. parentAction = action;
  1159. // 最后处理所有下面的子微博
  1160. var fixSonWeibo = function (son) {
  1161. var action = rules.parse(son) || 'unset';
  1162. setAction(son, action);
  1163. // 如果一列里面有白名单的,那么把白名单的特别换到外面去
  1164. if (parentAction !== 'show' && action === 'show') {
  1165. setAction(swapParentSonWeibo(feed, son), parentAction);
  1166. parentAction = action;
  1167. }
  1168. };
  1169. while (sonFeeds.length) fixSonWeibo(sonFeeds.shift());
  1170. setAction(feed, parentAction);
  1171. fixSonWeiboDisplay(feed);
  1172. };
  1173.  
  1174. // 对每条微博应用过滤和其他相关回调
  1175. var eachWeibo = (function () {
  1176. var befores = [], afters = [];
  1177. newNode.add(function () {
  1178. var feeds = Array.apply(Array,
  1179. document.querySelectorAll('[node-type="feed_list"] .WB_feed_type:not([yawf-display])'));
  1180. [befores, [weiboFilter], afters].forEach(function (callbacks) {
  1181. feeds.forEach(function (feed) {
  1182. callbacks.forEach(function (f) { f(feed); });
  1183. });
  1184. });
  1185. });
  1186. var add = function (callbacks) {
  1187. return function (callback) {
  1188. callbacks.push(withTry(callback));
  1189. return callback;
  1190. };
  1191. };
  1192. return {
  1193. 'before': add(befores),
  1194. 'after': add(afters),
  1195. };
  1196. }());
  1197.  
  1198. // 根据选择类型不同生成一些存取设置的函数
  1199. var typedConfig = (function () {
  1200. // 字符串
  1201. var baseConfig = function (type) {
  1202. return function (item) {
  1203. var skey = item.key;
  1204. if (item.internal) skey = skey.replace(/\.([^\.]*)$/, '._$1');
  1205. if (!item.getconf) item.getconf = function () {
  1206. return item.conf = config.get(skey, item['default'] || type(), type);
  1207. };
  1208. if (!item.putconf) item.putconf = function (conf) {
  1209. return config.put(skey, item.conf = conf);
  1210. };
  1211. config.reg(skey);
  1212. return item.putconf(item.getconf());
  1213. };
  1214. };
  1215. // 集合类型的add/del操作
  1216. var itemsConfig = function (item) {
  1217. var value = baseConfig(Array)(item);
  1218. if (!item.delconf) item.delconf = function (str) {
  1219. var val = item.getconf();
  1220. val = val.filter(function (x) { return x !== str; });
  1221. return item.putconf(val);
  1222. };
  1223. if (!item.addconf) item.addconf = function (str) {
  1224. item.delconf(str);
  1225. var val = item.getconf(); val.push(str);
  1226. return item.putconf(val);
  1227. };
  1228. return value;
  1229. };
  1230. return {
  1231. 'string': baseConfig(String),
  1232. 'strings': itemsConfig,
  1233. 'boolean': baseConfig(Boolean),
  1234. 'users': itemsConfig,
  1235. 'number': baseConfig(Number),
  1236. 'range': baseConfig(Number),
  1237. 'select': baseConfig(String),
  1238. 'color': baseConfig(String),
  1239. 'key': baseConfig(Number),
  1240. };
  1241. }());
  1242.  
  1243. // 将输入框和某个设置项绑定
  1244. var bindInputValue = (function () {
  1245. var bind = function (key, input, obj, standlize) {
  1246. var fine = standlize || function (x) { return x; };
  1247. var onchange = function () {
  1248. var val = input[key], valid;
  1249. valid = fine(val);
  1250. if (String(valid) !== val) input[key] = valid;
  1251. obj.putconf(valid);
  1252. };
  1253. input[key] = fine(obj.getconf());
  1254. input.addEventListener('change', onchange);
  1255. return onchange;
  1256. };
  1257. return {
  1258. 'text': bind.bind(null, 'value'),
  1259. 'checkbox': bind.bind(null, 'checked'),
  1260. 'select': bind.bind(null, 'value'),
  1261. };
  1262. }());
  1263.  
  1264. // 根据不同类型生成带有事件的文档节点
  1265. var typedHtml = (function () {
  1266.  
  1267. var base = function (base, binder) {
  1268. return function () {
  1269. var item = this;
  1270. // 引用的设置项
  1271. var ref = item.ref;
  1272. // 显示的文字
  1273. var inp = '{{}}', outer = inp, inner = '', text;
  1274. var hasInput = !!html['config' + base + 'Input'];
  1275. if (!item.nogui) {
  1276. text = fillStr(item.text || '').replace(/\|\|/g, html['||']).replace(/\|/g, html['|']);
  1277. if (hasInput && text.indexOf(inp) === -1) text = inp + text;
  1278. outer = fillStr(html['config' + base], { 'text': text }, item);
  1279. }
  1280. if (hasInput) inner = fillStr(html['config' + base + 'Input'], item);
  1281. var line = outer.replace(inp, inner);
  1282. // 在需要引用其他控件的地方留空
  1283. if (!item.nogui) line = line.replace(/{{<([a-zA-Z0-9_-]*)>}}/g, function (m, p) {
  1284. return ref[p] ? fillStr(html.configPrefill, { 'id': p }) : m;
  1285. });
  1286. // 构造基本的文档
  1287. var dom = cewih('div', line).firstChild;
  1288. // 将引用的设置控件填回
  1289. var pf = Array.apply(Array, dom.querySelectorAll('span.yawf-configPrefill'));
  1290. pf.forEach(function (pfi) {
  1291. pfi.parentNode.replaceChild(ref[pfi.id].show(), pfi);
  1292. });
  1293. if (binder) binder(dom, item);
  1294. return dom;
  1295. };
  1296. };
  1297.  
  1298. // 副标题
  1299. var subtitle = base('Subtitle');
  1300. // 文本
  1301. var text = base('Text');
  1302. // 不缩进的文本
  1303. var remark = base('Remark');
  1304.  
  1305. // 真假值的设置项
  1306. var boolean = base('Boolean', function (dom, item) {
  1307. bindInputValue.checkbox(dom.querySelector('input'), item);
  1308. });
  1309.  
  1310. // 选择框设置项
  1311. var select = base('Select', function (dom, item) {
  1312. var select = dom.querySelector('select');
  1313. var defaultValue = item['default'] || item.select.key;
  1314. var keys = item.select.map(function (option) {
  1315. select.appendChild(cewih('select', fillStr(html.option, option)).firstChild);
  1316. return option.value;
  1317. });
  1318. bindInputValue.select(select, item, function (val) {
  1319. return keys.indexOf(val) === -1 ? defaultValue : val;
  1320. });
  1321. });
  1322.  
  1323. // 字符串的设置项
  1324. var string = base('String', function (dom, item) {
  1325. var textarea = dom.querySelector('textarea');
  1326. var onchange = bindInputValue.text(textarea, item);
  1327. textarea.addEventListener('keyup', function () {
  1328. call(onchange);
  1329. });
  1330. });
  1331.  
  1332. // 颜色的设置项
  1333. var color = base('Color', function (dom, item) {
  1334. bindInputValue.text(dom.querySelector('input'), item);
  1335. });
  1336.  
  1337. // 数字的设置项
  1338. var number = base('Number', function (dom, item) {
  1339. var number = dom.querySelector('input');
  1340. var max = Infinity, min = -Infinity;
  1341. if ('max' in item) max = item.max;
  1342. if ('min' in item) min = item.min;
  1343. if (min > max) min = max;
  1344. number.min = min; number.max = max;
  1345. bindInputValue.text(number, item, function (val) {
  1346. if (isNaN(Number(val))) return val = 0;
  1347. return Math.min(max, Math.max(min, Number(val)));
  1348. });
  1349. });
  1350.  
  1351. // 字符串数组设置项模板
  1352. var items = function (base, genli) {
  1353. var item = this;
  1354. var dom = cewih('div', fillStr(base, item)).firstChild;
  1355. var form = dom.querySelector('form'), input = dom.querySelector('input'), ul = dom.querySelector('ul');
  1356. var shown = {};
  1357. // 将某个已经有的字符串显示到末尾
  1358. var moveToEnd = function (x) {
  1359. var p = x.parentNode; p.appendChild(p.removeChild(x));
  1360. };
  1361. // 显示一个新的字符串
  1362. var showStrings = function (userinput, str, onsucc, ondone) {
  1363. genli(item, userinput, str, function (str, li) {
  1364. if (ondone) ondone();
  1365. if (!str || !li) return;
  1366. if (shown[str]) return moveToEnd(shown[str]);
  1367. var del = li.querySelector('a.icon_close');
  1368. del.addEventListener('click', function () {
  1369. delete shown[str];
  1370. if (item.del) item.del(str);
  1371. li.parentNode.removeChild(li);
  1372. item.delconf(str);
  1373. });
  1374. ul.appendChild(shown[str] = li);
  1375. if (onsucc) onsucc(str);
  1376. });
  1377. };
  1378. item.getconf().forEach(function (str) { showStrings(false, str); });
  1379. form.addEventListener('submit', function (e) {
  1380. e.preventDefault();
  1381. var str = input.value;
  1382. input.disabled = true;
  1383. showStrings(true, str, function (str) {
  1384. if (item.add) item.add(str);
  1385. item.addconf(str);
  1386. }, function () {
  1387. input.value = '';
  1388. input.disabled = false;
  1389. });
  1390. });
  1391. return dom;
  1392. };
  1393.  
  1394. // 字符串设置项
  1395. var strings = function () {
  1396. return items.call(this, html.configStrings, function (item, userinput, str, callback) {
  1397. if (userinput && item.add && !(str = item.add(str))) callback();
  1398. else callback(str, cewih('ul', fillStr(html.configStringsItem, { 'item': item.display ? item.display(str) : str })).firstChild);
  1399. });
  1400. };
  1401.  
  1402. // 用户列表的设置项
  1403. var users = function () {
  1404. return items.call(this, html.configUsers, function (item, userinput, str, callback) {
  1405. var showUserNotExistError = function () {
  1406. Alert('yawf-user-not-exist', {
  1407. 'title': fillStr('{{accountNotExistErrorTitle}}'),
  1408. 'text': fillStr('{{{accountNotExistError}}}', { 'name': escapeXml(str) }),
  1409. 'icon': 'error'
  1410. });
  1411. callback();
  1412. };
  1413. if (userinput) {
  1414. if (!(str = str.trim().replace(/^@/, ''))) return callback();
  1415. account.name(str, function (info) {
  1416. if (!info) showUserNotExistError();
  1417. else if (item.add && !item.add(info)) callback();
  1418. else callback(info.id, cewih('ul', fillStr(html.configUsersItem, info)).firstChild);
  1419. }, showUserNotExistError);
  1420. } else {
  1421. var emptyInfo = { 'id': str, 'name': ' ', 'avatar': ' ' };
  1422. var li = cewih('ul', fillStr(html.configUsersItem, emptyInfo)).firstChild;
  1423. callback(str, li);
  1424. account.id(str, function (info) {
  1425. var u = li.querySelector('[uid]');
  1426. u.setAttribute('uid', info.id); u.setAttribute('title', info.name); u.textContent = info.name;
  1427. var p = li.querySelector('.pic img');
  1428. p.src = info.avatar;
  1429. });
  1430. }
  1431. });
  1432. };
  1433.  
  1434. // 一个有数字和垂直范围条的输入框
  1435. var range = base('Range', function (dom, item) {
  1436. var n = dom.querySelector('input[type="number"]');
  1437. var r = dom.querySelector('input[type="range"]');
  1438. n.max = item.max; r.max = item.max;
  1439. n.min = item.min; r.min = item.min;
  1440. var onchange = bindInputValue.text(n, item, function (val) {
  1441. if (isNaN(parseInt(val))) return item['default'] || item.min;
  1442. return Math.min(Math.max(parseInt(val), item.min), item.max);
  1443. });
  1444. r.value = n.value;
  1445. var updateN = function () { if (r.value !== n.value) n.value = r.value; onchange(); };
  1446. var updateR = function () { if (r.value !== n.value) r.value = n.value; onchange(); };
  1447. r.addEventListener('change', updateN); r.addEventListener('mousemove', updateN);
  1448. n.addEventListener('change', updateR); n.addEventListener('keypress', updateR);
  1449. });
  1450.  
  1451. // 一个输入按键的输入框
  1452. var key = base('Key', function (dom, item) {
  1453. var i = dom.querySelector('input'), s = dom.querySelector('button');
  1454. var copyName = function () { s.textContent = keys.name(Number(i.value)); };
  1455. var eventClear = function (e) { e.stopPropagation(); e.preventDefault(); };
  1456. var valid = function (key) { if (key === 27) return 0; return key; }
  1457. var onchange = bindInputValue.text(i, item, function (val) {
  1458. call(copyName); return Number(val);
  1459. });
  1460. dom.addEventListener('keydown', function (e) {
  1461. var val = valid(keys.get(e));
  1462. i.value = val;
  1463. copyName();
  1464. onchange();
  1465. eventClear(e);
  1466. }, false);
  1467. dom.addEventListener('keyup', eventClear, false);
  1468. dom.addEventListener('keypress', eventClear, false);
  1469. i.addEventListener('change', copyName);
  1470. call(copyName);
  1471. });
  1472.  
  1473. return {
  1474. 'subtitle': subtitle,
  1475. 'text': text,
  1476. 'remark': remark,
  1477. 'string': string,
  1478. 'color': color,
  1479. 'number': number,
  1480. 'select': select,
  1481. 'strings': strings,
  1482. 'boolean': boolean,
  1483. 'users': users,
  1484. 'range': range,
  1485. 'key': key,
  1486. };
  1487. }());
  1488.  
  1489. // 过滤器组
  1490. var filterGroup = function (groupName) {
  1491. var items = [];
  1492. // 向过滤器组里面添加一个项目
  1493. var add = function (item) {
  1494. // 先加入所有被引用的对象
  1495. if (item.ref) Object.keys(item.ref).forEach(function (key) {
  1496. item.ref[key].key = item.key + '.' + key;
  1497. item.ref[key].nogui = true;
  1498. add(item.ref[key]);
  1499. });
  1500. // 再加入自己
  1501. items.push(item);
  1502. return item;
  1503. };
  1504. // 网页被初始化时初始化所有过滤器
  1505. var init = function () {
  1506. items.forEach(withTry(function (item) {
  1507. // 初始化过滤器的设置
  1508. if (item.type && typedConfig[item.type])
  1509. item.conf = typedConfig[item.type](item);
  1510. // 初始化过滤器的显示
  1511. if (!item.show && item.type && typedHtml[item.type])
  1512. item.show = typedHtml[item.type].bind(item);
  1513. // 过滤器自己的初始化
  1514. if (item.init) item.init();
  1515. // 真假设置项若设置激活时调用
  1516. if (item.type === 'boolean' && item.conf && item.ainit) item.ainit();
  1517. // 将规则加入到列表中
  1518. if (item.rule) rules.add(item.priority || 0, item.rule.bind(item));
  1519. }));
  1520. };
  1521. // 需要显示选项时生成界面
  1522. var show = function (inner) {
  1523. items.forEach(withTry(function (item) {
  1524. var dom = null;
  1525. if (item.show) dom = item.show(dom);
  1526. if (dom) {
  1527. if (item.shown) item.shown(dom);
  1528. if (!item.nogui) inner.appendChild(dom);
  1529. }
  1530. }));
  1531. };
  1532. // 注册到过滤器分组
  1533. var group = {
  1534. 'name': groupName,
  1535. 'show': show,
  1536. 'init': init,
  1537. 'add': add
  1538. };
  1539. return filters.add(group);
  1540. };
  1541.  
  1542. // 把后面的对象的元素加到第一个上面去
  1543. var extend = function (obj1) {
  1544. return Array.apply(Array, arguments).reduce(function (x, y) {
  1545. for (var k in y) x[k] = y[k];
  1546. return x;
  1547. });
  1548. };
  1549.  
  1550. // 当前是查看分组的页面吗?
  1551. var onGroupPage = function () {
  1552. return location.pathname.slice(-9) === '/mygroups';
  1553. };
  1554.  
  1555. // 白名单、黑名单和折叠名单一式三份
  1556. var allInOneFilters = function (details) {
  1557. var filters = [
  1558. { 'type': 'whitelist', 'priority': 1e5, 'action': 'show' },
  1559. { 'type': 'blacklist', 'priority': 0, 'action': 'hidden' },
  1560. { 'type': 'foldlist', 'priority': -1e5, 'action': 'fold' },
  1561. ];
  1562. var rules = {};
  1563. var typedFilterGroup = filterGroup(details.name + 'FilterGroup');
  1564. filters.forEach(function (filter) {
  1565. // 标题
  1566. typedFilterGroup.add({
  1567. 'type': 'subtitle',
  1568. 'text': '{{{' + filter.type + 'FilterDesc}}}',
  1569. 'typed': '{{' + details.name + 'FilterDetails}}',
  1570. 'shown': function (dom) {
  1571. dom.classList.add('yawf-' + filter.type + 'FilterTitle');
  1572. },
  1573. });
  1574. // 过滤器
  1575. var rule = {
  1576. 'type': details.type || 'strings',
  1577. 'key': 'weibo.filters.' + details.name + '.' + filter.type,
  1578. 'priority': filter.priority,
  1579. 'text': '{{' + details.name + 'FilterDesc}}',
  1580. };
  1581. if (details.add) rule.add = details.add.bind(rule);
  1582. if (details.display) rule.display = details.display.bind(rule);
  1583. rule.rule = details.rule.bind(rule, filter.action);
  1584. if (details[filter.type]) {
  1585. Object.keys(details[filter.type]).forEach(function (override) {
  1586. rule[override] = details[filter.type][override](rule[override]);
  1587. });
  1588. }
  1589. rules[filter.type] = typedFilterGroup.add(rule);
  1590. });
  1591. // 快速创建过滤器的拖动
  1592. if (details.fast) {
  1593. dropdown.add({
  1594. 'valid': details.fast.valid,
  1595. 'desc': details.fast.desc,
  1596. 'add': function (val, action) {
  1597. rules[action].addconf(details.fast.add(val));
  1598. },
  1599. });
  1600. }
  1601. return typedFilterGroup;
  1602. };
  1603.  
  1604. // 方便的选择器
  1605. var weiboContentSelector = function (feed, f) {
  1606. var content = feed.querySelector('[node-type="feed_list_content"]');
  1607. var reason = feed.querySelector('[node-type="feed_list_reason"] em');
  1608. var items = [];
  1609. if (content) items = items.concat(Array.apply(Array, f(content)));
  1610. if (reason) items = items.concat(Array.apply(Array, f(reason)));
  1611. return items;
  1612. };
  1613.  
  1614. // 关键字过滤
  1615. var keywordFilterGroup = allInOneFilters({
  1616. 'name': 'keyword',
  1617. 'add': function (s) { return s.trim(); },
  1618. 'rule': function keywordMatch(action, feed) {
  1619. var keywords = this.conf;
  1620. var texts = weiboContentSelector(feed, function (m) {
  1621. return Array.apply(Array, m.childNodes)
  1622. .filter(function (node) { return node.nodeType === Node.TEXT_NODE; })
  1623. .map(function (node) { return node.textContent; });
  1624. }).join(' ').toUpperCase();
  1625. var match = keywords.some(function (keyword) {
  1626. if (!keyword) return false;
  1627. return texts.indexOf(keyword.toUpperCase()) !== -1;
  1628. });
  1629. if (match) return action; else return null;
  1630. },
  1631. 'fast': {
  1632. 'valid': function (element, callback) {
  1633. var valid = false;
  1634. if (element.nodeType === Node.TEXT_NODE) valid = true;
  1635. else if ((element.tagName || '').toLowerCase() === 'em' &&
  1636. element.firstChild === element.lastChild &&
  1637. element.firstChild.nodeType === Node.TEXT_NODE) valid = true;
  1638. if (valid) callback({ 'text': element.textContent }); else callback();
  1639. },
  1640. 'desc': function (val) {
  1641. return fillStr('{{{keywordFilterFast}}}', { 'text': escapeXml(val.text) });
  1642. },
  1643. 'add': function (val) { return val.text; },
  1644. }
  1645. });
  1646.  
  1647. // 按照正则式过滤
  1648. var regexpFilterGroup = allInOneFilters({
  1649. 'name': 'regexp',
  1650. 'add': function (s) {
  1651. s = s.trim();
  1652. if (s[0] === '/' && s[s.length - 1] === '/') s = s.slice(1, -1);
  1653. try { RegExp(s).exec(''); } catch (e) {
  1654. Alert('yawf-regexp-bad-formed', {
  1655. 'title': fillStr('{{regexpBadFormedTitle}}'),
  1656. 'text': fillStr('{{{regexpBadFormed}}}', { 'regexp': escapeXml(s) }),
  1657. 'icon': 'error'
  1658. });
  1659. s = null;
  1660. }
  1661. return s;
  1662. },
  1663. 'display': function (s) { return '/' + s + '/'; },
  1664. 'rule': function regexpMatch(action, feed) {
  1665. if (!this.regexen) this.regexen = this.conf.map(function (s) {
  1666. try { return RegExp(s); }
  1667. catch (e) { debug('erorr while compile regexp %s : %o', s, e); }
  1668. }).filter(function (x) { return x; });
  1669. var regexen = this.regexen;
  1670. var texts = weiboContentSelector(feed, function (m) {
  1671. return Array.apply(Array, m.childNodes)
  1672. .filter(function (node) { return node.nodeType === Node.TEXT_NODE; })
  1673. .map(function (node) { return node.textContent; });
  1674. }).join(' ');
  1675. var match = regexen.some(function (regexp) {
  1676. return !!(regexp.exec(texts));
  1677. });
  1678. if (match) return action; else return null;
  1679. },
  1680. });
  1681.  
  1682. // 关于正则式的说明
  1683. regexpFilterGroup.add({
  1684. 'type': 'text',
  1685. 'text': '{{regexpFilterRemark}}',
  1686. });
  1687.  
  1688. // 从一条微博中找到他的作者
  1689. var getFeedAuthorId = function (feed) {
  1690. var author = feed.querySelector('.WB_name[usercard]');
  1691. if (!author) return null;
  1692. return author.getAttribute('usercard').split('=')[1];
  1693. };
  1694.  
  1695. // 检查一个元素是不是用户相关的
  1696. var validUserElement = function (element, callback) {
  1697. if (element.nodeType === Node.TEXT_NODE) return callback();
  1698. var a = null;
  1699. // 作者或来源
  1700. try {
  1701. if (element.classList.contains('WB_name')) a = element;
  1702. else a = element.querySelector('.WB_name');
  1703. if (a && (a.getAttribute('usercard') || '').indexOf('id=') === 0) {
  1704. return callback({
  1705. 'name': a.getAttribute('title'),
  1706. 'id': a.getAttribute('usercard').slice(3),
  1707. });
  1708. }
  1709. } catch (e) { }
  1710. // 提及
  1711. try {
  1712. if (element.getAttribute('usercard')) a = element;
  1713. else a = element.querySelector('[usercard]');
  1714. if (a && (a.getAttribute('usercard') || '').indexOf('name=') === 0) {
  1715. return account.name(a.getAttribute('usercard').slice(5), callback, callback);
  1716. }
  1717. } catch (e) { }
  1718. // 其他
  1719. try {
  1720. if (element.getAttribute('title') && element.getAttribute('uid')) a = element;
  1721. else if (element.getAttribute('title') && element.getAttribute('usercard').indexOf('id=') === 0) a = element;
  1722. else a = element.querySelector('[title][uid], [title][usercard^="id="]');
  1723. if (a) {
  1724. return callback({
  1725. 'name': a.getAttribute('title'),
  1726. 'id': a.getAttribute('uid') || a.getAttribute('usercard').slice(3),
  1727. });
  1728. }
  1729. } catch (e) { }
  1730. callback();
  1731. };
  1732.  
  1733. // 作者用户过滤
  1734. var accountFilterGroup = allInOneFilters({
  1735. 'name': 'account',
  1736. 'type': 'users',
  1737. 'rule': function accountMatch(action, feed) {
  1738. var accounts = this.conf, id = getFeedAuthorId(feed);
  1739. if (!id) return null;
  1740. var match = accounts.some(function (x) { return x === id; });
  1741. if (match) return action; else return null;
  1742. },
  1743. 'blacklist': {
  1744. 'rule': function accountMatchBlacklistOverride(_super) {
  1745. return function accountMatchBlacklist(feed) {
  1746. if (!accountByGroup.conf || !onGroupPage()) return _super(feed);
  1747. return null;
  1748. };
  1749. },
  1750. },
  1751. 'fast': {
  1752. 'valid': validUserElement,
  1753. 'desc': function (val) {
  1754. return fillStr('{{{accountFilterFast}}}', { 'name': escapeXml(val.name) });
  1755. },
  1756. 'add': function (val) { return val.id; },
  1757. },
  1758. });
  1759.  
  1760. accountFilterGroup.add({
  1761. 'type': 'text',
  1762. 'text': '{{accountFilterRemark}}',
  1763. });
  1764.  
  1765.  
  1766. // 从一条微博中找到他的作者
  1767. var getFeedOriginalId = function (feed) {
  1768. var originalAuthor = feed.querySelector('.WB_media_expand .WB_info .WB_name');
  1769. if (!originalAuthor) return null;
  1770. return originalAuthor.getAttribute('usercard').split('=')[1];
  1771. };
  1772.  
  1773. // 原创用户过滤
  1774. var originalFilterGroup = allInOneFilters({
  1775. 'name': 'original',
  1776. 'type': 'users',
  1777. 'rule': function originalMatch(action, feed) {
  1778. var originals = this.conf, id = getFeedOriginalId(feed);
  1779. if (!id) return null;
  1780. var match = originals.some(function (x) { return x === id; });
  1781. if (match) return action; else return null;
  1782. },
  1783. 'fast': {
  1784. 'valid': validUserElement,
  1785. 'desc': function (val) {
  1786. return fillStr('{{{originalFilterFast}}}', { 'name': escapeXml(val.name) });
  1787. },
  1788. 'add': function (val) { return val.id; },
  1789. },
  1790. });
  1791.  
  1792. // 找到在一条微博里面被提到的人的昵称
  1793. var getFeedMentionList = function (feed) {
  1794. return weiboContentSelector(feed, function (m) {
  1795. return Array.apply(Array, m.querySelectorAll('a[usercard^="name="][href$="loc=at"]'));
  1796. }).map(function (link) {
  1797. return link.getAttribute('usercard').slice('name='.length);
  1798. });
  1799. };
  1800.  
  1801. // 提到某人的微博
  1802. var mentionFilterGroup = allInOneFilters({
  1803. 'name': 'mention',
  1804. 'type': 'strings',
  1805. 'add': function (s) { return s.trim().replace(/^@/, ''); },
  1806. 'display': function (s) { return '@' + s; },
  1807. 'rule': function mentionMatch(action, feed) {
  1808. var mentions = this.conf, users = getFeedMentionList(feed);
  1809. var match = users.some(function (name) {
  1810. return mentions.indexOf(name) !== -1;
  1811. });
  1812. if (match) return action; else return null;
  1813. },
  1814. 'fast': {
  1815. 'valid': validUserElement,
  1816. 'desc': function (val) {
  1817. return fillStr('{{{mentionFilterFast}}}', { 'name': escapeXml(val.name) });
  1818. },
  1819. 'add': function (val) { return val.name; },
  1820. },
  1821. });
  1822.  
  1823. var getFeedTopicList = function (feed) {
  1824. return weiboContentSelector(feed, function (m) {
  1825. return Array.apply(Array, m.querySelectorAll('.a_topic'));
  1826. }).map(function (topic) { return topic.textContent; });
  1827. };
  1828.  
  1829. // 话题过滤
  1830. var topicFilterGroup = allInOneFilters({
  1831. 'name': 'topic',
  1832. 'add': function (s) { return s.trim().replace(/#/g, ''); },
  1833. 'display': function (s) { return '#' + s + '#'; },
  1834. 'rule': function topicMatch(action, feed) {
  1835. var topics = this.conf;
  1836. var text = getFeedTopicList(feed).join('##');
  1837. var match = topics.some(function (topic) { return text.indexOf(topic) !== -1; });
  1838. if (match) return action; else return null;
  1839. },
  1840. 'fast': {
  1841. 'valid': function (element, callback) {
  1842. var a = null;
  1843. if (element.nodeType === Node.TEXT_NODE) return callback();
  1844. try {
  1845. if (element.tagName.toLowerCase() === 'a' && element.classList.contains('a_topic')) a = element;
  1846. else a = element.querySelector('a.a_topic');
  1847. if (a) return callback({ 'topic': a.textContent.trim().replace(/#/g, '') });
  1848. } catch (e) { }
  1849. callback();
  1850. },
  1851. 'desc': function (val) {
  1852. return fillStr('{{{topicFilterFast}}}', { 'topic': escapeXml(val.topic) });
  1853. },
  1854. 'add': function (val) { return val.topic; },
  1855. }
  1856. });
  1857.  
  1858. // 获取一条微博的所有来源(包括转发)
  1859. var getFeedSourceList = function (feed) {
  1860. return ['[node-type="feed_list_funcLink"] [action-type="app_source"]',
  1861. '.WB_media_expand [action-type="app_source"]'].map(function (qs) {
  1862. var st = feed.querySelector(qs); if (!st) return null;
  1863. return st.getAttribute('title') || st.textContent || '未通过审核应用';
  1864. }).filter(function (x) { return x; });
  1865. };
  1866.  
  1867. // 来源过滤
  1868. var sourceFilterGroup = allInOneFilters({
  1869. 'name': 'source',
  1870. 'add': function (s) {
  1871. s = s.trim();
  1872. if (s === '微博 weibo.com') {
  1873. Alert('yawf-source-filter-warning', {
  1874. 'title': fillStr('{{sourceFilterWarningTitle}}'),
  1875. 'text': fillStr('{{sourceFilterWarning}}'),
  1876. 'icon': 'error'
  1877. });
  1878. s = null;
  1879. }
  1880. return s;
  1881. },
  1882. 'rule': function sourceMatch(action, feed) {
  1883. var sources = this.conf, _sources = getFeedSourceList(feed);
  1884. var match = _sources.some(function (s) { return sources.indexOf(s) !== -1; });
  1885. if (match) return action; else return null;
  1886. },
  1887. 'fast': {
  1888. 'valid': function (element, callback) {
  1889. var a = null;
  1890. if (element.nodeType === Node.TEXT_NODE) return callback();
  1891. try {
  1892. if (element.getAttribute('action-type') === 'app_source') a = element;
  1893. else a = element.querySelector('[action-type="app_source"]');
  1894. if (a) {
  1895. var source = a.getAttribute('title') || a.textContent || '未通过审核应用';
  1896. if (source && source !== '微博 weibo.com') return callback({ 'source': source });
  1897. }
  1898. } catch (e) { debug('%o: %o', e, e.stack); }
  1899. callback();
  1900. },
  1901. 'desc': function (val) {
  1902. return fillStr('{{{sourceFilterFast}}}', { 'source': escapeXml(val.source) });
  1903. },
  1904. 'add': function (val) { return val.source; },
  1905. }
  1906. });
  1907.  
  1908. // 从一条微博中找到所有超链接
  1909. var getFeedHyperlinkList = function (feed) {
  1910. return Array.apply(Array, feed.querySelectorAll('a[title][href^="http://t.cn/"]')).map(function (a) {
  1911. return a.getAttribute('title');
  1912. });
  1913. };
  1914.  
  1915. // 超链接过滤
  1916. var hyperlinkFilterGroup = allInOneFilters({
  1917. 'name': 'hyperlink',
  1918. 'add': function (s) { return s.trim(); },
  1919. 'rule': function hyperlinkMatch(action, feed) {
  1920. var links = this.conf, _links = getFeedHyperlinkList(feed);
  1921. var match = _links.some(function (l) {
  1922. return links.some(function (link) {
  1923. return l.indexOf(link) !== -1;
  1924. });
  1925. });
  1926. if (match) return action; else return null;
  1927. },
  1928. 'fast': {
  1929. 'valid': function (element, callback) {
  1930. if (element.nodeType === Node.TEXT_NODE) return callback();
  1931. try {
  1932. var a;
  1933. if (element.tagName.toLowerCase() === 'a' &&
  1934. element.getAttribute('title') &&
  1935. element.getAttribute('href').indexOf('http://t.cn/') === 0) a = element;
  1936. else a = element.querySelector('a[title][href^="http://t.cn/"]');
  1937. if (a) {
  1938. var x = document.createElement('a'); x.href = a.getAttribute('title');
  1939. if (x.host !== 'weibo.com') return callback({ 'host': x.host });
  1940. }
  1941. } catch (e) { }
  1942. callback();
  1943. },
  1944. 'desc': function (val) {
  1945. return fillStr('{{{hyperlinkFilterFast}}}', { 'host': escapeXml(val.host) });
  1946. },
  1947. 'add': function (val) { return val.host; },
  1948. },
  1949. });
  1950.  
  1951. var otherFilterGroup = filterGroup('otherFilterGroup');
  1952.  
  1953. otherFilterGroup.add({
  1954. 'type': 'subtitle',
  1955. 'text': '{{otherWhitelistTitle}}'
  1956. });
  1957.  
  1958. // 总是显示自己的微博
  1959. otherFilterGroup.add({
  1960. 'type': 'boolean',
  1961. 'key': 'weibo.other.my_weibo',
  1962. 'text': '{{showMyWeiboDesc}}',
  1963. 'priority': 1e5 - 1e3, // 略低于白名单,但高于其他
  1964. 'rule': function showMyWeiboRule(feed) {
  1965. if (!this.conf) return;
  1966. if (getFeedAuthorId(feed) === config.uid) return 'showme'; else return null;
  1967. },
  1968. });
  1969.  
  1970. // 总是显示自己原创的微博
  1971. otherFilterGroup.add({
  1972. 'type': 'boolean',
  1973. 'key': 'weibo.other.my_original',
  1974. 'text': '{{showMyOriginalDesc}}',
  1975. 'priority': 1e5 - 1e3, // 略低于白名单,但高于其他
  1976. 'rule': function showMyOriginalRule(feed) {
  1977. if (!this.conf) return;
  1978. if (getFeedOriginalId(feed) === config.uid) return 'showme'; else return null;
  1979. },
  1980. });
  1981.  
  1982. // 总是显示自己原创的微博
  1983. otherFilterGroup.add({
  1984. 'type': 'boolean',
  1985. 'key': 'weibo.other.mention_me',
  1986. 'text': '{{showMentionMeDesc}}',
  1987. 'priority': 1e5 - 1e3, // 略低于白名单,但高于其他
  1988. 'rule': function showMentionMeRule(feed) {
  1989. if (!this.conf) return;
  1990. if (getFeedMentionList(feed).indexOf(config.uid) !== -1) return 'showme'; else return null;
  1991. },
  1992. });
  1993.  
  1994. otherFilterGroup.add({
  1995. 'type': 'subtitle',
  1996. 'text': '{{otherBlacklistTitle}}'
  1997. });
  1998.  
  1999. // 推广微博
  2000. otherFilterGroup.add({
  2001. 'type': 'boolean',
  2002. 'key': 'weibo.other.ad_feed',
  2003. 'text': '{{adfeedFilterDesc}}',
  2004. 'priority': 1e5 + 1e3, // 优先于白名单
  2005. 'rule': function adFeedFilterRule(feed) {
  2006. if (!this.conf) return null;
  2007. return feed.getAttribute('feedtype') === 'ad' ? 'hidden' : null;
  2008. },
  2009. });
  2010.  
  2011. // 混入新鲜事流的其他东西
  2012. otherFilterGroup.add({
  2013. 'type': 'boolean',
  2014. 'key': 'weibo.other.fake_weibo',
  2015. 'text': '{{fakeWeiboFilterDesc}}',
  2016. 'rule': function fakeWeiboFilterRule(feed) {
  2017. if (!this.conf) return null;
  2018. if (!feed.getAttribute('mid')) return 'hidden';
  2019. return null;
  2020. },
  2021. });
  2022.  
  2023. // 已删除或没有权限查看的微博的转发
  2024. otherFilterGroup.add({
  2025. 'type': 'boolean',
  2026. 'key': 'weibo.other.deleted_forward',
  2027. 'text': '{{deletedForwardFilterDesc}}',
  2028. 'rule': function deletedForwardFilterRule(feed) {
  2029. if (!this.conf) return null;
  2030. if (feed.getAttribute('isforward') === '1' &&
  2031. !feed.querySelector('.WB_media_expand .WB_info .WB_name')) return 'hidden';
  2032. return null;
  2033. },
  2034. });
  2035.  
  2036. // 投票微博
  2037. otherFilterGroup.add({
  2038. 'type': 'boolean',
  2039. 'key': 'weibo.other.vote_weibo',
  2040. 'text': '{{voteWeiboFilterDesc}}',
  2041. 'rule': function voteWeiboFilterRule(feed) {
  2042. if (!this.conf) return null;
  2043. if (feed.querySelector('.WB_from a[href^="http://vote.weibo.com/"]'))
  2044. return 'hidden';
  2045. return null;
  2046. },
  2047. });
  2048.  
  2049. // 淘宝/天猫商品
  2050. otherFilterGroup.add({
  2051. 'type': 'boolean',
  2052. 'key': 'weibo.other.tb_tm_wb',
  2053. 'text': '{{taobaoTianmaoWeibo}}',
  2054. 'rule': function taobaoTianmaoFilterRule(feed) {
  2055. if (!this.conf) return null;
  2056. if (feed.querySelector('a .icon_fl_tb, a .icon_fl_tmall'))
  2057. return 'hidden';
  2058. return null;
  2059. },
  2060. });
  2061.  
  2062. // 刷屏与版聊
  2063. otherFilterGroup.add({
  2064. 'type': 'subtitle',
  2065. 'text': '{{otherSpammingTitle}}',
  2066. });
  2067.  
  2068. // 添加数量和折叠/隐藏的
  2069. var lotShown = function (defnum, defact) {
  2070. return {
  2071. 'number': {
  2072. 'type': 'number',
  2073. 'default': defnum || 3,
  2074. 'min': 1,
  2075. },
  2076. 'action': {
  2077. 'type': 'select',
  2078. 'default': defact || 'fold',
  2079. 'select': [
  2080. { 'value': 'fold', 'text': '{{foldlistActionDesc}}' },
  2081. { 'value': 'hidden', 'text': '{{blacklistActionDesc}}' },
  2082. ]
  2083. },
  2084. };
  2085. };
  2086.  
  2087. // 相同账号的过多微博
  2088. otherFilterGroup.add({
  2089. 'type': 'boolean',
  2090. 'priority': -1e6, // 低优先级
  2091. 'key': 'weibo.other.same_account',
  2092. 'ref': lotShown(5, 'fold'),
  2093. 'text': '{{sameAccountFilterDesc}}',
  2094. 'rule': function sameAccountRule(feed) {
  2095. if (!this.conf) return null;
  2096. // 如果在分组页面,而且用户设置了分组页面忽略该过滤器,则不工作
  2097. if (sameAccountByGroup.conf && onGroupPage()) return null;
  2098. var author = feed.querySelector('.WB_name[usercard]');
  2099. if (!author) return null;
  2100. var id = author.getAttribute('usercard').split('=')[1];
  2101. var number = document.querySelectorAll('[node-type="feed_list"] ' +
  2102. '.WB_feed_type[yawf-display]:not([yawf-display$="-fold"]):not([yawf-display$="-unfold"]):not([yawf-display$="-hidden"])' +
  2103. '>.WB_feed_datail>.WB_detail>.WB_info>a.WB_name[usercard="id=' + id + '"]').length;
  2104. if (number >= this.ref.number.conf) return 'account-' + this.ref.action.conf; else return null;
  2105. },
  2106. });
  2107.  
  2108.  
  2109. // 相同微博的过多转发
  2110. otherFilterGroup.add({
  2111. 'type': 'boolean',
  2112. 'priority': -1e6, // 低优先级
  2113. 'key': 'weibo.other.same_forward',
  2114. 'ref': lotShown(3, 'fold'),
  2115. 'text': '{{sameForwardFilterDesc}}',
  2116. 'rule': function sameForwardRule(feed) {
  2117. if (!this.conf) return null;
  2118. var omid = feed.getAttribute('omid');
  2119. if (!omid) return null;
  2120. var number = document.querySelectorAll('[node-type="feed_list"] ' +
  2121. '.WB_feed_type[omid="' + omid + '"][yawf-display]:not([yawf-display$="-fold"]):not([yawf-display$="-unfold"]):not([yawf-display$="-hidden"])').length;
  2122. if (number >= this.ref.number.conf) return 'forward-' + this.ref.action.conf; else return null;
  2123. },
  2124. });
  2125.  
  2126. // 分组浏览
  2127. otherFilterGroup.add({
  2128. 'type': 'subtitle',
  2129. 'text': '{{otherGroupTitle}}',
  2130. });
  2131.  
  2132. // 分组浏览不做按帐号隐藏
  2133. var accountByGroup = otherFilterGroup.add({
  2134. 'type': 'boolean',
  2135. 'key': 'weibo.other.group_account',
  2136. 'text': '{{accountByGroup}}',
  2137. });
  2138.  
  2139. // 分组浏览不做刷屏检查
  2140. var sameAccountByGroup = otherFilterGroup.add({
  2141. 'type': 'boolean',
  2142. 'key': 'weibo.other.group_same_account',
  2143. 'text': '{{sameAccountByGroup}}',
  2144. });
  2145.  
  2146. var layoutFilterGroup = filterGroup('layoutFilterGroup');
  2147.  
  2148. layoutFilterGroup.add({
  2149. 'type': 'subtitle',
  2150. 'text': '{{layoutFilterGroupDesc}}',
  2151. });
  2152.  
  2153. // 大部分选择器参考了 眼不见心不烦 脚本
  2154. var layouts = (function () {
  2155. var current = null;
  2156. var subtitle = function (name) {
  2157. layoutFilterGroup.add({
  2158. 'type': 'remark',
  2159. 'text': '{{layoutHide' + name + '}}',
  2160. });
  2161. current = name;
  2162. };
  2163.  
  2164. var item = function (name, cssText, defaultValue) {
  2165. layoutFilterGroup.add({
  2166. 'type': 'boolean',
  2167. 'key': 'weibo.layoutHide' + current + name,
  2168. 'default': defaultValue || false,
  2169. 'text': '{{layoutHide' + current + name + '}}',
  2170. 'ainit': css(cssText),
  2171. });
  2172. };
  2173.  
  2174. subtitle('Icon');
  2175. item('Level', '.icon_bed[node-type="level"], .W_level_ico { display: none !important; }');
  2176. item('Member', '.W_ico16[class*="ico_member"], .ico_member_dis { display: none !important; }');
  2177. item('Approve', '.approve { display: none !important; }');
  2178. item('ApproveCo', '.approve_co { display: none !important; }');
  2179. item('Club', '.ico_club { display: none !important; }');
  2180. item('VGirl', '.ico_vlady { display: none !important; }');
  2181. item('Taobao', '.ico_taobao { display: none !important; }');
  2182. item('Zongyika', '.zongyika2014 { display: none !important; }');
  2183. item('Youji', '.lvxing2014 { display: none !important; }');
  2184.  
  2185. subtitle('Nav');
  2186. item('Main', '.gn_nav>div:nth-child(1) { display: none !important; }');
  2187. item('Hot', '.gn_nav>div:nth-child(2) { display: none !important; }');
  2188. item('App', '.gn_nav>div:nth-child(3) { display: none !important; }');
  2189. item('Game', '.gn_nav>div:nth-child(4) { display: none !important; }');
  2190. item('Member', '.gn_setting[node-type="member"] { display: none !important; }');
  2191.  
  2192. subtitle('Left');
  2193. item('ToMe', '#pl_leftnav_common a[href^="/direct/tome"] { display: none !important; }');
  2194. item('Friends', '#pl_leftnav_group > div[node-type="groupList"] > .level_1_Box, #pl_leftnav_common .level_1_Box > form.left_nav_line { display: none !important; }');
  2195. item('App', '#pl_leftnav_app { display: none !important; }');
  2196.  
  2197. subtitle('Middle');
  2198. item('RecommendedTopic', '#pl_content_publisherTop div[node-type="recommendTopic"] { display: none !important; }');
  2199. item('FeedRecommand', 'a.notes[node-type="feed_list_newBar"][href^="http"]:not([action-type="feed_list_newBar"]) { display: none !important; }');
  2200. item('MemberTip', '[node-type="feed_list_shieldKeyword"] { display: none !important; }');
  2201.  
  2202. subtitle('Right');
  2203. item('Template', '.templete_enter { display: none !important; }');
  2204. item('Info', '.W_person_info { display: none !important; }');
  2205. item('Atten', '#pl_rightmod_myinfo .user_atten { display: none !important; }');
  2206. item('Trial', '#trustPagelet_checkin_lotteryv5 { display: none !important; }');
  2207. item('Interest', '[yawf-id="rightmod_recom_interest"] { display: none !important; }');
  2208. item('HotTopic', '[yawf-id="rightmod_zt_hottopic"] { display: none !important; }');
  2209. item('Member', '#trustPagelet_recom_memberv5 { display: none !important; }');
  2210. item('Weibo', '[yawf-id="rightmod_recom_weibo"] { display: none !important; }');
  2211. item('Location', '[yawf-id="rightmod_recom_location"] { display: none !important; }');
  2212. item('Music', '[yawf-id="rightmod_recom_music"] { display: none !important; }');
  2213. item('Movie', '[yawf-id="rightmod_recom_movie"] { display: none !important; }');
  2214. item('Book', '[yawf-id="rightmod_recom_book"] { display: none !important; }');
  2215. item('Notice', '#pl_rightmod_noticeboard { display: none !important; }');
  2216.  
  2217. subtitle('Weibo');
  2218. item('RecomFeed', '[node-type="feed_list_recommend"] { display: none !important; }');
  2219. item('FeedTip', '[node-type="feed_privateset_tip"] { display: none !important; }');
  2220. item('TopicCard', '.WB_feed_spec[exp-data*="value=1022-topic"] { display: none !important; }');
  2221. item('SonTitle', '.WB_feed_type .WB_feed_together .wft_hd { display: none !important; }');
  2222. item('LocationCard', '.WB_feed_spec[exp-data*="value=1022-place"] { display: none !important; }');
  2223. item('Source', '.WB_time+.S_txt2, .WB_time+.S_txt2+.S_link2 { display: none !important; }');
  2224. item('Report', '.WB_time~.hover { display: none !important; }');
  2225. item('Like', 'a[action-type="feed_list_like"], a[action-type="feed_list_like"]+.S_txt3 { display: none !important; }');
  2226. item('Forward', 'a[action-type="feed_list_forward"], a[action-type="feed_list_forward"]+.S_txt3 { display: none !important; }');
  2227. item('Favourite', 'a[action-type="feed_list_favorite"], a[action-type="feed_list_favorite"]+.S_txt3 { display: none !important; }');
  2228. item('BlockBySource', 'div.layer_menu_list[action-type="feed_list_layer"] a[action-type="feed_list_shield_by_app"] { display: none !important; }');
  2229. item('BlockByKeyword', 'div.layer_menu_list[action-type="feed_list_layer"] a[action-type="feed_list_shield_setkeyword"] { display: none !important; }');
  2230.  
  2231. subtitle('Person');
  2232. item('Cover', funcStr(function () { /*!CSS
  2233. .S_profile_pic { display: none; }
  2234. .profile_top { margin-top: 20px; }
  2235. .profile_top .pf_head { top: 5px; margin-top: 0 !important; }
  2236. .profile_top .pf_head_pic { height: 120px; width: 120px; float: right; }
  2237. .profile_top .pf_head_pic img { height: 120px; }
  2238. .profile_top .pf_head .user_atten { width: 60px; float: left; height: 120px; }
  2239. .profile_top .pf_head .user_atten li, .profile_top .pf_head .user_atten .follower { width: 54px; padding: 0 3px 3px; height: 37px; border-right: none; }
  2240. .profile_top .pf_head .user_atten li strong { margin: 3px 0 0; }
  2241. */ }));
  2242. item('BadgeIcon', '.pf_badge_icon { display: none !important; }');
  2243. item('Stats', '.profile_top .user_atten { display: none !important; } .profile_top .pf_head { margin-top: 51px; } ');
  2244. item('MyData', '.W_main_c [id^="Pl_Official_MyMicroworld__"] { display: none !important; }');
  2245. item('Group', '.W_main_2r [id^="Pl_Core_RightGroupsBtn__"] { display: none !important; }');
  2246. item('SuggestUser', '.W_main_2r [id^="Pl_Core_RightUserList__"] { display: none !important; }');
  2247. item('Relation', '.W_main_2r [id^="Pl_Core_RightUserGrid__"] { display: none !important; }');
  2248. item('Album', '.W_main_2r [id^="Pl_Core_RightPicMulti__"] { display: none !important; }');
  2249. item('HotTopic', '.W_main_2r [id^="Pl_Core_RightTextSingle__"] { display: none !important; }');
  2250. item('HotWeibo', '.W_main_2r [id^="Pl_Core_RightPicText__"] { display: none !important; }');
  2251.  
  2252. subtitle('Other');
  2253. item('Ads', '#plc_main [id^="pl_rightmod_ads"], #Box_right [id^="ads_"], #trustPagelet_zt_hottopicv5 [class*="hot_topicad"], div[ad-data], .WB_feed .popular_buss, [id^="sinaadToolkitBox"] { display: none !important; } #wrapAD, .news_logo { visibility: hidden !important; }');
  2254. item('FeedRecom', '.W_main_2r [id^="Pl_Third_Inline__"] { display: none !important; }');
  2255. item('Footer', '.global_footer { display: none !important; }');
  2256. item('WbIm', '.WBIM_news { display: none !important; }');
  2257. item('Tip', '.layer_tips { display: none !important; }');
  2258.  
  2259. var tagRightbarMods = function () {
  2260. var mods = document.querySelectorAll('#trustPagelet_indexright_recom .WB_right_module:not([yawf-id])');
  2261. if (!mods) return;
  2262. var identifiers = {
  2263. '.right_content.hot_topic': 'rightmod_zt_hottopic',
  2264. '.right_content.person_list': 'rightmod_recom_interest',
  2265. '[change-data*="key=index_weibo"]': 'rightmod_recom_weibo',
  2266. '[change-data*="key=index_LBS"]': 'rightmod_recom_location',
  2267. '[change-data*="key=index_song"]': 'rightmod_recom_music',
  2268. '[change-data*="key=index_mov"]': 'rightmod_recom_movie',
  2269. '[change-data*="key=index_book"]': 'rightmod_recom_book'
  2270. };
  2271. Array.apply(Array, mods).forEach(function (mod) {
  2272. Object.keys(identifiers).forEach(function (qs) {
  2273. if (mod.querySelector(qs)) mod.setAttribute('yawf-id', identifiers[qs]);
  2274. });
  2275. });
  2276. };
  2277.  
  2278. newNode.add(tagRightbarMods);
  2279. tagRightbarMods();
  2280. css.add('.W_miniblog { visibility: visible !important; }');
  2281.  
  2282. }());
  2283.  
  2284. // 改造设置
  2285. var toolFilterGroup = filterGroup('toolFilterGroup');
  2286.  
  2287. // 边栏相关工具
  2288. toolFilterGroup.add({
  2289. 'type': 'subtitle',
  2290. 'text': '{{sideColumnToolsTitle}}',
  2291. });
  2292.  
  2293. // 展开左栏分组
  2294. toolFilterGroup.add({
  2295. 'type': 'boolean',
  2296. 'key': 'weibo.tool.showAllGroup',
  2297. 'text': '{{showAllGroupDesc}}',
  2298. 'ainit': css('#pl_leftnav_group div[node-type="moreList"] { display: block !important } #pl_leftnav_group > div[node-type="groupList"] > .level_2_Box > .levmore { display: none }'),
  2299. });
  2300.  
  2301. // 展开左栏消息
  2302. toolFilterGroup.add({
  2303. 'type': 'boolean',
  2304. 'key': 'weibo.tool.showAllMsgNav',
  2305. 'text': '{{showAllMsgNavDesc}}',
  2306. 'ainit': css('#pl_leftnav_common > .level_1_Box > .lev2_new { display: block !important }'),
  2307. });
  2308.  
  2309. // 合并左右边栏
  2310. var mergeLeftRight = toolFilterGroup.add({
  2311. 'type': 'boolean',
  2312. 'key': 'weibo.tool.mergeColumns',
  2313. 'text': '{{mergeLeftRight}}',
  2314. 'ref': {
  2315. 'side': {
  2316. 'type': 'select',
  2317. 'select': [
  2318. { 'value': 'left', 'text': '{{mergeLeftRightLeft}}' },
  2319. { 'value': 'right', 'text': '{{mergeLeftRightRight}}' },
  2320. ],
  2321. 'default': 'right',
  2322. }
  2323. },
  2324. 'init': function () {
  2325. if (!this.conf) return;
  2326. var main = document.querySelector('.W_main');
  2327. var left = document.querySelector('.W_main_l');
  2328. var attr = 'yawf-left';
  2329. if (!left) return;
  2330. var left0 = cewih('div', '');
  2331. left.parentNode.insertBefore(left0, left);
  2332. left.parentNode.removeChild(left);
  2333. var positionLeft = function () {
  2334. var ref = document.querySelector('#pl_rightmod_myinfo')
  2335. var leftn = document.querySelector('.W_main_l');
  2336. if (leftn) { left = leftn; return; }
  2337. if (ref) ref.parentNode.insertBefore(left, ref.nextSibling);
  2338. else left0.parentNode.insertBefore(left, left0);
  2339. main.setAttribute('yawf-merge-left', ref ? 'merge' : 'none');
  2340. };
  2341. positionLeft();
  2342. newNode.add(function () { positionLeft(); })
  2343. css.add(funcStr(function () { /*!CSS
  2344. .W_main[yawf-merge-left="merge"] .W_main_l { width: 229px; padding: 0; float: none; }
  2345. .W_main[yawf-merge-left="merge"] .WB_left_nav .lev a:hover, .WB_left_nav .lev2 a:hover, .WB_left_nav .lev2 a.lev_curr, .WB_left_nav .lev2 a.lev_curr:hover, .WB_left_nav .lev3 a:hover { background-image: none; }
  2346. .W_main[yawf-merge-left="merge"] { width: 830px; background-position: -300px center; background-size: 200% 100%; }
  2347. body.B_index:not([yawf-weibo-only]) .W_main[yawf-merge-left="merge"]~.W_gotop { margin-left: 415px !important; }
  2348. body.B_index:not([yawf-weibo-only]) #yawf-drop-area { left: calc(50% + 185px); }
  2349. */ }));
  2350. if (this.ref.side.conf === 'left') css.add(funcStr(function () { /*!CSS
  2351. .W_main[yawf-merge-left="merge"] .W_main_r { float: left; }
  2352. .W_main[yawf-merge-left="merge"] .W_main_c { float: right; }
  2353. .W_main[yawf-merge-left="merge"] .templete_enter a { right: auto; left: 0; transform: scaleX(-1); }
  2354. .W_main[yawf-merge-left="merge"] .send_weibo .input .arrow, .send_weibo .input.clicked .arrow { right: auto; left: -11px; transform: scaleX(-1); }
  2355. .W_main[yawf-merge-left="merge"] #Box_center { border-left: 2px solid rgba(128, 128, 128, 0.2); margin-left: -2px; }
  2356. body.B_index:not([yawf-weibo-only]) #yawf-drop-area { left: calc(50% - 415px); }
  2357. */ })); else css.add(funcStr(function () { /*!CSS
  2358. .W_main[yawf-merge-left="merge"] #Box_center { border-right: 2px solid rgba(128, 128, 128, 0.2); margin-right: -2px; }
  2359. */ }));
  2360. },
  2361. });
  2362.  
  2363. // 左边栏浮动
  2364. toolFilterGroup.add({
  2365. 'type': 'boolean',
  2366. 'key': 'weibo.tool.fixedLeft',
  2367. 'text': '{{fixedLeft}}',
  2368. 'default': true,
  2369. 'ref': {
  2370. 'items': {
  2371. 'type': 'select',
  2372. 'select': [
  2373. { 'value': 'default', 'text': '{{fixedLeftDefault}}' },
  2374. { 'value': 'whole', 'text': '{{fixedLeftWhole}}' },
  2375. ],
  2376. 'default': 'default',
  2377. }
  2378. },
  2379. 'init': function () {
  2380. var left = document.querySelector('.W_main_l');
  2381. if (!left) return;
  2382. var type = this.conf ? this.ref.items.conf : 'none';
  2383. var merged = mergeLeftRight.conf;
  2384. // 完全默认的情况下不需要脚本
  2385. if (type === 'default' && !merged) return;
  2386. // 禁用掉默认的浮动
  2387. css.add('.W_main [node-type="left_fixed"]:not([yawf-fixed]) { height: auto !important; padding-top: 0 !important; position: static !important; top: 40px !important; animation: none; }');
  2388. // 不浮动的如果禁用了默认的浮动,那么就完成了
  2389. if (type === 'none') return;
  2390. // 否则如果合并了左右边栏,而且我要浮动,那么右面就不要动
  2391. css.add('.W_main[yawf-merge-left="merge"] [node-type="right_module_fixed"] { padding-top: 0 !important; position: static !important; animation: none; }');
  2392. // 最后自定义的浮动
  2393. css.add('.W_main [yawf-fixed] { animation-duration: 0.5s; animation-iteration-count: 1; animation-name: dropdown; animation-timing-function: ease; position: fixed; top: 65px; overflow: hidden; }');
  2394. if (merged) css.add('.W_main [yawf-fixed] { width: 229px; }');
  2395. var container = document.querySelector('.W_main');
  2396. var reference = merged && document.querySelector('.W_main_r') || left;
  2397. var floatitem = type === 'default' && left.querySelector('[node-type="left_fixed"]') || left.querySelector('[node-type="left_all"]');
  2398. var floating = false;
  2399. var updatePosition = function () {
  2400. if (!floating) {
  2401. if (reference.getClientRects()[0].bottom < -65) {
  2402. floating = true;
  2403. floatitem.setAttribute('yawf-fixed', '');
  2404. }
  2405. } else {
  2406. if (reference.getClientRects()[0].bottom > 65 - floatitem.clientHeight) {
  2407. floating = false;
  2408. floatitem.removeAttribute('yawf-fixed');
  2409. call(updatePosition);
  2410. }
  2411. }
  2412. var cip = container.getClientRects()[0];
  2413. var fip = floatitem.getClientRects()[0];
  2414. floatitem.style.maxHeight = Math.max(cip.bottom - fip.top - 20, 0) + 'px';
  2415. };
  2416. document.addEventListener('scroll', updatePosition);
  2417. newNode.add(updatePosition);
  2418. updatePosition();
  2419. },
  2420. });
  2421.  
  2422. // 微博相关工具
  2423. toolFilterGroup.add({
  2424. 'type': 'subtitle',
  2425. 'text': '{{weiboToolsTitle}}',
  2426. });
  2427.  
  2428. // 清除发布框中的默认话题 (wcf)
  2429. toolFilterGroup.add({
  2430. 'type': 'boolean',
  2431. 'key': 'weibo.tool.clear_def_topic',
  2432. 'text': '{{clearDefTopicDesc}}',
  2433. 'ainit': function () {
  2434. var clearDefTopic = function () {
  2435. var inputBox = document.querySelector('#pl_content_publisherTop .send_weibo .input textarea');
  2436. if (inputBox && inputBox.hasAttribute('hottopic')) {
  2437. inputBox.removeAttribute('hottopic'); inputBox.removeAttribute('hottopicid');
  2438. inputBox.value = 'DUMMY'; inputBox.focus();
  2439. inputBox.value = ''; inputBox.blur();
  2440. }
  2441. };
  2442. newNode.add(clearDefTopic);
  2443. },
  2444. });
  2445.  
  2446. // 微博作者与正文同行
  2447. toolFilterGroup.add({
  2448. 'type': 'boolean',
  2449. 'key': 'weibo.tool.unwrapText',
  2450. 'text': '{{unwrapTextDesc}}',
  2451. 'ainit': css('.WB_info, .WB_text { display: inline } .WB_info+.WB_text::before { content: ": " } .WB_func { margin-top: 5px } .B_index .WB_feed .W_ico16 { vertical-align: -3px !important }'),
  2452. });
  2453.  
  2454. // 个人主页自动打开微博列表
  2455. toolFilterGroup.add({
  2456. 'type': 'boolean',
  2457. 'key': 'weibo.tool.redirectWeibo',
  2458. 'text': '{{personalRedirectWeibo}}',
  2459. 'ainit': function () {
  2460. var locat = unsafeWindow.$CONFIG.location;
  2461. if (!locat || locat.slice(-5) !== '_home') return;
  2462. if (!document.body.classList.contains('B_profile')) return;
  2463. var from = (location.search.match(/from=([^&]*)/) || {})[1];
  2464. if (locat.indexOf(from) === 0) return;
  2465. var redirect = function () {
  2466. var link = document.querySelector('.PRF_tab_noicon li.pftb_itm a[href*="/weibo?"]'); if (!link) return;
  2467. if (!link) return false;
  2468. newNode.remove(redirect);
  2469. window.stop(); // 虽然不知道为什么,但是加上就正常了……
  2470. location.replace(link.href);
  2471. };
  2472. newNode.add(redirect);
  2473. redirect();
  2474. },
  2475. });
  2476.  
  2477. // 查看大图旁添加查看原图链接
  2478. toolFilterGroup.add({
  2479. 'type': 'boolean',
  2480. 'default': true,
  2481. 'key': 'weibo.tool.viewOriginal',
  2482. 'text': '{{viewOriginalDesc}}',
  2483. 'ainit': function () {
  2484. var addOriLink = function () {
  2485. var a = document.querySelector('a.show_big[action-data]:not([yawf-viewori])');
  2486. if (!a) return; a.setAttribute('yawf-viewori', 'yawf-viewori');
  2487. var arg = a.getAttribute('action-data').match(/pid=(\w+)&mid=(\d+)&uid=(\d+)/);
  2488. if (!arg) return;
  2489. var vol = cewih('div', fillStr(html.viewOriginalLink)), l = vol.firstChild;
  2490. var img = fillStr(url.view_ori, { 'uid': arg[3], 'mid': arg[2], 'pid': arg[1] });
  2491. while (vol.firstChild) a.parentNode.insertBefore(vol.firstChild, a);
  2492. if (0) GM_xmlhttpRequest({
  2493. 'method': 'GET',
  2494. 'url': img,
  2495. 'onload': function (resp) {
  2496. var h = (new DOMParser()).parseFromString(resp.responseText, 'text/html');
  2497. l.href = h.querySelector('#pic').src;
  2498. },
  2499. }); l.href = img;
  2500. };
  2501. newNode.add(addOriLink);
  2502. },
  2503. });
  2504.  
  2505. // 脚本工具
  2506. toolFilterGroup.add({
  2507. 'type': 'subtitle',
  2508. 'text': '{{scriptToolsTitle}}',
  2509. });
  2510.  
  2511. // 快速创建过滤器
  2512. toolFilterGroup.add({
  2513. 'type': 'boolean',
  2514. 'key': 'weibo.tool.use_fast_creator',
  2515. 'default': true,
  2516. 'text': '{{useFastCreator}}',
  2517. 'ainit': function () {
  2518. dropdown.init();
  2519. },
  2520. });
  2521.  
  2522.  
  2523. // 屏蔽隐藏微博
  2524. toolFilterGroup.add({
  2525. 'type': 'boolean',
  2526. 'key': 'weibo.tool.block_hidden',
  2527. 'text': '{{blockHiddenWeiboDesc}}',
  2528. 'ainit': function () {
  2529. eachWeibo.after(function (feed) {
  2530. [feed].concat(Array.apply(Array, feed.querySelectorAll('.WB_feed_type'))).forEach(function (feed) {
  2531. var display = feed.getAttribute('yawf-display');
  2532. if (display !== 'display-hidden') return;
  2533. if (!feed.getAttribute('mid')) return;
  2534. blockWeibo(feed.getAttribute('mid'));
  2535. feed.setAttribute('yawf-block', 'block');
  2536. });
  2537. });
  2538. }
  2539. });
  2540.  
  2541. // 样式相关工具
  2542. toolFilterGroup.add({
  2543. 'type': 'subtitle',
  2544. 'text': '{{styleToolsTitle}}',
  2545. });
  2546.  
  2547. // 一个带有颜色/透明度的选框项
  2548. var coloredConfigItem = function (details) {
  2549.  
  2550. // 将颜色和透明度转换为一个表示颜色的字符串
  2551. var colorStr = function (color, transparency) {
  2552. return 'rgba(' + color.slice(1)
  2553. .split(/(..)/).filter(function (x) { return x; })
  2554. .map(function (x) { return parseInt(x, 16); }).join(',') +
  2555. ',' + (100 - transparency) / 100 + ')';
  2556. };
  2557.  
  2558. var color = {
  2559. 'type': 'color',
  2560. 'default': details.color || '#000000',
  2561. };
  2562. var transparency = {
  2563. 'type': 'range',
  2564. 'default': details.transparency || 0,
  2565. 'min': 0,
  2566. 'max': 100,
  2567. };
  2568. return {
  2569. 'color': color,
  2570. 'transparency': transparency,
  2571. 'rgba': {
  2572. 'toString': function () {
  2573. return colorStr(color.conf, transparency.conf);
  2574. },
  2575. }
  2576. };
  2577. };
  2578.  
  2579. // 高亮显示白名单微博
  2580. toolFilterGroup.add({
  2581. 'type': 'boolean',
  2582. 'key': 'weibo.tool.whitelist_highlight',
  2583. 'ref': coloredConfigItem({ 'color': '#fef3da' }),
  2584. 'text': '{{whitelistHighlightDesc}}',
  2585. 'ainit': function () {
  2586. css.add(fillStr(funcStr(function () { /*
  2587. .WB_media_expand .WB_arrow { display: none !important; }
  2588. [node-type="feed_list"] .WB_feed_type[yawf-display$="-show"] { background-color: {{color}} !important; box-shadow: -20px 0 0 {{color}}, 20px 0 0 {{color}}; }
  2589. [node-type="feed_list"] .WB_feed_together .WB_feed_type[yawf-display$="-show"] { background-color: {{color}} !important; box-shadow: -10px 0 0 {{color}}, 10px 0 0 {{color}}; }
  2590. */ }), { 'color': '' + this.ref.rgba }));
  2591. },
  2592. });
  2593.  
  2594. // 首页背景
  2595. toolFilterGroup.add({
  2596. 'type': 'boolean',
  2597. 'key': 'weibo.tool.my_background_color',
  2598. 'ref': coloredConfigItem({
  2599. 'color': '#ffffff',
  2600. 'transparency': 30,
  2601. }),
  2602. 'text': '{{mainBackgroundColorOverride}}',
  2603. 'ainit': function () {
  2604. css.add(fillStr(funcStr(function () { /*
  2605. body:not(.S_profile) .W_main { background-image: none !important; background-color: {{color}} !important }
  2606. body:not(.S_profile) .S_bg4, body:not(.S_profile) .W_main_a, body:not(.S_profile) .W_main_bg { background: transparent !important; }
  2607. */ }), { 'color': '' + this.ref.rgba }));
  2608. },
  2609. });
  2610.  
  2611. // 个人主页背景
  2612. toolFilterGroup.add({
  2613. 'type': 'boolean',
  2614. 'key': 'weibo.tool.other_background_color',
  2615. 'ref': coloredConfigItem({
  2616. 'color': '#ffffff',
  2617. 'transparency': 30,
  2618. }),
  2619. 'text': '{{profileBackgroundColorOverride}}',
  2620. 'ainit': function () {
  2621. css.add(fillStr(funcStr(function () { /*
  2622. .S_profile .W_profile_bg, .S_profile .S_bg5 { background-color: {{color}} !important; }
  2623. .S_profile .S_bg4:not(.W_profile_bg) { background: none transparent !important }
  2624. */ }), { 'color': '' + this.ref.rgba }));
  2625. },
  2626. });
  2627.  
  2628. toolFilterGroup.add({
  2629. 'type': 'boolean',
  2630. 'text': '{{weiboOnly}}',
  2631. 'key': 'weibo.tool.weibo_only',
  2632. 'ref': extend({
  2633. // 宽度
  2634. 'width': {
  2635. 'type': 'range',
  2636. 'min': 480,
  2637. 'max': 980,
  2638. 'default': 600,
  2639. },
  2640. // 快捷键
  2641. 'key': {
  2642. 'type': 'key',
  2643. 'default': 119 // F8
  2644. },
  2645. // 是否显示快捷链接
  2646. 'switch': {
  2647. 'type': 'boolean',
  2648. 'default': false,
  2649. },
  2650. }, coloredConfigItem({
  2651. // 颜色
  2652. 'color': '#ffffff',
  2653. // 透明度
  2654. 'transparency': 20,
  2655. }), {
  2656. 'enabled': {
  2657. 'type': 'boolean',
  2658. 'default': false,
  2659. 'internal': true,
  2660. },
  2661. }),
  2662. 'ainit': function () {
  2663. if (!document.body.classList.contains('B_index') &&
  2664. !document.body.classList.contains('B_profile')) return;
  2665. var that = this;
  2666. var key = that.ref.key, attr = 'yawf-weibo-only';
  2667. // 切换阅读模式开关
  2668. var switchMode = function (enable) {
  2669. var enabled = document.body.hasAttribute(attr);
  2670. if (enable == null) enable = !enabled;
  2671. else if (enable === enabled) return;
  2672. if (enable) document.body.setAttribute(attr, attr);
  2673. else document.body.removeAttribute(attr);
  2674. that.ref.enabled.putconf(enable);
  2675. };
  2676. // 检查快捷键按键
  2677. keys.reg(key.conf, function () {
  2678. // 只有有切换按钮的时候才生效
  2679. if (document.querySelector('.group_read .right_bar[yawf-weibo-only-added]'))
  2680. switchMode();
  2681. });
  2682. // 显示切换按钮
  2683. if (that.ref['switch'].conf) {
  2684. var showSwitch = function () {
  2685. var rightBar = document.querySelector('.group_read .right_bar:not([yawf-weibo-only-added])');
  2686. if (!rightBar) return; rightBar.setAttribute('yawf-weibo-only-added', 'added');
  2687. var weiboOnly = cewih('div', fillStr(html.weiboOnlyButton, {
  2688. 'text': text.weiboOnlyButton,
  2689. 'shortcut': key.conf === 0 ? '' : ' (' + keys.name(key.conf) + ')',
  2690. })).firstChild;
  2691. weiboOnly.addEventListener('click', switchMode.bind(that, null));
  2692. rightBar.insertBefore(weiboOnly, rightBar.querySelector('.right_item~.right_item'));
  2693. newNode.remove(showSwitch);
  2694. };
  2695. newNode.add(showSwitch);
  2696. }
  2697. // 注册样式
  2698. css.add(fillStr(funcStr(function () { /*!CSS
  2699. body.B_index[{{attr}}] .W_main_l,
  2700. body.B_index[{{attr}}] .W_main_r,
  2701. body.B_index[{{attr}}] #Box_center>div:not(#pl_content_homeFeed),
  2702. body.B_index[{{attr}}] .global_footer { display: none; }
  2703. body.B_profile[{{attr}}] #Pl_Official_Header__1,
  2704. body.B_profile[{{attr}}] #Pl_Core_Header__1,
  2705. body.B_profile[{{attr}}] #Pl_Core_Nav__2,
  2706. body.B_profile[{{attr}}] .W_main_2r,
  2707. body.B_profile[{{attr}}] .global_footer { display: none; }
  2708. body.B_index[{{attr}}] .WB_global_nav,
  2709. body.B_profile[{{attr}}] .WB_global_nav { position: absolute !important; }
  2710. body.B_index[{{attr}}] .W_miniblog,
  2711. body.B_profile[{{attr}}] .W_miniblog { padding-top: 60px; }
  2712. body.B_index[{{attr}}] .WB_feed .WB_screen,
  2713. body.B_profile[{{attr}}] .WB_feed .WB_screen { margin-left: calc({{width}} - 48px); }
  2714. body.B_index[{{attr}}] .W_main,
  2715. body.B_profile[{{attr}}] .W_main { width: {{width}} !important; background: {{bgcolor}} !important; }
  2716. body.B_index[{{attr}}] #Box_center,
  2717. body.B_index[{{attr}}] .WB_feed .repeat .input textarea,
  2718. body.B_profile[{{attr}}] .WB_feed .repeat .input textarea { width: 100%; }
  2719. body.B_index[{{attr}}] .WB_feed .type_text,
  2720. body.B_profile[{{attr}}] .WB_feed .type_text { margin-left: calc({{width}} - 165px); }
  2721. body.B_index[{{attr}}] .W_gotop,
  2722. body.B_profile[{{attr}}] .W_gotop { margin-left: calc({{width}} / 2) !important; }
  2723. body.B_index[{{attr}}] .WB_feed .between_line,
  2724. body.B_profile[{{attr}}] .WB_feed .between_line { padding: 0 calc({{width}} / 2 - 132px) !important; }
  2725. body.B_index[{{attr}}] .WB_media_expand .WB_arrow,
  2726. body.B_profile[{{attr}}] .WB_media_expand .WB_arrow { display: none !important; }
  2727. body.B_index[{{attr}}] #yawf-drop-area,
  2728. body.B_profile[{{attr}}] #yawf-drop-area { left: calc(50% + {{width}} / 2 - 230px); top: 0; }
  2729. body.B_index[{{attr}}] .W_main_a { width: {{width}}; }
  2730. body.B_profile[{{attr}}] .W_main_c { padding-top: 9px; width: {{width}}; }
  2731. .input_search { float: left; }
  2732. */ }), {
  2733. 'width': that.ref.width.conf + 'px',
  2734. 'bgcolor': that.ref.rgba + '',
  2735. 'attr': attr,
  2736. }));
  2737. var updateModeByConf = function () {
  2738. switchMode.call(that, that.ref.enabled.getconf());
  2739. };
  2740. updateModeByConf();
  2741. window.addEventListener('focus', updateModeByConf);
  2742. }
  2743. });
  2744.  
  2745. // 自定义样式
  2746. toolFilterGroup.add({
  2747. 'type': 'string',
  2748. 'text': '{{userstyleTitle}}',
  2749. 'key': 'weibo.tool.userstyle',
  2750. 'init': function () {
  2751. var conf = this.conf; GM_addStyle(conf);
  2752. var set = this.putconf.bind(this);
  2753. // 在 GM 中注册菜单项以支持对自定义 CSS 的修改
  2754. // 此处并非常规设置方式,仅应在因用户加入的CSS导致无法正常显示脚本设置界面时使用。
  2755. // 所以此处设置时不应依赖网页界面实现。
  2756. var putconf = function (css) {
  2757. conf = css;
  2758. setTimeout(function () { set(css); config.write(); location.reload(); }, 0);
  2759. };
  2760. GM_registerMenuCommand(fillStr('{{userstyleEditDesc}}'), function () {
  2761. var newcss = prompt(fillStr('{{userstyleEditDetails}}'), conf);
  2762. if (newcss !== null) putconf(newcss);
  2763. }, "S");
  2764. },
  2765. });
  2766.  
  2767. // 脚本设置
  2768. var scriptFilterGroup = filterGroup('scriptFilterGroup');
  2769.  
  2770. // 导入导出
  2771. scriptFilterGroup.add({
  2772. 'type': 'subtitle',
  2773. 'text': '{{configImportAndExport}}',
  2774. });
  2775.  
  2776. scriptFilterGroup.add({
  2777. 'show': function () {
  2778. var dom = cewih('div', html.configImportExport).firstChild;
  2779. var bii = dom.querySelector('input[type="file"]');
  2780. var be = dom.querySelector('[node-type="export"]');
  2781. var br = dom.querySelector('[node-type="reset"]');
  2782. // 导出按钮
  2783. var updateExportButton = function () {
  2784. be.href = 'data:application/octet-stream;base64,' +
  2785. btoa(unescape(encodeURIComponent(config.export())));
  2786. be.setAttribute('download', 'yawf-config.yawf');
  2787. };
  2788. // 导入按钮
  2789. var doImport = function (file) {
  2790. var reader = new FileReader();
  2791. // 导入成功
  2792. var success = function () {
  2793. Alert('yawf-config-import-success', {
  2794. 'title': fillStr('{{configImportSuccessTitle}}'),
  2795. 'text': fillStr('{{configImportSuccess}}'),
  2796. 'icon': 'succ'
  2797. });
  2798. };
  2799. // 导入失败
  2800. var error = function () {
  2801. Alert('yawf-config-import-fail', {
  2802. 'title': fillStr('{{configImportFailTitle}}'),
  2803. 'text': fillStr('{{configImportFail}}'),
  2804. 'icon': 'error'
  2805. });
  2806. };
  2807. // 读文件
  2808. if (file.size > (1 << 24)) error();
  2809. else reader.addEventListener('load', function () {
  2810. if (config.import(reader.result)) {
  2811. updateExportButton();
  2812. success();
  2813. } else error();
  2814. });
  2815. reader.readAsText(file);
  2816. bii.value = '';
  2817. };
  2818. bii.addEventListener('change', function () {
  2819. var file = bii.files[0];
  2820. Confirm('yawf-config-import-warning', {
  2821. 'title': fillStr('{{configImportWarningTitle}}'),
  2822. 'text': fillStr('{{configImportWarning}}'),
  2823. 'onOk': function () { doImport(file); },
  2824. });
  2825. });
  2826. updateExportButton();
  2827. // 重置按钮
  2828. var doReset = function () {
  2829. config.clear();
  2830. updateExportButton();
  2831. };
  2832. br.addEventListener('click', function () {
  2833. Confirm('yawf-config-reset-warning', {
  2834. 'title': fillStr('{{configResetWarningTitle}}'),
  2835. 'text': fillStr('{{configResetWarning}}'),
  2836. 'onOk': doReset,
  2837. });
  2838. });
  2839. return dom;
  2840. },
  2841. });
  2842.  
  2843. // 调试
  2844. scriptFilterGroup.add({
  2845. 'type': 'subtitle',
  2846. 'text': '{{scriptDebugTitle}}',
  2847. });
  2848.  
  2849. scriptFilterGroup.add({
  2850. 'type': 'boolean',
  2851. 'text': '{{scriptDebug}}',
  2852. 'getconf': function () { return !!GM_getValue('debug', false); },
  2853. 'putconf': function (value) { GM_setValue('debug', !!value); return !!value; },
  2854. });
  2855.  
  2856. // 关于
  2857. scriptFilterGroup.add({
  2858. 'type': 'subtitle',
  2859. 'text': '{{scriptAboutTitle}}',
  2860. });
  2861.  
  2862. scriptFilterGroup.add({
  2863. 'type': 'text', 'text': '',
  2864. 'shown': function (dom) {
  2865. dom.innerHTML = fillStr(text.scriptAbout, {
  2866. 'version': ((GM_info || {}).script || {}).version || '?'
  2867. });
  2868. },
  2869. });
  2870.  
  2871. scriptFilterGroup.add({
  2872. 'init': function () {
  2873. var isEn = i18n.lang === 'en';
  2874. css.add(fillStr(funcStr(function () { /*!CSS
  2875. .profile_tab .pftb_lk { padding-left: {{headerWidth}}; padding-right: {{headerWidth}}; }
  2876. .profile_tab .current.pftb_lk { padding-left: {{headerWidth2}}; padding-right: {{headerWidth2}}; }
  2877. .layoutFilterGroupLayer .yawf-configBoolean { width: {{layoutOptionWidth}}; }
  2878. */ }), {
  2879. 'headerWidth': isEn ? '12px' : '15px',
  2880. 'headerWidth2': isEn ? '9px' : '12px',
  2881. 'layoutOptionWidth': isEn ? '320px' : '160px',
  2882. }));
  2883. },
  2884. });
  2885.  
  2886. // 检查是否要在本页上运行
  2887. var validPage = function () {
  2888. if (self !== top) return false;
  2889. if (!unsafeWindow.$CONFIG) return false;
  2890. if (!unsafeWindow.$CONFIG.uid) return false;
  2891. if (!unsafeWindow.$CONFIG.lang) return false;
  2892. return true;
  2893. };
  2894.  
  2895. // 完成加载时
  2896. var dcl = function () {
  2897. if (!validPage()) return;
  2898. // 初始化用户语言
  2899. i18n(unsafeWindow.$CONFIG.lang);
  2900. // 加载用户配置
  2901. config = config(unsafeWindow.$CONFIG.uid);
  2902. // 初始化文本和网页数据(基于用户选择的语言)
  2903. Object.keys(text).map(function (key) { i18n(text[key]); text[key] = text[key].local; });
  2904. Object.keys(html).map(function (key) { html[key] = fillStr(html[key]); });
  2905. // 初始化所有过滤器
  2906. filters.init();
  2907. // 初始化折叠微博后的显示
  2908. fixFoldWeibo.init();
  2909. // 注册样式
  2910. css.init();
  2911. // 初始化界面
  2912. showIcon();
  2913. // 开始过滤
  2914. newNode.active();
  2915. };
  2916. if (document.body) call(dcl);
  2917. else document.addEventListener('DOMContentLoaded', dcl);
  2918.  
  2919. GM_addStyle(fillStr((funcStr(function () { /*!CSS
  2920. // 在顶部添加按钮
  2921. .gn_setting[node-type="member"]:last-child { margin-right: 44px; }
  2922. .WB_global_nav .gn_setting .gn_tab.gn_filter .ico { background-image: url("{{filter-img}}"); !important; background-position: 0 0 !important; }
  2923. .WB_global_nav .gn_search { width: 210px !important; left: 440px !important; position: absolute !important; }
  2924. .WB_global_nav .gn_search .gn_input { width: 168px !important; }
  2925. // 设置框相关样式
  2926. .yawf-Layer.yawf-drag { opacity: 0.67; -moz-user-select: none; user-select: none; }
  2927. #yawf-config [node-type="inner"] { padding: 20px; }
  2928. .yawf-config-body { margin: -20px; max-height: 360px; overflow-y: auto; padding: 20px; width: 760px; }
  2929. #yawf-config .profile_tab { font-size: 12px; margin: -20px -20px 20px; width: 800px; }
  2930. .yawf-config-layer { padding-bottom: 20px; }
  2931. .yawf-groupSubtitle, .yawf-groupRemark { margin: 5px 10px; padding: 10px 0 0 0; }
  2932. .yawf-groupSubtitle { font-weight: bold; }
  2933. .yawf-configInput { display: inline; }
  2934. .yawf-configStringInput { display: block; }
  2935. .yawf-configItem, .yawf-groupText { margin: 0 20px; padding: 0 0; }
  2936. .yawf-configItem { line-height: 30px; }
  2937. .yawf-groupText { line-height: 1em; }
  2938. .yawf-groupText p { margin: 2px 0 0; }
  2939. .yawf-configKeyInput button { padding: 0 1em; }
  2940. .yawf-configItem label+label { margin-left: 0.5em; }
  2941. .yawf-configItem br+label { margin-left: 4em; }
  2942. .yawf-whitelistFilterTitle::before, .yawf-blacklistFilterTitle::before, .yawf-foldlistFilterTitle::before { content: " "; display: inline-block; width: 0.8em; height: 0.8em; border-radius: 1em; margin-right: 0.5em; border: 1px solid white; vertical-align: middle; }
  2943. .yawf-whitelistFilterTitle::before { background: #37c837; box-shadow: 0 0 2px #37c837; }
  2944. .yawf-blacklistFilterTitle::before { background: #c83737; box-shadow: 0 0 2px #c83737; }
  2945. .yawf-foldlistFilterTitle::before { background: #c8c837; box-shadow: 0 0 2px #c8c837; }
  2946. .yawf-configString span { line-height: 16px; width:calc(100% - 56px); margin: 1px 1px -21px; padding: 2px 10px; display: block; position: relative; }
  2947. .yawf-configString textarea.W_input { width: calc(100% - 20px); padding-top: 20px; min-height: 80px; resize: vertical; background: linear-gradient(to bottom, -moz-Dialog 0px, -moz-Dialog 20px, transparent 21px, transparent 100%); }
  2948. .yawf-configStringsInput, .yawf-configUsersInput { margin: 5px; }
  2949. .yawf-configStringsItems, .yawf-configUsersItems { padding: 5px 10px; line-height: 1em; }
  2950. .yawf-configStringsItem, .yawf-configUsersItem { display: inline-block; margin: 2px; }
  2951. .yawf-configStringsItem a.icon_close, .yawf-configUsersItem a.icon_close { margin-left: 3px; vertical-align: -2px; }
  2952. .yawf-configUsersItem .shield_object_card { display: inline-block; }
  2953. .yawf-configUsersItem .shield_object_card .card_bg { border: 1px solid #e6e6e6; border-radius: 2px; box-shadow: 0 1px 1px rgba(0, 0, 0, 0.02); padding: 1px 5px 1px 1px; }
  2954. .yawf-configUsersItem .shield_object_card .card_pic { float: left; width: 50px; }
  2955. .yawf-configUsersItem .shield_object_card .card_pic .pic_box { display: block; }
  2956. .yawf-configUsersItem .shield_object_card .card_content { margin-left: 60px; }
  2957. .yawf-configUsersItem .shield_object_card .object_info { height: 22px; margin-top: 2px; }
  2958. .yawf-configUsersItem .shield_object_card .object_name { line-height: 22px; overflow: hidden; }
  2959. .yawf-configUsersItem .shield_object_card .other_info { line-height: 24px; }
  2960. .yawf-configImportExport [node-type] { margin-right: 20px; }
  2961. .yawf-configAdd { appearance: none; }
  2962. #yawf-config .btn { border-top: 1px solid #ccc; margin: 15px 0 0; padding: 10px 0 0; }
  2963. #yawf-config .btn .W_btn_b_disable:hover { border-color: #d9d9d9; }
  2964. #yawf-config .btn .W_btn_b_disable:hover span { border-color: #ffffff; }
  2965. .layoutFilterGroupLayer .yawf-configBoolean { display: inline-block; margin-right: 0; }
  2966. .yawf-userstyles-tip { float: right; }
  2967. // 隐藏微博
  2968. [yawf-display$="-hidden"] { display: none !important; }
  2969. [node-type="feed_list"] .WB_feed_type:not([yawf-display]), [node-type="feed_list"] .WB_feed_type .WB_feed_type:not([yawf-display]) { visibility: hidden !important; }
  2970. // 折叠微博
  2971. [node-type="feed_list"] .WB_feed_type[yawf-display$="-fold"]::before { display: block; width: 100%; height: 24px; line-height: 24px; padding: 0 2em 20px; border-bottom: #e6e6e6 1px solid; }
  2972. [node-type="feed_list"] .WB_feed_type[yawf-display$="-fold"] { height: 45px; overflow: hidden; cursor: pointer; }
  2973. [node-type="feed_list"] .WB_feed_type[yawf-display$="-fold"] .WB_screen { margin-top: -40px !important; }
  2974. [node-type="feed_list"] .WB_feed_type[yawf-display$="-fold"] .WB_feed_datail,
  2975. [node-type="feed_list"] .WB_feed_type[yawf-display$="-fold"] .type_spe_pos { display: none !important; }
  2976. // 子微博
  2977. [node-type="feed_list"] .WB_feed_type[yawf-display$="-hidden"]+.WB_feed_type[yawf-display$="-son"],
  2978. [node-type="feed_list"] .WB_feed_type[yawf-display$="-fold"]+.WB_feed_type[yawf-display$="-son"] { display: none !important; }
  2979. [node-type="feed_list"] .WB_feed_type:not([yawf-display$="-son"]) .WB_feed_together { display: none !important; }
  2980. [node-type="feed_list"] .WB_feed_type[yawf-withson="son"] .WB_feed_datail,
  2981. [node-type="feed_list"] .WB_feed_type[yawf-withson="son"][yawf-display$="-fold"]::before { border: 0 none !important; }
  2982. [node-type="feed_list"] .WB_feed_type[yawf-display$="-son"] { padding-top: 0 !important; }
  2983. [node-type="feed_list"] .WB_feed_type[yawf-display$="-son"]>.WB_screen,
  2984. [node-type="feed_list"] .WB_feed_type[yawf-display$="-son"]>.WB_feed_datail>.WB_face,
  2985. [node-type="feed_list"] .WB_feed_type[yawf-display$="-son"]>.WB_feed_datail>.WB_detail>*:not(.WB_feed_together) { display: none !important; }
  2986. [node-type="feed_list"] .WB_feed_type[yawf-display$="-son"]>.WB_feed_datail>.WB_detail { margin-top: -20px; margin-bottom: -10px; }
  2987. // 其他
  2988. .WB_feed_together .wft_users { display: none; }
  2989. .WB_feed_together[yawf-sonfold="display"] [node-type="feed_list_wrapForward"] { display: block !important; }
  2990. .WB_feed_together[yawf-sonfold="display"] [action-type="feed_list_seeAll"],
  2991. .WB_feed_together[yawf-sonfold="display"] [action-type="feed_list_foldForward"] { display: none !important; }
  2992. .W_miniblog { visibility: hidden; }
  2993. .yawf-range-container { background-color: #f0f0f0; background-color: -moz-dialog; position: relative; display: inline-block; margin-left: -66px; width: 81px; margin-right: -15px; -webkit-transform: rotate(270deg); transform: rotate(270deg); top: calc(-1em - 36px); box-shadow: 0px 12px #f0f0f0, 0px -12px #f0f0f0; box-shadow: 0px 12px -moz-dialog, 0px -12px -moz-dialog; }
  2994. input[type="number"]:not(:focus) ~ .yawf-range-container:not(:hover) > input[type="range"]:not(:focus) { display: none; }
  2995. // 拖拽
  2996. #yawf-drop-area { background: rgba(251, 251, 216, 0.8); display: none; height: 230px; left: calc(50% + 260px); position: fixed; top: 40px; width: 230px; z-index: 9999; }
  2997. .yawf-drop-area-desc { height: 170px; width: 170px; margin: 16px 16px -206px 16px; padding: 10px; -moz-user-select: none; user-select: none; border: 4px dashed #ddd; border-radius: 20px; }
  2998. .yawf-drop-area-title { font-size: 150%; font-weight: bold; }
  2999. .yawf-drop-area-text { padding: 10px; }
  3000. #yawf-drop-area-content { height: 230px; width: 230px; position: relative; z-index: 10002; opacity: 0; }
  3001. #yawf-fast-filter-chose { padding: 20px 40px; }
  3002. #yawf-fast-filter-text { font-weight: bold; }
  3003. */ }) + '\n').replace(/\/\/.*\n/g, '\n'), {
  3004. 'filter-img': images.filter,
  3005. }));