Pistachio Shells Candle Holder

I confess, I’m a compulsive pistachio eater. About three years ago I started accumulating shells in a bag, because I was convinced that they would be useful for something, fascinated by their shape and color, or perhaps only in the hope that being impressed by the volume of the bag that grew rapidly could help me in managing my addiction (as some smokers do by building castles of empty cigarette packs). Or maybe I’m just suffering from a selective form of disposophobia. I already had a fair amount of them, when I decided to search the Internet if someone had done something beautiful and / or useful with a weird collection similar to mine. What I present in this post is a combination of a couple of ideas stolen from the internet and of my experience accumulated when in view of a recent Christmas, I made a number of candle holders to give them away. You will not need to devour a huge amount of pistachios before starting, a candle holder is made with the shells of about 30 of them. Obviously if you are suffering from my own pathology you will soon have a variety of shapes and sizes of shells available which could make your work a little easier, but it is not very important: we are going to use the shells as if they were petals, which, as everything in nature, are not all perfectly identical.

Materials needed

  • Tealight candle.
  • Pistachio shells.
  • Hot glue gun with cartridges.
    I used a small gun (40W) which is even better especially if you want to work with children.
  • Cardboard, better if thick (otherwise you may need glue too).
  • Pencil or pen to help you cut out the base.
  • Scissors to cut the base.
  • Two to three centimeters of adhesive tape for the internal disk.
  • Newspaper or anything to protect your work plan.
  • Optional: small containers to group pistachio shells according to their size.
  • Optional, if you want to color the shells: acrylic paints and freezer bags.

Procedure

The first step is to cut out a cardboard base. Cut out a kind of pin with the help of anything of the right size (for example a glass or the cap of a jar): the large circle should have a diameter almost double that of the candle (or about 7 cm/2.5”), the handle a diameter of about 3 cm/1.2”. If you are using a thick enough corrugated cardboard, just one layer is enough. Otherwise you may need to cut out two layers (1) and glue them together (2).

Before starting to glue the shells, it is better to prepare everything else, to avoid having to put the gun by the side while it is hot (mine drools, and honestly the ethylene vinyl acetate that dissolves does not produce the most enjoyable of the odors) or to avoid to having to turn it off and then having to bring it back to temperature.
For example, to avoid wasting too much time cleaning the shells and looking for the best shape while the gun is on, first it is useful to remove the skin from those shells with too much of it and place them in three containers according to their shapes (3), in particular if we find slightly flatter and larger shells they will be perfect for the outer disk and for the flower of the handle, the smaller shells are fine for the innermost disk, the intermediate shells for the intermediate corollas .
In fact, we will make four discs of sepals and petals (call them whatever you want, if you have a botanist in your family to make you shiver).

Now cut a strip of cardboard about the same height as the candle and long enough to wrap it (4). With the help of a little tape we make a cardboard ring and wrap it around the candle (5). Later we will glue the innermost layer of pistachios on it.

Load the gun, turn it on and wait for it to warm up according to the manufacturer’s instructions.

Glue pistachio shells around the large circle of the base to make the outer disk: put some glue on the base of a shell (6) and insert it in the thickness of the cardboard by pressing lightly. Repeat with another shell following the perimeter of the base until completing the calyx of the flower (7). If there are, use slightly flatter shells.

Do the same to make a corolla by gluing the shells almost vertically (8), always applying light pressure so that a small notch is created in the cardboard.

Now, applying the glue in a similar way, insert the shells between the two already made disks, placing them at about 45 degrees and in positions offset from the already glued shells (9).

To glue the shells in the inner cardboard ring previously made, this time you need to apply glue on one of the two edges (10). Choose small shells and continue by gluing a shell next to the previous one each time. (11)

To make the flower of the handle you must first glue two shells togheter with a little glue on the base, then add the shells always using glue on the internal base of the same (12). After a few shells, the flower will be too fragile to handle: at this point glue it on the handle of your candlestick, then you can add more petals by gluing them underneath, between the cardboard and the others. (13)

Tips

In order to decreas burrs, glue the shells in an orderly clockwise or counterclockwise fashion (instead of jumping from a point to another). Some burrs are inevitable, but after your candle holder is complete and the glue is cold, you can melt the horrible excess threads of glue by using a hairdryer for 30 seconds.

If you want a colored candle holder, you will have to color the shells before you start. I like them as they are, but I refer to this video which basically recommends to follow these steps:

  • put the shells in a freezer bag
  • pour a few drops of acrylic paint and a little water into the bag
  • close the bag and shake it
  • leave the shells to dry on a sheet of paper

Portacandele con gusci di pistacchio 🇮🇹

