A Firefox Webextension to save interesting stuff found online.

A Firefox Webextension to save interesting stuff found online.

TL;DR; My journey  writing a Firefox add-on to help saving links. You can find the add-on here, and the code on this Github repo.

ABSTRACT:

As my first post, I would like to introduce you a little Firefox add-on that I wrote, using Webextension.

My add-on’s purpose is to save and download a json file containing the title, the url and an user comment of a webpage.

It is intended to keep track of interesting/cool/funny stuff users encounter surfing the net.

It is written in Html/Javascript, although I’m not a Javascript guy: writing it has been a pain, because I don’t like very much Javascript and I had to  ask some experts to get work done (I didn’t quite get the definitive answer, but I got some useful insight for sure!).

USEFUL RESOURCE

Webextensions are a new browser extension API aimed to develop a cross-browser system to develop add-ons.

This page really helped me getting started.

Lurking also on this Github repo  is also very useful.

SHIT HAPPENS

Unluckily I also discovered what I would need had some little bugs.

However, I managed to make it work.

GIMME THE CODE

Ok, going to speak about the actual code now.

Here is what I wanted to come to:

txt_add-on

This is the resulting  txt file being downloaded that we can later access with some Python scripts in order to generate some kind of “saved link reference”.

First thing first, to make this happen, you have to write your manifest, here is “manifest.json“:

{
"browser_action": {
"browser_style": true,
"default_title": "Visto nel Web by CS PRIO30",
"default_popup": "save_url.html",
"default_icon": {
"32" : "save32.png"
}
},
"description": "Visto nel Web by CS PRIO30",
"icons": {
"48": "save48.png",
"32": "save32.png"
},
"manifest_version": 2,
"name": "Visto nel Web by CS PRIO30",
"permissions": [
"tabs",
"downloads",
"activeTab"
],
"page_action": {
"default_icon": "save.png",
"default_title": "Visto nel Web by CS PRIO30"
},
"version": "1.1"
}

This file is intended to declare all the necessary permissions and generic information such as add-on name, title, and icons.

For my main icon, I chose this one:

save

Kind of intuitive,right?

Dimensions should be 32×32 or 48×48.

Here it is the html interface, “save_url.html

<!DOCTYPE html>

<html>
  <head>
    <meta charset="utf-8">
    	<link rel="stylesheet" href="tabs.css"/>
  </head>

<body onload="save_url_try.js">
<div style="margin-left:5%" id="BODY">
<form>

  <strong>TITOLO:</strong>

  <input type="text" value="" id="TITLE_TAB" style="width:90%;"></input>

  <strong>URL:</strong>

  <input type="text" value="" id="URL_TAB" style="width:90%;"></input>

  <strong>DESCRIZIONE:</strong>

  <textarea rows="5" cols="5" placeholder="Descrizione.." style="width:90%;" id="COMMENT"></textarea>

  <center>
  <a href="" download="" id="SAVE" style="width:90%;" value = "" ><center>SALVA</center></a>
  </center>

  </form></div>
<script src="save_url_try.js"></script>
</body>

</html>

It is showing this popup:

firefox_add-on_vnw

With the body onload parameter, it calls a Javascript file wich actually does the work.

Here it is, “save_url_try.js”

var CONST_DIR = "Visto_nel_Web"

browser.tabs.query({'active': true, 'lastFocusedWindow': true}, function (tabs) {
	var url = tabs[0].url;
	var title = tabs[0].title;

	var x = document.getElementById("URL_TAB");
    x.value = url;
    var y = document.getElementById("TITLE_TAB");
    y.value = title;
});

document.addEventListener('DOMContentLoaded', function() {
  document.getElementById("SAVE").addEventListener("click", salva);
});

function exit_extension(){
	window.close();    //added because if I wrote <HERE> blob resource failed;
}

function onStartedDownload(id) {
  console.log(`Started downloading: ${id}`);
  //window.close();
}

function onFailed(error) {
  console.log(`Download failed: ${error}`);

}

function handleChanged(delta) {  //god bless https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/downloads/onChanged
  if (delta.state && delta.state.current === "complete") {
    window.close();
  }
}

function salva(){
	var d = new Date();
	var year = d.getFullYear();
	var month = d.getMonth(); //starts_with_zero
	var arrayMonth = 	["01_Gennaio","02_Febbraio","03_Marzo","04_Aprile","05_Maggio","06_Giugno","07_Luglio","08_Agosto","09_Settembre","10_Ottobre",						"11_Novembre","12_Dicembre"];
	Date.prototype.getWeek = function() {
        var onejan = new Date(this.getFullYear(), 0, 1);
        return Math.ceil((((this - onejan) / 86400000) + onejan.getDay() + 1) / 7);
    }

    var weekNumber = (new Date()).getWeek();
	var dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];

	var offset = d.getTimezoneOffset();
	var watermark = "Generated by CS PRIO30: for complaints cs.prio30p@gmail.com";
	var comment = document.getElementById('COMMENT').value;
	var titolo=document.getElementById('TITLE_TAB').value;
	var indirizzo=document.getElementById('URL_TAB').value;
	var debug = {WATERMARK: watermark,URL: indirizzo,TITLE: titolo,COMMENT: comment,DATA: d,TIME_MINUTES_OFFSET_BY_UTC: offset};
    var blob = new Blob([JSON.stringify(debug, null, 2)], {type : 'application/json'});
    var urlDOWNLOAD = URL.createObjectURL(blob);
    var downloading = 	browser.downloads.download({
								url: urlDOWNLOAD,
								filename : CONST_DIR + "/" + year + "/" + arrayMonth[month] + "/" + weekNumber + "/Visto_nel_Web.txt"
						});
	downloading.then(onStartedDownload, onFailed);
	browser.downloads.onChanged.addListener(handleChanged);
	// <HERE> window.close();
}

I’m building the download path to be different every year,month, week because I wanted to keep my saving chronologically ordered.

When browser finish the download, I’m calling

window.close()

which is a function to automatically close this popup, so the user shouldn’t be bothered closing it theirself.

I had to insert this tiny piece of code inside another function, because otherwise there could be a problem.

In fact, the window will close before the download actually terminated, making it fail.

So I had to insert a function waiting for the download to complete, and then trigger the exit.

WHAT I LEARNED

  1. I have to start studying Javascript, copy-pasting sometimes works but it is not enough.
  2. How to upload a Firefox extension to Mozilla headquarters.
  3. Mozilla guys are quite some nice people!
  4. They told me to remove console.log code, because they do not reccomend it in production. However add-on has been approved nevertheless

WHAT NEXT

A python script to organize such collected information and render them into a blog post.

I know  some two friends this add-on could prove handy, at least I hope.

 

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s