Ben Pickles. Saying stuff about stuff.

js-model-rails

Last week I put together a Gem for use with the new Rails 3.1 asset pipeline, it packages up js-model and means you'll never have right-click-save-as-move-to-directory ever again. Now all you have to do is include it in your Gemfile:

gem 'js-model-rails'

And require js-model in your application.js:

//= require js-model

Being able to manage JavaScript and CSS dependencies using Gems is a nice feature but it really starts to get interesting when you consider how a little backend integration can ease your frontend development. For instance, at the moment js-model-rails does something I always forget to do myself:

config.active_record.include_root_in_json = false

But in the future it'll read your include_root_in_json setting and configure the JavaScript accordingly.

There are so many areas where having access to the backend environment will make working on the frontend much simpler. I'm not suggesting that every JavaScript library needs a corresponding Gem but in this case - and no doubt many others - it's the start of a beautiful friendship.

Here's the source on Github.

Posted on

Using onScreen to power infinite scrolling

A while ago I wrote a really simple jQuery plugin called onScreen that adds a selector to filter out elements that are currently visible on the screen. Here's a quick snippet of jQuery that uses it to help power infinite scrolling.

$(function() {
  function loadMorePosts(post_id) {
    var path = "/posts?offset=" + post_id
    if (history.pushState) history.pushState({}, null, path)
    $.ajax(path)
  }

  // Every second check if the last post is on-screen, if it is
  // then load the next chunk of posts.
  setTimeout(function() {
    var post = $(".post:last:onScreen")
    if (post.length) loadMorePosts(post.data("id"))
  }, 1000)
})

Posted on

Fixing the port number in OmniAuth callbacks with a Varnish/nginx/Passenger stack

My current server setup is thus: Varnish on port 80 and nginx (with Passenger) on 8080. Unfortunately my Rails app thought it was running on port 8080 and therefore my OmniAuth/Twitter login was returning to the wrong URL - something along the lines of http://example.com:8080/auth/twitter/callback... .

I tried setting X-Forwarded-Port etc in Varnish and fastcgi_param SERVER_PORT 80; in nginx, both without success. However, if you are using Passenger then you want to set:

passenger_set_cgi_param SERVER_PORT 80;

Here are the docs - they say you can set it in an http block but that didn't work for me and I had to add it to the server block.

Anyway, this solved my problem and will hopefully help a few more people.

Posted on

Finding a DOM node’s common ancestor using JavaScript

I recently ran into a problem where I needed a way to find the common ancestor of two DOM nodes (using JavaScript). I wasn't happy with the accepted answer I found on Stack Overflow so I made my own.

function parents(node) {
  var nodes = []
  for (; node; node = node.parentNode) {
    nodes.push(node)
  }
  return nodes
}

function commonAncestor(node1, node2) {
  var parents1 = parents(node1)
  var parents2 = parents(node2)

  for (var i = 0; i < parents1.length; i++) {
    if (parents2.indexOf(parents1[i]) > -1) return parents1[i]
  }

  throw "No common ancestor!"
}

Unfortunately, after working with Mr Blimke for so long this first algorithm is no longer good enough for me as it has an efficiency of O(N1 * N2) - basically O(N2).

So I thought a little longer and made some simple changes. The new algorithm has efficiency O(N) and a bonus of detecting whether the nodes are actually related before attempting to compute anything.

function parents(node) {
  var nodes = []
  for (; node; node = node.parentNode) {
    nodes.unshift(node)
  }
  return nodes
}

function commonAncestor(node1, node2) {
  var parents1 = parents(node1)
  var parents2 = parents(node2)

  if (parents1[0] != parents2[0]) throw "No common ancestor!"

  for (var i = 0; i < parents1.length; i++) {
    if (parents1[i] != parents2[i]) return parents1[i - 1]
  }
}

The thing is, I imagine there are even more efficient solutions but this one will have to do for now! Anyway I added it as an answer to the original Stack Overflow question so mod it up if you like it!

Posted on

Bash script to convert media to MP3 and add it to iTunes