Lo confesso, sono un mangiatore compulsivo di pistacchi. Circa tre anni fa ho iniziato ad accumulare gusci in un sacchetto, perché ero convinto che sarebbero serviti a qualcosa, affascinato dalla loro forma e dal loro colore, o forse solo nella speranza che essere impressionato dal volume del sacchetto che cresceva rapidamente mi potesse aiutare nella gestione della mia dipendenza (come fanno certi fumatori quando costruiscono castelli di pacchetti di sigarette vuoti). O forse sono solo affetto da una forma selettiva di disposofobia. Ne avevo già una discreta quantità quando mi decisi a cercare su Internet se qualcuno ne avesse fatto qualcosa di bello e/o utile. Quello che vi presento in questo post è una combinazione di un paio idee rubate su internet e della mia esperienza accumulata quando in vista di un recente Natale, ho realizzato un certo numero di portacandele per regalarli. Non avrete bisogno di divorarne una quantità immane prima di iniziare, un portacandele è fatto con i gusci di circa 30 pistacchi. Ovviamente se siete affetti dalla mia stessa patologia avrete presto a disposizione una varietà di forme e dimensioni di gusci che vi potrebbe rendere il lavoro un po più semplice, ma non è molto importante: andrete ad usare i gusci come se fossero petali, che, come ogni cosa in natura, non sono tutti perfettamente identici.

Materiale necessario

  • Candelina scaldavivande.
  • Gusci di pistacchio.
  • Pistola per colla a caldo con relative cartucce.
    Io ho usato una pistola piccola (40W) che è anche meglio sopratutto se volete lavorare assieme ai bambini.
  • Cartone, meglio se spesso (altrimenti vi potrebbe servire anche la colla).
  • Matita o penna per aiutarvi a ritagliare la base.
  • Forbice per tagliare la base.
  • Due-tre centimetri di nastro adesivo per il disco interno.
  • Giornale o qualsiasi cosa per protegger il vostro piano di lavoro.
  • Opzionale: dei piccoli contenitori per raggruppare i gusci di pistacchio in base alle dimensioni.
  • Facoltativo, se volete colorare i gusci: colori acrilici e sacchetti per congelatore.

Procedimento

Il primo passo consiste nel ritagliare una base di cartone. Ritagliate una specie di birillo aiutandovi con qualsiasi cosa della dimensione giusta (per esempio un bicchiere o il tappo di un barattolo): il cerchio grande dovrebbe avere un diametro quasi il doppio di quello della candela (o circa 7 cm) il manico un diametro di circa 3 cm. Se avete un cartone ondulato abbastanza spesso, basta uno strato. Altrimenti potreste avere bisogno di ritagliare due strati (1) e incollarli tra di loro (2).

Prima di iniziare a incollare i gusci è meglio preparare tutto il resto, per evitare di dover mettere da fianco la pistola mentre è calda (la mia si sbrodola, e in fondo l’etilene vinil acetato che scioglie non produce il migliore degli odori) o peggio di spegnerla e doverla riportarla nuovamente a temperatura.
Per esempio, per evitare di perdere troppo tempo a pulire i gusci e cercare quello della forma migliore mentre la pistola è accesa, come prima cosa è utile rimuovere la pellicina da quei gusci ai quali ne è rimasta attaccata troppa e riporli in tre contenitori in base alla loro forma (3), in particolare se troviamo dei gusci leggermente più piatti e grandi saranno perfetti per il disco esterno e per il fiore del manico, i gusci più piccoli vanno bene per il disco più interno, i gusci intermedi per le corolle intermedie.
Realizzeremo infatti quattro dischi di sepali e petali (chiamateli come volete, se avete in famiglia un botanico da far rabbrividire).

Ritagliamo adesso una striscia di cartone alta più o meno quanto la candela e lunga quanto basta per avvolgere la stessa (4). Con l’aiuto di un po’ di nastro adesivo realizziamo un anello di cartone e avvolgiamolo sulla candela (5). Più tardi incolleremo su di esso lo strato più interno di pistacchi.

Caricate la pistola, accendetela e aspettate che si riscaldi seguendo le istruzioni del produttore.

Incollate dei gusci di pistacchio intorno al cerchio grande della base per realizzare il disco esterno: mettete un po’ di colla sulla base di un guscio (6) e inseritelo nello spessore del cartone facendo una leggera pressione. Ripetete con un altro guscio seguendo il perimetro della base fino a completare il calice del fiore (7). Se ci sono, usate i gusci leggermente più piatti.

Fate lo stesso per realizzare una corolla incollando i gusci quasi verticalmente (8), sempre applicando una leggera pressione in modo che si crei una piccola incavatura nel cartone.

Adesso, applicando la colla in maniera simile, inserite i gusci tra i due dischi già realizzati, disponendoli a circa 45 gradi e in posizioni sfalsate rispetto ai gusci già incollati (9).

