- // 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();
-