Birthday Solidarity Action for Jabbar Savalan

Eynulla Fətullayev's was released from prison in Azerbaijan earlier this year, but it hasn't been long before his place was taken by another human rights activist.

Jabbar Savalan will be 20 on 4 September. He was sentenced to 2 1/2 years in prison on made-up drugs charges (not dissimilar to Eynulla). He had posted an entry on Facebook calling for a "Day of Rage" inspired by protests in the Middle East and North Africa.

Amnesty International considers him a Prisoner of Conscience and believes he was punished for peacefully exercising his right to freedom of expression. We would like to think he will be supported by people from the UK and around the world.

Jabbar is a football fan. His favourite team is Liverpool, and his favourite player is Fernando Torres.

If you have time, could you please send birthday cards (football-themed would be great) to

Jabbar Savalan
Detention Centre No. 10
Muzaffar Narimanov Street
Narimanov District
Baku City
AZERBAIJAN

Jabbar’s birthday is on Sunday 4 September. Mail from the UK takes 7-10 days to reach him.

Please include the following message in Azeri in your card (which means ‘Happy birthday Jabbar’):

Ad günün mübarək olsun Cabbar

There's also a 'Free Jabbar' action  that you can take on the Amnesty International UK website.

Finally, the Guardian data blog has posted some fascinating information on how the Former Soviet Union states are doing, 20 years on.

Get excited and make things with science

Last weekend, 19th & 20th June 2010, saw the first Science Hack Day at the Guardian offices in London. Jeremy Keith organised a venue, food and drink and sponsorship for around 100 people to spend two days building small science projects. Saturday morning saw a series of short talks to introduce the event,  give people some ideas of what they might make, and what tools were available to help them make it. 24 hours of hacking and building followed, with presentations and prizes for the best hacks on Sunday afternoon. Ed Gomez has a great write-up of the hack day itself, and the winning hacks. My personal favourites were the Aurorascope, which shows auroral activity by lighting up LEDs inside a ball representing the Earth, and Random Orbit, a RESTful service to track satellites in real time.

I was asked by Jeremy to give one of the talks at the beginning of the hack day, so I chatted a bit about the work we’ve done with astrometry.net to tag Flickr photos with their celestial locations — astrotagging.

Continue reading Get excited and make things with science

Solving a browser bug with invalid HTML

Recently, I’ve been involved in building Solar Stormwatch, a citizen science project which asks people to look for by watching videos of data recorded by the wide-field cameras on the STEREO spacecraft. We ask people a series of questions about each video and record their answers. The answers they submit then determine subsequent questions that they’ll be asked. Answers are tracked in the underlying API by assigning each answer a numerical ID, so the markup for an answer looks something like this (using HTML buttons for each answer):
<button value="2">Ahead</button>

If the site user wants to pick the answer ‘Ahead’ to a question, they click this button. With jQuery, I get the value of the button (2) using var answer_id = $(this).val(). Users confirm their answer by pressing a ‘Next’ button to move onto the next question:

<button id="next" value="">Next</button>

I decided to use the ‘Next’ button to store the currently selected answer: $('#next').val(answer_id). I can then look at the value of the ‘Next’ button to decide which question to ask next when they submit an answer. All fairly straightforward HTML and JavaScript, which works in any standards-compliant web browser.

However, after launching the site we started to get strange bug reports on the Solar Stormwatch forum. Buttons were displaying numbers instead of words as their labels. Questions were repeating rather than moving onto the next question for a given answer. It turns out there is a bug in IE6 and IE7, which was being picked up by our users — IE6 and IE7 do not support the value attribute on buttons.

In IE6 and IE7, calling $(this).val() returns the text of a button — ‘Ahead’ in the example given above. Similarly, setting .val() for a button sets the button text, not its value. My first reaction was to change my jQuery code to use .attr('value') to access the value attribute instead. This runs into exactly the same underlying browser bug — .attr('value') gets and sets the button text in IE6 and 7.

To solve this, and get the questions working in IE, I ended up changing the value attribute on buttons to data-value. This is invalid in HTML 4, though it will be valid in HTML 5.

