// ==UserScript==
// @name Amazon Vine Explorer
// @namespace http://tampermonkey.net/
// @version 0.10.9.0.1
// @updateURL https://raw.githubusercontent.com/Amazon-Vine-Explorer/AmazonVineExplorer/main/VineExplorer.user.js
// @downloadURL https://raw.githubusercontent.com/Amazon-Vine-Explorer/AmazonVineExplorer/main/VineExplorer.user.js
// @description Better View, Search and Explore for Amazon Vine Products - Vine Voices Edition
// @author MarkusSR1984, Christof121
// @match *://www.amazon.de/*
// @match *://www.amazon.com/*
// @match *://www.amazon.co.uk/*
// @license MIT
// @icon https://raw.githubusercontent.com/Amazon-Vine-Explorer/AmazonVineExplorer/main/vine_logo.png
// @run-at document-start
// @grant GM_getValue
// @grant GM_setValue
// @grant GM.getValue
// @grant GM.setValue
// @grant GM.xmlHttpRequest
// @grant unsafeWindow
// @require https://raw.githubusercontent.com/Amazon-Vine-Explorer/AmazonVineExplorer/main/globals.js
// @require https://raw.githubusercontent.com/Amazon-Vine-Explorer/AmazonVineExplorer/main/class_db_handler.js
// @require https://raw.githubusercontent.com/Amazon-Vine-Explorer/AmazonVineExplorer/main/class_product.js
// External Source
// @require https://raw.githubusercontent.com/eligrey/FileSaver.js/v2.0.4/src/FileSaver.js
// @require https://raw.githubusercontent.com/Christof121/VineFetchFix/main/fetchfix.js
// ==/UserScript==
/*
Versioning:
a.b.c[.d]
a => Hauptversion(Major), ändert sich nur bei breaking oder anderen gravirenden änderungen. Solle In diesem Fall also die 1 nie überschreiten.
b => Feature(Minor), ändert sich nur wenn neue Features hinzukommen oder gößere umstellungen im Hintergrund passiert sind
c => Patch, kleinere Änderungen oder "größere" Bugfixes
d => Micro(OPTIONAL), kleine Bugfixes die nur wenige Zeilen Code beinhalten. Wird normalerweise nicht an die Versionnummer angehängt und nur in ausnahmefällen verwendet. Wie z.B. 0.6.4.1 - Das war nur eine Fehlerhafte Variablendeklaration. musste aber public gehen weil es ein Breaking Bug war
Sammlung der Ideen:
- Pageination nach oben schieben || Kopieren
- Tooltipp mit der langen Beschreibung auf der kurzen
- Bestellte Produkte mit Tag versehen ?
- Automatisches Bestellen via Prioliste ?!?
Todo:
- Reload der Neue Produkte Seite nach einem Click auf "Alle als gesehen Markieren"
- Originale Pagination auf den eigenen Seiten verstecken
*/
'use strict';
console.log(`Init Vine Voices Explorer ${AVE_VERSION}`);
/**
* On witch page are we atm ? PAGETYPE
* @type {PAGETYPE}
*/
let currentMainPage;
loadSettings();
fastStyleChanges();
let searchInputTimeout;
let backGroundScanTimeout;
let TimeouteScrollTilesBufferArray = [];
let BackGroundScanIsRunning = false;
// Make some things accessable from console
unsafeWindow.ave = {
classes: [
DB_HANDLER = DB_HANDLER
],
config: SETTINGS,
event: ave_eventhandler,
};
const database = new DB_HANDLER(DATABASE_NAME, DATABASE_OBJECT_STORE_NAME, DATABASE_VERSION, (res, err) => {
if (err) {
console.error(`Somithing was going wrong while init database :'(`);
return;
} else {
let _execLock = false;
console.log('Lets Check where we are....');
if (SITE_IS_VINE){
console.log('We are on Amazon Vine'); // We are on the amazon vine site
if(SETTINGS.DarkMode){
waitForHtmlElmement('body', () => {
injectDarkMode();
})
}
const urlParams = new URLSearchParams(window.location.search);
const aveData = urlParams.get('vine-data');
let aveShareData = localStorage.getItem('ave-share-details');
if(aveData || aveShareData){
let _data = aveShareData ? JSON.parse(aveShareData) : (aveData ? JSON.parse(aveData) : null);
waitForHtmlElmement('body', () => {
let aveShareElementTmp = document.createElement('div');
aveShareElementTmp.style.display = "none";
aveShareElementTmp.innerHTML = `
Weitere Details
`;
document.body.appendChild(aveShareElementTmp);
// Warte auf das nächste Ereigniszyklus, um sicherzustellen, dass das Element vollständig gerendert wurde
setTimeout(() => {
aveShareElementTmp.querySelector('input').click();
setTimeout(() => {
//aveShareElementTmp.remove();
localStorage.removeItem('ave-share-details');
}, 200);
}, 500);
})
//https://www.amazon.de/vine/api/recommendations/A1PA6795UKMFR9%23B0CW9Q5N53%23vine.enrollment.41aad59f-9ff3-49c4-a3e1-d3f3c43c2536/item/B0CW9Q5N53?imageSize=180
}
addAveSettingsTab();
addAVESettingsMenu();
waitForHtmlElmement('.vvp-details-btn', () => {
if (_execLock) return;
_execLock = true;
addBranding();
detectCurrentPageType();
let _tileCount = 0;
const _initialWaitForAllTiles = setInterval(() => {
const _count = document.getElementsByClassName('vvp-details-btn').length // Buttons take a bit more time as tiles
if (_count > _tileCount) {
_tileCount = _count;
} else {
clearInterval(_initialWaitForAllTiles);
init(true);
}
}, 100);
});
waitForHtmlElmement('.vvp-no-offers-msg', () => { // Empty Page ?!?!
if (_execLock) return;
_execLock = true;
if(SETTINGS.DarkMode){
waitForHtmlElmement('body', () => {
injectDarkMode();
})
}
addBranding();
init(false);
});
} else if (SITE_IS_SHOPPING) {
console.log('We are on Amazon Shopping'); // We are on normal amazon shopping - maybe i hve forgotten any other site then we have to add it as not here
_execLock = true;
waitForHtmlElmement('body', () => {
addBranding(); // For now, olny show that the script is active
});
useEnrollmentData() // Function to use enrollment data from URL
function useEnrollmentData() {
const urlParams = new URLSearchParams(window.location.search);
const aveData = urlParams.get('vine-data');
if (aveData) {
const enrollmentData = JSON.parse(decodeURIComponent(aveData));
//Redirect to Vine and Open Item
localStorage.setItem('ave-share-details', JSON.stringify(enrollmentData));
window.open(`${window.location.origin}/vine/vine-items`, '_blank');
}
}
}
}
});
unsafeWindow.ave.database = database;
let oldCountOfNewItems = 0;
let showDbUpdateLogoTimeout = null;
let showDbUpdateLogoIcon = null;
ave_eventhandler.on('ave-database-changed', () => {
console.warn('EVENT - Database has new Data for us! we should look what has changed');
updateNewProductsBtn();
if (showDbUpdateLogoTimeout) clearTimeout(showDbUpdateLogoTimeout);
if (!showDbUpdateLogoIcon) showDbUpdateLogoIcon = addDBLoadingSymbol();
showDbUpdateLogoTimeout = setTimeout(() => {
if (showDbUpdateLogoIcon) showDbUpdateLogoIcon.remove();
showDbUpdateLogoTimeout = null;
showDbUpdateLogoIcon = null;
}, 5000);
})
window.onscroll = () => { // ONSCROLL Event handler
stickElementToTopScrollEVhandler('ave-btn-allseen', '5px');
stickElementToTopScrollEVhandler('ave-btn-db-allseen', '40px');
stickElementToTopScrollEVhandler('ave-btn-backtotop', '75px');
if (currentMainPage == PAGETYPE.ALL) handleInfiniteScroll();
};
let blockHandleInfiniteScroll = false;
let infiniteScrollLastPreloadedPage = 1;
let infiniteScrollMaxPreloadPage = 125; // Hardcoded for scrolltest, must lated get extracted from Pagination
let inifiniteScrollBlockAppend = false;
let infiniteScrollTilesBufferArray = [];
function injectDarkMode() {
const _darkModeIgnoreBackgroundColor = `
i,
span.a-declarative *,
#navbar-main *,
#ave-btn-allseen *,
#ave-btn-db-allseen *,
#ave-btn-backtotop *,
#ave-branding-text,
#ave-brandig-text,
.animated-progress *,
.a-switch.a-declarative,
.vvp-reviews-table--actions-col *,
.a-tab-heading,
.a-tab-heading a,
.ave-favorite-star,
.vvp-item-tile,
.vvp-item-tile-content,
.vvp-item-tile-content *,
.a-popover-lgtbox,
.a-modal-scroller.a-declarative,
#ave-btn-favorites *,
#ave-btn-list-new *,
.ave-settings-label-switch *,
.a-last *
`
const _darkModeIgnoreColor = `
span.a-declarative *,
#navbar-main *,
#ave-btn-allseen *,
#ave-btn-db-allseen *,
#ave-btn-backtotop *,
#ave-branding-text,
#ave-brandig-text,
.a-switch.a-declarative,
.vvp-reviews-table--actions-col *,
.vvp-details-btn *,
.vvp-header-link *,
.a-link-normal,
#ave-btn-favorites *,
#ave-btn-list-new *,
.a-last *
`
const _darkModeIgnoreIcons = `
#vvp-feedback-star-rating
`
const darkCSS = `
:root{
--primary-color: ${SETTINGS.DarkModeColor};
--secondary-color: ${SETTINGS.DarkModeBackgroundColor};
}
.ave-color, .ave-color *:not(${_darkModeIgnoreColor}){
color: var(--primary-color) !important;
}
.ave-background-color, .ave-background-color *:not(${_darkModeIgnoreBackgroundColor}){
background-color: var(--secondary-color) !important;
}
.a-expander-content-fade,
.a-popover-footer::before,
.a-popover-wrapper::after
{
background: none !important;
}
i:not(${_darkModeIgnoreIcons}){
background-color: transparent !important;
filter: invert(1) !important;
}
`
// Erstelle ein neues Style-Element
var styleElement = document.createElement('style');
styleElement.type = 'text/css';
// Füge die CSS-Variable und den Wert am Anfang des Style-Elements hinzu
styleElement.textContent = darkCSS;
// Füge das Style-Element am Anfang des
-Tags hinzu
document.head.insertBefore(styleElement, document.head.firstChild);
document.body.classList.add('ave-color','ave-background-color');
}
function handleInfiniteScroll() {
console.log('Called handleInfiniteScroll()');
if (!inifiniteScrollBlockAppend) {
inifiniteScrollBlockAppend = true;
// setTimeout(async ()=> {},10);
appendInfiniteScrollTiles(()=>{inifiniteScrollBlockAppend = false;})
}
if (SETTINGS.EnableInfiniteScrollLiveQuerry) {
if (blockHandleInfiniteScroll) return;
blockHandleInfiniteScroll = true;
const _maxScrollHeight = Math.max(document.body.scrollHeight - window.innerHeight, document.documentElement.scrollHeight - window.innerHeight);
console.log(`handleInfiniteScroll(): _maxScrollHeight: ${_maxScrollHeight} window.scrollY+inner: ${window.scrollY + window.innerHeight}`);
if (_maxScrollHeight > (window.scrollY + (window.innerHeight * 2))){
blockHandleInfiniteScroll = false;
return;
} else if (infiniteScrollTilesBufferArray.length < 1000 && infiniteScrollLastPreloadedPage < infiniteScrollMaxPreloadPage) {
const _baseUrl = (/(http[s]{0,1}\:\/\/[w]{0,3}.amazon.[a-z]{1,}.{0,1}[a-z]{0,}\/vine\/vine-items)/.exec(window.location.href))[1];
infiniteScrollLastPreloadedPage++;
getTilesFromURL(`${_baseUrl}?queue=encore&pn=&cn=&page=${infiniteScrollLastPreloadedPage}`, (tiles) =>{
infiniteScrollTilesBufferArray = infiniteScrollTilesBufferArray.concat(tiles);
blockHandleInfiniteScroll = false;
if (infiniteScrollTilesBufferArray.length < 500) handleInfiniteScroll();
});
} else {
blockHandleInfiniteScroll = false;
}
}
}
function getUrlParameter(name) {
const _queryString = window.location.search;
const _urlParams = new URLSearchParams(_queryString);
return _urlParams.get(name);
}
function detectCurrentPageType(){
if (/http[s]{0,1}\:\/\/[w]{0,3}.amazon.[a-z]{1,}.{0,1}[a-z]{0,}\/vine\/vine-items$/.test(window.location.href)) {
currentMainPage = PAGETYPE.ORIGINAL_LAST_CHANCE;
} else if (getUrlParameter('queue') == 'last_chance') {
currentMainPage = PAGETYPE.ORIGINAL_LAST_CHANCE;
} else if (getUrlParameter('queue') == 'potluck') {
currentMainPage = PAGETYPE.OROGINAL_POTLUCK;
} else if (getUrlParameter('queue') == 'encore') {
currentMainPage = PAGETYPE.ORIGINAL_SELLER;
}
// alert(`currentMainPage is: ${currentMainPage}`);
// getUrlParameter('ave-subpage');
}
async function parseTileData(tile) {
return new Promise((resolve, reject) => {
if (SETTINGS.DebugLevel > 5) console.log(`Called parseTileData(`, tile, ')');
const _id = tile.getAttribute('data-recommendation-id');
database.get(_id).then((_ret) => {
if (_ret) {
_ret.gotFromDB = true;
_ret.ts_lastSeen = unixTimeStamp();
if (SETTINGS.DebugLevel > 14) console.log(`parseTileData(): got DB Entry`);
database.update(_ret);
resolve(_ret);
} else {
//We have to wait for a lot of Stuff
waitForHtmlElmement('.vvp-item-tile-content',async () => {
const _div_vpp_item_tile_content = tile.getElementsByClassName('vvp-item-tile-content')[0];
if (SETTINGS.DebugLevel > 14) console.log(`parseTileData(): wait 1`);
waitForHtmlElmement('img', async () => {
const _div_vpp_item_tile_content_img = _div_vpp_item_tile_content.getElementsByTagName('img')[0];
if (SETTINGS.DebugLevel > 14) console.log(`parseTileData(): wait 2`);
waitForHtmlElmement('.vvp-item-product-title-container', async () => {
const _div_vvp_item_product_title_container = _div_vpp_item_tile_content.getElementsByClassName('vvp-item-product-title-container')[0];
if (SETTINGS.DebugLevel > 14) console.log(`parseTileData(): wait 3`);
waitForHtmlElmement('a', async () => {
const _div_vvp_item_product_title_container_a = _div_vvp_item_product_title_container.getElementsByTagName('a')[0];
if (SETTINGS.DebugLevel > 14) console.log(`parseTileData(): wait 4`);
waitForHtmlElmement('.a-button-inner', async () => {
const _div_vpp_item_tile_content_button_inner = _div_vpp_item_tile_content.getElementsByClassName('a-button-inner')[0];
if (SETTINGS.DebugLevel > 14) console.log(`parseTileData(): wait 5`);
waitForHtmlElmement('input', async () => {
const _div_vpp_item_tile_content_button_inner_input = _div_vpp_item_tile_content_button_inner.getElementsByTagName('input')[0];
if (SETTINGS.DebugLevel > 14) console.log(`parseTileData(): wait 6`);
const _newProduct = new Product(_id);
_newProduct.data_recommendation_id = _id;
_newProduct.data_img_url = tile.getAttribute('data-img-url');
_newProduct.data_img_alt = _div_vpp_item_tile_content_img.getAttribute('alt') || "";
_newProduct.link = _div_vvp_item_product_title_container_a.getAttribute('href');
_newProduct.description_full = _div_vvp_item_product_title_container_a.getElementsByClassName('a-truncate-full')[0].textContent;
_newProduct.data_asin = _div_vpp_item_tile_content_button_inner_input.getAttribute('data-asin');
_newProduct.data_recommendation_type = _div_vpp_item_tile_content_button_inner_input.getAttribute('data-recommendation-type');
_newProduct.data_asin_is_parent = (_div_vpp_item_tile_content_button_inner_input.getAttribute('data-is-parent-asin') == 'true');
_newProduct.description_short = _div_vvp_item_product_title_container_a.getElementsByClassName('a-truncate-cut')[0].textContent;
if (_newProduct.description_short == '') {
if (SETTINGS.DebugLevel > 14) console.log(`parseTileData(): we don´t have a shot description`);
let _timeLoopCounter = 0;
const _maxLoops = Math.round(SETTINGS.FetchRetryMaxTime / SETTINGS.FetchRetryTime);
const _halfdelay = (SETTINGS.FetchRetryTime / 2)
function timeLoop() {
if (_timeLoopCounter++ < _maxLoops){
setTimeout(() => {
const _short = _div_vvp_item_product_title_container_a.getElementsByClassName('a-truncate-cut')[0].textContent;
if (_short != ""){
_newProduct.description_short = _short;
resolve(_newProduct);
} else {
timeLoop();
}
}, _halfdelay + Math.round(Math.random() * _halfdelay * 2));
} else {
_newProduct.description_short = `${_newProduct.description_full.substr(0,50)}...`;
_newProduct.generated_short = true;
resolve(_newProduct);
}
}
timeLoop();
} else {
if (SETTINGS.DebugLevel > 14) console.log(`parseTileData(): END`);
resolve(_newProduct);
}
// if (SETTINGS.DebugLevel > 10) console.log(`parseTileData(${tile}) RETURNS :: ${JSON.stringify(_newProduct, null, 4)}`);
}, _div_vpp_item_tile_content_button_inner)
}, _div_vpp_item_tile_content)
}, _div_vvp_item_product_title_container)
}, _div_vpp_item_tile_content);
}, _div_vpp_item_tile_content);
}, tile)
}
});
})
}
function reloadPageWithSubpageTarget(target) {
if (window.location.href.includes('?')) {
window.location.href = window.location.href + `&ave-subpage=${target}`;
} else {
window.location.href = window.location.href + `?ave-subpage=${target}`;
}
}
function addLeftSideButtons(forceClean) {
const _nodesContainer = document.getElementById('vvp-browse-nodes-container');
if (forceClean) _nodesContainer.innerHTML = '';
_nodesContainer.appendChild(document.createElement('p')); // A bit of Space above our Buttons
const _setAllSeenBtn = createButton('Aktuelle Seite als gesehen markieren','ave-btn-allseen', `width: 240px; background-color: ${SETTINGS.BtnColorMarkCurrSiteAsSeen};`, () => {
if (SETTINGS.DebugLevel > 10) console.log('Clicked All Seen Button');
markAllCurrentSiteProductsAsSeen();
});
const _setAllSeenDBBtn = createButton('Alle als gesehen markieren','ave-btn-db-allseen', `left: 0; width: 240px; background-color: ${SETTINGS.BtnColorMarkAllAsSeen};`, () => {
if (SETTINGS.DebugLevel > 10) console.log('Clicked All Seen Button');
setTimeout(() => {
database.getAll().then((prodsArr) => {
const _prodsArryLength = prodsArr.length;
for (let i = 0; i < _prodsArryLength; i++) {
const _currProd = prodsArr[i];
_currProd.isNew = false;
database.update(_currProd);
}
})
}, 30);
});
const _backToTopBtn = createButton('Zum Seitenanfang','ave-btn-backtotop', `width: 240px; background-color: ${SETTINGS.BtnColorBackToTop};`, () => {
if (SETTINGS.DebugLevel > 10) console.log('Clicked back to Top Button');
window.scrollTo(0, 0);
});
_nodesContainer.appendChild(_setAllSeenBtn);
_nodesContainer.appendChild(_setAllSeenDBBtn);
_nodesContainer.appendChild(_backToTopBtn);
// const _clearDBBtn = createButton('Datenbank Bereinigen', 'background-color: orange;', () => {
// if (SETTINGS.DebugLevel > 10) console.log('Clicked clear DB Button');
// cleanUpDatabase();
// });
// _nodesContainer.appendChild(_clearDBBtn);
}
function markAllCurrentSiteProductsAsSeen(cb = () => {}) {
const _tiles = document.getElementsByClassName('vvp-item-tile');
const _tilesLength = _tiles.length;
let _returned = 0;
for (let i = 0; i < _tilesLength; i++) {
const _tile = _tiles[i];
const _id = _tile.getAttribute('data-recommendation-id');
database.get(_id).then((prod) => {
prod.isNew = false;
database.update(prod).then( () => {
updateTileStyle(prod);
_returned++;
if (_returned == _tilesLength) cb();
})
})
}
}
function markAllCurrentDatabaseProductsAsSeen(cb = () => {}) {
if (SETTINGS.DebugLevel > 10) console.log('Called markAllCurrentDatabaseProductsAsSeen()');
database.getNewEntries().then((prods) => {
const _prodsLength = prods.length;
let _returned = 0;
if (SETTINGS.DebugLevel > 10) console.log(`markAllCurrentDatabaseProductsAsSeen() - Got ${_prodsLength} Products with Tag isNew`);
if (_prodsLength == 0) {
cb(true);
return;
}
for (let i = 0; i < _prodsLength; i++) {
const _currProd = prods[i];
_currProd.isNew = false;
database.update(_currProd, ()=> {
if (SETTINGS.DebugLevel > 10) console.log(`markAllCurrentDatabaseProductsAsSeen() - Updated ${_currProd.id}`);
_returned++
if (_returned == _prodsLength) cb(true);
})
}
});
}
function createButton(text, id, style, clickHandler){
const _btnSpan = document.createElement('span');
_btnSpan.setAttribute('id', id);
_btnSpan.setAttribute('class', 'a-button a-button-normal a-button-toggle');
_btnSpan.setAttribute('aria-checked', 'true');
_btnSpan.style.marginLeft = '0';
_btnSpan.style.marginTop = '5px';
_btnSpan.innerHTML = `
${text}
`;
_btnSpan.addEventListener('click', (ev) => {
if (clickHandler) {
clickHandler(ev);
} else {
alert('\r\nHier gibt es nix zu sehen.\r\nZumindest noch nicht :P');
}
});
return _btnSpan;
}
async function createTileFromProduct(product, btnID, cb) {
if (!product && SETTINGS.DebugLevel > 10) console.error(`createTileFromProduct got no valid product element`);
return new Promise((resolve, reject) => {
const _btnAutoID = btnID || Math.round(Math.random() * 10000);
const _tile = document.createElement('div');
_tile.setAttribute('class', 'vvp-item-tile');
_tile.setAttribute('data-recommendation-id', product.data_recommendation_id);
_tile.setAttribute('data-img-url', product.data_img_url);
_tile.setAttribute('style', (product.notSeenCounter > 0) ? SETTINGS.CssProductRemovalTag : (product.isFav) ? SETTINGS.CssProductNewTag : (product.isNew) ? SETTINGS.CssProductNewTag : SETTINGS.CssProductDefault);
_tile.innerHTML =`
`;
_tile.prepend(createFavStarElement(product, btnID));
_tile.prepend(createShareElement(product, btnID));
waitForHtmlElmement('.vvp-item-product-title-container', (_elem) => {
insertHtmlElementAfter(_elem, createTaxInfoElement(product, btnID));
}, _tile)
// insertHtmlElementAfter((_tile.getElementsByClassName('vvp-item-product-title-container')[0]), createTaxInfoElement(product, btnID));
if (cb) cb(_tile);
resolve(_tile);
})
}
function createFavStarElement(prod, index = Math.round(Math.random()* 10000)) {
const _favElement = document.createElement('div');
_favElement.setAttribute("id", `p-fav-${index || Math.round(Math.random() * 5000)}`);
_favElement.classList.add('ave-favorite-star');
_favElement.style.cssText = SETTINGS.CssProductFavStar();
_favElement.textContent = '★';
if (prod.isFav) _favElement.style.color = SETTINGS.FavStarColorChecked; // SETTINGS.FavStarColorChecked = Gelb;
return _favElement;
}
function createShareElement(prod, index = Math.round(Math.random()* 10000)) {
const _shareElement = document.createElement('div');
_shareElement.setAttribute("id", `ave-p-share-${index || Math.round(Math.random() * 5000)}`);
_shareElement.classList.add('ave-share');
_shareElement.textContent = '🔗';
_shareElement.style.float = 'left';
_shareElement.style.display = 'flex';
_shareElement.style.margin = '0';
_shareElement.style.cursor = 'pointer';
return _shareElement;
}
let run = 0;
function shareEventHandlerClick(event, _data){
if(_data.recommendation_id){
console.log("[AVE]",_data);
const newUrl = `${window.location.origin}/dp/${_data.asin}?vine-data=${encodeURIComponent(JSON.stringify({
asin: _data.asin,
isParentAsin: _data.parent_asin,
recommendationId: _data.recommendation_id,
tax: _data.tax,
}))}`;
const urlParams = new URLSearchParams(window.location.search);
let queueParam = currentMainPage;
//let queueParam = urlParams.get('queue');
let pageParam = urlParams.get('page');
if(pageParam == null){pageParam = 1}
let page = ""
switch(queueParam){
case PAGETYPE.OROGINAL_POTLUCK:
queueParam = "Mein FSE"
page = `Seite: ${pageParam}`
break;
case PAGETYPE.ORIGINAL_LAST_CHANCE:
queueParam = "Verfügbar für Alle"
page = `Seite: ${pageParam}`
break;
case PAGETYPE.ORIGINAL_SELLER:
queueParam = "Zusätzliche Artikel"
page = `Seite: ${pageParam}`
break;
default:
queueParam = ""
page = ``
break;
}
let shareText = `
${queueParam}
${page}
${_data.tax}
${newUrl}`
const cursorPosition = event.target.selectionStart;
const inputRect = event.target.getBoundingClientRect();
const scrollX = window.pageXOffset || document.documentElement.scrollLeft;
const scrollY = window.pageYOffset || document.documentElement.scrollTop;
let avePopup = document.createElement('div');
avePopup.style.position = 'absolute';
avePopup.style.zIndex = '9999';
avePopup.style.padding = '5px'
avePopup.style.top = `${inputRect.top + scrollY}px`;
avePopup.style.left = `${inputRect.left + scrollX}px`;
avePopup.style.border = '5px solid black';
avePopup.style.borderRadius = '100vh';
avePopup.style.backgroundColor = 'white'
avePopup.style.transform = 'translate(-50%, -100%)'
avePopup.style.opacity = '0';
avePopup.style.transition = "opacity 0.2s ease-in-out";
navigator.clipboard.writeText(shareText).then(() => {
avePopup.innerText = "Text wurde in die Zwischenablage kopiert."
}).catch(err => {
avePopup.innerText = `Fehler beim Kopieren in die Zwischenablage: ${err}`
});
document.body.appendChild(avePopup);
// Timeout 0ms for the next Event Cycle -> Give time to render
setTimeout(()=> {
avePopup.style.opacity = '1';
}, 0);
setTimeout(()=> {
avePopup.style.opacity = '0';
setTimeout(()=> {
avePopup.remove();
}, 200);
}, 3500);
}
}
function createTaxInfoElement(prod, index = Math.round(Math.random()* 10000)) {
console.log('Called createTaxInfo()');
let _currencySymbol = '';
if (prod.data_tax_currency && prod.data_tax_currency == 'EUR') _currencySymbol = '€';
const _taxElement = document.createElement('span');
_taxElement.setAttribute("id", `ave-taxinfo-${index}`);
_taxElement.style.cssText = 'position: relative; transform: translate(0px, -30px); width: fit-content; right: 0px;';
const _taxElement_span = document.createElement('span');
_taxElement_span.setAttribute("id", `ave-taxinfo-${index}-text`);
_taxElement_span.classList.add('ave-taxinfo-text');
const _prize = prod.data_estimated_tax_prize;
console.log('Called createTaxInfo(): We have a Taxprize of: ', _prize);
_taxElement_span.innerText = `Tax Price: ${(typeof(_prize) == 'number') ? _prize :'--.--'} ${_currencySymbol}`;
console.log('createTaxInfo(): After innerText');
_taxElement.appendChild(_taxElement_span);
console.log('createTaxInfo(): END', _taxElement);
return _taxElement;
}
function insertHtmlElementAfter(referenceNode, newNode) {
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
}
async function createProductSite(siteType, productArray, cb) {
if (!productArray) return;
const _productArrayLength = productArray.length;
const _fastCount = Math.min(_productArrayLength, SETTINGS.MaxItemsPerPage);
if (SETTINGS.DebugLevel > 10) console.log(`Create Overview for ${_productArrayLength} Products`);
// Remove Pagination
const _pagination = document.querySelector('.a-pagination')
if (_pagination) _pagination.remove();
const _contentContainer = document.querySelector('.a-section.vvp-tab-content');
if(_contentContainer.querySelector('.vvp-no-offers-msg')){
_contentContainer.querySelector('.vvp-no-offers-msg').remove();
let _tileStructure = document.createElement('div');
_tileStructure.classList = 'a-section vvp-items-container';
_tileStructure.innerHTML = `
`;
_contentContainer.appendChild(_tileStructure);
};
// Cear Left Nodes Container
const _nodesContainer = document.getElementById('vvp-browse-nodes-container');
if (_nodesContainer) _nodesContainer.innerHTML = '';
// Items Grid Container
const _tilesContainer = document.getElementById('vvp-items-grid-container');
if (!_tilesContainer) reloadPageWithSubpageTarget(siteType);
// Edit Top Line
if (_tilesContainer) {
const _topLine = _tilesContainer.getElementsByTagName('p')[0];
_topLine.innerHTML = `Anzeigen von ${_fastCount} von ${_productArrayLength} Ergebnissen
`
}
const _tilesGrid = document.getElementById('vvp-items-grid');
if (!_tilesGrid) reloadPageWithSubpageTarget(siteType);
_tilesGrid.innerHTML = '';
let _index = 0;
let _returned = 0;
for (; _index < _fastCount; _index++) {
createTileFromProduct(productArray[_index], _index, (tile) => {
_tilesGrid.append(tile);
_returned++;
if (SETTINGS.DebugLevel > 10) console.log(`Created Tile (${_returned}/${_fastCount})`);
if (_returned == _fastCount) cb(true);
});
}
addLeftSideButtons(true);
}
async function createInfiniteScrollSite(siteType, cb) {
if (SETTINGS.DebugLevel > 10) console.log(`Called createInfiniteScrollSite()`);
// Remove Pagination
const _pagination = document.querySelector('.a-pagination')
if (_pagination) _pagination.remove();
const _contentContainer = document.querySelector('.a-section.vvp-tab-content');
if(_contentContainer.querySelector('.vvp-no-offers-msg')){
_contentContainer.querySelector('.vvp-no-offers-msg').remove();
let _tileStructure = document.createElement('div');
_tileStructure.classList = 'a-section vvp-items-container';
_tileStructure.innerHTML = `
`;
_contentContainer.appendChild(_tileStructure);
};
// Cear Left Nodes Container
const _nodesContainer = document.getElementById('vvp-browse-nodes-container');
if (_nodesContainer) _nodesContainer.innerHTML = '';
// Items Grid Container
const _tilesContainer = document.getElementById('vvp-items-grid-container');
if (!_tilesContainer) reloadPageWithSubpageTarget(siteType);
// Edit Top Line
if (_tilesContainer) {
const _topLine = _tilesContainer.getElementsByTagName('p')[0];
_topLine.innerHTML = ''
}
const _tilesGrid = document.getElementById('vvp-items-grid');
if (!_tilesGrid) reloadPageWithSubpageTarget(siteType);
_tilesGrid.innerHTML = '';
addLeftSideButtons(true);
cb(_tilesGrid);
}
async function appendInfiniteScrollTiles(cb = ()=>{}){
// So lange tiles hinzufügen bis wir wieder über dem sichtbaren bereich sind
console.log('appendInfiniteScrollTiles(): ', infiniteScrollTilesBufferArray);
const _tilesContainer = document.getElementById('vvp-items-grid');
// setTimeout(async () => {
let _stopCreation = false;
let _createdCount = 0;
while (infiniteScrollTilesBufferArray.length > 0 && !_stopCreation) {
const _tile = infiniteScrollTilesBufferArray.shift();
if (SETTINGS.EnableInfiniteScrollLiveQuerry) {
_tilesContainer.appendChild(_tile);
parseTileData(_tile).then((_product) => {
if (SETTINGS.DebugLevel > 14) console.log('Come Back from parseTileData <<<<<<<<<< INFINITYSCROLL <<<<<<<<<<<<<<<<<<<<<<<', _tile, _product);
addStyleToTile(_tile, _product);
addTileEventhandlers(_tile);
});
} else {
createTileFromProduct(_tile).then((_elem) => {
_tilesContainer.appendChild(_elem);
addTileEventhandlers(_elem);
})
}
if (_createdCount++ >= 100) _stopCreation = true;
const _maxScrollHeight = Math.max(document.body.scrollHeight - window.innerHeight, document.documentElement.scrollHeight - window.innerHeight);
if (_maxScrollHeight > (window.scrollY + (window.innerHeight * 2))) _stopCreation = true;
console.log(`appendInfiniteScrollTiles(): Inside WHILE: _maxScrollHeigt: ${_maxScrollHeight} currPosition ${window.scrollY}`);
}
console.log(`appendInfiniteScrollTiles(): After WHILE: left tile to create: ${infiniteScrollTilesBufferArray.length}`);
cb(true);
// },100);
}
/**
* AVE PAGETYPE ENUM
* @readonly
* @enum {number}
*/
const PAGETYPE = {
NEW_ITEMS: 0,
FAVORITES: 1,
ALL: 2,
SEARCH_RESULT: 9,
OROGINAL_POTLUCK: 100,
ORIGINAL_LAST_CHANCE: 101,
ORIGINAL_SELLER: 102
}
function createNewSite(type, data) {
// Unhightlight nav buttons
const _btnContainer = document.getElementById('vvp-items-button-container');
const _selected = _btnContainer.getElementsByClassName('a-button-selected');
for (let i = 0; i < _selected.length; i++) {
const _btn = _selected[i];
_btn.classList.remove("a-button-selected");
_btn.classList.add("a-button-normal");
_btn.removeAttribute('aria-checked');
}
switch(type) {
case PAGETYPE.NEW_ITEMS:{
currentMainPage = PAGETYPE.NEW_ITEMS;
database.getNewEntries().then((_prodArr) => {
createProductSite(type, _prodArr, () => {
initTileEventHandlers();
const _btn = document.getElementById('ave-btn-list-new');
_btn.classList.add('a-button-selected');
_btn.setAttribute('aria-checked', true);
});
})
break;
}
case PAGETYPE.FAVORITES:{
currentMainPage = PAGETYPE.FAVORITES;
database.getFavEntries().then((_prodArr) => {
createProductSite(type, _prodArr, () => {
initTileEventHandlers();
const _btn = document.getElementById('ave-btn-favorites');
_btn.classList.add('a-button-selected');
_btn.setAttribute('aria-checked', true);
});
})
break;
}
case PAGETYPE.ALL:{
currentMainPage = PAGETYPE.ALL;
createInfiniteScrollSite(currentMainPage,(tilesContainer) => {
const _baseUrl = (/(http[s]{0,1}\:\/\/[w]{0,3}.amazon.[a-z]{1,}.{0,1}[a-z]{0,}\/vine\/vine-items)/.exec(window.location.href))[1];
const _preloadPages = ['potluck', 'last_chance', 'encore']
infiniteScrollLastPreloadedPage = 1;
infiniteScrollMaxPreloadPage = 100;
infiniteScrollTilesBufferArray = [];
if (SETTINGS.EnableInfiniteScrollLiveQuerry) {
getTilesFromURL(`${_baseUrl}?queue=${_preloadPages[0]}`, (tiles1) =>{
infiniteScrollTilesBufferArray = infiniteScrollTilesBufferArray.concat(tiles1);
appendInfiniteScrollTiles();
getTilesFromURL(`${_baseUrl}?queue=${_preloadPages[1]}`, (tiles2) =>{
infiniteScrollTilesBufferArray = infiniteScrollTilesBufferArray.concat(tiles2);
appendInfiniteScrollTiles();
getTilesFromURL(`${_baseUrl}?queue=${_preloadPages[2]}`, (tiles3) =>{
infiniteScrollTilesBufferArray = infiniteScrollTilesBufferArray.concat(tiles3);
appendInfiniteScrollTiles();
setTimeout(()=> {
handleInfiniteScroll(); // Just to trigger first preloads
}, 500);
})
})
})
} else {
database.getAll().then((prodArr) => {
infiniteScrollTilesBufferArray = prodArr;
appendInfiniteScrollTiles();
});
}
});
break;
}
case PAGETYPE.SEARCH_RESULT:{
currentMainPage = PAGETYPE.SEARCH_RESULT;
createProductSite(type, data, () => {
initTileEventHandlers();
});
break;
}
}
}
let lastGetTilesFromURLQuerry = 0;
function getTilesFromURL(url, cb = (tilesArray) => {}) {
if (lastGetTilesFromURLQuerry + SETTINGS.PageLoadMinDelay > Date.now()) {
const _delay = Math.max(1, lastGetTilesFromURLQuerry + SETTINGS.PageLoadMinDelay - Date.now());
console.warn(`getTilesFromURL() DELAYED for ${_delay}ms`)
setTimeout(() => {getTilesFromURL(url, cb)}, _delay);
return;
}
GM.xmlHttpRequest({
method: "GET",
url: url,
onload: function(response) {
const _parser = new DOMParser();
const _doc = _parser.parseFromString(response.responseText, "text/html");
lastGetTilesFromURLQuerry = Date.now();
waitForHtmlElmement('#vvp-items-grid', (itemsContainer) => {
console.log('getTileFromURL(): itemsContainer:', itemsContainer);
// cb(itemsContainer.getElementsByClassName('vvp-item-tile'));
const _retArr = [];
const _elemArr = itemsContainer.querySelectorAll('.vvp-item-tile');
for (let i = 0; i < _elemArr.length; i++){
_retArr.push(_elemArr[i].cloneNode(true));
}
cb(_retArr);
const _paginationData = getPageinationData();
if (_paginationData) infiniteScrollMaxPreloadPage = _pageinationData.maxPage;
}, _doc);
}
})
}
let lastBtnEventhandlerClickTimeStamp = 0;
function btnEventhandlerClick(event, data) {
if (lastBtnEventhandlerClickTimeStamp + 1000 >= Date.now()) return;
lastBtnEventhandlerClickTimeStamp = Date.now();
if (SETTINGS.DebugLevel > 10) console.log(`called btnEventhandlerClick(${JSON.stringify(event)}, ${JSON.stringify(data)})`);
if (data.recommendation_id) {
database.get(data.recommendation_id).then(async (prod) => {
if (SETTINGS.DebugLevel > 10) console.log(`btnEventhandlerClick() got respose from DB:`, prod);
if (prod) {
prod.isNew = false;
requestProductDetails(prod).then((_newProd) => {
database.update(_newProd || prod).then( () => {
updateTileStyle(_newProd || prod);
});
})
}
})
}
}
function favStarEventhandlerClick(event, data) {
if (SETTINGS.DebugLevel > 10) console.log(`called favStarEventhandlerClick(${JSON.stringify(event)}, ${JSON.stringify(data)})`);
if (data.recommendation_id) {
database.get(data.recommendation_id).then((prod) => {
if (SETTINGS.DebugLevel > 10) console.log(`favStarEventhandlerClick() got respose from DB:`, prod);
if (prod) {
prod.isFav = !prod.isFav;
database.update(prod).then(() => {
updateTileStyle(prod);
});
}
})
}
}
/**
* Updates Style and Text of a Product Tile
* @param {Product} prod
* @returns
*/
function updateTileStyle(prod) {
if (SETTINGS.DebugLevel > 10) console.log(`Called updateTileStyle(${JSON.stringify(prod, null, 4)})`);
const _tiles = document.getElementsByClassName('vvp-item-tile');
const _tilesLength = _tiles.length;
if (SETTINGS.DebugLevel > 10) console.log(`Searching for tile with id ${prod.id}`);
for (let i = 0; i < _tilesLength; i++) {
const _tile = _tiles[i];
const _id = _tile.getAttribute('data-recommendation-id');
if (_id == prod.data_recommendation_id) {
if (SETTINGS.DebugLevel > 10) console.log(`Found Tile with id: ${prod.id}`);
_tile.setAttribute('style', (prod.isFav) ? SETTINGS.CssProductFavTag : (prod.isNew) ? SETTINGS.CssProductNewTag : SETTINGS.CssProductDefault);
const _favStar = _tile.querySelector('.ave-favorite-star');
_favStar.style.color = (prod.isFav) ? SETTINGS.FavStarColorChecked : 'white'; // SETTINGS.FavStarColorChecked = Gelb;
const _taxValue = prod.data_estimated_tax_prize;
if (typeof(_taxValue) == 'number') {
const _taxValueElem = _tile.querySelector('.ave-taxinfo-text');
_taxValueElem.innerText = (_taxValueElem.innerText).replace('--.--', _taxValue);
}
return;
}
}
}
// Adds Eventhandler to Product Buttons
function initTileEventHandlers() {
if (SETTINGS.DebugLevel > 10) console.log('Called inttTileEventHandlers() >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>');
const _tiles = document.getElementsByClassName('vvp-item-tile');
const _tileLength = _tiles.length;
for(let i = 0; i < _tileLength; i++) {
if (SETTINGS.DebugLevel > 10) console.log(`Adding Eventhandler to Tile ${i}`);
const _currTile = _tiles[i];
//console.log('init');
addTileEventhandlers(_currTile);
}
}
function addTileEventhandlers(_currTile) {
console.log('Tile Event Handler');
// const _favStar = _currTile.querySelector('.ave-favorite-star');
const _btn = _currTile.querySelector('.vvp-details-btn input');
const _data = new Object()
_data.asin = _btn.getAttribute('data-asin');
_data.parent_asin = _btn.getAttribute('data-is-parent-asin');
_data.recommendation_id = _btn.getAttribute('data-recommendation-id');
waitForHtmlElmement('[id^="ave-taxinfo-"]', (elem) => {
_data.tax = _currTile.querySelector('[id^="ave-taxinfo-"] > span').textContent;
});
const _childs = _btn.childNodes;
_btn.addEventListener('click', (event) => {btnEventhandlerClick(event, _data)});
for(let j = 0; j < _childs.length; j++) {
if (SETTINGS.DebugLevel > 10) console.log(`Adding Eventhandler to Children ${j} of Tile ${i}`);
_childs[j].addEventListener('click', (event) => {btnEventhandlerClick(event, _data)});
}
waitForHtmlElmement('.ave-favorite-star', (elem) => {
elem.addEventListener('click', (event) => {favStarEventhandlerClick(event, _data)});
}, _currTile);
waitForHtmlElmement('.ave-share', (elem) => {
elem.addEventListener('click', (event) => {shareEventHandlerClick(event, _data)});
}, _currTile);
}
function completeDelayedInit() {
initTileEventHandlers();
}
function showAutoScanScreen(text) {
const _overlay = document.createElement('div');
_overlay.style.position = 'fixed';
_overlay.style.top = '0';
_overlay.style.left = '0';
_overlay.style.width = '100%';
_overlay.style.height = '100%';
_overlay.style.backgroundColor = 'rgba(0, 0, 0, 0.8)'; // Grauer Hintergrund mit Transparenz
_overlay.style.zIndex = '1000'; // Stelle sicher, dass das Overlay über anderen Elementen liegt
const _text = document.createElement('div');
_text.style.position = 'absolute';
_text.style.top = '50%';
_text.style.left = '50%';
_text.style.transform = 'translate(-50%, -50%)';
_text.style.color = 'orange'; // Textfarbe
_text.style.textAlign = 'center';
_text.style.fontSize = '50px'; // Ändere die Schriftgröße hier
_text.style.lineHeight = "1";
_text.style.zIndex = '1001';
_text.innerHTML = `${text}
`;
document.body.appendChild(_overlay);
document.body.appendChild(_text);
}
function updateAutoScanScreenText(text = '') {
const _elem = document.getElementById('ave-autoscan-text');
_elem.textContent = text;
}
function addAveSettingsTab(){
waitForHtmlElmement('.vvp-tab-set-container > ul', (_upperButtonsContainer) => {
const _upperSettingsButton = document.createElement('li');
_upperSettingsButton.id = 'vvp-ave-settings-tab';
_upperSettingsButton.classList = 'a-tab-heading';
_upperSettingsButton.role = 'presentation';
_upperSettingsButton.innerHTML += `AVE Einstellungen `;
_upperSettingsButton.addEventListener('click',function(){
const _upperButtons = document.body.querySelectorAll('.a-tab-container.vvp-tab-set-container > ul > li');
_upperButtons.forEach(element => element.classList.remove('a-active'));
const _contentContainer = document.body.querySelectorAll('.a-tab-container.vvp-tab-set-container > div');
_contentContainer.forEach(element => element.classList.add('a-hidden'));
console.log(_contentContainer);
_upperSettingsButton.classList.add('a-active');
const _settingsContent = document.body.querySelector('[data-a-name="ave-settings"]');
_settingsContent.classList.remove('a-hidden');
const _settingsContainer = _settingsContent.querySelector('#ave-settings-container');
console.log(_settingsContainer);
for (const elem of SETTINGS_USERCONFIG_DEFINES) {
console.log('Creating Settings Menu Element: ', elem);
_settingsContainer.appendChild(createSettingsMenuElement(elem));
}
});
_upperButtonsContainer.appendChild(_upperSettingsButton);
})
}
function addAVESettingsMenu(){
waitForHtmlElmement('.a-tab-container.vvp-tab-set-container', (_tabContainer) => {
//const _tabContainer = document.body.querySelector('.a-tab-container.vvp-tab-set-container');
const _boxContainer = document.createElement('div');
_boxContainer.setAttribute('data-a-name', 'ave-settings');
_boxContainer.classList = 'a-box a-box-tab a-tab-content a-hidden';
_boxContainer.role = 'tabpanel';
_boxContainer.tabindex = '0';
const _contentContainer = document.createElement('div');
_contentContainer.classList = 'a-box-inner'
_boxContainer.appendChild(_contentContainer);
_tabContainer.appendChild(_boxContainer);
console.log(_tabContainer);
_contentContainer.innerHTML = `
`;
})
}
function createSettingsMenuElement(dat){
const _elem = document.createElement('div');
if (dat.key) _elem.setAttribute('ave-config-key', dat.key);
_elem.classList.add('ave-settings-item');
if (dat.type == 'bool') {// Boolean Value
const _elem_item_left = document.createElement('div');
_elem_item_left.classList.add('ave-item-left');
const _elem_item_left_label = document.createElement('label');
_elem_item_left_label.classList.add('ave-settings-label-switch');
const _elem_item_left_label_input = document.createElement('input');
_elem_item_left_label_input.type = 'checkbox';
_elem_item_left_label_input.className = 'ave-input-binary';
_elem_item_left_label_input.setAttribute('ave-data-key', dat.key);
// _elem_item_left_label_input.setAttribute('checked', SETTINGS[dat.key])
// _elem_item_left_label_input.value = `${SETTINGS[dat.key]}`;
_elem_item_left_label_input.checked = SETTINGS[dat.key];
_elem_item_left_label_input.addEventListener('click', (event) => {console.log('This is a Boolean Value Input', event); SETTINGS[dat.key] = event.target.checked; SETTINGS.save();})
const _elem_item_left_label_span = document.createElement('span');
_elem_item_left_label_span.classList.add('ave-settings-switch-toggle-slider');
_elem_item_left_label.appendChild(_elem_item_left_label_input);
_elem_item_left_label.appendChild(_elem_item_left_label_span);
_elem_item_left.appendChild(_elem_item_left_label);
_elem.appendChild(_elem_item_left);
const _elem_item_right = document.createElement('div');
_elem_item_right.classList.add('ave-item-right');
_elem_item_right.innerHTML = `${dat.name} `
_elem.appendChild(_elem_item_right);
} else if (dat.type == 'number') { // Number Value
const _elem_item_left = document.createElement('div');
_elem_item_left.classList.add('ave-item-left');
const _elem_item_left_input = document.createElement('input');
_elem_item_left_input.type = 'number';
_elem_item_left_input.className = 'ave-input-number';
_elem_item_left_input.setAttribute('ave-data-key', dat.key);
_elem_item_left_input.setAttribute('value', SETTINGS[dat.key]);
//_elem_item_left_input.setAttribute('onInput', 'this.style.width = "calc(" + (this.value.length + 1) + "ch + 30px)";')
if (!isNaN(dat.min)) _elem_item_left_input.setAttribute('min', dat.min);
if (!isNaN(dat.max)) _elem_item_left_input.setAttribute('max', dat.max);
_elem_item_left_input.addEventListener('change', (event) => {
const _value = event.target.value;
const _min = parseFloat(event.target.min);
const _max = parseFloat(event.target.max);
console.log('This is a Number Value Input', event);
if(_value <= _max && _value >= _min){
console.log("Eingabe Valid");
SETTINGS[dat.key] = parseInt(event.target.value);
SETTINGS.save();
}else{
console.log("Eingabe Fehlerhaft");
}
})
_elem_item_left_input.addEventListener('input', (event) => {
const _value = event.target.value;
const _min = parseFloat(event.target.min);
const _max = parseFloat(event.target.max);
if(_value <= _max && _value >= _min){
event.target.style.borderColor = 'inherit';
event.target.style.color = 'inherit';
}else{
event.target.style.borderColor = 'red';
event.target.style.color = 'red';
}
})
_elem_item_left.appendChild(_elem_item_left_input);
_elem.appendChild(_elem_item_left);
const _elem_item_right = document.createElement('div');
_elem_item_right.classList.add('ave-item-right');
_elem_item_right.innerHTML = `${dat.name} `
_elem.appendChild(_elem_item_right);
} else if (dat.type == 'button') { // Number Value
const _elem_item_left = document.createElement('div');
_elem_item_left.classList.add('ave-item-left');
const _elem_item_left_input_label = document.createElement('label');
_elem_item_left_input_label.setAttribute('data-ave-tooltip', (dat.description && dat.description != '') ? dat.description : dat.name);
_elem_item_left_input_label.setAttribute('class', 'a-button');
_elem_item_left_input_label.style.width = "250px";
if (dat.bgColor) _elem_item_left_input_label.style.backgroundColor = dat.bgColor;
const _elem_item_left_input = document.createElement('button');
_elem_item_left_input.type = 'button';
_elem_item_left_input.className = 'ave-input-button';
// _elem_item_left_input.setAttribute('ave-data-key', dat.key);
_elem_item_left_input.innerText = dat.name;
//_elem_item_left_input.setAttribute('data-ave-tooltip',dat.description);
_elem_item_left_input.addEventListener('click', (event) => {console.log('This is a button Input', event); if(dat.btnClick) dat.btnClick();})
_elem_item_left_input_label.appendChild(_elem_item_left_input);
_elem_item_left.appendChild(_elem_item_left_input_label);
_elem.appendChild(_elem_item_left);
// const _elem_item_right = document.createElement('div');
// _elem_item_right.classList.add('ave-item-right');
// _elem_item_right.innerHTML = `${dat.name} `
// _elem.appendChild(_elem_item_right);
} else if (dat.type == 'color') {
const _elem_item_left = document.createElement('div');
_elem_item_left.classList.add('ave-item-left');
const _elem_item_left_input = document.createElement('input');
_elem_item_left_input.type = 'color';
_elem_item_left_input.className = 'ave-input-color';
_elem_item_left_input.setAttribute('ave-data-key', dat.key);
_elem_item_left_input.setAttribute('value', colorToHex(SETTINGS[dat.key]));
_elem_item_left_input.addEventListener('change', (event) => {console.log('This is a Color Value Input', event); SETTINGS[dat.key] = event.target.value; SETTINGS.save();})
_elem_item_left.appendChild(_elem_item_left_input);
_elem.appendChild(_elem_item_left);
const _elem_item_right = document.createElement('div');
_elem_item_right.classList.add('ave-item-right');
_elem_item_right.innerHTML = `${dat.name} `
_elem.appendChild(_elem_item_right);
} else if (dat.type == 'title'){
const _elem_spacer_horizontal = document.createElement('hr');
_elem_spacer_horizontal.style.width = '100%';
const _elem_spacer_title = document.createElement('h4');
_elem_spacer_title.textContent = dat.name;
_elem.style.height = 'fit-content';
_elem.style.display = 'flex';
_elem.style.flexWrap = 'wrap';
_elem.appendChild(_elem_spacer_horizontal);
_elem.appendChild(_elem_spacer_title);
} else if (dat.type == 'keywords') {
_elem.classList.remove('ave-settings-item');
_elem.classList.add('ave-keyword-wrapper');
_elem.innerHTML = `${dat.name} `;
const _elem_keyword_input = document.createElement('div');
_elem_keyword_input.innerHTML = ' ';
_elem_keyword_input.classList.add('ave-keyword-input');
const _elem_keyword_input_label = document.createElement('label');
_elem_keyword_input_label.setAttribute('data-ave-tooltip', dat.description);
const _elem_keyword_input_input = document.createElement('input');
_elem_keyword_input_input.setAttribute('type', 'text');
_elem_keyword_input_input.setAttribute('placeholder', dat.inputPlaceholder);
_elem_keyword_input_input.addEventListener('change', (elm, ev) => {
console.log('EVENTHANDLER CHANGE:', elm, 'event:', ev);
const _value = elm.target.value.trim();
if (_value && _value.length > 0 && !SETTINGS[dat.key].includes(_value)) SETTINGS[dat.key].push(_value);
SETTINGS.save();
elm.target.value = '';
const _table = document.getElementById(dat.key);
_table.innerHTML = '';
for (let i = 0; i < SETTINGS[dat.key].length; i++){
_table.appendChild(createSettingsKeywordsTableElement(dat, i, SETTINGS[dat.key][i]));
}
})
// const _elem_keyword_input_button = document.createElement('button');
// _elem_keyword_input_button.innerText = '+';
_elem_keyword_input_label.appendChild(_elem_keyword_input_input);
_elem_keyword_input.appendChild(_elem_keyword_input_label);
// _elem_keyword_input.appendChild(_elem_keyword_input_button);
_elem.appendChild(_elem_keyword_input);
const _elem_keyword_list = document.createElement('div');
_elem_keyword_list.classList.add('ave-keyword-list-wrapper');
const _elem_keyword_list_table = document.createElement('table');
const _elem_keyword_list_table_tbody = document.createElement('tbody');
_elem_keyword_list_table_tbody.setAttribute('id', dat.key);
for (let i = 0; i < SETTINGS[dat.key].length; i++){
_elem_keyword_list_table_tbody.appendChild(createSettingsKeywordsTableElement(dat, i, SETTINGS[dat.key][i]));
}
_elem_keyword_list_table.appendChild(_elem_keyword_list_table_tbody);
_elem_keyword_list.appendChild(_elem_keyword_list_table);
_elem.appendChild(_elem_keyword_list);
}
return _elem;
}
function createSettingsKeywordsTableElement(dat, index, entry){
const _tableRow = document.createElement('tr');
_tableRow.setAttribute('index', index);
const _tableRow_td1 = document.createElement('td');
const _tableRow_td1_button = document.createElement('button');
_tableRow_td1_button.innerHTML = ` `;
_tableRow_td1_button.setAttribute('ave-data-keyword', entry);
_tableRow_td1_button.addEventListener('click', (elm, ev) =>{
// console.log('DELETE_BTN:: ', elm)
if (true) SETTINGS[dat.key].splice(index, 1);
SETTINGS.save();
const _table = document.getElementById(dat.key);
_table.innerHTML = '';
for (let i = 0; i < SETTINGS[dat.key].length; i++){
_table.appendChild(createSettingsKeywordsTableElement(dat, i, SETTINGS[dat.key][i]));
}
});
_tableRow_td1.appendChild(_tableRow_td1_button);
_tableRow.appendChild(_tableRow_td1);
const _tableRow_td2 = document.createElement('td');
_tableRow_td2.innerText = entry;
_tableRow.appendChild(_tableRow_td2);
return _tableRow;
}
function addOverlays() { // Old Settings Code
const _overlayBackground = document.createElement('div');
_overlayBackground.style.position = 'fixed';
_overlayBackground.style.backgroundColor = '#000000b0';
_overlayBackground.style.zIndex = '1750';
_overlayBackground.style.width = '100%';
_overlayBackground.style.height = '100%';
_overlayBackground.style.top = '0';
document.body.appendChild(_overlayBackground);
const _settingsDiv = document.createElement('div');
_settingsDiv.style.position = 'fixed';
_settingsDiv.style.zIndex = '1750';
_settingsDiv.style.width = '100%';
_settingsDiv.style.height = '100%';
_settingsDiv.style.top = '0';
_settingsDiv.style.display = 'flex';
_settingsDiv.style.justifyContent = 'center';
_settingsDiv.style.alignItems = 'center';
_settingsDiv.innerHTML = `
Amazon Vine Explorer Einstellungen
`
document.body.appendChild(_settingsDiv);
}
function componentToHex(c) {
const _c = Math.min(c, 255)
const _hex = _c.toString(16);
return _hex.length == 1 ? "0" + _hex : _hex;
}
function rgbToHex(r, g, b){
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
function rgbaToHex(r, g, b, a){
return "#" + componentToHex(a) + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
function colorToHex(color) {
const _color = color.replace(/\s/g,''); // Remove all spaces
let _cache;
if (_color == 'white'){
return '#ffffff';
} else if (_color == 'black'){
return '#000000';
} else if (_cache = /rgb\(([\d]+),([\d]+),([\d]+)\)/.exec(_color)){ // rgb(0,0,0)
return rgbToHex(_cache[1], _cache[2], _cache[3]);
} else if (_cache = /rgba\(([\d]+),([\d]+),([\d]+),([\d]+|[\d]*.[\d]+)\)/.exec(_color)){ // rgba(0,0,0,0)
return rgbaToHex(_cache[1], _cache[2], _cache[3], _cache[4]);
} else if (/\#[0-9a-fA-F]{6}|[0-9a-fA-F]{8}$/.exec(_color)){ // #000000
return _color;
}
}
ave.colorToHex = colorToHex;
function addDBCleaningSymbol(){
const _cleaningDiv = document.createElement('div');
_cleaningDiv.style.width = "25px";
_cleaningDiv.style.height = "25px";
// _cleaningDiv.style.position = 'absolute';
_cleaningDiv.style.position = 'fixed';
_cleaningDiv.style.zIndex = '9999';
_cleaningDiv.style.left = '10px';
_cleaningDiv.style.bottom = '35px';
_cleaningDiv.innerHTML = `
`;
//Vector used
//Vector DB: https://www.svgrepo.com/svg/525311/database
//Vector Lupe: https://www.svgrepo.com/svg/532552/search-alt-2
document.body.appendChild(_cleaningDiv);
return _cleaningDiv;
}
function addDBLoadingSymbol(){
const _loadingDiv = document.createElement('div');
_loadingDiv.style.width = "25px";
_loadingDiv.style.height = "25px";
// _loadingDiv.style.position = 'absolute';
_loadingDiv.style.position = 'fixed';
_loadingDiv.style.zIndex = '9999';
_loadingDiv.style.left = '10px';
_loadingDiv.style.bottom = '35px';
_loadingDiv.innerHTML = `
`;
//Vector used
//Vector DB: https://www.svgrepo.com/svg/525311/database
//Vector Loading: https://www.svgrepo.com/svg/448500/loading
document.body.appendChild(_loadingDiv);
return _loadingDiv;
}
function addLoadingSymbol(){
const _loadingDiv = document.createElement('div');
_loadingDiv.style.width = "25px";
_loadingDiv.style.height = "25px";
// _loadingDiv.style.position = 'absolute';
_loadingDiv.style.position = 'fixed';
_loadingDiv.style.zIndex = '9999';
_loadingDiv.style.left = '10px';
_loadingDiv.style.bottom = '35px';
_loadingDiv.innerHTML = `
`;
//Vector used
//Vector DB: https://www.svgrepo.com/svg/525311/database
//Vector Loading: https://www.svgrepo.com/svg/448500/loading
document.body.appendChild(_loadingDiv);
return _loadingDiv;
}
function getPageinationData(localDocument = document) {
if (SETTINGS.DebugLevel > 10) console.log('Called getPageinationData()');
const _ret = new Object();
const _paginationContainer = localDocument.querySelector('.a-pagination');
if (!_paginationContainer) return;
if (!_paginationContainer.lastChild) return;
let _currChild = _paginationContainer.lastChild;
while ((!_ret.href || !_ret.maxPage) && _currChild) {
const _curr = _currChild.childNodes[0];
if (_curr.hasAttribute('href')) _ret.href = _curr.getAttribute('href').replace(/=[0-9]+/, '=');
if (parseInt(_curr.text)) _ret.maxPage = parseInt(_curr.text);
_currChild = _currChild.previousSibling
}
return _ret;
}
// CleanUp and Fix Database Entrys
async function cleanUpDatabase(cb = () => {}) {
if (SETTINGS.DebugLevel > 10) console.log('Called cleanUpDatabase()');
const _dbCleanIcon = addDBCleaningSymbol();
database.getAll().then((prodArr) => {
const _prodArrLength = prodArr.length;
const _workersProms = [];
if (SETTINGS.DebugLevel > 10) console.log(`cleanUpDatabase() - Checking ${_prodArrLength} Entrys`);
let _updated = 0;
let _deleted = 0;
for (const _currEntry of prodArr) {
_workersProms.push(new Promise((resolve, reject) => {
let _needUpdate = false;
if (SETTINGS.DebugLevel > 10) console.log(`cleanUpDatabase() - Checking Entry ${_currEntry.id} `);
// Checking Product Vars
if (!_currEntry.ts_firstSeen){
_currEntry.ts_firstSeen = (unixTimeStamp() - Math.round(Math.random() * (SECONDS_PER_WEEK / 2)));
_needUpdate = true;
if (SETTINGS.DebugLevel > 14) console.log(`cleanUpDatabase() - Entry ${_currEntry.id} had no valid firstseen timestamp. fixed`);
}
if (!_currEntry.ts_lastSeen) {
_currEntry.ts_lastSeen = (_currEntry.ts_firstSeen + SECONDS_PER_DAY);
_needUpdate = true;
if (SETTINGS.DebugLevel > 14) console.log(`cleanUpDatabase() - Entry ${_currEntry.id} had no valid lastseen timestamp. fixed`);
}
let _notSeenCounter = _currEntry.notSeenCounter;
if (_currEntry.data_recommendation_type == 'VENDOR_TARGETED' && _currEntry.ts_lastSeen < (unixTimeStamp() - SECONDS_PER_DAY)) { // If PotLuck start revoving after 1 day
_notSeenCounter++;
if (SETTINGS.DebugLevel > 14) console.log(`cleanUpDatabase() - Entry ${_currEntry.id} increased notSeenCounter to ${_notSeenCounter}`);
} else if (_currEntry.ts_lastSeen < (unixTimeStamp() - SECONDS_PER_WEEK)) { // Normal Product Start Removing after 1 week
_notSeenCounter++;
if (SETTINGS.DebugLevel > 14) console.log(`cleanUpDatabase() - Entry ${_currEntry.id} increased notSeenCounter to ${_notSeenCounter}`);
}
if (_currEntry.notSeenCounter != _notSeenCounter) {
if (SETTINGS.DebugLevel > 14) console.log(`cleanUpDatabase() - Entry ${_currEntry.id} update notSeenCounter from ${_currEntry.notSeenCounter} to ${_notSeenCounter}`);
_currEntry.notSeenCounter = _notSeenCounter;
_needUpdate = true;
}
if ((_currEntry.notSeenCounter > SETTINGS.NotSeenMaxCount || _currEntry.forceRemove) && !_currEntry.isFav) {
if (SETTINGS.DebugLevel > 10) console.log(`cleanUpDatabase() - Removing Entry ${_currEntry.id}`);
database.removeID(_currEntry.id).then((ret) => {
_deleted++;
resolve()
});
} else if (!_needUpdate){
resolve()
} else {
database.update(_currEntry).then((ret) => {_updated++; resolve();});
}
}))
}
Promise.allSettled(_workersProms).then(() => {
if (SETTINGS.DebugLevel > 0) console.log(`Databasecleanup Finished: Entrys:${_prodArrLength} Updated:${_updated} Deleted:${_deleted}`);
_dbCleanIcon.remove();
cb(true);
})
});
}
unsafeWindow.ave.dbCleanup = cleanUpDatabase;
function exportDatabase() {
console.log('Create Database Dump...');
database.getAll().then((db) => {
try{
console.log("Creating db export JSON as BLOB (uncompressed)");
const dbBlob = new Blob([JSON.stringify(db, null, 4)], {type: "application/json;charset=utf-8"});
console.log("Emulating download file using saveAs script to export JSON file (uncompressed)");
saveAs(dbBlob, "AmazonVineExplorerDatabase.json");
} catch (error) {
console.log("Oops, there was an error exporting AVE user database");
console.log(error);
}
});
}
/**
* Opens a file selector dialog and imports a database from a JSON file.
* @async
* @returns {Promise}
*/
async function importDatabase() {
return new Promise((resolve, reject) => {
// Create an input element of type "file"
const fileInput = document.createElement('input');
fileInput.type = 'file';
fileInput.accept = '.json';
// Set up an event listener for when a file is selected
fileInput.addEventListener('change', async (event) => {
const file = event.target.files[0];
if (file) {
try {
const jsonData = await readFile(file);
// Assuming that the `database` object has a method like `add` to insert data
// Adjust this part based on the actual methods provided by your database object
for (const data of jsonData) {
const existingRecord = await database.get(data.id);
if (!existingRecord) {
await database.add(data);
} else {
console.warn(`Record with ID ${data.id} already exists. Skipping.`);
}
}
console.log('Data imported successfully.');
resolve();
} catch (error) {
console.error('Error importing data:', error);
reject(error);
}
}
});
// Trigger a click event to open the file selector dialog
fileInput.click();
});
}
unsafeWindow.ave.importDB = importDatabase;
/**
* Reads the content of a file as text.
* @param {File} file - The file to read.
* @returns {Promise} - A Promise that resolves to the file content as a string.
*/
function readFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (event) => {
resolve(JSON.parse(event.target.result));
};
reader.onerror = (error) => {
reject(error);
};
reader.readAsText(file);
});
}
function initBackgroundScan() {
if (SETTINGS.DebugLevel > 10) console.log('Called initBackgroundScan()');
if (BackGroundScanIsRunning) {console.warn('initBackgroundScan(): Backgroundscan is already running => Exit');return;}
if (!SETTINGS.EnableBackgroundScan) {console.warn('initBackgroundScan(): Backgroundscan is disabled => Exit');return;}
if (!AVE_IS_THIS_SESSION_MASTER) {console.warn('initBackgroundScan(): This Instance is not the Master Session! => don´t start BackgroundScan'); return;}
BackGroundScanIsRunning = true;
const _baseUrl = (/(http[s]{0,1}\:\/\/[w]{0,3}.amazon.[a-z]{1,}.{0,1}[a-z]{0,}\/vine\/vine-items)/.exec(window.location.href))[1];
// Create iFrame if not exists
if (!document.querySelector('#ave-iframe-backgroundloader')) {
if (SETTINGS.DebugLevel > 10) console.log('initBackgroundScan(): create iFrame');
const iframe = document.createElement('iframe');
iframe.src = encodeURI(`${_baseUrl}?queue=encore&pn=&cn=&page=1`);
iframe.id = 'ave-iframe-backgroundloader';
iframe.style.position = 'fixed';
iframe.style.top = '0';
iframe.style.left = '-10000';
iframe.style.width = '100%';
iframe.style.height = '100%';
iframe.style.display = 'none';
iframe.style.zIndex = '100';
document.body.appendChild(iframe);
}
const _paginatinWaitLoop = setInterval(() => {
const _pageinationData = getPageinationData(document.querySelector('#ave-iframe-backgroundloader').contentWindow.document);
if (_pageinationData) {
clearInterval(_paginatinWaitLoop);
if (SETTINGS.DebugLevel > 10) console.log('initBackgroundScan(): pagination WaitLoop');
if (!(localStorage.getItem('AVE_BACKGROUND_SCAN_IS_RUNNING') == 'true')) {
if (SETTINGS.DebugLevel > 10) console.log('initBackgroundScan(): init localStorage Variables');
localStorage.setItem('AVE_BACKGROUND_SCAN_PAGE_MAX',_pageinationData.maxPage);
localStorage.setItem('AVE_BACKGROUND_SCAN_IS_RUNNING', true);
localStorage.setItem('AVE_BACKGROUND_SCAN_PAGE_CURRENT', 1);
localStorage.setItem('AVE_BACKGROUND_SCAN_STAGE', 0);
}
let _loopIsWorking = false;
let _subStage = 0;
const _stageZeroSites = ['queue=potluck', 'queue=last_chance']
backGroundScanTimeout = setTimeout(initBackgroundScanSubFunctionScannerLoop, SETTINGS.BackGroundScanDelayPerPage);
function initBackgroundScanSubFunctionScannerLoop(){
if (_loopIsWorking) return;
_loopIsWorking = true;
let _backGroundScanStage = parseInt(localStorage.getItem('AVE_BACKGROUND_SCAN_STAGE')) || 0;
if (SETTINGS.DebugLevel > 10) console.log('initBackgroundScan(): loop with _backgroundScanStage ', _backGroundScanStage, ' and Substage: ', _subStage);
switch (_backGroundScanStage) {
case 0:{ // potluck, last_chance
if (SETTINGS.DebugLevel > 10) console.log('initBackgroundScan().loop.case.0 with _subStage: ', _subStage);
if (_stageZeroSites[_subStage]) {
if (SETTINGS.DebugLevel > 10) console.log('initBackgroundScan().loop.case.0 with _subStage: ', _subStage, ' inside IF');
backGroundTileScanner(`${_baseUrl}?${_stageZeroSites[_subStage]}` , (elm) => {_scanFinished()});
_subStage++
} else {
if (SETTINGS.DebugLevel > 10) console.log('initBackgroundScan().loop.case.0 with _subStage: ', _subStage, ' inside ELSE');
_subStage = 0;
_backGroundScanStage++;
_scanFinished();
}
break;
}
case 1: { // queue=encore | queue=encore&pn=&cn=&page=2...x
_subStage = parseInt(localStorage.getItem('AVE_BACKGROUND_SCAN_PAGE_CURRENT'));
if (SETTINGS.DebugLevel > 10) console.log('initBackgroundScan().loop.case.1 with _subStage: ', _subStage);
if (_subStage < (parseInt(localStorage.getItem('AVE_BACKGROUND_SCAN_PAGE_MAX')) || 0)) {
backGroundTileScanner(`${_baseUrl}?queue=encore&pn=&cn=&page=${_subStage + 1}` , () => {_scanFinished()});
_subStage++
localStorage.setItem('AVE_BACKGROUND_SCAN_PAGE_CURRENT', _subStage);
} else {
_subStage = 0;
_backGroundScanStage++;
_scanFinished();
}
break;
}
case 2: { // qerry about other values (tax, real prize, ....) ~ 20 - 30 Products then loopover to stage 1
//Disaled due to Bugs fetching the Tax
_backGroundScanStage++;
_scanFinished();
break;
if (SETTINGS.DebugLevel > 10) console.log('initBackgroundScan().loop.case.2 with _subStage: ', _subStage);
database.getAll().then((products) => {
const _needUpdate = [];
const _randCount = Math.round(Math.random() * 4);
for (const _prod of products) {
if (_needUpdate.length < _randCount) {
if (typeof(_prod.data_estimated_tax_prize) != 'number') _needUpdate.push(_prod);
} else {
break;
}
}
const _promises = [];
for (const _prod of _needUpdate) {
requestProductDetails(_prod).then((_newProd) => {
_promises.push(database.update(_newProd));
});
}
Promise.all(_promises).then(() => {
_scanFinished();
_subStage++;
}).catch(() => {
console.error('There was an error while updating an product in database');
_scanFinished();
_subStage++;
});
});
if (_subStage++ >= 10)
{
_subStage = 0;
_backGroundScanStage++;
_scanFinished();
}
break;
}
default: {
cleanUpDatabase(() => {
_backGroundScanStage = 0;
_subStage = 0;
_scanFinished();
})
//clearInterval(backGroundScanTimeout);
}
}
function _scanFinished() {
if (SETTINGS.DebugLevel > 10) console.log(`initBackgroundScan()._scanFinished()`);
localStorage.setItem('AVE_BACKGROUND_SCAN_STAGE', _backGroundScanStage);
localStorage.setItem('AVE_BACKGROUND_SCAN_PAGE_CURRENT', _subStage);
_loopIsWorking = false;
backGroundScanTimeout = setTimeout(initBackgroundScanSubFunctionScannerLoop, SETTINGS.BackGroundScanDelayPerPage + Math.round(Math.random() * SETTINGS.BackGroundScannerRandomness));
}
}
}
}, 250);
}
function backGroundTileScanner(url, cb) {
if (SETTINGS.DebugLevel > 10) console.log(`Called backgroundTileScanner(${url})`);
const _iconLoading = addLoadingSymbol();
const _iframeDoc = document.querySelector('#ave-iframe-backgroundloader').contentWindow.document;
ave.backGroundIFrame = _iframeDoc;
_iframeDoc.location.href = url;
const _loopDelay = setInterval(() => {
if (SETTINGS.DebugLevel > 10) console.log(`backgroundTileScanner(): check if we have tiles to read...`);
const _tiles =_iframeDoc.querySelectorAll('.vvp-item-tile');
if (_tiles) {
if (SETTINGS.DebugLevel > 10) console.log(`backgroundTileScanner(): Found first Tile`);
const _tilesLength = _tiles.length;
if (SETTINGS.DebugLevel > 10) console.log(`BackgroundsScan Querryd: ${url} and got ${_tilesLength} Tiles`);
clearInterval(_loopDelay);
if (_tilesLength > 0) {
let _returned = 0;
const _tilesProm = []
for (let i = 0; i < _tilesLength; i++) {
_tilesProm.push(parseTileData(_tiles[i]).then((prod) => {
_returned++;
if (SETTINGS.DebugLevel > 14) console.log(`BACKGROUNDSCAN => Got TileData Back: Tile ${_returned}/${_tilesLength} =>`, prod);
if (!prod.gotFromDB) database.add(prod);
}))
}
Promise.allSettled(_tilesProm).then(() => {
cb(true);
_iconLoading.remove();
});
} else {
if (SETTINGS.DebugLevel > 10) console.log(`BACKGROUNDSCAN => We dont have anything to do here anything => resume autoscan`);
cb(true); // We dont have to do here anything
_iconLoading.remove();
}
}
}, 100);
}
function startAutoScan() {
if (SETTINGS.DebugLevel > 10) console.log('Called startAutoScan()');
showAutoScanScreen('Init Autoscan, please wait...');
markAllCurrentDatabaseProductsAsSeen(() => {
if (SETTINGS.DebugLevel > 10) console.log('startAutoScan() - Got Callback from markAllCurrentDatabaseProductsAsSeen()');
const _pageiDat = getPageinationData();
localStorage.setItem('AVE_INIT_AUTO_SCAN', false);
localStorage.setItem('AVE_AUTO_SCAN_IS_RUNNING', true);
localStorage.setItem('AVE_AUTO_SCAN_PAGE_MAX',_pageiDat.maxPage);
localStorage.setItem('AVE_AUTO_SCAN_PAGE_CURRENT', 1);
setTimeout(() => {
const _url = `${_pageiDat.href}1`;
if (SETTINGS.DebugLevel > 10) console.log(`Loding new Page ${_url}`)
window.location.href = _url;
}, 5000);
})
}
function handleAutoScan() {
let _href;
const _delay = Math.max(SETTINGS.PageLoadMinDelay - (Date.now() - PAGE_LOAD_TIMESTAMP), 0) + 500;
if (SETTINGS.DebugLevel > 10) console.log(`handleAutoScan() - _delay: ${_delay}`);
if (AUTO_SCAN_PAGE_CURRENT < AUTO_SCAN_PAGE_MAX) {
const _nextPage = AUTO_SCAN_PAGE_CURRENT + 1;
localStorage.setItem('AVE_AUTO_SCAN_PAGE_CURRENT', _nextPage);
setTimeout(() => {
window.location.href = window.location.href.replace(/=[0-9]+/, `=${_nextPage}`);
}, _delay);
} else { // We are done ;)
updateAutoScanScreenText('Success, cleaning up Database...');
cleanUpDatabase(()=> {
localStorage.setItem('AVE_AUTO_SCAN_IS_RUNNING', false);
localStorage.setItem('AVE_AUTO_SCAN_PAGE_MAX', -1);
localStorage.setItem('AVE_AUTO_SCAN_PAGE_CURRENT', -1);
setTimeout(() => {
updateAutoScanScreenText('Finished Database\nupdate and cleanup\n\nPage reloading incoming... please wait');
setTimeout(()=> {
window.location.href = window.location.href.replace(/=[0-9]+/, '=1');
}, 10000);
}, _delay + 2000);
});
}
}
function stickElementToTopScrollEVhandler(elemID, dist) {
const _elem = document.getElementById(elemID);
if (_elem) {
const maxScrollHeight = Math.max(
document.body.scrollHeight - window.innerHeight,
document.documentElement.scrollHeight - window.innerHeight
);
requestAnimationFrame(() => {
const _elemRect = _elem.getBoundingClientRect();
const _elemInitialTop = parseInt(_elem.getAttribute('ave-data-default-top'));
if (!_elemInitialTop) {_elem.setAttribute('ave-data-default-top', (window.scrollY + _elemRect.top)); return;}
if (SETTINGS.DebugLevel > 10) console.log(`### scrollY:${window.scrollY} maxScrollHeigt ${maxScrollHeight} initialTop: ${_elemInitialTop}`);
if (window.scrollY >= (_elemInitialTop - parseInt(dist))) {
_elem.style.position = "fixed";
_elem.style.top = dist;
} else {
_elem.style.position = "static";
}
})
}
}
let lastDesktopNotifikationTimestamp = 0;
function updateNewProductsBtn() {
if (AUTO_SCAN_IS_RUNNING) return;
if (SETTINGS.DebugLevel > 1) console.log('Called updateNewProductsBtn()');
database.getNewEntries().then((prodArr) => {
const _btnBadge = document.getElementById('ave-new-items-btn-badge');
const _pageTitle = document.title.replace(/^[^\|]*\|/, '').trim();
const _prodArrLength = prodArr.length;
if (SETTINGS.DebugLevel > 1) console.log(`updateNewProductsBtn(): Got Database Response: ${_prodArrLength} New Items`);
if (_prodArrLength > 0) {
_btnBadge.style.display = 'inline-block';
_btnBadge.innerText = _prodArrLength;
document.title = `${_prodArrLength} | ${_pageTitle}`;
} else {
_btnBadge.style.display = 'none';
_btnBadge.innerText = '';
document.title = `${_pageTitle}`;
}
let _notifyed = false;
if (SETTINGS.EnableDesktopNotifikation && SETTINGS.DesktopNotifikationKeywords?.length > 0) {
if (SETTINGS.DebugLevel > 1) console.log(`updateNewProductsBtn(): Insige IF`);
const _configKeyWords = SETTINGS.DesktopNotifikationKeywords;
for (let i = 0; i < _prodArrLength; i++) {
const _prod = prodArr[i];
const _descFull = _prod.description_full.toLowerCase();
if (SETTINGS.DebugLevel > 1) console.log(`updateNewProductsBtn(): Search Product Deescription: ${_descFull} for keys: `, _configKeyWords);
const _configkeyWordsLength = _configKeyWords.length;
for (let j = 0; j < _configkeyWordsLength; j++) {
const _currKey = _configKeyWords[j].toLowerCase();
if (SETTINGS.DebugLevel > 1) console.log(`updateNewProductsBtn(): Search Product Deescription for Keyword: ${_currKey}`);
if (_descFull.includes(_currKey)) {
desktopNotifikation(`Amazon Vine Explorer - ${AVE_VERSION}`, _prod.description_full, _prod.data_img_url, true);
_notifyed = true;
}
break;
}
}
}
if (SETTINGS.EnableDesktopNotifikation && !_notifyed && _prodArrLength > oldCountOfNewItems){
if (unixTimeStamp() - lastDesktopNotifikationTimestamp >= SETTINGS.DesktopNotifikationDelay) {
oldCountOfNewItems = _prodArrLength;
lastDesktopNotifikationTimestamp = unixTimeStamp();
desktopNotifikation(`Amazon Vine Explorer - ${AVE_VERSION}` , `Es wurden ${_prodArrLength} neue Vine Produkte gefunden`);
}
}
})
}
/**
* Send a Desktop Notifikation
* @param {string} title
* @param {string} message
* @param {string} icon
*
*/
function desktopNotifikation(title, message, image = null, requireInteraction = null, onClick = () => {}) {
const _vineLogo = 'https://raw.githubusercontent.com/Amazon-Vine-Explorer/AmazonVineExplorer/main/vine_logo.png';
const _vineLogoImp = 'https://raw.githubusercontent.com/Amazon-Vine-Explorer/AmazonVineExplorer/dev-main/vine_logo_important.png'
const _defaultImage = 'https://raw.githubusercontent.com/Amazon-Vine-Explorer/AmazonVineExplorer/dev-main/vine_logo_notification_image.png'
if (Notification.permission === 'granted') {
const _notification = new Notification(title, {
body: message,
icon: (!requireInteraction) ? _vineLogo : _vineLogoImp,
image: image || _defaultImage,
tag: (requireInteraction) ? `ave-notify-${Math.round(Math.random()* 10000000)}`: 'ave-notify',
requireInteraction: requireInteraction,
});
_notification.onclick = onClick;
} else {
Notification.requestPermission().then(function(permission) {
if (permission === 'granted') {
console.log('Berechtigung für Benachrichtigungen erhalten!');
desktopNotifikation(title, message, icon, onclick);
}
});
}
}
function createNavButton(mainID, text, textID, color, onclick, badgeId, badgeValue) {
const _btn = document.createElement('span');
_btn.setAttribute('id', mainID);
_btn.setAttribute('class', 'a-button a-button-normal a-button-toggle');
_btn.addEventListener('click', onclick);
const _btnInner = document.createElement('span');
_btnInner.classList.add('a-button-inner');
_btnInner.style.backgroundColor = color;
_btnInner.style.display = 'flex';
_btn.append(_btnInner);
const _btnInnerText = document.createElement('span');
_btnInnerText.setAttribute('id', textID);
_btnInnerText.classList.add('a-button-text');
_btnInnerText.innerText = text;
_btnInner.append(_btnInnerText);
if (badgeId) {
const _btnInnerBadge = document.createElement('span');
_btnInnerBadge.setAttribute('id', badgeId)
_btnInnerBadge.setAttribute('class', 'a-button-text')
_btnInnerBadge.style.backgroundColor = 'red';
_btnInnerBadge.style.color = 'white';
_btnInnerBadge.style.display = 'inline-block';
_btnInnerBadge.style.textAlign = 'center';
// _btnInnerBadge.style.transform = 'translate(-75%, -100%)';
_btnInnerBadge.style.zIndex = '50';
_btnInnerBadge.style.position = 'relativ';
// _btnInnerBadge.style.padding = '5px';
_btnInnerBadge.innerText = badgeValue;
_btnInner.append(_btnInnerBadge);
}
return _btn;
}
function addStyleToTile(_currTile, _product) {
if (!_product.gotFromDB) { // We have a new one ==> Save it to our Database ;)
database.add(_product);
_currTile.style.cssText = SETTINGS.CssProductSaved;
_currTile.classList.add('ave-element-saved');
} else {
let _style = SETTINGS.CssProductDefault;
if(_product.isNew) {
_style = SETTINGS.CssProductNewTag;
_currTile.classList.add('ave-element-new');
}
if(_product.isFav) {
_style = SETTINGS.CssProductFavTag;
_currTile.classList.add('ave-element-fav');
}
_currTile.style.cssText = _style;
// Update Timestamps
}
_currTile.prepend(createFavStarElement(_product));
_currTile.prepend(createShareElement(_product));
// insertHtmlElementAfter((_currTile.getElementsByClassName('vvp-item-product-title-container')[0]), createTaxInfoElement(_product));
waitForHtmlElmement('.vvp-item-product-title-container', (_elem) => {
insertHtmlElementAfter(_elem, createTaxInfoElement(_product));
}, _currTile)
}
async function requestProductDetails(prod) {
return new Promise(async (resolve, reject) => {
if (prod.data_asin_is_parent) {// Lets get the Childs first
fetch(`${window.location.origin}/vine/api/recommendations/${prod.id}`.replace(/#/g, '%23')).then(r => r.json()).then(async (res) => {
if (res.error) {
if (res.error.exceptionType == 'ITEM_NOT_IN_ENROLLMENT') {
prod.forceRemove = true;
resolve(prod);
} else {
console.error('requestProductDetails():ERROR:', res.error);
reject(res.error.exceptionType);
}
}
const _data = res.result;
console.log('DATA:', _data)
prod.data_childs = _data.variations || [];
const _promArray = new Array();
prod.data_estimated_tax_prize = prod.data_estimated_tax_prize || 0;
for (_child of prod.data_childs) {
_promArray.push(fetch(`${window.location.origin}/vine/api/recommendations/${(prod.id).replace(/#/g, '%23')}/item/${_child.asin}`.replace(/#/g, '%23')).then(r => r.json()).then((childData) => {
console.log('CHILD_DATA:', childData);
if (!childData.error) {
// Copy over all returned datapoints od child asin
for (_datapoint of Object.keys(childData.result)) {
_child[_datapoint] = childData.result[_datapoint];
}
if (prod.data_estimated_tax_prize < _child.taxValue) {
prod.data_estimated_tax_prize = _child.taxValue;
prod.data_tax_currency = _child.taxCurrency;
}
}
}))
}
Promise.all(_promArray).then((values) => {
console.log('All fetches returned: ', values);
resolve(prod);
});
})
} else {
fetch(`${window.location.origin}/vine/api/recommendations/${prod.id}/item/${prod.data_asin}`.replace(/#/g, '%23')).then(r => r.json()).then(ret => {
console.log('RETURN:', ret);
if (ret.error) {
reject(ret.error.exceptionType) // => "ITEM_NOT_IN_ENROLLMENT"
} else {
const data = ret.result;
prod.data_feature_bullets = data.featureBullets;
prod.data_contributors = data.byLineContributors;
prod.data_catalogSize = data.catalogSize;
prod.data_tax_currency = data.taxCurrency;
prod.data_estimated_tax_prize = data.taxValue;
prod.data_limited_quantity = data.limitedQuantity;
resolve(prod);
}
})
}
})
}
function init(hasTiles) {
// Get all Products on this page ;)
if (AUTO_SCAN_IS_RUNNING) showAutoScanScreen(`Autoscan is running...Page (${AUTO_SCAN_PAGE_CURRENT}/${AUTO_SCAN_PAGE_MAX})`);
const _aveSubpageRequest = getUrlParameter('ave-subpage');
if (SETTINGS.DebugLevel > 10) console.log(`Got Subpage Parameter`, _aveSubpageRequest)
if (_aveSubpageRequest) createNewSite(parseInt(_aveSubpageRequest));
if (hasTiles) {
const _tiles = document.getElementsByClassName('vvp-item-tile');
const _tilesLength = _tiles.length;
const _tilePorms = [];
const _parseStartTime = Date.now();
for (let i = 0; i < _tilesLength; i++) {
const _currTile = _tiles[i];
_currTile.style.cssText = "background-color: yellow;";
_tilePorms.push(parseTileData(_currTile).then((_product) => {
if (SETTINGS.DebugLevel > 14) console.log('Come Back from parseTileData <<<<<<<<<< INIT <<<<<<<<<<<<<<<<<<<<<<<', _currTile, _product);
addStyleToTile(_currTile, _product);
}));
}
Promise.allSettled(_tilePorms).then(() => {
if(INIT_AUTO_SCAN) {
startAutoScan();
} else if (AUTO_SCAN_IS_RUNNING) {
handleAutoScan();
} else {
completeDelayedInit();
}
})
} else {
if (SETTINGS.DebugLevel > 10) console.log(`init(): NO TILES TO PARSE ON THIS SITE => SKIP`);
}
if (AUTO_SCAN_IS_RUNNING) return;
const _searchbarContainer = document.getElementById('vvp-items-button-container');
_searchbarContainer.appendChild(createNavButton('ave-btn-favorites', 'Alle Produkte', '', SETTINGS.BtnColorAllProducts, () => {createNewSite(PAGETYPE.ALL);}));
_searchbarContainer.appendChild(createNavButton('ave-btn-favorites', 'Favoriten', '', SETTINGS.BtnColorFavorites, () => {createNewSite(PAGETYPE.FAVORITES);}));
_searchbarContainer.appendChild(createNavButton('ave-btn-list-new', 'Neue Einträge', 'ave-new-items-btn', SETTINGS.BtnColorNewProducts, () => {createNewSite(PAGETYPE.NEW_ITEMS);}, 'ave-new-items-btn-badge', '-'));
updateNewProductsBtn();
// Searchbar
const _searchBarSpan = document.createElement('span');
_searchBarSpan.setAttribute('class', 'ave-search-container');
_searchBarSpan.style.cssText = `margin: 0.5em;`;
// _searchBarSpan.innerHTML = ` `;
const _searchBarInput = document.createElement('input');
_searchBarInput.setAttribute('type', 'search');
_searchBarInput.setAttribute('placeholder', 'Suche Vine Produkte');
_searchBarInput.setAttribute('name', 'ave-search');
_searchBarInput.style.cssText = `width: 30em;`;
_searchBarInput.addEventListener('keyup', (ev) => {
const _input = _searchBarInput.value.toLowerCase();
if (SETTINGS.DebugLevel > 10) console.log(`Updated Input: ${_input}`);
if (_input.length >= 2) {
if (searchInputTimeout) clearTimeout(searchInputTimeout);
searchInputTimeout = setTimeout(() => {
database.query(_input.split(' ')).then((_objArr) => {
if (SETTINGS.DebugLevel > 10) console.log(`Found ${_objArr.length} Items with this Search`);
createNewSite(PAGETYPE.SEARCH_RESULT, _objArr);
searchInputTimeout = null;
})
}, 250);
}
});
_searchBarSpan.appendChild(_searchBarInput);
_searchbarContainer.appendChild(_searchBarSpan);
// Deactivatet due to Bugs
// Manual Autoscan and Backgroundscan can not run together, so don´t create the button
//if (!SETTINGS.EnableBackgroundScan) _searchbarContainer.appendChild(createNavButton('ave-btn-updateDB', 'Update Database', 'ave-btn-updateDB-text',SETTINGS.BtnColorUpdateDB, () => {localStorage.setItem('AVE_INIT_AUTO_SCAN', true); window.location.href = "vine-items?queue=encore";}));
if (hasTiles) addLeftSideButtons();
if (SETTINGS.EnableBackgroundScan) initBackgroundScan();
// Modify Pageination if exists
const _pageinationContainer = document.getElementsByClassName('a-pagination')[0];
if (_pageinationContainer) {
if (SETTINGS.DebugLevel > 10) console.log('Manipulating Pageination');
const _nextBtn = _pageinationContainer.lastChild;
const _isNextBtnDisabled = _nextBtn.classList.contains('a-disabled');
const _nextBtnLink = _nextBtn.lastChild.getAttribute('href');
const _btn = _nextBtn.cloneNode(true);
const anchorTag = _btn.querySelector('a');
//const _aveNextPageButtonText = 'Alle als gesehen markieren und Nächste → ';
const _aveNextPageButtonText = 'Gelesen → ';
const _AveNextArrow = document.createElement('style');
_AveNextArrow.type = 'text/css';
_AveNextArrow.innerHTML = `.ave-arrow::after{border-style: solid; border-width: 2px 2px 0 0; content: ''; padding: 2.5px; visibility: visible; display: inline-block; position: relative; left: -9px; top: -1px; transform: rotate(45deg);}`;
if (!_isNextBtnDisabled) {
_nextBtn.setAttribute('class', 'a-normal');
_nextBtn.querySelector('span.larr').style.visibility = 'hidden';
_nextBtn.querySelector('span.larr').classList.add('ave-arrow');
}
if (anchorTag) {
anchorTag.innerHTML = _aveNextPageButtonText;
}
else {
//_btn.innerHTML = _aveNextPageButtonText;
_btn.innerHTML = 'Gelesen'
}
_btn.style.color = 'unset';
_btn.style.backgroundColor = 'lime';
_btn.style.borderRadius = '8px';
_btn.style.cursor = 'pointer';
_btn.addEventListener('click', () => {
markAllCurrentSiteProductsAsSeen(() => {
if(!_nextBtn.classList.contains('a-disabled')){
window.location.href = (_nextBtnLink);
}
});
})
_pageinationContainer.appendChild(_btn);
_pageinationContainer.appendChild(_AveNextArrow);
}
}