Per incollare i gusci nell’anello interno di cartone precedentemente realizzato, questa volta dovete applicate la colla su uno dei due bordi (10). Scegliete gusci piccoli e proseguite incollando ogni volta un guscio a fianco di quello precedente. (11)

Per realizzare il fiore del manico dovete prima incollare due gusci con un po’ di colla sulla base, poi aggiungere i gusci usando sempre la colla sulla base interna degli stessi (12). Dopo pochi gusci il fiore risulterà troppo fragile da maneggiare: a questo punto incollatelo sul manico del vostro candeliere, solo dopo potrete aggiungere altri petali incollandoglieli di sotto, tra il cartone e gli altri. (13)

Consigli

Nell’attaccare i gusci, proseguire in maniera ordinata in senso orario o antiorario da uno spazio a quello contiguo (anziché a saltare) diminuirà le sbavature. Queste sono comunque inevitabili, potete però sciogliere gli orribili fili superflui che si creano riscaldando il vostro candeliere completato (a colla già fredda) usando un asciugacapelli per 30 secondi.

Se volete un porta candele colorato, dovrete colorare i gusci prima di iniziare. A me piacciono così come sono, ma rimando a questo video che in pratica consiglia di seguire i seguenti passi:

  • mettere i gusci in un sacchetto per congelatore
  • versare qualche goccia di colore acrilico e un poco d’acqua nel sacchetto
  • chiudere la busta e agitarla
  • lasciare i gusci ad asciugare su un foglio di carta

Scaricare video da RaiPlay 🇮🇹

Recentemente ho notato che la popolare estensione per Firefox Chrome Video DownloadHelper non rileva più i video dalle pagine di RaiPlay.it.
Ecco una soluzione alternativa per scaricare i video usando avconv e Firefox (ma è possibile anche con Chrome), che è anche più affidabile quando si scaricano video di grosse dimensioni, infatti non sembra bloccarsi mai.

  1. Scarica libav-tools o installalo tramite il package management system della tua distro. Per esempio con ubuntu:
    sudo apt-get install libav-tools
    
  2. Attiva i Developer Tools in Firefox con il tasto F12 e poi apri la scheda “Network”
  3. Vai alla pagina contenente il video.
  4. Digita “index” come filtro Url delle richieste http elencate dai Developer Tools, troverai uno o più elementi con estensione m3u8. Si tratta di playlist contenenti gli url dei segmenti del video, oltre ad qualche metadata.
    Se per esempio nella lista c’è index_0_av.m3u8 e index_1_av.m3u8 il secondo file si riferirà probabilmente ad uno stream a risoluzione maggiore di quella del primo. 

  5. Scarica il file index. Il modo più veloce consiste nel cliccare col tasto destro sull’url e poi su “Apri link in nuova scheda”
  6. Naviga nella directory contenente il file scaricato e usa avconv per scaricare lo stream, per esempio:
    avconv -i index_0_av.m3u8 -c copy gazebo.ts
    

    raiplay2
    Il precedente è uno screenshot di una sessione di bash che gira su Windows 10 grazie al Windows Subsystem for Linux (WSL).

Tab notifier for Firefox and SeaMonkey 1.2

Tab notifier for Firefox 1.2.0 has been released and now it supports also SeaMonkey!

Several features have been added since the first release, announced in this blog.

The following localizations are available:

  • Dutch (markh van BabelZilla.org)
  • German (Archaeopteryx)
  • Italian
  • Simplified Chinese (Wang.H.K)
  • Spanish
  • Swedish (Mikael Hiort af Ornäs)

To contribute you can either donate on the add-ons page or translate it in you language with BabelZilla

To report bugs or suggest improvements please post on the support page

A slideshow with Flex

Several months ago we needed a simple slideshow showing some pictures but emphasizing mainly some texts over them. I created a slideshow with the free and open source Flex SDK and TweenMax. You can see a demo here.

How to use it

Download the slideshow and modify the example slideshow.xml. The only other file that you need is slideshow.swf, but if you don’t want to use it on-line but to show images on a local drive use slideshow-local.swf instead. slideshow.xml is the default filename for the configuration file, but you can change it by means of the flashVar “xml“. The most important part of the xml are several slide elements contained in the root element. Each one of them can contain an optional text element and has to contain at least an image element (with its file attribute containing an url or a path depending on the version). The example explains the meaning of other tags and attributes:

