A collection of graph and diagram tools

Debugging cucumber feature with javascript + firefox vnc

TL;DR Debugging problems with javascript errors in cucumber tests is sometimes easier in the browser. Run the test, stop at the problematic point (with Then pause from Spreewald 1.7+) and open VNC for Firefox.

Features:

Capybara: Find an element that contains a string

There is no CSS selector for matching elements that contains a given string ¹. Luckily, Capybara offers the :text option to go along with your selector:

page.find('div', text: 'Expected content')

You can also pass a regular expression!

page.find('div', text: /Expected contents?/i)

Note that if your CSS selector is as generic as div, you might get a lot more results than you expect. E.g. a <div class="container"> that surrounds your entire layout will probably also contain that text (in a descendant) and ...

Copy to clipboard without flash (clipboard.js)

We used zeroclipboard.js in some of our projects but now we switched to clipboard.js because it does not rely on flash. Flash support of the major browsers has ended.

Some more advantages of clipboard.js:

  • it consists only of a single javascript file, so it does not trigger additional requests with rails
  • it automagically provides user feedback by selecting the text it has copied
  • it provides callbacks for success and error which make it easier to add custom behaviour after copying to the clipboar...

tesseract.js: Pure Javascript OCR for 62 Languages

This might be relevant for us since we're often managing customer documents in our apps.

I played around with the library and this is what I found:

  • A 200 DPI scan of an English letter (500 KB JPEG) was processed in ~6 seconds on my desktop PC. It does the heavy lifting in a Web worker so you're rendering thread isn't blocked.
  • It detected maybe 95% of the text flawlessly. It has difficulties with underlined text or tight table borders.
  • When you feed ...

Debugging flickering VCR tests

We often use VCR to stub external APIs. Unfortunately VCR can have problems matching requests to recorded cassettes, and these issues are often hard to debug.

VCR's error messages mostly look like this and are not very helpful:

An HTTP request has been made that VCR does not know how to handle:
         POST http://another-site.de:9605/json/index

VCR fails if the request does not exactly look like the request it has recorded. If the request is d...

VCR fails if the same request is triggered multiple times

Same requests are recorded only once in vcr. Replaying a test fails, if you trigger the same request multiple times. The error message is somehow confusing, as your cassette contains the request:

An HTTP request has been made that VCR does not know how to handle

If you want to allow to match a request multiple times, you need to configure this explicit with allow_playback_repeats: true. Some exa...

Javascript: Read params from url

There is no build in functionally in jQuery and Prototype to extract params from a url.

  1. You can use this library (not tested): jquery-deparam

  2. Use Unpoly and the following code snippet

ClockPicker: Android-style time picker for Bootstrap

ClockPicker is a JavaScript and Bootstrap implementation of an Android-style time picker which looks and feels great.

Unfortunately, it is no longer maintained.

ClockPicker.png

An intro to Javascript promises

Promises are the new way™ to express "Do this, and once you're done, do that". In contrast to callbacks, promises are easily chainable. From the readme of Q, an early implementer of the pattern:

The callback approach is called an “inversion of control”. A function that accepts a callback instead of a return value is saying, “Don’t call me, I’ll call you.”. Promises un-invert the inversion, cleanly separating the input arguments from control flow arguments. This simplifies the use and creation of APIs, p...

How to human-join an array in JavaScript

To simulate Rails' to_sentence in your JavaScript application, you can use these few lines of CoffeeScript code:

joinSentence = (array) ->
  if array.length > 1
    array[0..-2].join(', ') + ', and ' + array[-1..]
  else
    array.join ', '

Examples:

> joinSentence(['cats', 'dogs', 'pandas'])
# => 'cats, dogs, and pandas'

^
> joinSentence(['llamas'])
# => 'llamas'

Here is some plain JavaScript, should you prefer that:

