How To: Speed Up The MacRumorsLive Ajax Refresh

title_macrumors.gifBy default, MacRumorsLive polls the server for updates only every 60 seconds. I know, I know.. <enter sad puppy face here> If you're like me, you probably want that action with a touch more zip to it! And thus...

To poke the server every 10 seconds for a new update, drop this badboy into the location bar on your MacRumorsLive tab and hit enter:

javascript:(function(){var booyah=10,str='ro[0].firstChild.nodeValue',countdown='document.getElementById("ti").innerHTML  = (rr-x) + " seconds till next update..."; setTimeout';rr=booyah;eval('d = '+d.toString().replace(str,booyah));eval('ppd = '+ppd.toString().replace(str,booyah));eval("l = "+l.toString().replace('setTimeout',countdown));x=5;})();

btw- this doesn't work in IE. Deal with it.

Update (11:41am): It now has a countdown till next update. countdown.PNG

Bookmarklet: Inject new css rules

I love doing as much CSS debugging as I can without hitting reload. (see also: my 'refresh css' bookmarklet)

newcssrule.PNGFirebug can get you a far way in playing with styles. You can edit current css rules and add new styles to a given element, but there is no way to add a brand new rule to the page. UNTIL NOW!!!

Drag this bad boy to your bookmark bar:

>>> newcssrule <<<

The code is obviously trivial, but it's a helpful button to have around sometimes. Plus you can then edit those rules in firebug immediately.

Update (2008.06.05): I should point out this code only works in Firefox (maybe Opera), but for full compatibility, understand the landscape at InstallStyles on Google Doctype.
And this code should get you started:

    function addCSS(newcss){
        if ($.browser.msie) {
            document.createStyleSheet().cssText = newcss;
        } else {
            var tag = document.createElement('style'); tag.type = 'text/css'; document.getElementsByTagName('head')[0].appendChild(tag); 
            tag[ $.browser.safari ? 'innerText' : 'innerHTML'] = newcss;    
        }
    }

Best practice: Poll instead of a setTimeout hack

Very often you'll have events happening asynchronously, but you need to wait until one has completed before you fire the second.
And you may not have the ability of attaching a callback function to the first.

In my less wise days I'd say "Lets just setTimeout it for a couple seconds..." but always felt really dirty about it.

A classier approach I've used lately is to poll for a change. Here I'm using the gmail greasemonkey API and waiting for it to load in before I start using it:

gmonkey.load("1.0");
 
// this is a self-executing anonymous function that uses setTimeout to call itself 
// at 50ms intervals until the isLoaded variable resolves as true.
 
(function(){
  if (gmonkey.isLoaded){
    // do stuff i want to do with the API
    gmonkey.get('1.0').addNavModule('notepad', '<iframe src="http://aaronboodman.com/halfnote/"></iframe>');
  } else {
    setTimeout(arguments.callee,50);
  }
})();

I found myself doing this a lot when loading in multiple external resources and playing with them.. So to generalize the code I wrote executeWhenLoaded():

// executeWhenLoaded() will be overloaded with as many arguments as i want to check for presence.
function executeWhenLoaded(func){  
 
  for (var i = 1; i<arguments.length; i++){ // for loop starts at 1 to skip the function argument.
    if (! window[ arguments[i] ]) {
      setTimeout(arguments.callee,50);      
      return;
    } 
  }
 
  func(); // only reaches here when for loop is satisfied.
}
 
// and in use:
 
executeWhenLoaded(function(){ 
    console.log(session.data);
},'session');   // session will return a value when the whatever preceding functionality is done.

executeWhenLoaded's first argument is the function to call, it can an unlimited number of arguments, which are all strings that reflect objects in the global namespace that have to be present in order to execute that function.

Update: In the comments, ProggerPete notes that this is not cross-browser compatible.. yet! In IE6, at least, the browser loses the original reference to the arguments object when it cycles through on the arguments.callee call. He offers a fix below.

Quick internationalized sort in javascript

Let's take a list of countries that was originally alphabetized in English, but is now translated to French.

