/*===========================================================================*\ | TART for amazon.de | | Based on The Amazon Review Tabulator - TART v1.5.5 | | (c) 2016-17 by Another Floyd | | German fork by Strg-Alt-Entf | | Ausgehend von "Mein Profil - Rezensionen" auf Amazon, listet es alle | | deine Rezensionen auf und informiert dich über Änderungen | | (neue Hilfreich-/Nicht hilfreich-Klicks, neue Kommentare). Klicke auf | | "Tabelle", um eine Übersicht über alle Rezensionen aufzurufen. | | Klicke auf "Optionen" für Optionen. | \*===========================================================================*/ // ==UserScript== // @name TART for amazon.de // @namespace Strg-Alt-Entf.scripts // @version 1.0.3 // @author Strg-Alt-Entf // @description Listet alle deine Rezensionen auf und informiert dich über Änderungen (neue Hilfreich-/Nicht hilfreich-Klicks, neue Kommentare) // @include https://*amazon.de/gp/cdp/member-reviews* // @grant GM_getValue // @grant GM_setValue // @grant GM_xmlhttpRequest // @grant GM_log // @grant GM_openInTab // @grant GM_info // @require https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js // @require https://greasyfork.org/scripts/20744-sortable/code/sortable.js?version=132520 // @require https://openuserjs.org/src/libs/sizzle/GM_config.js // @icon data:image/png;base64,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 // @downloadURL https://update.greasyfork.icu/scripts/31289/TART%20for%20amazonde.user.js // @updateURL https://update.greasyfork.icu/scripts/31289/TART%20for%20amazonde.meta.js // ==/UserScript== // Start (function() { var showUpdatesOnly = false; var primaryDisplayBuffer = ""; var updateDisplayBuffer = ""; var oldTARTstats = []; var userID = ""; var reviewCount = 0; var reviewerRanking = ""; //wird nicht mehr genutzt, aber beibehalten, falls Amazon die Seite mal wieder ändert var helpfulVotes = 0; var oldStoreItemIDs = []; var oldStoreUpvotes = []; var oldStoreDownvotes = []; var oldStoreComments = []; var newStoreItemIDs = ""; var newStoreUpvotes = ""; var newStoreDownvotes = ""; var newStoreComments = ""; var tallyWordcount = 0; var tallyUpvotes = 0; var tallyDownvotes = 0; var tallyAllvotes = 0; var tallyStars = 0; var tallyComments = 0; var tallyAVP = 0; var tallyVine = 0; // use this reference for progress indicator var profileDiv = ""; var profileDivOriginalHTML = ""; var profileDivTabulateHTML = "

Tabelle Optionen"; function assembleDisplayBuffers (completeSetOfTableRows, reviewsProcessed) { var today = new Date(); var formattedToday = today.toLocaleDateString('de-DE',{month:'long',day:'numeric',year:'numeric'}); var toggleLink = (GM_config.get('DisplayMode')) ? "

Ansicht umschalten: Alle Rezensionen / Nur Änderungen" : ""; var bMargin = (GM_config.get('FixedFooter')) ? "36" : "0"; var upvoteReviewRatio = (helpfulVotes/reviewCount).toFixed(2); // set up top of display page primaryDisplayBuffer = "" + "TART Amazon Rezensionsübersicht" + "" + "Amazon Rezensionsübersicht
" + "Erstellt mit TART for amazon.de " + GM_info.script.version + " - " + formattedToday + "

Kundenrezensionen: " + checkChange(reviewCount, oldTARTstats[6], false) + "
" + "Hilfreich-Klicks: " + checkChange(helpfulVotes, oldTARTstats[7], false) + "
" + "Hilfreich-Klicks pro Rezension: " + checkChange(upvoteReviewRatio, oldTARTstats[8], false) + toggleLink + "

" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""; // Column widths above are assigned to columns that have heading shorter than // data is likely to be; widths are duplicated at separate footer table, to keep // them all in sync updateDisplayBuffer = primaryDisplayBuffer; // both displays have same top section primaryDisplayBuffer += completeSetOfTableRows; // info needed in footer var calcStars = (tallyStars/reviewsProcessed).toFixed(1); var calcHelpfulPct = helpfulPercent(tallyUpvotes,tallyDownvotes); var avgWordsPerReview = (tallyWordcount/reviewsProcessed).toFixed(0); var newTARTstats = calcStars + " " + tallyUpvotes + " " + tallyDownvotes + " " + calcHelpfulPct + " " + tallyComments + " " + reviewerRanking + " " + reviewCount + " " + helpfulVotes + " " + upvoteReviewRatio + " " + tallyAllvotes + " " + tallyAVP + " " + tallyVine + " " + reviewsProcessed + " " + avgWordsPerReview; GM_setValue("recentFooterValues", newTARTstats.trim()); // write 'em with new values var visibleFooterRow = "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""; // add footer either to be fixed at bottom of screen, or normal if(GM_config.get('FixedFooter')) { var hiddenRowForColumnWidths = "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""; // create detached table for footer // fixed, by virtue of the styled 'footer' id primaryDisplayBuffer += "
#ProduktDatumSterneHilfreichNicht hilfreichGesamt% HilfreichKommentareVerifiziertVine
" + checkChange(reviewsProcessed, oldTARTstats[12], true) + "Durchschnittliche Wortzahl pro Rezension: " + checkChange(avgWordsPerReview, oldTARTstats[13], true) + "" + checkChange(calcStars, oldTARTstats[0], true) + "" + checkChange(tallyUpvotes, oldTARTstats[1], true) + "" + checkChange(tallyDownvotes, oldTARTstats[2], true) + "" + checkChange(tallyAllvotes, oldTARTstats[9], true) + "" + checkChange(calcHelpfulPct, oldTARTstats[3], true) + "" + checkChange(tallyComments, oldTARTstats[4], true) + "" + checkChange(tallyAVP, oldTARTstats[10], true) + "" + checkChange(tallyVine, oldTARTstats[11], true) + "
SterneHilfreichNicht HilfreichGesamt% HilfreichKommentare
" + hiddenRowForColumnWidths + visibleFooterRow + ""; } else { primaryDisplayBuffer += "" + visibleFooterRow + ""; // normal } // get rows containing updated reviews, only var tempDiv = document.createElement('div'); tempDiv.innerHTML = primaryDisplayBuffer; var findUpdateRows = document.evaluate("//td[@class='hilite-left']/..", tempDiv, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); for(var d = 0; d < findUpdateRows.snapshotLength; d++) { updateDisplayBuffer += findUpdateRows.snapshotItem(d).outerHTML; } updateDisplayBuffer += ""; } function tabulate() { // reset global accumulators to ensure that repeated script runs // (non-enhanced mode) remain clean newStoreItemIDs = ""; newStoreUpvotes = ""; newStoreDownvotes = ""; newStoreComments = ""; tallyWordcount = 0; tallyUpvotes = 0; tallyDownvotes = 0; tallyAllvotes = 0; tallyStars = 0; tallyComments = 0; tallyAVP = 0; tallyVine = 0; // read in stored info from past run, for use in change detection oldStoreItemIDs = GM_getValue("recentItemIDs", "").split(" "); oldStoreUpvotes = GM_getValue("recentUpvotes", "").split(" "); oldStoreDownvotes = GM_getValue("recentDownvotes", "").split(" "); oldStoreComments = GM_getValue("recentComments", "").split(" "); // prepare url with country domain and user ID, ready for review page number var tld = "de"; var url = window.location.href; var urlStart = "https://www.amazon." + tld + "/gp/cdp/member-reviews/" + userID + "?ie=UTF8&display=public&page="; var urlEnd = "&sort_by=MostRecentReview"; // space and counters for incoming data var perPageResponseDiv = []; var pageSetOfTableRows = []; var pageResponseCount = 0; var reviewsProcessed = 0; var pageCount = Math.floor(reviewCount / 10) + ((reviewCount % 10 > 0) ? 1 : 0); //var pageCount = 3; // for testing // initialize the progress indicator // sort of pre-redundant to do this here AND in the loop, but, // looks better, if there is a lag before the first response var progressHTML = "

" + pageCount + ""; profileDiv.innerHTML = profileDivOriginalHTML + progressHTML; // download and process Amazon pages var receivedPageWithNoReviews = false; var x = 1; while (x <= pageCount) { (function(x){ var urlComplete = urlStart + x + urlEnd; perPageResponseDiv[x] = document.createElement('div'); GM_xmlhttpRequest({ method: "GET", url: urlComplete, onload: function(response) { // capture incoming data perPageResponseDiv[x].innerHTML = response.responseText; pageResponseCount++; // update the progress indicator var progressHTML = "