<slides>
<!-- style is the css applied to the text (every text block is wrapped in a div) -->
<style>
div{text-align:center;}
.key{font-style:italic;}
small {font-weight:normal;}
</style>
<parameters>
<!-- the first image of every slide shifts vertically and then shrinks toward the bottom of the screen until a size
defined by imagesFinalHeight (e.g. 0.5 means 50% of the height of screen) and with an alpha defined by imagesFinalAlpha
(from 0 invisible to 1 unchanged). If there are extra images in the same slide, these two parameters will define
height and alpha of these images as well -->
<imagesFinalAlpha>0.75</imagesFinalAlpha>
<imagesFinalHeight>0.75</imagesFinalHeight>
<!-- tune is the background music (optional) -->
<tune>http://www.joshwoodward.com/mp3/ht/NoVox/JoshWoodward-HereTodayNoVox-06-LearnToFly.mp3</tune>
<!-- if firstSlideIsBlack is true you need an initial click to start the slideshow (used with a projector) -->
<firstSlideIsBlack>false</firstSlideIsBlack>
</parameters>

<slide>
<image file="http://3.bp.blogspot.com/_N_mOB63qPaE/SwmFueUiiJI/AAAAAAAAMm0/_7CS3KAsp_M/s400/Desktop-Wallpaper.jpg" />
<text>
<![CDATA[<span class='key'>Enter</span>: fullscreen
click on on the left border or <span class='key'>Page Up</span>: go to the previous slide
click or <span class='key'>Page Down</span>: go to the next slide
]]></text>
</slide> 

<slide>
<text>If you put more than one image in the same slide the extra images will scroll from the right to the left</text>
<image file="http://1.bp.blogspot.com/_N_mOB63qPaE/SwmFtcpw54I/AAAAAAAAMmc/fmCo9rSEdI0/s400/Beautifull-Birds.jpg" />
<image file="http://4.bp.blogspot.com/_N_mOB63qPaE/SwmF7LSAa7I/AAAAAAAAMm8/WZArZHzJ1Ws/s400/Red-Bird-Wallpaper.Jpg" />
<!-- the optional attribute delay specifies the number of seconds before the image starts to scroll to replaces the previous one -->
<image delay="2.5" file="http://4.bp.blogspot.com/_N_mOB63qPaE/SwmFt7Kxy0I/AAAAAAAAMmk/CW8cMKBduFY/s400/Birds-Wallpaper.jpg" />
</slide> 

<slide>
<text>
<![CDATA[When you click on the last slide image,
text and sound fade out
<small>The images are taken from collectionbird.blogspot.com
The song is "Lern to fly", by Josh Woodward (www.joshwoodward.com)</small>
]]></text>
<image file="http://1.bp.blogspot.com/_N_mOB63qPaE/SwmFuGRR8nI/AAAAAAAAMms/WOenHtvajMw/s400/Bird-Wallpaper.jpg" />
</slide> 
</slides>

How to build it

Download the same zip file, that contains the source code. It was coded hastily, still I think it’s a good example of what you can do with Flex SDK without framework (that’s with completely free and open source tools and with a target swf really small) and TweenLite or TweenMax. It embeds code of TwinMax that has its own dual license. To build the slideshow you can use Flash Builder (formerly named Flex Builder) or, if you can’t afford to buy it, download the Flex SDK as I did.
An ant script is not provided with the slideshow, but there are a batch file for windows (build.bat) and a shell script for Linux (build.sh) used to build it with Flex SDK 4. You may need to configure them depending on the installation directory of the SDK etc.

Options for a chrome extension: tab notifier 1.1

The version 1.1.0 of tab notifier for chrome has been released. It now stores persistently the list of ignored sites and allows to manage it by means of a option page. Let’s see how it’s done.
To store the options now the extension uses the localStorage attribute of the window object.
This is the simplest part. As we saw in the previous post everytime there is a click on the menu item “Ignore this site” we change the object mutedDomains. Now we also stringify it to store it in localStorage:

var menuItemId=chrome.contextMenus.create({type:"checkbox",
	title:"Ignore this site",
	checked:true,
	contexts:["all"],
	documentUrlPatterns:["http://*/*", "https://*/*"],
	onclick:function(info,tab){
		var hostname = getHostname(tab.url);
		if (hostname) {
			if(info.checked) {
				mutedDomains[hostname]=true;
			} else {
				delete mutedDomains[hostname];
			}
			window.localStorage.ignoredSites = JSON.stringify(mutedDomains);
		}
	}
});

Please note that we don’t need to put the attributes of localStorage inside a namespace (hence ignoredSites is totally arbitrary) because they will be unique for every extension in the way they are unique for each subdomain when they are used in the scope of a web page.

To restore the object we use the following code:

var updateOptions=function() {
	var s=window.localStorage.ignoredSites;
	if (s) try {
		mutedDomains=JSON.parse(s);
	} catch(e){console.log(e);};
};
updateOptions();
window["updateOptions"]=updateOptions;