<button data-value="2">Ahead</button>

This works, however, because the default behaviour of HTML parsers in all the major browsers is the same when they encounter an attribute they don’t understand. They simply add it to the DOM as a property of the current node ie. you get something like button.data-value=2. So, wherever I had used .val() in my jQuery code, I now use .attr('data-value') to get and set the value of buttons.

I’m not hugely happy with this solution. It’s invalid HTML and relies on default error-handling behaviour in HTML parsers. It does work cross-browser, and it is robust in that HTML parsers aren’t likely to change the way they handle unrecognised attributes. I wish Internet Explorer supported the standards-compliant, valid solution though.

Jack Pickard – a thoroughly decent chap

About five years ago, I joined accessifyforum, after attending a Carson workshop on web accessibility. I think Jack Pickard was one of the first people I met on the forum, as he made a point of saying hello to new members. A couple of years later, I had the privilege of meeting him at @media 2006. He was a top bloke – friendly, extremely knowledgable about the web, always quick to help with answers to questions or step in to calm down arguments. Over time, I stopped visiting the forum, preferring to use twitter for quick questions about web development. @thepickards was one account I made sure of following, though, for banter about web standards, Newcastle United, beer and jokes. Only this time last week, Jack was telling me how to pronounce Uranus.

I was stunned to hear that Jack died last Saturday night. The comments on his last blog post, accessifyforum, and the reaction on twitter and facebook are a testament to the respect with which Jack was held online. As pixeldiva says, his loss is a tragedy for his wife, his children and family and the wider web accessiblity community.

I used to swap Monty Python jokes with Jack online, so I’m going to end with a clip from The Life of Brian. I think Jack would have agreed with the sentiment.

Azeri bloggers appeal hearing today

Update: The hearing for Emin Abdullayev and Adnan Hajizade has been put back to 8 January 2010. Please continue to send letters on their behalf.

A new urgent action, and update, from Amnesty International on the case of the jailed Azeri bloggers.

Two jailed male Azerbaijani youth activists and bloggers will have their appeal heard in the capital, Baku, on 22 December. Emin Abdullayev and Adnan Hajizade were imprisoned on charges Amnesty International believes to have been fabricated.

The two youth activists and bloggers were arrested on charges of “hooliganism” on 8 July, after reporting to police that they had been assaulted in a restaurant in Baku. Their arrest came just over a week after Adnan Hajizade posted a video critical of the Azerbaijani government on the video-sharing website YouTube. On 11 November, Adnan Hajizade was sentenced to two years, and Emin Abdullayev to two and a half years, after being convicted in an unfair trial.

On 23 November, Emin Abdullayev and Adnan Hajizade’s lawyers filed an appeal against the verdict. On 10 December the lawyers informed Amnesty International that the two youth activists and bloggers had been granted a hearing at the Baku Court of Appeal on 22 December.

Please write immediately in English, Azeri, or your own language:

  • Urging the authorities to ensure that Adnan Hajizade and Emin Abdullayev receive a fair appeal hearing in line with international fair trial standards;
  • Noting that Amnesty International believes them to be prisoners of conscience, detained solely for the peaceful exercise of their right to freedom of expression and association, and is calling for their immediate and unconditional release.

I have prepared updated letters to the Minister of Justice, the Ombudsperson and the Azerbaijan Embassy in London.

Generating astrotags for Flickr photos

In December, I talked at London Web Standards about tagging astronomy photos with position and name information. I mentioned that around 400 photos have been tagged already on Flickr but this is only a tiny fraction of the 4,900 photos that have been solved by astrometry.net. It would be great if the remaining 4,500 photos could also be tagged, and it ought to be straightforward to generate tags for those photos too. Inspired by the iNaturalist Taxonomic Tagging Tool, I’ve written a little astrotagging form for Flickr photos.

When the astrometry.net robot solves a photo on Flickr, it leaves a comment identifying the coordinates of the photo and listing the names of objects in the field.

