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.

当前为 2017-03-24 提交的版本,查看 最新版本

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

You will need to install an extension such as Tampermonkey to install this script.

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

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

您需要先安装一款用户脚本管理器扩展,例如 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     0.0.17
// @grant		metadata
// @grant		GM_xmlhttpRequest
// @grant		GM_addStyle
// @grant		GM_getValue
// @grant		GM_setValue
// @grant		GM_deleteValue
// @grant		GM_setClipboard
// @require		https://greasyfork.org/scripts/10194-ebay-custom-page-controls-and-seller-block-list/code/EBay:%20Custom%20Page%20Controls%20And%20Seller%20Block%20List.js?version=54713
// ==/UserScript==

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

	var 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",
			StyleSheetLocation: "https://cdn.se7en-soft.com/greasemonkey/ebayenhancer/ebay.debug.css",
			NegativeFeedbackLink: "http://feedback.ebay.com/ws/eBayISAPI.dll?ViewFeedback2&userid=[SELLERNAME]&myworld=true&items=25&iid=-1&de=off&which=negative&interval=365",
			USPSTrackingLink: "https://tools.usps.com/go/TrackConfirmAction_input?origTrackNum=",
			FedExTrackingLink: "https://www.fedex.com/apps/fedextrack/?tracknumbers=[TRACKING#]&cntry_code=us",
			UPSTrackingLink: "http://wwwapps.ups.com/WebTracking/processInputRequest?sort_by=status&error_carried=true&tracknums_displayed=1&TypeOfInquiryNumber=T&AgreeToTermsAndConditions=yes&InquiryNumber1=[TRACKING#]"
		},
		
		
	};
	
	//local property object
	var ScriptProps = {};

	var DrawManager = {
		DrawPads: [],
		
		GetDrawPad: function(padId){
			var drawPad = null;
			for(var i = 0; i < this.DrawPads.length; i++){
				var dp = this.DrawPads[i];
				if(dp.Identifier === padId){
					drawPad = dp;
					break;
				}
			}
			return drawPad;
		}
	};
	
	var DrawModes = Object.freeze({
		FreeDraw: 0,
		Rectangles: 1,
		Ovals: 2
	});
	
	function DrawPad(){}
	DrawPad.prototype = {
		Identifier: "",
		IsVisible: false,
		InDrawMode: false,
		DrawMode: DrawModes.FreeDraw,
		LastDrawMode: DrawModes.FreeDraw,
		IsMouseDown: false,
		Canvas: null,
		DrawContext: null,
		StartX: NaN,
		StartY: NaN,
		LastX: NaN,
		LastY: NaN,
		InEraseMode: false,
		
		Pen: {
			Color: "black",
			Thickness: 2,
			HiLite: false
		},
		
		Initialize: function(canvas, id){
			this.Identifier = id;
			this.Canvas = canvas;
			this.DrawContext = canvas.getContext("2d");
			this.InDrawMode = false;
			this.SetDrawState(false);
			
			var self = this;
			
			canvas.addEventListener("mousedown", function(e){
				self.IsMouseDown = true;
				
				var rc = this.getBoundingClientRect();
				self.StartX = Math.floor(e.clientX - rc.x);
				self.StartY = Math.floor(e.clientY - rc.y);
			}, false);
			
			canvas.addEventListener("mouseup", function(e){
				self.IsMouseDown = false;
				//var rc = this.getBoundingClientRect();
				//self.LastX = Math.floor(e.clientX - rc.x - self.StartX);
				//self.LastY = Math.floor(e.clientY - rc.y - self.StartY);
				
				self.StartX = NaN;
				self.StartY = NaN;
				self.LastX = NaN;
				self.LastY = NaN;
			}, false);
			
			canvas.addEventListener("mousemove", function(e){
				if(!self.IsMouseDown) return;
				var rc = this.getBoundingClientRect();
				var x = Math.floor(e.clientX - rc.x);
				var y = Math.floor(e.clientY - rc.y);

				switch(self.DrawMode){
					case DrawModes.FreeDraw:
					{
						self.Draw(x, y);
						break;
					}
				}
			}, false);
			
			canvas.addEventListener("mouseleave", function(e){
				self.IsMouseDown = false;
			}, false);
		},
		
		Clear: function(){
			this.DrawContext.clearRect(0, 0, this.Canvas.width, this.Canvas.height);
		},
		
		ToggleEraseMode: function(erase){
			this.InEraseMode = erase;
			if(this.InEraseMode){
				this.LastDrawMode = this.DrawMode;
				this.DrawMode = DrawModes.FreeDraw;
			} else {
				this.DrawMode = this.LastDrawMode;
			}
		},
		
		SetVisibility: function(visible){
			this.IsVisible = visible;
			this.Canvas.style.display = (visible ? "block" : "none");
		},
		
		SetDrawState: function(allowDraw){
			if(allowDraw)
				this.Canvas.style.pointerEvents = "";
			else 
				this.Canvas.style.pointerEvents = "none";
		},
		
		Draw: function(x, y){
			if (this.IsMouseDown && this.InDrawMode) {
				var penColor;
				if(this.Pen.HiLite){
					var rgb = new RGBColor(this.Pen.Color);
					penColor = "rgba(" + rgb.r +"," + rgb.g +"," + rgb.b +"," + .05 +")";
				} else {
					penColor = this.Pen.Color;
				}
				
				try{
					this.DrawContext.beginPath();
					this.DrawContext.globalCompositeOperation = (this.InEraseMode ? "destination-out" : "source-over");
					
					this.DrawContext.strokeStyle = this.InEraseMode ? "rgba(0,0,0,1)" : penColor;
					this.DrawContext.lineWidth = this.Pen.Thickness;
					this.DrawContext.lineJoin = "round";
					this.DrawContext.moveTo(this.LastX, this.LastY);
					this.DrawContext.lineTo(x, y);
					this.DrawContext.closePath();
					this.DrawContext.stroke();
				} catch(e){
					alert(e);
				}
			}
			this.LastX = x; 
			this.LastY = y;
		},
	};
	
	var EBayUsabilityEnhancer = {
		
		Initialize : function(){
			
			//Get object properties setup for access
			Utilities.SetupProperties();
			
			//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
			//Get the ball rollin'.
			var 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 mycdn.atwebpages.com.
		//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){
						var css = r.responseText;
						GM_addStyle(css);
						
						if(callback)
							callback.call(this);
					}
				});
			}catch(e){
				alert(e);
			}
		},
		
		SetupPage : function() {
			var self = this;
			var 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){
				var ordersDiv = document.getElementById("orders");
				if(ordersDiv){
					clearInterval(SharedObjects.StartupInterval);
					var itemsContainer = ordersDiv.querySelector("div.ajax-wrap div.result-set-r");
					
					if(itemsContainer){
						var itemContainers = itemsContainer.querySelectorAll("div.order-r");
						var len = itemContainers.length, i = 0;
						for(i; i < len; i++){
							var item = itemContainers[i];
							
							//----------------------------------------------------------------------
							//this.InsertDrawPad(item);
							
							var sellerLink = item.querySelector("div div a.seller-id");
							if(sellerLink){
								//append X button to allow blocking this seller...
								var sellerName = sellerLink.textContent.trim();
								var img = new Image();
							
								img.setAttribute("style", "height:12px;width:12px;cursor:pointer;");
								img.setAttribute("Seller-ID", sellerName);
								
								sellerLink.parentNode.appendChild(img);
								
								img.onload = function(){
									var 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;
							}
						}
					}
				}
				
				var trackingLabelContainers = document.getElementsByClassName("tracking-label");
				if(trackingLabelContainers && trackingLabelContainers.length > 0){
					var containers = [].slice.call(trackingLabelContainers);
					containers.forEach(function(labelContainer, index){
						
						var labelLink = labelContainer.querySelector("a");
						var href = labelLink.dataset.url;
						
						var b = labelLink.querySelector("b");
						if(b)
							labelLink.removeChild(b);
						
						self.FindCarrier(href, function(carrier){
							var trackingNumber = labelLink.textContent;		
							labelLink.textContent = trackingNumber;
							if(carrier && carrier !== "Unknown"){
								var trackingLink = document.createElement("a");
								trackingLink.textContent = " [Carrier Track]";
								
								carrier = carrier.toLowerCase();
								
								if(carrier === "usps")
									trackingLink.href = SharedObjects.Constants.USPSTrackingLink + trackingNumber;
								else if (carrier === "fedex")
									trackingLink.href = SharedObjects.Constants.FedExTrackingLink.replace("[TRACKING#]", trackingNumber);
								else if (carrier === "ups")
									trackingLink.href = SharedObjects.Constants.UPSTrackingLink.replace("[TRACKING#]", trackingNumber);
								
								trackingLink.target = "_blank";
								
								labelContainer.appendChild(trackingLink);
							}
						});
					});
				}
				
			} else {
			
				//find DIV elements for each product item in the search page
				var 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(function(){
							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(var i = 0; i < itmLinks.length; i++){
					
						//get the DIV at index 'i', from the collection
						var dLnk = itmLinks[i];
						SharedObjects.Arrays.ResultItems.push(dLnk);
					}

					//alert(SharedObjects.Arrays.ResultItems.length);
				}
			}
		},
		
		InsertDrawPad: function(item){
			var itemId = item.querySelector("div.display-item-id").textContent;
			itemId = itemId.replace("(", "").replace(")", "").trim();
			
			var canvas = UIBuilder.CreateDrawPadCanvas(item);
			var drawPad = new DrawPad();
			drawPad.Initialize(canvas, itemId);
			
			DrawManager.DrawPads.push(drawPad);
			
			canvas.id = "canvas_" + itemId;

			var div = document.createElement("div");
			div.style.display = "inline-block";
			
			var div2 = document.createElement("div");
			div2.style.display = "none";
			div2.id = "dvControls_" + itemId;
			
			var div3 = document.createElement("div");
			div3.style.display = "none";
			div3.id = "dvControls2_" + itemId;
			
			var a1 = document.createElement("span");
			a1.id = "sp_" + itemId;
			a1.style.color = "blue";
			a1.style.cursor = "pointer";
			a1.textContent = "Scratch Pad: Off";
			a1.onclick = function(e){
				var padId = this.id.replace("sp_", "");
				var pad = DrawManager.GetDrawPad(padId);
				pad.IsVisible = !pad.IsVisible;
				pad.SetVisibility(pad.IsVisible);
				this.textContent = "Scratch Pad: " + (pad.IsVisible ? "On" : "Off");
				document.getElementById("dvControls_" + itemId).style.display = (pad.IsVisible ? "inline-block" : "none");
			};
			
			var a2 = document.createElement("span");
			a2.id = "ad_" + itemId;
			a2.textContent = "Draw: No";
			a2.style.color = "blue";
			a2.style.cursor = "pointer";
			a2.onclick = function(e){
				var padId = this.id.replace("ad_", "");
				var pad = DrawManager.GetDrawPad(padId);
				pad.InDrawMode = !pad.InDrawMode;
				pad.SetDrawState(pad.InDrawMode);
				this.textContent = "Draw: " + (pad.InDrawMode ? "Yes" : "No");
				document.getElementById("dvControls2_" + itemId).style.display = (pad.InDrawMode ? "inline-block" : "none");
			};
			
			var s1 = document.createElement("select");
			s1.id = "pc_" + itemId;
			
			var s2 = document.createElement("select");
			s2.id = "ps_" + itemId;
			
			var pens = [
				["Red", "#ff0000"],
				["Blue", "#0000ff"],
				["Black", "#000000"],
				["Yellow", "#ffff00"]
			];
			
			var strokes = [
				["1 px", 1],
				["2 px", 2],
				["5 px", 5],
				["7 px", 7],
				["10 px", 10]
			];
			
			pens.forEach(function(pen){
				var opt = Utilities.CreateSelectOption(pen[0], pen[1]);
				s1.appendChild(opt);
			});
			
			s1.selectedIndex = 2;
			
			strokes.forEach(function(stroke){
				var opt = Utilities.CreateSelectOption(stroke[0], stroke[1]);
				s2.appendChild(opt);
			});
			
			s2.selectedIndex = 1;

			s1.onchange = function(e){
				var option = this.childNodes[this.selectedIndex];
				var padId = this.id.replace("pc_", "");
				var pad = DrawManager.GetDrawPad(padId);
				pad.Pen.Color = option.value;
			};
			
			s2.onchange = function(e){
				var option = this.childNodes[this.selectedIndex];
				var padId = this.id.replace("ps_", "");
				var pad = DrawManager.GetDrawPad(padId);
				pad.Pen.Thickness = option.value;
			};
			
			var lbl = document.createElement("label");
			lbl.textContent = "Erase";
			var cb = document.createElement("input");
			cb.id = "cb_" + itemId;
			cb.type = "checkbox";
			lbl.appendChild(cb);
			
			cb.onchange = function(e){
				var padId = this.id.replace("cb_", "");
				var pad = DrawManager.GetDrawPad(padId);
				pad.ToggleEraseMode(this.checked);
			};
			
			var lbl2 = document.createElement("label");
			lbl2.textContent = "HiLite";
			var cb2 = document.createElement("input");
			cb2.id = "cb2_" + itemId;
			cb2.type = "checkbox";
			lbl2.appendChild(cb2);
			
			cb2.onchange = function(e){
				var padId = this.id.replace("cb2_", "");
				var pad = DrawManager.GetDrawPad(padId);
				pad.Pen.HiLite = this.checked;
			};
			
			var btn = document.createElement("button");
			btn.textContent = "Clear";
			btn.id = "cc_" + itemId;
			btn.onclick = function(){
				var padId = this.id.replace("cc_", "");
				var pad = DrawManager.GetDrawPad(padId);
				pad.Clear();
			};
			
			var dAction = item.querySelector("div.action-spec");
			var asAction = item.querySelector("div.action-spec a.saction");
			
			a1.classList.add("saction")
			dAction.insertBefore(a1, asAction);
			
			div2.appendChild(a2);
			div3.appendChild(document.createTextNode(" | "));
			div3.appendChild(s2);
			div3.appendChild(document.createTextNode(" "));
			div3.appendChild(s1);
			div3.appendChild(document.createTextNode(" | "));
			div3.appendChild(lbl);
			div3.appendChild(lbl2);
			div3.appendChild(btn);
			
			div2.appendChild(div3);
			div.appendChild(div2);
			
			item.appendChild(div);
		},
		
		//Attempt to find the carrier for the shipment based on the supplied url
		FindCarrier: function(href, callback){
			Utilities.GetPageContent(href, null, function(listingId, result){
				var div = document.createElement("div");
				div.innerHTML = result;
				
				var elm = div.querySelector("div.popupWrapper div div div:nth-child(2) div span");
				if(callback && typeof callback === "function")
					callback.call(listingId, elm.textContent);
			});
		},
		
		//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
			var overlay = UIBuilder.BuildOverlay();
			
			//add controls to the overlay container
			UIBuilder.BuildOverlayControls(overlay);
			
			var 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;
			
			var pArray = SharedObjects.Arrays.OriginalPrices.slice(0);
			
			if(lowToHigh) {
				try{
					pArray.sort(SortFunctions.SortLowToHigh);
				} catch(e){
					alert(e);
				}
			}	
			
			var results = document.getElementById("ListViewInner");
			//var results = document.getElementsByClassName("rsittlref");
			//var resultParent = results[0].parentNode;

			while(results.childNodes.length)
				results.removeChild(results.firstChild);
				
			for(var 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(var i = 0; i < SharedObjects.Arrays.ResultItems.length; i++){
				var dLnk = SharedObjects.Arrays.ResultItems[i];
				
				//grab the anchor element from the H3 or H4 title element
				var anchor = dLnk.querySelector('h3 a') || dLnk.querySelector("h4 a");
				
				if(anchor){
					if(openNew){
						//get the link pointer location
						var 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){
			var filterBar = result.querySelector("tr.GM_eBayBadSeller_ResultFilterBarContainer");
			
			if(memberName.lastIndexOf("..") !== -1){
				var badSellers = ScriptProps.BlockedSellerList;
	
				var len = badSellers.length, i = 0;
				for(i; i < len; i++){
					var bs = badSellers[i].toLowerCase();
						
					var 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");
					
					var cell = document.createElement("td");
					filterBar.appendChild(cell);
					
					var dvBar = document.createElement("div");
					dvBar.setAttribute("class", "GM_eBayBadSeller_ResultFilterBar")
					cell.appendChild(dvBar);
					
					var table = document.createElement("table");
					table.style.width = "100%";
					var tRow = document.createElement("tr");
					table.appendChild(tRow);
					
					for(var i = 0; i < 4; i++){
						var tCell = document.createElement("td");
						tRow.appendChild(tCell);
					}
					
					dvBar.appendChild(table);
					
					var 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");
					var link = document.createElement("a");
					link.setAttribute("class", "GM_eBayBadSeller_FilterBarLink");
					link.setAttribute("target", "_blank");
					link.textContent = "(View Listing)";
					link.title = "View item listing.";
					
					
					var 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){
						var 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);
						
					var tbody = result.querySelector("tbody");
					var id = result.attributes["listingid"].value;
					
					
					var 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';
					
				
				
				var id = result.attributes["listingid"].value;
				var len = SharedObjects.Arrays.FilteredItems.length;
				var sellerData, content;
				
				for(var i = 0; i < len; i++){
					var fItem = SharedObjects.Arrays.FilteredItems[i];
					if(fItem[1] == id){
						var seller = SellerManager.GetSellerById(id);
						if(seller && seller.ID.lastIndexOf("...") !== -1){
							var fuzzyMatch = SellerManager.FuzzyMatchedName(seller.ID);
							if(fuzzyMatch){
								content = fItem[0];
								break;
							}
						} else if(seller && seller.ID === memberName){
							content = fItem[0];
							break;
						}
					}
				}
				
				if(content){
					for(var n = 0; n < content.length; n++){
						var c = content[n];
						result.appendChild(c);
					}
					
					if(filterBar)
						result.removeChild(filterBar);
					
					result.style.display = "block";
				}
			}
		},
		
		//ToggleFilterBlockedSellerResults: Toggles blocked seller item results.
		ToggleFilterBlockedSellerResults : function(filterThem){
			var resultCount = SharedObjects.Arrays.OriginalPrices.length;
			
			var fiaLen = SharedObjects.Arrays.FilteredItems.length;
			
			 if(fiaLen > 0){
				for(var i = 0; i < fiaLen; i++){
					var filteredItem = SharedObjects.Arrays.FilteredItems[i];
					var itemId = filteredItem[1];
					var priceItem = Utilities.GetResultItem(itemId);
					if(priceItem){
						var memberName = filteredItem[2];
						this.ToggleResultFilterBar(priceItem, memberName, filterThem);
					}
				}
			}

			if(filterThem){
				for(var i = 0; i < resultCount; i++){
					
					var resultItem = SharedObjects.Arrays.OriginalPrices[i][0];
					var listingId = resultItem.attributes["listingid"].value;
					var 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(var n = 0; n < SharedObjects.Arrays.FilteredItems.length; n++){
						var fi = SharedObjects.Arrays.FilteredItems[n];
						var fid = fi[1];
						if(fid === listingId){
							skip = true;
							break;
						}
					}
									
					//should we skip passed everything and continue?
					if(skip) continue;
					
					var sd = SellerManager.GetSellerById(listingId);
					
					if(sd){
						var userName = sd.ID;

						if(userName.length > 0){
							var fn = SellerManager.FuzzyMatchedName(userName);
							userName = fn || userName;
							if(SellerManager.IsBlockedSeller(userName, true)){
								var blockedUserSpan = document.getElementById("EBayEnhancer_BlockedSeller_" + userName);
								blockedUserSpan.style.opacity = "1";
								this.ToggleResultFilterBar(resultItem, userName, true);
							}
						}
					}
				}
			} else {
				for(var i = 0; i < resultCount; i++){
					var result = SharedObjects.Arrays.OriginalPrices[i];
					this.ToggleResultFilterBar(result[0], "", false);
				}
			}
		},
		
		//ToggleFilteredItemPlaceholders: Toggles visibility of removed item placeholders.
		ToggleFilteredItemPlaceholders : function(show){
			var filterBars = document.getElementsByClassName("GM_eBayBadSeller_ResultFilterBarContainer");
			var len = filterBars.length;
			
			if(!show){	
				if(len > 0){
					for(var i = 0; i < len; i++){
						var fbar = filterBars[i];
						fbar.style.visibility = 'collapse';
						fbar.parentNode.style.display = "none";
					}
				}
			} else {
				if(len > 0){
					for(var i = 0; i < len; i++){
						var fbar = filterBars[i];
						var 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.ShowTopPager);
			GM_deleteValue(SharedObjects.Constants.RankByPrice);
			GM_deleteValue(SharedObjects.Constants.FixLinks);
			GM_deleteValue(SharedObjects.Constants.UsePlaceholders);
		},
		
		ResetAll : function(){
			this.CleanGMValues();
			location.reload();
		}
		
	};

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

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

			var 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);
				
			var overlay = document.createElement("div");
			overlay.id = "GM_eBayOverlay";
					
			var img = new Image();
			img.src = SharedObjects.Constants.ReloadButtonIcon;
			img.id = "GM_eBaySettingsResetIcon";
			img.title = "Reset All";
			img.addEventListener('click', function(){
				var result = confirm("Are you sure you want to clear and reset everything? This action cannot be undone.");
				if(result){
					//-----------------------------------
					EBayUsabilityEnhancer.ResetAll();
				}
			});
			overlay.appendChild(img);
			
			var header = document.createElement("h3");
			header.textContent = "Usability Settings";
			header.setAttribute("class", "GM_ebayHeader");
			
			overlay.appendChild(header);
			
			var 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);
			
			var 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){
			var ul = document.createElement("ul");
			ul.style.marginTop = "-8px";
			for(var i = 0; i < 5; i++){
				var li = document.createElement("li");
				ul.appendChild(li);
			}
			
			// -- Top Pager
			var cbContainer = this.BuildCheckbox("Show Pager At Top", "GM_cbShowTopPager");
			cbContainer.title = "Display a pager element at the top of the results view.";
			var cbShowTopPager = cbContainer.firstChild;
				
			cbShowTopPager.addEventListener('change', function(r) {
				var cb = r.target;
				ScriptProps.ShowTopPager = cb.checked;
				EBayUsabilityEnhancer.ToggleTopPager(cb.checked);
			});

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

			// -- Reorder Price-wise
			cbContainer = this.BuildCheckbox("Order Low To High", "GM_cbRankByPrice");
			cbContainer.title = "Re-order results by price; lowest to highest.";
			var cbReorderByPrice = cbContainer.firstChild;
			
			cbReorderByPrice.addEventListener('change', function(r){
				var cb = r.target;
				ScriptProps.RankByPrice = cb.checked;
				EBayUsabilityEnhancer.ToggleResultOrder(cb.checked);
			});
			
			ul.childNodes[1].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.";
			var cbTabbedLinks = cbContainer.firstChild;
			
			cbTabbedLinks.addEventListener('change', function(r){
				var cb = r.target;
				ScriptProps.FixLinks = cb.checked;
				EBayUsabilityEnhancer.ToggleLinkFix(cb.checked);
			});
			
			ul.childNodes[2].appendChild(cbContainer);
			// -- Fix Links
			
			// -- Filter Bad Sellers
			
			var fDiv = document.createElement("div");
			cbContainer = this.BuildCheckbox("Prune Results", "GM_cbFilterBlockedSellers");
			cbContainer.title = "Remove items posted by blocked sellers.";
			var cbBlockSellers = cbContainer.firstChild;
			cbBlockSellers.addEventListener('change', function(r){
				var cb = r.target;
				ScriptProps.FilterResults = cb.checked;
				document.getElementById('GM_cbFilterBlock_ShowPlaceholders').disabled = !cb.checked;
				EBayUsabilityEnhancer.ToggleFilterBlockedSellerResults(cb.checked);
			});
			
			fDiv.appendChild(cbContainer);
			
			var fUl = document.createElement("ul");
			var fli = document.createElement("li");
			fUl.appendChild(fli);
			
			cbContainer = this.BuildCheckbox("Use Placeholders", "GM_cbFilterBlock_ShowPlaceholders");
			cbContainer.title = "Show placeholders for removed items.";
			var cbPlaceholder = cbContainer.firstChild;
			cbPlaceholder.addEventListener('change', function(r){
				var cb = r.target;
				ScriptProps.UsePlaceholders = cb.checked;
				EBayUsabilityEnhancer.ToggleFilteredItemPlaceholders(cb.checked);
			});
			fli.appendChild(cbContainer);
			
			fDiv.appendChild(fUl);
			
			ul.childNodes[3].appendChild(fDiv);
			// Filter Bad Sellers --
			
			//append the actual list element to the overlay
			pOverlay.appendChild(ul);	
		},
		
		CreateDrawPadCanvas: function(item){
			var canvas = document.createElement("canvas");
			var width, height;
				
			try{
				width = Math.max(item.scrollWidth, item.offsetWidth);
				height = Math.max(item.scrollHeight, item.offsetHeight);
			} catch(e){
				alert(e);
			}
			
			item.style.position = "relative";
			canvas.style = "z-index:10000;background-color:rgba(255,255,255,0);position:absolute !important;left:0 !important;top:0 !important;";
			canvas.width = width;
			canvas.height = height;
			
			canvas.style.display = "none";
			
			item.insertBefore(canvas, item.firstChild);
			return canvas;
		},
		
		//CreateTopPagerControl: Creates the top pagination control.
		CreateTopPagerControl : function(){
			var paginator = document.getElementById("Pagination");
			if(paginator){
				var pager2 = paginator.cloneNode(true);
				if(pager2){
					pager2.style.visibility = 'collapse';
					pager2.id = "Pagination2";
					pager2.style.marginTop = "10px";
					pager2.style.marginBottom = "10px";
					
					var msgContainer = document.getElementById("MessageContainer");
					if(msgContainer)
						msgContainer.parentNode.insertBefore(pager2, msgContainer.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);
			
			//Checkbox setup: Use Placeholders
			var 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;
			
			var table = document.createElement('table');
			table.className = "GM_eBayTableElement";
			var row = document.createElement("tr");
			for(var i = 0; i < 3; i++){
				var cell = document.createElement('td');
				row.appendChild(cell);
			}
			
			table.appendChild(row);
			
			var nameSpan = document.createElement("span");
			nameSpan.id = "EBayEnhancer_BlockedSeller_" + sellerName;
			nameSpan.style.fontWeight = "bold";
			nameSpan.style.cursor = "default";
			nameSpan.className = "unselectable";
			var 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);
			
			var 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){
				var img = r.target;
				var sn = img.attributes["data-sellerName"].value;
				GM_setClipboard(sn);
			});
			
			var cell1 = row.childNodes[1];
			cell1.style.width = "16px";
			cell1.appendChild(copyImage);
			
			var 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){
				var btn = r.target;
				var sllr = btn.getAttribute('data-badSellerName');
				var result = confirm("Are you sure you want to remove '" + sllr + "' from your seller black list?");
				if(result){
					var badSellers = ScriptProps.BlockedSellerList;
					var idx = badSellers.indexOf(sllr);
					if(idx > -1){
						
						var listings = SellerManager.GetListingsBySeller(sllr);
						
						for(var i = 0; i < listings.length; i++){
							var listing = listings[i][0];
							EBayUsabilityEnhancer.ToggleResultFilterBar(listing, sllr, false);
						}
						
						var remSellers = badSellers.splice(idx, 1);
						ScriptProps.BlockedSellerList = badSellers;

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

				var 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);
			};
			
			var label = document.createElement("label");
			label.textContent = "Add, Import or Export:"
			label.setAttribute('for', 'GM_eBayNewBadSeller_InputBox');
			
			var table = document.createElement("table");
			table.style.width = "100%";
			
			var row = document.createElement("tr");
			for(var i = 0; i < 2; i++)
				row.appendChild(document.createElement("td"));
			table.appendChild(row);
			
			var 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){
				//------------------------------------------
				var blockedSellers = ScriptProps.BlockedSellerList;
				if(blockedSellers){
					var sellerList = "";
					for(var 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);
			
			var 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){
				var blockedSellers = ScriptProps.BlockedSellerList;
				if(blockedSellers){
					var 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);
		}
	};

	var 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;
			var badSellers = ScriptProps.BlockedSellerList;
			
			if(fuzzyMatch && fuzzyMatch === true){
				var len = badSellers.length, i = 0;
				for(i; i < len; i++){
					var bs = badSellers[i].toLowerCase();
						
					var 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;
			
			var badSellers = ScriptProps.BlockedSellerList,
				len = badSellers.length, n = 0, retval,
				tLen = SharedObjects.Arrays.TruncatedSellers.length;
			
			if(tLen > 0){
				//check if we've got a match in the TruncatedSellers array.
				var t = 0;
				for(t; t < tLen; t++){
					var pair = SharedObjects.Arrays.TruncatedSellers[t];
					if(pair[0].toLowerCase() === fuzzyName.toLowerCase())
						return pair[1];
				}
			}
			
			for(n; n < len; n++){
				var bs = badSellers[n].toLowerCase();
				var 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){
			var count = SharedObjects.Arrays.AllSellerData.length;
			for(var i = 0; i < count; i++){
				var sellerData = SharedObjects.Arrays.AllSellerData[i];
				var 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;
			
			var 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;
			}
			
			var sellerLink = UIBuilder.BuildBlockedSellerNameLink(sellerName);
			var li = document.createElement("li");
			if(sellerLink)
				li.appendChild(sellerLink);
			
			var uList = document.getElementById("GM_eBayBadSellers_UList");
			if(uList){
				var badSellers = ScriptProps.BlockedSellerList;
				badSellers.sort(SortFunctions.SortAlphaNum);
				var idx = badSellers.indexOf(sellerName);
				if(idx !== -1){
					var 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(){
			var sellers = ScriptProps.BlockedSellerList;
			if(sellers == undefined) return;
			
			var 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(var i = 0; i < sellers.length; i++){
					var seller = sellers[i];
					this.AddBlockedSeller(seller);
				}
			}
		},
		
		//GetListingsBySeller: Returns a collection of all listings that belong to the supplied sellerName.
		GetListingsBySeller : function(sellerName){
			var count = SharedObjects.Arrays.AllSellerData.length;
			var listings = [];
			
			sellerName = sellerName.toLowerCase();
			
			for(var i = 0; i < count; i++){
				var sd = SharedObjects.Arrays.AllSellerData[i];
				var id = sd[1];
				var seller = sd[0].ID;
				
				if(seller.lastIndexOf("...") !== -1){
					var badSellers = ScriptProps.BlockedSellerList;
					var len = badSellers.length, n = 0;
					
					for(n; n < len; n++){
						var bs = badSellers[n].toLowerCase();
						var 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){
					var priceCount = SharedObjects.Arrays.OriginalPrices.length;
					for(var j = 0; j < priceCount; j++){
						var pd = SharedObjects.Arrays.OriginalPrices[j];
						var pdId = pd[0].attributes["listingid"].value;
						if(id === pdId){
							listings.push(pd);
						}
					}
				}
			}
			return listings;
		},
		
		GetSellerData : function(resultNode){
		
			var sellerData = {
				ID: "",
				FeedbackScore: "",
				ReviewCount: ""
			};
			
			var dynDiv = resultNode.querySelector("ul.lvdetails");
			var listingId = resultNode.getAttribute("listingid");
			var len = dynDiv.childNodes.length;
			var self = this;
			
			if(len > 0) {
				
				for(var m = 0; m < len; m++){
					var n = dynDiv.childNodes[m];
					
					
					var spns;
					
					try{
						spns = n.querySelectorAll("span.selrat");
					} catch (e){
						continue;
					}
					
					if(spns){
						var text = n.textContent.trim();
						if(text.indexOf("Seller:") !== -1){
							var idx = text.indexOf("Seller:");
							var idx2 = text.indexOf("(");
							var idx3 = text.indexOf(")");
							
							sellerData.ID = text.substring(idx + 8, idx2).trim();
							
							var img = new Image();
							img.setAttribute("style", "height:12px;width:12px;cursor:pointer;");
							img.setAttribute("Seller-ID", sellerData.ID);
							
							try{
								var revCount = spns[0].textContent.replace("(", "").replace(")", "");
								while(revCount.indexOf(",") != -1)
									revCount = revCount.replace(",","");
								
								sellerData.ReviewCount = parseInt(revCount);
							}catch(e){
								//alert(e);
							}
							
							try{
								var fbScore = parseFloat(spns[1].textContent.replace("%", "").trim())
								
								sellerData.FeedbackScore = fbScore;
								
								var score = sellerData.FeedbackScore,
									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 + ";");
								
								var 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){
										var 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.";
												var 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(){
										var 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;
			}
			
			var 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){
			var 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');
							
			var li = document.createElement("li");
			li.appendChild(fbLink);
			parentNode.appendChild(li);
		},
		
	};

	var 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, "BlockedSellerList", {
				get: function() { 
					var blockList = GM_getValue(SharedObjects.Constants.BlockedSellerList); 
					return blockList != null ? blockList.split(' ') : []; 
				},
				set: function(value) {
					var len = value.length,
						agg = "";
					for(var 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){
			var 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({
				method: "GET",
				url: url,
				onload: function(r){
					var result = r.responseText;
					callback.call(this, listingid, result);
				}
			});
		},
		
		GetFullUserName : function(listingId, callback){
			if(!listingId || listingId.length === 0) return null;
			var url = "http://www.ebay.com/itm/" + listingId;
			this.GetPageContent(url, listingId, callback /*function(l, r) {
				
			}*/);
		},
		
		//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;
			var len = SharedObjects.Arrays.OriginalPrices.length;
			for(var i = 0; i < len; i++){
				var result = SharedObjects.Arrays.OriginalPrices[i][0];
				var id = result.attributes["listingid"].value;
				if(id === itemId)
					return result;
			}
			return null;
		},
		
		//GetItemPrices: Gets an array of items and their prices.
		GetItemPrices : function(){
			var results = document.getElementsByClassName("sresult");
			
			if(results.length > 0 && SharedObjects.Arrays.OriginalPrices.length == 0){
				var i = 0, pText, nText, result;
				for(i; i < results.length; i++){
					
					try{
						result = results[i];
						var sd = SellerManager.GetSellerData(result);
						SharedObjects.Arrays.AllSellerData.push([sd, result.attributes["listingid"].value]);

						var prc = result.querySelector("ul.lvprices li.lvprice.prc span");
						
						if(prc){
							pText = prc.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("<"));
							var price = pText.trim();
							if(price.indexOf(' ') != -1)
								price = price.split(' ')[1].trim();
								
							var 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;
			
			var ridx = textChunk.indexOf("RightSummaryPanel");
			var lidx = textChunk.indexOf("LeftSummaryPanel");
			

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

	var SortFunctions = {
		
		reA : /[^a-zA-Z]/g,
		reN : /[^0-9]/g,
		SortAlphaNum : function(a,b) {
			var aA = a.replace(this.reA, "");
			var bA = b.replace(this.reA, "");
			if(aA === bA) {
				var aN = parseInt(a.replace(this.reN, ""), 10);
				var 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){
			var n1 = parseFloat(a[1]);
			var 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();
		}
	}
	

	
	//alert(navigator.appCodeName + " : " + navigator.product);
	
	/* for(let o in window.navigator)
		alert(o + ":" + window.navigator[o]); */
})()