A Better eBay Filter

Allow filtering eBay searches based on additional criteria, global site support

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// abef.user.js
//
// Copyright 2007-2015, Michael Devore
// This file is licensed under the terms of the Artistic License 2.0.
// See http://opensource.org/licenses/artistic-license-2.0 for the license itself.
//
// This is a Greasemonkey script.
// See http://www.greasespot.net/ for more information on Greasemonkey.
//
// ==UserScript==
// @name          A Better eBay Filter
// @namespace     http://www.devoresoftware.com/gm/abef
// @description	Allow filtering eBay searches based on additional criteria, global site support
// @version       9.0
// @include       http://*listings*.ebay.*/*
// @include       https://*listings*.ebay.*/*
// @include       http://*search*.ebay.*/*
// @include       https://*search*.ebay.*/*
// @include       http://*shop*.ebay.*/*
// @include       https://*shop*.ebay.*/*
// @include       http://www.ebay.*/sch/*
// @include       https://www.ebay.*/sch/*
// @include       http://www.ebay.*/dsc/*
// @include       https://www.ebay.*/dsc/*
// @grant GM.setValue 
// @grant GM.getValue
// @run-at document-end
// ==/UserScript==
//
// Version 1.0, released April 2007
// Version 2.0, released late-April 2007
// Version 3.0, release early January 2010, rework for revised eBay
// Version 3.1, release mid-January 2010, fix for eBay Motors
// Version 3.2, release early September 2010, fix for Ebay update
// Version 4.0, release November 2010, add support for Top-rated seller filtering
// Version 4.1, release November 2011, add @include to support changed eBay search links
// Version 5.0, release March 2012, rework due to eBay changing internals
// Version 6.0, release February 2013, rework due to eBay redesign
// Version 7.0, release October 2014, update for eBay changes, newer Greasemonkey version compatibility
// Version 8.0, release late July 2015, update for compatibility with searches within results
// Version 8.1, release mid-August 2015, update to support searches with include description checked
// Version 9.0, release March 2018, new Greasemonkey api compatibility

var defaultScoreThreshold = 50;
var defaultPercentThreshold = 98.5;
var defaultScoreMax = "";
var FILTER_SCORE_FLAG = 1;
var FILTER_PERCENT_FLAG = FILTER_SCORE_FLAG << 1;
var FILTER_SCOREMAX_FLAG = FILTER_PERCENT_FLAG << 1;
var filteredCount = 0;
var buttonText = "button";
var divText = "DIV";
var spanText = "SPAN";
var inputText = "INPUT";
var imgText = "IMG";
var brText = "BR";
var ulText = "UL";
var liText = "LI";
var noneText = "none";
var blockText = "block";
var xText = "x";
var changedText = "\u00a0 (Values changed.)";
var changeNode = null;
var configNode = null;
var filteredNode = null;
var scoreThreshold = defaultScoreThreshold;
var percentThreshold = defaultPercentThreshold;
var scoreMax = defaultScoreMax;
var noTrsChecked = false;

var abefActiveControl = null;

async function getAbefConfiguration()
{
	var abefParam = await GM.getValue("scoreThreshold", xText);
	if (abefParam != xText)
	{
		scoreThreshold = abefParam - 0;
	}
	else
	{
		GM.setValue("scoreThreshold", scoreThreshold + "");
	}
  
	abefParam = await GM.getValue("percentThreshold", xText);
	if (abefParam != xText)
	{
		percentThreshold = abefParam - 0;
	}
	else
	{
		GM.setValue("percentThreshold", percentThreshold + "");
	}
  
	abefParam = await GM.getValue("scoreMax", xText);
	if (abefParam != xText)
	{
		if (isNaN(parseInt(abefParam)))
		{
			scoreMax = "";
		}
		else
		{
			scoreMax = abefParam - 0;
		}
	}
	else
	{
		GM.setValue("scoreMax", scoreMax + "");
	}

	abefParam = await GM.getValue("noTrsChecked", xText);
	if (abefParam != xText)
	{
		noTrsChecked = abefParam;
	}
	else
	{
		GM.setValue("noTrsChecked", noTrsChecked);
	}
}

function updateAbefConfiguration()
{
	GM.setValue("scoreThreshold", scoreThreshold + "");
	GM.setValue("percentThreshold", percentThreshold + "");
	GM.setValue("scoreMax", scoreMax + "");
	GM.setValue("noTrsChecked",noTrsChecked);
}

function showFilteredCount()
{
	filteredNode.nodeValue =
		"\u00a0 " + filteredCount + " entr"+ (filteredCount != 1 ? "ies" : "y") + " filtered from view";
	configNode.parentNode.appendChild(filteredNode);
}

