Moshe's Blog

Tips and tricks learnt along the way.

Object.observe Nested Objects

| Comments

A great article came out yesterday: Data-binding Revolutions with Object.observe()

If you didn’t read it yet, go read it then come back here.

Ok the basic idea of that article is that you can do things like this

var model = {};

// Which we then observe
Object.observe(model, function(changes){

    // This asynchronous callback runs and aggregates changes
    changes.forEach(function(change) {

        // Letting us know what changed
        console.log(change.type, change.name, change.oldValue);
    });

});
model.adding = 'a prop';
model.nested = { obj: 'thing' };
model.nested.obj = 'another thing';

running that in the console we get:

add adding undefined
add nested undefined

Now it would be cool if there was a way to observe nested objects. Here’s a simple function to do so:

function observeNested(obj, callback) {
    Object.observe(obj, function(changes){
        changes.forEach(function(change) {
            if (typeof obj[change.name] == 'object') {
                observeNested(obj[change.name], callback);
            }
        });
        callback.apply(this, arguments);
    });
}

Now at first glance it would seem this doesn’t work:

function observeNested(obj, callback) {
    Object.observe(obj, function(changes){
        changes.forEach(function(change) {
            if (typeof obj[change.name] == 'object') {
                observeNested(obj[change.name], callback);
            }
        });
        callback.apply(this, arguments);
    });
}

var model = {};

// Which we then observe
observeNested(model, function(changes){
    changes.forEach(function(change) {
        console.log(change.type, change.name, change.oldValue);
    });
});
model.adding = 'a prop';
model.nested = { obj: 'thing' };
model.nested.obj = 'another thing';

Still shows:

add adding undefined
add nested undefined

But then I remembered that (and I’m quiting from the article): Object.observe(), part of a future ECMAScript standard, is a method for asynchronously observing changes to JavaScript objects

So in the case above since the two assignments to model.nested happen right after another, it only logs it as one event, If you were using promises to some other async mechanism it would work as advertised:

function observeNested(obj, callback) {
    Object.observe(obj, function(changes){
        changes.forEach(function(change) {
            if (typeof obj[change.name] == 'object') {
                observeNested(obj[change.name], callback);
            }
        });
        callback.apply(this, arguments);
    });
}

var model = {};

// Which we then observe
observeNested(model, function(changes){
    changes.forEach(function(change) {
        console.log(change.type, change.name, change.oldValue);
    });
});
setTimeout(function() {
    model.adding = 'a prop';
}, 200);
setTimeout(function() {
    model.nested = { obj: 'thing' };
}, 400);
setTimeout(function() {
    model.nested.obj = 'another thing';
}, 600);

Which shows:

add adding undefined
add nested undefined
update obj thing

Unoptimize V8 for Easy Debugging

| Comments

Every now and then I need to debug something in DevTools and I get this puzzling error:

ReferenceError

That’s V8 optimizing the function and seeing that thing isn’t used in the function and therefore not putting it into the closure scope. There are a couple of ways to break this optimization, one is to use thing somewhere in the debugging function, however the problem with this is that you must enumerate all the vars you want to inspect. An easier technique I’ve found is to make it impossible for V8 to know what vars will be used in the function. What that entails is to just have a blank eval in the function somewhere (I usually put in right before the debugger statement):

ReferenceError

Future Proof Your Code With Promises and Promise.all

| Comments

When starting out with async code there are a few option to manage the callback hell. The one gaining the most traction is promises.

I starting writing a promise-like library when I first heard of promises: WTTT (When This Then That)

I was playing around with socket.io and had to do some async code as follows:

socket.on('join', function(id, name, callback) {
  socket.join(id);
  socket.name = name;
  var people = _.map(io.sockets.clients(room), function(socket) {
    return {
      id: socket.id,
      name: socket.name,
    };
  });
  callback(people);
});

Looking at the docs show that I should be using an async version as follows:

socket.on('join', function(id, name, callback) {
  socket.join(id) // this is still a sync function
  socket.set('name', name, function() {
    var people = ???;
    callback(people);
  });
});

As you can see there’s some magic that we need to do. Enter promises.

The basic usage is as follows

var promise = new Promise(function(resolve, reject) {
  setTimeout(function() {
    resolve("It's now two seconds later");
  }, 2000);
});
promise.then(function(value) {
  // value === "It's now two seconds later";
});

The library that I’m using is bluebird, they provide a Promise.promisifyAll method which takes an object and converts all it’s methods that have node style callbacks (function callback(err, result) {...}) as a last argument to promises

