Posts Tagged ‘safari’

Ad blocking one website at a time

Monday, June 7th, 2010

I wrote a small piece of Applescript code that allows me to zap ads from web pages that I browse using Safari. It uses javascript to remove the ads or containing objects from the page directly. It doesn’t use intelligent ad recognition in any way. Rather my idea was much simpler. Most people spend 90% of there time on the web looking at a small number of websites: facebook, new york times, twitter, google, wikipedia, gmail, youtube, etc. See Pareto principle. If I can block 100% of the ads on 90% of the pages I look at then my ad blocker is effectively 90% efficient.

The ads on these big sites are usually in the same place on the page making them very easy to remove from the html with javascript. I organized my applescript so that adding a new site on which to block ads is very simple (just a line or two) and blocking a certain type of ad on that site is just another line.

I use the magnifying glass tool, in the Safari Developer tool, Web Inspector, to select the ad object on the page. Inevitably the website has used a class or id which then I feed into my script. Often the containing object is as simple as “ad” or “bigAd”.

screenshot of web inspector, selecting ad

When I have my blocker running in the background, it usually takes up 2% of the CPU. If I crank down the delay in the script so that the ads disappear even faster then it can get as high as 10% or so, but it’s not really worth it.

There is no reason why you couldn’t easily port this style of ad blocker to grease monkey, opera, or anything that runs client-side javascript.

Here is the applescript, it creates a few useful javascript methods and sets up the blocker to run in the background (with a little error checking).


-- Safari AdBlocker Applescript
-- Author: Alec Jacobson http://alecjacobson.com
--

