Greasy Fork

Greasy Fork is available in English.

The Amazon Review Tabulator - TART

Lists all of your reviews with vote and comment tallies, with updates highlighted

当前为 2016-11-01 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

/*=====================================================================================*\
|  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.                               |
\*=====================================================================================*/

// ==UserScript==
// @name           The Amazon Review Tabulator - TART
// @namespace      floyd.scripts
// @version        1.0.8
// @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/*
// @grant          GM_getValue
// @grant          GM_setValue
// @grant          GM_xmlhttpRequest
// @grant          GM_log
// @grant          GM_openInTab
// ==/UserScript==

// Start

(function() {

var userId = "";
var reviewCount = 0;
var reviewerRanking = "";
var helpfulVotes = 0;
var urlStart = "";
var urlEnd = "";

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 = "<br></br><a href='javascript:tabulate();'>Tabulate</a>";

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;

	// set up top of display page
    var displayBuffer = "<!DOCTYPE html><html lang='en'>" +
    "<head><meta charset='utf-8'/><title>TART Amazon Review Details</title><style type='text/css'>" +
    ".tg  {border-collapse:collapse;border-spacing:0;width:100%}" +
    ".tg td{font-family:Arial, sans-serif;font-size:12px;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal}" +
    ".tg th{font-family:Arial, sans-serif;font-size:12px;padding:10px 5px;border-style:solid;border-width:1px;overflow:hidden;word-break:normal}" +
    ".tg .header-left{font-weight:bold;background-color:#010066;color:#ffffff;text-align:left}" +
    ".tg .header-right{font-weight:bold;background-color:#010066;color:#ffffff;text-align:right}" +
    ".tg .cell-left{text-align:left}" +
    ".tg .cell-right{text-align:right}" +
    ".tg .hilite-left{text-align:left;background-color:#FFFF12}" +
    ".tg .hilite-right{text-align:right;background-color:#FFFF12}" +
    ".t-large {font-size:18px;font-weight:bold;font-family:Arial,sans-serif}" +
    ".t-small {font-size:8px;font-family:Arial,sans-serif}" +
    "</style></head><body>" +
    "<span class='t-large'>Amazon Review Details</span><br>" +
    "<span class='t-small'>Prepared with The Amazon Review Tabulator - TART</span>" +
    "<p>Top Reviewer Ranking: " + reviewerRanking + "<br>" +
    "Reviews Available: " + reviewCount + "<br>" +
    "Helpful Votes: " + helpfulVotes + "<br>" +
    "Upvote/Review Ratio: " + (helpfulVotes/reviewCount).toFixed(2) + "</p>" +
    "<table class='tg'>" +
    "<tr><th class='header-left'>#</th><th class='header-left'>Item</th><th class='header-left'>Date</th><th class='header-right'>Stars<br></th><th class='header-right'>Upvotes</th><th class='header-right'>Downvotes</th><th class='header-right'>% Helpful<br></th><th class='header-right'>Comments</th></tr>";
	
	// 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 user ID, ready for review page number
    urlStart = "https://www.amazon.com/gp/cdp/member-reviews/" + userID + "?ie=UTF8&display=public&page=";
    urlEnd = "&sort_by=MostRecentReview";

	// lots of page loading and data retrieval
    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 = "<br></br><b>" + pageCount + "</b>";
    profileDiv.innerHTML = profileDivOriginalHTML + progressHTML;

    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) {
				   
                    // save the incoming data
                    perPageResponseDiv[x].innerHTML = response.responseText;					
                    pageResponseCount++;

                    // update the progress indicator
                    var progressHTML = "<br></br><b>" + (pageCount - pageResponseCount) + "</b>";
                    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

                    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+2,paren2-1);
                            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++) {                      
                            displayBuffer += pageSetOfTableRows[y];
						}
                        
						// add footer and complete the results page
						displayBuffer += "<tr><td class='header-left'></td><td class='header-left'></td><td class='header-left'></td><td class='header-right'>" + (tallyStars/reviewsProcessed).toFixed(1) + "</td><td class='header-right'>" + tallyUpvotes + "</td><td class='header-right'>" + tallyDownvotes + "</td><td class='header-right'>" + helpfulPercent(tallyUpvotes,tallyDownvotes) + "</td><td class='header-right'>" + tallyComments + "</td></tr></table></body></html>";
						
						// 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;
                        
                        // a display alternative -- put results IN the Amazon page
                        //document.body.innerHTML = displayBuffer;
                        
                        // to open new window, user must allow popups for https://www.amazon.com
                        //var resultsWindow = window.open("data:text/html," + encodeURIComponent(displayBuffer), "_blank", "scrollbars=yes");

                        // using GM_openInTab does not require exception to be set by user
                        GM_openInTab("data:text/html," + encodeURIComponent(displayBuffer));
						}
					}
				});
			})(x);
		x++;
	}   
}

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) {
	
	// do these before mangling the values with <b> tags </b>
	var helpfulPct = helpfulPercent(upVotes,downVotes); 
	itemTitle = "<a href='" + permaLink + "' target='_new'>" + itemTitle.substring(0,65) + "</a>";
	
	// keep tallies to use in table footer
	tallyUpvotes += upVotes;
	tallyDownvotes += downVotes;
	tallyStars += parseInt(starRating);
	tallyComments += commentCount;
	
	// 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<oldStoreItemIDs.length; i++) {
		if(oldStoreItemIDs[i] == itemID) {
			// we have a match, an item that has previously been seen
			matchIdx = i;
			break;
		}
	}
			
	var hiliteRow = false;	
	if(matchIdx > -1) {
		// entry exists; see if any of the numbers have changed
		if(oldStoreUpvotes[matchIdx] != upVotes) {
			// for changed number, make it bold, and hilite row
			upVotes = "<b>" + upVotes + "</b>";
			hiliteRow = true;
		}
		if(oldStoreDownvotes[matchIdx] != downVotes) {
			downVotes = "<b>" + downVotes + "</b>";
			hiliteRow = true;
		}
		if(oldStoreComments[matchIdx] != commentCount) {
			commentCount = "<b>" + commentCount + "</b>";
			hiliteRow = true;
		}
	}
	else {
		// no match, so, it's a new review; bold the title and hilite the row
		itemTitle = "<b>" + itemTitle + "</b>";
		hiliteRow = true;		
	}

    var tdLeft = "<td class='cell-left'>";
    var tdRight = "<td class='cell-right'>";
    if(hiliteRow===true && oldStoreItemIDs[0].length > 0) {
        tdLeft = "<td class='hilite-left'>";
        tdRight = "<td class='hilite-right'>";    
    }
	
	var tableRow = "<tr>" + tdLeft + row + "</td>" + tdLeft + itemTitle + "</td>" + tdLeft + reviewDate + "</td>" + tdRight + starRating + "</td>" + tdRight + upVotes + "</td>" + tdRight + downVotes + "</td>" + tdRight + helpfulPct + "</td>" + tdRight + commentCount + "</td></tr>";

