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.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

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