Flickr Filter Hearts

By Steffen J. Last update May 15, 2009 — Installed 1,607 times. Daily Installs: 1, 1, 0, 0, 0, 2, 2, 1, 3, 2, 2, 1, 1, 1, 1, 3, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 3, 0, 0, 2, 0, 1

There are 7 previous versions of this script.

// ==UserScript==
// @name Flickr Filter Hearts
// @namespace http://www.jakob.at/greasemonkey/
// @description This script filters images in photo comments to make the more readable. Version 0.11.
// @include http://*flickr.com/photos/*
// @include http://*flickr.com/
// @include http://*flickr.com/photos_comments.gne*
// @include http://*flickr.com/recent_activity.gne*
// @include http://*flickr.com/activity*
// @include http://*flickr.com/replies*
// @version 0.11 (2009-05-15)
// @creator Steffen A. Jakob (http://www.flickr.com/photos/steffenj/)
// ==/UserScript==
//
// Copyright (C) 2008-2009 Steffen A. Jakob
// 
// 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 2
// 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.
// 
// The GNU General Public License is available by visiting
// http://www.gnu.org/copyleft/gpl.html
// or by writing to
// Free Software Foundation, Inc.
// 51 Franklin Street, Fifth Floor
// Boston, MA  02110-1301
// USA

// Changelog
// 2009-05-15 0.11 The script no longer changes comments of the picture owner (this was broken in the last version). 
//    Improved detection of group icons. Added a white list for some images.
// 2009-02-13 0.10 Adapted to changed layout. Remove linked images to groups completely.
// 2008-10-24 0.9 Keep images from the photostream of the commenter.
// 2008-09-11 0.8 Adapted script for new flickr layout.
// 2008-02-18 0.7 Keep images in comments from the owner of the photo.
// 2008-02-17 0.6 Filter images which are directly followed by a link to a group.
// 2008-02-16 0.5 Always filter images, which are linked to flickr groups.
// 2008-02-15 0.4 Keep external images.
// 2008-02-15 0.3 Keep inline defined images.
// 2008-02-15 0.2 Filter linked group icons which have a buddy icon URL.
// 2008-02-14 0.1 First version.

// Try to retrieve all images in comments from
// 1. a photo page
// 2. recent activites
// 3. recent comments

var images = document.evaluate(
	'//div[@id="DiscussPhoto"]//img |' +
	'//div[@class="act-content"]//img |' +
	'//div[@class="act-data"]//img |' +
	'//table[@class="NewComments"]//img',
    document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

// Remember URLs of images in own comments. We want to keep them.
var ownImageUrls = {};
if (unsafeWindow.page_p != null && unsafeWindow.page_p.ownersUrl != null) {
	var ownImages = document.evaluate(
		'//li[contains(@class, "own-activity")]//img |' +
		'//div[@class="comment-block"]/div[contains(@class, "Who")]/a[@href="' + unsafeWindow.page_p.ownersUrl + '"]/../../..//img',
		document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
	for (var i = 0; i < ownImages.snapshotLength; i++) {
		ownImageUrls[ownImages.snapshotItem(i).src] = true;
	}
}

// The commenter is allowed to embed pictures from his own stream.
var commenterImages = {};
var links = document.evaluate(
	'//div[@class="comment-block"]/div[contains(@class, "comment-owner")]/a',
	document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i = 0; i < links.snapshotLength; i++) {
	var node = links.snapshotItem(i);
	var href = node.href;
	var pos = href.indexOf("/photos/")
	if (pos >= 0) {
		href = href.substr(pos)
	}
	var imgs = document.evaluate('//div[@class="comment-content"]//a[contains(@href, "' + href + '")]/img',
		node.parentNode, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
	for (var j = 0; j < imgs.snapshotLength; j++) {
		var node = imgs.snapshotItem(j);
		commenterImages[node.src] = true;
	}
}

var ImageType = { "keep" : 0, "group" : 1, "replace" : 2 };

// Examine all images and replace them if necessary.
for (var i = 0; i < images.snapshotLength; i++) {
	var image = images.snapshotItem(i);
	var type = isImportantImage(image);
	if (type != ImageType.keep) {
		replaceImage(image, type);
	}
}

// Checks if an image is important for the comment and should not be
// filtered.
function isImportantImage(image) {
	var src = image.src;
	
	// Flick specific icons e.g. the "pro" icon
	if (src.indexOf('flickr.com/images') >= 0) {
		return ImageType.keep;
	}
	// Keep inline images. These are sometimes used by other Greasemonkey
	// scripts like FlickrPM.
	if (src.indexOf('data:image') >= 0) {
		return ImageType.keep;
	}
	if (isLinked(image)) {
		// Filter images within links to groups.
		if (image.parentNode.href.indexOf('flickr.com/groups/') >= 0) {
			// White list
			if (image.parentNode.href.indexOf('handpickedguild') >= 0 || image.parentNode.href.indexOf('473902@N20') >= 0) {
				return ImageType.keep;
			}	
			return ImageType.group;
		}
	} else {
		// Filter images which are directly followed by a link to a group.
		var node = image.nextSibling;
		while (node != null && (node.nodeName == 'BR' || isEmptyTextNode(node))) {
			node = node.nextSibling;
		}
		if (node != null && node.nodeName == 'A' && node.href.indexOf('flickr.com/groups/') >= 0) {
			return ImageType.group;
		}
	}
	// Keep external images.
	if (src.indexOf('http://') >= 0 || src.indexOf('https://') >= 0) {
		if (src.indexOf('flickr.com') < 0) {
			return ImageType.keep;
		}
	}
	// Keep buddy icons from users but filter icons from groups.
	if (src.indexOf('buddyicons') >= 0) {
		return ImageType.keep;
	}
	// Keep images in comments from the owner of the photo.
	if (src in ownImageUrls) {
		return ImageType.keep;
	}
	// Keep images from the photostream of the commenter.
	if (src in commenterImages) {
		return ImageType.keep;
	}
	// Everything else will be replaced.
	return ImageType.replace;
}

// Checks if the node is a text node with an empty or only whitespace and newline containing value.
function isEmptyTextNode(node) {
	if (node != null && node.nodeName == '#text') {
		if (node.nodeValue.replace(/[\n\s]*/,"") == '') {		
			return true;
		}
	}
	return false;
}

// Checks if an node is part of a hyperlink.
function isLinked(node) {
	return node.parentNode.nodeName == 'A';
}

// Creates a hyperlink for a text object.
function linkText(text, url) {
	var	link = document.createElement('a');
	link.setAttribute('href', url);
	link.appendChild(text);
	return link;
}

// Replace the image with a text and - if exists - the alternative text from the image.
// The text is linked to the image URL (which is only active if there wasn't a wrapping
// link around the image).
function replaceImage(image, type) {
	if (type == ImageType.group) {
		image.parentNode.removeChild(image)
	} else {
		var div = document.createElement('div');
		div.style.fontWeight = "bold";
		var newText = image.alt;
		if (newText != "") {
			newText = ' "' + newText + '"';
		}
		var text = document.createTextNode('[image' + newText + ']');
		if (!isLinked(image)) {
			var	link = linkText(text, image.src);
			div.appendChild(link);
		} else {
			div.appendChild(text);
		}
		image.parentNode.replaceChild(div, image);
	}
}