Promises by itself is not really that usefull, but the real power comes when you need to do many things at once, that’s what Promise.all is for

var resolveTo = function(thing) {
  return new Promise(function(resolve) {
    resolve(thing);
  });
};
var promises = [resolveTo('Apple'), resolveTo('Orange')]
Promise.all(promises).then(function(results) {
  // results === ['Apple', 'Orange'];
});

Promise.all also can take a non-thenable value and use it as is for example

var promises = [resolveTo('Apple'), resolveTo('Orange'), 42]
Promise.all(promises).then(function(results) {
  // results === ['Apple', 'Orange', 42];
});

Armed with this we can now write the above socket code as follows

socket.on('join', function(id, name, callback) {
  socket.join(id) // this is still a sync function
  socket.setAsync('name').then(function() {
    getPeopleAsync(id).then(function(people) {
      callback(people);
    });
  });
});
function getPeopleAsync(roomId) {
  return new Promise(function(resolve, reject) {
    var people = io.sockets.clients(room);
    var promises = [];
    people.forEach(function(socket) {
      promises.push(
        new Promise(function(resolveInner) {
          Promise.all([socket.id, socket.getAsync('name')]).then(function(results) {
            resolveInner({id: results[0], name: results[1]});
          });
        });
      );
    });
    Promise.all(promises).then(function(people) {
      resolve(people);
    });
  });
}

Which getPeopleAsync can be refactored to:

function getPeopleAsync(roomId) {
  return new Promise(function(resolve, reject) {
    var people = io.sockets.clients(room);
    var promises = [];
    people.forEach(function(socket) {
      promises.push(
        new Promise(function(resolveInner) {
          Promise.all([socket.id, socket.getAsync('name')]).then(function(results) {
            resolveInner({id: results[0], name: results[1]});
          })
        })
      );
    });
    Promise.all(promises).then(resolve);
  });
}

Which getPeopleAsync can be refactored to:

function getPeopleAsync(roomId) {
  var people = io.sockets.clients(room);
  var promises = [];
  people.forEach(function(socket) {
    promises.push(
      new Promise(function(resolve) {
        Promise.all([socket.id, socket.getAsync('name')]).then(function(results) {
          resolve({id: results[0], name: results[1]});
        })
      })
    );
  });
  return Promise.all(promises)
}

Now this lets us use Promise, notably Promise.all without having to worry about code ever changing from sync to async, consider:

Promise.all(socket.getAsync('name'), socket.getAsync('joined'), serverId, personRecord).then(function(results) {
  // do things with results
});

and

Promise.all(socket.getAsync('name'), socket.getAsync('joined'), serverId, db.getPersonAsync(personId)).then(function(results) {
  // do things with results
  // absolutely no changes needed here
});

Happy coding

My Favorite jQuery Plugin Template

| Comments

I’ve dabbled quite a bit in jQuery and writing plugins for it. I’ve played around with quite a few different ways to start a plugin, and now I’ve got a new favorite:

;(function($) {
  // multiple plugins can go here
  (function(pluginName) {
    var defaults = {
      color: 'black',
      testFor: function(div) {
        return true;
      }
    };
    $.fn[pluginName] = function(options) {
      options = $.extend(true, {}, defaults, options);
            
      return this.each(function() {
        var elem = this,
          $elem = $(elem);

        // heres the guts of the plugin
          if (options.testFor(elem)) {
            $elem.css({
              borderWidth: 1,
              borderStyle: 'solid',
              borderColor: options.color
            });
          }
      });
    };
    $.fn[pluginName].defaults = defaults;
  })('borderize');
})(jQuery);

Now let’s see how we would use it.

$('div').borderize();
$('div').borderize({color: 'red'});

Here’s some of the reasons that I like this technique

  1. You can still use a default option inside of a override (similar to calling a parent property in class based programming)
  2. Easily change the name of the plugin as long as we use pluginName all over (also there’s an insignificant minification advantage of that).
  3. Cleaner (at least in my opinion)

Point #1 is huge, let’s see an example that. Let’s say we want to override the testFor function but still want the option of defaulting to the original behaviour

$('.borderize').borderize({
    testFor: function(elem) {
        var $elem = $(elem);
        if (elem.is('.inactive')) {
            return false;
        } else {
            // calling "parent" function
            return $.fn.borderize.defaults.testFor.apply(this, arguments);
        }
    }
});