-- this might not be necessary...
-- because safari already has getelementbyclassname...
-- but perhaps without localization?
-- and perhaps not older safari?
set getElementsByClass to "
/*
  Developed by Robert Nyman, http://www.robertnyman.com
  Code/licensing: http://code.google.com/p/getelementsbyclassname/
*/
var getElementsByClassName = function (className, tag, elm){
  if (document.getElementsByClassName) {
    getElementsByClassName = function (className, tag, elm) {
      elm = elm || document;
      var elements = elm.getElementsByClassName(className),
        nodeName = (tag)? new RegExp(\"\\b\" + tag + \"\\b\", \"i\") : null,
        returnElements = [],
        current;
      for(var i=0, il=elements.length; i<il; i+=1){
        current = elements[i];
        if(!nodeName || nodeName.test(current.nodeName)) {
          returnElements.push(current);
        }
      }
      return returnElements;
    };
  }
  else if (document.evaluate) {
    getElementsByClassName = function (className, tag, elm) {
      tag = tag || \"*\";
      elm = elm || document;
      var classes = className.split(\" \"),
        classesToCheck = \"\",
        xhtmlNamespace = \"http://www.w3.org/1999/xhtml\",
        namespaceResolver = (document.documentElement.namespaceURI === xhtmlNamespace)? xhtmlNamespace : null,
        returnElements = [],
        elements,
        node;
      for(var j=0, jl=classes.length; j<jl; j+=1){
        classesToCheck += \"[contains(concat(' ', @class, ' '), ' \" + classes[j] + \" ')]\";
      }
      try  {
        elements = document.evaluate(\".//\" + tag + classesToCheck, elm, namespaceResolver, 0, null);
      }
      catch (e) {
        elements = document.evaluate(\".//\" + tag + classesToCheck, elm, null, 0, null);
      }
      while ((node = elements.iterateNext())) {
        returnElements.push(node);
      }
      return returnElements;
    };
  }
  else {
    getElementsByClassName = function (className, tag, elm) {
      tag = tag || \"*\";
      elm = elm || document;
      var classes = className.split(\" \"),
        classesToCheck = [],
        elements = (tag === \"*\" && elm.all)? elm.all : elm.getElementsByTagName(tag),
        current,
        returnElements = [],
        match;
      for(var k=0, kl=classes.length; k<kl; k+=1){
        classesToCheck.push(new RegExp(\"(^|\\s)\" + classes[k] + \"(\\s|$)\"));
      }
      for(var l=0, ll=elements.length; l<ll; l+=1){
        current = elements[l];
        match = false;
        for(var m=0, ml=classesToCheck.length; m<ml; m+=1){
          match = classesToCheck[m].test(current.className);
          if (!match) {
            break;
          }
        }
        if (match) {
          returnElements.push(current);
        }
      }
      return returnElements;
    };
  }
  return getElementsByClassName(className, tag, elm);
};"
set setElementsToEmpty to "function setElementsToEmpty(a){
  for ( var i=0, len=a.length; i<len; ++i ){
    a[i].innerHTML = '';
  }
};
"

set this_url to ""
repeat
  try
    repeat while appIsRunning("Safari")
      tell application "Safari"

        try
          set doc to front document
          set this_url to URL of doc
          do JavaScript getElementsByClass in doc
          do JavaScript setElementsToEmpty in doc
          if this_url starts with "http://www.google.com/search?" then
            -- Regular google search
            -------------------------------------------------------------
            --
            -- ZAP CONTAINING DIV BY ITS ID
            --
            -------------------------------------------------------------
            do JavaScript "document.getElementById('rhsline').innerHTML = ''" in doc
            do JavaScript "document.getElementById('tads').innerHTML = ''" in doc
          else if this_url starts with "http://search.yahoo.com/search" then
            -- Regular yahoo search
            do JavaScript "document.getElementById('east').innerHTML = ''" in doc
            -------------------------------------------------------------
            --
            -- ZAP DIVS OF CERTAIN CLASS WITHIN  CERTAIN CONTAINING DIV (COULD BE NULL)
            --
            -------------------------------------------------------------
            do JavaScript "setElementsToEmpty(getElementsByClassName('ads horiz',null,document.getElementById('main')))" in doc
          -------------------------------------------------------------
          --
          -- ADD OTHER SITES HERE
          --
          -------------------------------------------------------------
          end if
          -- set delay amount accordingly to manage how much CPU to devote to blocking ads
          -- recommended between 1.0 and 0.001 seconds
          delay 0.1
        on error errText number errNum
          -- if anything but doc changing before ads removed or safari open but no windows
          -- pause so that CPU isn't stolen
          delay 2
        end try
      end tell
    end repeat
  on error errText number errNum
    if errNum is equal to -128 or errNum is equal to -609 then
      -- safari no longer open
    else
      display dialog errText & " " & errNum
    end if

  end try
  delay 5
end repeat

-- from http://codesnippets.joyent.com/posts/show/1124
on appIsRunning(appName)
  tell application "System Events" to (name of processes) contains appName
end appIsRunning

Download the ad blocker with all the sites I’ve blocked ads on

Imagine how strong this ad blocker could be if an army of users were updating the site specific zaps. Imagine the cold war it would start.

Note: Some sites inject their ads deeper into the content of the site like youtube does with its flash videos. I haven’t come up with a way to single this one out yet…

Hack infinite scroll javascript with infinite auto-scroll to bottom of page

Thursday, January 7th, 2010

This is a hack to have your browser load all search results when a page is using jQuery’s infinite scroll feature, like this site: http://instantwatcher.com/titles/all?infinite=1.
Here’s the client side javascript to keep auto-scrolling this page to the bottom, thus triggering infinite scroll to load more results. It runs until there are no more results to load:

    function scrollToBottom(){
      bottom = document.body.scrollHeight;
      current = window.innerHeight+ document.body.scrollTop;
      if((bottom-current) >0){
        window.scrollTo(0, bottom);
        setTimeout ( 'scrollToBottom()', 1000 );
      }
    };
    scrollToBottom();

I run this on Safari using this short applescript:


tell application "Safari"
  set doc to front document
  set this_url to URL of doc
  do JavaScript "
    function scrollToBottom(){
      bottom = document.body.scrollHeight;
      current = window.innerHeight+ document.body.scrollTop;
      if((bottom-current) >0){
        window.scrollTo(0, bottom);
        setTimeout ( 'scrollToBottom()', 1000 );
      }
    };
    scrollToBottom();
    " in doc
end tell

Try it!

Javascript: scroll to bottom of page/window

Thursday, January 7th, 2010

Seems to work great in Safari and Firefox. Will check IE later…


window.scrollTo(0,document.body.scrollHeight);

Try it!

Open lines in clipboard as URLs in Safari

Saturday, November 14th, 2009

I used to use a script that took the lines of a text edit document and opened each as a url in Safari. This was useful when I used text-edit. Now I’m using Terminal and other apps to gather urls so its easier and more general to just copy the urls to the clipboard (CMD + C) and run this script. The applescript below opens each line of the copied text as a new safari window setting the line as the url.


set clipboard_contents to the clipboard as text
set urlList to paragraphs of clipboard_contents
set numURLs to (count urlList)
tell application "Safari"
	activate
	repeat with this_url in urlList
		if length of this_url is greater than 0 then
			set this_doc to make new document at end of documents
			set URL of this_doc to this_url
		end if
	end repeat
end tell

Note: There are lots of hacks to open a list of urls as tabs, find one and use it if your desire tabs. I didn’t post this for tabs because as far as I know opening a bunch of tabs always requires and awkward hack like using system events and keystrokes etc.

Sun messenger express delete all messages in inbox: applescript + javascript

Sunday, October 25th, 2009

I recently received a notice that I had filled my email storage quota for my NYU email account. About a month or two ago I set up my gmail account to fetch my NYU account mail, so all my emails are backed up with google. I logged into NYU Home’s webmail client (Sun Java™ System Messenger Express) and was dismayed to find out that the only way to delete all the messages in my inbox was to select “all” 20 messages and hit delete over and over again (see update below). I tried going to the Folders tab, selecting Inbox and clicking delete but I got this error: “System folders – cannot be deleted”. It seems impossible to purge your inbox using the “Mess Express”.

Since applescript can tell Safari to run arbitrary javascript on an open webpage, I hunted (rather painfully, “Mess Express” is about 10 html frames within frames, no objects have ids and the js is split between many files) through the source and found the necessary functions to automate deletion. Here’s my applescript. Before running you should have signed into you messenger express client and be viewing your inbox in the frontmost Safari window:


tell application "Safari"
	set doc to front document
	repeat (4945 / 20) times
		repeat until source of doc contains "</html>"
			delay 1
		end repeat
		do JavaScript "parent.mailFrame.selectAll('true');" in doc
		do JavaScript "parent.mailFrame.delmsg();" in doc
		delay 1
	end repeat
end tell

Note: Replace 4945 with the total number of messages you need to delete.

After the script has successfully finished (it should take less than 2 seconds per 20 emails deleted), click on the Folders tab at the top,
folder tab link
then hit Empty Trash.
empty trash

If there is an easier way to delete all messages in your inbox, I’d love to know.

Update: It is possible to speed this up even more by setting the message view count from 20 to 100 by clicking on the Options tab then selecting Appearance and changing the Message List View amount to 100.

messenger express appearance options

Search current Safari selection on wikipedia etc.

Friday, August 21st, 2009

I love SafariSIA, but to shave even more seconds off a search query I’ve made the following applescript which uses the current selection on the current safari page and searches that as a query on wikipedia (or whatever site you’d like to change it to):



-- some pieces adapted from
-- http://scriptbuilders.net/files/safariselectiontoscripteditordebugger1.0.1.html

-- If Safari isn't running, notify and terminate.
tell application "System Events"
	if (exists process "Safari") is false then
		display dialog "Safari isn't running. The script will now terminate." buttons ¬
			{"OK"} default button 1 with icon 0
		return
	end if
end tell

tell application "Safari"
	-- If no document, notify and terminate.
	if (exists document 1) is false then
		display dialog "No Safari document was found. The script will now terminate." buttons ¬
			{"OK"} default button 1 with icon 0
		return
	end if

	-- Attempt to get the selected text. Notify and terminate if no selection.
	try
		set selected_text to (do JavaScript "getSelection()" in document 1) as string
		if selected_text is "" then
			error
		end if
	on error
		-- Advise user to copy and paste text into new script document 'cause somethin' failed.
		set selected_text to text returned of (display dialog "Safari was unable to obtain the selected text. Copy and paste " & ¬
			"it into the new document which has been created in Script Editor." buttons ¬
			{"OK"} default answer "" default button 1 with icon 1)

	end try

	-- construct url with selected text as query
	set theURL to "http://www.wikipedia.org/search-redirect.php?search=" & selected_text & "&language=en&go=++%3E++&go=Go"
	make new document at end of documents with properties {URL:theURL}
end tell

Change the set theURL to ... line to whatever site you’d like to search. For example:

Google Images:


set theURL to  "http://images.google.com/images?q="&theURL&"&hl=en&btnG=Search+Images"

Google Maps:


http://maps.google.com/maps?client=safari&rls=en&q="&theURL&"&oe=UTF-8&um=1&ie=UTF-8&sa=N&hl=en&tab=wl

English to spanish dictionary look up on WordReference:


set theURL to  "http://www.wordreference.com/es/translation.asp?tranword="&theURL

Mininova (sorted by number of seeds):


set theURL to "http://www.mininova.org/search/"&theURL&"/seeds"

Automagically drag url from current Safari page to desktop (as .webloc file)

Thursday, August 13th, 2009

I like to hold onto web pages in an albeit clumsy way.
Drag url by favicon to desktop
I drag the url by the favicon onto my Desktop. Apple creates a .webloc file there, which I can later double-click on to reopen the page.

My problem arises when I have so many windows I open that I can’t even find the Desktop. I could push F11 after I’ve started dragging the URL, but why not script this whole operation.


tell application "Safari"
	set current_url to URL of front document
	set current_name to name of front document
end tell
tell application "Finder" to set webloc to make new internet location file to current_url at desktop with properties {name:current_name}

Note: Forgive that long last line, but I’d rather have it easier to paste than read (Script Editor does not handle broken up lines well).

Note: These .webloc are also great as attachments in emails where long URLs often get broken up. According to http://www.fileinfo.com/extension/webloc these files are not universally open-able but widely accepted enough that accompanying a plain text URL in an email usually provides a safe bet of the recipient actually opening the page.

Restart Safari restoring windows and tabs

Thursday, August 13th, 2009

Safari only resets its JRE cache at restart. So when I am developing Java applets and testing them in my Safari browser, I have to restart Safari every time I recompile to view my changes. I have tried deleting the JRE cache manually and using Safari > Empty Cache, but the JRE cache is still there. Only restarting has worked.

So the least I could do is write an applescript to restart Safari and restore any windows and tabs I might have had open. Here’s my script:


-- Get all windows and their tabs as nested list
set url_list to {}
tell application "Safari"
	activate
	set tab_list to {}
	repeat while (count of documents) > 0
		set doc_count to count of documents
		set this_doc to front window
		set tab_list to tab_list & URL of front document of this_doc
		tell application "System Events" to keystroke "w" using {command down}
		if doc_count is not equal to (count of documents) then
			set url_list to url_list & {tab_list}
			set tab_list to {}
		end if
	end repeat
end tell

-- Save list to preference files just in case
-- convert url list  to text
set old_delim to AppleScript's text item delimiters
set url_text to ""
set AppleScript's text item delimiters to " "
repeat with this_item in url_list
	set url_text to (url_text & this_item as text) & return
end repeat
set AppleScript's text item delimiters to old_delim

-- get path to prefs file where URLs will be stored
set prefs_folder to path to preferences folder as string
set prefs_file to prefs_folder & "Safari Saved URLs"

try
	set open_file to ¬
		open for access file prefs_file with write permission
	-- erase current contents of file:
	set eof of open_file to 0
	write url_text to open_file starting at eof
	close access open_file
on error
	try
		close access file prefs_file
	end try
end try
display dialog url_text

-- Restart Safari
tell application "Safari" to quit
repeat while appIsRunning("Safari")
	delay 1
end repeat
tell application "Safari" to activate

-- Restore windows and tabs
tell application "Safari"
	repeat with i from 1 to length of url_list
		set this_doc to front document
		if i is not equal to 1 then
			set this_doc to make new document at end of documents
		end if
		repeat with j from 1 to length of (item i of url_list)
			set URL of this_doc to item j of (item i of url_list)
			if j is not equal to length of (item i of url_list) then
				tell application "System Events" to keystroke "t" using {command down}
			end if
		end repeat
	end repeat
end tell

-- http://codesnippets.joyent.com/posts/show/1124
on appIsRunning(appName)
	tell application "System Events" to (name of processes) contains appName
end appIsRunning

Note: I save the list of window and tab URLs as text in ~/Library/Preferences/Safari Saved URLs. That way if something goes wrong I have a second chance at retrieving the URLs I had open. Perhaps this is entirely unnecessary.