I know AdBlock (http://adblock.mozdev.org/) for Opera (http://www.opera.com/download/index.dml?platform=mac) has been discussed tons of times before on a number of different forums. The reason given for it never happening is that Opera doesn't support XUL and user-specified JS, and why should the extension writers have to rewrite their program from scratch using a proprietary SDK. These are good reasons.

However, Opera 8.0x was recently released, with some really interesting new features that would allow for a GUI-less adblock script to be installed.
See http://www.opera.com/support/tutorials/userjs/ . UserJS allows users to specify local JavaScript files for Opera to include on every page that they visit.

My radical idea is this: gut the primary AdBlock javascript (http://www.mozdev.org/source/browse/...nt/adblock.js), removing everything that has to do with XUL (the extension GUI). Hardcode in a simple filterset (or a complex one, like filterset.g - http://www.pierceive.com/filtersetg/), and instead of having the script ask the GUI for the values of "hide" and "enabled", set them manually.

In the following snippet, the bold code would remain the same, and the italic code would need to be slightly changed, as described above:
// Array holding all the patterns to block.
var Patterns;
// Boolean signalling whether to remove or hide images. Images will be hidden if true. They will be removed if false.
var Hide;
// These tags are being processed.
var tags = ["img", "embed", "object", "iframe"]; // iframe is still listed so it can be killed at it's source, if desired

// Loads the preferences.
function loadSettings() {

try {
prefObj = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
var Branch = prefObj.getBranch("adblock." ) ;
Enabled = Branch.getBoolPref("enabled" ) ;
Hide = Branch.getBoolPref("hide" ) ;
Patterns = Branch.getCharPref("patterns" ).split(" " ) ;
} catch(exception) {}

// If AdBlock is not enabled... Return false as a signal that the program should not filter.
if (!Enabled) {
return false;
}

for (var i = 0 ; i < Patterns.length ; i++) {
Patterns[i] = new RegExp(convert2RegExp(Patterns[i]), "i" ) ;
}
return true;
}
As far as I can tell, if we make these simple changes to the script in the beginning, for it to work with Opera, only 1 more line might need to be changed!
// Resolves the absolute URL of the image-address being sent as an argument.
// page is the document which contains the image.
function getImageURL(imageSrc, page) {
var URL = Components.classes["@mozilla.org/network/standard-url;1"].createInstance(Components.interfaces.nsIURL);
URL.spec = page.URL;
return URL.resolve(imageSrc);
}
I am no javascript wizard, so I can't make the changes myself. But everything I've read says that this should be trivial. It would take an experienced coder about 5 minutes. I have included below the entire script, with the GUI-interaction parts removed, and the lines to be changed in bold:
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is AdBlock for Mozilla.
*
* The Initial Developer of the Original Code is
* Henrik Aasted Sorensen.
* Portions created by the Initial Developer are Copyright (C) 2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Henrik Aasted Sorensen <henrik@aasted.org>
* Stefan Kinitz <mcmurmel.blah@gmx.de>
* Rue <quill@ethereal.net>
*
* ***** END LICENSE BLOCK ***** */

// Array holding all the patterns to block.
var Patterns;
// Boolean signalling whether to remove or hide images. Images will be hidden if true. They will be removed if false.
var Hide;
// These tags are being processed.
var tags = ["img", "embed", "object", "iframe"]; // iframe is still listed so it can be killed at it's source, if desired

// Loads the preferences.
function loadSettings() {
try {
prefObj = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService);
var Branch = prefObj.getBranch("adblock.");
Enabled = Branch.getBoolPref("enabled");
Hide = Branch.getBoolPref("hide");
Patterns = Branch.getCharPref("patterns").split(" ");
} catch(exception) {}

// If AdBlock is not enabled... Return false as a signal that the program should not filter.
if (!Enabled) {
return false;
}

for (var i = 0 ; i < Patterns.length ; i++) {
Patterns[i] = new RegExp(convert2RegExp(Patterns[i]), "i");
}
return true;
}

// Resolves the absolute URL of the image-address being sent as an argument.
// page is the document which contains the image.
function getImageURL(imageSrc, page) {
var URL = Components.classes["@mozilla.org/network/standard-url;1"].createInstance(Components.interfaces.nsIURL);
URL.spec = page.URL;
return URL.resolve(imageSrc);
}

// This method checks if a picture matches a filter.
// It returns true if the imageurl is caught by one of the filters.
// It returns false if the imageurl is not caught by any filter.
function validate(imageurl) {
for (var i = 0 ; i < Patterns.length ; i++) {
if (Patterns[i].test(imageurl))
return true;
}

return false;
}

// Converts a pattern in this programs simple notation to a regular expression.
function convert2RegExp( pattern ) {
// If the user already entered a regular expression.
if ( isRegExp( pattern ) ) {
return pattern.substr(1, pattern.length - 2);
}

s = new String(pattern);
res = new String("^");

for (var i = 0 ; i < s.length ; i++) {
switch(s[i]) {
case '*' :
res += ".*";
break;

case '.' :
case '?' :
case '^' :
case '$' :
case '+' :
case '{' :
case '[' :
case '|' :
case '(' :
case ')' :
case ']' :
res += "\\" + s[i];
break;

case '\\' :
res += "\\\\";
break;

case ' ' :
// Remove spaces from URLs.
break;

default :
res += s[i];
break;
}
}
return res + '$';
}


// initiates filtering on the given document (if possible).
// --accepts either the target-document directly, or through its parent-event
// --(mozilla sends an event parameter by default if nothing was directly passed *and* there's an event trigger)
function filter(eventorotherparam) {
// If loadsettings returns false, it means that the page should not be filtered.
if (!loadSettings()) {
return;
}

// if we were called by an event -- (as opposed to another function)
if (eventorotherparam.type) {
if (eventorotherparam.target.contentDocument)
var page = eventorotherparam.target.contentDocument; // This is the tab. We need to get the contents.
} else
var page = eventorotherparam; // we were sent the document directly -- (by the function)

parseFrames(page);// initiate the filtering!
}

// parses document looking for frames, *recursively* -- (embedded frames?)
function parseFrames(page) {
// if the element/page has frames
if (page.getElementsByTagName("frame").length > 0) {
for (var f = 0 ; f < page.getElementsByTagName("frame").length ; f++) {
var frame = page.getElementsByTagName("frame").item(f).contentDocument;
parseFrames(frame);
}
}
else
parseInlineFrames(page);
}

// parses document looking for iframes -- *Inline-Frames*
function parseInlineFrames(page) {
// if the element/page has iframes
if (page.getElementsByTagName("iframe").length > 0) {
for (var f = 0 ; f < page.getElementsByTagName("iframe").length ; f++) {
var iframe = page.getElementsByTagName("iframe").item(f).contentDocument;
parsePage(iframe);
}
}
// even if there weren't -- "onward, ho -!"
parsePage(page);
}

// Goes through all relevant tags and determines if they should stay or be banished.
function parsePage(page) {
for (var j = 0 ; j < tags.length ; j++) {
var scanTags = page.getElementsByTagName( tags[j] );

for (var i = 0 ; i < scanTags.length ; i++) {
var src = findAttribute(scanTags[i],"src");
if (src && validate( getImageURL(src, page))) {
if (Hide) {
scanTags[i].style.visibility = "hidden";
} else {
scanTags[i].style.display = "none";
}
}
}
}
}

// Checks an element and its children recursively for an attribute.
function findAttribute(element, name) {
if (element.hasAttribute(name)) {
return element.getAttribute(name);
}

if (element.hasChildNodes()) {
for (var i = 0 ; i < element.childNodes.length ; i++) {
var res = findAttribute(element.childNodes[i], name);
if (res != false)
return res;
}
}

return false; // Didn't find it!
}
I beg any javascript gurus out there to help me tweak the AdBlock script to run in Opera. Please! Take a few moments and see if I'm right.

Finally: I know there are a few different options for blocking ads in opera. I am running Mac OS Tiger and Opera 8.02. I know about all the other alternatives out there, but for now, I'd really like to see if we can make this work. I think we'd make a lot of people happy if we succeeded.