The function “updateOptions” is attached to the window object to be accessible to the option page as we’ll see pretty soon.
Since now the list of ignored sites won’t be reset at the beginning of every session, it becomes more important to allow the users to manage that list. We can achieve that with an option page. Let’s add a row to the manifest.json file:

{
   "background_page": "background.html",
   "content_scripts": [ {
      "js": [ "tabnot.js" ],
      "matches": [ "http://*/*", "https://*/*" ]
   } ],
   "description": "Notifies when the title of a tab changes",
   "icons": {
	"16": "icon16.png",
	"48": "icon.png"
   },
   "name": "Tab notifier",
   "permissions": [ "tabs", "notifications", "contextMenus" ],
   "options_page": "options.html",
   "homepage_url": "http://unusoft.it/blog/index.php/tab-notifier/",
   "update_url": "http://unusoft.it/extensions/updates.xml",
   "version": "1.1.0"
}

Then we add two new files to the extensions: options.html and options.js. The first contains the interface of the configuration, here’s the core of it:

<select id="ignoredSitesSl" multiple="multiple" size="16">
</select>
<div class="buttons">
<div class="left">
	<button id="removeBt">Remove selected</button>
	<button id="selectAllBt">Select all</button>
</div>
<div class="right">
	<button id="saveBt">Save</button>
	<button id="resetBt">Reset</button>
</div>
</div>
<script type="text/javascript">initOptionsPage();</script>

ignoredSitesSl will be programmatically filled when initOptionsPage (contained in options.js) is invoked. To see the full code follow the instructions in the previous post.
Beside the initialization of the interface options.js contains the javascript code bound to the click event of every button in the page:

(function() {
    var ignoredSites,ignoredSitesSl,removeBt,selectAllBt,saveBt,ResetBt
    ,load=function() {
		var s=window.localStorage.ignoredSites;
		ignoredSites={};
		if (s) try {
			ignoredSites=JSON.parse(s);
		} catch(e){console.log(e);};

		for (i in ignoredSites) {
			var o = document.createElement("option");
			o.innerHTML=i;
			ignoredSitesSl.appendChild(o);
		}
		disButts(true);
    }
	,disButts=function(a) {
		resetBt.disabled = a;
        saveBt.disabled = a;
	}
	
    window.initOptionsPage = function() {
        removeBt = document.getElementById("removeBt");
		selectAllBt = document.getElementById("selectAllBt");
		saveBt = document.getElementById("saveBt");
		resetBt = document.getElementById("resetBt");
		ignoredSitesSl = document.getElementById("ignoredSitesSl");
		load();
        removeBt.addEventListener("click",function(){
		var d=false;
			for(var c=0;c<ignoredSitesSl.options.length;c++) {
				var o=ignoredSitesSl.options;
				if(o.selected) {
					delete ignoredSites[o.innerHTML];
					ignoredSitesSl.removeChild(o);
					c--;d=true;
				}
			}
			if (d)disButts(false);
		},false);
        selectAllBt.addEventListener("click",function(){
			var l=ignoredSitesSl.options.length;
			for(var c=0;c<l;c++) {
				ignoredSitesSl.options.selected=true;
			}
		},false);
        saveBt.addEventListener("click",function(){
			window.localStorage.ignoredSites = JSON.stringify(ignoredSites);
			chrome.extension.getBackgroundPage()["updateOptions"]();
			disButts(true);
		},false);
        resetBt.addEventListener("click",function(){
			while (ignoredSitesSl.options.length> 0) ignoredSitesSl.options.remove(0);
			load();
		},false);
    };
})();

As you can see from the rows 48 and 49, when the button “Save” is pressed two actions are performed: the list of ignored sites is stored and the updateOptions function in the background page is called. In this way the background page always uses the most recent version of the list.

Tab notifier for Firefox

Tab notifier for Firefox has been released.
This extension shows a desktop notification when the title of a tab changes while the tab is not focused.
Here’s some features of this extension that are not yet available in the chrome version:

  • You can manage the list of web sites to block. Update (21/12/2011): added this feature to tab notifier for chrome as well
  • You can block a web site just with one click when the first notification appears.

You can install it from the Mozilla Add-ons web site.

Ontogeny of a chrome extension: the tab notifier

The tab notifier is our solution for who uses messaging web clients but don’t want to renounce nice toasts popping up as alerts. Let’s see how it’s done, in order to be able to customize it or to learn something that may be useful to make other extensions.
To develop a chrome extension you need to know javascript and few more things.
Let’s start from the begin.
We need to create at least a manifest.json file and a html file with the javascript code that will be run in background. A first simple technique that we can use to detect title changes is to compare every few seconds the title of every tab of every window with the previous value. We already know that we are going to do it in a better way, but getting the list of the tabs is definitely one thing that can turn out useful, so let’s start from here.

