ebaytotalprice-userscript

Add the total eBay auction price + shipping = total in the auction listing

目前为 2020-12-18 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name ebaytotalprice-userscript
  3. // @namespace https://github.com/subz390
  4. // @version 2.0.0.201218183259
  5. // @description Add the total eBay auction price + shipping = total in the auction listing
  6. // @author SubZ390
  7. // @license MIT
  8. // @run-at document-idle
  9. // @grant none
  10. // @noframes
  11. // @include /^https?://www\.ebay\.co\.uk/
  12. //
  13. //
  14. // ==/UserScript==
  15.  
  16. function realTypeOf(object, lowerCase = true) {
  17. if (typeof object !== 'object') {return typeof object}
  18. if (object === null) {return 'null'}
  19. const internalClass = Object.prototype.toString.call(object).match(/\[object\s(\w+)\]/)[1];
  20. return lowerCase === true ? internalClass.toLowerCase() : internalClass
  21. }
  22.  
  23. function styleInject(style, className = undefined) {
  24. const el = document.createElement('style');
  25. el.appendChild(document.createTextNode(style));
  26. if (className) {el.className = className;}
  27. document.head.appendChild(el);
  28. return el
  29. }
  30.  
  31. function dlh(pattern, regexFlags = 'i') {
  32. try {
  33. if (realTypeOf(pattern) == 'regexp') {
  34. return document.location.href.search(pattern) != -1 ? true : false
  35. }
  36. else if (realTypeOf(pattern) == 'string') {
  37. return document.location.href.search(RegExp(pattern, regexFlags)) != -1 ? true : false
  38. }
  39. else {
  40. return null
  41. }
  42. }
  43. catch (error) {
  44. console.error(error);
  45. }
  46. }
  47.  
  48. function findMatch(string, regex, index) {
  49. if (string === null) return null
  50. index = index || 1;
  51. let m = string.match(regex);
  52. return (m) ? (index=='all' ? m : (m[index] ? m[index] : m[0])) : null
  53. }
  54.  
  55. function getNode(node, debug = undefined, scope = document) {
  56. try {
  57. if (typeof node == 'string') {
  58. if (typeof scope == 'string') {
  59. const tempScope = document.querySelector(scope);
  60. if (tempScope == null) {
  61. return null
  62. }
  63. scope = tempScope;
  64. }
  65. const element = scope.querySelector(node);
  66. return element
  67. }
  68. return node
  69. }
  70. catch (error) {
  71. console.error(error);
  72. }
  73. }
  74.  
  75. function setDefault(paramOptions, paramDefault = undefined, paramAction = undefined, debugging = undefined) {
  76. let globalObject;
  77. let globalOptions;
  78. let globalAction;
  79. let globalDefault;
  80. if (realTypeOf(paramOptions) === 'object') {
  81. const getProp = (object, array) => {
  82. const name = array.find(name => object.hasOwnProperty(name));
  83. if (typeof name !== 'undefined') {
  84. return object[name]
  85. }
  86. else {
  87. return undefined
  88. }
  89. };
  90. globalOptions = getProp(paramOptions, ['option', 'options', 'property', 'props', 'properties']);
  91. globalObject = getProp(paramOptions, ['object']);
  92. globalDefault = getProp(paramOptions, ['default']);
  93. globalAction = getProp(paramOptions, ['action', 'callback']);
  94. }
  95. else {
  96. globalOptions = paramOptions;
  97. globalDefault = paramDefault;
  98. globalAction = paramAction;
  99. }
  100. if (globalOptions === undefined) {
  101. return globalDefault
  102. }
  103. if (typeof globalAction === 'string' && globalAction === 'set') {
  104. return globalDefault
  105. }
  106. function doAction(option, action = undefined, object = {}) {
  107. if (typeof action === 'function') {
  108. return action(option, object)
  109. }
  110. else {
  111. return option
  112. }
  113. }
  114. if (realTypeOf(globalObject) === 'object') {
  115. if (realTypeOf(globalOptions) === 'array') {
  116. for (let i=0; i < globalOptions.length; i++) {
  117. if (globalObject.hasOwnProperty(globalOptions[i])) {
  118. const result = doAction(globalObject[globalOptions[i]], globalAction, globalObject);
  119. if (result !== undefined) {
  120. return result
  121. }
  122. }
  123. }
  124. }
  125. else if (typeof globalOptions === 'string' && globalObject.hasOwnProperty(globalOptions)) {
  126. const result = doAction(globalObject[globalOptions], globalAction, globalObject);
  127. if (result !== undefined) {
  128. return result
  129. }
  130. }
  131. }
  132. else if (realTypeOf(globalOptions) === 'array') {
  133. for (let i=0; i < globalOptions.length; i++) {
  134. if (globalOptions[i] !== undefined) {
  135. const result = doAction(globalOptions[i], globalAction, globalObject);
  136. if (result !== undefined) {
  137. return result
  138. }
  139. }
  140. }
  141. }
  142. else {
  143. const result = doAction(globalOptions, globalAction, globalObject);
  144. if (result !== undefined) {
  145. return result
  146. }
  147. }
  148. return globalDefault
  149. }
  150.  
  151. function qs({selector = null, scope = document, array = false, all = false, contains = null, unittest = false} = {}) {
  152. try {
  153. if (selector === null) {
  154. return null
  155. }
  156. if (scope !== document) {
  157. scope = getNode(scope);
  158. if (scope === null) {
  159. return null
  160. }
  161. }
  162. if (unittest === 'scope') {return scope}
  163. if (all === true) {
  164. const qsNodeList = scope.querySelectorAll(selector);
  165. if (qsNodeList.length === 0) {return null}
  166. if (array === true) {
  167. if (contains !== null) {
  168. let tempArray = [];
  169. qsNodeList.forEach((element) => {
  170. console.log('element.textContent', element, element.textContent);
  171. if (element.textContent.search(contains) !== -1) {
  172. tempArray.push(element);
  173. }
  174. });
  175. if (tempArray.length === 0) {return null}
  176. else {return tempArray}
  177. }
  178. return Array.from(qsNodeList)
  179. }
  180. else {
  181. if (contains !== null) {
  182. for (let index = 0; index < qsNodeList.length; index++) {
  183. if (qsNodeList[index].textContent.search(contains) !== -1) {return qsNodeList[index]}
  184. }
  185. return null
  186. }
  187. return qsNodeList
  188. }
  189. }
  190. else {
  191. const qsHTMLElement = scope.querySelector(selector);
  192. if (qsHTMLElement === null) {return null}
  193. if (typeof contains === 'string' || contains instanceof RegExp) {
  194. if (qsHTMLElement.textContent.search(contains) === -1) {return null}
  195. }
  196. if (array === true) {return [qsHTMLElement]}
  197. else {return qsHTMLElement}
  198. }
  199. }
  200. catch (error) {
  201. console.error(error);
  202. }
  203. }
  204.  
  205. function sprintf(...args) {
  206. if (Object.keys(args).length == 0) {
  207. return null
  208. }
  209. if (args[0] === '') {
  210. return null
  211. }
  212. const options = setDefault({
  213. options: [realTypeOf(args[0]) == 'object' ? args[0] : undefined],
  214. default: {regex: /{([^{}]+)}/g, template: args[0]},
  215. });
  216. if (realTypeOf(args[1]) == 'object') {
  217. return options.template.replace(options.regex, function(match, n) {
  218. for (let k = 1; args[k]; k++) {
  219. if (args[k][n]) {
  220. if (typeof args[k][n] == 'function') {return args[k][n]().toString()}
  221. return args[k][n]
  222. }
  223. }
  224. return match
  225. })
  226. }
  227. else {
  228. return options.template.replace(options.regex, function(match, n) {
  229. return args[n] || match
  230. })
  231. }
  232. }
  233.  
  234. var stylesheet = ".total-price{background:#bf0;color:#111!important;outline:2px solid;padding:1px 4px;margin-left:5px;font-size:20px!important;font-weight:400!important;}.s-item__detail{overflow:visible!important;}";
  235.  
  236. styleInject(stylesheet);
  237. function processListGallery({listItemsSelector, itemPriceElementSelector, itemPriceElementTemplate = null, itemShippingElementSelector, itemShippingElementTemplate = null}) {
  238. const listItems = qs({selector: listItemsSelector, all: true, array: true});
  239. if (listItems) {
  240. for (let i=0; listItems[i]; i++) {
  241. const itemPriceElement = qs({selector: itemPriceElementSelector, scope: listItems[i]});
  242. const itemShippingElement = qs({selector: itemShippingElementSelector, scope: listItems[i], contains: /\d/});
  243. if (itemPriceElement && itemShippingElement) {
  244. const priceCurrencySymbol = findMatch(itemPriceElement.textContent.trim(), /(\$|£|EUR)/);
  245. const shippingCurrencySymbol = findMatch(itemShippingElement.textContent.trim(), /(\$|£|EUR)/);
  246. if (shippingCurrencySymbol && (shippingCurrencySymbol === priceCurrencySymbol)) {
  247. const totalPrice = parseFloat(findMatch(itemPriceElement.textContent.trim(), /(\d+[\.,]\d+)/).replace(',', '.')) +
  248. parseFloat(findMatch(itemShippingElement.textContent.trim(), /(\d+[\.,]\d+)/).replace(',', '.'));
  249. const HTML = sprintf(
  250. itemShippingElementTemplate || itemPriceElementTemplate, {
  251. itemPrice: itemPriceElement.textContent.trim(),
  252. itemShippingAmount: itemShippingElement.textContent.trim(),
  253. shippingCurrencySymbol: shippingCurrencySymbol,
  254. totalPrice: totalPrice.toFixed(2)});
  255. if (itemPriceElementTemplate) {
  256. itemPriceElement.insertAdjacentHTML('afterend', HTML);
  257. }
  258. else {
  259. itemShippingElement.innerHTML = HTML;
  260. }
  261. }
  262. }
  263. }
  264. }
  265. }
  266. function processItemListing({listItemsSelector, itemPriceElementSelector, convertPriceElementSelector, itemPriceElementTemplate = null, itemShippingElementSelector, convertShippingElementSelector, itemShippingElementTemplate = null}) {
  267. const content = qs({selector: listItemsSelector});
  268. if (content) {
  269. const priceElement = qs({selector: convertPriceElementSelector, scope: content}) || qs({selector: itemPriceElementSelector, scope: content});
  270. const shippingElement = qs({selector: convertShippingElementSelector, scope: content, contains: /\d/}) || qs({selector: itemShippingElementSelector, scope: content, contains: /\d/});
  271. if (priceElement && shippingElement) {
  272. const priceCurrencySymbol = findMatch(priceElement.textContent.trim(), /([^\d ]+) ?\d+\.\d+/);
  273. const shippingCurrencySymbol = findMatch(shippingElement.textContent.trim(), /([^\d ]+) ?\d+\.\d+/);
  274. if (shippingCurrencySymbol && (shippingCurrencySymbol === priceCurrencySymbol)) {
  275. const totalPrice = parseFloat(findMatch(priceElement.textContent.trim(), /(\d+\.\d+)/)) + parseFloat(findMatch(shippingElement.textContent.trim(), /(\d+\.\d+)/));
  276. const HTML = sprintf(
  277. itemShippingElementTemplate || itemPriceElementTemplate, {
  278. itemPrice: priceElement.textContent.trim(),
  279. itemShippingAmount: shippingElement.textContent.trim(),
  280. shippingCurrencySymbol: shippingCurrencySymbol,
  281. totalPrice: totalPrice.toFixed(2)});
  282. if (itemPriceElementTemplate) {
  283. priceElement.insertAdjacentHTML('afterend', HTML);
  284. }
  285. else {
  286. shippingElement.innerHTML = HTML;
  287. }
  288. }
  289. }
  290. }
  291. }
  292. try {
  293. const itemPriceElementTemplate = '<span class="total-price">{shippingCurrencySymbol}{totalPrice}</span>';
  294. if (dlh('\\/(b|str)\\/')) {
  295. processListGallery({
  296. listItemsSelector: '.s-item',
  297. itemPriceElementSelector: '.s-item__price',
  298. itemShippingElementSelector: '.s-item__shipping',
  299. itemPriceElementTemplate: itemPriceElementTemplate
  300. });
  301. }
  302. else if (dlh('\\/sch\\/')) {
  303. processListGallery({
  304. listItemsSelector: '#mainContent li',
  305. itemPriceElementSelector: '.lvprice span',
  306. itemShippingElementSelector: '.lvshipping span.fee',
  307. itemPriceElementTemplate: itemPriceElementTemplate
  308. });
  309. }
  310. else if (dlh('\\/itm\\/')) {
  311. processItemListing({
  312. listItemsSelector: '#mainContent',
  313. itemPriceElementSelector: '#prcIsum_bidPrice',
  314. convertPriceElementSelector: '#prcIsumConv',
  315. itemShippingElementSelector: '#fshippingCost',
  316. convertShippingElementSelector: '#convetedPriceId',
  317. itemPriceElementTemplate: itemPriceElementTemplate
  318. });
  319. }
  320. }
  321. catch (error) {console.error(error);}