Greasy Fork

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.

目前为 2015-06-01 提交的版本。查看 最新版本

// ==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.13
// @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(){	
	var SharedObjects = {
		
		//Variables
		StartupInterval: 0,
		
		//Interval values
		Intervals : {
			Startup: 60,
			Timeout: 100
		},
		
		//local arrays
		Arrays : {
			OriginalPrices: [],
			ResultItems: [],
			FilteredItems: [],
			AllSellerData: [],
			TruncatedSellers: [] //jagged array
		},
		
		//strings
		Constants : {
			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: "//mycdn.atwebpages.com/css/ebay/ebay.css", // "//se7en-soft.com/misc/gm/ebay/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",
			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);
			//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
		},
		
		//InjectStyles: Injects a style file from the se7en-soft.com web site.
		//This file contains element styles that are used througout this script.
		InjectStyles : function(callback){
			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);
				}
			});
		},
		
		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?MyEbayBeta") !== -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, 13) + "...";
			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'.";
			txtAddBlockedSeller.setAttribute("style", "width:100px;margin:0;")
			
			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...
	EBayUsabilityEnhancer.Initialize();
})()