return tableRow;
}

document.addEventListener('click', function(event) {
    var tempstr = new String(event.target);

    if(tempstr.indexOf('tabulate') > -1) {
        tabulate();
        event.stopPropagation();
        event.preventDefault();
    }
}, true);

//--- main script block from this point
   
function Tabulate_Amazon_Reviews_Run() {

    // find profile info panel
    var findDiv = document.evaluate("//div[contains(.,'Helpful Votes')]", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
    profileDiv = findDiv.snapshotItem(0);
   
    // get reviewer ranking and user ID
    var lst = profileDiv.getElementsByTagName('a');
    reviewerRanking = lst[0].textContent;
    var charIdx = lst[0].getAttribute("href").indexOf('#');
    userID = lst[0].getAttribute("href").substring(charIdx+1);

    // get helpful votes
    charIdx = profileDiv.textContent.lastIndexOf(':');
    helpfulVotes = profileDiv.textContent.substring(charIdx+2);
   
    // get review count
    var prevSibDiv = profileDiv.previousElementSibling;
    charIdx = prevSibDiv.textContent.lastIndexOf(':');
    reviewCount = prevSibDiv.textContent.substring(charIdx+2);
       
    // add Tabulate link; also, save content for use with progress indicator
    profileDivOriginalHTML = profileDiv.innerHTML;
    profileDiv.innerHTML += profileDivTabulateHTML;
	
}
   
Tabulate_Amazon_Reviews_Run();

})();
// End