Thurloat.com Adam Thurlow

Listening with Backbone helps your Memory

Backbone 0.9.9 was released today! With it came the inclusion of a long-time requested feature to help views manage the binding and garbage collection of the events bound to the objects within the view. The new methods that I'm going on about are:

  • listenTo(object, events, callback): Takes the same arguments as Backbone's Event.on with the addition of having the view remember which object is bound to which events and callbacks to unbind them later on.
  • stopListening(object, events, callback): Calling it by yourself with the original object, events and callback will remove it from the registry, and it gets called for you during remove with no arguments to unbind all events for the view.

This is big news for folks developing single page applications. Especially where you are building up and tearing down many views without page reloads. If you're not careful about managing object event listeners, you end up with many detached objects that will never get garbage collected. The outcome is that your application continues to grow in memory usage, and performance degrades rapidly as time goes on.

Taking it Further

Being able to listenTo events on objects is great, but we can take it an additional step in the right direction. One of my favourite parts of Backbone is how declarative you can keep your view classes. This makes it immediately obvious what a view is responsible for and what it's connected to just by reading the first few ...

Mocha Style Reporters in PhantomJS with Jasmine

Everyone and their cat knows that the test reporters in Mocha are much more nicely put together than those in Jasmine. However, in the latest version of PhantomJS (1.7) there is a new API that allows Jasmine console runners to catch up. I got tired of watching a slew of bland and illegible console.log messages after every code change, so I went ahead and implemented the infamous nyan reporter that comes built into Mocha using the new API methods.

Jasmine Nyancat Runner

I also made a quick video of a failing test suite, and a sad Nyancat.

The general approach is to have your test runner within the headless browser make a call out to phantom with window.callPhantom and use it as a sort of message bus to report progress within the test reporter. And within your PhantomJS executed Javascript, hook onto page.onCallback and pass some parameterized objects around to notify Phantom of the child page's state.

Jasmine Reporter

Here's a stripped down version of what I have in place to do the console reporting to help illustrate the new API methods.