function joinSentence(array) {
  if (array.length > 1) {
    return ar...

Middleman does not fingerprint asset paths by default

We're using Middleman for some static sites like our blog.

Despite being very similar to Rails, Middleman does not add a fingerprint hash to its asset paths by default. This means that when you write this:

<%= javascript_include_tag 'all.js' %>

... you always get the same path, regardless of the contents of all.js:

<script src='/javascripts/all.js'>

Because browsers tend to cache assets for a while, this means that users might not get your changes until their cac...

Represent astral Unicode characters in Javascript, HTML or Ruby

Here is a symbol of an eight note: ♪

Its two-byte hex representation is 0x266A.

This card describes how to create a string with this symbol in various languages.

All languages

Since our tool chain (editors, languages, databases, browsers) is UTF-8 aware (or at least doesn't mangle bytes), you can usually get away with just pasting the symbol verbatim:

note = '♪'

This is great for shapes that are easily recognized by your fellow programmers.
It's not...

Using Google Analytics with Unpoly

The default Google Analytics might not work as expected with your Unpoly app. This is because your app only has a single page load when the user begins her session. After that only fragments are updated and the <script> tag that sends the page view to Google Analytics is probably never evaluated again.

Luckily you can fix this.

Simple mode: You just want to track all the page views

Embed your Google Analytics code as always.

Now add the following code snippet:...

object-fit polyfill by lazysizes

All new browsers support the new object-fit CSS property. It allows to specify how an element behaves within its parent element and is intended for images and videos. The most useful values are contain (fit-in) and cover (crop).

Unfortunately, IE does not support this yet. However, if you're already using lazysizes, you can use its object-fit polyfill!

Usage

In your Javascript manifest, require them like this:

#= require plugins/object-fit/ls.obj...

Tasks, microtasks, queues and schedules - JakeArchibald.com

The way that Javascript schedules timeouts and promise callbacks is more complicated than you think. This can be the reason why callbacks are not executed in the order that they are queued.

Please read this article!


This is an extract of the example in the article which demonstrates the execution order of tasks and microtasks.

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
})...

Making jQuery throw an error when it returns an empty collection

When you are working with jQuery selectors and collections, many times you want to know if the collection actually contains the elements you wanted to select. Here's a tiny jQuery plugin that does just that.

$.fn.ensure = ->
  if (@length == 0) then throw 'Empty jQuery collection'
  this

Use it like this:

$('input[id$=_cache]').ensure()
> Console: "Uncaught Empty jQuery collection"

Dynamically uploading files to Rails with jQuery File Upload

Say we want …

  • to create a Gallery that has a name and has_many :images, which in turn have a caption
  • to offer the user a single form to create a gallery with any number of images
  • immediate uploads with a progress bar per image
  • a snappy UI

Enter jQuery File Upload. It's a mature library that can do the job frontend-wise. On the server, we'll use Carrierwave, because it's capable of caching images.

(FYI, [here's how to do the u...

Javascript: Wait until an image has finished loading

The attached ImageLoader helper will start fetching an image and return an image that is resolved once the image is loaded:

ImageLoader.load('/my/image.png').then(function(image) {
  ...
});

The image argument that is yielded to the promise callback is an HTMLImageElement. This is the kind of object you get when you call new Image().

jQuery: Find a selector in both descendants and the element itself

jQuery's find looks in the element's descendants. It will never return the current element itself, even if the element matches the given selector.

Require the attached file and you can now say:

$('.container').findWithSelf('.selector')

This is sort of like closest, but it looks in descendants instead of ancestors.

Preloading images with CSS

Sometimes you want to preload images that you will be using later. E.g. if hovering over a an area changes its background image, the new image should be preloaded. If you only load it once the user starts hovering, there will be a delay until the background image flips.

The attached article explains how to preload images with only CSS. No Javascript required.

The gist is:

.element:after {
  content: url(img01.jpg) url(img02.jpg) url(img03.jpg);
  display: none;
}

Delay your Jasmine tests until the document is ready

To delay your entire Jasmine test suite until the DOM is ready, add the following:

beforeAll(function(done) {
  $(done);
});

jQuery Core 3.0 Upgrade Guide | jQuery

Since jQuery 3 saw it's first release candidate today, the links has a list of (breaking) changes.

How to: Client-side language detection

When you have a localized website, you may want to redirect users to their preferred language when they visit the root path.
Here is how to do it without a server-side component (like a Rails application).

  • Use JavaScript's navigator.language (real browsers and IE11+) and navigator.userLanguage (old IEs).
  • Use a <meta> refresh as fallback
  • Provide buttons for paranoid users that disabled JavaScript and meta refreshs.

JavaScript

The following JavaScript will try to auto-detect a user's preferred language.

It understands string...