Tuesday, June 26, 2012

The Aphephobic Caterpillars

Image "Borrowed" from Shu (http://shu-art.blogspot.ca/)
Once upon a time, two aphephobic caterpillars met, walking along the same side of the same twig in opposite directions. Both caterpillars, not wanting to be touched, tried to crawl over the other and be the one on top. Instead they ended up crawling up each other, perpendicular to the twig on which they were both walking. Each realized their mistake immediately but panicked, not wanting to be the one who hesitated and have the other caterpillar crawl up and over themselves. Instead each crawled even faster trying to be the one who crawled on top of the other.

Of course, before long, they were no longer crawling along the twig at all and were only holding on to each other, and fell from the twig plummeting towards the ground and certain death.

Now you may not realize this but time goes by more slowly for caterpillars than it does for humans and the two caterpillars, clinging on to each other out of fright, had time to have a conversation with each other before they collided with the ground below them. The conversation went something like this:

The first caterpillar said: “It seems our own unreasonable fears will be the end of us both. How ironic that our last act is to grasp each other tightly when our fear of being touched is the cause of our predicament”.

To which the second caterpillar responded: “I’m sure this doesn’t have to be our last act. Deep inside us we both have the knowledge of flight. We have thus far lived a life of legs; but we are also creatures of wings. We have crawled but it is our destiny to fly. Maybe, just maybe, we can tap that part of our inner selves and soar!”.

Inspired both caterpillars released their hold of each other; and died alone.

THE END

Wednesday, November 16, 2011

Want to know when Google is adding your area to Street Level View?

Google doesn't tell many people when they are driving around in their cars, photographing an area for Street Level View (I understand Google notifies Women's Shelters and other organizations that are sensitive about exterior photographs of their buildings).  There are several obvious reasons for not being too public about their mapping intentions, of course.  Most importantly, I imagine, is that Google wants to keep their data from being "tampered" with by people who would make some special display specifically for the mapping vehicle (e.g. a "Hi Mom!" banner).