Hello, this is the blind astrometry solver. Your results are:
(RA, Dec) center:(82.4668973542, 6.33857270637) degrees
(RA, Dec) center (H:M:S, D:M:S):(05:29:52.055, +6:20:18.862)
Orientation:161.45 deg E of N

Pixel scale:67.93 arcsec/pixel

Parity:Reverse (“Left-handed”)
Field size :53.14 x 39.85 degrees

Your field contains:
The star Rigel (βOri)
The star Betelgeuse (αOri)
The star Aldebaran (αTau)
The star Bellatrix (γOri)
The star Alnilam (εOri)
The star Alhena (γGem)
The star Alnitak (ζOri)
The star Saiph (κOri)
The star Mintaka (δOri)
The star Cursa (βEri)
IC 2118 / IC 2118 / Witch Head nebula
NGC 1976 / NGC 1976 / Great Nebula in Orion / M 42
NGC 1990 / NGC 1990
IC 434 / IC 434 / Horsehead nebula
IC 443 / IC 443
NGC 2264 / NGC 2264 / Christmas Tree cluster / Cone nebula

View in World Wide Telescope

—–
If you would like to have other images solved, please submit them to the astrometry group.
Posted 3 weeks ago. ( permalink | delete )

These comments are always in the same format, so it’s straightforward to parse them and extract the astrometry metadata as a list of tags. I’ve written a small form which does this, using YQL to grab the comments from a Flickr photo then parsing them using standard DOM traversal and manipulation methods.

If you have a photo which has been solved, generating tags is straightforward. Copy the address of a Flickr photo page into the tagging form and press the big blue ‘Get astrotags’ button. The script should find the comment from astrometry.net and print out the tags for celestial coordinates and names, which you can then paste into the ‘Add a tag’ form on Flickr.

The code to do this is fairly simple, and reproduced below. After initialising the page, we can take advantage of YQL’s HTML parser to fetch all of the comments for a Flickr photo page by selecting all paragraphs inside divs with a class of ‘comment-content’ at that URL.

select * from html where url='http://www.flickr.com/photos/eatyourgreens/4182924966/' and xpath='//div[@class="comment-content"]/p'

We then loop through the results of this query, looking for paragraphs which contain the text ‘blind astrometry solver’. If we have a match, we add this paragraph to the DOM so we can parse it with standard DOM methods. The code then loops through the child nodes of the comment paragraph, running regular expression matches against any text nodes it finds to extract the coordinates of the photo.

Names are slightly more tricky. For those, we grab every line of text between ‘Your field contains:’ and the line ‘—–‘ above the signature, strip out whitespace, split each line on ‘/’ to get individual names and store these in an associative array, keyed on name to remove duplicates. That done, we can then just loop through the arrays of coordinates and names and print them out.

Here’s the full code:

 var url = "http://www.flickr.com/photos/skiwalker79/4174398309";
 var comment_holder;
 var position_output;
 var names_output;
 
 function init() {
   var url_input = document.getElementById('photoURL');
   var url_button = document.getElementById('updateURL');
   
   comment_holder = document.getElementById('comment');
   position_output = document.getElementById('position');
   names_output = document.getElementById('names');
   
   url_input.disabled = false;
   url_input.value = url;
   url_button.disabled = false;
   position_output.disabled = false;
   names_output.disabled = false;
   
   addEvent(url_button, 'click', function(e) {
     getFlickrPhotoComments(url_input.value);
     return false;
   });
   addEvent(url_input, 'focus', function(e) {
     url_input.select();
   });
   addEvent(position_output, 'focus', function(e) {
     position_output.select();
   });
   addEvent(names_output, 'focus', function(e) {
     names_output.select();
   });
   
   // Mark up nodes which this script updates as
   // ARIA live regions.
   comment_holder.setAttribute('aria-live', 'polite');
   position_output.setAttribute('aria-live', 'polite');
   names_output.setAttribute('aria-live', 'polite');
   
   getFlickrPhotoComments(url);    
 }

 function getFlickrPhotoComments(url) {

   // YQL query to get all comments from a Flickr photo page.
   var yql = "select * from html where url='"+url+"' and xpath='//div[@class=\"comment-content\"]/p'";
   var yql_url = 'http://query.yahooapis.com/v1/public/yql?q='+escape(yql)+'&format=xml&callback=getAstrometryComment&diagnostics=false';
   position_output.value = '';
   names_output.value = '';
   comment_holder.innerHTML = 'Looking up '+url;
   makeYQLRequest(yql_url);
 }
 
 function makeYQLRequest(yql_url) {
   var script=document.getElementById('yqlscript');
   var newscript=document.createElement('script');
   newscript.type = 'text/javascript';
   newscript.src=yql_url;
   newscript.id='yqlscript';
   document.getElementsByTagName("body")[0].removeChild(script);
   document.getElementsByTagName("body")[0].appendChild(newscript);
 }