We can even do this with regular properties like this

var someVarThatMayBeSet = false;
/* code ... */

$('.borderize').borderize({
    color: someVarThatMayBeSet ? 'red' : $.fn.borderize.defaults.color
});

Do you have a different style that you like? Leave a comment below

Edit I’ve changed the $.each call to $.extend(true, {}, defaults, options); based on phlyingpenguin comment.

Beating Amazon

| Comments

Everytime I see a site that requires user interaction, it gives me a chance to practice some hackery. Case in point: Amazon black friday deals.

Amazon wants you to wait on the page counting down like you have nothing better to do. As a programmer I’d rather make a script to do that for me.

It brought me back a bit as their version of jQuery is 1.6.4. Anyway here’s the script, you can open the javascript console with Ctrl + Shift + J then paste the code below in or you can use the bookmarklet:

bookmarklet: Amazon Clicker

(function($) {
    function re__waitForIt__serve($item) {
        var interval = setInterval(function() {
            var $buyButton = $item.find('.btn');
            if ($buyButton.length) {
                clearInterval(interval);
                $buyButton[0].click();
                setTimeout(function() {
                    alert('Item (hopefully) available to buy!');
                    $item.css('outline', '1px solid green');
                }, 5000)
            }
        }, 100);
    }
    jQuery('body').delegate('li[id]', 'mouseenter.grabber', function() {
        var $this = $(this);
        $this.css('outline', '1px solid red');
        $this.delegate('*', 'click.grabber', function() {
            re__waitForIt__serve($this);
            $this.css('outline', '1px solid blue');
            $('*').unbind('.grabber')
            alert('All set to buy item');
            return false;
        });
        $this.bind('mouseleave.grabber', function() { $this.css('outline', 'none'); });
    });
})(jQuery);

(Tested in Chrome)

Think there’s a better way to do this? Did you ever have to do something like this?
Leave a comment below

Useful Snippets

| Comments

I recently found this useful page for devtools snippets

I never really used snippets before (I do have a ton of bookmarklets though), and I wanted to incorporate all the snippets on that page. There really isn’t any built in way to do that, but since practically everything in Chrome is exposed we can hack a way into it.

First off let’s see what we have already. Open dev-tools and take a look at the snippets.

check for snippets

If it doesn’t open in a new window then click the circled icon to open dev-tools in a new window.

Now you should have something that looks like this:

dev-tools in a new window

Now comes the cool part, we’re going to open devtools on the devtools window. With the active window the dev-tools window click Ctrl + Shift + J and you should see this:

inception dev-tools

Now go to the Resources tab and Local Storage, there may be more then one but it should be easy to figure out which one you need, then go to the scriptSnippets object:

scriptSnippets

Hopefully you can see where this is going. Let’s open up a dev-tools window on the second one by clicking Ctrl + Shift + J on the second window, I resized a bit to make it more manageable:

Now let’s head over to http://bgrins.github.io/devtools-snippets/ and scrape the snippets. Open up the console on that page and paste:

> var snippetsWrappers = document.getElementsByClassName('snippet-wrapper');
  var snippets = [];
  for (var i = 0; i < snippetsWrappers.length; i++) {
      snippets.push({
          name: snippetsWrappers[i].id,
          content: '//' + snippetsWrappers[i].childNodes[0].innerText.replace('\n', '\n//') +
              '\n' + snippetsWrappers[i].childNodes[1].innerText
      });
  }
  copy(JSON.stringify(snippets));

We now have a stringified version of the snippets in the system clipboard using the magic of copy on the console

Now that we have that in the clipboard we can now close that window. Now we need to merge in what we have in the clipboard with the current snippets, one way to do that is with this (in the right hand side window):

> var snippetsToAdd = JSON.parse(prompt('Hit ctrl + v and enter'));
  var currentSnippets = JSON.parse(localStorage.scriptSnippets);
  var nextId = Math.max.apply(Math, currentSnippets.map(function(obj) { return obj.id })) + 1;
  for (var i = 0; i < snippetsToAdd.length; i++) {
      currentSnippets.push({
          name: snippetsToAdd[i].name,
          content: snippetsToAdd[i].content,
          id: nextId++
      });
  }
  localStorage.scriptSnippets = JSON.stringify(currentSnippets);

merging in snippets

You’ll get a prompt and you need to paste in the contents of the clipboard. This was the easiest way I could think of to share data between windows

If all went as planned then put focus on the bottom left window, hit F5 and you should now see:

