Source for "Google Video Comments"

By recover
Has no other scripts.


Add Syntax Highlighting (this will take a few seconds, probably freezing your browser while it works)

// ==UserScript==
// @name         Google Video Comments
// @namespace    http://code.google.com/p/gvidcomments/
// @description  Displays information about the comments your videos have received in your Google Video Status pages. If there are any new comments since your last check, it will notify you. Revision 25.
// @include      http*://*google.tld/video/upload/Status*
// @include      http*://*google.tld/video/upload/Stats*
// @include      http*://upload.video.google.tld/Status*
// @include      http*://upload.video.google.tld/Stats*
// ==/UserScript==
//    Copyright (C) 2008  Stefan Sundin (recover89@gmail.com)
//    
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//    
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//    
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
//    
//  Documentation:
//   Displays information about the comments your videos have received
//   in your Google Video Status pages. If there are any new comments
//   since your last check, it will notify you.
//   
//   Ideas for future revisions:
//   * See http://code.google.com/p/gvidcomments/issues/list

function capitalize(str) {
	return str.charAt(0).toUpperCase() + str.substr(1);
}

function closure(callback,arg1,arg2,arg3,arg4,arg5) {
	if (arguments.length == 2) {
		callback(arg1);
	}
	else if (arguments.length == 3) {
		callback(arg1,arg2);
	}
	else if (arguments.length == 4) {
		callback(arg1,arg2,arg3);
	}
}

function clone(obj) {
	var objectClone = new obj.constructor();
	for (var property in obj) {
		if (typeof obj[property] == 'object') {
			objectClone[property] = clone(obj[property]);
		}
		else {
			objectClone[property] = obj[property];
		}
	}
	return objectClone;
}

function getFullMonth(date) {
	var d=date.getMonth();
	d++;
	if (d <= 9) { return '0'+d; }
	else { return d; }
}

function getFullDate(date) {
	var d=date.getDate();
	if (d <= 9) { return '0'+d; }
	else { return d; }
}

function getFullHours(date) {
	var d=date.getHours();
	if (d <= 9) { return '0'+d; }
	else { return d; }
}

function getFullMinutes(date) {
	var d=date.getMinutes();
	if (d <= 9) { return '0'+d; }
	else { return d; }
}

function stopPropagation(e) {
	e.stopPropagation();
}

