There are 36 previous versions of this script.
// GreaseMonkey exclude filter + Google Maps highlighter
// Copyright (c) 2009, Kuzmeech, version 1.22
// Released under the GNU General Public Licence.
// Visit http://www.gnu.org/copyleft/gpl.html for a copy of said licence.
// Image Preview functionality has been borrowed from "Craigslist image preview 2"
// filter by Kite who reworked Jeffrey Palm's original script. Thank you, guys!
// hides images less than 150px tall or wide as irrelevant
/*
** Changelog **
2010.03.03:
1.224- fix for jumping ads due to loaded images - now ads with images are moved to the top. Removed "show real pics" option due to that.
1.223- logic fixes: includes filter wasn't working, "next 100" known issue fixed
1.222- fix for ads with duplicate images - they were not filtered, hide empty ads overrides "has image"
1.221- fix for small images which were not hidden
1.21 - downscale image popups which do not fit the screen
1.20 - dual monitor & horizontal alignment fix.
1.19 - horizontally - shows smaller pics closer to cursor
1.18 - hides all extremely wide images and ones that are smaller than 150px height or width
- hides duplicate links that once were shown somewhere on the page
- added "td background=" support
- fix for & in url
1.17 - thanks for the tip from user "Perplexed Guide" - now full size popup picture previews with idea borrowed from CLPicView FF plugin
1.16 - fixed include logic, right align for labels on top
1.15 - image preview - fetches images only for _shown_ ad blocks
1.14 - include location filter added
1.13 - checkbox added to enable/disable exclusion filter
1.12 - performance imrovements in RE preparation, debug output removed
1.11 - added "oneshot" timer for 300ms quiesce time after typing stopped, because "onkeyup" in its' pure state was blocking the browser
1.1 - added "exclude" input element at top with onkeyup event handler
1.0 - initial versoin where exclusions were defined in script array
*/
// ==UserScript==
// @name Craigslist locations filter
// @include http://*.craigslist.org/*
// @include http://*.craigslist.ca/*
// @exclude http://*.craigslist.org/
// @exclude http://*.craigslist.ca/
// @namespace http://kuzmeech.blogspot.com/
// ==/UserScript==
String.prototype.trim = function(symbol) {
return this.replace(/^\s+|\s+$/g,"");
}
function $n(tag, on) {
var e = document.createElement(tag);
if (on) on.appendChild(e);
return e;
}
function $t(text, on) {
var e = document.createTextNode(text);
if (on) on.appendChild(e);
return e;
}
function $(id) {
return document.getElementById(id);
}
// called once when loading - converts location into G.maps link
function mapsLink() {
ps = document.getElementsByTagName('p');
for (var i = 0; i < ps.length; i++) // for each posting
{
if (ps[i].innerHTML.match(/index\d+\.html/)) continue;
var font = ps[i].getElementsByTagName( 'font' )[0];
if (font) {
var location = font.innerHTML.trim().replace(/\(/, '').replace(/\)/, '');
// replace with href to google maps
font.innerHTML = " (<a href=\"http://maps.google.com/?q=" + location + "&z=8\">"
+ location + "</a>)";
}
}
}
function oneshot() {
var timer;
return function( fun, time ) {
clearTimeout( timer );
timer = setTimeout( fun, time );
};
}
var processAdsOneShot = oneshot();
/** converts array of patterns to array of RegExps + trims patterns */
function prepareRexs(array) {
var rexs = new Array(); // prepare RE objects
for (var j = 0; j < array.length; j++) {
var pattern = array[j].trim();
if (pattern != '') {
rexs.push(new RegExp(pattern, 'i'));
}
}
return rexs;
}
var alphaDiv; // like alpha-male
/** main piece that walks through ads */
function processAds() {
var excludeFiltersArray = $("excludeLocationsInput").value.split(',');
var includeFiltersArray = $("includeLocationsInput").value.split(',');
var rexsInclude = prepareRexs(includeFiltersArray);
var rexsExclude = prepareRexs(excludeFiltersArray);
ps = document.getElementsByTagName('p');
for (var i = 0; i < ps.length; i++) // for each posting
{
var p = ps[i];
if (p.innerHTML.match(/index\d+\.html/)) continue;
if (!alphaDiv) {
alphaDiv = $n('div');
alphaDiv.id = 'alphaDiv';
p.parentNode.insertBefore(alphaDiv, p);
}
var font = ps[i].getElementsByTagName( 'font' )[0];
var visible = true;
var noPics = false;
if (font) {
var location = font.innerHTML;
if ($('includeEnabled').checked) {
visible = false;
for (j = 0; j < rexsInclude.length; j++) {
var re = rexsInclude[j];
if (re.test(location)) {
visible = true;
break;
}
}
}
if ($('excludeEnabled').checked) {
for (j = 0; j < rexsExclude.length; j++) {
var re = rexsExclude[j];
if (re.test(location)) {
visible = false;
break;
}
}
}
} else { // no <font> - locatin is unspecified - exclude if "only include" checked
visible = ! $('includeEnabled').checked;
}
ps[i].style.display = visible ? 'block' : 'none';
if (visible) { // images only for visible
var pclass = ps[i].className;
if (!pclass || !pclass.match(/thumbnails\-loaded/)) { // mark ads which we've added thumbnails to
if (pclass) { ps[i].className += " "; }
ps[i].className += "thumbnails-loaded";
// show image previews
showImages(ps[i]);
} else {
ps[i].style.display = noPics ? 'block' : 'none';
}
}
}
}
var EXCLUDE_LOCATIONS = "exclude.locations";
saveChangesAndRefresh = function() {
GM_setValue("excludeLocationsInput", $("excludeLocationsInput").value);
GM_setValue("includeLocationsInput", $("includeLocationsInput").value);
GM_setValue("excludeEnabled", $("excludeEnabled").checked);
GM_setValue("includeEnabled", $("includeEnabled").checked);
// GM_setValue("hideBullshit", $("hideBullshit").checked);
updateElementsVisibility();
processAdsOneShot( processAds, 200 );
};
function updateElementsVisibility() {
$("excludeLocationsInput").style.display = $("excludeEnabled").checked ? '' : 'none';
$("includeLocationsInput").style.display = $("includeEnabled").checked ? '' : 'none';
$("includeLabel").style.color = $("includeEnabled").checked ? 'red' : 'black';
$("excludeLabel").style.color = $("excludeEnabled").checked ? 'red' : 'black';
// $("hideBullshitCheckbox").style.color = $("hideBullshit").checked ? 'red' : 'black';
}
function addInput(name, defVal) {
var input = $n("input");
input.id = name;
input.size = 70;
input.addEventListener("keyup", saveChangesAndRefresh, true);
input.value = GM_getValue(name, defVal);
return input;
}
function addCheckbox(name) {
var checkbox = $n("input");
checkbox.type = "checkbox";
checkbox.id = name;
checkbox.addEventListener("change", saveChangesAndRefresh, true);
checkbox.checked = GM_getValue(name, true);
return checkbox;
}
var imagesOnly = false;
function addLinkAtTop() {
// find top FORM with TABLE inside
var form = document.getElementsByTagName("form");
if (form && form[0].action.search(/search/)) {
var tbody = form[0].getElementsByTagName('tbody')[0];
var tr = $n("tr",tbody);
var td = $n("td", tr);
td.align = "right";
var label = $n("label", td);
label.innerHTML = "Only following locations:";
label.id = "includeLabel";
td = $n("td", tr);
var input = addInput("includeLocationsInput", 'cambridge, belmont');
td.appendChild(addCheckbox("includeEnabled"));
td.appendChild(input);
// td.colspan = 2;
td = $n("td", tr);
/* var label = $n("label", td);
label.id = "hideBullshitCheckbox";
label.innerHTML = '<input type="checkbox" id="hideBullshit"/> really hide no-pics ads';
$("hideBullshit").addEventListener("change", saveChangesAndRefresh, true);
$("hideBullshit").checked = GM_getValue("hideBullshit", true);
label.title = "Hide ads without qualifying pictures which are showing like empty line";
*/
tr = $n("tr",tbody);
td = $n("td", tr);
td.align = "right";
label = $n("label", td);
label.innerHTML = "Exclude locations:";
label.id = "excludeLabel";
td = $n("td", tr);
excludeInput = addInput("excludeLocationsInput",
GM_getValue(EXCLUDE_LOCATIONS, 'malden, everett')); // for backwards compatibility, remove after march 2009
td.appendChild(addCheckbox("excludeEnabled"));
td.appendChild(excludeInput);
td.colspan = 2;
}
updateElementsVisibility();
}
/** finds all child <A/> and fetches images for it */
function showImages(element) {
var links = element.getElementsByTagName("a");
for (i=0; i<links.length; i++) {
var link = links[i];
if (link.href && link.href.match(/.*craigslist.*\/\d+\.html$/)) {
GM_xmlhttpRequest({
method:"GET",
url: link.href,
headers:{
"User-Agent": "monkeyagent",
"Accept":"text/html,text/monkey,text/xml,text/plain",
},
onload: processAd(link)
});
}
}
}
var bigPictureDivCounter = 0;
var knownPictures = {}
var size = 60;
// ajax callback for each ad text
function processAd(_a) {
var a = _a;
return function(details) {
if (details.responseText) {
var div;
if (m = details.responseText.match(/<img ([^>]+)>/gi)) {
processImgs(a, div, m, "src");
}
if (m = details.responseText.match(/<td ([^>]+)>/gi)) {
processImgs(a, div, m, "background");
}
}
};
}
var counter = 0;
function processImgs(a, div, m, word) {
for (j=0; j<m.length; j++) {
s = m[j];
if (!s) continue;
s = s.split(word + '="')[1];
if (!s) continue;
s = s.split('"')[0];
s = s.replace(/amp;/gi, ''); // some strange people post & in URL parameters - fix for that
if (knownPictures[s]) { // don't show duplicate pictures
continue;
} else {
knownPictures[s] = s;
}
if (!div) {
var d = $n("div",a.parentNode);
var br = $t(" ",a.parentNode);
div = $n("div",a.parentNode);
}
// small thumbnails shown
var newA = $n("a", div);
var img = $n("img", newA);
div.style.display = 'none';
img.src = s;
img.style.maxHeight = size + "px";
// img.title = "Thumbnail shown by Craigslist location filter Greasemonkey Script";
$t(" ", div);
newA.href = s;
addBigPictureDiv(a, s, img);
// remove irrelevant bull%%it
img.addEventListener("load", function() {
var bigImage = $("div" + this.id).firstChild;
if ((this.height < size)
|| bigImage.height < 150 || bigImage.width < 150
) { // non-proportional pictures and small images are ignored
bigImage.parentNode.className = 'bullshitPic';
this.parentNode.style.display = 'none';
} else {
this.parentNode.parentNode.style.display = 'block';
bigImage.parentNode.className = 'realPic';
if (this.parentNode.parentNode.parentNode.className != 'realAd') {
this.parentNode.parentNode.parentNode.className = 'realAd';
counter ++;
$('alphaDiv').appendChild(this.parentNode.parentNode.parentNode); // zz
// this.parentNode.parentNode.parentNode.innerHTML = counter + ": " + this.parentNode.parentNode.parentNode.innerHTML;
}
}
}, true);
}
};
// adds div with big picture popup + handlers
function addBigPictureDiv(a, s, img) {
// big picture hidden
var fullSizeImgDiv = $n("div", a.parentNode);
var fullSizeImg = $n("img", fullSizeImgDiv);
fullSizeImgDiv.style.display = 'none';
fullSizeImgDiv.style.position = 'absolute';
fullSizeImgDiv.style.left = '150px';
fullSizeImgDiv.style.border = '2px dashed blue';
fullSizeImg.src = s;
fullSizeImgDiv.id = "div" + bigPictureDivCounter;
img.id = "p" + bigPictureDivCounter;
fullSizeImgDiv.id = "divp" + bigPictureDivCounter;
bigPictureDivCounter ++;
img.addEventListener("mouseover", function(event) {
var div = $("div" + this.id);
// for extremely tall or wide images
if (window.innerWidth < div.firstChild.width * 1.05) {
var originalWidth = div.firstChild.width;
div.firstChild.width = window.innerWidth * 0.85;
div.firstChild.height = div.firstChild.height * (div.firstChild.width/originalWidth);
}
if (window.innerHeight < div.firstChild.height * 1.05) { // if still..
var originalHeight = div.firstChild.height;
div.firstChild.height = window.innerHeight * 0.85;
div.firstChild.width = div.firstChild.width * (div.firstChild.height/originalHeight);
}
var cursorDown = (event.screenY / screen.height) > 0.54; // vertically - above or below?
div.style.top = cursorDown
? (event.pageY - div.firstChild.height - 5)
: event.pageY + 5;
div.style.left =
(div.firstChild.width > screen.width / 2) ?
(screen.width - div.firstChild.width) / 2 // horizontally - centered
: (((event.screenX - window.screenX)/ window.innerWidth) < 0.54 ? (event.pageX + 15) : (event.pageX - div.firstChild.width - 10));
div.style.display = "block";
},
true);
img.addEventListener("mouseout", function(evt) { $("div" + this.id).style.display = "none"; }, true);
}
addLinkAtTop();
mapsLink();
processAds();
