diff --git a/css/skinport_styles.css b/css/skinport_styles.css
index 16c0f59..890c53a 100644
--- a/css/skinport_styles.css
+++ b/css/skinport_styles.css
@@ -29,4 +29,5 @@
.betterfloat-sale-tag {
font-size: 12px!important;
margin: 1px;
+ border-radius: 3px;
}
\ No newline at end of file
diff --git a/html/skinport.html b/html/skinport.html
index 899c124..6dbb568 100644
--- a/html/skinport.html
+++ b/html/skinport.html
@@ -27,8 +27,8 @@
diff --git a/src/skinport/content_script.ts b/src/skinport/content_script.ts
index c7755fb..8d13644 100644
--- a/src/skinport/content_script.ts
+++ b/src/skinport/content_script.ts
@@ -20,16 +20,7 @@ async function init() {
await loadMapping();
await loadBuffMapping();
- // //check if url is in supported subpages
- // if (url.endsWith('float.com/')) {
- // await firstLaunch();
- // } else {
- // for (let i = 0; i < supportedSubPages.length; i++) {
- // if (url.includes(supportedSubPages[i])) {
- // await firstLaunch();
- // }
- // }
- // }
+ await firstLaunch();
// mutation observer is only needed once
if (!isObserverActive) {
@@ -41,6 +32,27 @@ async function init() {
}
}
+async function firstLaunch() {
+ let url = window.location.href;
+ console.log('[BetterFloat] First launch on Skinport, url: ', url);
+ if (url.includes('/market')) {
+ let catalogItems = document.querySelectorAll('.CatalogPage-item');
+ for (let item of catalogItems) {
+ await adjustItem(item);
+ }
+ } else if (url.includes('/cart')) {
+ let cartContainer = document.querySelector('.Cart-container');
+ if (cartContainer) {
+ await adjustCart(cartContainer);
+ }
+ } else if (url.includes('/item')) {
+ let itemPage = document.querySelectorAll('.ItemPage');
+ for (let item of itemPage) {
+ await adjustItemPage(item);
+ }
+ }
+}
+
async function applyMutation() {
let observer = new MutationObserver(async (mutations) => {
if (extensionSettings.spBuffPrice) {
@@ -52,8 +64,14 @@ async function applyMutation() {
// console.log('[BetterFloat] Mutation observer triggered, added node:', addedNode);
- if (addedNode.className && addedNode.className.toString().includes('CatalogPage-item')) {
- await adjustItem(addedNode);
+ if (addedNode.className) {
+ if (addedNode.className.toString().includes('CatalogPage-item')) {
+ await adjustItem(addedNode);
+ } else if (addedNode.className.toString().includes('Cart-container')) {
+ await adjustCart(addedNode);
+ } else if (addedNode.className.toString() == 'ItemPage') {
+ await adjustItemPage(addedNode);
+ }
}
// item popout
// if (addedNode.tagName && addedNode.tagName.toLowerCase() == 'item-detail') {
@@ -69,8 +87,73 @@ async function applyMutation() {
observer.observe(document, { childList: true, subtree: true });
}
+async function adjustItemPage(container: Element) {
+ console.log('[BetterFloat] Adjusting item page: ', container);
+ let itemRating = container.querySelector('.ItemPage-rating');
+ if (itemRating) {
+ itemRating.remove();
+ }
+
+ let btnGroup = container.querySelector('.ItemPage-btnGroup');
+ if (!btnGroup) return;
+ let newGroup = document.createElement('div');
+ newGroup.className = btnGroup.className ?? newGroup.className;
+ // if an item is sold, the original links are unclickable, hence we reproduce them
+ let links = container.querySelectorAll('.ItemPage-link');
+ let linkSteam = (Array.from(links).find((el) => el.innerHTML.includes('Steam')) as HTMLAnchorElement | null)?.href;
+ let linkInspect = (Array.from(links).find((el) => el.innerHTML.includes('Inspect')) as HTMLAnchorElement | null)?.href;
+ if (linkInspect) {
+ newGroup.insertAdjacentHTML('beforeend', ``);
+ }
+ if (linkSteam) {
+ newGroup.insertAdjacentHTML('beforeend', ``);
+ }
+
+ let item = getFloatItem(container, itemSelectors.page);
+ console.log('[BetterFloat] Item: ', item);
+ if (!item) return;
+ let { buff_name: buff_name, priceListing, priceOrder } = await getBuffPrice(item);
+ let buffid = await getBuffMapping(buff_name);
+ let buffLink = buffid > 0 ? `https://buff.163.com/goods/${buffid}` : `https://buff.163.com/market/csgo#tab=selling&page_num=1&search=${encodeURIComponent(buff_name)}`;
+ newGroup.insertAdjacentHTML('beforeend', ``);
+ btnGroup.after(newGroup);
+
+ let tooltipLink = container.querySelector('.ItemPage-value .Tooltip-link');
+ const currencySymbol = tooltipLink.textContent?.charAt(0);
+ let suggestedContainer = container.querySelector('.ItemPage-suggested');
+ if (suggestedContainer) {
+ generateBuffContainer(suggestedContainer as HTMLElement, priceListing, priceOrder, currencySymbol ?? '$', true);
+ }
+
+ const difference = item.price - (extensionSettings.spPriceReference == 0 ? priceOrder : priceListing);
+ let priceContainer = container.querySelector('.ItemPage-price');
+ if (priceContainer) {
+ let newContainer = document.createElement('div');
+ let saleTag = document.createElement('span');
+ newContainer.className = 'ItemPage-discount betterfloat-discount-container';
+ newContainer.style.background = `linear-gradient(135deg,#0073d5,${difference == 0 ? 'black' : difference < 0 ? 'green' : '#ce0000'})`;
+ newContainer.style.transform = 'skewX(-15deg)';
+ newContainer.style.borderRadius = '3px';
+ saleTag.style.margin = '5px';
+ saleTag.style.fontWeight = '700';
+ saleTag.textContent = difference == 0 ? `-${currencySymbol}0` : (difference > 0 ? '+' : '-') + currencySymbol + Math.abs(difference).toFixed(2);
+ newContainer.appendChild(saleTag);
+ priceContainer.appendChild(newContainer);
+ }
+}
+
+async function adjustCart(container: Element) {
+ if (extensionSettings.spCheckBoxes) {
+ let checkboxes = container.querySelectorAll('.Checkbox-input');
+ for (let checkbox of checkboxes) {
+ (checkbox as HTMLInputElement).click();
+ await new Promise((r) => setTimeout(r, 50)); // to avoid bot detection
+ }
+ }
+}
+
async function adjustItem(container: Element) {
- const item = getFloatItem(container);
+ const item = getFloatItem(container, itemSelectors.preview);
if (!item) return;
await addBuffPrice(item, container);
// if (extensionSettings.stickerPrices) {
@@ -81,14 +164,39 @@ async function adjustItem(container: Element) {
// }
}
-function getFloatItem(container: Element): Skinport.Listing | null {
- let name = container.querySelector('.ItemPreview-itemName')?.textContent ?? '';
+const itemSelectors = {
+ preview: {
+ name: '.ItemPreview-itemName',
+ title: '.ItemPreview-itemTitle',
+ text: '.ItemPreview-itemText',
+ stickers: '.ItemPreview-stickers',
+ price: '.ItemPreview-price',
+ },
+ page: {
+ name: '.ItemPage-name',
+ title: '.ItemPage-title',
+ text: '.ItemPage-text',
+ stickers: '.ItemPage-stickers',
+ price: '.ItemPage-price',
+ },
+} as const;
+
+type ItemSelectors = (typeof itemSelectors)[keyof typeof itemSelectors];
+
+function getFloatItem(container: Element, selector: ItemSelectors): Skinport.Listing | null {
+ let name = container.querySelector(selector.name)?.textContent ?? '';
if (name == '') {
return null;
}
- let price = Number(container.querySelector('.ItemPreview-price .Tooltip-link')?.innerHTML.substring(1).replace(',', '')) ?? 0;
- let type = container.querySelector('.ItemPreview-itemTitle')?.textContent ?? '';
- let text = container.querySelector('.ItemPreview-itemText')?.innerHTML ?? '';
+ let price =
+ Number(
+ container
+ .querySelector(selector.price + ' .Tooltip-link')
+ ?.innerHTML.substring(1)
+ .replace(',', '')
+ ) ?? 0;
+ let type = container.querySelector(selector.title)?.textContent ?? '';
+ let text = container.querySelector(selector.text)?.innerHTML ?? '';
let style: ItemStyle = '';
if (name.includes('Doppler')) {
@@ -98,7 +206,7 @@ function getFloatItem(container: Element): Skinport.Listing | null {
}
let stickers: { name: string }[] = [];
- let stickersDiv = container.querySelector('.ItemPreview-stickers');
+ let stickersDiv = container.querySelector(selector.stickers);
if (stickersDiv) {
for (let sticker of stickersDiv.children) {
let stickerName = sticker.children[0].getAttribute('alt');
@@ -142,18 +250,16 @@ function getFloatItem(container: Element): Skinport.Listing | null {
};
}
-async function addBuffPrice(item: Skinport.Listing, container: Element): Promise {
- await loadMapping();
- let buff_name = handleSpecialStickerNames(createBuffName(item));
+async function getBuffPrice(item: Skinport.Listing): Promise<{ buff_name: string; priceListing: number; priceOrder: number }> {
let priceMapping = await getPriceMapping();
+ let buff_name = handleSpecialStickerNames(createBuffName(item));
let helperPrice: number | null = null;
if (!priceMapping[buff_name] || !priceMapping[buff_name]['buff163'] || !priceMapping[buff_name]['buff163']['starting_at'] || !priceMapping[buff_name]['buff163']['highest_order']) {
- console.debug(`[BetterFloat] No price mapping found for ${buff_name}: `, container);
+ console.debug(`[BetterFloat] No price mapping found for ${buff_name}`);
helperPrice = await getInventoryHelperPrice(buff_name);
}
- let buff_id = await getBuffMapping(buff_name);
// we cannot use the getItemPrice function here as it does not return the correct price for doppler skins
let priceListing = 0;
let priceOrder = 0;
@@ -188,6 +294,58 @@ async function addBuffPrice(item: Skinport.Listing, container: Element): Promise
priceOrder = priceOrder * currencyRate;
}
+ return { buff_name, priceListing, priceOrder };
+}
+
+async function generateBuffContainer(container: HTMLElement, priceListing: number, priceOrder: number, currencySymbol: string, isItemPage: boolean = false) {
+ container.className += ' betterfloat-buffprice';
+ let buffContainer = document.createElement('div');
+ buffContainer.style.display = 'flex';
+ buffContainer.style.marginTop = '5px';
+ buffContainer.style.alignItems = 'center';
+ if (!isItemPage) {
+ buffContainer.style.justifyContent = 'center';
+ }
+ let buffImage = document.createElement('img');
+ buffImage.setAttribute('src', runtimePublicURL + '/buff_favicon.png');
+ buffImage.setAttribute('style', `height: 20px; margin-right: 5px; ${isItemPage ? 'margin-bottom: 1px;' : ''}`);
+ buffContainer.appendChild(buffImage);
+ let buffPrice = document.createElement('div');
+ buffPrice.setAttribute('class', 'suggested-price betterfloat-buffprice');
+ if (isItemPage) {
+ buffPrice.style.fontSize = '18px';
+ }
+ let tooltipSpan = document.createElement('span');
+ tooltipSpan.setAttribute('class', 'betterfloat-buff-tooltip');
+ tooltipSpan.textContent = 'Bid: Highest buy order price; Ask: Lowest listing price';
+ buffPrice.appendChild(tooltipSpan);
+ let buffPriceBid = document.createElement('span');
+ buffPriceBid.setAttribute('style', 'color: orange;');
+ buffPriceBid.textContent = `Bid ${currencySymbol}${priceOrder.toFixed(2)}`;
+ buffPrice.appendChild(buffPriceBid);
+ let buffPriceDivider = document.createElement('span');
+ buffPriceDivider.setAttribute('style', 'color: gray;margin: 0 3px 0 3px;');
+ buffPriceDivider.textContent = '|';
+ buffPrice.appendChild(buffPriceDivider);
+ let buffPriceAsk = document.createElement('span');
+ buffPriceAsk.setAttribute('style', 'color: greenyellow;');
+ buffPriceAsk.textContent = `Ask ${currencySymbol}${priceListing.toFixed(2)}`;
+ buffPrice.appendChild(buffPriceAsk);
+ buffContainer.appendChild(buffPrice);
+ if (extensionSettings.spSteamPrice || isItemPage) {
+ let divider = document.createElement('div');
+ container.after(buffContainer);
+ container.after(divider);
+ } else {
+ container.replaceWith(buffContainer);
+ }
+}
+
+async function addBuffPrice(item: Skinport.Listing, container: Element): Promise {
+ await loadMapping();
+ let { buff_name, priceListing, priceOrder } = await getBuffPrice(item);
+ let buff_id = await getBuffMapping(buff_name);
+
const presentationDiv = container.querySelector('.ItemPreview-mainAction');
if (presentationDiv) {
let buffLink = document.createElement('a');
@@ -209,41 +367,7 @@ async function addBuffPrice(item: Skinport.Listing, container: Element): Promise
const currencySymbol = tooltipLink.textContent?.charAt(0);
let priceDiv = container.querySelector('.ItemPreview-oldPrice');
if (priceDiv && !container.querySelector('.betterfloat-buffprice')) {
- priceDiv.className += ' betterfloat-buffprice';
- let buffContainer = document.createElement('div');
- buffContainer.style.display = 'flex';
- buffContainer.style.marginTop = '5px';
- buffContainer.style.justifyContent = 'center';
- let buffImage = document.createElement('img');
- buffImage.setAttribute('src', runtimePublicURL + '/buff_favicon.png');
- buffImage.setAttribute('style', 'height: 20px; margin-right: 5px');
- buffContainer.appendChild(buffImage);
- let buffPrice = document.createElement('div');
- buffPrice.setAttribute('class', 'suggested-price betterfloat-buffprice');
- let tooltipSpan = document.createElement('span');
- tooltipSpan.setAttribute('class', 'betterfloat-buff-tooltip');
- tooltipSpan.textContent = 'Bid: Highest buy order price; Ask: Lowest listing price';
- buffPrice.appendChild(tooltipSpan);
- let buffPriceBid = document.createElement('span');
- buffPriceBid.setAttribute('style', 'color: orange;');
- buffPriceBid.textContent = `Bid ${currencySymbol}${priceOrder.toFixed(2)}`;
- buffPrice.appendChild(buffPriceBid);
- let buffPriceDivider = document.createElement('span');
- buffPriceDivider.setAttribute('style', 'color: gray;margin: 0 3px 0 3px;');
- buffPriceDivider.textContent = '|';
- buffPrice.appendChild(buffPriceDivider);
- let buffPriceAsk = document.createElement('span');
- buffPriceAsk.setAttribute('style', 'color: greenyellow;');
- buffPriceAsk.textContent = `Ask ${currencySymbol}${priceListing.toFixed(2)}`;
- buffPrice.appendChild(buffPriceAsk);
- buffContainer.appendChild(buffPrice);
- if (extensionSettings.spSteamPrice) {
- let divider = document.createElement('div');
- priceDiv.after(buffContainer);
- priceDiv.after(divider);
- } else {
- priceDiv.replaceWith(buffContainer);
- }
+ generateBuffContainer(priceDiv as HTMLElement, priceListing, priceOrder, currencySymbol ?? '$');
}
if (extensionSettings.spBuffDifference) {