function getAstrometryComment(data) {
  var results = data.results;
  
  var comment = 'Sorry, that photo has not been solved by <a href="http://astrometry.net">astrometry.net</a>.';
  for (var i in results) {
    var text = results[i];
    // Comments left by the solver contain the text 'blind astrometry solver'.
    if(text.match(/blind astrometry solver/gi)) {
      comment = text;
    }
  }
  parseComment(comment);
  
}

function parseComment(comment) {
  var astro = {};
  var names = {};
  var parsing_names = false;
  
  comment_holder.innerHTML = comment;
  var children = comment_holder.firstChild.childNodes;
  
  for (var i in children) {
    var child = children[i];
    var text = '';
    text = child.data;
    
    if (text) {
      if (text.match(/(RA, Dec)/g) && text.match(/degrees/g)) {
        text=text.match(/[-0-9\.]+/g);
        astro.RA = text[0];
        astro.Dec = text[1];
      } else if (text.match(/Orientation/g)) {
        text = text.match(/[-0-9\.]+/g);
        astro.orientation = text[0];
      } else if (text.match(/Pixel scale/g)) {
        text = text.match(/[0-9\.]+/g);
        astro.pixelScale = text[0];
      } else if(text.match(/Field size/g)) {
        text = text.match(/[0-9\.]+ x [0-9\.]+ (degrees|arcminutes|arcseconds)/g);
        astro.fieldsize = text[0];
      } else if(text.match(/Your field contains:/g)) {
        parsing_names = true;
      } else if (text.match(/-----/g)) {
        parsing_names = false;
      }

      if (parsing_names) {
        names = addNames(names, text);
      }
      
    }
  }

  renderPositionTags(astro);
  
  renderNameTags(names);
  
  if (astro.RA) {
    position_output.focus();
  }
}

function addNames(names, text) {
  
 text = trim(text);
 text = text.split('/');
 for (var j in text) {
   var name = text[j];
   name = trim(name);
   if (name && name !='Your field contains:'){
     names[name] = name;
   }
 }
 return names;
}

function renderPositionTags(astro) {
  
  position_output.value = '';
   for (var tag in astro) {
     position_output.value += 'astro:'+tag+'="'+astro[tag]+'" ';
   } 
}

function renderNameTags(names) {

  names_output.value = '';
   for (var name in names) {
     names_output.value += 'astro:name="'+name+'" '
   }
}

function trim(text) {
  // Trim leading and trailing whitespace from a string.
  text = text.replace(/^\s+/, '');
  text = text.replace(/\s+$/,'');
  return text;
}

function addEvent(obj, evType, fn) {
  if (obj.addEventListener) {
    obj.addEventListener(evType, fn, false);
    return true;
  } else if (obj.attachEvent) {
    var r = obj.attachEvent("on" + evType, fn);
    return r;
  } else {
    return false;
  }
}

Searching astro:name with YQL

Searching astro:name with YQL, originally uploaded by Eat your greens!.

The YQL team announced personal URLs for queries this week. I’ve used the new feature to set up a shortcut for looking up photos of astronomical objects by name. The URL is:

http://queries.yahooapis.com/v1/public/yql/eatyourgreens/astrolookup?name=M+42