function newFilterValues(event)
{
	if (event != null)
	{
		event.preventDefault();
		event.stopPropagation();
	}
	if (configNode.nextSibling == changeNode)
	{
		configNode.parentNode.removeChild(changeNode);
	}

	var fNode = document.getElementById("fbScore");
	scoreThreshold = fNode.value - 0;	// force numeric
	fNode = document.getElementById("fbPercent");
	percentThreshold = fNode.value - 0;
	fNode = document.getElementById("fbMax");
	if (isNaN(parseInt(fNode.value)))
	{
		scoreMax = "";
	}
	else
	{
		scoreMax = fNode.value - 0;
	}
	fNode = document.getElementById("fbTrs");
	noTrsChecked = fNode.checked;
	updateAbefConfiguration();
	walkTheListings();
}

function changedFilter(event)
{
	if (event != null)
	{
		event.preventDefault();
		event.stopPropagation();
	}
	if (configNode.nextSibling == changeNode)
	{
		return;
	}
	if (configNode.nextSibling == filteredNode)
	{
		configNode.parentNode.removeChild(filteredNode);
	}
	configNode.parentNode.appendChild(changeNode);
}

function getFeedbackScore(textNode)
{
	if (!textNode.nodeValue)
	{
		return xText;
	}
	var s = textNode.nodeValue;
	var result;
	if ((result = s.match(/([\.,0-9]+)/)) != null)
	{
		// remove comma and period
		result[1] = result[1].replace(/[\.,]/g, "");
		return result[1] - 0;	// force numeric
	}
	return xText;
}

function getFeedbackPercent(textNode)
{
	if (!textNode.nodeValue)
	{
		return xText;
	}
	var s = textNode.nodeValue;
	var result;
	// globalize by accepting comma as a period
	if ((result = s.match(/([\.,0-9]+)%/)) != null)
	{
		// change comma, if any, to period
		result[1] = result[1].replace(/,/, ".");
		return result[1] - 0;	// force numeric
	}
	return xText;
}

function shouldFilterOut(spanNode)
{
	var cNode = spanNode.firstChild;
	var validScore = false;
	var validPercent = false;
	var feedbackScore = 0;
	var feedbackPercent = 0;

	var parsed;
	while (!validScore || !validPercent)
	{
		parsed = false;
		if (cNode && spanNode.nodeName.toUpperCase() === spanText && cNode.nodeName === "#text")
		{
			if (!validPercent)
			{
				if ((feedbackPercent = getFeedbackPercent(cNode)) != xText)
				{
					validPercent = true;
					parsed = true;
				}
			}
			if (!validScore && !parsed)
			{
				if ((feedbackScore = getFeedbackScore(cNode)) != xText)
				{
					validScore = true;
				}
			}
		}
		spanNode = spanNode.nextSibling;
		if (!spanNode)
		{
			break;
		}
		cNode = spanNode.firstChild;
	}

	var retValue = 0;
	if (validScore && feedbackScore < scoreThreshold)
	{
		retValue |= FILTER_SCORE_FLAG;
	}
	if (validPercent && feedbackPercent < percentThreshold)
	{
		retValue |= FILTER_PERCENT_FLAG;
	}
	if (validScore && !isNaN(parseInt(scoreMax)) && feedbackScore > scoreMax)
	{
		retValue |= FILTER_SCOREMAX_FLAG;
	}
	if (retValue)
	{
		filteredCount++;
	}

	return retValue;
}

function walkTheListings()
{
	filteredCount = 0;

	var xpath = "//div[@id='ResultSetItems']//li[contains(@id, 'item') and contains(@class, 'lvresult')]";

	var liNodes = document.evaluate(
		xpath,
		document,
		null,
		XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
		null
	);

	var entryCounter = 0;
	for (var loopVar = 0; loopVar < liNodes.snapshotLength; loopVar++)
	{
		var liNode = liNodes.snapshotItem(loopVar);
		var detailNode = liNode.firstChild;
		while (detailNode && (!detailNode.className || detailNode.nodeName.toUpperCase() !== ulText || detailNode.className.indexOf('lvdetails') < 0))
		{
			detailNode = detailNode.nextSibling;
		}
		if (!detailNode)
		{
			continue;
		}

		var subLiNode = detailNode.firstChild;
		var spanNode = null;
		var found = false;
		while (subLiNode)
		{
			spanNode = subLiNode.firstChild;
			while (spanNode)
			{
				if (spanNode.nodeName.toUpperCase() === spanText && spanNode.className === 'selrat')
				{
					found = true;
					break;
				}
				spanNode = spanNode.nextSibling;
			}
			if (!found)
			{
				subLiNode = subLiNode.nextSibling;
			}
			else
			{
				break;
			}
		}
		if (!spanNode)
		{
			continue;
		}

		var topSellerFilter = false;
		if (noTrsChecked)
		{
			var checkLiNode = subLiNode.nextSibling;
			while (checkLiNode)
			{
				if (checkLiNode.nodeName.toUpperCase() === liText)
				{
					var checkNode = checkLiNode.firstChild;
					while (checkNode && checkNode.nodeName.toUpperCase() !== spanText)
					{
						checkNode = checkNode.nextSibling;
					}
					if (checkNode)
					{
						// got SPAN
						checkNode = checkNode.firstChild;
					}
					while (checkNode && (checkNode.nodeName.toUpperCase() !== imgText || checkNode.className.indexOf("iconETRS") < 0))
					{
						checkNode = checkNode.nextSibling;
					}
					if (checkNode)
					{
						// found top seller IMG
						topSellerFilter = true;
						filteredCount++;
						break;
					}
				}
				checkLiNode = checkLiNode.nextSibling;
			}
		}

		if (topSellerFilter || shouldFilterOut(spanNode))
		{
			if (liNode.style.display != noneText)
			{
				liNode.style.display = noneText;
			}
		}
		else
		{
			liNode.style.display = blockText;
		}
	}
	showFilteredCount();
}

