There are 7 previous versions of this script.
Add Syntax Highlighting (this will take a few seconds, probably freezing your browser while it works)
// ==UserScript==
// @name Google Reader - Colorful List View
// @namespace http://google.reader.colorful.list.view/kepp
// @include http://www.google.com/reader/*
// @include https://www.google.com/reader/*
// @include http://userscripts.org/scripts/source/8782.meta.js
// @description Colorizes the item headers in Google Reader list view and
// @description the entries in expanded view.
// @version 20090822
// ==/UserScript==
/**
* 20090822
* Fix to ensure that all items get colored.
* Fixes for Google Reader update
* Added script update notification to the settings page
* Added Opera compatibility
* - Remove use of "for each"
* - Add alternatives to GM_ functions (GM_addStyle, GM_setValue, GM_getValue)
* - Modify coloring CSS
* Cleaned up some code
* Also added DOM Storage fallback option
*
*
* 20081214
* Prefs split out into independent items and updated to apply instantly.
* Pref notification messages are also fixed to work properly.
* Fix for script not working if Google Gears was installed (my bad design).
* Works on expanded view too now. Possible/easier with Google Reader now using
* CSS for rounded borders.
**
* 20081104
* Added settings for coloring read/unread items
* Adjusted things in the settings
**
* 20080730
* Fixed css mistake of read items not being colored.
* Added https:// url to the include list
* Added coloring option on settings page, added settings page to the exclude
* list
**/
// var script = document.createElement("script");
// script.innerHTML = "(" +
(function()
{
// info used to check for script updates
const SCRIPT_INFO =
{
version: "20090822",
date: "Mon, 22 Aug 2009 00:00:00 GMT",
updateUrl: "http://userscripts.org/scripts/source/8782.meta.js",
installUrl: "http://userscripts.org/scripts/source/8782.user.js"
};
// CSS to allow items to be colored
const BASE_CSS = "\
.entry-likers /* like count */\
{\
background-color: transparent !important;\
}\
.gm-color-lv .collapsed /* list view headers */\
{\
border-color: transparent !important;\
}\
#entries.list.gm-color-lv #current-entry .collapsed\
{\
border: 2px solid #8181DC !important;\
}\
#entries.list.gm-color-ui #current-entry.expanded .collapsed\
{\
border-bottom-color: transparent !important;\
border-width: 2px 0 !important;\
}\
#entries .entry\
{\
padding: 5px 0;\
}";
const STRINGS =
{
// pref labels
list: "When in list view.",
expanded: "When in expanded view.",
read: "That have been marked as read.",
unread: "That are unread.",
scheme: "Color Scheme: ",
def: "Default",
custom: "Custom"
};
//=============================================================================
function $id( id )
{
return document.getElementById( id );
}
function $x( query, context )
{
var doc = ( context ) ? context.ownerDocument : document
return doc.evaluate( query, ( context || doc ), null,
XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue;
}
function $xa( query )
{
var res = document.evaluate( query, document, null,
XPathResult.UNORDERED_NODE_ITERATOR_TYPE, null );
var element, array = [];
while ( element = res.iterateNext() )
array.push( element );
return array;
}
function addStyle( css )
{
var style = document.createElement( "style" );
style.type = "text/css";
style.innerHTML = css;
document.getElementsByTagName( "head" )[0].appendChild( style );
return style;
}
//=============================================================================
// script updater
var updater =
{
loader: null,
version: 0,
homeUrl: "",
updateUrl: "",
installUrl: "",
init: function()
{
for ( var prop in SCRIPT_INFO )
this[ prop ] = SCRIPT_INFO[ prop ];
// test if this is the script meta info page that loaded
if ( location.href == SCRIPT_INFO.updateUrl )
{
document.body.setAttribute( "style", "visibility: hidden;\
overflow: hidden;" );
// running on userscripts.org domain
// update link not inserted yet document.write and there's an update
if ( !document.getElementsByTagName( "a" ).length )
{
if ( this.parseMetaInfo() )
this.insertUpdateLink(); // this will "reload" the page
}
else
{
document.body.setAttribute( "style", "visibility: visible;\
overflow: hidden;font-family: Arial, sans-serif;color: #2244BB;" );
}
return true; // notify that this was the script meta page
}
var loader = document.createElement( "iframe" );
loader.setAttribute( "style", "position: absolute;\
height: 0; width: 0;" );
this.loader = document.body.appendChild( loader );
},
parseMetaInfo: function()
{
var scriptInfo = document.body.innerHTML;
var updateAvailable;
// compare script versions
if ( /@version\s*([\S]+)/.test( scriptInfo ) )
updateAvailable = this.version < RegExp.$1;
// compare script dates
else if ( /@uso:timestamp\s*(\S.+)/.test( scriptInfo ) )
updateAvailable = new Date( this.date ) < new Date( RegExp.$1 );
return updateAvailable;
},
insertUpdateLink: function()
{
var me = this;
setTimeout( function() {
// insert update url. hackish =\
document.write( "<html><body>\<a href=\"" + me.installUrl +
"\" target=\"_blank\">Userscript Update Available</a>\
</body></html>" );
document.close();
}, 0 )
},
check: function() // runs on google.com domain
{
var lastCheck = storage.getItem( "last-check", 0 );
if ( new Date().getTime() - lastCheck < 3*24*60*60*1000 ) // 3 days
return false;
this.loader.setAttribute( "style", "visibility: visible;\
overflow: hidden;\
position: absolute; right: 2em;\
height: 2em; width: 20em;" );
this.loader.src = this.updateUrl;
storage.setItem( "last-check", new Date().getTime() + "" );
return this.loader;
}
};
// user interface for script settings added on settings page
var settings =
{
timeoutID: 0,
entries: null,
init: function() // insert page color options into the settings page
{
// ascend out of iframe. hopefully this works
this.entries = frameElement.ownerDocument.getElementById( "entries" );
var sect = this.addPrefs();
var check = updater.check();
if ( check )
sect.insertBefore( check, sect.firstChild );
},
addPrefs: function()
{
var sect = document.createElement( "div" );
sect.className = "extra";
sect.innerHTML = "<div class=\"extra-header\">Colors</div>\
Color items:\
<ul style=\"list-style-type: none; padding-left: 0;\
margin-bottom: 1em;\">\
</ul>";// + STRINGS.scheme;
$id( "setting-extras-body" ).appendChild( sect );
var list = sect.lastChild; //.previousSibling;
var me = this;
function tc( event )
{
me.toggleColors( event.target.id );
}
this.addColorPref( list, "gm-color-lv", STRINGS.list, tc );
this.addColorPref( list, "gm-color-ev", STRINGS.expanded, tc );
this.addColorPref( list, "gm-color-ri", STRINGS.read, tc );
this.addColorPref( list, "gm-color-ui", STRINGS.unread, tc );
// this.addSchemePref( sect );
return sect;
},
addColorPref: function ( list, id, text, handler )
{
var pref = document.createElement( "li" );
var selected = storage.getItem( id, id + " " );
pref.innerHTML = "<label><input id=\"" + id + "\" type=\"checkbox\" " +
( ( selected ) ? "checked=\"on\"" : "" ) +
"\"/>" + text + "</label>";
list.appendChild( pref );
var label = pref.firstChild.firstChild;
label.addEventListener( "change", handler, false );
},
addSchemePref: function( sect )
{
var sel = document.createElement("select");
var addButton = document.createElement( "input" );
sel.addEventListener( "change", this.togglePref, false )
sel.innerHTML = "<option style=\"font-style: normal;\">" +
STRINGS.def + "</option>\
<option style=\"font-style: italic;\">" +
STRINGS.custom + "</option>";
addButton.type = "button";
addButton.disabled = "true";
addButton.value = "Save";
addButton.style.marginLeft = "1em";
sect.appendChild( sel );
sect.appendChild( addButton );
this.addColorPickers( sect );
},
addColorPickers: function( section )
{
var bgRange = this.makeColorRange();
var fontRange = this.makeColorRange();
section.appendChild( bgRange );
// section.appendChild( fontRange );
},
makeColorRange: function()
{
var range = document.createElement( "div" );
range.setAttribute( "style", "width: 360px; height: 10px; margin-top: 1em; border: 1px solid #FFCC66;" );
for ( var i = 360; i; i-- )
{
var color = document.createElement( "div" );
color.setAttribute( "style", "float: left; width: 1px; height: 100%; background-color: hsl(" + i + ",100%,50%" );
range.appendChild( color );
}
var rangeContainer = document.createElement( "div" );
rangeContainer.setAttribute( "style", "height: 19px; width: 362px;" );
var rangeBegin = document.createElement( "div" );
rangeBegin.setAttribute( "style", "position: absolute; width: 1px; height: 13px; border: 1px solid black; background: transparent;" );
var rangeEnd = rangeBegin.cloneNode( true );
rangeBegin.style.marginLeft = "-1px";
rangeEnd.style.marginLeft = "359px";
rangeBegin.style.borderLeftWidth = "2px";
rangeEnd.style.borderRightWidth = "2px";
rangeContainer.appendChild( rangeBegin );
rangeContainer.appendChild( rangeEnd );
rangeContainer.appendChild( range );
return rangeContainer;
},
toggleColors: function( id )
{
var pref = storage.getItem( id, id );
var msg, newPref = "", cName = "";
if ( !pref )
{
newPref = id;
cName = id + " ";
msg = "<em>will</em>";
}
else
{
msg = "<em>will not</em>";
}
var re = new RegExp( id + " |^", "g" );
this.entries.className = this.entries.className.replace( re, cName );
storage.setItem( id, newPref );
this.setMessage( id, msg );
},
togglePref: function( event )
{
if ( this.value == "Custom" )
{
this.style.fontStyle = "italic";
addButton.removeAttribute( "disabled" );
}
else
{
this.style.fontStyle = "";
addButton.setAttribute( "disabled", "true" );
}
},
setMessage: function( id, msg )
{
clearTimeout( this.timeoutID );
var inner = $x( "id( 'message-area-inner' )" );
var outer = $x( "id( 'message-area-outer' )" );
// get the message string to insert into the page
var type = ( id == "gm-color-lv" ) ? "List view items " :
( id == "gm-color-ev" ) ? "Expanded view items " :
( id == "gm-color-ui" ) ? "Unread items " :
( id == "gm-color-ri" ) ? "Read items " : "unknown";
var newMsg = type + msg + " be colored.";
inner.innerHTML = newMsg; // set the message
// force display and set position and width
outer.setAttribute( "style", "display: block !important;" +
"margin-left:" +
Math.round( inner.offsetWidth/-2 ) + "px;" +
"width:" + (inner.offsetWidth + 10) + "px;" );
outer.className = "message-area info-message";
this.timeoutID = setTimeout( function()
{
outer.style.display = "";
// test if the same message is still showing.
// force lowercase to handle any (tag name) capitalization change
if ( inner.innerHTML.toLowerCase() == newMsg.toLowerCase() )
{
outer.className = outer.className.replace( / hidden|$/, " hidden" );
}
}, 7*1000 );
},
getColorPrefs: function()
{
var prefs = "";
prefs += storage.getItem( "gm-color-lv", "gm-color-lv" ) + " ";
prefs += storage.getItem( "gm-color-ev", "gm-color-ev" ) + " ";
prefs += storage.getItem( "gm-color-ui", "gm-color-ui" ) + " ";
prefs += storage.getItem( "gm-color-ri", "gm-color-ri" ) + " ";
return prefs;
}
};
// provide local data storage
var storage =
{
cookie: {},
init: function() // initialize methods for data storage access
{
if ( typeof GM_getValue != "undefined" )
{
this.getItem = GM_getValue;
this.setItem = GM_setValue;
return;
}
if ( typeof localStorage != "undefined" )
{
this.getItem = function( key, def )
{
var value = localStorage.getItem( key );
return ( typeof value == "undefined" ) ? def : value;
};
this.setItem = function( key, value )
{
localStorage.setItem( key, value );
};
return;
}
var pairs = {};
if ( /gm-color=([^;]*)/.test( unescape( document.cookie ) ) )
{
var cookie = RegExp.$1;
cookie.split( "/" ).forEach( function( pair )
{
var set = pair.split( ":" );
pairs[ set[ 0 ] ] = set[ 1 ];
} );
}
this.cookie = pairs;
},
getItem: function( name, def )
{
var cookieVal = this.cookie[ name ];
return ( typeof cookieVal == "undefined" ) ? def : cookieVal;
},
setItem: function( name, value )
{
this.cookie[ name ] = value;
var strCookie = "gm-color=";
for ( var prop in this.cookie )
strCookie += prop + ":" + this.cookie[ prop ] + "/";
var future = new Date( ( new Date().getTime() + 10*365*24*60*60*1000 ) );
strCookie += ";path=/reader;expires=" + future.toGMTString();
document.cookie = strCookie;
},
};
// used to keep track of all the calculated colors
var colors = {};
//=============================================================================
// calculate item hue
function getHue( title )
{
var hue = 0;
for ( var i = 0, ch; ch = title[ i ]; i++ )
hue += ch.charCodeAt( 0 );
hue %= 360;
colors[ title ] = hue;
return hue;
}
function getColorCss( title )
{
var hue = getHue( title );
return "\
.gm-color-ev.gm-color-ui div[ colored='" + title + "' ] .card,\
/* .ccard, .t2, .t3 in Opera expanded view */\
.gm-color-ev.gm-color-ui div[ colored='" + title + "' ] .ccard,\
.gm-color-ev.gm-color-ui div[ colored='" + title + "' ] .t2,\
.gm-color-ev.gm-color-ui div[ colored='" + title + "' ] .t3,\
.gm-color-lv.gm-color-ui div[ colored='" + title + "' ] .collapsed\
{\
background: hsl(" + hue + ", 70%, 80% ) !important;\
}\
.gm-color-ev.gm-color-ui div[ colored='" + title + "' ]:hover .card,\
.gm-color-ev.gm-color-ui div[ colored='" + title + "' ]:hover .ccard,\
.gm-color-ev.gm-color-ui div[ colored='" + title + "' ]:hover .t2,\
.gm-color-ev.gm-color-ui div[ colored='" + title + "' ]:hover .t3,\
.gm-color-lv.gm-color-ui div[ colored='" + title + "' ]:hover .collapsed\
{\
background: hsl(" + hue + ", 90%, 85% ) !important;\
}\
.gm-color-ev.gm-color-ui .read[ colored='" + title + "' ] .card,\
.gm-color-ev.gm-color-ui .read[ colored='" + title + "' ] .ccard,\
.gm-color-ev.gm-color-ui .read[ colored='" + title + "' ] .t2,\
.gm-color-ev.gm-color-ui .read[ colored='" + title + "' ] .t3,\
.gm-color-lv.gm-color-ui .read[ colored='" + title + "' ] .collapsed\
.gm-color-ev.gm-color-ui .read[ colored='" + title + "' ]:hover .card,\
.gm-color-ev.gm-color-ui .read[ colored='" + title + "' ]:hover .ccard,\
.gm-color-ev.gm-color-ui .read[ colored='" + title + "' ]:hover .t2,\
.gm-color-ev.gm-color-ui .read[ colored='" + title + "' ]:hover .t3,\
.gm-color-lv.gm-color-ui .read[ colored='" + title + "' ]:hover .collapsed\
{\
background: #F3F5FC !important; /* override to force no color */\
}\
.gm-color-ev.gm-color-ri div.read[ colored='" + title + "' ] .card,\
.gm-color-ev.gm-color-ri div.read[ colored='" + title + "' ] .ccard,\
.gm-color-ev.gm-color-ri div.read[ colored='" + title + "' ] .t2,\
.gm-color-ev.gm-color-ri div.read[ colored='" + title + "' ] .t3,\
.gm-color-lv.gm-color-ri div.read[ colored='" + title + "' ] .collapsed\
{\
/* color read items. overrides the unread item setting. */\
background: hsl(" + hue + ", 50%, 90% ) !important;\
}\
.gm-color-ev.gm-color-ri div[ colored='" + title + "' ].read:hover .card,\
.gm-color-ev.gm-color-ri div[ colored='" + title + "' ].read:hover .ccard,\
.gm-color-ev.gm-color-ri div[ colored='" + title + "' ].read:hover .t2,\
.gm-color-ev.gm-color-ri div[ colored='" + title + "' ].read:hover .t3,\
.gm-color-lv.gm-color-ri div.read[ colored='" + title + "' ]:hover\
.collapsed\
{\
/* color read items. overrides the unread item setting. */\
background: hsl(" + hue + ", 70%, 95% ) !important;\
}";
}
// inject color css into the page
function setColor()
{
// pick up all uncolored entries, including ones missed previously
var nocolor = $xa( "id( 'entries' )/div[ contains( @class, 'entry' ) ]" +
"[ not( @colored ) ]" );
if ( !nocolor.length )
return;
nocolor.forEach( function( nc )
{
// title is an "<a>" for expanded view, "<span>" for list view
var title = $x( ".//*[ contains( @class,'entry-source-title' ) ]" +
"[ not( * ) ]", nc ).innerHTML.replace( /\W/g, "-" );
nc.setAttribute( "colored", title );
if ( colors[ title ] == undefined )
addStyle( getColorCss( title ) );
} );
}
function watchLoading( chrome )
{
// pull this out here out of unsafeWindow context
var prefs = settings.getColorPrefs();
function setup( event )
{
var entries = $id( "entries" );
if ( entries )
{
chrome.removeEventListener( "DOMNodeInserted", setup, false );
// initial setup and toggling of settings
entries.className = prefs + entries.className;
entries.addEventListener( "DOMNodeInserted", setColor, false );
}
}
chrome.addEventListener( "DOMNodeInserted", setup, false );
}
(function()
{
var chrome = $id( "chrome" );
storage.init();
if ( chrome )
watchLoading( chrome ); // watch for the loading of rss entries
else // setting and script meta info page have no "chrome" element
{
if ( updater.init() ) // script meta info page
return;
settings.init();
}
addStyle( BASE_CSS );
} )();
})();
// .toString() + ")();";
// document.body.appendChild(script);