You can set the name parameter in the URL to change the name of the object you are looking for. I’ve also set up a demo page to render results from this query. The URL is:

http://eatyourgreens.org.uk/testapps/yql/astronamesearch.html?name=Horsehead+nebula

Again, change the name parameter in the URL to lookup different objects. Note that it looks for an exact match with the astro:name machine tag, so looking up stars is cumbersome:

http://eatyourgreens.org.uk/testapps/yql/astronamesearch.html?name=the+star+deneb+(αcyg)

Update: it seems Flickr’s machine tag search can match just the first part of a tag, so you can search for stars by supplying the first part of the tag’s value.

http://eatyourgreens.org.uk/testapps/yql/astronamesearch.html?name=the+star+deneb

I’ve also made some changes to flickr.photos.astro in order to enable faster searching by name. Use astro_name in a query to find objects by matching on astro:name:

select * from flickr.photos.astro where astro_name = 'M 31'

or use text to run a Flickr text search across photo descriptions and titles:

select * from flickr.photos.astro where text = 'orion'

If you want to see what values have been used for astro:name on Flickr, I recommend Paul Mison’s excellent machine tag browser.

London web standards: Introducing astrotags

Here are the slides from my talk at the December LWS meetup – Introducing astrotags: astrometry, machine tags and YQL (20MB pdf). The examples and demos should all be in the YQL category on this blog.

If you’re interested in the nuts and bolts of the automated astrometry robot, I recommend having a look at astrometry.net and reading Making the sky searchable: Fast geometric hashing for automated astrometry.

Is it about elephants?

I found my copy of Ghastly Beyond Belief, Neil Gaiman and Kim Newman’s wonderful collection of quotations from science fiction and fantasy. Page 164 has this excellent exchange between Michael Moorcock and Thomas Disch.

Tom Disch: I’m writing a book about what everyone wants most.

Moorcock: Really? Is it about elephants?

Disch: Elephants? No, it’s about becoming more intelligent.

Moorcock: Oh, what I’ve always wanted most is to be an elephant.

Azerbaijani bloggers imprisoned after unfair trial

From Amnesty International on 18 November 2009:

Azerbaijani youth activists and bloggers Emin Abdullayev (aged 30) and Adnan Hajizade (aged 26) have been sentenced to two and a half years and two years respectively in an unfair trial. Amnesty International believes the charges against them were fabricated and they have been imprisoned solely for exercising their right to freedom of expression.

On 28 June, a satirical video made by Adnan Hajizade criticizing the Azerbaijani government was posted on the video-sharing website YouTube. On 8 July, Adnan Hajizade and Emin Abdullayev (who blogs under the name Emin Milli) were approached by two men as they were dining with friends in a restaurant in the capital Baku. The men demanded to know what they were talking about. When Emin Abdullayev replied that it did not concern the two men, one of the men head-butted him and he fell to the floor. The same man then struck Adnan Hajizade, knocking him to the ground, and continued to kick and beat Adnan Hajizade and Emin Abdullayev until restaurant staff intervened.

Adnan Hajizade and Emin Abdullayev immediately went to a local police station to report the incident. However, police officers interrogated them as suspects for five hours, denying them access to a lawyer of their choosing and arrested them on charges of ‘hooliganism’. The activists were remanded in custody pending their trial. They were later also charged with inflicting minor bodily harm.

During their investigation the police and prosecutors failed to interview witnesses and to obtain video evidence from a security camera which may have recorded the incident in the restaurant. They also submitted documents to the court which wrongly stated that Adnan Hajizade was unemployed and that he had a criminal record. According to the activist’s lawyers, at the trial the court refused to consider photographs showing the injuries they sustained in the attack, as well as video evidence from mobile phones and the security camera, but provided no explanation as to why. The two men were convicted and sentenced to two and a half and two years respectively in prison. Emin Abdullayev and Adnan Hajizade intend to appeal against the decision.

There is a letter calling for the release of Emin and Adnan, which you can send to President Aliyev, the Minister of Justice, the Ombudsperson and the Azerbaijani Ambassador in London.