var arr = ["Argentine", "Australie", "Autriche", "Belgique", "Brésil", "Canada", "Chili", 
"Chine", "Costa Rica ", "République Tchèque", "Danemark", "Équateur", "El Salvador ", 
"Finlande", "France", "Allemagne", "Guatemala", "Hong Kong", "Hongrie", "Inde", "Irlande", 
"Italie", "Japon", "Corée du Sud", "Luxembourg", "Mexique", "Pays-Bas", "Nouvelle-Zélande", 
"Norvège", "Panama", "Pologne", "Portugal", "Russie", "Slovaquie", "Espagne", 
"la Suède", "Suisse", "Turquie", "Royaume-Uni", "Uruguay", "États-Unis"]

You can see the incorrect sort order for Germany ("Allemagne") and the US ("États-Unis").
Running the standard javascript Array.sort() will sort it according to the American English language:

arr.sort();
/*==>
["Allemagne", "Argentine", "Australie", "Autriche", "Belgique", "Brésil", "Canada", "Chili", 
"Chine", "Corée du Sud", "Costa Rica ", "Danemark", "El Salvador ", "Espagne", "Finlande", 
"France", "Guatemala", "Hong Kong", "Hongrie", "Inde", "Irlande", "Italie", "Japon", 
"Luxembourg", "Mexique", "Norvège", "Nouvelle-Zélande", "Panama", "Pays-Bas", "Pologne", 
"Portugal", "Royaume-Uni", "Russie", "République Tchèque", "Slovaquie", "Suisse", "Turquie",
 "Uruguay", "la Suède", "Équateur", "États-Unis"] */

Note the misplacement of the last three entries. A real internationalized sort of this would be a huge motherbitch to implement, but here is a quick and hacky way to get your ducks in order:

  arr.sort(function(a,b){
 
    function normalize(str){
       return str
               .toLowerCase()
               .replace(/è|é|ê|ë/,'e').replace(/ò|ó|ô|õ|ö/,'o').replace(/ì|í|î|ï/,'i')
               .replace(/à|á|â|ã|ä|å|æ/,'a').replace(/ù|ú|û|ü/,'u');
    }
 
    a = normalize(a);
    b = normalize(b);
 
    return ((a < b) ? -1 : ((a > b) ? 1 : 0));
  });
/*==>
["Allemagne", "Argentine", "Australie", "Autriche", "Belgique", "Brésil", "Canada", "Chili", 
"Chine", "Corée du Sud", "Costa Rica ", "Danemark", "El Salvador ", "Équateur", "Espagne",
 "États-Unis", "Finlande", "France", "Guatemala", "Hong Kong", "Hongrie", "Inde", "Irlande", 
"Italie", "Japon", "la Suède", "Luxembourg", "Mexique", "Norvège", "Nouvelle-Zélande", 
"Panama", "Pays-Bas", "Pologne", "Portugal", "République Tchèque", "Royaume-Uni", 
"Russie", "Slovaquie", "Suisse", "Turquie", "Uruguay"] */

It's not perfect (I bet that "la Suède" should actually be in the S's), but it'll get you a bit closer without too much effort.

what makes you sustainably happy?

i wonder about the personal fulfillment of:

  • doing what we think we should be doing
  • doing what we want to be doing
  • doing what we think makes us happy

because those three are certainly different..

How to keep friend requests out of your inbox

If you're incredibly popular on the internet, like me, then I can hear you crying for help. :)

Friend requests littering your inbox can get annoying. They certainly don't need your attention right now, so why not through them to a more passive information consumption area: RSS. That way you can process them en masse, when you're ready to.

First, we use the filters in Gmail to identify all friend request emails.

  • Set up a new filter.
  • In the subject area put in this text:
    {"friend request" "is now following you" "newest contact" "friends on yelp" "added you as a" "has requested your trust" "wants to be your friend" "invited you to connect" "would like to be added"}
  • Click next, then select Skip the Inbox
  • Check Forward it to and in the box put in a unique-email-address @ mailbucket.org. (For example: paulsfriendrequests@mailbucket.org) This will be a public feed, so... yeah.
  • Click Create Filter

friendrequestfilter.PNG

Your newly created RSS feed will be at http://mailbucket.org/unique-email-address.xml.

This filter will catch all friend requests from: Myspace, Facebook, LinkedIn, Flickr, Spokeo, Twitter, Yelp, and Plaxo Pulse. (And should be pretty trivial to add new ones. :)

How to quickly find a layer in a big PSD

