// ==UserScript== // @name 京东单价助手 // @namespace http://greasyfork.org/ // @version 1.07 // @description 京东(jd.com)商品列表页面的增强程序,增强了显示单价功能,帮助你在京东(jingdong)找到最便宜的商品 // @author Yarmu // @match *://search.jd.com/* // @match *://list.jd.com/* // @match *://item.jd.com/* // @grant none // @supportURL https://greasyfork.org/zh-CN/scripts/30522-%E4%BA%AC%E4%B8%9C%E5%8D%95%E4%BB%B7%E5%8A%A9%E6%89%8B // @downloadURL none // ==/UserScript== (function() { var host = window.location.host.toLowerCase(); if (host === 'list.jd.com' || host === 'search.jd.com') { addPriceTipListener('.p-price', addListPriceTip, 1000); } else if (host === 'item.jd.com') { addPriceTipListener('.p-price', addItemPriceTip); addPriceTipListener('.p-price-plus', addItemPriceTip); } })(); function addPriceTipListener(tag, func, time) { var onModifiedFunc = function() { $(this).unbind("DOMSubtreeModified"); func.call(this); $(this).bind("DOMSubtreeModified", onModifiedFunc); }; var eachCallFunc = function() { $(tag).each(function() { if (!$(this).attr('priceTip')) { $(this).attr('priceTip', '1'); onModifiedFunc.call(this); } }); }; eachCallFunc(); if (time) { setInterval(eachCallFunc, time); } } function addListPriceTip() { var priceItem = $(this).find('strong i'); var price = getFloat(priceItem.text()); if (isNaN(price)) { priceItem = $(this).find('strong span'); price = getFloat(priceItem.text()); if (isNaN(price)) { priceItem = $(this).find('strong'); price = getFloat(priceItem.text()); if (isNaN(price)) { priceItem = $(this); price = getFloat(priceItem.text()); } } } if (isNaN(price)) return; var title = null; var index = 0; $(this).parent().find('.p-scroll .ps-wrap .ps-main .ps-item a').each(function(idx) { if ($(this).attr('class') === 'curr' && $(this).attr('title')) { title = $(this).attr('title').trim(); index = idx; return false; } }); var unit = getUnit(title, price); if (unit === null && index === 0) { title = $(this).parent().find('.p-name a em').text().trim(); if (!title) { title = $(this).parent().find('.p-name a').text().trim(); } unit = getUnit(title, price); } if (unit === null) return; var htm = ' (' + unit.price + '/' + unit.unit + ')'; title = '(助手估重: ' + unit.capacity + unit.unit + ' = "' + unit.tip + '")\n' + title; priceItem.append(htm); var tipItem = $(this).parent().find('.p-name a'); setTimeout(function() { tipItem.attr('title', title); }, 1000); } function addItemPriceTip() { var priceItem = $(this).find('.price'); var price = getFloat(priceItem.text()); if (isNaN(price)) return; var title = $('.sku-name').text().trim(); var unit = getUnit(title, price); if (unit === null) return; var htm = ' (' + unit.price + '/' + unit.unit + ')'; title = ' (助手估重: ' + unit.capacity + unit.unit + ' = "' + unit.tip + '")'; priceItem.append(htm); if (!window.hasAddItemPriceTip) { window.hasAddItemPriceTip = true; setTimeout(function() { $('#summary-weight .dd').append(title); }, 1000); } } function getUnit(title, price) { if (!title) return null; if (price <= 0) return null; var name = title.replace(/[((][^))]+(\)|)|$)/g, ''); var regQuant = "个只瓶罐听桶条提卷包袋件盒箱组串册副份双对块本束杯枚枝根片盆盘碗粒颗管"; var regWeigh = "g|kg|ml|l|千克|克|斤|公斤|毫升|升"; var reg0 = new RegExp('总重约?\s*(\\d+\\.?\\d*?)\s*('+regWeigh+')'); var pos0 = {i: 0, pCap: 1, pUnit: 2, pCount: 3}; var reg1 = new RegExp('\\b(\\d+)\\s*['+regQuant+']?\\s*[*x×'+regQuant+']\\s*(\\d+\\.?\\d*?)\\s*('+regWeigh+')', 'ig'); var pos1 = {i: 1, pCap: 2, pUnit: 3, pCount: 1}; var reg2 = new RegExp('\\b(\\d+\\.?\\d*?(?:\s*-\s*\\d+\\.?\\d*?)?)\\s*('+regWeigh+')(?:\\s*\\/?[\\u4e00-\\u9fa5]*)((?:\\s*[*x×'+regQuant+']\\s*\\d+[\\u4e00-\\u9fa5]?)*)', 'ig'); var pos2 = {i: 2, pCap: 1, pUnit: 2, pCount: 3}; var reg, pos; if (reg0.test(title)) { name = title; reg = reg0; pos = pos0; } else if (reg1.test(name)) { reg = reg1; pos = pos1; } else if (reg2.test(name)) { reg = reg2; pos = pos2; } else if (reg1.test(title)) { name = title; reg = reg1; pos = pos1; } else { name = title; reg = reg2; pos = pos2; } reg.lastIndex = 0; var match = null; var cap = 0, count = 0, lastMul = 0; var un = '', tip = ''; var isOnlyOne = !/[++送和]/i.test(name); while ((match = reg.exec(name))) { var capacity; var caps = match[pos.pCap].split('-'); if (caps.length == 2) { capacity = (parseFloat(caps[0].trim()) + parseFloat(caps[1].trim()))/2; } else { capacity = parseFloat(match[pos.pCap].trim()); } if (match.length > 3 && match[pos.pCount]) { var multiple = match[pos.pCount].match(/\d+/g); if (multiple) for (var i=0; i 0) return { capacity: Math.round(cap * 10000) / 10000, unit: un, price: Math.round(parseFloat(price) / cap * 100) / 100, tip: tip }; else return null; } function getFloat(str) { str = str.replace('¥','').trim(); if (!/^\d+\.?\d*$/.test(str)) return NaN; return parseFloat(str); }