Netflix Rating Granulizer

By mabuse Last update Feb 9, 2009 — Installed 2,486 times.

There are 5 previous versions of this script.

// ==UserScript==
// @name          Netflix Rating Granulizer
// @description   allows half star user ratings on Netflix
// @author        mabuse
// @include       http://*netflix.com/*
// ==/UserScript==

var document = unsafeWindow.document;


/* setting "overflow: visible" _dramatically_ improves performance; netflix's "overflow: hidden" isn't necessary */
var head = document.getElementsByTagName('head')[0];
if (head)
{
    var style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = '.stbrIl li a, .stbrOl li a, .stbrStc li a { overflow: visible; }';
    head.appendChild(style);
}


var ratingStrings = [];
ratingStrings[1.5] = "Craptastic";
ratingStrings[2.5] = "Mediocre";
ratingStrings[3.5] = "Pretty pretty good";
ratingStrings[4.5] = "The shit, for reals";

var ratingWidths = [];
ratingWidths[1.5] = 27;
ratingWidths[2.5] = 46;
ratingWidths[3.5] = 65;
ratingWidths[4.5] = 84;

var niOffset = 17;  /* rating width offset when not interested button is to the left */



/**
 * Patches the sbHandler javascript object which netflix still uses on a few pages (instead of the
 * more accessible HTML/CSS approach).
 */
function patchSbHandler()
{
    var sbHandler = unsafeWindow.sbHandler;
    if (!sbHandler)
        return;

    sbHandler.sbOffsets = [18,27,37,46,56,65,75,84,94];

    sbHandler.displayStrings[1.5] = ratingStrings[1.5];
    sbHandler.displayStrings[2.5] = ratingStrings[2.5];
    sbHandler.displayStrings[3.5] = ratingStrings[3.5];
    sbHandler.displayStrings[4.5] = ratingStrings[4.5];

    for (var i=2; i<11; i++)
    {
        sbHandler.sbImages[i/2] = new Image();
        sbHandler.sbImages[i/2].src = sbHandler.imageRoot+"stars_2_"+(Math.floor(i/2))+(i%2?"5":"0")+".gif";
    }

    sbHandler.getStarCount=function(evt)
    {
        var x = unsafeWindow.getElementMouseCoordinate(evt,this.element);
        for (var i=0; i<10; i++)
            if (x <= this.sbOffsets[i])
                return (i+2)/2;
        return 0;
    };
}

/**
 * Patches UL elements having the given class name by adding LI child elements with half-star
 * rating widths among the existing elements.
 */
function patchUls(className, offset)
{
    var uls = document.getElementsByClassName(className);

    var hrefRegex = new RegExp('value=.');

    for (var i=uls.length-1; i >=0; i--)
    {
        var ul = uls[i];
        var lis = ul.childNodes;
        if (ul.childNodes.length <= 6)  /* only do once */
        {
            for (var j=4; j > 0; j--)
            {
                var rating = (5-j)+0.5;

                var currentLi = lis[j];
                var newLi = document.createElement('li');
                newLi.setAttribute('class', 'rv'+rating);   /* some netflix javascript parses this class name */

                var newAnchor = document.createElement('a');
                newLi.appendChild(newAnchor);
                
                newAnchor.href = currentLi.childNodes[0].href.replace(hrefRegex, 'value='+rating);
                newAnchor.rel = 'nofollow';
                newAnchor.title = 'Click to rate the movie "'+ratingStrings[rating]+'"';
                newAnchor.innerHTML = 'Rate '+rating+' stars';
                newAnchor.setAttribute('style', 'width:'+(ratingWidths[rating]+offset)+'px');

                ul.insertBefore(newLi, currentLi);
            }
        }
    }
}

/**
 * Patches elements displaying your current rating by setting half-star rating widths.
 */
function patchRated()
{
    var maskFgs = document.getElementsByClassName('stbrMaskFg');
    for (var i = maskFgs.length-1; i >= 0; i--)
    {
        var maskFg = maskFgs[i];
        if (maskFg.childNodes.length)
        {
            var text = maskFg.childNodes[0].data;
            if (text.indexOf('You') == 0)
            {
                var rating = text.replace(/.*: ([.0-9]+)/, '$1')*1.0;
                if (~~rating != rating)
                {
                    var offset = 0;
                    if (maskFg.parentNode.className.indexOf('stbrMaskBgIlNi') >= 0)
                        offset = niOffset;
                    maskFg.style.width = (ratingWidths[rating]+offset)+'px';
                }
            }
        }
    }
}



if (unsafeWindow.sbHandler)
    patchSbHandler();


var startTime = new Date();

patchUls('stbrIlNi', niOffset);
patchUls('stbrIl', 0);
patchUls('stbrOl', 0);
patchUls('stbrStc', 0);

var endTime = new Date();

if (unsafeWindow.console)
    unsafeWindow.console.log('patch time = ' + (endTime-startTime) + 'ms');


/* let DOM changes render before patching rated elements */
setTimeout(patchRated, 0);