I, however, really want to know anyway.  I live on a small island that relies heavily on tourism.  Our island has many artists and artisans who work out of studios on their own properties.  I would like to have these artists have displays of their work on the ends of their driveways when Google conducts a street level view on our island (our island hasn't been covered yet, but I think it may be covered in the future).  I think it would be good for the locals, and for the tourists who look at our island online.

As I said, google doesn't broadcast where they intend to cover next; but they DO broadcast where they are currently.  Google has a special site that you can visit to see the current location of their vehicles, categorized by country:

http://maps.google.com/help/maps/streetview/learn/where-is-street-view.html

As I said, the chart on this page shows the current location and only seems to list the city or general area they are currently mapping.  However, I think it's a start and below I'll show you how you can use this data to get an alert when Google starts mapping a new area in your country.

The method I'll describe here is a very simple one.  The country data on the page above is populated through an XML feed.  I decided to import the data into a Google Docs spreadsheet, and make the spreadsheet notify me via email if the cell that contains the data for my country changes.  The steps I took are as follows:

  1. Create a spreadsheet in Google Docs
  2. In the first cell on the default worksheet (A1) insert the following function:
    =ImportFeed("http://spreadsheets.google.com/feeds/list/0AjZ9lY-SjtYacnNVdGhsckJrM3k5X1hFd3BIWlhWcFE/oda/public/basic")
  3. Select Tools > Notification Rules and add a new rule
  4. Click the checkbox next to "Any of these cells are changed: ".  In the cell range box, put the name of the cell that contains the data for your country (for my country, Canada, the cell was C7)
  5. Click the checkbox next to "Email - Right Away" under "Notify Me With..."
  6. Save your spreadsheet and exit!  
The spreadsheet doesn't poll the source every second, but you should get a notification shortly after the data changes (certainly within the hour).  Again, this isn't exactly "notice", but hopefully it will give you some warning.

If there is a lot of activity in your country (and you're getting too many notifications) you can use the FIND function to locate your area/city name within your country data and then base your notification on the result.

Tuesday, July 19, 2011

Accessballot

I've completed the initial version of a English US EAC compliant paper ballot generating API. It takes XML candidate/issue data and uses it to generate a PDF of a ballot that is 100% compliant with the US EAC ballot design standards.


The application is Opensource and you can find it on Google Code at the following address:
http://code.google.com/p/accessballot/

You can also use the hosted version on the Accessvote.com (which is free of charge). The following is a sample ballot generated using the free API:
http://accessvote.com/api/us_eac/?s=http://accessvote.com/sample_ballots/us_eac-sample.xml.

If you would like more information on how to use it/integrate it into your own applications, you can view the project wiki at the following location:
http://code.google.com/p/accessballot/w/list.

Wednesday, January 26, 2011

Making DIVs, using the CSS "Float Left" property, all have uniform heights; automatically adjusting to match the tallest DIV in the row

Update: I modified the code so it would work with the onresize() event


Would you believe that was the best title I could come up with? Honestly, if you can think of a better one shoot me a line - I want to hear from you.

Okay, I'm a big fan of Fluid Design. I love to design websites/webapps that flow into the space available, from giant screened desktops to mobile phones, and always look like they were designed with exactly that screen size in mind.

One thing that CSS-P brought to web design that didn't exist back when we were using invisible nested tables to do webpage layout is the ability to have rows with a dynamic number of "columns". So, for instance, if you are showing rows of thumbnails, you can have more thumbnails on a row if the browser window is wide, and fewer if the window is narrower.

The method for doing this is to cause items to "Float Left". Usually this means putting your content (say a thumbnail and label) into a DIV; and giving the DIV the CSS property of:

div.column
{
   float:left;
}

If the above were added to a page's stylesheet, the DIVs with the class "column" assigned to them would stack to the right of the item before it on the page, and if there are more than one item, each will stack to the right of the previous DIV until there is no more room on that line, and then the next will appear below that row, starting a new one.

If the DIVs do not share the same height, each row will have their tops all lined up, but will have bottoms extend downwards as much as they need to to accommodate the content they contain. And the next row, which also has all the tops lined up, will appear just below the bottom of the DIV, from the previous row, that was "tallest". Like this:

(So above the second row begins below the "tallest" item in the first row, which is c)


This isn't bad, but many designers would rather have some uniformity to the DIV sizes and have, say, the container backgrounds and borders fill in the spaces below a, b, and d.  We can do that, of course, by giving each container a specified height, instead of letting the content drive the height, but then we would have to make all the containers as tall as they are likely ever to get which isn't prefect either. 

Ideally, I think, it would be best to have all of the DIVs in a row be all be the same height, but only be as tall as is actually needed to accommodate the tallest DIV in the row.  Like this:

(in the above example, the DIVs in the first row are as tall as the tallest DIV in the row, which is c, and all the DIVs in the second row are as tall as f, which is the tallest DIV in that row)

Well, with a bit of jQuery you can have your DIVs all adjust their heights so they are uniform across the entire row. You will have to add jQuery to your page (see How jQuery Works) and add the following Javascript to your page:
var currentTallest = 0;
var currentRowStart = 0;
var rowDivs = new Array();

function setConformingHeight(el, newHeight) {
 // set the height to something new, but remember the original height in case things change
 el.data("originalHeight", (el.data("originalHeight") == undefined) ? (el.height()) : (el.data("originalHeight")));
 el.height(newHeight);
}

function getOriginalHeight(el) {
 // if the height has changed, send the originalHeight
 return (el.data("originalHeight") == undefined) ? (el.height()) : (el.data("originalHeight"));
}

function columnConform() {

 // find the tallest DIV in the row, and set the heights of all of the DIVs to match it.
 $('div.column').each(function(index) {

  if(currentRowStart != $(this).position().top) {

   // we just came to a new row.  Set all the heights on the completed row
   for(currentDiv = 0 ; currentDiv < rowDivs.length ; currentDiv++) setConformingHeight(rowDivs[currentDiv], currentTallest);

   // set the variables for the new row
   rowDivs.length = 0; // empty the array
   currentRowStart = $(this).position().top;
   currentTallest = getOriginalHeight($(this));
   rowDivs.push($(this));

  } else {

   // another div on the current row.  Add it to the list and check if it's taller
   rowDivs.push($(this));
   currentTallest = (currentTallest < getOriginalHeight($(this))) ? (getOriginalHeight($(this))) : (currentTallest);

  }
  // do the last row
  for(currentDiv = 0 ; currentDiv < rowDivs.length ; currentDiv++) setConformingHeight(rowDivs[currentDiv], currentTallest);

 });

}


$(window).resize(function() {
 columnConform();
});

$(document).ready(function() {
 columnConform();
});
(the above code assumes you have assigned the CSS class called "column" to each of the DIVs that are "Floating Left")

If you can read Javascript/jQuery, you will find the code comments will sufficiently explain how it works; but in a nutshell: The script goes through each DIV, determining which are on the same row by comparing the X value of the top of each container.  It keeps track of which is the tallest, and then sets the height for each DIV in the row based on that value.

Thursday, December 9, 2010

Link Tracking using jQuery and the Google Analytics Asynchronous Tracking Code

The Google Analytics tracking code is triggered when pages that include the code are called. But what about when users request PDFs and other documents that aren't web pages or click on links to external web pages on other websites?  How do we track these events also?

Here is some jQuery you can add to your pages that will allow you to track when people click on these links.  You will have to add the Google Analytics Asynchronous Tracking code (found here: http://code.google.com/apis/analytics/docs/tracking/asyncTracking.html) and add jQuery to your page (see here: http://docs.jquery.com/How_jQuery_Works) to use this code:

$(document).ready(function(){

 $('a').click(function(){

  href = ($(this).attr('href') == undefined) ? ('') : ($(this).attr('href'));
  href_lower = href.toLowerCase();
  
  if(href_lower.substr(-3) == "pdf" || href_lower.substr(-3) == "xls" || href_lower.substr(-3) == "doc") {
   _gaq.push(['_trackEvent', 'document', 'download', href_lower.substr(-3), $(this).text()]);
   _gaq.push(['_trackPageview', href]);
  }
 
  if(href_lower.substr(0, 4).toLowerCase() == "http") {
   _gaq.push(['_trackEvent', 'external_link', 'open', 'href', $(this).text()]);
   _gaq.push(['_trackPageview', href]);
  }
  
  if ($(this).attr('target') != undefined && $(this).attr('target').toLowerCase() != '_blank' && href_lower.substr(0,10) != "javascript") {
   setTimeout(function() { location.href = href; }, 200);
   return false;
  }

 });

});

So, the example above will track clicks on internal links opening files with the extensions pdf, xls, and doc and will categorize them in your Google Analytics event tracking reports as "documents".  It will also track outgoing links (links with URLs beginning with "http") and categorize them as "external_links".

If you need more categories, add more if statements.  In the method above I create one click event that is attached to every link on the page, rather than just attaching specific events to the links that require them.  If a link satisfies more than one criteria (e.g. it's a link both to a PDF, and it's on an external website) then both events will be created.  In the end I thought this approach was probably more customizable and efficient overall, especially if there are several event categories.

The setTimeout bit is to allow links that replace the page content (i.e. regular links) a moment before calling the page; so that the GA function has time to execute before opening the page. Be careful when using plugins like Fancybox; add links to such plugins to the exception list in the condition wrapped around the setTimeout function as these functions remove the href attribute and the combination of that and the setTimeout can cause unpredictable behaviour.

You may want to track events for clicks made by specific links like the nav or featured content.  You can achieve this using a similar method:


$("#main_menu a").mouseup(function(){
if($(this).attr('href').toLowerCase() != "javascript:;" && $(this).attr('href') != "#") {
_gaq.push(['_trackEvent', 'main_menu', 'open', $(this).attr('href')]);
}
});

Change the ID in the yellow to whatever you call the container that your menu is kept in.  I added the check for JavaScript:; and # just in case there are links that don't open pages (e.g. links that reveal further options).

Leave comments if you have other suggestions for mixing jQuery and GA for better tracking.

Wednesday, December 8, 2010

Google Nexus S Fun

I happened to catch the second clue in the Google Nexus (@googlenexus) Nexus S contest about 20 minutes or so after they posted it. I didn't get the first puzzle; I thought I did but I was wrong. This one I was on the right track right away. The clue was:

Puzzle Challenge #2: There is more than meets the eye at http://goo.gl/MKoc8. You'll find the answer in a tweet.

So the FIRST thing I did when I went to the page was look at the source code. At the very bottom of the page I found this:

.
                               I                I                               
                                I      ++      I                                
                                 IIIIIIIIIIIIII                                 
                               IIIIIIIIIIIIIIIIII                               
                             IIIIIIIIIIIIIIIIIIIIII                             
                            IIII   IIIIIIIIII  IIIII                            
                           IIIIIIIIIIIIIIIIIIIIIIIIII                           
                          IIIIIIIIIIIIIIIIIIIIIIIIIII                           
                          IIIIIIIIIIIIIIIIIIIIIIIIIIII                          
                          IIIIIIIIIIIIIIIIIIIIIIIIIIII                          
                    IIII  IIIIIIIIIIIIIIIIIIIIIIIIIIII  IIII                    
                   IIIIII IIIIIIIIIIIIIIIIIIIIIIIIIIII IIIIII                   
                   IIIIII IIIIIIIIIIIIIIIIIIIIIIIIIIII IIIIII                   
                   IIIIII 0110100001110100011101000111 IIIIII                   
                   IIIIII 0000001110100010111100101111 IIIIII                   
                   IIIIII 0110011101101111011011110010 IIIIII                   
                   IIIIII 1110011001110110110000101111 IIIIII                   
                   IIIIII 0110101001010000010011110100 IIIIII                   
                   IIIIII 100101101100IIIIIIIIIIIIIIII IIIIII                   
                   IIIIII IIIIIIIIIIIIIIIIIIIIIIIIIIII IIIIII                   
                   IIIIII IIIIIIIIIIIIIIIIIIIIIIIIIIII IIIIII                   
                   +IIII  IIIIIIIIIIIIIIIIIIIIIIIIIIII  IIII:                   
                          IIIIIIIIIIIIIIIIIIIIIIIIIIII                          
                          IIIIIIIIIIIIIIIIIIIIIIIIIIII                          
                           IIIIIIIIIIIIIIIIIIIIIIIIII                           
                                IIIIII    IIIIII                                
                                IIIIII    IIIIII                                
                                IIIIII    IIIIII                                
                                IIIIII    IIIIII                                
                                IIIIII    IIIIII                                
                                IIIII,    IIIIII                                
                                  I:        ~I                     



I noticed the binary around the android's belly so I translated it into ascii text and got the following URL:

http://goo.gl/jPOIl

I went to the URL and was faced with a puzzle with a timer. The puzzle involved rearranging letters, by swapping two neighbours, in a string so they were in the "correct" order. You could see how many were correct at the bottom of the page. It was pretty simple and I got the solution before the timer ran out (it took me a few seconds to recognize I was being timed).



Clicking the Tweet your victory! button opened a web tweet box with the message:

@googlenexus Run, run, as fast as you can! You can't catch me, I'm the Gingerbread Android!


I hope it's not against the rules to post the solution; I looked quickly through the legalese and didn't see anything forbidding it.

Tuesday, July 27, 2010

Getting my Images on Canvas

I've been rendering some of my models to print them on canvas recently.



I had to wait to buy a new computer to render my stuff. Can you believe it though? I'm already upgrading. You'd think 8 gb would be lots of RAM, wouldn't you? My new RAM is in the mail.

I transferred one of the images to canvas already. It's only a little over 2' by a little over 4' but it cost me nearly $300 to print after taxes! My neighbour is going to stretch it onto a frame for me. I'm pretty excited.