" + (pageCount - pageResponseCount) + ""; profileDiv.innerHTML = profileDivOriginalHTML + progressHTML; // get parent of any reviewText DIV var findReviews = document.evaluate("//div[@class='reviewText']/..", perPageResponseDiv[x], null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); // evaluating the doc DIV made above var reviewsOnPage = findReviews.snapshotLength; if(reviewsOnPage == 0) receivedPageWithNoReviews = true; // process each review found on current page pageSetOfTableRows[x] = ""; // initialize each member prior to concatenating for (var j = 0; j < reviewsOnPage; j++) { var oneReview = findReviews.snapshotItem(j); var reviewChildren = oneReview.children; var childCount = reviewChildren.length; var commentCount = 0; var itemTitle = "Produkt nicht verfügbar"; var itemLink = ""; var permaLink = ""; var starRating = 0; var reviewDate = ""; var upVotes = 0; var downVotes = 0; var totalVotes = 0; var itemID = ""; var isAVP = 0; var isVine = 0; // get number of comments, and permalink var tempText = reviewChildren[childCount-2].textContent; if(tempText.indexOf('Kommentar (') > -1 || tempText.indexOf('Kommentare (') > -1) { var paren1 = tempText.indexOf('('); var paren2 = tempText.indexOf(')'); commentCount = tempText.substring(paren1+1,paren2); commentCount = parseInt(commentCount.replace(/\./g, '')); // entferne Tausenderpunkte } var lst = reviewChildren[childCount-2].getElementsByTagName('a'); permaLink = lst[2].getAttribute("href"); // get review wordcount and add to tally tempText = reviewChildren[childCount-3].textContent; tallyWordcount += countWords(tempText); // the data items below do not have reliable positions, due to presence // or not, of vine voice tags, verified purchase, votes, etc. // so, are done in a loop with IF checks. Must start loop just above review // text, in case the reviewer has used any of the phrases I am searching for for (var i = childCount - 4; i > -1; i--) { var childHTML = reviewChildren[i].innerHTML; // get item title and item link var titleClue = childHTML.indexOf('Rezension bezieht sich auf'); if(titleClue > -1) { var lst = reviewChildren[i].getElementsByTagName('a'); itemLink = lst[0].getAttribute("href"); itemTitle = lst[0].textContent; } // get star rating AND review date var ratingClue = childHTML.indexOf('von 5 Sternen'); if(ratingClue > -1) { starRating = childHTML.substring(ratingClue-4,ratingClue-1); reviewDate = reviewChildren[i].lastElementChild.textContent; var lst = reviewDate.split(" "); reviewDate = lst[0].substring(0,3) + " " + lst[1] + " " + lst[2]; } // get vote counts var childText = reviewChildren[i].textContent; var voteClue = childText.indexOf('Kunden fanden die folgende Rezension hilfreich'); if(voteClue > -1) { var list = childText.trim().split(" "); // there were extra, invisible spaces! upVotes = parseInt(list[0].replace(/\./g, '')); // entferne Tausenderpunkte totalVotes = parseInt(list[2].replace(/\./g, '')); downVotes = totalVotes - upVotes; } // check for AVP and Vine var avpClue = childHTML.indexOf('Verifizierter Kauf'); if(avpClue > -1) isAVP = 1; var vineClue = childHTML.indexOf('Vine Kundenrezension eines kostenfreien Produkts'); if(vineClue > -1) isVine = 1; } // get item ID var lst = oneReview.parentNode.getElementsByTagName('a'); itemID = lst[0].getAttribute("name"); // get HTML formatted table row; rows COULD be accumulated in // preOneTableRow; but, since they come in page sets that may be // received out of order, the non-enhanced view (which has no sort, // thus no default sort) would appear out of order pageSetOfTableRows[x] += prepOneTableRow((j+1+(x-1)*10),itemID,itemTitle,permaLink,reviewDate,starRating,upVotes,downVotes,commentCount,totalVotes,isAVP,isVine); reviewsProcessed++; // more reliable than reviewCount, for calculating avg. rating } // clear the response, to save memory -- // could be critical when there are many review pages perPageResponseDiv[x].innerHTML = ""; // see if all data from multiple page loads has arrived if(pageResponseCount==pageCount) { // assemble the sets of table rows, which will be in proper order // rather than order received var completeSetOfTableRows = ""; for(var y=1; y <= pageCount; y++) { completeSetOfTableRows += pageSetOfTableRows[y]; } assembleDisplayBuffers(completeSetOfTableRows, reviewsProcessed); // store info to be used in subsequent run, for change detection GM_setValue("recentItemIDs", newStoreItemIDs.trim()); GM_setValue("recentUpvotes", newStoreUpvotes.trim()); GM_setValue("recentDownvotes", newStoreDownvotes.trim()); GM_setValue("recentComments", newStoreComments.trim()); // replace progress indicator with Tabulate link profileDiv.innerHTML = profileDivOriginalHTML + profileDivTabulateHTML; // show message if any of the received pages contained NO reviews... // SOMETHING was received -- an empty, error, or 'please try again' type page if(receivedPageWithNoReviews) { alert("Eine oder mehr Rezensionenseiten wurden nicht empfangen. \n\nHervorgehobene Änderungen zu Rezensionen stimmen und werden beim nächsten Lauf nicht mehr hervorgehoben. \n\nAlle fehlenden Rezensionen werden im nächsten lauf als neu hervorgehoben."); } // --- display the results if(!GM_config.get('DisplayMode')) GM_openInTab("data:text/html," + encodeURIComponent(primaryDisplayBuffer)); else { document.body.innerHTML = primaryDisplayBuffer; manageColumns(); } } } }); })(x); x++; } } function manageColumns() { if(!GM_config.get('ShowAllVotes')) { document.getElementById("tblMain").classList.toggle("hide7"); if(!showUpdatesOnly) document.getElementById("footer").classList.toggle("hide7"); } if(!GM_config.get('ShowAVP')) { document.getElementById("tblMain").classList.toggle("hide10"); if(!showUpdatesOnly) document.getElementById("footer").classList.toggle("hide10"); } if(!GM_config.get('ShowVine')) { document.getElementById("tblMain").classList.toggle("hide11"); if(!showUpdatesOnly) document.getElementById("footer").classList.toggle("hide11"); } } function countWords(s){ // from 'neokio' on StackOverflow s = s.replace(/\n/g,' '); // newlines to space s = s.replace(/(^\s*)|(\s*$)/gi,''); // remove spaces from start + end s = s.replace(/[ ]{2,}/gi,' '); // 2 or more spaces to 1 return s.split(' ').length; } function invalidValue(oldStoredValue) { if(oldStoredValue === undefined || oldStoredValue == "?") return true; return false; } function checkChange(newStat,oldStat,forFooter) { if(newStat == oldStat || invalidValue(oldStat) === true) return newStat; else { var linkClass = "summaryLink"; if(forFooter) linkClass = "footerLink"; return "" + newStat + ""; } } function toggleView() { showUpdatesOnly = !showUpdatesOnly; if(showUpdatesOnly) document.body.innerHTML = updateDisplayBuffer; else document.body.innerHTML = primaryDisplayBuffer; manageColumns(); } function helpfulPercent(upVotes,downVotes) { var helpfulPercent = ""; upVotes = upVotes; downVotes = downVotes; if(upVotes + downVotes > 0) helpfulPercent = (upVotes/(upVotes+downVotes)*100).toFixed(1); return helpfulPercent; } function prepOneTableRow (row,itemID,itemTitle,permaLink,reviewDate,starRating,upVotes,downVotes,commentCount,totalVotes,isAVP,isVine) { // do these before mangling the values with tags var helpfulPct = helpfulPercent(upVotes,downVotes); itemTitle = "" + itemTitle.substring(0,40) + ""; // keep tallies to use in table footer tallyUpvotes += upVotes; tallyDownvotes += downVotes; tallyAllvotes += totalVotes; tallyStars += parseInt(starRating); tallyComments += commentCount; tallyAVP += isAVP; tallyVine += isVine; // assemble storage info, to use in subsequent run, for change detection newStoreItemIDs += itemID + " "; newStoreUpvotes += upVotes + " "; newStoreDownvotes += downVotes + " "; newStoreComments += commentCount + " "; // see if review for this item has previously been examined var matchIdx = -1; for(var i=0; i -1) { // entry exists; see if any of the numbers have changed if(oldStoreUpvotes[matchIdx] != upVotes) { // for changed number, make it bold, and hilite row // and store previous value for display as tooltip, for mouse hover upVotes = "" + upVotes + ""; hiliteRow = true; } if(oldStoreDownvotes[matchIdx] != downVotes) { downVotes = "" + downVotes + ""; hiliteRow = true; } if(oldStoreComments[matchIdx] != commentCount) { commentCount = "" + commentCount + ""; hiliteRow = true; } } else { // no match, so, it's a new review; bold the title and hilite the row itemTitle = "" + itemTitle + ""; hiliteRow = true; } var tdLeft = ""; var tdRight = ""; if(hiliteRow===true && oldStoreItemIDs[0].length > 0) { tdLeft = ""; tdRight = ""; } var tableRow = "" + tdLeft + row + "" + tdLeft + itemTitle + "" + tdLeft + reviewDate + "" + tdRight + starRating + "" + tdRight + upVotes + "" + tdRight + downVotes + "" + tdRight + totalVotes + "" + tdRight + helpfulPct + "" + tdRight +commentCount + "" + tdRight + ((isAVP > 0) ? "•" : "") + "" + tdRight + ((isVine > 0) ? "•" : "") + ""; return tableRow; } // create Options menu with GM_config var frame = document.createElement('div'); document.body.appendChild(frame); GM_config.init( { 'id': 'MyConfig', // The id used for this instance of GM_config 'title': 'TART Optionen', // Panel Title 'fields': // Fields object { 'DisplayMode': // Line item { 'type': 'checkbox', 'label': 'Erweiterte Ansicht (Abwählen für neuen Tabulator mit weniger Optionen)', 'default': true }, 'FixedFooter': { 'type': 'checkbox', 'label': 'Zeige fixierte Zusammenfassung am Ende der Seite', 'default': true }, 'ShowAllVotes': { 'type': 'checkbox', 'label': 'Zeige Alle-Klicks-Spalte', 'default': true }, 'ShowAVP': { 'type': 'checkbox', 'label': 'Zeige Verifizierter-Kauf-Spalte', 'default': true }, 'ShowVine': { 'type': 'checkbox', 'label': 'Zeige Vine-Spalte', 'default': true }, 'FontSize': { 'label': 'Textgröße', 'type': 'unsigned int', 'size': 2, 'default': 12 }, 'RowPadding': { 'label': 'Zeilenhöhe', 'type': 'unsigned int', 'size': 2, 'default': 10 }, 'HighliteColor': { 'label': 'Hervorhebungsfarbe (6-stelliger Hex-Code)', 'title': 'From graphics program or online color picker', 'type': 'text', 'size': 6, 'default': 'FFFF55' } }, 'events': // Callback functions object { 'open': function() { // style the panel as it's being displayed frame.style.position = "auto"; frame.style.width = "auto"; frame.style.height = "auto"; frame.style.backgroundColor = "#F3F3F3"; frame.style.padding = "10px"; frame.style.borderWidth = "5px"; frame.style.borderStyle = "ridge"; frame.style.borderColor = "gray"; var x = (document.documentElement.clientWidth - frame.offsetWidth) / 2; frame.style.left = x + 'px'; } }, 'frame': frame, // specify the DIV element used for the panel 'css': '#MyConfig .config_header { font-size: 12pt; font-weight:bold; margin-bottom:12px }' + '#MyConfig .field_label { font-size: 12px; font-weight:normal; margin: 0 3px }' }); // event listener to pick up mouse clicks, to run script functions document.addEventListener('click', function(event) { var tempstr = new String(event.target); var quash = false; if(tempstr.indexOf('tabulate') > -1) { tabulate(); quash = true; } if(tempstr.indexOf('options') > -1) { GM_config.open(); quash = true; } if(tempstr.indexOf('toggleView') > -1) { toggleView(); quash = true; } if(quash) { event.stopPropagation(); event.preventDefault(); } }, true); // initiate the script function main() { var findProfileLink = ""; var url = window.location.href; // read previous values for footer and top summary values oldTARTstats = GM_getValue("recentFooterValues", "? ? ? ? ? ? ? ? ? ? ? ? ? ?").split(" "); // Profil-Link suchen findProfileLink = document.evaluate("//a[contains(.,'Mein Profil')]", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); // User-ID extrahieren GM_log("Finding account info..."); var profileLink = findProfileLink.snapshotItem(0).getAttribute("href"); var lst = profileLink.split("/"); userID = lst[4]; GM_log("User ID: " + userID); // find profile info panel var findDiv = document.evaluate("//div[contains(.,'Hilfreiche Bewertungen')]", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); profileDiv = findDiv.snapshotItem(0); // hole die Hilfreich-Klicks lst = profileDiv.textContent.split(" "); helpfulVotes = lst[3].substring(12); GM_log("Helpful Votes: " + helpfulVotes); // get review count var prevSibDiv = profileDiv.previousElementSibling; charIdx = prevSibDiv.textContent.lastIndexOf(':'); reviewCount = prevSibDiv.textContent.substring(charIdx+2); // eventuelle Tausenderpunkte entfernen reviewCount = parseInt(reviewCount.replace(/\./g, '')); GM_log("Review Count: " + reviewCount); // add Tabulate link; also, save content for use with progress indicator profileDivOriginalHTML = profileDiv.innerHTML; profileDiv.innerHTML += profileDivTabulateHTML; // add delta symbol with mouseover note, if there are obvious new values to Tabulate // but, don't show delta on first run, which would have invalid comparison values if((helpfulVotes != oldTARTstats[7] && invalidValue(oldTARTstats[7]) === false)) { profileDiv.innerHTML += " Δ"; } } main(); })(); // End