Here’s the file manifest.json:

{
	"background_page": "background.html",
	"description": "Notifies when the title of a tab changes",
	"name": "Tab notifier",
	"permissions": [ "tabs" ],
	"version": "0.1"
}

Note that we need the tabs permission (as every time we need to use APIs from the chrome.windows or chrome.tabs namespace). This makes a warning show up when you install the extension from a compressed .crx file, but not when you install it form an unpacked directory (which it’s what we are going to do as long we are still developing it).

Here’s the file background.html:

<html><head><script>

var pollInterval = 1000 * 20;  // 20 seconds
var globalTabs = {};

function populateTabs(winList) {
    var newTabs = {}; //scanned tabs
    for (x=0;x<winList.length;x++) {
		var win=winList[x];
		win.tabs.forEach(function(tab) {
			if (tab.title) {
				var prevTab=globalTabs[tab.id];
				if (prevTab!==undefined && prevTab.title!=tab.title && prevTab.url==tab.url && (!tab.selected || !win.focused)) {
					var popup = window.webkitNotifications.createNotification(tab.favIconUrl ? tab.favIconUrl : "", "", tab.title);
					popup.show();
				}
				newTabs[tab.id]={title:tab.title, url:tab.url};
			}
		});
    }
	globalTabs=newTabs;
}

function check()
{
	chrome.windows.getAll({'populate' : true}, populateTabs);
	window.setTimeout(check, pollInterval);
}

</script></head><body onload="check()"></body></html>

The background page is loaded when the plug-in is started or reload, and it contain only the javascript code bound to the load event of the page. This code scans all the tabs every 20 seconds, please note from the row 13 that the pop up is showed only when a title is changed but the url is not (which means that the title has been changed by some javascrit code because the page is still the same) and only if the tab is not opened and the windows focused (that’s it only if the user is not seeing what it’s happening).
To try the extension go to the page chrome://extensions/, active the “Developer mode” and click on “Load unpacked extension…”, going to the directory where our two files are.
To test it more conveniently you may need a small html page like this. It generates a scrolling title (which ideally is one of the things that we wouldn’t like to cause a notification but it’s perfect for our aim because it keeps on generating the event that we are trying to detect, anyway we’ll try to distinguish a scrolling title from an actual message notification, in some cases, later on, not showing it a second time after we close it, refer to cyclic messages detection). We can experiment some defects easy predictable. Definitely the more obvious is that we can have a delay after the tile change. This can be acceptable when we receive an email, but can be intolerable when we receive a instant message. We can play with the pollInterval variable, however, another defects is that a script has frequently to go through all the tabs even when nothing is happening, and decreasing pollInterval would make things worse.

I believe in a better way (content scripts).

Instead of polling every tab we can proceed the opposite way. We can use the DOMSubtreeModified events in every tab we want to monitor to detect at once every change of the title. This is possible because chrome allows us to inject javascript in the pages. Since this injected javascript can access only to a small subset of the chrome APIs, we need to send a message to the background page and let it do the rest. To inject javascript code we have to add the content_scripts permission and the new field content_scripts to the manifest.json file:

{
	"content_scripts": [
		{
			"matches": ["http://*/*", "https://*/*"],
			"js": ["tabnot.js"]
		}
	],
   "background_page": "background.html",
   "description": "Notifies when the title of a tab changes",
   "name": "Tab notifier",
   "permissions": [ "tabs", "notifications" ],
   "version": "0.2"
}

our content_scripts field says that the javascript in tabnot.js will be injected in every page (but just if it’s the top frame) whose the url matches “http://*/*” or “https://*/*” (that’s for every page in a web site). This code will run inside an isolated world, so we don’t have to worry about clashes with any javascript in the monitored pages.

The content of tabnot.js file is:

var titleEl = document.getElementsByTagName("title")[0];
var docEl = document.documentElement;

if (docEl && docEl.addEventListener) {
	docEl.addEventListener("DOMSubtreeModified", function(evt) {
		var t = evt.target;
		if (t === titleEl || (t.parentNode && t.parentNode === titleEl)) {
			titleModified();
		}
	}, false);
} 

function titleModified() {
	chrome.extension.sendRequest("");
}

We’ll make more complex the function titleModifier to fit the needs will occur. Now it’s pretty simple: it send a message to the background page (as we said the content scripts can’t use all of the chrome APIs but can, off course, communicate with other parts of the extension).
This is the new background.html:

<html><head><script>

function init() {
	chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
		var tab=sender.tab;
		chrome.windows.get(tab.windowId, function(win) {
			if (tab.selected && win.focused) return;
			var popup = window.webkitNotifications.createNotification(tab.favIconUrl ? tab.favIconUrl : "", "", tab.title);
			popup.show();
		});

	});
}