//from json.org
json_fromString = function() {
    var at,ch,escapee = {
            '"':  '"',
            '\\': '\\',
            '/':  '/',
            b:    '\b',
            f:    '\f',
            n:    '\n',
            r:    '\r',
            t:    '\t'
        },text,error = function (m) {
            throw {
                name:    'SyntaxError',
                message: m,
                at:      at,
                text:    text
            };
        },next = function (c) {
            if (c && c !== ch) {
                error("Expected '" + c + "' instead of '" + ch + "'");
            }
            ch = text.charAt(at);
            at += 1;
            return ch;
        },number = function () {
            var number,
                string = '';

            if (ch === '-') {
                string = '-';
                next('-');
            }
            while (ch >= '0' && ch <= '9') {
                string += ch;
                next();
            }
            if (ch === '.') {
                string += '.';
                while (next() && ch >= '0' && ch <= '9') {
                    string += ch;
                }
            }
            if (ch === 'e' || ch === 'E') {
                string += ch;
                next();
                if (ch === '-' || ch === '+') {
                    string += ch;
                    next();
                }
                while (ch >= '0' && ch <= '9') {
                    string += ch;
                    next();
                }
            }
            number = +string;
            if (isNaN(number)) {
                error("Bad number");
            } else {
                return number;
            }
        },string = function () {
            var hex,
                i,
                string = '',
                uffff;
            if (ch === '"') {
                while (next()) {
                    if (ch === '"') {
                        next();
                        return string;
                    } else if (ch === '\\') {
                        next();
                        if (ch === 'u') {
                            uffff = 0;
                            for (i = 0; i < 4; i += 1) {
                                hex = parseInt(next(), 16);
                                if (!isFinite(hex)) {
                                    break;
                                }
                                uffff = uffff * 16 + hex;
                            }
                            string += String.fromCharCode(uffff);
                        } else if (typeof escapee[ch] === 'string') {
                            string += escapee[ch];
                        } else {
                            break;
                        }
                    } else {
                        string += ch;
                    }
                }
            }
            error("Bad string");
        },white = function () {
            while (ch && ch <= ' ') {
                next();
            }
        },word = function () {
            switch (ch) {
            case 't':
                next('t');
                next('r');
                next('u');
                next('e');
                return true;
            case 'f':
                next('f');
                next('a');
                next('l');
                next('s');
                next('e');
                return false;
            case 'n':
                next('n');
                next('u');
                next('l');
                next('l');
                return null;
            }
            error("Unexpected '" + ch + "'");
        },value,array = function () {
            var array = [];
            if (ch === '[') {
                next('[');
                white();
                if (ch === ']') {
                    next(']');
                    return array;   // empty array
                }
                while (ch) {
                    array.push(value());
                    white();
                    if (ch === ']') {
                        next(']');
                        return array;
                    }
                    next(',');
                    white();
                }
            }
            error("Bad array");
        },object = function () {
            var key,
                object = {};

            if (ch === '{') {
                next('{');
                white();
                if (ch === '}') {
                    next('}');
                    return object;
                }
                while (ch) {
                    key = string();
                    white();
                    next(':');
                    object[key] = value();
                    white();
                    if (ch === '}') {
                        next('}');
                        return object;
                    }
                    next(',');
                    white();
                }
            }
            error("Bad object");
        };
    value = function () {
        white();
        switch (ch) {
        case '{':
            return object();
        case '[':
            return array();
        case '"':
            return string();
        case '-':
            return number();
        default:
            return ch >= '0' && ch <= '9' ? number() : word();
        }
    };
    return function (source, reviver) {
        var result;
        text = source;
        at = 0;
        ch = ' ';
        result = value();
        white();
        if (ch) {
            error("Syntax error");
        }
        return typeof reviver === 'function' ?
            function walk(holder, key) {
                var k, v, value = holder[key];
                if (value && typeof value === 'object') {
                    for (k in value) {
                        if (Object.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }({'': result}, '') : result;
    };
}();

//from MDC
function json_toString(aJSObject, aKeysToDrop) {
    var pieces = [];
    function append_piece(aObj) {
      if (typeof aObj == "string") {
        aObj = aObj.replace(/[\\"\x00-\x1F\u0080-\uFFFF]/g, function($0) {
          switch ($0) {
          case "\b": return "\\b";
          case "\t": return "\\t";
          case "\n": return "\\n";
          case "\f": return "\\f";
          case "\r": return "\\r";
          case '"':  return '\\"';
          case "\\": return "\\\\";
          }
          return "\\u" + ("0000" + $0.charCodeAt(0).toString(16)).slice(-4);
        });
        pieces.push('"' + aObj + '"')
      }
      else if (typeof aObj == "boolean") {
        pieces.push(aObj ? "true" : "false");
      }
      else if (typeof aObj == "number" && isFinite(aObj)) {
        pieces.push(aObj.toString());
      }
      else if (aObj === null) {
        pieces.push("null");
      }
      else if (aObj instanceof Array ||
               typeof aObj == "object" && "length" in aObj &&
               (aObj.length === 0 || aObj[aObj.length - 1] !== undefined)) {
        pieces.push("[");
        for (var i = 0; i < aObj.length; i++) {
          arguments.callee(aObj[i]);
          pieces.push(",");
        }
        if (aObj.length > 0)
          pieces.pop();
        pieces.push("]");
      }
      else if (typeof aObj == "object") {
        pieces.push("{");
        for (var key in aObj) {
          if (aKeysToDrop && aKeysToDrop.indexOf(key) != -1)
            continue;
          arguments.callee(key.toString());
          pieces.push(":");
          arguments.callee(aObj[key]);
          pieces.push(",");
        }
        if (pieces[pieces.length - 1] == ",")
          pieces.pop();
        pieces.push("}");
      }
      else {
        throw new TypeError("No JSON representation for this object!");
      }
    }
    append_piece(aJSObject);
    return pieces.join("");
}

//Data
var images={
	x:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMBAMAAACkW0HUAAAALVBMVEUAABE9ZpFJb5dyjq7v8vZyjq/t8fXq7/NNcZhLcJihtcpzj6+kt8umuc3///+rkeKYAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfXCRoTDABDjgGYAAAAP0lEQVQI12NgbhQUlDBgiKp79+75UgaVd0DgxCD3EASBlKAgiHonKPgOQUEFIUqgGvbkvXv37CoD80RBQUkDALGOI1+GXF1SAAAAAElFTkSuQmCC', //http://img0.gmodules.com/ig/images/skins/winterscape/common/x_blue.gif
	x_highlight:'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMBAMAAACkW0HUAAAAIVBMVEUAACX///9Ca5VDa5ZZf6dYf6hDbJZZf6hagKlZgKg9ZpHHKAgpAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfXCRoTDAqjW+iGAAAAPUlEQVQI12NgyVq1apkDQ9UqIFjOoAWiFjGsWii1UGoVkBIUBFGrBAVXISioIEQJVEMXiFrJwGq1atXiAADJ8SNFlwHa2wAAAABJRU5ErkJggg==', //http://img0.gmodules.com/ig/images/skins/winterscape/common/x_blue_highlight.gif
	max: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMBAMAAACkW0HUAAAALVBMVEUAABE9ZpFJb5dyjq7v8vZyjq/t8fXq7/NNcZhLcJihtcpzj6+kt8umuc3///+rkeKYAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfXChERLB6/IDHmAAAAO0lEQVQI12NgbhQUlDBgiKp79+75UgaVd0DgxCD37iEQwamHgoJyCApFDqphT967d8+uMjBPFBSUNAAAQqUm05WARH4AAAAASUVORK5CYII=', //http://img0.gmodules.com/ig/images/skins/winterscape/common/max_blue.gif
	max_highlight: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMBAMAAACkW0HUAAAAIVBMVEUAACX///9Ca5VDa5ZZf6dYf6hDbJZZf6hagKlZgKg9ZpHHKAgpAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfXChERLCUOK9jCAAAAOElEQVQI12NgyVq1apkDQ9UqIFjOoAWiFjGsWrVQatUqOLVQUFAKQaHIQTV0gaiVDKxWq1YtDgAALmglqdzTtMoAAAAASUVORK5CYII=', //http://img0.gmodules.com/ig/images/skins/winterscape/common/max_blue_highlight.gif
	min: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMBAMAAACkW0HUAAAALVBMVEUAABE9ZpFJb5dyjq7v8vZyjq/t8fXq7/NNcZhLcJihtcpzj6+kt8umuc3///+rkeKYAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfXChERMxnsHqrbAAAAOklEQVQI12NgbhQUlDBgiKp79+75UgaVd0DgxCAHoh7CqYeCgnIICkUOqmFP3rt3z64yME8UFJQ0AADSJypH3N2URAAAAABJRU5ErkJggg==', //http://img0.gmodules.com/ig/images/skins/winterscape/common/min_blue.gif
	min_highlight: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMBAMAAACkW0HUAAAAIVBMVEUAACX///9Ca5VDa5ZZf6dYf6hDbJZZf6hagKlZgKg9ZpHHKAgpAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfXChERMx3rc27CAAAANElEQVQI12NgyVq1apkDQ9UqIFjOoAWiFjGsAgM4tVBQUApBochBNXSBqJUMrFarVi0OAACRwigNX3OIHQAAAABJRU5ErkJggg==', //http://img0.gmodules.com/ig/images/skins/winterscape/common/min_blue_highlight.gif
	update: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMBAMAAACkW0HUAAAAAXNSR0IArs4c6QAAAC1QTFRFAAAA////PWaRSW+Xco6u7/L2co6v7fH16u/zTXGYS3CYobXKc4+vpLfLprnN6jc8jAAAAAF0Uk5TAEDm2GYAAAABYktHRACIBR1IAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1wsbDxcQIE+0dwAAAD5JREFUCNdjYFmkpKTlwHC2UFBQ/BmDqSAQBDMoCgoBEZBSUgJRQkpKikIIQWQKqiG7UVBQYhsDyyQlJU0HAAO+C8zeBcuuAAAAAElFTkSuQmCC', //http://img0.gmodules.com/ig/images/skins/winterscape/common/arrow_blue.png
	update_highlight: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMBAMAAACkW0HUAAAAAXNSR0IArs4c6QAAACFQTFRFAAAEPWaR////QmuVQ2uWWX+nWH+oQ2yWWX+oWoCpWYCo5G5hqwAAAAF0Uk5TAEDm2GYAAAABYktHRACIBR1IAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1wsbDxYwAjql/gAAADpJREFUCNdjYHMUFBRJYJgoCARSDIYgSphBUFBIUVAQSCkpgSghJSVFQYQgMgXV0AiiJBhYCwUFxQMARBgHthtEeY8AAAAASUVORK5CYII=', //http://img0.gmodules.com/ig/images/skins/winterscape/common/arrow_blue_highlight.png
	about: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMBAMAAACkW0HUAAAAAXNSR0IArs4c6QAAAC1QTFRFAAAAPWaRSW+Xco6u7/L2co6v7fH16u/zTXGYS3CYobXKc4+vpLfLprnN////RptuVQAAAAF0Uk5TAEDm2GYAAAABYktHRACIBR1IAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH1wwOExAgiQwztQAAAEVJREFUCNdjYG4UFJQwYIiqe/fu+VIGlXdA4MQg9xAEGeQEgQCDeigoCJJ7Jyj47iFQw0M5oIY9ee/ePbvKwDxRUFDSAADigx4xlksa7gAAAABJRU5ErkJggg==', //bitcons/heart.gif
	about_highlight: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAHdElNRQfXDBYNIyIQfMajAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAa0lEQVQoz2P4//8/Q2T98v/OOdP+26ZNxIpBciA1ILVAxStwKkTHILUMTtlTidYAUsuALAADuPggzIAuiQsMgAZ8mrD6AZcmdHmswYpLMThYoxpWEh9xDcCIA0V3BDAGnbPxJA2gHEgNSC0A5PyKKqprvPoAAAAASUVORK5CYII=', //bitcons/heart.gif
	arrow: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAMCAYAAABBV8wuAAAAB3RJTUUH1woFFw8tZa+b6wAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAC1JREFUGJVjYMAO/mMVBAEGZAwT/I9L8D8uwf+4BPFL4DQKr+VYnYvTg7iCBADthKdZsHJhYQAAAABJRU5ErkJggg==',
	starLittle: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAJCAMAAAA8eE0hAAAAPFBMVEUAAADx59Ho17P/0wD/zgD/wwDawIb6ugHRsGjnqgXNqVnaoxfIoUrEmTvFlSq/kSvBjhvCiwu2gQ3u7u5vpfudAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfXCgUXAAHQ7+vHAAAAPklEQVQIHQXBCQJAMAwAsFQxps79/68SUADiC4Bt30CvOvKo6sTVMrNdgbiXXO4BznmfTxDvsz7vgKkPo0/8SEMB0HwCA6EAAAAASUVORK5CYII=', //http://video.google.com/images/starLittle.gif
	starLittleEmpty: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAJCAMAAAA8eE0hAAAAPFBMVEUAAADu7u7m6Ork5OTW2NrR0tLGx8q2triur7Kkpaabm56Vl5mSkpWIiYyHiIt/gIN/gIJ9foFtbnHu8PLzYnzdAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfXCgUWOzHLRl5kAAAAPklEQVQIHQXBCQJAMAwAsNScU4z+/68SkADa1wCO/QA986wzs9PutarWuyGeueYnwDXt0wXifbcxwNJD9IUfVM8B3pe248AAAAAASUVORK5CYII=', //http://video.google.com/images/starLittleEmpty.gif
	starLittleHalf: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAJCAMAAAA8eE0hAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAABpUExURXh5fv////X19e7u7ujp7vLnz+Hh4t/c0+nWr//YAP/TAM3Nz//HANu+f/+9APG+A9OuYOqqA86mUN+iEMqeQJ2go8WVMMiTIJGTlZiRfsGNIMSLEMWIAIeJjL6CAX+AhX1/gnZ3fHN1eCQKyv8AAAABdFJOUwBA5thmAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9cLAhcaIxUVQfEAAABISURBVAgdBcGJAkJAFADA2fSKDhu20qHw/x9pBhQA8TsC3LoX6EsZ8n2aRuLZNjnt/wfE+5LTWoHHqUsziO/nuiwV1H3YjWc2i14DLH32UawAAAAASUVORK5CYII=', //http://video.google.com/images/starLittleHalf.gif
};
var gvidcomments_rev='25';
var defaultdata={
	fullsize: false,
	videos: {}
};
var data=eval(GM_getValue('data')) || clone(defaultdata);
var session={
	openDocid: null,
	openId: null,
	numvideos: 0,
	numcomments: 0,
	newcomments: false,
	loading: true,
	error: false,
	communityrating: {loading:true, error:false},
	page: window.location.pathname.replace(/.*\//,''),
	videos: {},
	docids: []
};

function update() {
	GM_log('Checking for updates to Google Video Comments. Current revision '+gvidcomments_rev+'.');
	GM_xmlhttpRequest({
		method:'GET',
		url:'http://gvidcomments.googlecode.com/svn/trunk/gvidcomments-revision',
		headers:{
			'User-agent':'Mozilla/4.0 (compatible) Greasemonkey',
			'Accept':'application/atom+xml,application/xml,text/xml'
		},
		onload:function(responseDetails) {
			if (responseDetails.status == 200) {
				if (responseDetails.responseText.replace(/\r|\n/g,'') != gvidcomments_rev) {
					if (confirm('There is an update available!\nDo you want to install it?')) {
						window.location.assign('http://gvidcomments.googlecode.com/svn/trunk/gvidcomments.user.js');
					}
				}
				else {
					alert('Nothing new :/');
				}
			} else {
				var error='There was a problem checking for an update: '+responseDetails.status+' ('+responseDetails.statusText+')';
				GM_log(error);
				alert(error);
			}
		}
	});
}

function about() {
	alert('Google Video Comments - Revision '+gvidcomments_rev+'\nUserscript by recover89@gmail.com\nRead all about it at: http://code.google.com/p/gvidcomments/\n\nLicensed under GPL.');
}

function reset() {
	if (confirm('Are you sure you want to reset all stored data?')) {
		data=clone(defaultdata);
		GM_setValue('data',uneval(data));
		alert('Done. Refresh to see changes.');
	}
}

function resize() {
	var cp=document.getElementById('gvidcomments-commentspopup');
	if (cp.style.display == 'block') {
		var s=session.videos[session.openDocid];
		var cb=document.getElementById('gvidcomments-commentsbox');
		var arrow=document.getElementById('gvidcomments-arrow');
		var commentTd=document.getElementById('gvidcomments-td-'+s.videoId);
		
		//Reposition comments popup
		cp.style.left=(commentTd.offsetParent.offsetLeft+commentTd.offsetLeft+commentTd.offsetWidth+8)+'px';
		if (data.fullsize) {
			cp.style.height=(window.innerHeight-10)+'px';
			cp.style.width=(window.innerWidth-parseInt(cp.style.left)-20)+'px';
			cb.style.height=(parseInt(cp.style.height)-cb.offsetTop-10)+'px';
		}

		//Reposition arrow image
		arrow.style.left=(commentTd.offsetParent.offsetLeft+commentTd.offsetLeft+commentTd.offsetWidth+3)+'px';
		arrow.style.top=(commentTd.offsetParent.offsetTop+commentTd.offsetTop+(commentTd.offsetHeight/2)-6)+'px';
	}
}

function opencp(docid) {
	//Set session
	session.openDocid=docid;
	var s=session.videos[docid];
	session.openId=s.videoId;
	
	//Get objects
	var cp=document.getElementById('gvidcomments-commentspopup');
	var cb=document.getElementById('gvidcomments-commentsbox');
	var arrow=document.getElementById('gvidcomments-arrow');
	var header=document.getElementById('gvidcomments-header');
	var communityrating=document.getElementById('gvidcomments-communityrating');
	var commentTd=document.getElementById('gvidcomments-td-'+s.videoId);
	
	//Clear comments box
	while (cb.hasChildNodes()) { cb.removeChild(cb.firstChild); }
	
	//Hide comments box (avoid the tearing effect if its visible)
	//Note: This prevents the comments box from scrolling to the top (bad)
	//cp.style.display='none';
	arrow.style.display='none';
	
	//Reposition comments popup
	//commentTd.offsetParent.offsetTop = the height from the top of the page to the table
	//commentTd.offsetTop = the height from the top of the table to the column
	cp.style.left=(commentTd.offsetParent.offsetLeft+commentTd.offsetLeft+commentTd.offsetWidth+8)+'px';
	if (data.fullsize) {
		cp.style.position='fixed';
		cp.style.top='5px';
		cp.style.height=(window.innerHeight-10)+'px';
		cp.style.width=(window.innerWidth-parseInt(cp.style.left)-20)+'px';
	}
	else {
		cp.style.position='absolute';
		if (commentTd.offsetParent.offsetTop+commentTd.offsetTop-20 < window.pageYOffset) {
			//This happens if the popup would stick up over the edge of the screen (so the top of it wouldn't normally be seen)
			cp.style.top=(window.pageYOffset+5)+'px';
		}
		else if (commentTd.offsetParent.offsetTop+commentTd.offsetTop-20+parseInt(cp.style.height) > window.pageYOffset+window.innerHeight) {
			//This happens if the popup would stick down below the screen contents (so the bottom of it wouldn't normally be seen)
			cp.style.top=(window.pageYOffset+window.innerHeight-parseInt(cp.style.height)-5)+'px';
		}
		else {
			//This happens if the popup will be entirely visibile in its normal position relative to the position of the column
			cp.style.top=(commentTd.offsetParent.offsetTop+commentTd.offsetTop-20)+'px';
		}
	}
	cp.style.display='block';
	
	//Reposition arrow image
	arrow.style.left=(commentTd.offsetParent.offsetLeft+commentTd.offsetLeft+commentTd.offsetWidth+3)+'px';
	arrow.style.top=(commentTd.offsetParent.offsetTop+commentTd.offsetTop+(commentTd.offsetHeight/2)-6)+'px';
	arrow.style.display='block';
	
	//Edit header text
	header.innerHTML=s.title;
	header.title='docid: '+docid;
	
	//Clear communityrating
	while (communityrating.hasChildNodes()) { communityrating.removeChild(communityrating.firstChild); }
	communityrating.title='';
	//Put communityrating
	if (session.communityrating.loading) {
		communityrating.appendChild(document.createTextNode('Fetching ratings...'));
	}
	else if (session.communityrating.error) {
		communityrating.appendChild(document.createTextNode('Error fetching ratings...'));
	}
	else {
		//Get communityrating
		var rating=s.communityrating.rating;
		var numvotes=s.communityrating.numvotes;
		
		//Put stars
		for (var j=0; j < parseInt(rating); j++) {
			var star=document.createElement('img');
			star.src=images.starLittle;
			star.alt='*';
			communityrating.appendChild(star);
		}
		if (rating%1 > 0.25) {
			var star=document.createElement('img');
			star.src=images.starLittleHalf;
			star.alt="'";
			communityrating.appendChild(star);
			j++;
		}
		for (; j < 5; j++) {
			var star=document.createElement('img');
			star.src=images.starLittleEmpty;
			communityrating.appendChild(star);
		}
		
		//Put text
		communityrating.title=rating;
		communityrating.appendChild(document.createTextNode(' ('+numvotes+' rating'+(numvotes!=1?'s':'')+')'));
	}
	
	//Adjust the height of the comments box
	cb.style.height=(parseInt(cp.style.height)-cb.offsetTop-10)+'px';
	
	//Add the comments
	if (s.failed) {
		cb.appendChild(document.createTextNode(s.failed));
	}
	else if (session.error) {
		cb.appendChild(document.createTextNode(session.error));
	}
	else if (session.loading) {
		cb.appendChild(document.createTextNode('Loading comments...'));
	}
	else if (s.numcomments == 0) {
		cb.appendChild(document.createTextNode('No comments'));
	}
	else {
		s.comments.forEach(function(entry) {
			//Add name
			var b=document.createElement('b');
			b.innerHTML=entry.name;
			b.title=entry.id;
			cb.appendChild(b);
			
			//New comment?
			if (s.newids.some(function(newid) {
				if (newid == entry.id) {
					return true;
				}
			})) {
				var sup=document.createElement('sup');
				sup.style.color='rgb(255,0,0)';
				sup.appendChild(document.createTextNode(' new!'));
				cb.appendChild(sup);
			}
			cb.appendChild(document.createElement('br'));
			
			//Add date
			var date=new Date(entry.date*1000);
			var textdate=document.createElement('span');
			textdate.style.color='rgb(0,128,0)';
			textdate.appendChild(document.createTextNode(' '+capitalize(date.toLocaleFormat("%b %d, %Y"))));
			textdate.title=date.toLocaleFormat("%Y-%m-%d %H:%M");
			cb.appendChild(textdate);
			
			//Add text
			cb.appendChild(document.createElement('br'));
			var text=document.createElement('span');
			text.innerHTML=entry.text;
			cb.appendChild(text);
			
			//Add br
			cb.appendChild(document.createElement('br'));
			cb.appendChild(document.createElement('br'));
		});
	}
}

function closecp() {
	//Set session
	session.openDocid=null;
	session.openId=null;
	
	//Get objects
	var cp=document.getElementById('gvidcomments-commentspopup');
	var arrow=document.getElementById('gvidcomments-arrow');
	
	//Hide
	cp.style.display='none';
	arrow.style.display='none';
}

function addclick(docid) {
	var commentTd=document.getElementById('gvidcomments-td-'+session.videos[docid].videoId);
	commentTd.addEventListener('click', function(){ closure(opencp,docid); }, false);
	commentTd.addEventListener('click', stopPropagation, false);
}

(function() {
	//Add menu commands
	GM_registerMenuCommand('About gvidcomments',about);
	GM_registerMenuCommand('Update gvidcomments',update);
	GM_registerMenuCommand('Reset data',reset);
	
	//Grab report table
	var videoTable=document.getElementById('report');
	if (session.page == 'Stats') {
		videoTable.width=parseInt(videoTable.width)+100;
	}
	
	//Add comments header
	var tableTitleTr=videoTable.getElementsByTagName('tr')[0];
	var commentsTitle=document.createElement('td');
	commentsTitle.id='gvidcomments-commentstitle';
	if (session.page == 'Stats') {
		commentsTitle.style.textAlign='right';
	}
	commentsTitle.style.cursor='pointer';
	commentsTitle.title='About gvidcomments';
	commentsTitle.addEventListener('click', about, false);
	commentsTitle.appendChild(document.createTextNode('Comments'));
	tableTitleTr.insertBefore(commentsTitle,tableTitleTr.getElementsByTagName('td')[3]);
	
	//Add total footer
	if (session.page == 'Stats') {
		var tableFooterTr=videoTable.getElementsByTagName('tr')[videoTable.getElementsByTagName('tr').length-1];
		var commentsTotal=document.createElement('td');
		commentsTotal.id='gvidcomments-commentstotal';
		commentsTotal.style.textAlign='right';
		commentsTotal.appendChild(document.createTextNode('0'));
		tableFooterTr.insertBefore(commentsTotal,tableFooterTr.getElementsByTagName('td')[3]);
	}
	
	//Create comments popup
	var cp=document.createElement('div');
	cp.id='gvidcomments-commentspopup';
	cp.style.position='absolute';
	cp.style.width='275px';
	cp.style.height='420px';
	cp.style.background='rgb(255,255,255)';
	cp.style.border='1px solid rgb(0,0,0)';
	cp.style.padding='5px';
	cp.style.MozBorderRadius='10px';
	cp.style.display='none';
	document.body.appendChild(cp);
	
	//Create arrow image
	var arrow=document.createElement('img');
	arrow.id='gvidcomments-arrow';
	arrow.src=images.arrow;
	arrow.style.position='absolute';
	arrow.style.display='none';
	document.body.appendChild(arrow);
	
	//Make comments popup hide when clicking outside the popup
	document.addEventListener('click', closecp, false);
	cp.addEventListener('click', stopPropagation, false);
	
	//Don't hide comments popup when clicking links outside the popup
	var iterator=document.evaluate('//a', document, null, XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null);
	var thisNode=iterator.iterateNext();
	while (thisNode) {
		thisNode.addEventListener('click', stopPropagation, false);
		thisNode=iterator.iterateNext();
	}
	
	//Add close button to comments popup
	var button=document.createElement('img');
	button.title='Close';
	button.src=images.x;
	button.style.cssFloat='right';
	button.style.marginRight='3px';
	button.style.cursor='pointer';
	button.addEventListener('mouseover', function(){ this.src=images.x_highlight; }, false);
	button.addEventListener('mouseout', function(){ this.src=images.x; }, false);
	button.addEventListener('click', closecp, false);
	cp.appendChild(button);
	
	//Add togglesize button to comments popup
	var button=document.createElement('img');
	button.title='Toggle size';
	button.src=images[data.fullsize?'min':'max'];
	button.style.cssFloat='right';
	button.style.marginRight='3px';
	button.style.cursor='pointer';
	button.addEventListener('mouseover', function(){ this.src=images[(data.fullsize?'min':'max')+'_highlight']; }, false);
	button.addEventListener('mouseout', function(){ this.src=images[data.fullsize?'min':'max']; }, false);
	button.addEventListener('click', function(){
		//Toggle
		data.fullsize=data.fullsize?false:true;
		GM_setValue('data',uneval(data));
		this.src=images[data.fullsize?'min':'max'];
		//Reposition
		var s=session.videos[session.openDocid];
		var commentTd=document.getElementById('gvidcomments-td-'+s.videoId);
		if (data.fullsize) {
			cp.style.position='fixed';
			cp.style.top='5px';
			cp.style.width=(window.innerWidth-parseInt(cp.style.left)-20)+'px';
			cp.style.height=(window.innerHeight-10)+'px';
		}
		else {
			cp.style.position='absolute';
			cp.style.width='275px';
			cp.style.height='420px';
			if (commentTd.offsetParent.offsetTop+commentTd.offsetTop-20 < window.pageYOffset) {
				cp.style.top=(window.pageYOffset+5)+'px';
			}
			else if (commentTd.offsetParent.offsetTop+commentTd.offsetTop-20+parseInt(cp.style.height) > window.pageYOffset+window.innerHeight) {
				cp.style.top=(window.pageYOffset+window.innerHeight-parseInt(cp.style.height)-5)+'px';
			}
			else {
				cp.style.top=(commentTd.offsetParent.offsetTop+commentTd.offsetTop-20)+'px';
			}
		}
		cb.style.height=(parseInt(cp.style.height)-cb.offsetTop-10)+'px';
	}, false);
	cp.appendChild(button);
	
	//Add update button to comments popup
	var button=document.createElement('img');
	button.title='Update gvidcomments';
	button.src=images.update;
	button.style.cssFloat='right';
	button.style.marginRight='3px';
	button.style.cursor='pointer';
	button.addEventListener('mouseover', function(){ this.src=images.update_highlight; }, false);
	button.addEventListener('mouseout', function(){ this.src=images.update; }, false);
	button.addEventListener('click', update, false);
	cp.appendChild(button);
	
	//Add about button to comments popup
	var button=document.createElement('img');
	button.title='About gvidcomments';
	button.src=images.about;
	button.style.cssFloat='right';
	button.style.marginRight='3px';
	button.style.marginLeft='3px';
	button.style.cursor='pointer';
	button.addEventListener('mouseover', function(){ this.src=images.about_highlight; }, false);
	button.addEventListener('mouseout', function(){ this.src=images.about; }, false);
	button.addEventListener('click', about, false);
	cp.appendChild(button);
	
	//Add header text to comments popup
	var header=document.createElement('b');
	header.id='gvidcomments-header';
	cp.appendChild(header);
	cp.appendChild(document.createElement('br'));
	
	//Add communityrating div to comments popup
	var communityrating=document.createElement('div');
	communityrating.id='gvidcomments-communityrating';
	communityrating.style.color='grey';
	communityrating.style.paddingBottom='5px';
	cp.appendChild(communityrating);
	
	//Add comments box to comments popup
	var cb=document.createElement('div');
	cb.id='gvidcomments-commentsbox';
	cb.style.overflow='auto';
	cb.style.paddingTop='5px';
	cp.appendChild(cb);
	
	//Detect window resizes
	window.addEventListener('resize', resize, false);
	
	//Detect keypress (j/k)
	document.addEventListener('keypress', function(e){
		var key=String.fromCharCode(e.charCode).toLowerCase();
		var newId=0;
		if (key == 'j') {
			//Move down
			if (session.openId == null) { newId=0; }
			else if (session.openId+1 <= session.numvideos-1) { newId=session.openId+1; }
			else { newId=0; }
			opencp(session.docids[newId]);
		}
		else if (key == 'k') {
			//Move up
			if (session.openId == null) { newId=session.numvideos-1; }
			else if (session.openId-1 >= 0) { newId=session.openId-1; }
			else { newId=session.numvideos-1; }
			opencp(session.docids[newId]);
		}
	}, false);
	
	//Iterate videos
	var videoId=0, videoTr;
	while ((videoTr=document.getElementById(videoId+1)) !== null) {
		//Another video
		session.numvideos++;
		
		//Start a session
		var s={
			videoId:videoId,
			title:null,
			ids:[],
			previds:[],
			newids:[],
			numcomments:0,
			communityrating:{},
			newvideo:null,
			failed:false,
			comments:[]
		};
		
		//Add video comment cell
		var commentTd=document.createElement('td');
		commentTd.id='gvidcomments-td-'+videoId;
		commentTd.title='Loading';
		commentTd.vAlign='top';
		if (session.page == 'Stats') {
			commentTd.style.textAlign='right';
		}
		commentTd.appendChild(document.createTextNode('Loading'));
		videoTr.insertBefore(commentTd,videoTr.getElementsByTagName('td')[3]);
		
		//Do this video have a docid
		var docid;
		if (videoTr.getElementsByTagName('a').length == 0 || videoTr.getElementsByTagName('a')[0].href.indexOf('docid=') == -1) {
			var error='This video is either processing, needs action, have been rejected or failed';
			GM_log(error);
			commentTd.title=error;
			commentTd.removeChild(commentTd.firstChild);
			commentTd.appendChild(document.createTextNode('Video not ready'));
			//Set variables
			s.failed='This video is either processing, needs action, have been rejected or failed (no docid could be found).';
			s.communityrating.error=true;
			docid=videoId.toString(); //Use videoId as docid since we can't find the real docid
			s.title=videoTr.getElementsByTagName('td')[0].innerHTML;
		}
		else {
			s.title=videoTr.getElementsByTagName('a')[0].innerHTML;
			//Get the docid for this video
			docid=videoTr.getElementsByTagName('a')[0].href;
			docid=docid.substr(docid.indexOf('docid=')+'docid='.length);
			if (docid.indexOf('&') != -1) { //remove &hl=en
				docid=docid.substr(0,docid.indexOf('&'));
			}
		}
		
		//Get possible previous information and add this video to the session
		s.newvideo=(data.videos[docid] && data.videos[docid].ids?false:true);
		session.videos[docid]=s;
		if (!s.failed) {
			//data.videos[docid]=data.videos[docid] || {ids:[]};
			data.videos[docid]=data.videos[docid] || {};
		}
		session.docids.push(docid);
		
		//Add mouse click (I couldn't get this to work without embedding it in a function, WEIRD!)
		addclick(docid);
		commentTd.style.cursor='pointer';
		commentTd.style.color='rgb(0,50,250)';
		
		videoId++;
	}
	
	//Get comments
	var req={searchSpecs:[],applicationId:28};
	session.docids.forEach(function(docid) {
		req.searchSpecs.push({entities:[{url:'http://video.google.com/videoplay?docid='+docid}], groups:['public_comment'], matchExtraGroups:true, startIndex:0, numResults:100, includeNickNames:true});
	});
	/*var ta=document.createElement('textarea');
	ta.value='req='+json_toString(req);
	cp.appendChild(ta);*/
	GM_xmlhttpRequest({
		method:'POST',
		url:'http://video.google.com/reviews/json/search',
		headers:{
			'User-agent':'Mozilla/4.0 (compatible) Greasemonkey',
			'Accept':'application/atom+xml,application/xml,text/xml',
		},
		data:'req='+json_toString(req),
		onload:function(responseDetails) {
			if (responseDetails.status == 200) {
				/*var ta=document.createElement('textarea');
				ta.value=responseDetails.responseText;
				cp.appendChild(ta);*/
				var comments=json_fromString(responseDetails.responseText);
				if (!comments.channelHeader.errorCode) {
					comments.searchResults.forEach(function(searchResult) {
						//Iterate comments
						if (searchResult.annotations) {
							//Get docid
							var docid=searchResult.annotations[0].entity.url;
							docid=docid.substr(docid.indexOf('docid=')+'docid='.length);
							//Set crispy shortcut
							var s=session.videos[docid];
							//Count the number of comments
							s.numcomments=searchResult.numAnnotations;
							session.numcomments+=s.numcomments;
							//Iterate comments
							searchResult.annotations.forEach(function(annotation) {
								//Get entry
								var entry={
									name: (annotation.entity.nickname?annotation.entity.nickname:'(anonymous)'),
									date: annotation.timestamp,
									text: annotation.comment,
									id: annotation.entity.groups[1] };
								//Store
								s.comments.push(entry);
								s.ids.push(parseInt(entry.id));
							});
							
							//Check if I have the ids of the comments stored from a previous check
							if (s.newvideo) {
								data.videos[docid].ids=s.ids;
							}
							s.previds=data.videos[docid].ids.slice();
							
							//Check which comments are new
							s.ids.map(function(id) {
								if (!s.previds.some(function(previd) {
									if (id == previd) {
										return true;
									}
								})) {
									s.newids.push(id);
								}
							});
						}
					});
				}
				else {
					session.error='Failed to fetch comments. Error code: '+comments.channelHeader.errorCode;
					GM_log(session.error);
				}
			}
			else {
				session.error='Failed to fetch comments. HTTP response: '+responseDetails.status;
				GM_log(session.error);
			}
			session.loading=false;
			
			//Put numcomments
			session.docids.forEach(function(docid) {
				var s=session.videos[docid];
				var commentTd=document.getElementById('gvidcomments-td-'+s.videoId);
				commentTd.removeChild(commentTd.firstChild);
				if (session.error) {
					commentTd.appendChild(document.createTextNode('Error'));
					commentTd.title=session.error;
				}
				else {
					commentTd.appendChild(document.createTextNode(s.numcomments+(session.page=='Status'?' comment'+(s.numcomments!=1?'s':''):'')));
					commentTd.title='Click to view comments';
					//Are there any new comments on this video?
					if (s.newids.length != 0) {
						session.newcomments=true;
						//Put 'new!' text
						var sup=document.createElement('sup');
						sup.style.color='rgb(255,0,0)';
						sup.appendChild(document.createTextNode(' new!'));
						commentTd.appendChild(sup);
						//Add the new ids to the stored ids
						data.videos[docid].ids=s.newids.concat(data.videos[docid].ids); //Appending previds to newids will put the ids in the order they appear when fetching them
					}
					//Refresh cp?
					if (session.openDocid == docid) {
						opencp(docid);
					}
				}
			});
			//Put session.numcomments
			if (session.page == 'Status') {
				commentsTitle.title=session.numcomments+' comments on this page';
			}
			else if (session.page == 'Stats') {
				commentsTotal.removeChild(commentsTotal.firstChild);
				commentsTotal.appendChild(document.createTextNode(session.numcomments));
				commentsTotal.title='Average number of comments per video: '+Math.round(session.numcomments/session.numvideos);
			}
			//Add new text to comment header?
			if (session.newcomments) {
				var sup=document.createElement('sup');
				sup.style.color='rgb(255,0,0)';
				sup.appendChild(document.createTextNode(' new!'));
				commentsTitle.appendChild(sup);
			}
			
			//Save
			GM_setValue('data',uneval(data));
		}
	});
	
	//Get communityrating
	var req={entities:[],includeAggregateInfo:true,applicationId:28};
	session.docids.forEach(function(docid) {
		req.entities.push({url:'http://video.google.com/videoplay?docid='+docid});
	});
	/*var ta=document.createElement('textarea');
	ta.value='req='+json_toString(req);
	cp.appendChild(ta);*/
	GM_xmlhttpRequest({
		method:'POST',
		url:'http://video.google.com/reviews/json/lookup',
		headers:{
			'User-agent':'Mozilla/4.0 (compatible) Greasemonkey',
			'Accept':'application/atom+xml,application/xml,text/xml',
		},
		data:'req='+json_toString(req),
		onload:function(responseDetails) {
			if (responseDetails.status == 200) {
				/*var ta=document.createElement('textarea');
				ta.value=responseDetails.responseText;
				cp.appendChild(ta);*/
				var ratings=json_fromString(responseDetails.responseText);
				ratings.annotations.forEach(function(entry) {
					//Get docid
					var docid=entry.entity.url;
					docid=docid.substr(docid.indexOf('docid=')+'docid='.length);
					
					//Get data
					var rating=entry.aggregateInfo.averageRating;
					var numvotes=0;
					entry.aggregateInfo.starRatings.forEach(function(starRating) {
						numvotes+=starRating.count;
					});
					
					//Set crispy shortcut
					var s=session.videos[docid];
					
					//Set communityrating
					s.communityrating.rating=rating;
					s.communityrating.numvotes=numvotes;
				});
				//Refresh cp?
				if (session.openDocid == docid) {
					opencp(docid);
				}
			}
			else {
				session.communityrating.error=true;
			}
			session.communityrating.loading=false;
		}
	});
})();