new-console-jasmine-reporter.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// A simple jasmine reporter that will report over the `callPhantom` API in
// the new PhantomJS 1.7 release.
NewConsoleReporter = function() { };
NewConsoleReporter.prototype = {
  reportRunnerStarting: function(runner) {
    // Announce that the ...

Jasmine Asynchronous Specs

I'll start out by saying that I'm a huge Jasmine fan. I have been working with it exclusively for testing during the last few months. However, it has become common knowledge that Jasmine's built-in methods for testing async code are pretty weak and make for either lots of boilerplate, or fragile tests.

Let's go on a journey together from a small greenfield test suite through to a full(er) set of specs as the project grows. I will walk through how I came to implement a jasmine test extension for simplifying asynchronous specs which I believe is a more holistic approach than I've seen anywhere else.

The few key differences you should note about the final solution are that it:

  • Allows for fail-fast tests without breaking the waitsFor callback when an exception is thrown,
  • Provides a simple mechanism to wrap async methods so their exceptions are caught and reported correctly,
  • Doesn't require any different syntax than a Jasminaut is used to (less refactoring).

Affairs

I wrote a post a few months ago which, at the time, dealt with my asynchronous woes enough to keep me going. To quickly re-cap check out the small snippet below.

runs-spec.coffee

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
describe "My Functionality", ->

  it "should behave as expected", ->
    collection = new Backbone.Collection()
    collection.fetch
      success: -> runs -> # <-- the magic.
        expect(true).toBeTruthy()

        # something ridiculous
        do candy.eat

As you can see, I am now executing the callback function ...

Jasmine Tests Swallowing Asynchronous Exceptions

On a recent cross-continent programming expedition, I ran into an exceptionally frustrating bug while writing tests using the Jasmine unit testing framework. After coming up with a solution on my own, I scoured the web for additional insight and only found one other mention of this bug ( and have since lost it ). This reference proposed a solution very similar to mine. The most disheartening aspect is that this bug is not necessarily a bug within Jasmine but can only be worked around by following a specific code pattern which I'll be describing later.

This bug took ... a while to track down. Such a long while in fact that most of the time was spent detecting the bug in the first place. It manifested by causing tests to pass. I'm not sure if you are aware, but one of the first rules of good unit tests in my books is that they do not emit false positives. False positives in a test suite usually indicates that your tests don't have good branch coverage or they do not assert the correct behaviour - not that many of your tests are naively written in a way which makes them pass no matter what kind of junk you put between the {} in an asynchronous callback.

Steps To Reproduce

  1. Write some tests examining some asynchronous behaviour.
  2. Expect something reasonable.
  3. Write something that will throw an exception.

Outcome: All tests pass and you believe that you write bug-free code.

Desired Outcome: Test fails and ...

Jackson Deserializing Polymorphic JSON

Recently I've been spending time writing a REST API using a new stack. Java AppEngine + Objectify using Jersey to build the API, and Jackson to handle (de)serialization into/out of JSON. Needless to say I was running into many problems that just don't come up in the Python world.

There was one problem that our team recently ran into that I thought I'd share, as it took a long time to find the correct solution and there was a lack of help online in this area.

Problem

Reading and writing polymorphic data types using Jackson. For example a List<Key<Entity>> at runtime can only be recognized as List<?> due to Type Erasure with Generics so Jackson can really only try to serialize and deserialize that List as a list of POJOs. We already have a serializer that writes out the Long value of a Key as "[1, 2, 3]".

However Jackson has no idea how to turn "[1, 2, 3]" back into a List of Entity Keys and complains with something along the lines of "Jackson doesn't know how to turn type List<Integer> into List<Key<Entity>".

There are other documented solutions to this problem that didn't fit. The most common was to embed type metadata into the JSON and then tell Jackson about it using configuration. However after looking at the JSON users would have to POST to the API, this was obviously a no-start.

 1
 2
 3
 4
 5
 6 ...

Backbone.js - Modular, Readable and Testable Views

After more work with Backbone on projects at Sheepdog, we are starting to develop a solid foundation that will allow us to easily develop, test, and maintain our Backbone.js applications. Having these properties in our JS / CoffeeScript is very important, as we're striving toward better test coverage from server to client in order to best deliver the highest quality product to our clients.

The approach that I'm taking to solve our problem is a simple implementation of two common design patterns found in MVP applications. The Observer Pattern, and the Factory Pattern.

I, Observer

Obtaining much of my MVP knowledge from one of our products written in GWT (gTrax) using the awesome GWT-Presenter and EventBus libraries, it's been ingrained into my being that all independant pieces of the application should communicate exclusively over an agreed-upon API. Rather than touting the benefits of modular code, I'll assume that since you're still reading this you understand.

EventBus helps consolidate communication while developing in java and GWT, and I didn't have to look far to find a flexible solution already available. Backbone already mixes in the Backbone.Events class into all core pieces of the framework so it's the perfect candidate (shaving a yak, and all that). While I've seen this pattern implemented a few times, at Los Techies most notably, it has been missing a key piece of the puzzle. Either people are binding an instance of Backbone.Events directly to the window ...

Backbone.js - Keeping a View's model in sync

I'm doing a lot of experimenting with Backbone.js for the team at Sheepdog these days. We're working on compiling a front end stack to build our internal and client projects that require complex front end interactions.

One of our "wants" for Backbone is to easily bind a Backbone.View containing many form elements directly to a Backbone.Model instance. I wanted to implement this in the most declarative way possible, similar to the way a view is structured. The solution involved creating a subclass of Backbone.View called FormView.

Before I start, I'd just like to show an example of the git diff stat from the commit where I changed one of our View implementations to use FormView. They say that you can calculate the value of your work by the lines of code that it shrinks while maintaining functionality.

http://f.cl.ly/items/0Q3j0y1l1L2t0E1P3b0N/ImplementingFieldMap.png

Moving On

To explain fieldMap, I'll go through a simple example of creating a simple Model and View to lay it all out. I'll start by creating a model for the User with some fields fullName, foo, and bar.

model.coffee

1
2
3
4
5
class User extends Backbone.Model
  defaults:
    fullName: ''
    foo: ''
    bar: ''

Then create a View to display this new User class.

view.coffee

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
class UserForm extends Backbone.View
  tagName: 'div'

  events:
    'click #test_reset': 'resetModel'

  resetModel: ->
    @model.set
      foo: 'bang ...

Advanced App Engine Bulk Downloading

http://commondatastorage.googleapis.com/thurloat/appenginedump.png

The new Bulkloader data exporter is much easier and more automated than the nasty old way of exporting. As good as it is, there were a lot of additional requirements for one export script that I was tasked to write which looked beyond the scope of the Bulkloader YAML configuration file. Through reading the documentation and diving into the I was able to find some creative solutions and for the most part -- keep using the YAML file.

The long and winding road

I'm going to go through the problems I faced, and how I was able to solve them using primarily the YAML config file with the new Bulkloader.

CSV is out, Pipe separated is in

One of the requirements of the exported data was that it wasn't comma-separated. It needed to be delimited by a pipe, "|", character. The options for exporting data via the Bulkloader are CSV, simpletext and xml. I don't see "PSV" as an option.

Here's what I did to fix this:

1
2
3
4
5
- kind: model_name
  connector: csv
  connector_options:
    export_options:
        delimiter: "|"

I was able to re-use the Bulkloader CSV connector by passing through additional arguments by proxy to the Python CSV module. The outcome was a nice clean pipe separated data format. This was much, much better than the original thought: to try and use the Python module after the CSV connector generated the file to re-write the CSV as pipe separated.

The two parameters that you can pass to ...

Google Closure Linter TextMate Bundle

Google Closure TextMate Bundle

I love the Google Closure project. As a big fan of its compiler and the linter, I use both regularly at work and for my personal projects. However, it is not without its flaws. The biggest hurtle to integrating the Closure Linter into my workflow: it's strictly a command line tool. I have nothing against using the terminal but, the workflow for running the linter over my JS looks like this:

  1. Make some changes to the JS file.
  2. Open the terminal, run gsjlint source.js
  3. Look over the results (ugly terminal output).
  4. Jump back to the Editor, and find where the problems were.
  5. go to 1.

This tedium quickly became frustrating. I wanted a more automated approach to running the Closure Linter; so I hacked together a TextMate Bundle to make all of our lives a little easier. I was able to integrate the linter into my TextMate workflow without a hitch. Thus, here's my new workflow:

  1. Make some changes to the JS file.
  2. Hit "Shift + Ctl + Opt + A", results appear in a TextMate Browser window, next to the code.
  3. Keep making changes, and using keyboard shortcut to update the linter window.

Here's a screenshot of this new workflow in action:

Closure Linter Workflow

Pretty neat! However, some of the changes that the Closure Linter wants you to make are monotonous. I don't want to waste time putting a space before my = sign, or converting double quotes " to single quotes '. Luckily, part of the Closure Linter utilities is a ...

Rietveld Rocks. Tips for other newbies!

The Potatoes

Peer code review has become one of my favorite feedback processes by far. At the SheepDog office, we're forming the habit of reviewing any code that comes out of a sprint. In order to move a task from tested to verified, it must get signed off by an engineer who did not write the code. This process was quickly becoming cumbersome when done manually. For example; A large feature would be created, then tested, and no one had the time to look it over because the review process was: check out the source code at the correct location, comb through it, and provide manual feedback to the other developer -- had it not slipped your mind to review it in the first place.

We installed the code review tool Rietveld on our production domain this week. Getting it installed on our domain was incredibly easy; however it's not the easiest option to find. PROTIP: Once you're in the Google Apps Domain Control Panel, click Add Services, Then you'll see this little link at the bottom where you can install it..

Domain Labs

Once inside, you can install the application as you would any other to the domain. Of the many cool features, one of the things we quickly fell in love with was the ability to leave a review without touching the mouse. This made reviewing code much easier and seemingly familiar with vi-ish key bindings; for example:

  • j next diff,
  • k previous diff,
  • n next line ...

GitHub Gists + RSS Reader = GistRSS

I am pleased to announce GistRSS (a.k.a "Pretty GitHub Gist RSS Feed App") to the world. The idea for this application came about after a frustrating conversation with a friend of mine. The problem that we exposed was that GitHub currently has mediocre Gist atom feeds. GistRSS will allow you to subscribe to any GitHubber's Gist feed and their Gist content will be sent straight to your RSS Reader fully syntax highlighed.

The History

After scouring the internet for some great tech RSS feeds, and bugging my friends for help, Erik Kastner came to the rescue with an idea to get fresh content into my RSS Reader.

The Transcript

1
2
3
4
5
6
7
8
9
Erik: I have a programming section but don't really have much in there
Erik: I also follow defunkt's gists ;) http://gist.github.com/defunkt
Me: i never thought of following gists, thats a neat idea
Me: publish a new gist, so i can see what reader does when you post a new one
Erik: it's not very great :/
Me: sounds like a cool project, github gist pretty rss maker
Erik: that would be fantastic
Me: \*adds to to-do list\*
Erik: ttyl :)

A few hours later, I had a working prototype. Since then I've added some caching and syntax highlighting to the raw Gist content. I'm pretty excited to have people using GistRSS and it's become something that I use every day. There's ...

Google Storage for Developers on App Engine Python

I've been using Google Storage since I/O 2010, and I'm impressed with it so far. At work, we've converted some of our application resources from using S3 as a CDN to Google Storage this past week.

Being early adopters has its downfalls for sure; some of which usually include system instability, a lack of published tools, and proper or extensive documentation. I'll try to help address the latter in this short tutorial.

Configuring Google Storage for App Engine

This problem has come up a couple of times in the Google Storage for Developers group. After looking at the Python Library Docs for GS, the biggest hurdle looks like getting the boto library properly configured. Once boto is set up, you should be able to run all of the sample code provided by Google. Lets get you there.

The Problem

1
2
3
4
5
6
7
# Bring in the boto library
import boto

# Load the configuration variables from your .boto file
config = boto.config

""" FAIL. """

As you can see by my fail comment, App Engine has no way to access your ~/.boto file where your Secret Key is located. We can solve this by diving into boto.config. When the boto configuration is initialized, It checks for the initial file and tries to parse it and load it into memory by using add_section(section_name) and set(section_name,key,value). Knowing this, you're able to call those functions yourself after loading the boto lib and ...

Experimenting with a new Flash

I've been a little behind on my photography posts, but I have not been behind on expanding my horizons and taking all kinds of crazy pictures; most of which will not end up around here. However, for Christmas Dad got me a flash for our camera. He miraculously did this through leaving it in his old SLR camera bag (big thanks Dad!) I've had a heck of a time trying to get it working the right way with the much newer Canon camera.

Just a warning to digital camera owners

"Careful with old flash vs new camera voltages. They could blow the hot seat off your camera if you're not. So do your research in advance to make sure that it's safe with your camera."

—Mr. Ian Munroe

So after about 5 days of toying, learning that the auto settings on the old flash won't communicate with newer cameras, and trying desperately to search online for the manual, I decided to take my own way out and through caution to the wind. I spent a while toying with the shutter speed, aperture and ISO to try and stop the insane hot spots the flash was giving me. I ended up using something along the lines of 100ISO, 1/500, F16. This worked fine for the direct shots, but I'm finding the flash is more useful when I point it somewhere else**....** I really enjoy finding new ways to shoot the same shot, give it ...

SWAT Team in my building.

All of the Police activity around my apartment building is winding down, and Police report was finally submitted to halifax.ca, but they don't have permalinks for their reports so I thought I might just paste it below for future reference.

Drug Charges

December 9, 2009 Drug Search Results in Charges Four men and one woman face charges after a drug search last night in Fairview by the HRP/RCMP Integrated Drug Unit assisted by the Quick Response Unit. A search of a residence in the 0-100 block of Dawn Street resulted in the seizure of a quantity of marijuana, cocaine, hash and other drug paraphernalia. A handgun magazine containing bullets was also seized. Five people were arrested at the scene without incident. Stephen Andrew Coleman, 21, of Dartmouth, is charged with drug trafficking and weapons related offences. He was also wanted on a parole warrant. Shea Alexander Durnford, 21, of Halifax, is charged with drug trafficking and also had an outstanding parole warrant. Ryan William Ross, 20, of Dartmouth, Ashley Rose MacNeil, 23, and Eric Thomas Whickens, 53, both of Halifax face charges of drug trafficking. Coleman, Durnford, Ross and MacNeil were all held for court today while Whickens was released but also scheduled to appear in court today.

Photography &amp; The Tilt-Shift Effect

On a regular basis I find myself looking at something and thinking "damnit, I wish i could keep this"... often times what I'm seeing in my head isn't really what would be captured on film, my imagination is often much less literal. That has been one of Photography's biggest turn-offs for me in the past and hopefully this is where capturing and editing skills from a few of my friends will keep me motivated!

In my Photography & Lightroom corner, I have Ian Munroe. Ian is doing a lot of over time getting my technical photography skills up to par. Anything from helping to fix my over-zealous white balance settings, to being my personal Wikipedia for technical knowledge, he's been right there. He's also helping to keep me inspired with constant feedback on what I've placed in his DropBox, and donating tips (hard learned, I'm sure.) on using Lightroom to put some finishing touches on my photos. In my Photoshop & Editing corner, is Chris Lowe. I've been working with Chris at Norex for 6 months or so now, and he's been helping me out a lot with any Photoshop and design related questions I might have. I'm especially keen on learning editing techniques that will help me try and transfer my vision into something solid.