psdlayers.PNGI'll often get PSDs from my visual designers with hundreds of layers. When I'm slicing it up, I'll need to find a specific layer. And much of the time, I don't instantly understand the layer architecture that was defined.

Using the "Auto Select layer" option on the move tool gets partway there, but it chokes on layer groups.

The quickest route possible: Alt-right-click
That will immediately select the layer of whatever you clicked on. (The move tool (V) must be active. This doesnt work while on other tools.)

(On Mac, this has an additional click: option-click. then the revealed menu has your layer currently selected. click it.)

jQuery doOnce plugin for chaining addicts

// jQuery doOnce plugin
jQuery.fn.doOnce = function(func){ 
    func.apply(this); 
    return this; 
}

This allows you to execute arbitrary script during a single chain. A bad example of alert() debugging follows:

// example usage of doOnce()
$('div.toGrow')
  .doOnce(function(){
    alert('before color swap');
  })
  .css({ backgroundColor: 'red'})
  .find('h2')
    .text('And now we\'re red')
    .end() // go back to the div.toGrow
  .animate({ height: '+=50px'},'normal','swing',function(){
    $(this)  // callback when animations complete
      .find('h2')
        .text('And now we\'re taller')
        .doOnce(function(){
          alert('animation done');
        });
  });

Note this is a little different than each(), which will execute once for every element in your current collection.
The code is pretty trivial, but sometimes you just feel like it. Nobody likes to break the chain!

How to tweak 3rd party scripts “safely”

I'm working with some code from MultiMap (basically a Google Maps clone), and unfortunately their API doesn't support real internationalization. There are a number of strings hardcoded in English in their remotely-hosted JS, so this is my deliciously evil way of rectifying the situation.

I first started out redefining their functions in entirety, but that becomes "dangerous" when they modify a bit of code for a bug fix or something. This revised approach slips in like an evil drunken ninja and only replaces the offending parts. It's still completely crazy, but sometimes there are no better options...

// WARNING!!  This is such a massive hack. Oh-so-hackalicious
 
// Problem:     Multimap doesnt allow internationalization of its buttons, etc.
// Solution:    Redefine their JS functions to use variables that are internationalized.
// Assumption:  That these internal function names stay the same. 
// Risk:        If function names change, this code will (probably) silently fail.
 
// The following statements change the right-click context menu items and the map/aerial/hybrid buttons.
// Instead of hard-coded strings, it will use a variable which we control.
 
// ON TO THE HACKS!!
// Hack 1: modify the mmjr() and mmfl() functions with funcName.toString().replace()
// Hack 2: use eval() to set the definition
// Hack 3: browser sniff because IE and FF handle toString()'d strings differently (single-quote vs double-quote)
 
var isIE = $.browser.msie; // jQuery browser sniff.
 
eval(
  "mmki.prototype.mmjr = " +  
  mmki.prototype.mmjr
  .toString()
    .replace( isIE ? "'Move map to here'" : '"Move map to here"' ,      'i18n.retailLocator.moveMapToHere')
    .replace( isIE ? "'Zoom in to here'" : '"Zoom in to here"' ,        'i18n.retailLocator.zoomInToHere')
    .replace( isIE ? "'Zoom out from here'" : '"Zoom out from here"',   'i18n.retailLocator.zoomOutFromHere')
  );
 
eval(
  "MultimapViewer.prototype.mmfl = " +  
  MultimapViewer.prototype.mmfl
  .toString()
    .replace( isIE ? "'Map'" : '"Map"',       'i18n.retailLocator.map')
    .replace( isIE ? "'Hybrid'" : '"Hybrid"', "i18n.retailLocator.hybrid")
    .replace( isIE ? "'Aerial'" : '"Aerial"', 'i18n.retailLocator.aerial')
);

The top 55 best front-end development RSS feeds

I've put together all the feeds and blogs that I follow that cover front-end development.
Here is the OPML file: front-end-development-feeds.xml.opml

feeds.pngAll the classics like Ajaxian and A List Apart are in here.. but also more technical ninja developers like John Resig, Hedgerwow and Peter Michaux.

If you currently use a RSS aggregator (like Bloglines, Google Reader, or Netvibes) you can import this file right in.
iGoogle won't take an OPML file but you can do each RSS feed individiually.

You can also preview what's in it here: http://www.bloglines.com/public/molecular-frontend-feeds