Greasy Fork

Greasy Fork is available in English.

京东单价助手

京东(jd.com)商品列表页面的增强程序,增强了显示单价功能,帮助你在京东(jingdong)找到最便宜的商品

当前为 2017-06-13 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         京东单价助手
// @namespace    http://greasyfork.org/
// @version      1.08
// @description  京东(jd.com)商品列表页面的增强程序,增强了显示单价功能,帮助你在京东(jingdong)找到最便宜的商品
// @author       Yarmu
// @match        *://search.jd.com/*
// @match        *://list.jd.com/*
// @match        *://item.jd.com/*
// @grant        none
// @supportURL   http://greasyfork.icu/zh-CN/scripts/30522-%E4%BA%AC%E4%B8%9C%E5%8D%95%E4%BB%B7%E5%8A%A9%E6%89%8B
// ==/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;

    //处理包含:的情况
    if (title.indexOf(':') != -1){
        var titles = title.split(':');
        for (var t=0; t<titles.length; ++t) {
            var res = getUnit(titles[t], price);
            if (res) return res;
        }
    }

    var regQuant = "个只瓶罐听桶条提卷包袋件盒箱组串册副份双对块本束杯枚枝根片盆盘碗粒颗管";
    var regWeigh = "g|kg|ml|l|千克|克|斤|公斤|毫升|升";
    var regFloat = "\\d+\\.?\\d*?(?:\\s*-\\s*\\d+\\.?\\d*?)?";
    //处理有总重量的情况
    var reg0 = new RegExp('[总净]重量?约?\\s*('+regFloat+')\\s*('+regWeigh+')');
    var pos0 = {i: 0, pCap: 1, pUnit: 2, pCount: 3};
    //处理数量前置的情况
    var reg1 = new RegExp('\\b(\\d+)\\s*['+regQuant+']?\\s*[*x×'+regQuant+']\\s*('+regFloat+')\\s*('+regWeigh+')', 'ig');
    var pos1 = {i: 1, pCap: 2, pUnit: 3, pCount: 1};
    //处理数量后置的情况
    var reg2 = new RegExp('\\b('+regFloat+')\\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;
    //处理有括号的情况
    var name = title.replace(/[((][^))]+(\)|)|$)/g, '');
    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;
    }
    //处理有赠送的情况
    var isOnlyOne = !/[++送和]/i.test(name);

    var match = null;
    var cap = 0, count = 0, lastMul = 0;
    var un = '', tip = '';
    reg.lastIndex = 0;
    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<multiple.length; ++i) {
                lastMul = parseInt(multiple[i]);
                capacity *= lastMul;
            }
        }
        if (capacity <= 0) {
            continue;
        }
        var unit = match[pos.pUnit].toLowerCase();

        if (unit === 'g' || unit === '克') {
            capacity /= 1000;
            unit = 'kg';
        } else if (unit === '千克' || unit === '公斤') {
            unit = 'kg';
        } else if (unit === '斤') {
            capacity /= 2;
            unit = 'kg';
        } else if (unit === 'ml' || unit === '毫升') {
            capacity /= 1000;
            unit = 'L';
        } else if (unit === 'l' || unit === '升') {
            unit = 'L';
        }
        if (un === '' || un === unit) {
            un = unit;
            tip += match[0] + ' ';
            cap += capacity;
            ++count;
        }
        if (isOnlyOne) {
            break;
        }
    }
    //处理数量在其他位置的情况
    if (count == 1 && pos.i !== 0) {
        var regMul = '\\b(\\d+)['+regQuant+']';//(?:礼盒)?装
        reg = new RegExp(regMul, 'i');
        match = reg.exec(title);
        if (match && lastMul != parseInt(match[1])) {
            cap *= parseInt(match[1]);
            tip += match[0];
        }
    }

    if (cap > 0) return {
        capacity: Math.round(cap * 10000) / 10000,
        unit: un,
        price: Math.round(parseFloat(price) / cap * 100) / 100,
        tip: tip.trim()
    };
    else return null;
}

function getFloat(str) {
    str = str.replace('¥','').trim();
    if (!/^\d+\.?\d*$/.test(str)) return NaN;
    return parseFloat(str);
}