EBay: Custom Page Controls And Seller Block List

Adds more flexibility to eBay search results. Also, includes a seller block list and can filter out result items from blocked sellers.

// ==UserScript==
// @name        EBay: Custom Page Controls And Seller Block List
// @author		Scott Michaels
// @description Adds more flexibility to eBay search results. Also, includes a seller block list and can filter out result items from blocked sellers.
// @namespace   http://se7en-soft.com
// @include     http*://*.ebay.*/sch/*
// @include		http*://*.ebay.*/dsc/*
// @include		http*://*.ebay.*/mbf/PurchaseHistory?MyEbay*
// @include		http*://*.ebay.*/ws/eBayISAPI.dll?MyEbay*
// @include		http*://*.ebay.com/myb/*
// @version     2017.11.26.2
// @grant		metadata
// @grant       unsafeWindow
// @grant		GM_xmlhttpRequest
// @grant		GM_addStyle
// @grant		GM_getValue
// @grant		GM_setValue
// @grant		GM_deleteValue
// @grant		GM_setClipboard
// @connect     shiptrack.ebay.com
// @connect     *
// @run-at      document-start
// @nocompat    Chrome
// ==/UserScript==

(function(){	
	const nav = navigator;
	const codeName = nav.appCodeName;
	const product = nav.product;
	const vendor = nav.vendor;

    const wait = function(func, howLong) {
        setTimeout(()=>{func();}, howLong);
    };

	const SharedObjects = {
		
		//Variables
		StartupInterval: 0,
		
		//Interval values
		Intervals : {
			Startup: 60,
			Timeout: 100
		},
		
		//local arrays
		Arrays : {
			OriginalPrices: [],
			ResultItems: [],
			FilteredItems: [],
			AllSellerData: [],
			TruncatedSellers: [] //jagged array
		},
		
		//strings
		Constants : {
			IsChrome: vendor === "Google Inc.",
			
			ClipboardIcon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAiJJREFUeNqMU0toE1EUPRkmaYY4pBRMCYKkFQJtSF1UiriwK3WjbkSxKhbiQuhCaDeFLgpi6aa7KoiLBkRBEXUhgtSFi6wsVGgaS2msTVM/MZR+YhOM7czEe19+b0ooXji8zz3nvvuZcTzohTCHAzVLaR2uVU/oPG0PV67WA4WFt21/FnernFKpvKp1GR4RunnT8mtR7Tp3xncycsfP54/Rycz36Zej8MKocD8RbouH7/fWAsyevRLpTiQSONI/BbfbBa/3kHDkcnkUi7v48fgWwuEw3r+IcoATIgPTqqcQHJjCzJOniMWmSeyFrutwOp3IFwr4ncvBd3oIwb6rePc8WtOoplkPwGVdu3EdqZUVNLK29nbBkTW2DEzqjGVZOBoINAxgkq+kKJA1CkerwqIAp14HYRAxNjeHRDqNL9ksUpmMuGMwR9aohmV/4cOFBRjk6QmFbK8blbwdNG9ZoxqmvYT4yDBC98Yxv7xMk3BT94tiZdM0Dcf8fsga1TDsAYJ3x7BHmXRQwxr2gTiyRuFoVXAJo890ke4M9SBJPWB84x7QHYM5skbd21fCyKUN4Tje2dmwB8yRNUo6L2VAzocTE6Lbs/E4vq6t4fPSklgZfC9KqPBZq776CVxsBfyu8hgjg4MizfC+KVSnxJy/9EtlCG+y5Z9Jo41+uZkO9NlqHg8OMuZs7pB4Gz467ogABF+6gORNvQn/Y1slJFnDrfknwAA40SgApj324QAAAABJRU5ErkJggg==",
			CloseButtonIcon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACbklEQVR42qWTXUiTURjH/+/XPt5tbII1lokoSSAV7Cqo0UU5RkVpTIOiUqEb+6DvKKEcBhZhHxfSboS0Dy9aiyxK1rSLWNHdbkQQw5KMZUnb2N53be+7d529OpnOLqLn5jz/85zzO895nnMo/KdRxYIHbpmB2giwn8jfy9bqKoAnUWBSBM6VADRAd295+eU16TTaEok3P4FGMp1aCOttwNBjk8k5odXi1Nzc9QzQsQhggEs9Fkv3vtZWKidJ+DgwgOPJ5MgvYG8+vgp40Wc01m9qaQHFcXjW35+7EIt1ZIEbeYDOBcS8zc1azmZTj8vGYgj5/TgpCKP5BV6DYcdmtxuMxaLGpUgE7T5fOgBYClc4dNFsvn/C6WRpnp+HJBL4EAio/haXC4zJpPqKKKI3GJRvxuNtRD4qLqL7NM8Pnnc4NAWIIgjqSBsMi5t7QqHMXVE8SKS/pAvEdrVrtU+v2O36AqRg+c3XwuGUN51uIvL1im0kZrQC714aDPbqurolgS/j49gjCOHvwDYikysBTGsJeZDjHBurqgBS7SVGujM2PY0DkhSaIZmSmUQxwFQDDPtZdus6qxUUw6iTciajjqxGo465bBafZmfhluX3U8DOPEQF6IDbYzR9prKsjFSMVhdnyImH4/Efef+h2bxaU8hIUTATjWKDotwhr+ysCtADnucs2+nUE4+ikJJlNInit2GgXq0sMOLj+Qo9y5I0cgimUmiU5S4C6Cx+yp4HNH11N8NQDZL0+e385qmFcM12AhniuOpX2WzuiKJ0kct5SrpAkj9WCxydABqI/LqsQ5XryX+YBPoU4N7f2vjP9gfr3dEZbh82JAAAAABJRU5ErkJggg==",
			ReloadButtonIcon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAyUlEQVR42mNkoBAwkqGHHYh/EjLAGIjjgFgaquYJEC8E4nNA3ArEM4H4ETYDmIG4H4j/AfEkIL4HFVcC4jyo+l9APAWIH2IzAKTpIBCvRRMXAGJ1IA4G4gwg1sVmAMjZsUBcgMVLmkDsh8SfDsSf0A2YCMX3GEgAyAasAeIQIvXpAPEVSgyAqyXHC7AYKSAlEJHBBCBeDMRn0Q0AAVzRCAOgaLSHuoABmwGEEhITEBcC8V9cBiB7B5aUQeApEC+CORsZkJOZqGsAAKyGKhGReVeSAAAAAElFTkSuQmCC",
			ShowTopPager: "EBay.SellerBlockList.Property.ShowTopPager",
			RankByPrice: "EBay.SellerBlockList.Property.RankByPrice",
			FixLinks: "EBay.SellerBlockList.Property.FixLinks",
			FilterResults: "EBay.SellerBlockList.Property.FilterResults",
			UsePlaceholders: "EBay.SellerBlockList.Property.UsePlaceholders",
			BlockedSellerList: "EBay.SellerBlockList.Property.BlockedSellersList",
			RemoveSponsoredItems: "Ebay.SellerBlockList.Property.RemoveSponsoredItems",
			//StyleSheetLocation: "https://cdn.se7en-soft.com/greasemonkey/ebayenhancer/ebay.css",
			StyleSheetLocation: "https://gist.githubusercontent.com/se7ensoft/8ae678371193d638cd76a73961fe48a7/raw/41fed22db26e50e823f8d921d830ac466553b867/ebay.css",
			NegativeFeedbackLink: "http://feedback.ebay.com/ws/eBayISAPI.dll?ViewFeedback2&userid=[SELLERNAME]&myworld=true&items=25&iid=-1&de=off&which=negative&interval=365",
            SearchTrackingLink: "https://www.google.com/search?source=hp&ei=cOYaWtmMO4ujggeVnZjACw&q=[TRACKING#]&oq=[TRACKING#]&gs_l=psy-ab.12...0.0.0.1722.0.0.0.0.0.0.0.0..0.0....0...1c..64.psy-ab..0.0.0....0.c9-bd2l-Fo8"
		}
			
	};
	
	//local property object
	const ScriptProps = {};

	const EBayUsabilityEnhancer = {
		
		Initialize : function(){

			//Get object properties setup for access
			Utilities.SetupProperties();
			
			//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			//Get the ball rollin'.
			const pgSetup = this.SetupPage.bind(this);
			SharedObjects.StartupInterval = setInterval(pgSetup, SharedObjects.Intervals.Startup);
			//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		
			if(SharedObjects.Constants.IsChrome)
				document.getElementsByTagName("body")[0].style.overflowY = "auto";
		},
		
		//InjectStyles: Injects a style file from cdn.se7en-soft.com. This file will eventually be hosted elsewhere.
		//This file contains element styles that are used througout this script.
		InjectStyles : function(callback){
			try{
				GM_xmlhttpRequest({
					method: "GET",
					url: SharedObjects.Constants.StyleSheetLocation + "?" + Math.random(),
					onload: function(r){
						const css = r.responseText;
						GM_addStyle(css);
						
						if(callback)
							callback.call(this);
					}
				});
			}catch(e){
				alert(e);
			}
		},
		
		SetupPage : function() {
			const self = this;
			const winLoc = window.location.href;
			
			//determine if we're on the Purchase History page or not.
			if(winLoc.indexOf("PurchaseHistory?") !== -1 || winLoc.indexOf("eBayISAPI.dll?MyEbay") !== -1){
				const ordersDiv = document.getElementById("orders");
				if(ordersDiv){
					clearInterval(SharedObjects.StartupInterval);
					const itemsContainer = ordersDiv.querySelector("div.ajax-wrap div.result-set-r");
					
					if(itemsContainer){
						const itemContainers = itemsContainer.querySelectorAll("div.order-r");
						const len = itemContainers.length;
						for(let i = 0; i < len; i++){
							const item = itemContainers[i];
							
							const sellerLink = item.querySelector("div div a.seller-id");
							if(sellerLink){
								//append X button to allow blocking this seller...
								const sellerName = sellerLink.textContent.trim();
								const img = new Image();
							
								img.setAttribute("style", "height:12px;width:12px;cursor:pointer;");
								img.setAttribute("Seller-ID", sellerName);
								
								sellerLink.parentNode.appendChild(img);
								
								img.onload = function(){
									const seller = this.getAttribute("Seller-ID");
									this.title = "Add seller '" + seller + "' to the block list.";
									this.addEventListener("click", function(){
										SellerManager.ConfirmBlockSeller(seller);
									}, false);
								};
								
								img.src = SharedObjects.Constants.CloseButtonIcon;
							}
						}
					}
				}
				
				const trackingLabelContainers = document.getElementsByClassName("tracking-label");
				if(trackingLabelContainers && trackingLabelContainers.length > 0){
					const containers = [].slice.call(trackingLabelContainers);
					containers.forEach(function(labelContainer, index){
						
						const labelLink = labelContainer.querySelector("a");
						const href = labelLink.dataset.url;
						const b = labelLink.querySelector("b");
						if(b)
							labelLink.removeChild(b);

                        const trackingNumber = labelLink.textContent;
						labelLink.textContent = trackingNumber;

                        const trackingLink = document.createElement("a");
						trackingLink.textContent = " [Tracking # Search]";

                        let tHref = SharedObjects.Constants.SearchTrackingLink.replace("[TRACKING#]", trackingNumber);
                        while(tHref.indexOf("[TRACKING#]") !== -1)
                            tHref = tHref.replace("[TRACKING#]", trackingNumber);

                        trackingLink.href = tHref;
						trackingLink.target = "_blank";

						labelContainer.appendChild(trackingLink);
					});
				}
				
			} else { //we're on an item results page
			
				//find DIV elements for each product item in the search page
				const itmLinks = document.getElementsByClassName("sresult");
				
				//gotz links?
				if(itmLinks && itmLinks.length > 0){
					
					//remove the interval that was set when the function was first kicked off.
					clearInterval(SharedObjects.StartupInterval);
					
					try{
						//download and inject the css styles for our elements
						this.InjectStyles(() => {
							self.BeginOverlayConstruction();
						});
					} catch(e){
						alert(e);
					}

					//get the item prices and stuff them into an array
					Utilities.GetItemPrices();
					
					//loop through the collection of DIV elements
					for(let i = 0; i < itmLinks.length; i++){
					
						//get the DIV at index 'i', from the collection
						const dLnk = itmLinks[i];
						SharedObjects.Arrays.ResultItems.push(dLnk);
					}

				}
			}
		},
		
		//BeginOverlayConstruction: Begins construction of the control and display it on the page.
		BeginOverlayConstruction : function(){

			//create the top pagination bar
			UIBuilder.CreateTopPagerControl();
			
			//create the overlay container
			const overlay = UIBuilder.BuildOverlay();
			
			//add controls to the overlay container
			UIBuilder.BuildOverlayControls(overlay);
			
			const badSellerBox = document.getElementById("GM_eBayOverlay_BadSellerBox");
			UIBuilder.BuildBlockedSellersBox(badSellerBox);
			SellerManager.AddBlockedSellers();
		
			//setup the controls on the overlay container.
			UIBuilder.OverlaySetup();
			
			//show the primary control overlay!
			document.getElementById('GM_eBayOverlayContainer').style.visibility = 'visible';
		},
		
		//ToggleResultOrder: Reorders items from lowest to highest, or returns them to their original order.
		ToggleResultOrder : function(lowToHigh){
			if(SharedObjects.Arrays.OriginalPrices.length == 0) return;
			
			const pArray = SharedObjects.Arrays.OriginalPrices.slice(0);
			
			if(lowToHigh) {
				try{
					pArray.sort(SortFunctions.SortLowToHigh);
				} catch(e){
					alert(e);
				}
			}	
			
			const results = document.getElementById("ListViewInner");
			//var results = document.getElementsByClassName("rsittlref");
			//var resultParent = results[0].parentNode;

			while(results.childNodes.length)
				results.removeChild(results.firstChild);
				
			for(let i = 0; i < pArray.length; i++)
				results.appendChild(pArray[i][0]);
		},
		
		//ToggleLinkFix: Toggles links to open in a new tab/window, or not.
		ToggleLinkFix : function(openNew){
			
			for(let i = 0; i < SharedObjects.Arrays.ResultItems.length; i++){
				const dLnk = SharedObjects.Arrays.ResultItems[i];
				
				//grab the anchor element from the H3 or H4 title element
				const anchor = dLnk.querySelector('h3 a') || dLnk.querySelector("h4 a");
				
				if(anchor){
					if(openNew){
						//get the link pointer location
						const href = anchor.href;
						
						//attach to the 'onClick' event for the anchor element so that it opens the link in a new window/tab
						anchor.setAttribute('onclick',"window.open('" + href + "');return false;");
					} else {
						anchor.removeAttribute('onclick');
					}
				}
			}
		},
		
		//ToggleTopPager: Toggles the visibility of the top pagination control.
		ToggleTopPager : function(show){
			document.getElementById("Pagination2").style.visibility = show ? 'visible' : 'collapse';
		},
		
		//ToggleResultFilterBar: Toggles the display of the filtered result bar.
		ToggleResultFilterBar : function(result, memberName, show){
			let filterBar = result.querySelector("tr.GM_eBayBadSeller_ResultFilterBarContainer");
			
			if(memberName.lastIndexOf("..") !== -1){
				const badSellers = ScriptProps.BlockedSellerList;
	
				const len = badSellers.length;
				for(let i = 0; i < len; i++){
					const bs = badSellers[i].toLowerCase();
						
					let pSellerName;
					if(memberName.lastIndexOf("..") !== -1){
							pSellerName = memberName.substring(0, memberName.lastIndexOf("..") - 1);
					} else 
						pSellerName = memberName;
					
					if(bs.indexOf(pSellerName) !== -1){
						memberName = bs;
						break;
					}
				}
			}

			if(show){
				if(!filterBar){
					
					filterBar = document.createElement("tr");
					filterBar.setAttribute("class", "GM_eBayBadSeller_ResultFilterBarContainer");
					
					const cell = document.createElement("td");
					filterBar.appendChild(cell);
					
					const dvBar = document.createElement("div");
					dvBar.setAttribute("class", "GM_eBayBadSeller_ResultFilterBar");
					cell.appendChild(dvBar);
					
					const table = document.createElement("table");
					table.style.width = "100%";
					const tRow = document.createElement("tr");
					table.appendChild(tRow);
					
					for(let i = 0; i < 4; i++){
						const tCell = document.createElement("td");
						tRow.appendChild(tCell);
					}
					
					dvBar.appendChild(table);
					
					let span = document.createElement("span");
					span.setAttribute("class", "GM_eBayBadSeller_FilterBarHeader");
					span.textContent = "Filtered Result [" + memberName + "]";
					
					tRow.childNodes[0].appendChild(span);
				
					span = document.createElement("span");
					span.setAttribute("class", "GM_ebayHeader");
					
					const link = document.createElement("a");
					link.setAttribute("class", "GM_eBayBadSeller_FilterBarLink");
					link.setAttribute("target", "_blank");
					link.textContent = "(View Listing)";
					link.title = "View item listing.";
					

					let anchor = result.querySelector("h3 a");
					
					if(!anchor)
						anchor = result.querySelector("h4 a");
					
					link.href = anchor.href;
					span.appendChild(link);
					
					tRow.childNodes[2].appendChild(span);
								
					span = document.createElement("span");
					span.title = "Remove";
					span.textContent = "X";
					span.setAttribute("class", "GM_eBayBadSeller_FilterBarCloseButton");
					
					span.addEventListener('click', function(e){
						let p = e.target.parentNode;
						while(p != null){
							if(p.getAttribute("class") == "GM_eBayBadSeller_ResultFilterBarContainer")
								break;
								
							if(p.parentNode != null)
								p = p.parentNode;
							else 
								break;
						}
						
						p.parentNode.setAttribute("data-removed", true);
						p.style.visibility = 'collapse';
					});

					tRow.childNodes[3].appendChild(span);
						
					const tbody = result.querySelector("tbody");
					const id = result.attributes["listingid"].value;
					
					
					const children = [];
					while(result.firstChild){
						children.push(result.firstChild);
						result.removeChild(result.firstChild);
					}
					
					result.style.display = "none";
					
					SharedObjects.Arrays.FilteredItems.push([children,id,memberName]);
						
					if(!ScriptProps.UsePlaceholders)
						filterBar.style.visibility = 'collapse';
					
					if(!result.attributes["data-removed"])
						result.appendChild(filterBar);
						
				} else {		
					filterBar.style.visibility = ScriptProps.UsePlaceholders ? 'visible' : 'collapse';
				}
			} else {
				if(filterBar)
					filterBar.style.visibility = 'collapse';
				
				const id = result.attributes["listingid"].value;
				const len = SharedObjects.Arrays.FilteredItems.length;
				let sellerData, content;
				
				for(let i = 0; i < len; i++){
					const fItem = SharedObjects.Arrays.FilteredItems[i];
					if(fItem[1] == id){
						const seller = SellerManager.GetSellerById(id);
						if(seller && seller.ID.lastIndexOf("...") !== -1){
							const fuzzyMatch = SellerManager.FuzzyMatchedName(seller.ID);
							if(fuzzyMatch){
								content = fItem[0];
								break;
							}
						} else if(seller && seller.ID === memberName){
							content = fItem[0];
							break;
						}
					}
				}
				
				if(content){
					for(let n = 0; n < content.length; n++){
						const c = content[n];
						result.appendChild(c);
					}
					
					if(filterBar)
						result.removeChild(filterBar);
					
					result.style.display = "block";
				}
			}
		},
		
		ToggleRemoveSponsoredItemResults : function(){
			const allItems = document.querySelectorAll("li.sresult.lvresult");
			for(let itm of allItems){ 
				const sponsoredSpan = itm.querySelector("div.promoted-lv");
				if(sponsoredSpan) {
					itm.style.display = ScriptProps.RemoveSponsoredItems ? "none" : "block";
				}
			}
		},

        ToggleRemoveTopRatedItemResults : function(){
            const allItems = document.querySelectorAll("li.sresult.lvresult");
			for(let itm of allItems){
				const sponsoredSpan = itm.querySelector("img.iconETRS2");
				if(sponsoredSpan) {
					itm.style.display = ScriptProps.RemoveTopRatedItems ? "none" : "block";
				}
			}
        },
		
		//ToggleFilterBlockedSellerResults: Toggles blocked seller item results.
		ToggleFilterBlockedSellerResults : function(filterThem){
			const resultCount = SharedObjects.Arrays.OriginalPrices.length;
			
			const fiaLen = SharedObjects.Arrays.FilteredItems.length;
			
			 if(fiaLen > 0){
				for(let i = 0; i < fiaLen; i++){
					const filteredItem = SharedObjects.Arrays.FilteredItems[i];
					const itemId = filteredItem[1];
					const priceItem = Utilities.GetResultItem(itemId);
					if(priceItem){
						const memberName = filteredItem[2];
						this.ToggleResultFilterBar(priceItem, memberName, filterThem);
					}
				}
			}
			
			if(filterThem){
				for(let i = 0; i < resultCount; i++){
					
					const resultItem = SharedObjects.Arrays.OriginalPrices[i][0];
					const listingId = resultItem.attributes["listingid"].value;
					let skip = false;
					
					//loop through the 'SharedObjects.Arrays.FilteredItems' and see if the current 'resultItem' is contained in it.
					//if so, we'll not double process it. The check is based on the listingId value.
					for(let n = 0; n < SharedObjects.Arrays.FilteredItems.length; n++){
						const fi = SharedObjects.Arrays.FilteredItems[n];
						const fid = fi[1];
						if(fid === listingId){
							skip = true;
							break;
						}
					}
									
					//should we skip passed everything and continue?
					if(skip) continue;
					
					const sd = SellerManager.GetSellerById(listingId);
					
					if(sd){
						let userName = sd.ID;

						if(userName.length > 0){
							const fn = SellerManager.FuzzyMatchedName(userName);
							userName = fn || userName;
							if(SellerManager.IsBlockedSeller(userName, true)){
								
								const blockedUserSpan = document.getElementById("EBayEnhancer_BlockedSeller_" + userName);
								blockedUserSpan.style.opacity = "1";
								this.ToggleResultFilterBar(resultItem, userName, true);
							}
						}
					}
				}
			} else {
				for(let i = 0; i < resultCount; i++){
					const result = SharedObjects.Arrays.OriginalPrices[i];
					this.ToggleResultFilterBar(result[0], "", false);
				}
			}
		},
		
		//ToggleFilteredItemPlaceholders: Toggles visibility of removed item placeholders.
		ToggleFilteredItemPlaceholders : function(show){
			const filterBars = document.getElementsByClassName("GM_eBayBadSeller_ResultFilterBarContainer");
			const len = filterBars.length;
			
			if(!show){	
				if(len > 0){
					for(let i = 0; i < len; i++){
						const fbar = filterBars[i];
						fbar.style.visibility = 'collapse';
						fbar.parentNode.style.display = "none";
					}
				}
			} else {
				if(len > 0){
					for(let i = 0; i < len; i++){
						const fbar = filterBars[i];
						const removed = fbar.parentNode.attributes["data-removed"];
						if(removed == undefined){
							fbar.style.visibility = 'visible';
							fbar.parentNode.style.display = "block";
						}
					}
				}
			}
		},
		

		//CleanGMValues: Deletes all saved values associated with this script.
		CleanGMValues : function(){
			GM_deleteValue(SharedObjects.Constants.BlockedSellerList);
			GM_deleteValue(SharedObjects.Constants.FilterResults);
			GM_deleteValue(SharedObjects.Constants.RemoveSponsoredItems);
			GM_deleteValue(SharedObjects.Constants.ShowTopPager);
			GM_deleteValue(SharedObjects.Constants.RankByPrice);
			GM_deleteValue(SharedObjects.Constants.FixLinks);
			GM_deleteValue(SharedObjects.Constants.UsePlaceholders);
		},
		
		ResetAll : function(){
			this.CleanGMValues();
			location.reload();
		}
		
	};

	const UIBuilder = {
		//BuildOverlay: Builds the control overlay.
		BuildOverlay : function(){

			const rightPanel = document.getElementById("RightPanel");

			const overlayContainer = document.createElement("div");
			overlayContainer.id = "GM_eBayOverlayContainer";
			//set the overlay to be initially collapsed. we'll show it after the style sheet has been downloaded and applied.
			overlayContainer.style.visibility = 'collapse';
			rightPanel.insertBefore(overlayContainer, rightPanel.firstChild);
				
			const overlay = document.createElement("div");
			overlay.id = "GM_eBayOverlay";
					
			const img = new Image();
			img.src = SharedObjects.Constants.ReloadButtonIcon;
			img.id = "GM_eBaySettingsResetIcon";
			img.title = "Reset All";
			img.addEventListener('click', function(){
				const result = confirm("Are you sure you want to clear and reset everything? This action cannot be undone.");
				if(result){
					//-----------------------------------
					EBayUsabilityEnhancer.ResetAll();
				}
			});
			overlay.appendChild(img);
			
			let header = document.createElement("h3");
			header.textContent = "Usability Settings";
			header.setAttribute("class", "GM_ebayHeader");
			
			overlay.appendChild(header);
			
			const controlBox = document.createElement("div");
			controlBox.id = "GM_eBayOverlay_ControlBox";
			overlay.appendChild(controlBox);
			
			header = document.createElement("h3");
			header.textContent = "Seller Block List";
			header.setAttribute("class", "GM_ebayHeader");
			
			overlay.appendChild(header);
			
			const badSellerBox = document.createElement("div");
			badSellerBox.id = "GM_eBayOverlay_BadSellerBox";
			overlay.appendChild(badSellerBox);
			
			overlayContainer.appendChild(overlay);
			
			return controlBox;
		},
		
		//BuildOverlayControls: Builds the controls for the overlay container.
		BuildOverlayControls : function(pOverlay){
			const ul = document.createElement("ul");
			ul.style.marginTop = "-8px";
			for(let i = 0; i < 6; i++){
				const li = document.createElement("li");
				ul.appendChild(li);
			}
			
			// -- Top Pager
			let cbContainer = this.BuildCheckbox("Show Pager At Top", "GM_cbShowTopPager");
			cbContainer.title = "Display a pager element at the top of the results view.";
			const cbShowTopPager = cbContainer.firstChild;
				
			cbShowTopPager.addEventListener('change', function(r) {
				const cb = r.target;
				ScriptProps.ShowTopPager = cb.checked;
				EBayUsabilityEnhancer.ToggleTopPager(cb.checked);
			});

			ul.childNodes[0].appendChild(cbContainer);
			// Top Pager --

			// -- Remove Sponsored Items
			cbContainer = this.BuildCheckbox("Remove Sponsored", "GM_RemoveSponsoredItems");
			cbContainer.title = "Remove sponsored item listings.";
			const cbRemoveSponsoredItems = cbContainer.firstChild;
			
			cbRemoveSponsoredItems.addEventListener("change", function(r){
				const cb = r.target;
				ScriptProps.RemoveSponsoredItems = cb.checked;
				EBayUsabilityEnhancer.ToggleRemoveSponsoredItemResults(cb.checked);
			});
			ul.childNodes[1].appendChild(cbContainer);
			// Remove Sponsored Items --

            // -- Remove Top Rated Items
            cbContainer = this.BuildCheckbox("Remove Top Rated", "GM_RemoveTopRatedItems");
            cbContainer.title = "Remove Top Rated item listings.";
            const cbRemoveTopRatedItems = cbContainer.firstChild;
            cbRemoveTopRatedItems.addEventListener("change", (r) => {
                const cb = r.target;
                ScriptProps.RemoveTopRatedItems = cb.checked;
                EBayUsabilityEnhancer.ToggleRemoveTopRatedItemResults(cb.checked);
            });
            ul.childNodes[2].appendChild(cbContainer);
            // Remove Top Rated Items --

			// -- Reorder Price-wise
			cbContainer = this.BuildCheckbox("Order Low To High", "GM_cbRankByPrice");
			cbContainer.title = "Re-order results by price; lowest to highest.";
			const cbReorderByPrice = cbContainer.firstChild;
			
			cbReorderByPrice.addEventListener('change', function(r){
				const cb = r.target;
				ScriptProps.RankByPrice = cb.checked;
				EBayUsabilityEnhancer.ToggleResultOrder(cb.checked);
			});
			
			ul.childNodes[3].appendChild(cbContainer);
			// Reorder Price-wise --
			
			// -- Fix Links
			cbContainer = this.BuildCheckbox("Open In New Tab", "GM_cbTabbedLinks");
			cbContainer.title = "Open item links in a new tab.";
			const cbTabbedLinks = cbContainer.firstChild;
			
			cbTabbedLinks.addEventListener('change', function(r){
				const cb = r.target;
				ScriptProps.FixLinks = cb.checked;
				EBayUsabilityEnhancer.ToggleLinkFix(cb.checked);
			});
			
			ul.childNodes[4].appendChild(cbContainer);
			// -- Fix Links
			
			// -- Filter Bad Sellers
			
			const fDiv = document.createElement("div");
			cbContainer = this.BuildCheckbox("Prune Results", "GM_cbFilterBlockedSellers");
			cbContainer.title = "Remove items posted by blocked sellers.";
			const cbBlockSellers = cbContainer.firstChild;
			cbBlockSellers.addEventListener('change', function(r){
				const cb = r.target;
				ScriptProps.FilterResults = cb.checked;
				document.getElementById('GM_cbFilterBlock_ShowPlaceholders').disabled = !cb.checked;
				EBayUsabilityEnhancer.ToggleFilterBlockedSellerResults(cb.checked);
			});
			
			fDiv.appendChild(cbContainer);
			
			const fUl = document.createElement("ul");
			const fli = document.createElement("li");
			fUl.appendChild(fli);
			
			cbContainer = this.BuildCheckbox("Use Placeholders", "GM_cbFilterBlock_ShowPlaceholders");
			cbContainer.title = "Show placeholders for removed items.";
			const cbPlaceholder = cbContainer.firstChild;
			cbPlaceholder.addEventListener('change', function(r){
				const cb = r.target;
				ScriptProps.UsePlaceholders = cb.checked;
				EBayUsabilityEnhancer.ToggleFilteredItemPlaceholders(cb.checked);
			});
			fli.appendChild(cbContainer);
			
			fDiv.appendChild(fUl);
			
			ul.childNodes[5].appendChild(fDiv);
			// Filter Bad Sellers --
			
			//append the actual list element to the overlay
			pOverlay.appendChild(ul);	
		},
		
		//CreateTopPagerControl: Creates the top pagination control.
		CreateTopPagerControl : function(){
			const paginator = document.getElementById("Pagination");
			if(paginator){
				const pager2 = paginator.cloneNode(true);
				if(pager2){
					pager2.style.visibility = 'collapse';
					pager2.id = "Pagination2";
					pager2.style.marginTop = "10px";
					pager2.style.marginBottom = "10px";
					
					const container = document.getElementById("MessageContainer");
					if(container)
						container.parentNode.insertBefore(pager2, container.nextSibling);
				}
			}
		},
		
		//OverlaySetup: Sets up the controls inside the overlay container.
		OverlaySetup : function(){

			//Checkbox setup: Show Pager At Top
			document.getElementById("GM_cbShowTopPager").checked = ScriptProps.ShowTopPager;
			setTimeout(function(){
				EBayUsabilityEnhancer.ToggleTopPager(ScriptProps.ShowTopPager);
			}, SharedObjects.Intervals.Timeout);
			
			//Checkbox setup: Order Low To High
			document.getElementById("GM_cbRankByPrice").checked = ScriptProps.RankByPrice;
			setTimeout(function(){
				EBayUsabilityEnhancer.ToggleResultOrder(ScriptProps.RankByPrice);
			}, SharedObjects.Intervals.Timeout);
			
			//Checkbox setup: Open In New Tab
			document.getElementById("GM_cbTabbedLinks").checked = ScriptProps.FixLinks;
			setTimeout(function(){
				EBayUsabilityEnhancer.ToggleLinkFix(ScriptProps.FixLinks);
			}, SharedObjects.Intervals.Timeout);
			
			//Checkbox setup: Filter Results
			document.getElementById("GM_cbFilterBlockedSellers").checked = ScriptProps.FilterResults;
			setTimeout(function(){
				EBayUsabilityEnhancer.ToggleFilterBlockedSellerResults(ScriptProps.FilterResults);
			}, SharedObjects.Intervals.Timeout);
			
			document.getElementById("GM_RemoveSponsoredItems").checked = ScriptProps.RemoveSponsoredItems;
			setTimeout(function(){
				EBayUsabilityEnhancer.ToggleRemoveSponsoredItemResults(ScriptProps.RemoveSponsoredItems);
			}, SharedObjects.Intervals.Timeout);

            document.getElementById("GM_RemoveTopRatedItems").checked = ScriptProps.RemoveTopRatedItems;
            setTimeout(()=>{
                EBayUsabilityEnhancer.ToggleRemoveTopRatedItemResults(ScriptProps.RemoveTopRatedItems);
            }, SharedObjects.Intervals.Timeout);

			//Checkbox setup: Use Placeholders
			const cbPlaceHolders = document.getElementById("GM_cbFilterBlock_ShowPlaceholders");
			cbPlaceHolders.checked = ScriptProps.UsePlaceholders;
			cbPlaceHolders.disabled = !ScriptProps.FilterResults;
			setTimeout(function(){
				EBayUsabilityEnhancer.ToggleFilteredItemPlaceholders(ScriptProps.UsePlaceholders);
			}, SharedObjects.Intervals.Timeout);
		},
		
		//BuildBlockedSellerNameLink: Builds a seller name element that allows for copying or removal of the seller from the black list.
		BuildBlockedSellerNameLink : function(sellerName){
			if(!sellerName) return null;
			
			const table = document.createElement('table');
			table.className = "GM_eBayTableElement";
			const row = document.createElement("tr");
			for(let i = 0; i < 3; i++){
				const cell = document.createElement('td');
				row.appendChild(cell);
			}
			
			table.appendChild(row);
			
			const nameSpan = document.createElement("span");
			nameSpan.id = "EBayEnhancer_BlockedSeller_" + sellerName;
			nameSpan.style.fontWeight = "bold";
			nameSpan.style.cursor = "default";
			nameSpan.className = "unselectable";
			let slrName = sellerName;
			if(slrName.length > 13)
				slrName = slrName.substr(0, 12) + "...";
			nameSpan.textContent = slrName;
			nameSpan.title = sellerName;
			nameSpan.style.opacity = ".6"; //set to 60% opacity
			
			
			row.childNodes[0].appendChild(nameSpan);
			
			const copyImage = new Image();
			copyImage.src = SharedObjects.Constants.ClipboardIcon;
			copyImage.style.cursor = "pointer";
			copyImage.title = "Copy the seller name to the clipboard.";
			copyImage.setAttribute('data-sellerName', sellerName);
			copyImage.addEventListener('click', function(r){
				const img = r.target;
				const sn = img.attributes["data-sellerName"].value;
				GM_setClipboard(sn);
			});
			
			const cell1 = row.childNodes[1];
			cell1.style.width = "16px";
			cell1.appendChild(copyImage);
			
			const removeLink = new Image();
			removeLink.src = SharedObjects.Constants.CloseButtonIcon;
			removeLink.setAttribute('data-badSellerName', sellerName);
			removeLink.title = "Remove blocked seller '" + sellerName + "'.";
			removeLink.style.cursor = "pointer";
			removeLink.addEventListener('click', function(r){
				const btn = r.target;
				const sllr = btn.getAttribute('data-badSellerName');
				const result = confirm("Are you sure you want to remove '" + sllr + "' from your seller black list?");
				if(result){
					const badSellers = ScriptProps.BlockedSellerList;
					const idx = badSellers.indexOf(sllr);
					if(idx > -1){
						
						let listings = SellerManager.GetListingsBySeller(sllr);
						
						for(let i = 0; i < listings.length; i++){
							const listing = listings[i][0];
							EBayUsabilityEnhancer.ToggleResultFilterBar(listing, sllr, false);
						}
						
						const remSellers = badSellers.splice(idx, 1);
						ScriptProps.BlockedSellerList = badSellers;

						const ulst = document.getElementById("GM_eBayBadSellers_UList");
						while(ulst.firstChild != null)
							ulst.removeChild(ulst.firstChild);
						
						SellerManager.AddBlockedSellers();
					}
				}
			}, false);
			
			const cell2 = row.childNodes[2];
			cell2.style.width = "16px";
			cell2.appendChild(removeLink);
			
			return table;
		},
		
		//BuildCheckbox: Constructs a checkbox with the given title and id.
		BuildCheckbox : function(title, id){
			const d = document.createElement("div");
			d.setAttribute("class", "cbx unselectable");
			
			const i = document.createElement("input");
			i.id = id;
			i.type = "checkbox";
			
			const l = document.createElement("label");
			l.setAttribute('for', id);
			
			const s = document.createElement("span");
			s.className = "cbx GM_cbx";
			s.textContent = title;
			l.appendChild(s);
			
			d.appendChild(i);
			d.appendChild(l);
			
			return d;
		},
		
		ImportFromFile : function(file){
			const reader = new FileReader();
			reader.onload = function(r){
				const result = r.target.result;
				if(result){
					const names = result.split("\r\n");
					if(names.length > 0){
						//add each name to the bad sellers list.
						for(let x = 0; x < names.length; x++){
							const n = names[x].trim();
							SellerManager.AddBlockedSeller(n, true);
						}
					}
				}
			};
			reader.readAsText(file, "utf-8");
		},
		
		//BuildBlockedSellersBox: Builds the bad sellers control box.
		BuildBlockedSellersBox : function(pOverlay){
			const self = this;
			const ul = document.createElement("ul");
			ul.id = "GM_eBayBadSellers_UList";
			ul.className = "GM_eBayBadSeller_NameLinkList";
			
			const d = document.createElement("div");
			d.style.marginLeft = "8px";
			d.style.display = "table";
			d.style.marginBottom = "5px";
			d.style.marginTop = "-8px";
			
			const txtAddBlockedSeller = document.createElement("input");
			txtAddBlockedSeller.type = "text";
			txtAddBlockedSeller.id = "GM_eBayNewBadSeller_InputBox";
			txtAddBlockedSeller.title = "Type or paste the name of the seller you want to block and press 'Enter'.";
			
			let style = "width:90px;margin:0;";
            if(SharedObjects.Constants.IsChrome)
                style += "height:16px;";
            
			txtAddBlockedSeller.setAttribute("style", style);
			
			txtAddBlockedSeller.addEventListener('keyup', function(e){
				const key = e.keyCode;
				if(key === 13){
					const box = document.getElementById("GM_eBayNewBadSeller_InputBox");
					const txt = box.value.trim();
					box.value = "";
					
					if(SellerManager.AddBlockedSeller(txt, true)){
						//filter results for new seller name...
						const filterUser = ScriptProps.FilterResults;
						if(filterUser != undefined && filterUser){
							EBayUsabilityEnhancer.ToggleFilterBlockedSellerResults(true);
						}
					}
				}
			});
			
			const fileInput = document.createElement("input");
			fileInput.type = "file";
			fileInput.id = "GM_eBayNewBadSeller_fileInput";
			fileInput.style.display = "none";
			fileInput.setAttribute("accept", ".txt");
			
			const btnImportSellers = document.createElement("input");
			btnImportSellers.type = "button";
			btnImportSellers.value = "I";
			btnImportSellers.id = "GM_eBayNewBadSeller_btnImportBadSellers";
			btnImportSellers.setAttribute("style", "height:26px;margin-left:-2px;width:24px;");
			btnImportSellers.title = "Import sellers from a text file.";
			btnImportSellers.onclick = function(){
				fileInput.addEventListener("change", function(evt){
					const files = evt.target.files;
					for(let i = 0; i < files.length; i++){
						const f = files[i];
						
						if(!f.type.match("text/plain"))
							continue;
						
						self.ImportFromFile(f);
					}
				}, false);
				fileInput.click();
			};
			
			const btnExportSellers = document.createElement("input");
			btnExportSellers.type = "button";
			btnExportSellers.value = "O";
			btnExportSellers.id = "GM_eBayNewBadSeller_btnExportSellers";
			btnExportSellers.setAttribute("style", "height:26px;margin-left:-2px;width:24px;");
			btnExportSellers.title = "Export sellers to a text file.";
			btnExportSellers.onclick = function(){
				const badSellers = ScriptProps.BlockedSellerList;
				let agg = "";
				
				for(let i = 0; i < badSellers.length; i++)
					agg += badSellers[i] + "\r\n";
					
				agg = agg.trim();

				const dLink = document.createElement("a");
				dLink.download = "EBayUsabilityEnhancer_BlockedSellersList.txt";
				dLink.href = "data:text/plain," + encodeURI(agg);
				dLink.style.display = "none";
				dLink.textContent = "download";
				document.body.appendChild(dLink);
				dLink.click();
				document.body.removeChild(dLink);
			};
			
			const label = document.createElement("label");
			label.textContent = "Add, Import or Export:";
			label.setAttribute('for', 'GM_eBayNewBadSeller_InputBox');
			
			const table = document.createElement("table");
			table.style.width = "100%";
			
			const row = document.createElement("tr");
			for(let i = 0; i < 2; i++)
				row.appendChild(document.createElement("td"));
			table.appendChild(row);
			
			const copyAllLink = document.createElement("span");
			copyAllLink.textContent = "Copy All";
			copyAllLink.className = "GM_eBayBadSellers_ActionLink";
			copyAllLink.title = "Copy all blocked sellers to the clipboard.";
			copyAllLink.addEventListener('click', function(e){
				//------------------------------------------
				const blockedSellers = ScriptProps.BlockedSellerList;
				if(blockedSellers){
					let sellerList = "";
					for(let i = 0; i < blockedSellers.length; i++)
						sellerList += blockedSellers[i] + ",";
					sellerList = sellerList.substr(0, sellerList.lastIndexOf(","));
					GM_setClipboard(sellerList);
					alert("All blocked sellers have been copied to the system clipboard.");
				}
			});
			
			row.childNodes[0].appendChild(copyAllLink);
			
			const clearLink = document.createElement('span');
			clearLink.textContent = "Remove All";
			clearLink.className = "GM_eBayBadSellers_ActionLink";
			clearLink.title = "Remove all sellers from the block list.";
			clearLink.setAttribute("style", "float:right;margin-right:7px;");
			clearLink.addEventListener('click', function(e){
				let blockedSellers = ScriptProps.BlockedSellerList;
				if(blockedSellers){
					const result = confirm("Are you sure you want to remove all sellers from your block list? This action cannot be undone.");
					if(result){
						//-----------------------------------
						blockedSellers = [];
						ScriptProps.BlockedSellerList = blockedSellers;
						SellerManager.AddBlockedSellers();
					}
				}
			});
			
			row.childNodes[1].appendChild(clearLink);
			
			d.appendChild(label);
			d.appendChild(txtAddBlockedSeller);
			d.appendChild(fileInput);
			d.appendChild(btnImportSellers);
			d.appendChild(btnExportSellers);
			d.appendChild(table);
			
			pOverlay.appendChild(d);

			pOverlay.appendChild(ul);
		}
	};

	const SellerManager = {
		//IsBlockedSeller: Returns whether or not the specified seller name is in the seller black list.
		IsBlockedSeller : function(sellerName, fuzzyMatch){
			if(!sellerName || sellerName.length === 0) return true;
			const badSellers = ScriptProps.BlockedSellerList;
			
			if(fuzzyMatch && fuzzyMatch === true){
				const len = badSellers.length;
				for(let i = 0; i < len; i++){
					const bs = badSellers[i].toLowerCase();
						
					let pSellerName;
					if(sellerName.lastIndexOf("...") !== -1){
							pSellerName = sellerName.substring(0, sellerName.lastIndexOf("...") - 1);
					} else 
						pSellerName = sellerName;
					
					if(bs.indexOf(pSellerName) !== -1)
						return true;
				}
			}
			
			return badSellers.indexOf(sellerName) !== -1;
		},
		
		FuzzyMatchedName : function(fuzzyName){
			if(fuzzyName.lastIndexOf("...") === -1) 
				return null;
			
			const badSellers = ScriptProps.BlockedSellerList,
				len = badSellers.length;
			let retval;
			const tLen = SharedObjects.Arrays.TruncatedSellers.length;
			
			if(tLen > 0){
				//check if we've got a match in the TruncatedSellers array.
				for(let t = 0; t < tLen; t++){
					const pair = SharedObjects.Arrays.TruncatedSellers[t];
					if(pair[0].toLowerCase() === fuzzyName.toLowerCase())
						return pair[1];
				}
			}
			
			for(let n = 0; n < len; n++){
				const bs = badSellers[n].toLowerCase();
				let pSellerName;
				
				if(fuzzyName.lastIndexOf("...") !== -1)
					pSellerName = fuzzyName.substring(0, fuzzyName.lastIndexOf("...")).toLowerCase();
				else 
					pSellerName = fuzzyName.toLowerCase();

				if(bs.indexOf(pSellerName) !== -1){
					retval = bs;
					break;
				}
			}
			
			return retval;
		},
		
		//GetSellerById: Returns a SellerData object based on the supplied listingid parameter.
		GetSellerById : function(listingid){
			const count = SharedObjects.Arrays.AllSellerData.length;
			for(let i = 0; i < count; i++){
				const sellerData = SharedObjects.Arrays.AllSellerData[i];
				const id = sellerData[1];
				if(id === listingid)
					return sellerData[0];
			}
			return null;
		},
		
		//AddBlockedSeller: Creates and adds a bad seller based on the supplied seller name.
		AddBlockedSeller : function(sellerName, bailIfExists){
			sellerName = sellerName.trim();
			
			if(sellerName.length === 0) 
				return false;
			
			const badSellers = ScriptProps.BlockedSellerList;
			if(badSellers.indexOf(sellerName) === -1){
				try{
					badSellers.push(sellerName);
				} catch(e){
					alert("T: " + e);
				}
				
				ScriptProps.BlockedSellerList = badSellers;
			} else if(bailIfExists && bailIfExists === true) {
				return false;
			}
			
			const sellerLink = UIBuilder.BuildBlockedSellerNameLink(sellerName);
			const li = document.createElement("li");
			if(sellerLink)
				li.appendChild(sellerLink);
			
			const uList = document.getElementById("GM_eBayBadSellers_UList");
			if(uList){
				const badSellers = ScriptProps.BlockedSellerList;
				badSellers.sort(SortFunctions.SortAlphaNum);
				const idx = badSellers.indexOf(sellerName);
				if(idx !== -1){
					const liChild = uList.childNodes[idx];
					uList.insertBefore(li, liChild);
				} else {
					uList.appendChild(li);
				}
				return true;
			}
			return false;
		},
		
		//AddBlockedSellers: Adds the stored bad sellers to the page.
		AddBlockedSellers : function(){
			const sellers = ScriptProps.BlockedSellerList;
			if(sellers == undefined) return;
			
			const uList = document.getElementById("GM_eBayBadSellers_UList");
			if(uList){
				while(uList.firstChild) { uList.removeChild(uList.firstChild);}
			}
			
			if(sellers){
				sellers.sort(SortFunctions.SortAlphaNum);
				
				try{
					ScriptProps.BlockedSellerList = sellers;
				} catch(e2){
					//alert("err: " + e2)
				}
			}
				
			if(sellers.length > 0){
				for(let i = 0; i < sellers.length; i++){
					const seller = sellers[i];
					this.AddBlockedSeller(seller);
				}
			}
		},
		
		//GetListingsBySeller: Returns a collection of all listings that belong to the supplied sellerName.
		GetListingsBySeller : function(sellerName){
			const count = SharedObjects.Arrays.AllSellerData.length;
			let listings = [];
			
			sellerName = sellerName.toLowerCase();
			
			for(let i = 0; i < count; i++){
				const sd = SharedObjects.Arrays.AllSellerData[i];
				const id = sd[1];
				let seller = sd[0].ID;
				
				if(seller.lastIndexOf("...") !== -1){
					const badSellers = ScriptProps.BlockedSellerList;
					const len = badSellers.length;
					
					for(let n = 0; n < len; n++){
						const bs = badSellers[n].toLowerCase();
						let pSellerName;
						
						if(seller.lastIndexOf("...") !== -1)
							pSellerName = seller.substring(0, seller.lastIndexOf("...")).toLowerCase();
						else 
							pSellerName = seller.toLowerCase();

						if(bs.indexOf(pSellerName) !== -1){
							seller = bs;
							break;
						}
					}
				}
				
				seller = seller.toLowerCase();
				sellerName = sellerName.toLowerCase();
				
				if(seller === sellerName){
					const priceCount = SharedObjects.Arrays.OriginalPrices.length;
					for(let j = 0; j < priceCount; j++){
						const pd = SharedObjects.Arrays.OriginalPrices[j];
						const pdId = pd[0].attributes["listingid"].value;
						if(id === pdId){
							listings.push(pd);
						}
					}
				}
			}
			
			return listings;
		},
		
		GetSellerData : function(resultNode){
		
			const sellerData = {
				ID: "",
				FeedbackScore: "",
				ReviewCount: ""
			};
			
			const dynDiv = resultNode.querySelector("ul.lvdetails");
			const listingId = resultNode.getAttribute("listingid");
			const len = dynDiv.childNodes.length;
			const self = this;
			
			if(len > 0) {
				
				for(let m = 0; m < len; m++){
					const n = dynDiv.childNodes[m];
					
					
					let spns;
					
					try{
						spns = n.querySelectorAll("span.selrat");
					} catch (e){
						continue;
					}
					
					if(spns){
						const text = n.textContent.trim();
						if(text.indexOf("Seller:") !== -1){
							const idx = text.indexOf("Seller:");
							const idx2 = text.indexOf("(");
							const idx3 = text.indexOf(")");
							
							sellerData.ID = text.substring(idx + 8, idx2).trim();
							
							const img = new Image();
							img.setAttribute("style", "height:12px;width:12px;cursor:pointer;");
							img.setAttribute("Seller-ID", sellerData.ID);
							
							try{
								let revCount = spns[0].textContent.replace("(", "").replace(")", "");
								while(revCount.indexOf(",") != -1)
									revCount = revCount.replace(",","");
								
								sellerData.ReviewCount = parseInt(revCount);
							}catch(e){
								//alert(e);
							}
							
							try{
								const fbScore = parseFloat(spns[1].textContent.replace("%", "").trim());
								
								sellerData.FeedbackScore = fbScore;
								
								const score = sellerData.FeedbackScore;
								let color = "";
								
								if(score >= 99.2){
									//should be ok to buy from this guy.
									color = "green";
								} else if(score >= 98 && score <= 99.1) {
									//better check the neg's!
									color = "#E8823A";
								} else {
									//bad seller!
									color = "red";
								}
								
								spns[1].setAttribute("style", "color:" + color + ";");
								
								const p = spns[0];							
								p.parentNode.appendChild(img);
							} catch(e){
								img.setAttribute("style", "margin-left:5px;height:12px;width:12px;cursor:pointer;");
								n.appendChild(img);
							}
							
							try{
								if(sellerData.ID.lastIndexOf("...") !== -1){
									Utilities.GetFullUserName(listingId, function(lstId, resultText){
										const fullUserName = Utilities.ParseNameFromTextChunk(resultText);
										if(fullUserName){
											
											//add a mapping for the truncated name and the full name.
											SharedObjects.Arrays.TruncatedSellers.push([sellerData.ID, fullUserName]);
											
											img.onload = function(){
												this.title = "Add seller '" + fullUserName + "' to the block list.";
												const seller = this.getAttribute("Seller-ID");
												img.addEventListener("click", function(){
													self.ConfirmBlockSeller(fullUserName);
												}, false);
											};
											
											img.src = SharedObjects.Constants.CloseButtonIcon;
											self.SetupFeedbackLink(fullUserName, dynDiv);
										}
									});
								} else {
									img.onload = function(){
										const seller = this.getAttribute("Seller-ID");
										this.title = "Add seller '" + seller + "' to the block list.";
										img.addEventListener("click", function(){
											self.ConfirmBlockSeller(seller);
										}, false);
									};
									
									img.src = SharedObjects.Constants.CloseButtonIcon;
									
									this.SetupFeedbackLink(sellerData.ID, dynDiv);
								}
							}catch(e){
								alert(e);
							}
						}
					}
				}

			}
			
			
			return sellerData;
		},
		
		ConfirmBlockSeller : function(sellerName){
			if(!sellerName || sellerName.length === 0) return;
			if(SellerManager.IsBlockedSeller(sellerName)){
				alert("Seller '" + sellerName + "' is already in your block list.");
				return;
			}
			
			const msg = "Are you sure you want to block the seller '" + sellerName + "'?";
			if(confirm(msg)) {
				if(SellerManager.AddBlockedSeller(sellerName, true))
					EBayUsabilityEnhancer.ToggleFilterBlockedSellerResults(true);
			}
		},
		
		SetupFeedbackLink : function(sellerName, parentNode){
			const fbLink = document.createElement("a");
			fbLink.href = SharedObjects.Constants.NegativeFeedbackLink.replace("[SELLERNAME]", sellerName);
			fbLink.textContent = "Show Negative Feedback";
			fbLink.title = "Opens the seller's negative feedback ratings page in a new tab.";
			fbLink.setAttribute('target', '_blank');
							
			const li = document.createElement("li");
			li.appendChild(fbLink);
			parentNode.appendChild(li);
		},
		
	};

	const Utilities = {
		
		//SetupProperties: Creates property get/set functions.
		SetupProperties : function(){
			
			Object.defineProperty(ScriptProps, "ShowTopPager",{
				get: function(){ return GM_getValue(SharedObjects.Constants.ShowTopPager); },
				set: function(value){GM_setValue(SharedObjects.Constants.ShowTopPager, value); }
			});
			
			Object.defineProperty(ScriptProps, "RankByPrice", {
				get: function() { return GM_getValue(SharedObjects.Constants.RankByPrice); },
				set: function(value){ GM_setValue(SharedObjects.Constants.RankByPrice, value); }
			});
			
			Object.defineProperty(ScriptProps, "FixLinks", {
				get: function() { return GM_getValue(SharedObjects.Constants.FixLinks); },
				set: function(value) { GM_setValue(SharedObjects.Constants.FixLinks, value); }
			});
				
			Object.defineProperty(ScriptProps, "FilterResults", {
				get: function(){ return GM_getValue(SharedObjects.Constants.FilterResults); },
				set: function(value){ GM_setValue(SharedObjects.Constants.FilterResults, value); }
			});
			
			Object.defineProperty(ScriptProps, 'UsePlaceholders', {
				get: function(){ return GM_getValue(SharedObjects.Constants.UsePlaceholders); },
				set: function(value){ GM_setValue(SharedObjects.Constants.UsePlaceholders, value); }
			});
			
			Object.defineProperty(ScriptProps, "RemoveSponsoredItems", {
				get: function(){ return GM_getValue(SharedObjects.Constants.RemoveSponsoredItems); },
				set: function(value){ GM_setValue(SharedObjects.Constants.RemoveSponsoredItems, value); }
			});

            Object.defineProperty(ScriptProps, "RemoveTopRatedItems", {
                get: function(){return GM_getValue(SharedObjects.Constants.RemoveTopRatedItems);},
                set: function(value){GM_setValue(SharedObjects.Constants.RemoveTopRatedItems, value);}
            });
			
			Object.defineProperty(ScriptProps, "BlockedSellerList", {
				get: function() { 
					const blockList = GM_getValue(SharedObjects.Constants.BlockedSellerList); 
					return blockList != null ? blockList.split(' ') : []; 
				},
				set: function(value) {
					const len = value.length;
					let agg = "";
					for(let i = 0; i < len; i++){
						agg += value[i] + " ";
					}
					agg = agg.trim();
					
					try{
						GM_setValue(SharedObjects.Constants.BlockedSellerList, agg); 
					} catch(e){
						//alert("Err:\r\n" + e);
					}
				}
			});
		},
		
		CreateSelectOption: function(text, val){
			const opt = document.createElement("option");
			opt.value = val;
			opt.textContent = text;
			return opt;
		},

		//GetPageContent: Retrieves the content of the page at the supplied Url.
		GetPageContent : function(url, listingid, callback){
			if(!url) return;

            //GM_xmlhttpRequest onload event is not working. So, we're going old-school, here.
            var oReq = new XMLHttpRequest();
            oReq.addEventListener("load", function(){
                const result = this.responseText;
                if(callback)
                    callback.call(this, listingid, result);
            });
            oReq.open("GET", url, true);
            oReq.send(null);

            // GM_xmlhttpRequest({
            //     method: "GET",
            //     url: url,
            //     onload: function(r){
            //         console.log(r);
            //         const result = r.responseText;
            //         if(callback)
            //             callback.call(this, listingid, result);
            //     },
            //     onerror: function(r) {
            //         console.log(r);
            //     }
            // });
		},
		
		GetFullUserName : function(listingId, callback){
			if(!listingId || listingId.length === 0) return null;
			const url = "//www.ebay.com/itm/" + listingId;
			this.GetPageContent(url, listingId, callback);
		},
		
		//GetResultItem: Returns a result item, from the 'SharedObjects.Arrays.OriginalPrices' array, that matches the supplied 'itemId' parameter value.
		GetResultItem : function(itemId){
			if(!itemId) return null;
			const len = SharedObjects.Arrays.OriginalPrices.length;
			for(let i = 0; i < len; i++){
				const result = SharedObjects.Arrays.OriginalPrices[i][0];
				const id = result.attributes["listingid"].value;
				if(id === itemId)
					return result;
			}
			return null;
		},
		
		//GetItemPrices: Gets an array of items and their prices.
		GetItemPrices : function(){
			const results = document.getElementsByClassName("sresult");
			
			if(results.length > 0 && SharedObjects.Arrays.OriginalPrices.length == 0){
				let pText, nText, result;
				for(let i = 0; i < results.length; i++){
					
					try{
						result = results[i];
						const sd = SellerManager.GetSellerData(result);
						SharedObjects.Arrays.AllSellerData.push([sd, result.attributes["listingid"].value]);

						const prc = result.querySelector("ul.lvprices li.lvprice.prc span");
						
						if(prc){
							pText = prc.innerHTML;
							
							//<span class="prRange">$5.97 <span>to</span> $10.97</span>
							const prRange = prc.querySelector("span.prRange");
							if(prRange)
								pText = prRange.innerHTML;
							
							//the span may contain child elements.
							//in such case, we only want the text that preceeds any elements.
							if(pText.indexOf("<") !== -1)
								pText = pText.substring(0, pText.indexOf("<"));
							
							let price = pText.trim();
							if(price.indexOf(' ') != -1)
								price = price.split(' ')[1].trim();
								
							const n = price[0];
							if(n){
								if(!/\d/.test(n))
									price = price.substring(1);
								nText = result;
								while(price.indexOf(',') > -1) 
									price = price.replace(',','');
								
								if(price){
									SharedObjects.Arrays.OriginalPrices.push([result, price]);
								}
							}
						}
					}catch(e){
						//alert(result.outerHTML);
						//alert(e + "\r\niteration: " + i + "\r\n" + nText);
					}

				}
			}
		},
		
		ParseNameFromTextChunk : function(textChunk){
			if(!textChunk || textChunk.length === 0) return null;
			
			let ridx = textChunk.indexOf("RightSummaryPanel");
			let lidx = textChunk.indexOf("LeftSummaryPanel");
			

			for(let s = ridx; s > 0; s--){
				const chr = textChunk[s];
				if(chr === "<"){
					ridx = s;
					break;
				}
			}
			
			for(let s = lidx; s > 0; s--){
				const chr = textChunk[s];
				if(chr === "<"){
					lidx = s;
					break;
				}
			}
			
			const chunk = textChunk.substring(ridx, lidx);
			const container = document.createElement("div");
			container.style.visibility = "collapse";
			
			container.innerHTML = chunk;
			document.body.appendChild(container);
			
			const cSpan = container.querySelector("div a span.mbg-nw");
			return cSpan.textContent.trim();
		},
		
	};

	const SortFunctions = {
		
		reA : /[^a-zA-Z]/g,
		reN : /[^0-9]/g,
		SortAlphaNum : function(a,b) {
			const aA = a.replace(this.reA, "");
			const bA = b.replace(this.reA, "");
			if(aA === bA) {
				const aN = parseInt(a.replace(this.reN, ""), 10);
				const bN = parseInt(b.replace(this.reN, ""), 10);
				return aN === bN ? 0 : aN > bN ? 1 : -1;
			} else {
				return aA > bA ? 1 : -1;
			}
		},
		
		SortLowToHigh : function(a,b){
			const n1 = parseFloat(a[1]);
			const n2 = parseFloat(b[1]);
			
			if(n1 < n2) return -1;
			if(n1 > n2) return 1;
			return 0;
		}
	};

	String.prototype.splice = function( idx, rem, s ) {
		return (this.slice(0,idx) + s + this.slice(idx + Math.abs(rem)));
	};


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//Get the ball rolling...
	
	
	if(vendor === "Google Inc."){
		EBayUsabilityEnhancer.Initialize();
	} else if (codeName === "Mozilla" && product === "Gecko"){
		document.onreadystatechange = function(){
			if(document.readyState === "complete")
				EBayUsabilityEnhancer.Initialize();
		};
	}
})();