</script></head><body onload="init()"></body></html>

The _request_ parameter of the callback function passed as listener will be always an empty string according with the sendRequest inside tabnot.js. However we have all the information we need inside sender.tab.
Please note that, unlike what we could do with the previous method, when after any change to the code we needed to reload only the extension, now we need to refresh also the monitored tab in order to reload the injected javascript.
Testing it with this page we’ll see that that shortly the screen will be filled with notifications. Probably it’s better if every tab can trigger just one notification each time, and that if the title changes again while a notification is still open the old notification will be replaced accordingly. In this way scrolling titles and cyclic messages will be readable the same way you can do it on the tab.
The chrome API offer a simple way to achieve that, the property replaceId of the notification object. We set it before showing the popup and when we try to show it and a popup with the same value of replaceId is already open then the old one will be replaced. The API documentation says that the id of a tab is unique in a session, then we can use it as replaceId value.
Another desirable feature is that capability to open the tab when you click on the popup showed by it. Then we’ll close the popup because its job is done.Here’s the third version of the background page:

<html><head><script>

function init() {
	chrome.extension.onRequest.addListener(function(request, sender, sendResponse) {
		var tab=sender.tab;
		chrome.windows.get(tab.windowId, function(win) {
			if (tab.selected && win.focused) return;
			var popup = window.webkitNotifications.createNotification(tab.favIconUrl ? tab.favIconUrl : "", "", tab.title);
			popup.onclick=function() {
				this.cancel();
				chrome.tabs.update(tab.id, {'selected' : true}, null);
				chrome.windows.update(tab.winowId, {'focused' : true}, null);
			}
			popup.replaceId=tab.id;
			popup.show();
		});

	});
}

</script></head><body onload="init()"></body></html>

Cyclic messages detection

We have built something that detects promptly every change of title in the tabs but we’d like to show a notification only when it’s useful (and not annoying…).
Let’s consider for instance a case that can be applied to several web sites. When an instant message arrives, Facebook changes the title alternatively from the original value (e.g. “Facebook”) to something like “Charlie Brown messaged you” and vice versa. The user may not be interested in the message, and she can just choose to close the notification, but, if we simply show the notification every time a title change (as we have done so far), the notification will keep on showing up. To avoid this we may use an algorithm as complex as we like, but we are going to restrict the scenario to the case of messages showed always in the same order and all different from each other, which should cover all of the relevant eventualities.
We use an array containing the titles that could be part of a cyclic message.
If the title changes twice within 15 seconds we push the titles in the array, but if the title changes slower we empty the array.
As long as a change occurs fast enough we compare the new title with an old one (accordingly with an index increased every time there is a match and reset when there isn’t) and every time there isn’t a match we push it in the array. When the title matches the very last element of the array the boolean cycle is set to true. The comparison goes on with the following changes of the title and cycle will be reset the first time a new title doesn’t match an old one. If you close a popup during a sequence detection every new change to the title while cycle is true won’t trigger a new notification.
The cyclic messages detection can be implemented in the content script or in the background page. Every choice has its own pro and con but they are not a big deal. In the current version (1.0) of the tab notifier the detection is implemented in the content script (tabnot.js) and the requests sent to the background page contains a string that indicates whether to show or to hide the notification, while the background page distinguishes the reason of the closing of a popup and when it’s due to a click of the user on the closing icon (that’s, if you look into the code, when popup.closingReason is undefined) it uses the API chrome.tabs.sendRequest to communicate it to the content script.

Other improvements

To see the full code you can, after installing the extension, go to the directory where the files are stored by chrome, that should be ~/.config/google-chrome/Default/Extensions/ on linux, something like C:\Users\*UserName*\AppData\Local\Google\Chrome\User Data\Default\Extensions in a recent windows or C:\Documents and Settings\*UserName*\Local Settings\Application Data\Google\Chrome\User Data\Default\Extensions in an older version of windows.
The code is a bit more complex compared with the one in this post. In fact, beside the cyclic messages detection, it implements some small extra ideas. For instance it reproduces the behaviour of a IM client also when it hides an open notification when the tab that triggered it is opened manually (this is done by means of the events onSelectionChanged and onFocusChanged).

What we think is really important is to leave the user free to decide what has to be notified and what must not. Which actually is another excuse to introduce another magic of the chrome extensions: the context menus.

Let’s change again the manifest:

{
	"content_scripts": [
		{
			"matches": ["http://*/*", "https://*/*"],
			"js": ["tabnot.js"]
		}
	],
	"icons": {
		"16": "bell.png"
	},
   "background_page": "background.html",
   "description": "Notifies when the title of a tab changes",
   "name": "Tab notifier",
   "permissions": [ "tabs", "notifications", "contextMenus" ],
   "update_url": "http://unusoft.it/extensions/updates.xml",
   "version": "1.0.0"
}

