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.

  1. // ==UserScript==
  2. // @name EBay: Custom Page Controls And Seller Block List
  3. // @author Scott Michaels
  4. // @description Adds more flexibility to eBay search results. Also, includes a seller block list and can filter out result items from blocked sellers.
  5. // @namespace http://se7en-soft.com
  6. // @include http*://*.ebay.*/sch/*
  7. // @include http*://*.ebay.*/dsc/*
  8. // @include http*://*.ebay.*/mbf/PurchaseHistory?MyEbay*
  9. // @include http*://*.ebay.*/ws/eBayISAPI.dll?MyEbay*
  10. // @include http*://*.ebay.com/myb/*
  11. // @version 2017.11.26.2
  12. // @grant metadata
  13. // @grant unsafeWindow
  14. // @grant GM_xmlhttpRequest
  15. // @grant GM_addStyle
  16. // @grant GM_getValue
  17. // @grant GM_setValue
  18. // @grant GM_deleteValue
  19. // @grant GM_setClipboard
  20. // @connect shiptrack.ebay.com
  21. // @connect *
  22. // @run-at document-start
  23. // @nocompat Chrome
  24. // ==/UserScript==
  25.  
  26. (function(){
  27. const nav = navigator;
  28. const codeName = nav.appCodeName;
  29. const product = nav.product;
  30. const vendor = nav.vendor;
  31.  
  32. const wait = function(func, howLong) {
  33. setTimeout(()=>{func();}, howLong);
  34. };
  35.  
  36. const SharedObjects = {
  37. //Variables
  38. StartupInterval: 0,
  39. //Interval values
  40. Intervals : {
  41. Startup: 60,
  42. Timeout: 100
  43. },
  44. //local arrays
  45. Arrays : {
  46. OriginalPrices: [],
  47. ResultItems: [],
  48. FilteredItems: [],
  49. AllSellerData: [],
  50. TruncatedSellers: [] //jagged array
  51. },
  52. //strings
  53. Constants : {
  54. IsChrome: vendor === "Google Inc.",
  55. 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==",
  56. 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==",
  57. ReloadButtonIcon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAyUlEQVR42mNkoBAwkqGHHYh/EjLAGIjjgFgaquYJEC8E4nNA3ArEM4H4ETYDmIG4H4j/AfEkIL4HFVcC4jyo+l9APAWIH2IzAKTpIBCvRRMXAGJ1IA4G4gwg1sVmAMjZsUBcgMVLmkDsh8SfDsSf0A2YCMX3GEgAyAasAeIQIvXpAPEVSgyAqyXHC7AYKSAlEJHBBCBeDMRn0Q0AAVzRCAOgaLSHuoABmwGEEhITEBcC8V9cBiB7B5aUQeApEC+CORsZkJOZqGsAAKyGKhGReVeSAAAAAElFTkSuQmCC",
  58. ShowTopPager: "EBay.SellerBlockList.Property.ShowTopPager",
  59. RankByPrice: "EBay.SellerBlockList.Property.RankByPrice",
  60. FixLinks: "EBay.SellerBlockList.Property.FixLinks",
  61. FilterResults: "EBay.SellerBlockList.Property.FilterResults",
  62. UsePlaceholders: "EBay.SellerBlockList.Property.UsePlaceholders",
  63. BlockedSellerList: "EBay.SellerBlockList.Property.BlockedSellersList",
  64. RemoveSponsoredItems: "Ebay.SellerBlockList.Property.RemoveSponsoredItems",
  65. //StyleSheetLocation: "https://cdn.se7en-soft.com/greasemonkey/ebayenhancer/ebay.css",
  66. StyleSheetLocation: "https://gist.githubusercontent.com/se7ensoft/8ae678371193d638cd76a73961fe48a7/raw/41fed22db26e50e823f8d921d830ac466553b867/ebay.css",
  67. NegativeFeedbackLink: "http://feedback.ebay.com/ws/eBayISAPI.dll?ViewFeedback2&userid=[SELLERNAME]&myworld=true&items=25&iid=-1&de=off&which=negative&interval=365",
  68. SearchTrackingLink: "https://www.google.com/search?source=hp&ei=cOYaWtmMO4ujggeVnZjACw&q=[TRACKING#]&oq=[TRACKING#]&gs_l=psy-ab.12...0.0.0.1722.0.0.0.0.0.0.0.0..0.0....0...1c..64.psy-ab..0.0.0....0.c9-bd2l-Fo8"
  69. }
  70. };
  71. //local property object
  72. const ScriptProps = {};
  73.  
  74. const EBayUsabilityEnhancer = {
  75. Initialize : function(){
  76.  
  77. //Get object properties setup for access
  78. Utilities.SetupProperties();
  79. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  80. //Get the ball rollin'.
  81. const pgSetup = this.SetupPage.bind(this);
  82. SharedObjects.StartupInterval = setInterval(pgSetup, SharedObjects.Intervals.Startup);
  83. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  84. if(SharedObjects.Constants.IsChrome)
  85. document.getElementsByTagName("body")[0].style.overflowY = "auto";
  86. },
  87. //InjectStyles: Injects a style file from cdn.se7en-soft.com. This file will eventually be hosted elsewhere.
  88. //This file contains element styles that are used througout this script.
  89. InjectStyles : function(callback){
  90. try{
  91. GM_xmlhttpRequest({
  92. method: "GET",
  93. url: SharedObjects.Constants.StyleSheetLocation + "?" + Math.random(),
  94. onload: function(r){
  95. const css = r.responseText;
  96. GM_addStyle(css);
  97. if(callback)
  98. callback.call(this);
  99. }
  100. });
  101. }catch(e){
  102. alert(e);
  103. }
  104. },
  105. SetupPage : function() {
  106. const self = this;
  107. const winLoc = window.location.href;
  108. //determine if we're on the Purchase History page or not.
  109. if(winLoc.indexOf("PurchaseHistory?") !== -1 || winLoc.indexOf("eBayISAPI.dll?MyEbay") !== -1){
  110. const ordersDiv = document.getElementById("orders");
  111. if(ordersDiv){
  112. clearInterval(SharedObjects.StartupInterval);
  113. const itemsContainer = ordersDiv.querySelector("div.ajax-wrap div.result-set-r");
  114. if(itemsContainer){
  115. const itemContainers = itemsContainer.querySelectorAll("div.order-r");
  116. const len = itemContainers.length;
  117. for(let i = 0; i < len; i++){
  118. const item = itemContainers[i];
  119. const sellerLink = item.querySelector("div div a.seller-id");
  120. if(sellerLink){
  121. //append X button to allow blocking this seller...
  122. const sellerName = sellerLink.textContent.trim();
  123. const img = new Image();
  124. img.setAttribute("style", "height:12px;width:12px;cursor:pointer;");
  125. img.setAttribute("Seller-ID", sellerName);
  126. sellerLink.parentNode.appendChild(img);
  127. img.onload = function(){
  128. const seller = this.getAttribute("Seller-ID");
  129. this.title = "Add seller '" + seller + "' to the block list.";
  130. this.addEventListener("click", function(){
  131. SellerManager.ConfirmBlockSeller(seller);
  132. }, false);
  133. };
  134. img.src = SharedObjects.Constants.CloseButtonIcon;
  135. }
  136. }
  137. }
  138. }
  139. const trackingLabelContainers = document.getElementsByClassName("tracking-label");
  140. if(trackingLabelContainers && trackingLabelContainers.length > 0){
  141. const containers = [].slice.call(trackingLabelContainers);
  142. containers.forEach(function(labelContainer, index){
  143. const labelLink = labelContainer.querySelector("a");
  144. const href = labelLink.dataset.url;
  145. const b = labelLink.querySelector("b");
  146. if(b)
  147. labelLink.removeChild(b);
  148.  
  149. const trackingNumber = labelLink.textContent;
  150. labelLink.textContent = trackingNumber;
  151.  
  152. const trackingLink = document.createElement("a");
  153. trackingLink.textContent = " [Tracking # Search]";
  154.  
  155. let tHref = SharedObjects.Constants.SearchTrackingLink.replace("[TRACKING#]", trackingNumber);
  156. while(tHref.indexOf("[TRACKING#]") !== -1)
  157. tHref = tHref.replace("[TRACKING#]", trackingNumber);
  158.  
  159. trackingLink.href = tHref;
  160. trackingLink.target = "_blank";
  161.  
  162. labelContainer.appendChild(trackingLink);
  163. });
  164. }
  165. } else { //we're on an item results page
  166. //find DIV elements for each product item in the search page
  167. const itmLinks = document.getElementsByClassName("sresult");
  168. //gotz links?
  169. if(itmLinks && itmLinks.length > 0){
  170. //remove the interval that was set when the function was first kicked off.
  171. clearInterval(SharedObjects.StartupInterval);
  172. try{
  173. //download and inject the css styles for our elements
  174. this.InjectStyles(() => {
  175. self.BeginOverlayConstruction();
  176. });
  177. } catch(e){
  178. alert(e);
  179. }
  180.  
  181. //get the item prices and stuff them into an array
  182. Utilities.GetItemPrices();
  183. //loop through the collection of DIV elements
  184. for(let i = 0; i < itmLinks.length; i++){
  185. //get the DIV at index 'i', from the collection
  186. const dLnk = itmLinks[i];
  187. SharedObjects.Arrays.ResultItems.push(dLnk);
  188. }
  189.  
  190. }
  191. }
  192. },
  193. //BeginOverlayConstruction: Begins construction of the control and display it on the page.
  194. BeginOverlayConstruction : function(){
  195.  
  196. //create the top pagination bar
  197. UIBuilder.CreateTopPagerControl();
  198. //create the overlay container
  199. const overlay = UIBuilder.BuildOverlay();
  200. //add controls to the overlay container
  201. UIBuilder.BuildOverlayControls(overlay);
  202. const badSellerBox = document.getElementById("GM_eBayOverlay_BadSellerBox");
  203. UIBuilder.BuildBlockedSellersBox(badSellerBox);
  204. SellerManager.AddBlockedSellers();
  205. //setup the controls on the overlay container.
  206. UIBuilder.OverlaySetup();
  207. //show the primary control overlay!
  208. document.getElementById('GM_eBayOverlayContainer').style.visibility = 'visible';
  209. },
  210. //ToggleResultOrder: Reorders items from lowest to highest, or returns them to their original order.
  211. ToggleResultOrder : function(lowToHigh){
  212. if(SharedObjects.Arrays.OriginalPrices.length == 0) return;
  213. const pArray = SharedObjects.Arrays.OriginalPrices.slice(0);
  214. if(lowToHigh) {
  215. try{
  216. pArray.sort(SortFunctions.SortLowToHigh);
  217. } catch(e){
  218. alert(e);
  219. }
  220. }
  221. const results = document.getElementById("ListViewInner");
  222. //var results = document.getElementsByClassName("rsittlref");
  223. //var resultParent = results[0].parentNode;
  224.  
  225. while(results.childNodes.length)
  226. results.removeChild(results.firstChild);
  227. for(let i = 0; i < pArray.length; i++)
  228. results.appendChild(pArray[i][0]);
  229. },
  230. //ToggleLinkFix: Toggles links to open in a new tab/window, or not.
  231. ToggleLinkFix : function(openNew){
  232. for(let i = 0; i < SharedObjects.Arrays.ResultItems.length; i++){
  233. const dLnk = SharedObjects.Arrays.ResultItems[i];
  234. //grab the anchor element from the H3 or H4 title element
  235. const anchor = dLnk.querySelector('h3 a') || dLnk.querySelector("h4 a");
  236. if(anchor){
  237. if(openNew){
  238. //get the link pointer location
  239. const href = anchor.href;
  240. //attach to the 'onClick' event for the anchor element so that it opens the link in a new window/tab
  241. anchor.setAttribute('onclick',"window.open('" + href + "');return false;");
  242. } else {
  243. anchor.removeAttribute('onclick');
  244. }
  245. }
  246. }
  247. },
  248. //ToggleTopPager: Toggles the visibility of the top pagination control.
  249. ToggleTopPager : function(show){
  250. document.getElementById("Pagination2").style.visibility = show ? 'visible' : 'collapse';
  251. },
  252. //ToggleResultFilterBar: Toggles the display of the filtered result bar.
  253. ToggleResultFilterBar : function(result, memberName, show){
  254. let filterBar = result.querySelector("tr.GM_eBayBadSeller_ResultFilterBarContainer");
  255. if(memberName.lastIndexOf("..") !== -1){
  256. const badSellers = ScriptProps.BlockedSellerList;
  257. const len = badSellers.length;
  258. for(let i = 0; i < len; i++){
  259. const bs = badSellers[i].toLowerCase();
  260. let pSellerName;
  261. if(memberName.lastIndexOf("..") !== -1){
  262. pSellerName = memberName.substring(0, memberName.lastIndexOf("..") - 1);
  263. } else
  264. pSellerName = memberName;
  265. if(bs.indexOf(pSellerName) !== -1){
  266. memberName = bs;
  267. break;
  268. }
  269. }
  270. }
  271.  
  272. if(show){
  273. if(!filterBar){
  274. filterBar = document.createElement("tr");
  275. filterBar.setAttribute("class", "GM_eBayBadSeller_ResultFilterBarContainer");
  276. const cell = document.createElement("td");
  277. filterBar.appendChild(cell);
  278. const dvBar = document.createElement("div");
  279. dvBar.setAttribute("class", "GM_eBayBadSeller_ResultFilterBar");
  280. cell.appendChild(dvBar);
  281. const table = document.createElement("table");
  282. table.style.width = "100%";
  283. const tRow = document.createElement("tr");
  284. table.appendChild(tRow);
  285. for(let i = 0; i < 4; i++){
  286. const tCell = document.createElement("td");
  287. tRow.appendChild(tCell);
  288. }
  289. dvBar.appendChild(table);
  290. let span = document.createElement("span");
  291. span.setAttribute("class", "GM_eBayBadSeller_FilterBarHeader");
  292. span.textContent = "Filtered Result [" + memberName + "]";
  293. tRow.childNodes[0].appendChild(span);
  294. span = document.createElement("span");
  295. span.setAttribute("class", "GM_ebayHeader");
  296. const link = document.createElement("a");
  297. link.setAttribute("class", "GM_eBayBadSeller_FilterBarLink");
  298. link.setAttribute("target", "_blank");
  299. link.textContent = "(View Listing)";
  300. link.title = "View item listing.";
  301.  
  302. let anchor = result.querySelector("h3 a");
  303. if(!anchor)
  304. anchor = result.querySelector("h4 a");
  305. link.href = anchor.href;
  306. span.appendChild(link);
  307. tRow.childNodes[2].appendChild(span);
  308. span = document.createElement("span");
  309. span.title = "Remove";
  310. span.textContent = "X";
  311. span.setAttribute("class", "GM_eBayBadSeller_FilterBarCloseButton");
  312. span.addEventListener('click', function(e){
  313. let p = e.target.parentNode;
  314. while(p != null){
  315. if(p.getAttribute("class") == "GM_eBayBadSeller_ResultFilterBarContainer")
  316. break;
  317. if(p.parentNode != null)
  318. p = p.parentNode;
  319. else
  320. break;
  321. }
  322. p.parentNode.setAttribute("data-removed", true);
  323. p.style.visibility = 'collapse';
  324. });
  325.  
  326. tRow.childNodes[3].appendChild(span);
  327. const tbody = result.querySelector("tbody");
  328. const id = result.attributes["listingid"].value;
  329. const children = [];
  330. while(result.firstChild){
  331. children.push(result.firstChild);
  332. result.removeChild(result.firstChild);
  333. }
  334. result.style.display = "none";
  335. SharedObjects.Arrays.FilteredItems.push([children,id,memberName]);
  336. if(!ScriptProps.UsePlaceholders)
  337. filterBar.style.visibility = 'collapse';
  338. if(!result.attributes["data-removed"])
  339. result.appendChild(filterBar);
  340. } else {
  341. filterBar.style.visibility = ScriptProps.UsePlaceholders ? 'visible' : 'collapse';
  342. }
  343. } else {
  344. if(filterBar)
  345. filterBar.style.visibility = 'collapse';
  346. const id = result.attributes["listingid"].value;
  347. const len = SharedObjects.Arrays.FilteredItems.length;
  348. let sellerData, content;
  349. for(let i = 0; i < len; i++){
  350. const fItem = SharedObjects.Arrays.FilteredItems[i];
  351. if(fItem[1] == id){
  352. const seller = SellerManager.GetSellerById(id);
  353. if(seller && seller.ID.lastIndexOf("...") !== -1){
  354. const fuzzyMatch = SellerManager.FuzzyMatchedName(seller.ID);
  355. if(fuzzyMatch){
  356. content = fItem[0];
  357. break;
  358. }
  359. } else if(seller && seller.ID === memberName){
  360. content = fItem[0];
  361. break;
  362. }
  363. }
  364. }
  365. if(content){
  366. for(let n = 0; n < content.length; n++){
  367. const c = content[n];
  368. result.appendChild(c);
  369. }
  370. if(filterBar)
  371. result.removeChild(filterBar);
  372. result.style.display = "block";
  373. }
  374. }
  375. },
  376. ToggleRemoveSponsoredItemResults : function(){
  377. const allItems = document.querySelectorAll("li.sresult.lvresult");
  378. for(let itm of allItems){
  379. const sponsoredSpan = itm.querySelector("div.promoted-lv");
  380. if(sponsoredSpan) {
  381. itm.style.display = ScriptProps.RemoveSponsoredItems ? "none" : "block";
  382. }
  383. }
  384. },
  385.  
  386. ToggleRemoveTopRatedItemResults : function(){
  387. const allItems = document.querySelectorAll("li.sresult.lvresult");
  388. for(let itm of allItems){
  389. const sponsoredSpan = itm.querySelector("img.iconETRS2");
  390. if(sponsoredSpan) {
  391. itm.style.display = ScriptProps.RemoveTopRatedItems ? "none" : "block";
  392. }
  393. }
  394. },
  395. //ToggleFilterBlockedSellerResults: Toggles blocked seller item results.
  396. ToggleFilterBlockedSellerResults : function(filterThem){
  397. const resultCount = SharedObjects.Arrays.OriginalPrices.length;
  398. const fiaLen = SharedObjects.Arrays.FilteredItems.length;
  399. if(fiaLen > 0){
  400. for(let i = 0; i < fiaLen; i++){
  401. const filteredItem = SharedObjects.Arrays.FilteredItems[i];
  402. const itemId = filteredItem[1];
  403. const priceItem = Utilities.GetResultItem(itemId);
  404. if(priceItem){
  405. const memberName = filteredItem[2];
  406. this.ToggleResultFilterBar(priceItem, memberName, filterThem);
  407. }
  408. }
  409. }
  410. if(filterThem){
  411. for(let i = 0; i < resultCount; i++){
  412. const resultItem = SharedObjects.Arrays.OriginalPrices[i][0];
  413. const listingId = resultItem.attributes["listingid"].value;
  414. let skip = false;
  415. //loop through the 'SharedObjects.Arrays.FilteredItems' and see if the current 'resultItem' is contained in it.
  416. //if so, we'll not double process it. The check is based on the listingId value.
  417. for(let n = 0; n < SharedObjects.Arrays.FilteredItems.length; n++){
  418. const fi = SharedObjects.Arrays.FilteredItems[n];
  419. const fid = fi[1];
  420. if(fid === listingId){
  421. skip = true;
  422. break;
  423. }
  424. }
  425. //should we skip passed everything and continue?
  426. if(skip) continue;
  427. const sd = SellerManager.GetSellerById(listingId);
  428. if(sd){
  429. let userName = sd.ID;
  430.  
  431. if(userName.length > 0){
  432. const fn = SellerManager.FuzzyMatchedName(userName);
  433. userName = fn || userName;
  434. if(SellerManager.IsBlockedSeller(userName, true)){
  435. const blockedUserSpan = document.getElementById("EBayEnhancer_BlockedSeller_" + userName);
  436. blockedUserSpan.style.opacity = "1";
  437. this.ToggleResultFilterBar(resultItem, userName, true);
  438. }
  439. }
  440. }
  441. }
  442. } else {
  443. for(let i = 0; i < resultCount; i++){
  444. const result = SharedObjects.Arrays.OriginalPrices[i];
  445. this.ToggleResultFilterBar(result[0], "", false);
  446. }
  447. }
  448. },
  449. //ToggleFilteredItemPlaceholders: Toggles visibility of removed item placeholders.
  450. ToggleFilteredItemPlaceholders : function(show){
  451. const filterBars = document.getElementsByClassName("GM_eBayBadSeller_ResultFilterBarContainer");
  452. const len = filterBars.length;
  453. if(!show){
  454. if(len > 0){
  455. for(let i = 0; i < len; i++){
  456. const fbar = filterBars[i];
  457. fbar.style.visibility = 'collapse';
  458. fbar.parentNode.style.display = "none";
  459. }
  460. }
  461. } else {
  462. if(len > 0){
  463. for(let i = 0; i < len; i++){
  464. const fbar = filterBars[i];
  465. const removed = fbar.parentNode.attributes["data-removed"];
  466. if(removed == undefined){
  467. fbar.style.visibility = 'visible';
  468. fbar.parentNode.style.display = "block";
  469. }
  470. }
  471. }
  472. }
  473. },
  474.  
  475. //CleanGMValues: Deletes all saved values associated with this script.
  476. CleanGMValues : function(){
  477. GM_deleteValue(SharedObjects.Constants.BlockedSellerList);
  478. GM_deleteValue(SharedObjects.Constants.FilterResults);
  479. GM_deleteValue(SharedObjects.Constants.RemoveSponsoredItems);
  480. GM_deleteValue(SharedObjects.Constants.ShowTopPager);
  481. GM_deleteValue(SharedObjects.Constants.RankByPrice);
  482. GM_deleteValue(SharedObjects.Constants.FixLinks);
  483. GM_deleteValue(SharedObjects.Constants.UsePlaceholders);
  484. },
  485. ResetAll : function(){
  486. this.CleanGMValues();
  487. location.reload();
  488. }
  489. };
  490.  
  491. const UIBuilder = {
  492. //BuildOverlay: Builds the control overlay.
  493. BuildOverlay : function(){
  494.  
  495. const rightPanel = document.getElementById("RightPanel");
  496.  
  497. const overlayContainer = document.createElement("div");
  498. overlayContainer.id = "GM_eBayOverlayContainer";
  499. //set the overlay to be initially collapsed. we'll show it after the style sheet has been downloaded and applied.
  500. overlayContainer.style.visibility = 'collapse';
  501. rightPanel.insertBefore(overlayContainer, rightPanel.firstChild);
  502. const overlay = document.createElement("div");
  503. overlay.id = "GM_eBayOverlay";
  504. const img = new Image();
  505. img.src = SharedObjects.Constants.ReloadButtonIcon;
  506. img.id = "GM_eBaySettingsResetIcon";
  507. img.title = "Reset All";
  508. img.addEventListener('click', function(){
  509. const result = confirm("Are you sure you want to clear and reset everything? This action cannot be undone.");
  510. if(result){
  511. //-----------------------------------
  512. EBayUsabilityEnhancer.ResetAll();
  513. }
  514. });
  515. overlay.appendChild(img);
  516. let header = document.createElement("h3");
  517. header.textContent = "Usability Settings";
  518. header.setAttribute("class", "GM_ebayHeader");
  519. overlay.appendChild(header);
  520. const controlBox = document.createElement("div");
  521. controlBox.id = "GM_eBayOverlay_ControlBox";
  522. overlay.appendChild(controlBox);
  523. header = document.createElement("h3");
  524. header.textContent = "Seller Block List";
  525. header.setAttribute("class", "GM_ebayHeader");
  526. overlay.appendChild(header);
  527. const badSellerBox = document.createElement("div");
  528. badSellerBox.id = "GM_eBayOverlay_BadSellerBox";
  529. overlay.appendChild(badSellerBox);
  530. overlayContainer.appendChild(overlay);
  531. return controlBox;
  532. },
  533. //BuildOverlayControls: Builds the controls for the overlay container.
  534. BuildOverlayControls : function(pOverlay){
  535. const ul = document.createElement("ul");
  536. ul.style.marginTop = "-8px";
  537. for(let i = 0; i < 6; i++){
  538. const li = document.createElement("li");
  539. ul.appendChild(li);
  540. }
  541. // -- Top Pager
  542. let cbContainer = this.BuildCheckbox("Show Pager At Top", "GM_cbShowTopPager");
  543. cbContainer.title = "Display a pager element at the top of the results view.";
  544. const cbShowTopPager = cbContainer.firstChild;
  545. cbShowTopPager.addEventListener('change', function(r) {
  546. const cb = r.target;
  547. ScriptProps.ShowTopPager = cb.checked;
  548. EBayUsabilityEnhancer.ToggleTopPager(cb.checked);
  549. });
  550.  
  551. ul.childNodes[0].appendChild(cbContainer);
  552. // Top Pager --
  553.  
  554. // -- Remove Sponsored Items
  555. cbContainer = this.BuildCheckbox("Remove Sponsored", "GM_RemoveSponsoredItems");
  556. cbContainer.title = "Remove sponsored item listings.";
  557. const cbRemoveSponsoredItems = cbContainer.firstChild;
  558. cbRemoveSponsoredItems.addEventListener("change", function(r){
  559. const cb = r.target;
  560. ScriptProps.RemoveSponsoredItems = cb.checked;
  561. EBayUsabilityEnhancer.ToggleRemoveSponsoredItemResults(cb.checked);
  562. });
  563. ul.childNodes[1].appendChild(cbContainer);
  564. // Remove Sponsored Items --
  565.  
  566. // -- Remove Top Rated Items
  567. cbContainer = this.BuildCheckbox("Remove Top Rated", "GM_RemoveTopRatedItems");
  568. cbContainer.title = "Remove Top Rated item listings.";
  569. const cbRemoveTopRatedItems = cbContainer.firstChild;
  570. cbRemoveTopRatedItems.addEventListener("change", (r) => {
  571. const cb = r.target;
  572. ScriptProps.RemoveTopRatedItems = cb.checked;
  573. EBayUsabilityEnhancer.ToggleRemoveTopRatedItemResults(cb.checked);
  574. });
  575. ul.childNodes[2].appendChild(cbContainer);
  576. // Remove Top Rated Items --
  577.  
  578. // -- Reorder Price-wise
  579. cbContainer = this.BuildCheckbox("Order Low To High", "GM_cbRankByPrice");
  580. cbContainer.title = "Re-order results by price; lowest to highest.";
  581. const cbReorderByPrice = cbContainer.firstChild;
  582. cbReorderByPrice.addEventListener('change', function(r){
  583. const cb = r.target;
  584. ScriptProps.RankByPrice = cb.checked;
  585. EBayUsabilityEnhancer.ToggleResultOrder(cb.checked);
  586. });
  587. ul.childNodes[3].appendChild(cbContainer);
  588. // Reorder Price-wise --
  589. // -- Fix Links
  590. cbContainer = this.BuildCheckbox("Open In New Tab", "GM_cbTabbedLinks");
  591. cbContainer.title = "Open item links in a new tab.";
  592. const cbTabbedLinks = cbContainer.firstChild;
  593. cbTabbedLinks.addEventListener('change', function(r){
  594. const cb = r.target;
  595. ScriptProps.FixLinks = cb.checked;
  596. EBayUsabilityEnhancer.ToggleLinkFix(cb.checked);
  597. });
  598. ul.childNodes[4].appendChild(cbContainer);
  599. // -- Fix Links
  600. // -- Filter Bad Sellers
  601. const fDiv = document.createElement("div");
  602. cbContainer = this.BuildCheckbox("Prune Results", "GM_cbFilterBlockedSellers");
  603. cbContainer.title = "Remove items posted by blocked sellers.";
  604. const cbBlockSellers = cbContainer.firstChild;
  605. cbBlockSellers.addEventListener('change', function(r){
  606. const cb = r.target;
  607. ScriptProps.FilterResults = cb.checked;
  608. document.getElementById('GM_cbFilterBlock_ShowPlaceholders').disabled = !cb.checked;
  609. EBayUsabilityEnhancer.ToggleFilterBlockedSellerResults(cb.checked);
  610. });
  611. fDiv.appendChild(cbContainer);
  612. const fUl = document.createElement("ul");
  613. const fli = document.createElement("li");
  614. fUl.appendChild(fli);
  615. cbContainer = this.BuildCheckbox("Use Placeholders", "GM_cbFilterBlock_ShowPlaceholders");
  616. cbContainer.title = "Show placeholders for removed items.";
  617. const cbPlaceholder = cbContainer.firstChild;
  618. cbPlaceholder.addEventListener('change', function(r){
  619. const cb = r.target;
  620. ScriptProps.UsePlaceholders = cb.checked;
  621. EBayUsabilityEnhancer.ToggleFilteredItemPlaceholders(cb.checked);
  622. });
  623. fli.appendChild(cbContainer);
  624. fDiv.appendChild(fUl);
  625. ul.childNodes[5].appendChild(fDiv);
  626. // Filter Bad Sellers --
  627. //append the actual list element to the overlay
  628. pOverlay.appendChild(ul);
  629. },
  630. //CreateTopPagerControl: Creates the top pagination control.
  631. CreateTopPagerControl : function(){
  632. const paginator = document.getElementById("Pagination");
  633. if(paginator){
  634. const pager2 = paginator.cloneNode(true);
  635. if(pager2){
  636. pager2.style.visibility = 'collapse';
  637. pager2.id = "Pagination2";
  638. pager2.style.marginTop = "10px";
  639. pager2.style.marginBottom = "10px";
  640. const container = document.getElementById("MessageContainer");
  641. if(container)
  642. container.parentNode.insertBefore(pager2, container.nextSibling);
  643. }
  644. }
  645. },
  646. //OverlaySetup: Sets up the controls inside the overlay container.
  647. OverlaySetup : function(){
  648.  
  649. //Checkbox setup: Show Pager At Top
  650. document.getElementById("GM_cbShowTopPager").checked = ScriptProps.ShowTopPager;
  651. setTimeout(function(){
  652. EBayUsabilityEnhancer.ToggleTopPager(ScriptProps.ShowTopPager);
  653. }, SharedObjects.Intervals.Timeout);
  654. //Checkbox setup: Order Low To High
  655. document.getElementById("GM_cbRankByPrice").checked = ScriptProps.RankByPrice;
  656. setTimeout(function(){
  657. EBayUsabilityEnhancer.ToggleResultOrder(ScriptProps.RankByPrice);
  658. }, SharedObjects.Intervals.Timeout);
  659. //Checkbox setup: Open In New Tab
  660. document.getElementById("GM_cbTabbedLinks").checked = ScriptProps.FixLinks;
  661. setTimeout(function(){
  662. EBayUsabilityEnhancer.ToggleLinkFix(ScriptProps.FixLinks);
  663. }, SharedObjects.Intervals.Timeout);
  664. //Checkbox setup: Filter Results
  665. document.getElementById("GM_cbFilterBlockedSellers").checked = ScriptProps.FilterResults;
  666. setTimeout(function(){
  667. EBayUsabilityEnhancer.ToggleFilterBlockedSellerResults(ScriptProps.FilterResults);
  668. }, SharedObjects.Intervals.Timeout);
  669. document.getElementById("GM_RemoveSponsoredItems").checked = ScriptProps.RemoveSponsoredItems;
  670. setTimeout(function(){
  671. EBayUsabilityEnhancer.ToggleRemoveSponsoredItemResults(ScriptProps.RemoveSponsoredItems);
  672. }, SharedObjects.Intervals.Timeout);
  673.  
  674. document.getElementById("GM_RemoveTopRatedItems").checked = ScriptProps.RemoveTopRatedItems;
  675. setTimeout(()=>{
  676. EBayUsabilityEnhancer.ToggleRemoveTopRatedItemResults(ScriptProps.RemoveTopRatedItems);
  677. }, SharedObjects.Intervals.Timeout);
  678.  
  679. //Checkbox setup: Use Placeholders
  680. const cbPlaceHolders = document.getElementById("GM_cbFilterBlock_ShowPlaceholders");
  681. cbPlaceHolders.checked = ScriptProps.UsePlaceholders;
  682. cbPlaceHolders.disabled = !ScriptProps.FilterResults;
  683. setTimeout(function(){
  684. EBayUsabilityEnhancer.ToggleFilteredItemPlaceholders(ScriptProps.UsePlaceholders);
  685. }, SharedObjects.Intervals.Timeout);
  686. },
  687. //BuildBlockedSellerNameLink: Builds a seller name element that allows for copying or removal of the seller from the black list.
  688. BuildBlockedSellerNameLink : function(sellerName){
  689. if(!sellerName) return null;
  690. const table = document.createElement('table');
  691. table.className = "GM_eBayTableElement";
  692. const row = document.createElement("tr");
  693. for(let i = 0; i < 3; i++){
  694. const cell = document.createElement('td');
  695. row.appendChild(cell);
  696. }
  697. table.appendChild(row);
  698. const nameSpan = document.createElement("span");
  699. nameSpan.id = "EBayEnhancer_BlockedSeller_" + sellerName;
  700. nameSpan.style.fontWeight = "bold";
  701. nameSpan.style.cursor = "default";
  702. nameSpan.className = "unselectable";
  703. let slrName = sellerName;
  704. if(slrName.length > 13)
  705. slrName = slrName.substr(0, 12) + "...";
  706. nameSpan.textContent = slrName;
  707. nameSpan.title = sellerName;
  708. nameSpan.style.opacity = ".6"; //set to 60% opacity
  709. row.childNodes[0].appendChild(nameSpan);
  710. const copyImage = new Image();
  711. copyImage.src = SharedObjects.Constants.ClipboardIcon;
  712. copyImage.style.cursor = "pointer";
  713. copyImage.title = "Copy the seller name to the clipboard.";
  714. copyImage.setAttribute('data-sellerName', sellerName);
  715. copyImage.addEventListener('click', function(r){
  716. const img = r.target;
  717. const sn = img.attributes["data-sellerName"].value;
  718. GM_setClipboard(sn);
  719. });
  720. const cell1 = row.childNodes[1];
  721. cell1.style.width = "16px";
  722. cell1.appendChild(copyImage);
  723. const removeLink = new Image();
  724. removeLink.src = SharedObjects.Constants.CloseButtonIcon;
  725. removeLink.setAttribute('data-badSellerName', sellerName);
  726. removeLink.title = "Remove blocked seller '" + sellerName + "'.";
  727. removeLink.style.cursor = "pointer";
  728. removeLink.addEventListener('click', function(r){
  729. const btn = r.target;
  730. const sllr = btn.getAttribute('data-badSellerName');
  731. const result = confirm("Are you sure you want to remove '" + sllr + "' from your seller black list?");
  732. if(result){
  733. const badSellers = ScriptProps.BlockedSellerList;
  734. const idx = badSellers.indexOf(sllr);
  735. if(idx > -1){
  736. let listings = SellerManager.GetListingsBySeller(sllr);
  737. for(let i = 0; i < listings.length; i++){
  738. const listing = listings[i][0];
  739. EBayUsabilityEnhancer.ToggleResultFilterBar(listing, sllr, false);
  740. }
  741. const remSellers = badSellers.splice(idx, 1);
  742. ScriptProps.BlockedSellerList = badSellers;
  743.  
  744. const ulst = document.getElementById("GM_eBayBadSellers_UList");
  745. while(ulst.firstChild != null)
  746. ulst.removeChild(ulst.firstChild);
  747. SellerManager.AddBlockedSellers();
  748. }
  749. }
  750. }, false);
  751. const cell2 = row.childNodes[2];
  752. cell2.style.width = "16px";
  753. cell2.appendChild(removeLink);
  754. return table;
  755. },
  756. //BuildCheckbox: Constructs a checkbox with the given title and id.
  757. BuildCheckbox : function(title, id){
  758. const d = document.createElement("div");
  759. d.setAttribute("class", "cbx unselectable");
  760. const i = document.createElement("input");
  761. i.id = id;
  762. i.type = "checkbox";
  763. const l = document.createElement("label");
  764. l.setAttribute('for', id);
  765. const s = document.createElement("span");
  766. s.className = "cbx GM_cbx";
  767. s.textContent = title;
  768. l.appendChild(s);
  769. d.appendChild(i);
  770. d.appendChild(l);
  771. return d;
  772. },
  773. ImportFromFile : function(file){
  774. const reader = new FileReader();
  775. reader.onload = function(r){
  776. const result = r.target.result;
  777. if(result){
  778. const names = result.split("\r\n");
  779. if(names.length > 0){
  780. //add each name to the bad sellers list.
  781. for(let x = 0; x < names.length; x++){
  782. const n = names[x].trim();
  783. SellerManager.AddBlockedSeller(n, true);
  784. }
  785. }
  786. }
  787. };
  788. reader.readAsText(file, "utf-8");
  789. },
  790. //BuildBlockedSellersBox: Builds the bad sellers control box.
  791. BuildBlockedSellersBox : function(pOverlay){
  792. const self = this;
  793. const ul = document.createElement("ul");
  794. ul.id = "GM_eBayBadSellers_UList";
  795. ul.className = "GM_eBayBadSeller_NameLinkList";
  796. const d = document.createElement("div");
  797. d.style.marginLeft = "8px";
  798. d.style.display = "table";
  799. d.style.marginBottom = "5px";
  800. d.style.marginTop = "-8px";
  801. const txtAddBlockedSeller = document.createElement("input");
  802. txtAddBlockedSeller.type = "text";
  803. txtAddBlockedSeller.id = "GM_eBayNewBadSeller_InputBox";
  804. txtAddBlockedSeller.title = "Type or paste the name of the seller you want to block and press 'Enter'.";
  805. let style = "width:90px;margin:0;";
  806. if(SharedObjects.Constants.IsChrome)
  807. style += "height:16px;";
  808. txtAddBlockedSeller.setAttribute("style", style);
  809. txtAddBlockedSeller.addEventListener('keyup', function(e){
  810. const key = e.keyCode;
  811. if(key === 13){
  812. const box = document.getElementById("GM_eBayNewBadSeller_InputBox");
  813. const txt = box.value.trim();
  814. box.value = "";
  815. if(SellerManager.AddBlockedSeller(txt, true)){
  816. //filter results for new seller name...
  817. const filterUser = ScriptProps.FilterResults;
  818. if(filterUser != undefined && filterUser){
  819. EBayUsabilityEnhancer.ToggleFilterBlockedSellerResults(true);
  820. }
  821. }
  822. }
  823. });
  824. const fileInput = document.createElement("input");
  825. fileInput.type = "file";
  826. fileInput.id = "GM_eBayNewBadSeller_fileInput";
  827. fileInput.style.display = "none";
  828. fileInput.setAttribute("accept", ".txt");
  829. const btnImportSellers = document.createElement("input");
  830. btnImportSellers.type = "button";
  831. btnImportSellers.value = "I";
  832. btnImportSellers.id = "GM_eBayNewBadSeller_btnImportBadSellers";
  833. btnImportSellers.setAttribute("style", "height:26px;margin-left:-2px;width:24px;");
  834. btnImportSellers.title = "Import sellers from a text file.";
  835. btnImportSellers.onclick = function(){
  836. fileInput.addEventListener("change", function(evt){
  837. const files = evt.target.files;
  838. for(let i = 0; i < files.length; i++){
  839. const f = files[i];
  840. if(!f.type.match("text/plain"))
  841. continue;
  842. self.ImportFromFile(f);
  843. }
  844. }, false);
  845. fileInput.click();
  846. };
  847. const btnExportSellers = document.createElement("input");
  848. btnExportSellers.type = "button";
  849. btnExportSellers.value = "O";
  850. btnExportSellers.id = "GM_eBayNewBadSeller_btnExportSellers";
  851. btnExportSellers.setAttribute("style", "height:26px;margin-left:-2px;width:24px;");
  852. btnExportSellers.title = "Export sellers to a text file.";
  853. btnExportSellers.onclick = function(){
  854. const badSellers = ScriptProps.BlockedSellerList;
  855. let agg = "";
  856. for(let i = 0; i < badSellers.length; i++)
  857. agg += badSellers[i] + "\r\n";
  858. agg = agg.trim();
  859.  
  860. const dLink = document.createElement("a");
  861. dLink.download = "EBayUsabilityEnhancer_BlockedSellersList.txt";
  862. dLink.href = "data:text/plain," + encodeURI(agg);
  863. dLink.style.display = "none";
  864. dLink.textContent = "download";
  865. document.body.appendChild(dLink);
  866. dLink.click();
  867. document.body.removeChild(dLink);
  868. };
  869. const label = document.createElement("label");
  870. label.textContent = "Add, Import or Export:";
  871. label.setAttribute('for', 'GM_eBayNewBadSeller_InputBox');
  872. const table = document.createElement("table");
  873. table.style.width = "100%";
  874. const row = document.createElement("tr");
  875. for(let i = 0; i < 2; i++)
  876. row.appendChild(document.createElement("td"));
  877. table.appendChild(row);
  878. const copyAllLink = document.createElement("span");
  879. copyAllLink.textContent = "Copy All";
  880. copyAllLink.className = "GM_eBayBadSellers_ActionLink";
  881. copyAllLink.title = "Copy all blocked sellers to the clipboard.";
  882. copyAllLink.addEventListener('click', function(e){
  883. //------------------------------------------
  884. const blockedSellers = ScriptProps.BlockedSellerList;
  885. if(blockedSellers){
  886. let sellerList = "";
  887. for(let i = 0; i < blockedSellers.length; i++)
  888. sellerList += blockedSellers[i] + ",";
  889. sellerList = sellerList.substr(0, sellerList.lastIndexOf(","));
  890. GM_setClipboard(sellerList);
  891. alert("All blocked sellers have been copied to the system clipboard.");
  892. }
  893. });
  894. row.childNodes[0].appendChild(copyAllLink);
  895. const clearLink = document.createElement('span');
  896. clearLink.textContent = "Remove All";
  897. clearLink.className = "GM_eBayBadSellers_ActionLink";
  898. clearLink.title = "Remove all sellers from the block list.";
  899. clearLink.setAttribute("style", "float:right;margin-right:7px;");
  900. clearLink.addEventListener('click', function(e){
  901. let blockedSellers = ScriptProps.BlockedSellerList;
  902. if(blockedSellers){
  903. const result = confirm("Are you sure you want to remove all sellers from your block list? This action cannot be undone.");
  904. if(result){
  905. //-----------------------------------
  906. blockedSellers = [];
  907. ScriptProps.BlockedSellerList = blockedSellers;
  908. SellerManager.AddBlockedSellers();
  909. }
  910. }
  911. });
  912. row.childNodes[1].appendChild(clearLink);
  913. d.appendChild(label);
  914. d.appendChild(txtAddBlockedSeller);
  915. d.appendChild(fileInput);
  916. d.appendChild(btnImportSellers);
  917. d.appendChild(btnExportSellers);
  918. d.appendChild(table);
  919. pOverlay.appendChild(d);
  920.  
  921. pOverlay.appendChild(ul);
  922. }
  923. };
  924.  
  925. const SellerManager = {
  926. //IsBlockedSeller: Returns whether or not the specified seller name is in the seller black list.
  927. IsBlockedSeller : function(sellerName, fuzzyMatch){
  928. if(!sellerName || sellerName.length === 0) return true;
  929. const badSellers = ScriptProps.BlockedSellerList;
  930. if(fuzzyMatch && fuzzyMatch === true){
  931. const len = badSellers.length;
  932. for(let i = 0; i < len; i++){
  933. const bs = badSellers[i].toLowerCase();
  934. let pSellerName;
  935. if(sellerName.lastIndexOf("...") !== -1){
  936. pSellerName = sellerName.substring(0, sellerName.lastIndexOf("...") - 1);
  937. } else
  938. pSellerName = sellerName;
  939. if(bs.indexOf(pSellerName) !== -1)
  940. return true;
  941. }
  942. }
  943. return badSellers.indexOf(sellerName) !== -1;
  944. },
  945. FuzzyMatchedName : function(fuzzyName){
  946. if(fuzzyName.lastIndexOf("...") === -1)
  947. return null;
  948. const badSellers = ScriptProps.BlockedSellerList,
  949. len = badSellers.length;
  950. let retval;
  951. const tLen = SharedObjects.Arrays.TruncatedSellers.length;
  952. if(tLen > 0){
  953. //check if we've got a match in the TruncatedSellers array.
  954. for(let t = 0; t < tLen; t++){
  955. const pair = SharedObjects.Arrays.TruncatedSellers[t];
  956. if(pair[0].toLowerCase() === fuzzyName.toLowerCase())
  957. return pair[1];
  958. }
  959. }
  960. for(let n = 0; n < len; n++){
  961. const bs = badSellers[n].toLowerCase();
  962. let pSellerName;
  963. if(fuzzyName.lastIndexOf("...") !== -1)
  964. pSellerName = fuzzyName.substring(0, fuzzyName.lastIndexOf("...")).toLowerCase();
  965. else
  966. pSellerName = fuzzyName.toLowerCase();
  967.  
  968. if(bs.indexOf(pSellerName) !== -1){
  969. retval = bs;
  970. break;
  971. }
  972. }
  973. return retval;
  974. },
  975. //GetSellerById: Returns a SellerData object based on the supplied listingid parameter.
  976. GetSellerById : function(listingid){
  977. const count = SharedObjects.Arrays.AllSellerData.length;
  978. for(let i = 0; i < count; i++){
  979. const sellerData = SharedObjects.Arrays.AllSellerData[i];
  980. const id = sellerData[1];
  981. if(id === listingid)
  982. return sellerData[0];
  983. }
  984. return null;
  985. },
  986. //AddBlockedSeller: Creates and adds a bad seller based on the supplied seller name.
  987. AddBlockedSeller : function(sellerName, bailIfExists){
  988. sellerName = sellerName.trim();
  989. if(sellerName.length === 0)
  990. return false;
  991. const badSellers = ScriptProps.BlockedSellerList;
  992. if(badSellers.indexOf(sellerName) === -1){
  993. try{
  994. badSellers.push(sellerName);
  995. } catch(e){
  996. alert("T: " + e);
  997. }
  998. ScriptProps.BlockedSellerList = badSellers;
  999. } else if(bailIfExists && bailIfExists === true) {
  1000. return false;
  1001. }
  1002. const sellerLink = UIBuilder.BuildBlockedSellerNameLink(sellerName);
  1003. const li = document.createElement("li");
  1004. if(sellerLink)
  1005. li.appendChild(sellerLink);
  1006. const uList = document.getElementById("GM_eBayBadSellers_UList");
  1007. if(uList){
  1008. const badSellers = ScriptProps.BlockedSellerList;
  1009. badSellers.sort(SortFunctions.SortAlphaNum);
  1010. const idx = badSellers.indexOf(sellerName);
  1011. if(idx !== -1){
  1012. const liChild = uList.childNodes[idx];
  1013. uList.insertBefore(li, liChild);
  1014. } else {
  1015. uList.appendChild(li);
  1016. }
  1017. return true;
  1018. }
  1019. return false;
  1020. },
  1021. //AddBlockedSellers: Adds the stored bad sellers to the page.
  1022. AddBlockedSellers : function(){
  1023. const sellers = ScriptProps.BlockedSellerList;
  1024. if(sellers == undefined) return;
  1025. const uList = document.getElementById("GM_eBayBadSellers_UList");
  1026. if(uList){
  1027. while(uList.firstChild) { uList.removeChild(uList.firstChild);}
  1028. }
  1029. if(sellers){
  1030. sellers.sort(SortFunctions.SortAlphaNum);
  1031. try{
  1032. ScriptProps.BlockedSellerList = sellers;
  1033. } catch(e2){
  1034. //alert("err: " + e2)
  1035. }
  1036. }
  1037. if(sellers.length > 0){
  1038. for(let i = 0; i < sellers.length; i++){
  1039. const seller = sellers[i];
  1040. this.AddBlockedSeller(seller);
  1041. }
  1042. }
  1043. },
  1044. //GetListingsBySeller: Returns a collection of all listings that belong to the supplied sellerName.
  1045. GetListingsBySeller : function(sellerName){
  1046. const count = SharedObjects.Arrays.AllSellerData.length;
  1047. let listings = [];
  1048. sellerName = sellerName.toLowerCase();
  1049. for(let i = 0; i < count; i++){
  1050. const sd = SharedObjects.Arrays.AllSellerData[i];
  1051. const id = sd[1];
  1052. let seller = sd[0].ID;
  1053. if(seller.lastIndexOf("...") !== -1){
  1054. const badSellers = ScriptProps.BlockedSellerList;
  1055. const len = badSellers.length;
  1056. for(let n = 0; n < len; n++){
  1057. const bs = badSellers[n].toLowerCase();
  1058. let pSellerName;
  1059. if(seller.lastIndexOf("...") !== -1)
  1060. pSellerName = seller.substring(0, seller.lastIndexOf("...")).toLowerCase();
  1061. else
  1062. pSellerName = seller.toLowerCase();
  1063.  
  1064. if(bs.indexOf(pSellerName) !== -1){
  1065. seller = bs;
  1066. break;
  1067. }
  1068. }
  1069. }
  1070. seller = seller.toLowerCase();
  1071. sellerName = sellerName.toLowerCase();
  1072. if(seller === sellerName){
  1073. const priceCount = SharedObjects.Arrays.OriginalPrices.length;
  1074. for(let j = 0; j < priceCount; j++){
  1075. const pd = SharedObjects.Arrays.OriginalPrices[j];
  1076. const pdId = pd[0].attributes["listingid"].value;
  1077. if(id === pdId){
  1078. listings.push(pd);
  1079. }
  1080. }
  1081. }
  1082. }
  1083. return listings;
  1084. },
  1085. GetSellerData : function(resultNode){
  1086. const sellerData = {
  1087. ID: "",
  1088. FeedbackScore: "",
  1089. ReviewCount: ""
  1090. };
  1091. const dynDiv = resultNode.querySelector("ul.lvdetails");
  1092. const listingId = resultNode.getAttribute("listingid");
  1093. const len = dynDiv.childNodes.length;
  1094. const self = this;
  1095. if(len > 0) {
  1096. for(let m = 0; m < len; m++){
  1097. const n = dynDiv.childNodes[m];
  1098. let spns;
  1099. try{
  1100. spns = n.querySelectorAll("span.selrat");
  1101. } catch (e){
  1102. continue;
  1103. }
  1104. if(spns){
  1105. const text = n.textContent.trim();
  1106. if(text.indexOf("Seller:") !== -1){
  1107. const idx = text.indexOf("Seller:");
  1108. const idx2 = text.indexOf("(");
  1109. const idx3 = text.indexOf(")");
  1110. sellerData.ID = text.substring(idx + 8, idx2).trim();
  1111. const img = new Image();
  1112. img.setAttribute("style", "height:12px;width:12px;cursor:pointer;");
  1113. img.setAttribute("Seller-ID", sellerData.ID);
  1114. try{
  1115. let revCount = spns[0].textContent.replace("(", "").replace(")", "");
  1116. while(revCount.indexOf(",") != -1)
  1117. revCount = revCount.replace(",","");
  1118. sellerData.ReviewCount = parseInt(revCount);
  1119. }catch(e){
  1120. //alert(e);
  1121. }
  1122. try{
  1123. const fbScore = parseFloat(spns[1].textContent.replace("%", "").trim());
  1124. sellerData.FeedbackScore = fbScore;
  1125. const score = sellerData.FeedbackScore;
  1126. let color = "";
  1127. if(score >= 99.2){
  1128. //should be ok to buy from this guy.
  1129. color = "green";
  1130. } else if(score >= 98 && score <= 99.1) {
  1131. //better check the neg's!
  1132. color = "#E8823A";
  1133. } else {
  1134. //bad seller!
  1135. color = "red";
  1136. }
  1137. spns[1].setAttribute("style", "color:" + color + ";");
  1138. const p = spns[0];
  1139. p.parentNode.appendChild(img);
  1140. } catch(e){
  1141. img.setAttribute("style", "margin-left:5px;height:12px;width:12px;cursor:pointer;");
  1142. n.appendChild(img);
  1143. }
  1144. try{
  1145. if(sellerData.ID.lastIndexOf("...") !== -1){
  1146. Utilities.GetFullUserName(listingId, function(lstId, resultText){
  1147. const fullUserName = Utilities.ParseNameFromTextChunk(resultText);
  1148. if(fullUserName){
  1149. //add a mapping for the truncated name and the full name.
  1150. SharedObjects.Arrays.TruncatedSellers.push([sellerData.ID, fullUserName]);
  1151. img.onload = function(){
  1152. this.title = "Add seller '" + fullUserName + "' to the block list.";
  1153. const seller = this.getAttribute("Seller-ID");
  1154. img.addEventListener("click", function(){
  1155. self.ConfirmBlockSeller(fullUserName);
  1156. }, false);
  1157. };
  1158. img.src = SharedObjects.Constants.CloseButtonIcon;
  1159. self.SetupFeedbackLink(fullUserName, dynDiv);
  1160. }
  1161. });
  1162. } else {
  1163. img.onload = function(){
  1164. const seller = this.getAttribute("Seller-ID");
  1165. this.title = "Add seller '" + seller + "' to the block list.";
  1166. img.addEventListener("click", function(){
  1167. self.ConfirmBlockSeller(seller);
  1168. }, false);
  1169. };
  1170. img.src = SharedObjects.Constants.CloseButtonIcon;
  1171. this.SetupFeedbackLink(sellerData.ID, dynDiv);
  1172. }
  1173. }catch(e){
  1174. alert(e);
  1175. }
  1176. }
  1177. }
  1178. }
  1179.  
  1180. }
  1181. return sellerData;
  1182. },
  1183. ConfirmBlockSeller : function(sellerName){
  1184. if(!sellerName || sellerName.length === 0) return;
  1185. if(SellerManager.IsBlockedSeller(sellerName)){
  1186. alert("Seller '" + sellerName + "' is already in your block list.");
  1187. return;
  1188. }
  1189. const msg = "Are you sure you want to block the seller '" + sellerName + "'?";
  1190. if(confirm(msg)) {
  1191. if(SellerManager.AddBlockedSeller(sellerName, true))
  1192. EBayUsabilityEnhancer.ToggleFilterBlockedSellerResults(true);
  1193. }
  1194. },
  1195. SetupFeedbackLink : function(sellerName, parentNode){
  1196. const fbLink = document.createElement("a");
  1197. fbLink.href = SharedObjects.Constants.NegativeFeedbackLink.replace("[SELLERNAME]", sellerName);
  1198. fbLink.textContent = "Show Negative Feedback";
  1199. fbLink.title = "Opens the seller's negative feedback ratings page in a new tab.";
  1200. fbLink.setAttribute('target', '_blank');
  1201. const li = document.createElement("li");
  1202. li.appendChild(fbLink);
  1203. parentNode.appendChild(li);
  1204. },
  1205. };
  1206.  
  1207. const Utilities = {
  1208. //SetupProperties: Creates property get/set functions.
  1209. SetupProperties : function(){
  1210. Object.defineProperty(ScriptProps, "ShowTopPager",{
  1211. get: function(){ return GM_getValue(SharedObjects.Constants.ShowTopPager); },
  1212. set: function(value){GM_setValue(SharedObjects.Constants.ShowTopPager, value); }
  1213. });
  1214. Object.defineProperty(ScriptProps, "RankByPrice", {
  1215. get: function() { return GM_getValue(SharedObjects.Constants.RankByPrice); },
  1216. set: function(value){ GM_setValue(SharedObjects.Constants.RankByPrice, value); }
  1217. });
  1218. Object.defineProperty(ScriptProps, "FixLinks", {
  1219. get: function() { return GM_getValue(SharedObjects.Constants.FixLinks); },
  1220. set: function(value) { GM_setValue(SharedObjects.Constants.FixLinks, value); }
  1221. });
  1222. Object.defineProperty(ScriptProps, "FilterResults", {
  1223. get: function(){ return GM_getValue(SharedObjects.Constants.FilterResults); },
  1224. set: function(value){ GM_setValue(SharedObjects.Constants.FilterResults, value); }
  1225. });
  1226. Object.defineProperty(ScriptProps, 'UsePlaceholders', {
  1227. get: function(){ return GM_getValue(SharedObjects.Constants.UsePlaceholders); },
  1228. set: function(value){ GM_setValue(SharedObjects.Constants.UsePlaceholders, value); }
  1229. });
  1230. Object.defineProperty(ScriptProps, "RemoveSponsoredItems", {
  1231. get: function(){ return GM_getValue(SharedObjects.Constants.RemoveSponsoredItems); },
  1232. set: function(value){ GM_setValue(SharedObjects.Constants.RemoveSponsoredItems, value); }
  1233. });
  1234.  
  1235. Object.defineProperty(ScriptProps, "RemoveTopRatedItems", {
  1236. get: function(){return GM_getValue(SharedObjects.Constants.RemoveTopRatedItems);},
  1237. set: function(value){GM_setValue(SharedObjects.Constants.RemoveTopRatedItems, value);}
  1238. });
  1239. Object.defineProperty(ScriptProps, "BlockedSellerList", {
  1240. get: function() {
  1241. const blockList = GM_getValue(SharedObjects.Constants.BlockedSellerList);
  1242. return blockList != null ? blockList.split(' ') : [];
  1243. },
  1244. set: function(value) {
  1245. const len = value.length;
  1246. let agg = "";
  1247. for(let i = 0; i < len; i++){
  1248. agg += value[i] + " ";
  1249. }
  1250. agg = agg.trim();
  1251. try{
  1252. GM_setValue(SharedObjects.Constants.BlockedSellerList, agg);
  1253. } catch(e){
  1254. //alert("Err:\r\n" + e);
  1255. }
  1256. }
  1257. });
  1258. },
  1259. CreateSelectOption: function(text, val){
  1260. const opt = document.createElement("option");
  1261. opt.value = val;
  1262. opt.textContent = text;
  1263. return opt;
  1264. },
  1265.  
  1266. //GetPageContent: Retrieves the content of the page at the supplied Url.
  1267. GetPageContent : function(url, listingid, callback){
  1268. if(!url) return;
  1269.  
  1270. //GM_xmlhttpRequest onload event is not working. So, we're going old-school, here.
  1271. var oReq = new XMLHttpRequest();
  1272. oReq.addEventListener("load", function(){
  1273. const result = this.responseText;
  1274. if(callback)
  1275. callback.call(this, listingid, result);
  1276. });
  1277. oReq.open("GET", url, true);
  1278. oReq.send(null);
  1279.  
  1280. // GM_xmlhttpRequest({
  1281. // method: "GET",
  1282. // url: url,
  1283. // onload: function(r){
  1284. // console.log(r);
  1285. // const result = r.responseText;
  1286. // if(callback)
  1287. // callback.call(this, listingid, result);
  1288. // },
  1289. // onerror: function(r) {
  1290. // console.log(r);
  1291. // }
  1292. // });
  1293. },
  1294. GetFullUserName : function(listingId, callback){
  1295. if(!listingId || listingId.length === 0) return null;
  1296. const url = "//www.ebay.com/itm/" + listingId;
  1297. this.GetPageContent(url, listingId, callback);
  1298. },
  1299. //GetResultItem: Returns a result item, from the 'SharedObjects.Arrays.OriginalPrices' array, that matches the supplied 'itemId' parameter value.
  1300. GetResultItem : function(itemId){
  1301. if(!itemId) return null;
  1302. const len = SharedObjects.Arrays.OriginalPrices.length;
  1303. for(let i = 0; i < len; i++){
  1304. const result = SharedObjects.Arrays.OriginalPrices[i][0];
  1305. const id = result.attributes["listingid"].value;
  1306. if(id === itemId)
  1307. return result;
  1308. }
  1309. return null;
  1310. },
  1311. //GetItemPrices: Gets an array of items and their prices.
  1312. GetItemPrices : function(){
  1313. const results = document.getElementsByClassName("sresult");
  1314. if(results.length > 0 && SharedObjects.Arrays.OriginalPrices.length == 0){
  1315. let pText, nText, result;
  1316. for(let i = 0; i < results.length; i++){
  1317. try{
  1318. result = results[i];
  1319. const sd = SellerManager.GetSellerData(result);
  1320. SharedObjects.Arrays.AllSellerData.push([sd, result.attributes["listingid"].value]);
  1321.  
  1322. const prc = result.querySelector("ul.lvprices li.lvprice.prc span");
  1323. if(prc){
  1324. pText = prc.innerHTML;
  1325. //<span class="prRange">$5.97 <span>to</span> $10.97</span>
  1326. const prRange = prc.querySelector("span.prRange");
  1327. if(prRange)
  1328. pText = prRange.innerHTML;
  1329. //the span may contain child elements.
  1330. //in such case, we only want the text that preceeds any elements.
  1331. if(pText.indexOf("<") !== -1)
  1332. pText = pText.substring(0, pText.indexOf("<"));
  1333. let price = pText.trim();
  1334. if(price.indexOf(' ') != -1)
  1335. price = price.split(' ')[1].trim();
  1336. const n = price[0];
  1337. if(n){
  1338. if(!/\d/.test(n))
  1339. price = price.substring(1);
  1340. nText = result;
  1341. while(price.indexOf(',') > -1)
  1342. price = price.replace(',','');
  1343. if(price){
  1344. SharedObjects.Arrays.OriginalPrices.push([result, price]);
  1345. }
  1346. }
  1347. }
  1348. }catch(e){
  1349. //alert(result.outerHTML);
  1350. //alert(e + "\r\niteration: " + i + "\r\n" + nText);
  1351. }
  1352.  
  1353. }
  1354. }
  1355. },
  1356. ParseNameFromTextChunk : function(textChunk){
  1357. if(!textChunk || textChunk.length === 0) return null;
  1358. let ridx = textChunk.indexOf("RightSummaryPanel");
  1359. let lidx = textChunk.indexOf("LeftSummaryPanel");
  1360.  
  1361. for(let s = ridx; s > 0; s--){
  1362. const chr = textChunk[s];
  1363. if(chr === "<"){
  1364. ridx = s;
  1365. break;
  1366. }
  1367. }
  1368. for(let s = lidx; s > 0; s--){
  1369. const chr = textChunk[s];
  1370. if(chr === "<"){
  1371. lidx = s;
  1372. break;
  1373. }
  1374. }
  1375. const chunk = textChunk.substring(ridx, lidx);
  1376. const container = document.createElement("div");
  1377. container.style.visibility = "collapse";
  1378. container.innerHTML = chunk;
  1379. document.body.appendChild(container);
  1380. const cSpan = container.querySelector("div a span.mbg-nw");
  1381. return cSpan.textContent.trim();
  1382. },
  1383. };
  1384.  
  1385. const SortFunctions = {
  1386. reA : /[^a-zA-Z]/g,
  1387. reN : /[^0-9]/g,
  1388. SortAlphaNum : function(a,b) {
  1389. const aA = a.replace(this.reA, "");
  1390. const bA = b.replace(this.reA, "");
  1391. if(aA === bA) {
  1392. const aN = parseInt(a.replace(this.reN, ""), 10);
  1393. const bN = parseInt(b.replace(this.reN, ""), 10);
  1394. return aN === bN ? 0 : aN > bN ? 1 : -1;
  1395. } else {
  1396. return aA > bA ? 1 : -1;
  1397. }
  1398. },
  1399. SortLowToHigh : function(a,b){
  1400. const n1 = parseFloat(a[1]);
  1401. const n2 = parseFloat(b[1]);
  1402. if(n1 < n2) return -1;
  1403. if(n1 > n2) return 1;
  1404. return 0;
  1405. }
  1406. };
  1407.  
  1408. String.prototype.splice = function( idx, rem, s ) {
  1409. return (this.slice(0,idx) + s + this.slice(idx + Math.abs(rem)));
  1410. };
  1411.  
  1412.  
  1413. ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  1414. //Get the ball rolling...
  1415. if(vendor === "Google Inc."){
  1416. EBayUsabilityEnhancer.Initialize();
  1417. } else if (codeName === "Mozilla" && product === "Gecko"){
  1418. document.onreadystatechange = function(){
  1419. if(document.readyState === "complete")
  1420. EBayUsabilityEnhancer.Initialize();
  1421. };
  1422. }
  1423. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址