With all this help, I'm sure that I'll be able to do some transferring of vision; That's what I like to call it ...

Google App Engine Case Study!

Earlier this week I was presented with the opportunity to write a blog post / case study for the Google App Engine Blog about how I leveraged App Engine while developing the Canoe '09 results application. How could I refuse such an honor? Well, I couldn't. So after some careful edits from the guys over at Google, and the folks here at the Norex office, we came up with an interesting piece about the Agility, Reliability and Stability that we saw during the course of the event. True Story!

Preview

... we saw over 1,000,000 page views from 93 countries around the world, and experienced incredible stability and scalability from Google App Engine even when we were spiking 350 requests per second during the finals. It was a big win for Norex, and App Engine has proven itself to be a serious contender for developing scalable web applications. Thanks so much to the Google App Engine team for providing such an outstanding product!

link

Director's Cut

One thing I think may have been missing from the case study is the notion of how great of a team we had working on the application. As much as I did the bulk of the programming and behind the scenes work, It wouldn't have gone anywhere without the:

  • Idea & Motivation (Julia Rivard)
  • Management who believed in the project (Brandon Kolybaba)
  • Development Help (David Wolfe & Chris Troup)
  • Thorough Testing (Anas Trabulsi)
  • Human Power (Mark Jamieson)
  • Designer (Justin Bellefontaine)
  • My Fiancee (Jessica ...

iPhone Tethering

This is my iPhone tethering story. With the cable down and no where else to turn I looked to my trusty swiss-army knife of utilities: My iPhone... did it pass or fail? Well, before I tell my Saturday evening Internet story, I'll preface with the fact that I'm upset that Rogers isn't including Internet tethering unto us lowly 500MB/month folks. Seriously, what's up with that? They can't be worried about me going over my data limit, as that would just make them more money!

The Story

The cable seemed to be out in the apartment this evening, so I decided what better time than now to get tethering work on my phone. This was not a huge feat of wizardry, it is quite well documented throughout the web. After that went smoothly, I just plugged it in. I'm writing this post on my new fan-dangled tethering device, and the connection is wonderfully persistent (so far). I'll be Frank, Frank is surprised how easily and wonderfully easy (did he mention that it was easy?) it was to get running. One thing Frank noticed is that iTunes appears to be taking ~%12-18 CPU to keep the connection going, which he figured was just because iTunes was pulling double duty as an emergency Internet connection, and frankly, Frank was okay with that. Frank also enjoyed the browsing speed. He immediately noticed that pages were a lot snappier on his laptop as opposed to his phone ...

Goals In Review

It's about that time that I come out and see how my planned goals are coming. I hate making excuses for not doing things, and it hurts me to do so. So here's a long list of excuses.

Results

Whether or not anyone has heard, my father passed away from complications with chemotherapy on July 3rd, 2010. Sparing all of the emotional outcry, it's thrown a wrench into my plans and put me out of commission for a week or two. I won't say that I haven't made ANY progress, as a couple goals are in the double digit completion range. Here's the breakdown.

Contribute something good to an Open Source project

Verdict : 0%. Sadly I haven't had any time to work on personal projects / scour github for fun code to do stuff with.

Follow up with at least 1 of the micro-startup idea

Verdict : 15%. I've actually had a few minutes to think about this, so I determined that any decision is a good one. So I started investigating the isdanecookinthismovie.com idea. Haven't gone any further than doing some proof of concept iMDB apps, but the ball IS rolling.

Break the 100 Twitter Followers barrier (real 125)

Verdict : ~75%. At the time of writing, my twitter counter reads 119 followers. I'm surprised at this growth, as I've really been slacking lately in good tweets / anything not involving my father or his funeral. It's amazing how supportive ...

The Greatest Man Who Ever Lived

I took a day off from going to the hospital and visiting my Dad. As much as we love to be there with him, we there are other people's needs that need met. While working today I was slowly writing down my thoughts about the current events and it turned into a letter. I think that the best way for me to keep on top is to share how I'm feeling, and I'm feeling like hell... So as you might imagine, this is very emotional and probably sounds like a bunch of rambling, but either way.

A Letter to my Father

Hey Dad,

I love you. I know we don't talk about mushy things much, but there are a lot of things I'd like to tell you. You're an amazing inspiration to me, you've taught me so much about living life. I have so many memories of us having a good time.

Jumping off of the roof in Ottawa into your arms, you taught me to trust.

Making those cassette tapes with scary noises around halloween, you taught me to laugh.

Being with mum and always doing the most thoughtful and wonderful things for her, you taught me to love.

Preserving through your entire life of obstacles, you've taught me to be so strong.

You've introduced me to all of my hobbies and loves of life. When I was just a kid you would bring me to the base and let ...

It's Time for a Change!

I had a good chat with Jessica the other day about my work load, the things I say I will do, things promise I will do, priorities, and life decisions. She always gets me into a thought provoking mood. It turns out that I have made it a habit for myself to tell everyone YES! to whatever they're asking ... then attempt to cram it into my schedule. It's been pointed out to me that if I somehow don't get these things fit into my schedule... I appear un-reliable. eek. So there are 2 action items that I've decided I NEED to take. I need to start telling people that I'm busy, and I need to come up with some clear goals that I am going to work on. So I've decided that in order to build motivation I need a list! I've always been inspired by Erik Kastner's Blog posts about his goals and I think that writing them down and making it permanent will help make it happen. I have a reputation to uphold, you know!

This Month's Goals

  1. Write this post.
  2. Contribute something good to an Open Source project.
  3. Follow up with at least 1 idea.
  4. Break 100 twitter followers.
  5. Work on our Wedding & RSVP web page.
  6. Finish 2 paintings.
  7. Finish my mother's website.
  8. Lose 15 pounds.

Write this Post!

...and more more after this

I've had this post stored in a text file on my computer ...

The Picard Loop

So, here I was (you are here). Sitting on the couch, leisurely watching “John and Kate plus 8”. When I looked over at Jessica’s Psychology Quiz. This text is quoted straight from an University Psych textbook... seriously.

If Captain Picard leaves Deep Space Nine at 0900 hours and travels at Warp 4 his shuttlecraft, he can rendezvous with the Starship Enterprise in 4. 2 hours. However, in order to reach the ship so quickly, he would have to travel through the Neutral Zone, and his presence would undoubtedly be noticed by Romulan warships, which would attack and destroy his craft. Thus, Picard realizes that he will have to take another route to the ship and thus devises what comes to be known as the “Picard loop” to get around the neutral zone. The Captain has applied ____.

  1. crystallized intelligence ability
  2. an algorithm
  3. deductive reasoning skill
  4. means-ends analysis

My question is:

Why is picard on Deep Space Nine?

Google Chrome for Mac!

I just got my mitts on Google Chrome for Mac. The JS speed is incredible.

http://img.skitch.com/20090605-gwj26p3tbqtwfak4f9u1t6kwna.png

There is actually a visually noticeable speed difference when using our Framework back-end. I might just start using it for adminning sites / content entry because it doesn’t ever bog down on the heavy JS activity with Chunk revisions. It just seems to be more snappy in general compared to FF 3.01, where, on many occasions FF will freeze for 1/4 of a second when it’s processing JS or finishing a page load. It creates a frustration inside of me so deep and burning ...

Seriously. It’s only quarter of a second, but if I had all of those quarter seconds back, I’d for sure have at least a minute on my hands.

P.S. i just noticed that half of the toolbar for making tumblr posts has disappeared in chrome.. weird.