Greasy Fork is available in English.

京东自营过滤

在京东商品列表和搜索结果页面增加【自营】【非自营】以及【满赠】【满减】等超过30个商品过滤选项,为【京东配送】【仅显示有货】以及【排序】选项增加记忆功能。

  1. // ==UserScript==
  2. // @name 京东自营过滤
  3. // @version 0.5.3.4
  4. // @icon https://www.jd.com/favicon.ico
  5. // @description 在京东商品列表和搜索结果页面增加【自营】【非自营】以及【满赠】【满减】等超过30个商品过滤选项,为【京东配送】【仅显示有货】以及【排序】选项增加记忆功能。
  6. // @author You!
  7. // @grant GM_setValue
  8. // @grant GM_getValue
  9. // @grant GM_deleteValue
  10. // @grant GM_listValues
  11. // @grant GM_addStyle
  12. // @grant unsafeWindow
  13. // @include *://list.jd.com/list.html?*
  14. // @include *://coll.jd.com/list.html?*
  15. // @include *://list.jd.com/search?*
  16. // @include *://list.jd.com/Search?*
  17. // @include *://search.jd.com/search?*
  18. // @include *://search.jd.com/Search?*
  19. // @include *://www.jd.com/pinpai/*
  20. // _require https://code.jquery.com/jquery-3.5.0.min.js
  21. // @require https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js
  22. // _require https://code.jquery.com/color/jquery.color-2.1.2.min.js
  23. // @require https://cdn.bootcdn.net/ajax/libs/jquery-color/2.1.2/jquery.color.min.js
  24. // @run-at document-start
  25. // @namespace https://greasyfork.org/zh-CN/scripts/33729-京东自营过滤
  26. // ==/UserScript==
  27.  
  28. (function() {
  29. 'use strict';
  30.  
  31. //过滤器
  32. var jdGoodsFilters = [
  33. //自营,非自营
  34. {name:'自营',nameN:'非自营',lv:0,conditions:[
  35. {type:'attr',tag:'i',attr:'data-tips',vals:[
  36. '京东自营,品质保障'
  37. ]}
  38. ]},
  39.  
  40. //PLUS会员价
  41. {name:'PLUS会员',nameN:'非PLUS会员',lv:1,conditions:[
  42. {type:'attr',tag:'span',attr:'title',vals:[
  43. 'PLUS会员专享价'
  44. ]}
  45. ]},
  46.  
  47. //优惠券
  48. {name:'优惠券',nameN:'无优惠券',lv:1,conditions:[
  49. {type:'attr',tag:'i',attr:'data-tips',vals:[
  50. '优惠券',
  51. '本商品可领用优惠券'
  52. ]}
  53. ]},
  54.  
  55. //满减
  56. {name:'满减',nameN:'非满减',lv:1,conditions:[
  57. {type:'attr',tag:'i',attr:'data-tips',vals:[
  58. '该商品参加满减活动',
  59. '本商品参与满减促销',
  60. '满减'
  61. ]},
  62. ]},
  63.  
  64. //满件
  65. {name:'满件',nameN:'非满件',lv:1,conditions:[
  66. {type:'attr',tag:'i',attr:'data-tips',vals:[
  67. '本商品参与满件促销'
  68. ]}
  69. ]},
  70.  
  71. //满赠
  72. {name:'满赠',nameN:'非满赠',lv:1,conditions:[
  73. {type:'attr',tag:'i',attr:'data-tips',vals:[
  74. '该商品参加满赠活动'
  75. ]}
  76. ]},
  77.  
  78. //赠品
  79. {name:'赠品',nameN:'无赠品',lv:1,conditions:[
  80. {type:'attr',tag:'i',attr:'data-tips',vals:[
  81. '购买本商品送赠品'
  82. ]}
  83. ]},
  84.  
  85. //京东物流
  86. {name:'京东物流',nameN:'非京东物流',lv:1,conditions:[
  87. {type:'attr',tag:'i',attr:'data-tips',vals:[
  88. '京东物流仓配,商家提供售后服务'
  89. ]}
  90. ]},
  91.  
  92. //厂商配送
  93. {name:'厂商配送',nameN:'非厂商配送',lv:1,conditions:[
  94. {type:'attr',tag:'i',attr:'data-tips',vals:[
  95. '厂家或供应商发货和配送'
  96. ]}
  97. ]},
  98.  
  99. //到店自取
  100. {name:'到店自取',nameN:'非到店自取',lv:1,conditions:[
  101. {type:'attr',tag:'i',attr:'data-tips',vals:[
  102. '该商品支持到店自取'
  103. ]}
  104. ]},
  105.  
  106. //免邮
  107. {name:'免邮',nameN:'不免邮',lv:1,conditions:[
  108. {type:'attr',tag:'i',attr:'data-tips',vals:[
  109. '当前收货地址,该商品免邮费',
  110. '当前收货地址,本商品免邮费'
  111. ]}
  112. ]},
  113.  
  114. //运费险
  115. {name:'运费险',nameN:'无运费险',lv:1,conditions:[
  116. {type:'attr',tag:'i',attr:'data-tips',vals:[
  117. '运费险',
  118. '退换货免运费'
  119. ]},
  120. ]},
  121.  
  122. //京尊达
  123. {name:'京尊达',nameN:'非京尊达',lv:1,conditions:[
  124. {type:'attr',tag:'i',attr:'data-tips',vals:[
  125. '专人配送,尊贵体验'
  126. ]}
  127. ]},
  128.  
  129. //预约抢购
  130. {name:'预约抢购',nameN:'非预约抢购',conditions:[
  131. {type:'multi',vals:[
  132. {tag:'div',cls:'p-presell-time',textAny:['预约']},
  133. ]},
  134. ]},
  135.  
  136. //秒杀
  137. {name:'秒杀',nameN:'非秒杀',lv:1,conditions:[
  138. {type:'attr',tag:'i',attr:'data-tips',vals:[
  139. '天天低价,正品保证'
  140. ]}
  141. ]},
  142.  
  143. //闪购
  144. {name:'闪购',nameN:'非闪购',lv:1,conditions:[
  145. {type:'attr',tag:'i',attr:'data-tips',vals:[
  146. '品牌限时特卖'
  147. ]}
  148. ]},
  149.  
  150. //定期购
  151. {name:'定期购',nameN:'非定期购',conditions:[
  152. {type:'attr',tag:'i',attr:'data-tips',vals:[
  153. '定期免运费,省心又优惠'
  154. ]}
  155. ]},
  156.  
  157. //全球购
  158. {name:'全球购',nameN:'非全球购',conditions:[
  159. {type:'text',tag:'span',vals:[
  160. '全球购'
  161. ]}
  162. ]},
  163.  
  164. //包税
  165. {name:'包税',nameN:'不包税',conditions:[
  166. {type:'attr',tag:'i',attr:'data-tips',vals:[
  167. '本商品已包税'
  168. ]}
  169. ]},
  170.  
  171. //新品
  172. {name:'新品',nameN:'非新品',lv:1,conditions:[
  173. {type:'attr',tag:'i',attr:'data-tips',vals:[
  174. '该商品是当季新品'
  175. ]}
  176. ]},
  177.  
  178. //商场同款
  179. {name:'商场同款',nameN:'非商场同款',lv:1,conditions:[
  180. {type:'attr',tag:'i',attr:'data-tips',vals:[
  181. '该商品是商场同款'
  182. ]}
  183. ]},
  184.  
  185. //京东精选
  186. {name:'京东精选',nameN:'非京东精选',lv:1,conditions:[
  187. {type:'attr',tag:'img',attr:'src',vals:[
  188. '//img14.360buyimg.com/uba/jfs/t6919/268/501386350/1257/92e5fb39/5976fcf9Nd915775f.png'
  189. ]},
  190. {type:'attr',tag:'img',attr:'data-tips',vals:[
  191. '京东精选'
  192. ]}
  193. ]},
  194.  
  195. //品质优选
  196. {name:'品质优选',nameN:'非品质优选',lv:1,conditions:[
  197. {type:'attr',tag:'i',attr:'data-tips',vals:[
  198. '只为品质生活'
  199. ]}
  200. ]},
  201.  
  202. //京东超市
  203. {name:'京东超市',nameN:'非京东超市',lv:1,conditions:[
  204. {type:'text',tag:'span',vals:[
  205. '京东超市'
  206. ]}
  207. ]},
  208.  
  209. //Sam's
  210. {name:'山姆会员',nameN:'非山姆会员',lv:1,conditions:[
  211. {type:'shop',vals:[
  212. '山姆会员商店官方旗舰店',
  213. '山姆会员商店全球购官方旗舰店'
  214. ]},
  215. {type:'multi',vals:[
  216. {tag:'span',cls:'price-sams-1'},
  217. {tag:'em',textStart:'¥'}
  218. ]},
  219. ]},
  220.  
  221. //有机
  222. {name:'有机',nameN:'非有机',conditions:[
  223. {type:'attr',tag:'i',attr:'data-tips',vals:[
  224. '有机'
  225. ]}
  226. ]},
  227.  
  228. //绿色
  229. {name:'绿色',nameN:'非绿色',conditions:[
  230. {type:'attr',tag:'i',attr:'data-tips',vals:[
  231. '绿色'
  232. ]}
  233. ]},
  234.  
  235. //无公害
  236. {name:'无公害',nameN:'非无公害',conditions:[
  237. {type:'text',tag:'i',vals:[
  238. '无公害'
  239. ]}
  240. ]},
  241.  
  242. //三同
  243. {name:'三同',nameN:'非三同',conditions:[
  244. {type:'attr',tag:'i',attr:'data-tips',vals:[
  245. '三同'
  246. ]}
  247. ]},
  248.  
  249. //京东检测
  250. {name:'京东检测',nameN:'非京东检测',conditions:[
  251. {type:'attr',tag:'i',attr:'data-tips',vals:[
  252. '京东检测'
  253. ]}
  254. ]},
  255.  
  256. //双11保价
  257. {name:'双11保价',nameN:'无双11保价',conditions:[
  258. {type:'attr',tag:'i',attr:'data-tips',vals:[
  259. '商品当前价格已与11.11当天一致,买贵返还差额'
  260. ]}
  261. ]},
  262.  
  263. //老字号
  264. {name:'老字号',nameN:'非老字号',conditions:[
  265. {type:'text',tag:'i',vals:[
  266. '老字号'
  267. ]}
  268. ]},
  269.  
  270. //地标产品
  271. {name:'地标产品',nameN:'非地标产品',conditions:[
  272. {type:'attr',tag:'i',attr:'data-tips',vals:[
  273. '地标产品'
  274. ]}
  275. ]},
  276.  
  277. //ASC
  278. {name:'ASC',nameN:'非ASC',conditions:[
  279. {type:'attr',tag:'i',attr:'data-tips',vals:[
  280. 'ASC'
  281. ]}
  282. ]},
  283.  
  284. //BAP
  285. {name:'BAP',nameN:'非BAP',conditions:[
  286. {type:'attr',tag:'i',attr:'data-tips',vals:[
  287. 'BAP'
  288. ]}
  289. ]},
  290.  
  291. //海产品捕捞认证
  292. {name:'海捕证',nameN:'无海捕证',conditions:[
  293. {type:'text',tag:'i',vals:[
  294. '海产品捕捞认证'
  295. ]}
  296. ]},
  297.  
  298. //商家客服
  299. {name:'商家客服',nameN:'无商家客服',lv:1,conditions:[
  300. {type:'attr',tag:'b',attr:'title',vals:[
  301. '联系客服'
  302. ]},
  303. {type:'attr',tag:'em',attr:'title',vals:[
  304. '联系供应商进行咨询',
  305. '联系第三方卖家进行咨询',
  306. '供应商客服不在线,可留言'
  307. ]}
  308. ]},
  309.  
  310. //抢购中
  311. {name:'抢购中',nameN:'非抢购中',conditions:[
  312. {type:'multi',vals:[
  313. {tag:'div',cls:'p-presell-time'},
  314. {tag:'span',textAny:['抢购中']}
  315. ]}
  316. ]},
  317.  
  318. //有货
  319. {name:'有货',nameN:'无货',conditions:[
  320. {type:'multi',not:true,vals:[
  321. {tag:'div',cls:'p-stock',textAny:['无货']}
  322. ]}
  323. ]},
  324.  
  325. //预订
  326. {name:'预订',nameN:'非预订',conditions:[
  327. {type:'multi',vals:[
  328. {tag:'div',cls:'p-stock',textAny:['预订']}
  329. ]}
  330. ]},
  331.  
  332. //配送
  333. {name:'支持配送',nameN:'不支持配送',conditions:[
  334. {type:'multi',not:true,vals:[
  335. {tag:'div',cls:'p-stock',textAny:['不支持配送']}
  336. ]}
  337. ]},
  338.  
  339. //二手有售
  340. {name:'二手有售',nameN:'非二手有售',conditions:[
  341. {type:'multi',vals:[
  342. {tag:'a',cls:'spu-link',texts:['二手有售']}
  343. ]}
  344. ]},
  345.  
  346. //二手
  347. {name:'二手',nameN:'非二手',conditions:[
  348. {type:'multi',vals:[
  349. {tag:'span',cls:'p-tag',texts:['拍拍']}
  350. ]}
  351. ]},
  352.  
  353. //2人拼
  354. {name:'2人拼',nameN:'非2人拼',conditions:[
  355. {type:'text',tag:'span',vals:[
  356. '2人拼'
  357. ]}
  358. ]},
  359.  
  360. //放心购
  361. {name:'放心购',nameN:'非放心购',conditions:[
  362. {type:'text',tag:'i',vals:[
  363. '放心购'
  364. ]}
  365. ]},
  366.  
  367. //爱心东东
  368. {name:'爱心东东',nameN:'非爱心东东',conditions:[
  369. {type:'text',tag:'span',vals:[
  370. '爱心东东'
  371. ]}
  372. ]},
  373.  
  374. //广告
  375. {name:'广告',nameN:'非广告',conditions:[
  376. {type:'text',tag:'span',vals:[
  377. '广告'
  378. ]}
  379. ]},
  380.  
  381. //本地仓
  382. {name:'本地仓',nameN:'非本地仓',conditions:[
  383. {type:'text',tag:'i',vals:[
  384. '本地仓'
  385. ]}
  386. ]},
  387.  
  388. //商品名匹配
  389. {name:'商品名匹配',nameN:'商品名不匹配',conditions:[
  390. {type:'multi',vals:[
  391. {tag:'div',cls:'p-name'},
  392. {tag:'em'}
  393. ]}],
  394. _hlInfo:{ //私有数据
  395. tag: 'b',
  396. cls: 'hl',
  397. getEmTag: function(elem) {
  398. return elem.emTag || (elem.emTag = elem.getElementsByClassName('p-name')[0].getElementsByTagName('em')[0]);
  399. }
  400. },
  401. onPassed: function(elem) { //过滤器命中时执行:先取消高亮,再重新高亮
  402. if (!unsafeWindow.x$.highlightGoodsPattern) return this.onMissed(elem);
  403. var hlInfo = this._hlInfo;
  404. var em = hlInfo.getEmTag(elem);
  405. var condVal = this.conditions[0].vals[1];
  406. var match = condVal.textAny || condVal.textAll || condVal.textRex;
  407. if (!match) return this.onMissed(elem);
  408. var hlMatch = elem.highlighted;
  409. var html = em.innerHTML;
  410. var hlHtml = hlMatch === undefined ?
  411. highlightHtml(html, match, hlInfo) : //新加高亮
  412. replaceHighlightHtml(html, match, hlMatch, hlInfo); //已经高亮过,尝试替换
  413. if (hlHtml !== null) {
  414. elem.highlighted = match;
  415. em.innerHTML = hlHtml;
  416. }
  417. },
  418. onMissed: function(elem) { //过滤器未命中时执行:取消高亮
  419. if (!elem.highlighted) return;
  420. var hlInfo = this._hlInfo;
  421. var em = hlInfo.getEmTag(elem);
  422. var html = unhighlightHtml(em.innerHTML, hlInfo);
  423. if (html) em.innerHTML = html;
  424. delete elem.highlighted;
  425. },
  426. init: function() { //过滤器初始化,只运行一次
  427. var hlInfo = this._hlInfo;
  428. hlInfo.tag0 = [];
  429. for(var i=1; i<=9; ++i) hlInfo.tag0.push('<'+hlInfo.tag+' class="'+hlInfo.cls+i+'">');
  430. hlInfo.tag1 = '</'+hlInfo.tag+'>';
  431. }
  432. }
  433. ];
  434. //过滤器处理程序
  435. var goodsFilterMap = {
  436. attr: function(elem, cond) {
  437. var tags = elem.getElementsByTagName(cond.tag);
  438. for (var i=0; i<tags.length; ++i) {
  439. var attr = tags[i].getAttribute(cond.attr);
  440. if (attr !== null && includesAny(attr, cond.vals)) return true;
  441. }
  442. return false;
  443. },
  444. text: function(elem, cond) {
  445. var tags = elem.getElementsByTagName(cond.tag);
  446. for (var i=0; i<tags.length; ++i) {
  447. var text = tags[i].innerText;
  448. if (text.length > 0 && (text = text.trim()).length > 0 && includesAny(text, cond.vals)) return true;
  449. }
  450. return false;
  451. },
  452. multi: function(elem, cond) {
  453. return filterTag(elem, cond.vals, 0);
  454. function filterTag(elem, conds, ci) {
  455. var cond = conds[ci];
  456. var tags = elem.getElementsByTagName(cond.tag);
  457. for (var i = 0; i < tags.length; ++i) {
  458. var tag = tags[i];
  459. if (cond.cls && !hasClasses(tag, cond.cls)) continue;
  460. //if (cond.attr && !hitAttributes(tag, cond.attr)) continue;
  461. var text = tag.innerText.trim();
  462. if (cond.texts && (text.length === 0 || !isAny(text, cond.texts))) continue;
  463. if (cond.textAny && (text.length === 0 || !includesAny(text, cond.textAny))) continue;
  464. if (cond.textAll && (text.length === 0 || !includesAll(text, cond.textAll))) continue;
  465. if (cond.textStart && (text.length === 0 || !text.startsWith(cond.textStart))) continue;
  466. // if (cond.textEnd && (text.length === 0 || !text.endsWith(cond.textEnd))) continue;
  467. // if (cond.textSub && (text.length === 0 || !text.includes(cond.textSub))) continue;
  468. if (cond.textRex && (text.length === 0 || (cond.textRex.lastIndex = 0) || !cond.textRex.test(text))) continue;
  469. return (++ci === conds.length) || filterTag(tag, conds, ci);
  470. }
  471. return false;
  472. function hasClasses(e,c) {
  473. if (!(c instanceof Array)) return e.classList.contains(c);
  474. for (var i=0; i<c.length; ++i) if (!e.classList.contains(c[i])) return false;
  475. return true;
  476. }
  477. // function hitAttributes(e,c) {
  478. // if (!(c instanceof Array)) return hitAttribute(e, c);
  479. // for (var i=0; i<c.length; ++i) if (!hitAttribute(e, c[i])) return false;
  480. // return true;
  481. // function hitAttribute(e,c) {
  482. // var a = e.getAttribute(c.attr);
  483. // return a !== null && includesAny(a, c.vals);
  484. // }
  485. // }
  486. }
  487. },
  488. shop: function(elem, cond) {
  489. var tags = elem.getElementsByTagName('div');
  490. for (var i=0; i<tags.length; ++i) {
  491. var tag = tags[i];
  492. if (tag.classList.contains('p-shop')) {
  493. var tags2 = tag.getElementsByTagName('a');
  494. for (var j=0; j<tags2.length; ++j) {
  495. var title = tags2[j].title;
  496. if (title.length > 0 && (title = title.trim()).length > 0 && cond.vals.includes(title)) return true;
  497. }
  498. }
  499. }
  500. return false;
  501. }//,
  502. // fn: function(elem, cond) {
  503. // for (var i=0; i<cond.vals.length; ++i) {
  504. // if (cond.vals[i](elem, cond)) return true;
  505. // }
  506. // return false;
  507. // }
  508. };
  509.  
  510. //防止页面重入
  511. if (document.getElementById('goodsFilter'+jdGoodsFilters[0].name) !== null) return;
  512.  
  513. //扫描页面的间隔频率时间
  514. var timerFreq = 333;
  515. //当前页面路径
  516. var currentPathname = location.pathname.toLowerCase();
  517. if (currentPathname.startsWith('/pinpai/')) currentPathname = '/search';
  518. //京东自带过滤器和排序选项
  519. var jdFilters = {
  520. 京东配送: {type:0, gm_name:'京东配送', val:'1'},
  521. 货到付款: {type:0, gm_name:'货到付款', val:'1'},
  522. 仅显示有货: {type:0, gm_name:'仅显示有货', name:'stock',val:'1',def:'0'},
  523. 排序: {type:1, gm_name:'排序', vals:[
  524. //综合没有值
  525. 'sort_dredisprice_desc',
  526. 'sort_dredisprice_asc',
  527. 'sort_totalsales15_desc',
  528. 'sort_commentcount_desc',
  529. 'sort_winsdate_desc'
  530. ]}
  531. };
  532. //检查插件数据
  533. CheckSettings(true);
  534. //通过重定向应用保存的京东自带过滤器和排序选项(v0.5.3.3 - jd更新【京东配送】【货到付款】等选项的参数后,某些情况商品页面会自动跳转到登录页,此版中暂时禁用。很久以后有时间了再修吧)
  535. //var newSearch = OnDocStart();
  536. //if (newSearch !== undefined) location.search = newSearch;
  537. //else
  538. setTimeout(OnDocReady, 0);
  539.  
  540. //载入【京东配送】【仅显示有货】和排序的设置,返回重定向的路径
  541. function OnDocStart() {
  542. switch (currentPathname) {
  543. case '/search':
  544. jdFilters.京东配送.name = 'wtype';
  545. jdFilters.货到付款.name = 'cod';
  546. jdFilters.排序.name = 'psort';
  547. break;
  548.  
  549. case '/list.html':
  550. jdFilters.京东配送.name = 'delivery';
  551. jdFilters.货到付款.name = 'delivery_daofu';
  552. jdFilters.货到付款.val = '3';
  553. jdFilters.排序.name = 'sort';
  554. break;
  555. }
  556. var urlOpts = url2Obj();
  557. if (readOpts(urlOpts)) {
  558. var newSearch = '?';
  559. for (var opt in urlOpts) {
  560. if (newSearch.length > 1) newSearch += '&';
  561. newSearch += opt;
  562. newSearch += '=';
  563. newSearch += urlOpts[opt];
  564. }
  565. return newSearch.length > 1 ? newSearch : '';
  566. }
  567.  
  568. function url2Obj() {
  569. var ret = {};
  570. var u = location.search.split(/[\?&]/);
  571. for (var i in u) {
  572. var opt = u[i];
  573. if (opt.length > 0) {
  574. opt = opt.split('=');
  575. ret[opt[0]] = opt[1];
  576. }
  577. }
  578. return ret;
  579. }
  580. function readOpts(opts) {
  581. var modified;
  582. for (var optKey in jdFilters) {
  583. var opt = jdFilters[optKey];
  584. switch (opt.type) {
  585. case 0: modified |= readFilterOpt(opts, opt); break;
  586. case 1: modified |= readSortOpt(opts, opt); break;
  587. }
  588. }
  589. return modified;
  590.  
  591. function readFilterOpt(opts, opt) {
  592. if (GM_getValue(opt.gm_name)) { //有保存的值
  593. if (!opts.hasOwnProperty(opt.name) || opts[opt.name] !== opt.val) {
  594. opts[opt.name] = opt.val;
  595. return true;
  596. }
  597. } else { //没有保存的值
  598. if (opt.def !== undefined) { //不允许url不带该参数
  599. if (opts.hasOwnProperty(opt.name)) {
  600. if (opts[opt.name] !== opt.def) {
  601. opts[opt.name] = opt.def;
  602. return true;
  603. }
  604. } else {
  605. opts[opt.name] = opt.def;
  606. return true;
  607. }
  608. } else if (opts.hasOwnProperty(opt.name) && opts[opt.name] !== '0') {
  609. delete opts[opt.name];
  610. return true;
  611. }
  612. }
  613. return false;
  614. }
  615. function readSortOpt(opts, opt) {
  616. var sortOpt = GM_getValue(opt.gm_name);
  617. switch (currentPathname) {
  618. case '/search':
  619. if (sortOpt !== undefined) ++sortOpt;
  620. break;
  621.  
  622. case '/list.html':
  623. sortOpt = jdFilters.排序.vals[sortOpt];
  624. break;
  625. }
  626.  
  627. if (sortOpt !== undefined) {
  628. if (!opts.hasOwnProperty(opt.name) || urlOpts[opt.name] != sortOpt) {
  629. urlOpts[opt.name] = sortOpt;
  630. return true;
  631. }
  632. } else if (opts.hasOwnProperty(opt.name)) {
  633. delete opts[opt.name];
  634. return true;
  635. }
  636. return false;
  637. }
  638. }
  639. }
  640. //载入页面后,为页面增加【自营】【非自营】等过滤器
  641. function OnDocReady() {
  642. //防止页面重入
  643. if (document.getElementById('goodsFilter'+jdGoodsFilters[0].name) !== null) return;
  644.  
  645. runMain();
  646.  
  647. //页面入口点
  648. function runMain() {
  649. if ($ && $.fn && $.fn.jquery) {
  650. enableAllFilters();
  651. loadAllSettings();
  652. initAllFilters();
  653. uiInitAndLoadAllGoods();
  654. } else setTimeout(runMain, timerFreq);
  655.  
  656. function enableAllFilters() {
  657. for (var i=0; i<jdGoodsFilters.length; ++i) if (jdGoodsFilters[i].lv === undefined) jdGoodsFilters[i].lv = 1;
  658. }
  659. //加载设置
  660. function loadAllSettings() {
  661. unsafeWindow.x$ = { //init:设置数据缓冲
  662. infoLogText: '正在等待商品列表加载,请稍候……'
  663. };
  664. unsafeWindow.y$ = {}; //init:设置控件引用缓冲
  665. unsafeWindow.z$ = {}; //init:设置临时缓冲
  666.  
  667. loadAllFiltersStates(); //加载过滤器选中状态
  668. loadFilterMode(); //加载过滤方式
  669. loadMisc(); //加载其他设置
  670.  
  671. function loadAllFiltersStates() {
  672. for (var i=0; i<jdGoodsFilters.length; ++i) {
  673. var f = jdGoodsFilters[i];
  674. //加载启用状态
  675. f.level = GM_getValue('level'+f.name);
  676. if (f.level === undefined && f.lv !== undefined) f.level = f.lv;
  677. //加载选中状态
  678. f.checked = undefined === GM_getValue(f.name);
  679. f.checkedN = undefined === GM_getValue(f.nameN);
  680. }
  681. //【商品名匹配使用正则】设置
  682. var useRegex = GM_getValue('商品名匹配正则');
  683. if (useRegex !== undefined) unsafeWindow.x$.useRegexChecked = true; //init:商品名匹配正则
  684. //【商品名匹配高亮】设置
  685. if (GM_getValue('商品名匹配不高亮') === undefined) unsafeWindow.x$.highlightGoodsPattern = true; //init:商品名匹配高亮
  686.  
  687. //【商品名匹配】过滤器
  688. var cond = jdGoodsFilters[jdGoodsFilters.findIndex(function(j) {return j.name === '商品名匹配';})].conditions[0].vals[1];
  689. var pattern = GM_getValue('商品名匹配规则');
  690. if (pattern !== undefined) {
  691. unsafeWindow.x$.goodsPattern = pattern;
  692. if (useRegex) cond.textRex = ToRegex(pattern);
  693. else {
  694. var txtCond = ToTextCond(pattern);
  695. if (txtCond.allMatch) cond.textAll = txtCond.txts;
  696. else cond.textAny = txtCond.txts;
  697. }
  698. }
  699. unsafeWindow.x$.goodsPatternCondition = cond; //init:商品名匹配规则
  700. }
  701. function loadFilterMode() {
  702. unsafeWindow.x$.filterModes = [ //init:设置过滤方式字典
  703. 'akari',
  704. 'haruhi',
  705. 'cirno',
  706. 'yuno',
  707. 'mizuki',
  708. 'burnIt',
  709. 'succubus',
  710. 'conan',
  711. 'hphphp'
  712. ];
  713. unsafeWindow.x$.goodsFilterModeIndex = GM_getValue('过滤方式'); //init:过滤方式索引
  714. if (unsafeWindow.x$.goodsFilterModeIndex === undefined) unsafeWindow.x$.goodsFilterModeIndex = 0; //init:过滤方式索引没有保存
  715. else if (unsafeWindow.x$.goodsFilterModeIndex < 0 || unsafeWindow.x$.goodsFilterModeIndex >= unsafeWindow.x$.filterModes.length) unsafeWindow.x$.goodsFilterModeIndex = 0; //init:过滤方式索引超限
  716. unsafeWindow.x$.goodsFilterModeClass = unsafeWindow.x$.filterModes[unsafeWindow.x$.goodsFilterModeIndex]; //init:过滤方式的className
  717.  
  718. if (GM_getValue('后排') !== undefined) unsafeWindow.x$.filteredToLastChecked = true; //init:后排设置
  719. }
  720. function loadMisc() {
  721. if (GM_getValue('老爷机不要动画') !== undefined) unsafeWindow.x$.noAnimateChecked = true; //init:关闭动画设置
  722. }
  723. }
  724. //初始化过滤器
  725. function initAllFilters() {
  726. for (var i=0; i<jdGoodsFilters.length; ++i) {
  727. var f = jdGoodsFilters[i];
  728. if (f.init !== undefined) f.init();
  729. }
  730. }
  731. }
  732. //向页面添加自营过滤选项
  733. function uiInitAndLoadAllGoods() {
  734. var uiPos = $(document.querySelector('.f-feature ul'));
  735. if (uiPos.length > 0) {
  736. //改造京东自身过滤器和排序按钮
  737. hookJdFilters();
  738. //向页面添加过滤器
  739. addFiltersParts();
  740. //分页面情况处理后续是否加载商品
  741. switch (currentPathname) {
  742. case '/search':
  743. if (document.querySelector('div.notice-filter-noresult') !== null) { //搜索没有结果
  744. updateInfoLog('');
  745. return;
  746. }
  747. }
  748. //UI 初始化成功,加载所有商品
  749. loadAllGoods();
  750. } else setTimeout(uiInitAndLoadAllGoods, timerFreq);
  751.  
  752. function hookJdFilters() {
  753. switch (currentPathname) {
  754. case '/search':
  755. //【京东配送】【货到付款】【仅显示有货】【全球配送】【节日促销】按钮重载 searchlog
  756. uiPos.find('li a[onclick]').each(function() {
  757. var tagA = $(this);
  758. if (tagA.attr('onclick').length > 0) this.oldOnClick = this.onclick;
  759. tagA.removeAttr('onclick');
  760. tagA.click(function() {
  761. if (this.oldOnClick !== undefined) {
  762. this.oldOnClick();
  763. var waitAjaxLoadGoodsList = function() {
  764. if (unsafeWindow.SEARCH.loading) setTimeout(waitAjaxLoadGoodsList, timerFreq);
  765. else runMain();
  766. };
  767. setTimeout(waitAjaxLoadGoodsList, timerFreq);
  768. }
  769. });
  770. });
  771.  
  772. //【综合】【销量】【评论】【新品】【价格】排序按钮重载
  773. hookSearchSortHtml();
  774. //翻页按钮重载
  775. hookPager();
  776. //重载搜索排序函数,保存排序选项
  777. hookSearchSort();
  778.  
  779. //【京东配送】【货到付款】【仅显示有货】按钮
  780. jdFilters.京东配送.aTag = uiPos.find('li a[data-field="wtype"]').first();
  781. jdFilters.货到付款.aTag = uiPos.find('li a[data-field="cod"]').first();
  782. jdFilters.仅显示有货.aTag = uiPos.find('li a[data-field="stock"]').first();
  783. break;
  784.  
  785. case '/list.html':
  786. //【综合】【销量】【价格】【评论】【上架】排序按钮重载,保存排序设置
  787. $(document.querySelectorAll('#J_filter div.f-sort a:not([id])')).click(function() {
  788. var aTagSort = $(this);
  789. setTimeout(function() {
  790. var sortOpt = aTagSort.attr('href').match(/[&\?]sort=([^&]*)/i);
  791. if (sortOpt && 2 === sortOpt.length) {
  792. sortOpt = unescape(sortOpt[1]);
  793. for (var i=0; i<jdFilters.排序.vals.length; ++i) {
  794. if (sortOpt === jdFilters.排序.vals[i]) {
  795. GM_setValue(jdFilters.排序.gm_name, i);
  796. break;
  797. }
  798. }
  799. } else GM_deleteValue(jdFilters.排序.gm_name);
  800. }, 0);
  801. });
  802.  
  803. //【京东配送】【货到付款】【仅显示有货】按钮
  804. switch (location.host) {
  805. case 'list.jd.com':
  806. jdFilters.京东配送.aTag = uiPos.find('li#delivery a').first();
  807. jdFilters.货到付款.aTag = uiPos.find('li#delivery_daofu a').first();
  808. jdFilters.仅显示有货.aTag = uiPos.find('li#stock a').first();
  809. break;
  810.  
  811. case 'coll.jd.com':
  812. jdFilters.京东配送.aTag = uiPos.find('li a:contains(京东配送)').first();
  813. jdFilters.货到付款.aTag = uiPos.find('li a:contains(货到付款)').first();
  814. jdFilters.仅显示有货.aTag = uiPos.find('li a:contains(仅显示有货)').first();
  815. break;
  816. }
  817. break;
  818. }
  819. //【京东配送】按钮保存设置
  820. jdFilters.京东配送.aTag.click(function() {
  821. setTimeout(function() {
  822. if (jdFilters.京东配送.aTag.hasClass('selected') ^ (currentPathname === '/search')) GM_deleteValue(jdFilters.京东配送.gm_name);
  823. else GM_setValue(jdFilters.京东配送.gm_name, true);
  824. }, 0);
  825. });
  826. //【货到付款】按钮保存设置
  827. jdFilters.货到付款.aTag.click(function() {
  828. setTimeout(function() {
  829. if (jdFilters.货到付款.aTag.hasClass('selected') ^ (currentPathname === '/search')) GM_deleteValue(jdFilters.货到付款.gm_name);
  830. else GM_setValue(jdFilters.货到付款.gm_name, true);
  831. }, 0);
  832. });
  833. //【仅显示有货】按钮保存设置
  834. jdFilters.仅显示有货.aTag.click(function() {
  835. setTimeout(function() {
  836. if (jdFilters.仅显示有货.aTag.hasClass('selected') ^ (currentPathname === '/search')) GM_deleteValue(jdFilters.仅显示有货.gm_name);
  837. else GM_setValue(jdFilters.仅显示有货.gm_name, true);
  838. }, 0);
  839. });
  840.  
  841. function hookSearchSortHtml() {
  842. if (unsafeWindow.SEARCH !== undefined && unsafeWindow.SEARCH.sort_html !== undefined) {
  843. if (unsafeWindow.SEARCH.oldFunc_sort_html === undefined) {
  844. unsafeWindow.SEARCH.oldFunc_sort_html = unsafeWindow.SEARCH.sort_html;
  845. unsafeWindow.SEARCH.sort_html = function(C) { //重载 SEARCH.sort_html 函数
  846. unsafeWindow.SEARCH.oldFunc_sort_html(C);
  847. //在 loadAllGoods 处理 /search 页面的分支中会调用 SEARCH.scroll 来加载商品列表后半页,
  848. //这会导致 SEARCH.sort_html 被再次调用,如果在重载的函数中再次【直接】【自动】调用 loadAllGoods 会构成递归溢出,
  849. //因此必须将 loadAllGoods 的调用放在排序按钮的 onclick 事件响应里面,并删除原始的 onclick 属性内联调用
  850. $(document.querySelectorAll('#J_filter div.f-sort a[onclick]')).each(function() {
  851. var tagA = $(this);
  852. if (tagA.attr('onclick').length > 0) this.oldOnClick = this.onclick;
  853. tagA.removeAttr('onclick');
  854. tagA.click(function() {
  855. if (this.oldOnClick !== undefined) {
  856. this.oldOnClick();
  857. var waitAjaxLoadGoodsList = function() {
  858. if (unsafeWindow.SEARCH.loading) setTimeout(waitAjaxLoadGoodsList, timerFreq);
  859. else loadAllGoods();
  860. };
  861. setTimeout(waitAjaxLoadGoodsList, timerFreq);
  862. }
  863. });
  864. });
  865. };
  866. }
  867. } else setTimeout(hookSearchSortHtml, timerFreq);
  868. }
  869. function hookPager() {
  870. if (undefined !== unsafeWindow.SEARCH && undefined !== unsafeWindow.SEARCH.page) {
  871. if (unsafeWindow.SEARCH.oldFunc_page === undefined) {
  872. unsafeWindow.SEARCH.oldFunc_page = unsafeWindow.SEARCH.page;
  873. unsafeWindow.SEARCH.page = function(F, C) { //重载 SEARCH.page 函数
  874. unsafeWindow.SEARCH.oldFunc_page(F, C);
  875. //loadAllGoods 不会与 SEARCH.page 发生嵌套循环调用,所以可以在重载函数内直接调用 loadAllGoods
  876. var waitAjaxLoadGoodsList = function() {
  877. if (unsafeWindow.SEARCH.loading) setTimeout(waitAjaxLoadGoodsList, timerFreq);
  878. else loadAllGoods();
  879. };
  880. setTimeout(waitAjaxLoadGoodsList, timerFreq);
  881. };
  882. }
  883. } else setTimeout(hookPager, timerFreq);
  884. }
  885. function hookSearchSort() {
  886. if (unsafeWindow.SEARCH !== undefined && unsafeWindow.SEARCH.sort !== undefined) {
  887. if (unsafeWindow.SEARCH.oldFunc_sort === undefined) {
  888. unsafeWindow.SEARCH.oldFunc_sort = unsafeWindow.SEARCH.sort;
  889. unsafeWindow.SEARCH.sort = function(A) { //重载 SEARCH.sort 函数
  890. unsafeWindow.SEARCH.oldFunc_sort(A);
  891. setTimeout(function() {
  892. if (!A) GM_deleteValue(jdFilters.排序.gm_name); else GM_setValue(jdFilters.排序.gm_name, A-1);
  893. }, 0);
  894. };
  895. }
  896. } else setTimeout(hookSearchSort, timerFreq);
  897. }
  898. }
  899. function addFiltersParts() {
  900. //向页面添加样式表
  901. if (document.getElementById('styleGoodsFilters') === null) GM_addStyle(
  902. //计数器占位符和计数器样式
  903. 'span.goodsCountPlaceholder{width:36px;display:inline-block;}'+
  904. 'span.goodsCount{font-size:10px;color:#fff;background-color:#e23a3a;border-radius:3px;padding:0px 3px;}'+
  905. //顶级过滤器样式
  906. 'div.f-feature>ul>li>a>span.goodsCount{margin-left:2px;}'+
  907. //过滤器样式(必须带【.filter .f-feature ul li】前置,否则会被京东自身样式覆盖)
  908. '.filter .f-feature ul li a{padding-right:8px;}'+
  909. '.filter .f-feature ul li a.filterN{color:#888;}'+
  910. '.filter .f-feature ul li a.filterN:hover{color:#e23a3a;}'+
  911. '.filter .f-feature ul li a.filterN.selected i{border-color:rgba(228,57,60,0.3);}'+
  912. '.filter .f-feature ul li a.filterN.selected:hover i{border-color:#e4393c;}'+
  913. '.filter .f-feature ul li a.zeroCountGoods{color:#ccc;}'+
  914. '.filter .f-feature ul li a.zeroCountGoods.selected i{border-color:#ccc;}'+
  915. '.filter .f-feature ul li a.zeroCountGoods span.goodsCount{background-color:#ccc;}'+
  916. '.filter .f-feature ul li a.zeroCountGoods:hover{color:#e23a3a;}'+
  917. '.filter .f-feature ul li a.zeroCountGoods.selected:hover i{border-color:#e4393c;}'+
  918. //【下级过滤器】按钮
  919. '.filter .f-feature ul li.showMoreFilters{padding-right:12px;}'+
  920. '.filter .f-feature ul li.showMoreFilters a{padding:0 2px;color:#e23a3a;line-height:normal;border:1px solid #e23a3a;border-radius:3px;}'+
  921. '.filter .f-feature ul li.showMoreFilters a.opened{color:white;background:#e23a3a;}'+
  922. //过滤器数量蓝点
  923. '.filter .f-feature ul li.showMoreFilters a #moreFiltersCount{color:white;background:dodgerblue;position:absolute;top:-4px;right:-10px;width:16px;height:16px;line-height:15px;border-radius:8px;font-size:8px;text-align:center;user-select:none;cursor:pointer;}'+
  924. //过滤器面板
  925. '#moreFiltersPanel{border:1px solid #E7E3E7;z-index:0;}'+
  926. '#moreFiltersPanel *{white-space:nowrap;}'+
  927. //过滤器表格和动作条分割线
  928. '#moreFiltersPanel hr{margin:0 5px;border:0;border-top:1px solid #E7E3E7;}'+
  929. //过滤器面板div布局
  930. '#moreFiltersPanel div{display:flex;justify-content:center;align-items:center;}'+
  931. //过滤器表格样式
  932. 'table.f-feature{margin:3px 0;border-collapse:collapse;}'+
  933. 'table.f-feature td{padding:0 5px;text-align:center;}'+
  934. //过滤器组分割线
  935. 'table.f-feature td:nth-child(3n+4){border-left:1px solid #E7E3E7;}'+
  936. //左侧过滤器样式
  937. 'table.f-feature td:nth-child(3n+1){text-align:right;}'+
  938. 'table.f-feature td:nth-child(3n+1) ul li a{padding:0;}'+
  939. 'table.f-feature td:nth-child(3n+1) ul li a i{margin:0 0 0 4px;position:relative;top:3px;left:auto;right:0;}'+
  940. //右侧过滤器样式
  941. 'table.f-feature td:nth-child(3n+3){text-align:left;}'+
  942. 'table.f-feature td:nth-child(3n+3) ul li a{padding:0;}'+
  943. 'table.f-feature td:nth-child(3n+3) ul li a i{margin:0 4px 0 0;position:relative;top:3px;left:0;right:auto;}'+
  944. //右侧过滤器计数样式
  945. 'a.filterN span.goodsCount{background-color:rgba(226,58,58,0.3);}'+
  946. 'a.filterN:hover span.goodsCount{background-color:#e23a3a;}'+
  947. //自定义匹配
  948. '#regFilter{margin:0 5px;}'+
  949. '#regFilter .txt{margin:5px 0;padding:0 2px;width:50%;border:1px solid #E7E3E7;font-size:12px;color:#666;}'+
  950. '.gl-item .p-name em b{font-weight:normal;}'+
  951. '.gl-item .p-name em .hl1{background:hsla(0,100%,50%,0.5);}'+
  952. '.gl-item .p-name em .hl2{background:hsla(200,100%,50%,0.5);}'+
  953. '.gl-item .p-name em .hl3{background:hsla(40,100%,50%,0.5);}'+
  954. '.gl-item .p-name em .hl4{background:hsla(240,100%,50%,0.5);}'+
  955. '.gl-item .p-name em .hl5{background:hsla(80,100%,50%,0.5);}'+
  956. '.gl-item .p-name em .hl6{background:hsla(280,100%,50%,0.5);}'+
  957. '.gl-item .p-name em .hl7{background:hsla(120,100%,50%,0.5);}'+
  958. '.gl-item .p-name em .hl8{background:hsla(320,100%,50%,0.5);}'+
  959. '.gl-item .p-name em .hl9{background:hsla(160,100%,50%,0.5);}'+
  960. //动作条按钮
  961. '#moreFiltersPanel a.btn.btn-default{margin:5px;}'+
  962. '#moreFiltersPanel a.btn.btn-default.btn-icon{padding:4px 5px;}'+
  963. '#moreFiltersPanel a.btn-icon{margin:5px;height:25px;}'+
  964. //动作条checkbox
  965. '#moreFiltersPanel .ckbox{margin:1px 0 0 6px;}'+
  966. //动作条图标
  967. '#actionBar i{display:inline-block;width:25px;height:25px;background:url(https://misc.360buyimg.com/user/passport/1.0.0/widget/login-form-2016-1124/i/qr-coagent.png) no-repeat;}'+
  968. //动作条提示
  969. '#actionBar i.info{margin:0 5px;background-position-x:-27px;}'+
  970. '#actionBar #infoLog{color:blue;font-weight:bold;}'+
  971. //动作条下拉选择框
  972. '#actionBar select{padding:0 20px 0 3px;border:1px solid #E7E3E7;font-size:12px;color:#666;-webkit-appearance: none;background:url(//misc.360buyimg.com/product/list/1.0.7/css/i/search.ele.png) no-repeat 78px 5px;}'+
  973. '#actionBar select:hover{background-position-y:-12px;}'+
  974. //被过滤商品的样式
  975. 'li.gl-item:hover{filter:none;}'+
  976. 'li.gl-item:hover .gl-i-wrap .p-img{filter:none;}'+
  977. //样式0:淡化
  978. 'li.akari{background-color:#fff;filter:opacity(10%);}'+
  979. 'li.akari:hover .gl-i-wrap div{filter:opacity(30%);}'+
  980. '.goods-list-v1 .gl-item.akari:hover{border-color:#ccc;}'+
  981. '.goods-list-v2 .gl-item.akari:hover .gl-i-wrap {border-color:#ccc;}'+
  982. //样式1:隐藏
  983. 'li.haruhi{display:none;}'+
  984. //样式2:边框
  985. 'li.cirno{background-color:#fff;}'+
  986. 'li.cirno:hover .gl-i-wrap div{filter:opacity(30%);}'+
  987. '.goods-list-v1 .gl-item.cirno{border-color:#000;}'+
  988. '.goods-list-v1 .gl-item.cirno:hover{border-color:#48f;}'+
  989. '.goods-list-v2 .gl-item.cirno .gl-i-wrap {border-color:#000;}'+
  990. '.goods-list-v2 .gl-item.cirno:hover .gl-i-wrap {border-color:#48f;}'+
  991. //样式3:黑白
  992. 'li.yuno{background-color:#fff;filter:grayscale(100%) brightness(60%);}'+
  993. 'li.yuno:hover .gl-i-wrap div{filter:grayscale(100%);}'+
  994. '.goods-list-v1 .gl-item.yuno:hover{border-color:#888;}'+
  995. '.goods-list-v2 .gl-item.yuno:hover .gl-i-wrap {border-color:#888;}'+
  996. //样式4:转色
  997. 'li.mizuki{background-color:#fff;filter:hue-rotate(180deg);}'+
  998. 'li.mizuki:hover .gl-i-wrap div{filter:hue-rotate(180deg);}'+
  999. '.goods-list-v1 .gl-item.mizuki:hover{border-color:#3af;}'+
  1000. '.goods-list-v2 .gl-item.mizuki:hover .gl-i-wrap {border-color:#3af;}'+
  1001. //样式5:异端通通烧死
  1002. 'li.burnIt{background-color:#fff;filter:sepia(100%);}'+
  1003. 'li.burnIt:hover .gl-i-wrap div{filter:sepia(100%);}'+
  1004. '.goods-list-v1 .gl-item.burnIt:hover{border-color:#e85;}'+
  1005. '.goods-list-v2 .gl-item.burnIt:hover .gl-i-wrap {border-color:#e85;}'+
  1006. //样式6:模糊
  1007. 'li.succubus{background-color:#fff;filter:blur(5px);}'+
  1008. 'li.succubus:hover .gl-i-wrap div{filter:blur(1.2px);}'+
  1009. '.goods-list-v1 .gl-item.succubus:hover{border-color:#f3c;}'+
  1010. '.goods-list-v2 .gl-item.succubus:hover .gl-i-wrap {border-color:#f3c;}'+
  1011. //样式7:反色
  1012. 'li.conan{background-color:#fff;filter:invert(100%);}'+
  1013. 'li.conan:hover .gl-i-wrap div{filter:invert(100%);}'+
  1014. '.goods-list-v1 .gl-item.conan:hover{border-color:#000;}'+
  1015. '.goods-list-v2 .gl-item.conan:hover .gl-i-wrap {border-color:#000;}'+
  1016. //样式8:暗刻
  1017. 'li.hphphp{background-color:#fff;filter:grayscale(10%) contrast(50) sepia(100%) saturate(100) hue-rotate(180deg) invert(100%) blur(1px);}'+
  1018. 'li.hphphp:hover .gl-i-wrap div{filter:grayscale(10%) contrast(50) sepia(100%) saturate(100) hue-rotate(180deg) invert(100%) blur(1px);}'+
  1019. '.goods-list-v1 .gl-item.hphphp:hover{border-color:#000;}'+
  1020. '.goods-list-v2 .gl-item.hphphp:hover .gl-i-wrap {border-color:#000;}'+
  1021. ''
  1022. ).id = 'styleGoodsFilters';
  1023. //生成顶级过滤器HTML
  1024. var uiPrependFilters = '';
  1025. for (var i=0; i<jdGoodsFilters.length; ++i) {
  1026. var f = jdGoodsFilters[i];
  1027. if (f.level !== 0) continue; //剔除不是顶级的
  1028.  
  1029. var checkedClass = f.checked ? 'class="selected"' : '';
  1030. var checkedNClass = f.checkedN ? 'class="filterN selected"' : 'class="filterN"';
  1031. uiPrependFilters +=
  1032. '<li><a '+checkedClass+' href="javascript:;" id="goodsFilter'+f.name+'" filterId="'+i+'">'+
  1033. '<i></i>'+f.name+'<span class="goodsCount" id="goodsCount'+f.name+'">0</span>'+
  1034. '</a></li>'+
  1035. '<li><a '+checkedNClass+' href="javascript:;" id="goodsFilter'+f.nameN+'" filterId="'+i+'">'+
  1036. '<i></i>'+f.nameN+'<span class="goodsCount" id="goodsCount'+f.nameN+'">0</span>'+
  1037. '</a></li>';
  1038. }
  1039. //生成【下级过滤器】按钮HTML
  1040. uiPrependFilters +=
  1041. '<li class="showMoreFilters">'+
  1042. '<a id="showMoreFiltersPanel" href="javascript:;">'+
  1043. '下级过滤器'+
  1044. '<span id="moreFiltersCount" class="zeroFiltersCount" style="display:none;">0</span>'+
  1045. '</a>'+
  1046. '</li>';
  1047. //添加顶级过滤器和【下级过滤器】按钮
  1048. uiPos.first().prepend($(uiPrependFilters));
  1049. //保存控件引用
  1050. unsafeWindow.y$.aShowMoreFiltersPanel = $(document.getElementById('showMoreFiltersPanel'));
  1051. unsafeWindow.y$.spanMoreFiltersCount = $(document.getElementById('moreFiltersCount'));
  1052. //更新过滤器计数
  1053. var spanMoreFiltersCount = updateUsingMoreFiltersCount(); //添加顶级过滤器和【下级过滤器】按钮
  1054. //安装顶级过滤器响应函数
  1055. installFilterClickHandler(0);
  1056. //安装【下级过滤器】按钮响应函数
  1057. unsafeWindow.y$.aShowMoreFiltersPanel.click(function() {
  1058. setTimeout(toggleMoreFiltersPanel, 0);
  1059. });
  1060. //根据保存的设置打开下级过滤器面板
  1061. if (GM_getValue('打开下级过滤器面板') !== undefined) toggleMoreFiltersPanel(true);
  1062. //否则根据计数显示过滤器计数圆点
  1063. else if (!spanMoreFiltersCount.hasClass('zeroFiltersCount')) spanMoreFiltersCount.css('display', ''); //初始化不用动画
  1064. //安装过滤器响应函数
  1065. function installFilterClickHandler(level) {
  1066. for (var i=0; i<jdGoodsFilters.length; ++i) {
  1067. var f = jdGoodsFilters[i];
  1068. if (f.level !== level) continue; //剔除不是传入等级的
  1069.  
  1070. //绑定HTML元素
  1071. f.aFilter = document.getElementById('goodsFilter'+f.name);
  1072. f.spanFilterCount = document.getElementById('goodsCount'+f.name);
  1073. f.aFilterN = document.getElementById('goodsFilter'+f.nameN);
  1074. f.spanFilterNCount = document.getElementById('goodsCount'+f.nameN);
  1075. //初始化标记
  1076. f.created = true;
  1077.  
  1078. //安装点击响应函数
  1079. f.aFilter.addEventListener('click', filterClickHandler);
  1080. f.aFilterN.addEventListener('click', filterClickHandler);
  1081. }
  1082. //过滤器点击响应函数
  1083. function filterClickHandler() {
  1084. if (undefined !== unsafeWindow.z$.currentFilterData) return; //防止重入
  1085. unsafeWindow.z$.currentFilterData = this || event.currentTarget || arguments[0].currentTarget;
  1086. setTimeout(function() {
  1087. var aFilter = unsafeWindow.z$.currentFilterData;
  1088. var cl = aFilter.classList;
  1089. cl.toggle('selected');
  1090. var checked = cl.contains('selected');
  1091. var filterN = cl.contains('filterN');
  1092. var f = jdGoodsFilters[aFilter.getAttribute('filterId')];
  1093. if (filterN) f.checkedN = checked;
  1094. else f.checked = checked;
  1095. if ((filterN ? f.goodsCountFilteredN+f.goodsCountN : f.goodsCountFiltered+f.goodsCount) !== 0) applyGoodsFilters(); //过滤器点击响应
  1096. //更新过滤器计数
  1097. if (f.level === 1) updateUsingMoreFiltersCount(); //过滤器点击响应
  1098. //保存设置
  1099. var valName = filterN ? f.nameN : f.name;
  1100. if (checked) GM_deleteValue(valName); else GM_setValue(valName, true);
  1101. delete unsafeWindow.z$.currentFilterData;
  1102. }, 0);
  1103. }
  1104. }
  1105. //打开/关闭下级过滤器面板
  1106. function toggleMoreFiltersPanel(isInitializing) {
  1107. if (!isInitializing) saveStates();
  1108. var div = unsafeWindow.y$.divMoreFiltersPanel;
  1109. if (div === undefined) {
  1110. setupMoreFiltersPanel();
  1111. div = unsafeWindow.y$.divMoreFiltersPanel;
  1112. }
  1113. var span = unsafeWindow.y$.spanMoreFiltersCount;
  1114. if (unsafeWindow.y$.aShowMoreFiltersPanel.toggleClass('opened').hasClass('opened')) {
  1115. //保存下级过滤器面板打开状态
  1116. GM_setValue('打开下级过滤器面板', true);
  1117. //显示下级过滤器面板
  1118. if (isInitializing || unsafeWindow.x$.noAnimateChecked) { //不允许动画
  1119. div.css('display', '');
  1120. if (!span.hasClass('zeroFiltersCount')) span.css('display', 'none');
  1121. } else {
  1122. div.slideDown('fast');
  1123. if (!span.hasClass('zeroFiltersCount')) span.fadeOut();
  1124. }
  1125. } else {
  1126. //保存下级过滤器面板关闭状态
  1127. GM_deleteValue('打开下级过滤器面板');
  1128. //结束正在执行的提醒动画
  1129. $('#moreFiltersPanel td:has(a[filterId])').finish(); //querySelectorAll 不支持 :has
  1130. //关闭下级过滤器面板
  1131. if (isInitializing || unsafeWindow.x$.noAnimateChecked) { //不允许动画
  1132. div.css('display', 'none');
  1133. if (!span.hasClass('zeroFiltersCount')) span.css('display', '');
  1134. } else {
  1135. div.slideUp('fast');
  1136. if (!span.hasClass('zeroFiltersCount')) span.fadeIn();
  1137. }
  1138. }
  1139. //向页面添加下级过滤器面板
  1140. function setupMoreFiltersPanel() {
  1141. var uiFilters = [];
  1142. //生成过滤器HTML数组
  1143. for (var i=0; i<jdGoodsFilters.length; ++i) {
  1144. var f = jdGoodsFilters[i];
  1145. if (f.level !== 1) continue; //剔除不是下级的
  1146.  
  1147. var checkedClass = f.checked ? 'class="selected"' : '';
  1148. var checkedNClass = f.checkedN ? 'class="filterN selected"' : 'class="filterN"';
  1149. uiFilters.push({
  1150. name: f.name,
  1151. filter: '<li><a '+checkedClass+' href="javascript:;" id="goodsFilter'+f.name+'" filterId="'+i+'" title="【'+f.name+'】商品">'+
  1152. '<span class="goodsCountPlaceholder"><span class="goodsCount" id="goodsCount'+f.name+'">0</span></span><i></i>'+
  1153. '</a></li>',
  1154. filterN: '<li><a '+checkedNClass+' href="javascript:;" id="goodsFilter'+f.nameN+'" filterId="'+i+'" title="【'+f.nameN+'】商品">'+
  1155. '<i></i><span class="goodsCountPlaceholder"><span class="goodsCount" id="goodsCount'+f.nameN+'">0</span></span>'+
  1156. '</a></li>'
  1157. });
  1158. }
  1159. //计算过滤器表格列数
  1160. var cols = 6;
  1161. //生成过滤器表格内容HTML
  1162. var filtersTableRows = '';
  1163. for (var j=0; j<uiFilters.length; j+=cols) {
  1164. filtersTableRows += '<tr>';
  1165. for (var k=0; k<cols; ++k) {
  1166. var l = j + k;
  1167. if (l < uiFilters.length) {
  1168. var m = uiFilters[l];
  1169. filtersTableRows += '<td><ul>'+m.filter+'</ul></td><td>'+m.name+'</td><td><ul>'+m.filterN+'</ul></td>';
  1170. } else filtersTableRows += '<td></td><td></td><td></td>';
  1171. }
  1172. filtersTableRows += '</tr>';
  1173. }
  1174. //向页面添加过滤器表格
  1175. document.getElementById('J_filter').insertAdjacentHTML('beforeend',
  1176. '<div id="moreFiltersPanel" class="filter" style="display:none;">'+
  1177. '<table class="f-feature" style="float:none;">'+filtersTableRows+'</table>'+
  1178. '<hr>'+
  1179. '<div id="regFilter">'+
  1180. '<div style="flex-grow:1;">'+
  1181. '<a target="_blank" href="https://www.bilibili.com/video/av15760606" style="color:transparent;text-shadow:transparent 0 0 0!important;">国际通用手势教程</a>'+
  1182. '</div>'+
  1183. '<div style="flex-grow:1;">'+
  1184. '<label for="goodsPattern" title="过滤商品名【包含指定字串】或【匹配指定正则表达式】的商品">商品名匹配规则:</label><input id="goodsPattern" class="txt" type=text title="键入【回车】或点击【手动更新过滤结果】以应用商品名匹配规则"></input>'+
  1185. '<input class="ckbox" id="useRegex" type="checkbox"><label for="useRegex" title="'+
  1186. '普通模式:\n'+
  1187. '支持搜索多个值,以反引号【`】分隔值列表,\n'+
  1188. '值列表以反引号开头采用同时命中规则(AND),\n'+
  1189. '否则采用任一命中规则(OR)\n'+
  1190. '\n'+
  1191. '正则模式:【程序猿&攻城狮专用 ◕◡◕✧】\n'+
  1192. '支持 js 正则语法(正斜线分隔)\n'+
  1193. '非正则语法的情况下默认追加 igm 标志'+
  1194. '">正则</label>'+
  1195. '<input class="ckbox" id="highlightGoodsPattern" type="checkbox"><label for="highlightGoodsPattern">高亮</label>'+
  1196. '</div>'+
  1197. '<div style="flex-grow:1;">'+
  1198. '<a target="_blank" href="https://www.bilibili.com/video/av17978378" style="color:transparent;text-shadow:transparent 0 0 0!important;">脸控福利</a>'+
  1199. '</div>'+
  1200. '</div>'+
  1201. '<hr>'+
  1202. '<div id="actionBar">'+
  1203. '<a id="allFiltersReset" class="btn btn-default" href="javascript:;">重置全部过滤器</a>'+
  1204. '<a id="moreFiltersReset" class="btn btn-default" href="javascript:;">重置下级过滤器</a>'+
  1205. '<a id="highlightFilters" class="btn btn-default" href="javascript:;">提醒一下?</a>'+
  1206. '<a id="refreshCount" class="btn btn-default" href="javascript:;" title="'+
  1207. '一般情况下过滤结果都是正确的,但是不排除因为\n'+
  1208. '网络/缓存以及插件本身的 bug 等问题而可能导致\n'+
  1209. '过滤结果不准确或者出现各种莫名其妙的错误,\n'+
  1210. '此时可尝试手动更新过滤结果。'+
  1211. '">手动更新过滤结果</a>'+
  1212. '<div style="flex-grow:1;">'+
  1213. '<div style="display:none;">'+
  1214. '<i class="info"></i><label id="infoLog"></label>'+
  1215. '</div>'+
  1216. '</div>'+
  1217. '<select id="goodsFilterMode">'+
  1218. '<option value="0" selected>淡化</option>'+
  1219. '<option value="1">隐藏</option>'+
  1220. '<option value="2">边框</option>'+
  1221. '<option value="3">黑白</option>'+
  1222. '<option value="4">转色</option>'+
  1223. '<option value="5">异端通通烧死</option>'+
  1224. '<option value="6">模糊</option>'+
  1225. '<option value="7">反色</option>'+
  1226. '<option value="8">暗刻</option>'+
  1227. '</select>'+
  1228. '<div>'+
  1229. '<input id="filteredToLast" class="ckbox" type="checkbox"><label for="filteredToLast" title="o(* ̄3 ̄)o 过滤掉的商品扔到后面去">后排</label>'+
  1230. '<input id="noAnimate" class="ckbox" type="checkbox"><label for="noAnimate" title="囧rz">老爷机不要动画!</label>'+
  1231. '</div>'+
  1232. '<a id="showSettings" class="btn-icon" href="javascript:;" title="一个神秘的按钮……'+
  1233. '\n\n按钮背面刻着一串放荡不羁的狂草,'+
  1234. '\n经过漫漫五千年的悠久岁月,'+
  1235. '\n字迹已经被风化得几乎看不清楚:'+
  1236. '\n【欲点此钮,后果自负 ◕◡◕✧】"><i></i></a>'+
  1237. '</div>'+
  1238. '</div>'
  1239. );
  1240. //保存控件引用
  1241. unsafeWindow.y$.divMoreFiltersPanel = $(document.getElementById('moreFiltersPanel'));
  1242. unsafeWindow.y$.inputGoodsPattern = $(document.getElementById('goodsPattern'));
  1243. unsafeWindow.y$.checkboxUseRegex = $(document.getElementById('useRegex'));
  1244. unsafeWindow.y$.checkboxHighlightGoodsPattern = $(document.getElementById('highlightGoodsPattern'));
  1245. unsafeWindow.y$.aAllFiltersReset = $(document.getElementById('allFiltersReset'));
  1246. unsafeWindow.y$.aMoreFiltersReset = $(document.getElementById('moreFiltersReset'));
  1247. unsafeWindow.y$.aHighlightFilters = $(document.getElementById('highlightFilters'));
  1248. unsafeWindow.y$.aRefreshCount = $(document.getElementById('refreshCount'));
  1249. unsafeWindow.y$.labelInfoLog = $(document.getElementById('infoLog'));
  1250. unsafeWindow.y$.selectGoodsFilterMode = $(document.getElementById('goodsFilterMode'));
  1251. unsafeWindow.y$.checkboxFilteredToLast = $(document.getElementById('filteredToLast'));
  1252. unsafeWindow.y$.checkboxNoAnimate = $(document.getElementById('noAnimate'));
  1253. unsafeWindow.y$.aShowSettings = $(document.getElementById('showSettings'));
  1254. //安装下级过滤器响应函数
  1255. installFilterClickHandler(1);
  1256. //显示下级过滤器面板之前更新计数
  1257. applyGoodsFilters(true); //更新下级过滤器面板计数
  1258. //正则模式
  1259. var inputGoodsPattern = unsafeWindow.y$.inputGoodsPattern;
  1260. if (unsafeWindow.x$.goodsPattern) inputGoodsPattern.val(unsafeWindow.x$.goodsPattern);
  1261. inputGoodsPattern.change(function() {setTimeout(updateGoodsPattern, 0);});
  1262. inputGoodsPattern.blur(function() {setTimeout(updateGoodsPattern, 0);});
  1263. inputGoodsPattern.keypress(function(e) {
  1264. if (e.keyCode === 13) {
  1265. updateInfoLog('计算中……');
  1266. unsafeWindow.y$.inputGoodsPattern.prop('disabled', true);
  1267. setTimeout(function() {
  1268. updateGoodsPattern();
  1269. applyGoodsFilters();
  1270. unsafeWindow.y$.inputGoodsPattern.prop('disabled', false);
  1271. updateInfoLog('');
  1272. }, 0);
  1273. }
  1274. });
  1275. //设置checkbox【正则匹配】值和响应函数
  1276. if (unsafeWindow.x$.useRegexChecked) unsafeWindow.y$.checkboxUseRegex.prop('checked', true);
  1277. unsafeWindow.y$.checkboxUseRegex.change(function() {
  1278. setTimeout(function() {
  1279. if (unsafeWindow.y$.checkboxUseRegex[0].checked) {
  1280. unsafeWindow.x$.useRegexChecked = true; //操作更新
  1281. GM_setValue('商品名匹配正则', true);
  1282. } else {
  1283. delete unsafeWindow.x$.useRegexChecked;
  1284. GM_deleteValue('商品名匹配正则');
  1285. }
  1286. updateGoodsPattern(true);
  1287. applyGoodsFilters();
  1288. }, 0);
  1289. });
  1290. //设置checkbox【高亮匹配】值和响应函数
  1291. if (unsafeWindow.x$.highlightGoodsPattern) unsafeWindow.y$.checkboxHighlightGoodsPattern.prop('checked', true);
  1292. unsafeWindow.y$.checkboxHighlightGoodsPattern.change(function() {
  1293. setTimeout(function() {
  1294. if (unsafeWindow.y$.checkboxHighlightGoodsPattern[0].checked) {
  1295. unsafeWindow.x$.highlightGoodsPattern = true; //操作更新
  1296. GM_deleteValue('商品名匹配不高亮');
  1297. } else {
  1298. delete unsafeWindow.x$.highlightGoodsPattern;
  1299. GM_setValue('商品名匹配不高亮', true);
  1300. }
  1301. applyGoodsFilters();
  1302. }, 0);
  1303. });
  1304. //【重置全部过滤器】按钮响应函数
  1305. unsafeWindow.y$.aAllFiltersReset.click(function() {
  1306. resetGoodsFilters(true);
  1307. });
  1308. //【重置下级过滤器】按钮响应函数
  1309. unsafeWindow.y$.aMoreFiltersReset.click(function() {
  1310. resetGoodsFilters(false);
  1311. });
  1312. //【提醒】按钮响应函数
  1313. unsafeWindow.y$.aHighlightFilters.click(function() {
  1314. var filters = $('#moreFiltersPanel td:has(a[filterId])'); //querySelectorAll 不支持 :has
  1315. var aside = filters.filter(':has(a.selected)');
  1316. var bside = filters.not(':has(a.selected)');
  1317. if (aside.length === 0 || bside.length === 0) return;
  1318. filters.finish();
  1319. (aside.length < bside.length ? aside : bside)
  1320. .animate({backgroundColor:'#0cf'}, 100)
  1321. .animate({backgroundColor:'rgba(0,204,255,0)'}, 5000, function() {
  1322. this.removeAttribute('style');
  1323. });
  1324. });
  1325. //【手动更新过滤结果】按钮响应函数
  1326. unsafeWindow.y$.aRefreshCount.click(function() {
  1327. updateInfoLog('计算中……');
  1328. unsafeWindow.y$.aRefreshCount.prop('disabled', true);
  1329. setTimeout(function() {
  1330. saveStates();
  1331. var t = Date.now();
  1332. var updated = applyGoodsFilters(); //【手动更新过滤结果】按钮响应
  1333. t = Date.now() - t;
  1334. var aTag = unsafeWindow.y$.aRefreshCount;
  1335. var title0 = aTag.attr('title0');
  1336. if (title0 === undefined) {
  1337. title0 = aTag.attr('title');
  1338. aTag.attr('title0', title0);
  1339. }
  1340. var log = '上次计算耗时 '+t/1000+' 秒,'+(updated>0?('更新了 '+updated+' 项数据'):'没有需要更新的数据');
  1341. aTag.attr('title', title0+'\n【'+log+'】');
  1342. aTag.prop('disabled', false);
  1343. updateInfoLog('');
  1344. }, 30);
  1345. });
  1346. //设置infoLog的信息
  1347. updateInfoLog(); //根据预设变量显示/隐藏infoLog
  1348. //设置select值和响应函数
  1349. unsafeWindow.y$.selectGoodsFilterMode.val(unsafeWindow.x$.goodsFilterModeIndex).change(function() {
  1350. unsafeWindow.z$.goodsFilterModeNew = parseInt(event.currentTarget.value);
  1351. if (unsafeWindow.z$.goodsFilterModeNew === unsafeWindow.x$.goodsFilterModeIndex) {
  1352. delete unsafeWindow.z$.goodsFilterModeNew;
  1353. } else setTimeout(function() {
  1354. if (unsafeWindow.z$.goodsFilterModeNew === undefined) return;
  1355. //改变当前过滤商品的样式
  1356. var ca = unsafeWindow.x$.filterModes[unsafeWindow.z$.goodsFilterModeNew];
  1357. var cr = unsafeWindow.x$.goodsFilterModeClass;
  1358. document.querySelectorAll('li.gl-item.'+unsafeWindow.x$.goodsFilterModeClass).forEach(function(e) {
  1359. e.classList.add(ca);
  1360. e.classList.remove(cr);
  1361. });
  1362. //保存设置
  1363. if (unsafeWindow.z$.goodsFilterModeNew === 0) GM_deleteValue('过滤方式');
  1364. else GM_setValue('过滤方式', unsafeWindow.z$.goodsFilterModeNew);
  1365. //更新值
  1366. unsafeWindow.x$.goodsFilterModeIndex = unsafeWindow.z$.goodsFilterModeNew; //操作更新
  1367. delete unsafeWindow.z$.goodsFilterModeNew;
  1368. unsafeWindow.x$.goodsFilterModeClass = unsafeWindow.x$.filterModes[unsafeWindow.x$.goodsFilterModeIndex]; //操作更新
  1369. }, 0);
  1370. });
  1371. //设置checkbox【后排】值和响应函数
  1372. if (unsafeWindow.x$.filteredToLastChecked) unsafeWindow.y$.checkboxFilteredToLast.prop('checked', true);
  1373. unsafeWindow.y$.checkboxFilteredToLast.change(function() {
  1374. setTimeout(function() {
  1375. if (unsafeWindow.y$.checkboxFilteredToLast[0].checked) {
  1376. unsafeWindow.x$.filteredToLastChecked = true; //操作更新
  1377. GM_setValue('后排', true);
  1378. reorderGoodsByFilteredState();
  1379. } else {
  1380. delete unsafeWindow.x$.filteredToLastChecked;
  1381. GM_deleteValue('后排');
  1382. reorderGoodsByOriginalOrder();
  1383. }
  1384. }, 0);
  1385. });
  1386. //设置checkbox【关闭动画】值和响应函数
  1387. if (unsafeWindow.x$.noAnimateChecked) unsafeWindow.y$.checkboxNoAnimate.prop('checked', true);
  1388. unsafeWindow.y$.checkboxNoAnimate.change(function() {
  1389. setTimeout(function() {
  1390. if (unsafeWindow.y$.checkboxNoAnimate[0].checked) {
  1391. unsafeWindow.x$.noAnimateChecked = true; //操作更新
  1392. GM_setValue('老爷机不要动画', true);
  1393. } else {
  1394. delete unsafeWindow.x$.noAnimateChecked;
  1395. GM_deleteValue('老爷机不要动画');
  1396. }
  1397. }, 0);
  1398. });
  1399. //神秘按钮
  1400. unsafeWindow.y$.aShowSettings.click(function() {
  1401. setTimeout(CheckSettings, 0);
  1402. });
  1403.  
  1404. //更新正则模式
  1405. function updateGoodsPattern(modeSwitched) {
  1406. var v = unsafeWindow.y$.inputGoodsPattern[0].value;
  1407. var cond = unsafeWindow.x$.goodsPatternCondition;
  1408. var hasPattern = v.length > 0;
  1409. if (hasPattern) {
  1410. if (!modeSwitched && v === unsafeWindow.x$.goodsPattern) return false;
  1411. if (unsafeWindow.x$.useRegexChecked) {
  1412. var rex = ToRegex(v);
  1413. if (rex !== null) {
  1414. cond.textRex = ToRegex(v);
  1415. delete cond.textAny;
  1416. delete cond.textAll;
  1417. } else hasPattern = false;
  1418. } else {
  1419. var txtCond = ToTextCond(v);
  1420. if (txtCond.txts.length > 0) {
  1421. if (txtCond.allMatch) {
  1422. cond.textAll = txtCond.txts;
  1423. delete cond.textAny;
  1424. } else {
  1425. cond.textAny = txtCond.txts;
  1426. delete cond.textAll;
  1427. }
  1428. delete cond.textRex;
  1429. } else hasPattern = false;
  1430. }
  1431. }
  1432. if (hasPattern) {
  1433. unsafeWindow.x$.goodsPattern = v;
  1434. GM_setValue('商品名匹配规则', v);
  1435. } else {
  1436. delete unsafeWindow.x$.goodsPattern;
  1437. delete cond.textAny;
  1438. delete cond.textAll;
  1439. delete cond.textRex;
  1440. GM_deleteValue('商品名匹配规则');
  1441. }
  1442. }
  1443. //重置过滤器
  1444. function resetGoodsFilters(all) {
  1445. setTimeout(function() {
  1446. for (var i=0; i<jdGoodsFilters.length; ++i) {
  1447. var f = jdGoodsFilters[i];
  1448. if (all ? f.level === undefined : f.level !== 1) continue; //重置全部,剔除未启用的;重置下级,剔除不是下级的
  1449.  
  1450. if (!f.checked) {
  1451. f.checked = true;
  1452. GM_deleteValue(f.name);
  1453. }
  1454. if (!f.checkedN) {
  1455. f.checkedN = true;
  1456. GM_deleteValue(f.nameN);
  1457. }
  1458. }
  1459. //结束正在执行的提醒动画
  1460. $('#moreFiltersPanel td:has(a[filterId])').finish(); //querySelectorAll 不支持 :has
  1461. //重置选中状态
  1462. var n = document.querySelectorAll((all?'':'table')+'.f-feature a[filterId]:not(.selected)');
  1463. for (var j=0; j<n.length; ++j) n[j].classList.add('selected');
  1464. applyGoodsFilters(); //重置过滤器
  1465. }, 0);
  1466. }
  1467. }
  1468. }
  1469. //保存设置
  1470. function saveStates(noPanelState, noFiltersStates, noMisc) {
  1471. //保存过滤器面板打开状态
  1472. if (!noPanelState) {
  1473. saveBoolIfDiff(unsafeWindow.y$.aShowMoreFiltersPanel.hasClass('opened'), '打开下级过滤器面板');
  1474. }
  1475. //保存过滤器状态
  1476. if (!noFiltersStates) {
  1477. for (var i=0; i<jdGoodsFilters.length; ++i) {
  1478. var f = jdGoodsFilters[i];
  1479. var sname = 'level'+f.name;
  1480. if (GM_getValue(sname) !== f.level) {
  1481. if (f.level !== undefined) GM_setValue(sname, f.level);
  1482. else GM_deleteValue(sname);
  1483. }
  1484. if (GM_getValue(f.name)) {
  1485. if (f.checked) GM_deleteValue(f.name);
  1486. } else if (!f.checked) GM_setValue(f.name, true);
  1487. if (GM_getValue(f.nameN)) {
  1488. if (f.checkedN) GM_deleteValue(f.nameN);
  1489. } else if (!f.checkedN) GM_setValue(f.nameN, true);
  1490. }
  1491. }
  1492. //保存其他设置
  1493. if (!noMisc) {
  1494. //过滤方式
  1495. saveIntIfDiff(unsafeWindow.x$.goodsFilterModeIndex, '过滤方式', true);
  1496. //过滤掉的商品扔到后面去
  1497. saveBoolIfDiff(unsafeWindow.x$.filteredToLastChecked, '后排');
  1498. //关闭动画
  1499. saveBoolIfDiff(unsafeWindow.x$.noAnimateChecked, '老爷机不要动画');
  1500. }
  1501. function saveIntIfDiff(i, gm_name, undefMeansZero) {
  1502. var val = GM_getValue(gm_name);
  1503. if (undefMeansZero) {
  1504. if (val === undefined) val = 0;
  1505. if (val !== i) {
  1506. if (val === 0) GM_deleteValue(gm_name);
  1507. else GM_setValue(gm_name, i);
  1508. }
  1509. } else if (val !== i) GM_setValue(gm_name, i);
  1510. }
  1511. function saveBoolIfDiff(b, gm_name) {
  1512. if (GM_getValue(gm_name)) {
  1513. if (!b) GM_deleteValue(gm_name);
  1514. } else if (b) GM_setValue(gm_name, true);
  1515. }
  1516. }
  1517. }
  1518. }
  1519. //更新【下级过滤器】的提示和过滤器计数蓝点
  1520. function updateUsingMoreFiltersCount() {
  1521. var moreFiltersTitle = '';
  1522. var moreFiltersCount = 0;
  1523. for (var i=0; i<jdGoodsFilters.length; ++i) {
  1524. var f = jdGoodsFilters[i];
  1525. if (f.level !== 1) continue; //剔除不是下级的
  1526.  
  1527. if (f.checked === f.checkedN) {
  1528. if (f.checked) continue;
  1529. moreFiltersTitle += '\n同时隐藏了【'+f.name+'】和【'+f.nameN+'】商品所以你啥也看不见了 囧rz';
  1530. moreFiltersCount += 2;
  1531. } else {
  1532. moreFiltersTitle += '\n隐藏【'+(f.checked ? f.nameN : f.name)+'】商品 '+
  1533. (f.checked ?
  1534. (f.goodsCountFilteredN === f.goodsCountN ?
  1535. (f.goodsCountN === 0 ? '' : parentheses(f.goodsCountN)) :
  1536. parentheses(f.goodsCountFilteredN+'/'+f.goodsCountN)) :
  1537. (f.goodsCountFiltered === f.goodsCount ?
  1538. (f.goodsCount === 0 ? '' : parentheses(f.goodsCount)) :
  1539. parentheses(f.goodsCountFiltered+'/'+f.goodsCount)));
  1540. ++moreFiltersCount;
  1541. }
  1542. }
  1543. var aShowMoreFiltersPanel = unsafeWindow.y$.aShowMoreFiltersPanel;
  1544. var spanMoreFiltersCount = unsafeWindow.y$.spanMoreFiltersCount;
  1545. if (moreFiltersCount > 0) {
  1546. aShowMoreFiltersPanel.attr('title', '当前下级过滤器('+moreFiltersCount+'):'+moreFiltersTitle);
  1547. spanMoreFiltersCount.text(moreFiltersCount);
  1548. if (spanMoreFiltersCount.hasClass('zeroFiltersCount')) spanMoreFiltersCount.removeClass('zeroFiltersCount');
  1549. } else {
  1550. aShowMoreFiltersPanel.attr('title', '当前下级过滤器:无');
  1551. spanMoreFiltersCount.text(0);
  1552. if (!spanMoreFiltersCount.hasClass('zeroFiltersCount')) spanMoreFiltersCount.addClass('zeroFiltersCount');
  1553. }
  1554. return spanMoreFiltersCount;
  1555. }
  1556. //更新【状态栏】
  1557. function updateInfoLog(log) {
  1558. //将传入 log 和 unsafeWindow.x$.infoLogText 同步
  1559. if (undefined === log) {
  1560. log = unsafeWindow.x$.infoLogText;
  1561. } else {
  1562. if (log.length) unsafeWindow.x$.infoLogText = log;
  1563. else delete unsafeWindow.x$.infoLogText;
  1564. }
  1565. //根据 log 的值操作 labelInfoLog
  1566. var labelInfoLog = unsafeWindow.y$.labelInfoLog;
  1567. if (labelInfoLog) {
  1568. if (undefined === log || !log.length) labelInfoLog.parent().css('display', 'none');
  1569. else labelInfoLog.text(log).parent().css('display', '');
  1570. }
  1571. }
  1572. //应用过滤器,计算商品数量
  1573. function applyGoodsFilters(setCountOnly) {
  1574. var allGoods = document.querySelectorAll('li.gl-item');
  1575. //计算商品属性
  1576. calcGoodsProperties();
  1577. //逐个更新过滤器
  1578. var updatedCount = 0;
  1579. for (var i=0; i<jdGoodsFilters.length; ++i) {
  1580. var f = jdGoodsFilters[i];
  1581. if (f.level === undefined) continue; //剔除未启用的
  1582. //计算计数
  1583. var goodsCount = getGoodsCount(f);
  1584. var goodsCountN = allGoods.length - goodsCount;
  1585. var filteredCount = getGoodsCountFiltered(f);
  1586. //更新左侧过滤器
  1587. var updated = updatedCount;
  1588. if (f.goodsCount !== goodsCount) {
  1589. f.goodsCount = goodsCount;
  1590. ++updatedCount;
  1591. }
  1592. if (f.goodsCountFiltered !== filteredCount[0]) {
  1593. f.goodsCountFiltered = filteredCount[0];
  1594. ++updatedCount;
  1595. }
  1596. if (updatedCount > updated) {
  1597. if (f.aFilter) updateFilterCount(f);
  1598. updated = updatedCount;
  1599. } else if (f.created) updateFilterCount(f);
  1600. //更新右侧过滤器
  1601. if (f.goodsCountN !== goodsCountN) {
  1602. f.goodsCountN = goodsCountN;
  1603. ++updatedCount;
  1604. }
  1605. if (f.goodsCountFilteredN !== filteredCount[1]) {
  1606. f.goodsCountFilteredN = filteredCount[1];
  1607. ++updatedCount;
  1608. }
  1609. if (updatedCount > updated) {
  1610. if (f.aFilterN) updateFilterNCount(f);
  1611. } else if (f.created) updateFilterNCount(f);
  1612. //删除初始化标志
  1613. if (f.created) delete f.created;
  1614. }
  1615. //更新【下级过滤器】的提示
  1616. updateUsingMoreFiltersCount();
  1617. //执行过滤器
  1618. if (!setCountOnly) applyFilters();
  1619.  
  1620. //返回更新的数据计数
  1621. return updatedCount;
  1622.  
  1623. function calcGoodsProperties() {
  1624. for (var i=0; i<allGoods.length; ++i) {
  1625. var g = allGoods[i];
  1626. for (var j=0; j<jdGoodsFilters.length; ++j) {
  1627. var f = jdGoodsFilters[j];
  1628. if (f.level === undefined) continue; //剔除未启用的
  1629.  
  1630. if (hitFilter(g, f)) g[f.name] = true;
  1631. else delete g[f.name];
  1632. }
  1633. }
  1634. function hitFilter(elem, filter) {
  1635. for (var i=0; i<filter.conditions.length; ++i) {
  1636. var cond = filter.conditions[i];
  1637. if (goodsFilterMap[cond.type](elem, cond) ^ (cond.not !== undefined)) return true;
  1638. }
  1639. return false;
  1640. }
  1641. }
  1642. function getGoodsCount(filter) {
  1643. var count = 0;
  1644. for (var i=0; i<allGoods.length; ++i) {
  1645. if (allGoods[i][filter.name]) ++count;
  1646. }
  1647. return count;
  1648. }
  1649. function getGoodsCountFiltered(filter) {
  1650. var filteredGoods = [];
  1651. fg: for (var i=0; i<allGoods.length; ++i) {
  1652. var g = allGoods[i];
  1653. for (var j=0; j<jdGoodsFilters.length; ++j) {
  1654. var f = jdGoodsFilters[j];
  1655. if (f.level === undefined) continue; //剔除未启用的
  1656. if (filter === f) continue; //剔除正在计数的
  1657.  
  1658. if (f.checked === f.checkedN) {
  1659. if (f.checked) continue; //忽略正反都显示的过滤器
  1660. return [0, 0]; //有正反都不显示的过滤器,直接返回 0
  1661. }
  1662.  
  1663. if (g[f.name] ? f.checked : f.checkedN) continue; //测试通过
  1664.  
  1665. continue fg;
  1666. }
  1667. filteredGoods.push(g);
  1668. }
  1669. var count = 0;
  1670. for (var k=0; k<filteredGoods.length; ++k) {
  1671. if (filteredGoods[k][filter.name]) ++count;
  1672. }
  1673. return [count, filteredGoods.length - count];
  1674. }
  1675. function updateFilterCount(filter) {
  1676. filter.spanFilterCount.textContent = filter.goodsCountFiltered===filter.goodsCount ? filter.goodsCount : filter.goodsCountFiltered+'/'+filter.goodsCount;
  1677. if (filter.goodsCountFiltered === 0) filter.aFilter.classList.add('zeroCountGoods');
  1678. else filter.aFilter.classList.remove('zeroCountGoods');
  1679. }
  1680. function updateFilterNCount(filter) {
  1681. filter.spanFilterNCount.textContent = filter.goodsCountFilteredN===filter.goodsCountN ? filter.goodsCountN : filter.goodsCountFilteredN+'/'+filter.goodsCountN;
  1682. if (filter.goodsCountFilteredN === 0) filter.aFilterN.classList.add('zeroCountGoods');
  1683. else filter.aFilterN.classList.remove('zeroCountGoods');
  1684. }
  1685. function applyFilters() {
  1686. var inGoods = [];
  1687. var outGoods = [];
  1688. fg: for (var i=0; i<allGoods.length; ++i) {
  1689. var g = allGoods[i];
  1690. for (var j=0; j<jdGoodsFilters.length; ++j) {
  1691. var f = jdGoodsFilters[j];
  1692. if (f.level === undefined) continue; //剔除未启用的
  1693.  
  1694. if (g[f.name]) {
  1695. if (f.onPassed) f.onPassed(g);
  1696. } else {
  1697. if (f.onMissed) f.onMissed(g);
  1698. }
  1699.  
  1700. if (f.checked === f.checkedN) {
  1701. if (f.checked) continue; //忽略正反都显示的过滤器
  1702. var cf = unsafeWindow.x$.goodsFilterModeClass;
  1703. for (var k=0; k<allGoods.length; ++k) allGoods[k].classList.add(cf); //正反都隐藏
  1704. return;
  1705. }
  1706.  
  1707. if (g[f.name] ? f.checked : f.checkedN) continue; //测试通过
  1708. //测试失败
  1709. outGoods.push(g);
  1710. continue fg;
  1711. }
  1712. inGoods.push(g);
  1713. }
  1714. var c = unsafeWindow.x$.goodsFilterModeClass;
  1715. if (inGoods.length > 0) for (var l=0; l<inGoods.length; ++l) inGoods[l].classList.remove(c);
  1716. if (outGoods.length > 0) for (var m=0; m<outGoods.length; ++m) outGoods[m].classList.add(c);
  1717. if (unsafeWindow.x$.filteredToLastChecked) reorderGoodsByFilteredState(allGoods);
  1718. }
  1719. }
  1720. //重排商品
  1721. function reorderGoodsByFilteredState(allGoods) {
  1722. if (undefined === allGoods) allGoods = document.querySelectorAll('li.gl-item');
  1723. groupGoods(allGoods, function(x,y) {
  1724. var xx = x.classList.contains(unsafeWindow.x$.goodsFilterModeClass);
  1725. var yy = y.classList.contains(unsafeWindow.x$.goodsFilterModeClass);
  1726. return xx === yy ? x.sortOrder-y.sortOrder : (xx?1:-1);
  1727. });
  1728. }
  1729. function reorderGoodsByOriginalOrder(allGoods) {
  1730. if (undefined === allGoods) allGoods = document.querySelectorAll('li.gl-item');
  1731. groupGoods(allGoods, function(x,y) {
  1732. return x.sortOrder-y.sortOrder;
  1733. });
  1734. }
  1735. //将商品分组并记录原始顺序,然后对商品重新排序
  1736. function groupGoods(allGoods, sortBy) {
  1737. if (allGoods && allGoods.length) {
  1738. var grouped = [];
  1739. //分组
  1740. for (var i=0; i<allGoods.length; ++i) {
  1741. var goods = allGoods[i];
  1742. var parent = goods.parentElement;
  1743. var current = undefined;
  1744. for (var j=0; j<grouped.length; ++j) {
  1745. if (grouped[j].parent === parent) {
  1746. current = grouped[j];
  1747. break;
  1748. }
  1749. }
  1750. if (!current) grouped.push(current = {parent: parent, goods:[]});
  1751. //记录原始顺序
  1752. if (undefined === goods.sortOrder) goods.sortOrder = current.goods.length;
  1753. current.goods.push(goods);
  1754. }
  1755. //对每组商品单独排序
  1756. for (var k=0; k<grouped.length; ++k) {
  1757. var group = grouped[k];
  1758. group.goods.sort(sortBy);
  1759. $(group.parent).append(group.goods);
  1760. }
  1761. }
  1762. }
  1763. //加载所有商品
  1764. function loadAllGoods() {
  1765. if (unsafeWindow.SEARCH !== undefined && unsafeWindow.SEARCH.scroll !== undefined) {
  1766. unsafeWindow.SEARCH.scroll();
  1767. setTimeout(waitAllGoods, timerFreq);
  1768. } else setTimeout(loadAllGoods, timerFreq);
  1769. //等待所有商品加载完成
  1770. function waitAllGoods() {
  1771. if (unsafeWindow.SEARCH.loading) setTimeout(waitAllGoods, timerFreq);
  1772. else processGoodsList();
  1773. }
  1774. //收尾处理
  1775. function processGoodsList() {
  1776. if (document.querySelectorAll('li.gl-item').length) {
  1777. var noResultNotice = document.querySelector('div.notice-filter-noresult');
  1778. if (noResultNotice) noResultNotice.remove();
  1779. }
  1780. forceLoadLazyImgs();
  1781. applyGoodsFiltersOnLoaded();
  1782. //取消图片延迟加载
  1783. function forceLoadLazyImgs() {
  1784. unsafeWindow.z$.forceLoadLazyImgsCount = 0;
  1785. unsafeWindow.z$.forceLoadLazyImgsInterval = setInterval(function() {
  1786. if (unsafeWindow.z$.forceLoadLazyImgsCount++ < 3) {
  1787. var aName = 'data-lazy-img';
  1788. document.querySelectorAll('ul.gl-warp img[data-lazy-img]:not([src])').forEach(function(img) {
  1789. img.setAttribute('src', img.getAttribute(aName));
  1790. img.removeAttribute(aName);
  1791. });
  1792. return;
  1793. }
  1794. if (unsafeWindow.z$.forceLoadLazyImgsInterval) {
  1795. clearInterval(unsafeWindow.z$.forceLoadLazyImgsInterval);
  1796. delete unsafeWindow.z$.forceLoadLazyImgsInterval;
  1797. }
  1798. }, 2000);
  1799. }
  1800. //延时应用过滤器
  1801. function applyGoodsFiltersOnLoaded() {
  1802. //每秒执行一次,前2次仅更新计数,15次后由 applyGoodsFiltersOnAjaxStop 接管,并计划一个10秒后的更新
  1803. unsafeWindow.z$.applyGoodsFiltersOnLoadedCount = 0;
  1804. unsafeWindow.z$.applyGoodsFiltersOnLoadedCountInterval = setInterval(function() {
  1805. if (unsafeWindow.z$.applyGoodsFiltersOnLoadedCount < 15) {
  1806. applyGoodsFilters(unsafeWindow.z$.applyGoodsFiltersOnLoadedCount++ < 2); //延迟收尾
  1807. if (unsafeWindow.z$.applyGoodsFiltersOnLoadedCount === 3) updateInfoLog('');
  1808. return;
  1809. }
  1810. if (unsafeWindow.z$.applyGoodsFiltersOnLoadedCountInterval) {
  1811. clearInterval(unsafeWindow.z$.applyGoodsFiltersOnLoadedCountInterval);
  1812. delete unsafeWindow.z$.applyGoodsFiltersOnLoadedCountInterval;
  1813. setTimeout(applyGoodsFilters, 1000 * 10);
  1814. applyGoodsFiltersOnAjaxStop();
  1815. }
  1816. }, 1000);
  1817. }
  1818. //ajaxStop收尾
  1819. function applyGoodsFiltersOnAjaxStop() {
  1820. //开始计时
  1821. unsafeWindow.z$.goodsLoadedTime = Date.now();
  1822. //注册 ajaxStop 事件
  1823. unsafeWindow.$(document).ajaxStop(function() {
  1824. var t = Date.now() - unsafeWindow.z$.goodsLoadedTime;
  1825. if (t > 1000 * 15) return; //15秒之后不再响应该事件
  1826. //console.log('ajaxStop -> '+t+'ms');
  1827.  
  1828. //对挂起的操作重新计时(清除已有的并重新挂起一个新的延时操作)
  1829. if (unsafeWindow.z$.applyGoodsFiltersTimeout !== undefined) {
  1830. clearTimeout(unsafeWindow.z$.applyGoodsFiltersTimeout);
  1831. delete unsafeWindow.z$.applyGoodsFiltersTimeout;
  1832. }
  1833. unsafeWindow.z$.applyGoodsFiltersTimeout = setTimeout(function() {
  1834. delete unsafeWindow.z$.applyGoodsFiltersTimeout;
  1835. applyGoodsFilters(); //ajaxStop 延时操作
  1836. }, 33);
  1837. });
  1838. }
  1839. }
  1840. }
  1841. }
  1842. //检查保存数据的版本信息
  1843. function CheckSettings(versionCheck) {
  1844. var version = 50102; //每个子版本号占2位 //GM_info.script.version //用GM_info会使每个小版本更新都强行重置所有保存的设置
  1845. if (versionCheck && GM_getValue('插件版本') >= version) return;
  1846. var vals = GM_listValues();
  1847. for (var i=0; i<vals.length; ++i) GM_deleteValue(vals[i]);
  1848. GM_setValue('插件版本', version);
  1849. }
  1850.  
  1851. //圆括号包围字串
  1852. function parentheses(str){return surround(str,'(',')');}
  1853. function surround(str, prefix, suffix){return prefix+str+suffix;}
  1854. //字串检测
  1855. function isAny(str, vals) { //str∈vals
  1856. for (var i=0; i<vals.length; ++i) {
  1857. if (str === vals[i]) return true;
  1858. }
  1859. return false;
  1860. }
  1861. function includesAny(str, vals) { //∃v∈vals,str⊇v
  1862. for (var i=0; i<vals.length; ++i) {
  1863. if (str.includes(vals[i])) return true;
  1864. }
  1865. return false;
  1866. }
  1867. function includesAll(str, vals) { //∀v∈vals,str⊇v
  1868. for (var i=0; i<vals.length; ++i) {
  1869. if (!str.includes(vals[i])) return false;
  1870. }
  1871. return true;
  1872. }
  1873. //字串转 RegExp
  1874. function ToRegex(str) {
  1875. if (str[0] === '/') {
  1876. try {
  1877. var r = eval(str);
  1878. if (r instanceof RegExp) return new RegExp(r); //重新调用一次 RegExp 对字面量进行优化
  1879. } catch(e) {}
  1880. }
  1881. try { return new RegExp(str, 'gim'); } catch(e) {}
  1882. return null;
  1883. }
  1884. //字串转匹配规则,去掉空白串
  1885. function ToTextCond(str) {
  1886. var allMatch = str[0] === '`';
  1887. var vals = str.split('`');
  1888. var txts = [];
  1889. for (var i=allMatch?1:0; i<vals.length; ++i) {
  1890. var txt = vals[i];
  1891. if (txt.length > 0 && (txt = txt.trim()).length > 0) txts.push(txt);
  1892. }
  1893. return {allMatch:allMatch, txts:txts};
  1894. }
  1895. //高亮 HTML (Regex)
  1896. function highlightHtmlUsingRegex(html, rex, hlInfo) {
  1897. var i=0;
  1898. rex.lastIndex = 0;
  1899. html = html.replace(rex, function(m) {
  1900. return m.length > 0 ? hlInfo.tag0[i++%9]+m+hlInfo.tag1 : '';
  1901. });
  1902. return i>0 ? html : null;
  1903. }
  1904. //高亮 HTML (string[])
  1905. function highlightHtmlUsingTexts(html, txts, hlInfo) {
  1906. if (txts.length === 0) return null;
  1907. for (var i=0; i<txts.length; ++i) {
  1908. var txt = txts[i];
  1909. if (html.includes(txt)) html = html.replace(txt, hlInfo.tag0[i%9]+txt+hlInfo.tag1);
  1910. }
  1911. return html;
  1912. }
  1913. //高亮 HTML
  1914. function highlightHtml(html, match, hlInfo) {
  1915. return match instanceof RegExp ? highlightHtmlUsingRegex(html, match, hlInfo) : highlightHtmlUsingTexts(html, match, hlInfo);
  1916. }
  1917. //替换高亮 HTML
  1918. function replaceHighlightHtml(html, match, hlMatch, hlInfo) {
  1919. if (match instanceof RegExp) {
  1920. if (hlMatch instanceof RegExp) { //正则替换正则
  1921. if (match.source === hlMatch.source && match.flags === hlMatch.flags) return null; //相等的正则,跳过处理
  1922. }
  1923. html = unhighlightHtml(html, hlInfo);
  1924. html = highlightHtmlUsingRegex(html, match, hlInfo);
  1925. } else {
  1926. if (hlMatch instanceof RegExp) {
  1927. html = unhighlightHtml(html, hlInfo);
  1928. html = highlightHtmlUsingTexts(html, match, hlInfo);
  1929. } else { //数组替换数组
  1930. var hit = false;
  1931. for (var i=0; i<match.length; ++i) {
  1932. var txt = match[i];
  1933. var ti = i%9;
  1934. if (i < hlMatch.length) {
  1935. var hlTxt = hlMatch[i];
  1936. if (txt === hlTxt) continue; //高亮的位置和内容相等,跳过处理
  1937. html = html.replace(hlInfo.tag0[ti]+hlTxt+hlInfo.tag1, hlTxt); //先取消高亮
  1938. }
  1939. html = html.replace(txt, hlInfo.tag0[ti]+txt+hlInfo.tag1); //重新高亮
  1940. hit = true;
  1941. }
  1942. if (match.length < hlMatch.length) {
  1943. for (var j=match.length; j<hlMatch.length; ++j) {
  1944. var hl = hlMatch[j];
  1945. html = html.replace(hlInfo.tag0[j%9]+hl+hlInfo.tag1, hl);
  1946. }
  1947. hit = true;
  1948. }
  1949. return hit ? html : null;
  1950. }
  1951. }
  1952. return html;
  1953. }
  1954. //取消高亮 HTML
  1955. function unhighlightHtml(html, hlInfo) {
  1956. for (var i=0; i<hlInfo.tag0.length; ++i) {
  1957. html = html.replace(hlInfo.tag0[i], '');
  1958. }
  1959. return html.replace(hlInfo.tag1, '');
  1960. }
  1961. }());