/*===========================================================================*\
| The Amazon Review Tabulator - TART |
| (c) 2016 by Another Floyd |
| From your "Public Reviews Written by You" page on Amazon, this script |
| collects and tabulates vote tallies and related information, from all of |
| your Amazon reviews. Click the "Tabulate" link in the "Your Profile" |
| panel. See item on script host menu, on browser toolbar, to activate |
| Alternate Display Mode, which allows toggle between seeing all reviews, or |
| only those with recent activity. |
\*===========================================================================*/
// ==UserScript==
// @name The Amazon Review Tabulator - TART
// @namespace floyd.scripts
// @version 1.1.9
// @author Another Floyd at Amazon.com
// @description Lists all of your reviews with vote and comment tallies, with updates highlighted
// @include https://www.amazon.com/gp/cdp/member-reviews/*
// @include https://www.amazon.co.uk/gp/cdp/member-reviews/*
// @include https://www.amazon.ca/gp/cdp/member-reviews/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @grant GM_log
// @grant GM_openInTab
// @grant GM_registerMenuCommand
// @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
// @downloadURL none
// ==/UserScript==
// Start
(function() {
var enhancedDisplayMode = true;
var showUpdatesOnly = false;
var primaryDisplayBuffer = "";
var updateDisplayBuffer = "";
var userID = "";
var reviewCount = 0;
var reviewerRanking = "";
var helpfulVotes = 0;
var oldStoreItemIDs = [];
var oldStoreUpvotes = [];
var oldStoreDownvotes = [];
var oldStoreComments = [];
var newStoreItemIDs = "";
var newStoreUpvotes = "";
var newStoreDownvotes = "";
var newStoreComments = "";
var tallyUpvotes = 0;
var tallyDownvotes = 0;
var tallyStars = 0;
var tallyComments = 0;
// use this reference for progress indicator
var profileDiv = "";
var profileDivOriginalHTML = "";
var profileDivTabulateHTML = "
Tabulate";
function tabulate() {
// reset global accumulators to ensure that repeated script runs remain clean
newStoreItemIDs = "";
newStoreUpvotes = "";
newStoreDownvotes = "";
newStoreComments = "";
tallyUpvotes = 0;
tallyDownvotes = 0;
tallyStars = 0;
tallyComments = 0;
var today = new Date();
var formattedToday = today.toLocaleDateString('en-US',{month:'long',day:'numeric',year:'numeric'});
var toggleLink = (enhancedDisplayMode) ? "
Toggle View: All Reviews | Updates Only" : "";
// set up top of display page
primaryDisplayBuffer = "" +
"
TART Amazon Review Details" +
"Amazon Review Details
" +
"Prepared with TART - " + formattedToday +
"Reviewer Ranking: " + reviewerRanking + "
" +
"Review Count: " + reviewCount + "
" +
"Helpful Votes: " + helpfulVotes + "
" +
"Upvote/Review Ratio: " + (helpfulVotes/reviewCount).toFixed(2) + toggleLink +
"
" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"" +
"
";
updateDisplayBuffer = primaryDisplayBuffer; // for alternate display mode
// 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 = "com";
var url = window.location.href;
if(url.indexOf('amazon.co.uk/') > -1) tld = "co.uk";
if(url.indexOf('amazon.ca/') > -1) tld = "ca";
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 = 2; // 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 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
// process each review found on current page
pageSetOfTableRows[x] = ""; // initialize each member prior to concatenating
for (var j = 0; j < findReviews.snapshotLength; j++) {
var oneReview = findReviews.snapshotItem(j);
var reviewChildren = oneReview.children;
var childCount = reviewChildren.length;
var commentCount = 0;
var itemTitle = "No Title Available";
var itemLink = "";
var permaLink = "";
var starRating = 0;
var reviewDate = "";
var upVotes = 0;
var downVotes = 0;
var itemID = "";
// get number of comments, and permalink
var tempText = reviewChildren[childCount-2].textContent;
if(tempText.indexOf('Comment (') > -1 || tempText.indexOf('Comments (') > -1) {
var paren1 = tempText.indexOf('(');
var paren2 = tempText.indexOf(')');
commentCount = tempText.substring(paren1+1,paren2);
commentCount = parseInt(commentCount.replace(/,/g, '')); // remove commas
}
var lst = reviewChildren[childCount-2].getElementsByTagName('a');
permaLink = lst[2].getAttribute("href");
// 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; // used 2x, below
// get item title and item link
var titleClue = childHTML.indexOf('This review is from');
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('out of 5 stars');
if(ratingClue > -1) {
starRating = childHTML.substring(ratingClue-4,ratingClue-1);
reviewDate = reviewChildren[i].lastElementChild.textContent;
}
// get vote counts
var childText = reviewChildren[i].textContent;
var voteClue = childText.indexOf('people found the following review helpful');
if(voteClue > -1) {
var list = childText.trim().split(" "); // there were extra, invisible spaces!
upVotes = parseInt(list[0].replace(/,/g, '')); // remove commas
var totalVotes = parseInt(list[2].replace(/,/g, ''));
downVotes = totalVotes - upVotes;
}
}
// get item ID
var lst = oneReview.parentNode.getElementsByTagName('a');
itemID = lst[0].getAttribute("name");
// get HTML formatted table row
pageSetOfTableRows[x] += prepOneTableRow((j+1+(x-1)*10),itemID,itemTitle,permaLink,reviewDate,starRating,upVotes,downVotes,commentCount);
// clear the response, to save memory --
// could be critical when there are many review pages
perPageResponseDiv[x].innerHTML = "";
reviewsProcessed++; // more reliable than reviewCount, for calculating avg. rating
}
// see if all data from multiple page loads has arrived
if(pageResponseCount==pageCount) {
// assemble the sets of table rows
for(var y=1; y <= pageCount; y++) {
primaryDisplayBuffer += pageSetOfTableRows[y];
}
// add footer and complete the results page
var calcStars = (tallyStars/reviewsProcessed).toFixed(1);
var calcHelpfulPct = helpfulPercent(tallyUpvotes,tallyDownvotes);
var newStoreFooter = calcStars + " " + tallyUpvotes + " " + tallyDownvotes + " " + calcHelpfulPct + " " + tallyComments;
var oldStoreFooter = GM_getValue("recentFooterValues", " ").split(" ");
primaryDisplayBuffer += "" +
"" +
"" +
"" +
"" +
"" + "
";
// 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 += "