function buildControls()
{
	var xpath;
	var divNodes;

	xpath = "//div[@id='RelatedSearchesDF']";

	divNodes = document.evaluate(
		xpath,
		document,
		null,
		XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
		null
	);

	var isRelatedSearch = true;
	if (divNodes.snapshotLength <= 0)
	{
//		return false;
		isRelatedSearch = false;
		xpath = "//div[@id='TopPanelDF']";
		divNodes = document.evaluate(
			xpath,
			document,
			null,
			XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
			null
		);
		if (divNodes.snapshotLength <= 0)
		{
			return false;
		}
	}

	changeNode = document.createTextNode(changedText);
	filteredNode = document.createTextNode("\u00a0 0 entries filtered from view");

	var divSibling;

	divSibling = divNodes.snapshotItem(0);

	var newDivNode = document.createElement(divText);

	if (!isRelatedSearch)
	{
		newDivNode.appendChild(document.createElement(brText));
	}

	var span1Node = document.createElement(spanText);
	span1Node.style.fontWeight = "bold";
	var input1Node = document.createElement(inputText);
	input1Node.type = "text";
	input1Node.size = 4;
	input1Node.style.textAlign = "right";
	input1Node.setAttribute("id", "fbScore");
	input1Node.defaultValue = scoreThreshold;
	input1Node.addEventListener('change', changedFilter, false);
	span1Node.appendChild(input1Node);
	span1Node.appendChild(document.createTextNode(" Minimum feedback score\u00a0\u00a0\u00a0\u00a0"));

	newDivNode.appendChild(span1Node);
	var span2Node = document.createElement(spanText);
	span2Node.style.fontWeight = "bold";
	var input2Node = document.createElement(inputText);
	input2Node.type = "text";
	input2Node.size = 4;
	input2Node.style.textAlign = "right";
	input2Node.setAttribute("id", "fbPercent");
	input2Node.defaultValue = percentThreshold;
	input2Node.addEventListener('change', changedFilter, false);
	span2Node.appendChild(input2Node);
	span2Node.appendChild(document.createTextNode("% Minimum positive feedback\u00a0\u00a0\u00a0\u00a0"));
	newDivNode.appendChild(span2Node);

	var spanMaxNode = document.createElement(spanText);
	spanMaxNode.style.fontWeight = "bold";
	var inputMaxNode = document.createElement(inputText);
	inputMaxNode.type = "text";
	inputMaxNode.size = 4;
	inputMaxNode.style.textAlign = "right";
	inputMaxNode.setAttribute("id", "fbMax");
	inputMaxNode.defaultValue = scoreMax;
	inputMaxNode.addEventListener('change', changedFilter, false);
	spanMaxNode.appendChild(inputMaxNode);
	spanMaxNode.appendChild(document.createTextNode(" Maximum feedback score (blank to disable)\u00a0\u00a0\u00a0\u00a0"));
	newDivNode.appendChild(spanMaxNode);

	var spanTrsNode = document.createElement(spanText);
	spanTrsNode.style.fontWeight = "bold";
	var inputTrsNode = document.createElement(inputText);
	inputTrsNode.type = "checkbox";
	inputTrsNode.size = 4;
	inputTrsNode.style.textAlign = "right";
	inputTrsNode.setAttribute("id", "fbTrs");
	inputTrsNode.checked = noTrsChecked;
	inputTrsNode.defaultChecked = noTrsChecked;
	inputTrsNode.addEventListener('change', changedFilter, false);
	spanTrsNode.appendChild(inputTrsNode);
	spanTrsNode.appendChild(document.createTextNode(" No Top-rated Sellers"));
	newDivNode.appendChild(spanTrsNode);

	// second line
	newDivNode.appendChild(document.createElement(brText));
	configNode = document.createElement(buttonText);
	configNode.appendChild(document.createTextNode("Update Filter"));
	configNode.addEventListener('click', newFilterValues, false);
	newDivNode.appendChild(configNode);

	divSibling.parentNode.insertBefore(document.createElement(brText), divSibling.nextSibling);
	divSibling.parentNode.insertBefore(document.createElement(brText), divSibling.nextSibling);

	divSibling.parentNode.insertBefore(newDivNode, divSibling.nextSibling);

	return true;
}

async function init()
{
	await getAbefConfiguration();
  if (!buildControls())
	{
		return;
	}
	walkTheListings();
}

function main()
{
	if (!GM.setValue)
	{
		return;
	}
	init();
}


main();