The highlighted changes show a new permission (contextMenus) that allows us to use the APIs in the chrome.contextMenus namespace. The icon 16×16 is showed next to a new “Tab notifier” item on the context menu by chrome. Just right-click on a web page too see it.
In the background page we create a checkbox item in the context menu of chrome titled “Ignore this domain for the current session” with chrome.contextMenus.create during the initialization and we check or un-check it by means of chrome.contextMenus.update whenever the user selects a different tab. When the user clicks the menu item the extension updates a set of blocked hostnames. Here’s the creation of the item:

var menuItemId=chrome.contextMenus.create({type:"checkbox",
	title:"Ignore this domain for the current session",
	checked:true,
	contexts:["all"],
	documentUrlPatterns:["http://*/*", "https://*/*"],
	onclick:function(info,tab){
		var hostname = getHostname(tab.url);
		if (hostname) {
			if(info.checked) {
				mutedDomains[hostname]=true;
			} else {
				delete mutedDomains[hostname];
			}
		}
	}
});	

getHostname is a simple function that extracts the hostname by mean of a regular expression. contexts determines the types of elements of the page that will have the item in their context menu, documentUrlPatterns limits the documents that will have the item in their context menu on the basis of the url, in our case it’s equal to the content_scripts.matches field of the manifest, that limits the monitored pages. Here’s the function used to update the menu item:

function updateMenu(url) {
	var hostname = getHostname(url);
	chrome.contextMenus.update(menuItemId,{checked:hostname.length>0 && (hostname in mutedDomains)});
}

Ideas for the future

Maybe you don’t like the matter that the list of ignored domains are not kept stored between two consecutive sessions. Indeed, doing that would be easy enough thanks to a new feature of Html5, localStorage. But I wouldn’t do that until we enable the user to see and edit directly the list. We can do that adding an option page, and with that we can do even more like managing regular expressions to ignore groups of url or subdomains (besides to ignore single urls). The list should be initially filled with a default set of web sites that already use desktop notifications.
Html5 audio may helps to add another cool feature, that’s configurable audio notifications (that have to be disabled for a configurable list of web sites, initially filled with well-known sites that already play sounds when they have something to notify.

Update (21/12/2011): by the time you read this post some of the listed features will have been implemented.
Please check the changelog

Notifications for everyone

If you use one or more web-based emails or web thin clients for Instant Messaging such as Yahoo! Messenger, GMail chat or Facebook, it’s probably because for some reason you find them better than the equivalent desktop applications, or likely you don’t want to install stuff at the office, or you can’t do it. A disadvantage of this solution is that you don’t have a visual notification when a new email or chat message arrives (at lest so far, and unless your browser is chrome and your web-mail is gmail (which uses the desktop notifications) or you can take advantage of some other combination of browser-plugin-webclient using HTML5 Notifications or similar, which is not my case).

If you can install at least one desktop application, an excellent “all-in-one” solution is a multi-protocol IM program such as pidgin that notifies also new incoming emails for some kind of mailboxes. If you opt for pidgin you should install a plug-in that shows notifications in a “toaster popup”, for this porpuse  I used guification 2 for long, but I don’t think it’s nice with the default theme. To install a theme, after downloading it, open the themes tab (Tools>Plugins>Guifications>Configure Plugin>Themes), drag the downloaded file and drop on it. This post shows a screenshot of guification with the Green Toast Theme. On a recent linux distribution you may prefer to activate the pre-installed libnotify plug-in.
Pidgin is not the only multiprotocol chat client out there, I’d like to mention at least Empaty.

An alternative for users without administrator privileges is to install a suitable extensions for your browser. I checked the chrome web store and there were several of them. Some act as proper email clients, which means that you have to configure them the same way you would do it with your desktop email application, others use specific ways to check the mailbox such as the Yahoo! Mail API, I didn’t find an all-in-one solution (please post it if you find something with nice alerts for any of the major browsers).

Let’s do it ourselves

The basic idea here is that to solve a simple problem you should need a simple solution. Most of the mentioned web clients change the title of the page to tell the user that something happened. If we can detect and correctly interpret these alerts we can take advantage of that, in this way we won’t need to poll the mailboxes duplicating the work of the web-based mails and we won’t need to configure anything. Basically we won’t need to reinvent te wheel, the web clients are there and usually they are good enough, we just need to add the things that they need: the desktop notifications.
This is what the Tab notifier for chrome does. In the next post we’ll see how it is made, using it as an excuse to explore some of the capabilities in developing chrome extensions.