Despite this task sounding difficult it's actually very simple, FFmpeg does all the hard work (I suggest using Homebrew to install brew install ffmpeg) and not only converts other audio formats to MP3 but can also extract audio from video files.

Adding to iTunes is also ridiculously easy nowadays. There's no mucking about with AppleScript, you simply have to move the file to a special iTunes folder (~/Music/iTunes/iTunes Music/Automatically Add to iTunes/) and iTunes will assimilate the file into its directory structure.

Here's the Gist.

Posted on

Input favicons

The newly available <input> types are one of the easiest and most useful (well, when viewed on an iPhone at least) ways to practise HTML5 citizenship. I have an <input> that is used for a URL and the excellent news is, lo and behold there is an <input type="url">. But I also wanted to add an extra little nicety by displaying the URL's related favicon along with the <input> like so:

This can achieved really simply using Google's unofficial favicon API and a sprinkling of jQuery:

$("input[type=url]").change(function() {
  var match = /^(?:https?:\/\/)?([\w\d\._-]+)/i.exec(this.value)
  var domain = (match && match[1]) || ""

  $(this).css("background-image",
    "url(http://www.google.com/s2/favicons?domain=" + domain + ")")
}).change()

Suprisingly this actually works in IE8(!) and in IE6/7 everything will degrade gracefully: the browser falls back to using type="text" and jQuery won't find the correct <input>s to add the behaviour to - the user simply won't see the functionality.

Of course the same thing could have been achieved by using a class="url" instead of type="url", it would have even worked in IE6/7, but that just wouldn't have felt so right - plus it's exactly what IE users deserve!

P.S. You'll need to add a teeny bit of styling, Google's favicon images are 16×16px and this did it for me:

input[type=url] {
  background-repeat: no-repeat;
  background-position: 2px 2px;
  padding: 3px 3px 3px 19px;
}

P.P.S. g.etfv.co also looks interesting and it's even open-source, though that looks suspiciously like SVN. SVN‽

Posted on

Minify command-line tool

While creating the jQuery plugin "onScreen" I had a sudden burst of inspiration and hacked together a quick command-line script to minify JavaScript files. It's a really simple way to generate a .min.js version of a file and is particularly helpful when distributing JavaScript like the aforementioned jQuery plugin.

Here's the Gist.

At its most simple you can pass a single argument which is the path to the JavaScript file to minify, a .min.js version will be created for you in the same directory. So the following will create jquery.myplugin.min.js:

$ minify jquery.myplugin.js

You can also pass more paths and it will concat and minify them into a single file - the last argument is always the output file:

$ minify input.js output.min.js
$ minify a.js few.js files.js output.min.js

Internally it uses Google's Closure Compiler via the closure-compiler Gem (which requires Java and, obviously, Ruby) so all the hard work has already been done for me.

I've been writing a few command-line tools like this one, it feels great to scratch your own itch. I also recently wrote ding which - yes you may chuckle - I find surprisingly useful!

Posted on

onScreen

I made a simple jQuery plugin called onScreen that can detect whether an element is currently visible on-screen. It adds a custom :onScreen selector to jQuery allowing you to do something like this: $("span:onScreen").

The example is a little contrived:

$(function() {                          // On document load
  $(window).scroll(function() {         // when the page is scrolled
    $("h2")                             // get all <h2>s
      .css("background-color", "")      // reset their background colours
      .filter(":onScreen")              // get only <h2>s on screen
        .css("background-color", "red") // give them a red background
  })
})

I'll explain exactly how I'm using it another time, in the meantime it's on GitHub for your perusal.

Posted on

Bespin Bookmarklet

I've been watching Bespin for a while and am impressed with the feature-set and their rapid development pace but their latest addition is really nice - a bookmarklet to turn any <textarea> field into a code editor.

Their demo gives a perfect use-case with github's gist creation: Introducing the Bespin Bookmarklet.

Posted on

Peity Goes Global

Peity seems to be getting some global love, here in Japanese and Slovakian.

I think people will like the new "line" and "bar" chart types that @ismasan and I have added. My personal favourite is "line" - go check them out!

Posted on