Success!!

You can see that the snippets are there in the top left window.

Play around while you’re in there, hopefully you’ll learn about the internals of how dev-tools work

Make News More Fun

| Comments

I just read this xkcd and thought, I never again will have to read boring news Substitutions

So I made a quick bookmarklet accomplish just that

News Interestinger

Hint: Click it and watch the text in the code change.

Source:

var dict = {
    "Witnesses": "these dudes I know",
    "Allegedly": "kinda probably",
    "New study": "tumblr post",
    "Rebuild": "avenge",
    "Space": "spaaace",
    "Google Glass": "virtual Boy",
    "Smartphone": "pokedex",
    "Electric": "atomic",
    "Senator": "elf-lord",
    "Car": "cat",
    "Election": "eating contest",
    "Congressional leaders": "river spirits",
    "Homeland security": "Homestar Runner",
    "Could not be reached for comment": "is guilty and everyone knows it"
}
var dictRegex = new RegExp('(.)\\s*\\b(' + Object.keys(dict).join('|').replace(/\s\s*/g, '\\s*') + ')', 'gi');
var dictReplacers = [];
for (var i in dict) {
    dictReplacers.push([new RegExp(i.replace(/\s\s*/g, '\\s'), 'i'), dict[i]])
}
var textNodes = [];
var all = document.getElementsByTagName('*');
for (var i = 0; i < all.length; i++) {
    for (var j = 0; j < all[i].childNodes.length; j++) {
        if (all[i].childNodes[j].nodeType === 3) {            
            textNodes.push(all[i].childNodes[j]);
        }
    }
}
for (i = 0; i < textNodes.length; i++) {
    if (textNodes[i] && textNodes[i].nodeValue) {
        textNodes[i].nodeValue = textNodes[i].nodeValue.replace(dictRegex, function(all, preChar) {
            console.log(preChar)
            for (var j = 0; j < dictReplacers.length; j++) {
                if (dictReplacers[j][0].test(all)) {
                    return preChar + (!/[a-z\x27]/i.test(preChar) ?
                        dictReplacers[j][1].charAt(0).toUpperCase() + dictReplacers[j][1].slice(1) :
                        dictReplacers[j][1])
                }
            }
        });
    }
}

Circular Doubly Linked List

| Comments

I was working on a cycling plugin for javascript and kept getting bogged down by accessing the prev and next elements in the array. I kept having to do things like:

function step() {
    var currentItem = items[currentIndex];
    var nextItem = items[(currentIndex + 1) % items.length];
    var prevItem = items[(items.length + currentIndex - 1) % items.length];
    // more code
    index = (index + 1) % items.length
}

While this is doable it just seems like a pain.
Giving it more thought I realized that a circular doubly linked list would be a great solution to this:



var list = [{label: 'first'}, {label: 'second'}, {label: 'third'}];
var circled = circleLink(list);

assert(circled[0].next === circled[1]);
assert(circled[1].next === circled[2]);
assert(circled[2].next === circled[0]);

assert(circled[0].prev === circled[2]);
assert(circled[1].prev === circled[0]);
assert(circled[2].prev === circled[1]);

document.write(circled[0].index + '<br>');
document.write(circled[1].index + '<br>');
document.write(circled[2].index + '<br>');
document.write(circled[0].next.next.next.prev.prev.index + '<br>');

function circleLink(array) {
  var linkedListCircle = []
  for (var i = 0; i < array.length; i++) {
    linkedListCircle[i] = array[i];
    linkedListCircle[i].index = i;
    if (i > 0) {
      linkedListCircle[i].prev = linkedListCircle[i - 1];
    }
  }
  linkedListCircle[0].prev = linkedListCircle[i - 1]
  for (var i = 0; i < array.length - 1; i++) {
    linkedListCircle[i].next = linkedListCircle[i + 1];
  }
  linkedListCircle[i].next = linkedListCircle[0];
  return linkedListCircle;
}
function assert(assertion) { document.write((assertion ? true : false) + '<br>') }

See the Pen %= penName %> by Moshe Kolodny (@kolodny) on CodePen

Devtools Copy to Clipboard

| Comments

I was playing around with devtools and i saw in the autocomplete list of functions had copy()
It does what it sounds like it does (copies text to the clipboard)

> copy("I'm about to hit ctrl+v")
undefined
> I'm about to hit ctrl+v

Very useful for when you need to manipulate large chunks of text.

Works in Devtools and Firebug (at least)