AngularJS directive to format a text with paragraphs and new lines

If you are using Angular and want something like Rails' simple_format which HTML-formats a plain-text input into paragraphs and line breaks, this directive is for you.

Any HTML fragments inside that text will still be escaped properly.

Use it like this, where your text attribute specifies something available in your current scope:

<simple-format text="email.message"></simple-format>

This is the directive, in CoffeeScript syntax:

@app.directive 'simpleFor...

Bootstrap 4 is coming

What's new

  • Moved from Less to Sass. Bootstrap now compiles faster than ever thanks to Libsass, and we join an increasingly large community of Sass developers.
  • Improved grid system. We’ve added a new grid tier to better target mobile devices and completely overhauled our semantic mixins.
    Opt-in flexbox support is here. The future is now—switch a boolean variable and recompile your CSS to take advantage of a flexbox-based grid system and components.
  • Dropped wells, thumbnails, and panels for cards. Cards are a brand new co...

Testing focus/blur events with Cucumber

This is a problem when using Selenium with Firefox. We recommend using ChromeDriver for your Selenium tests.


This setup allows to test focus/blur events in Cucumber tests (using Selenium). For background information, see How to solve Selenium focus issues.

Cucumber step definition:

# This step is needed because in Selenium tests, blur events are not triggered
# when the browser has no focus.
When /^I unfocus the "(.*?)" field to trigger ja...

Force absolute URLs in views throughout a response

This is more tricky than it should be because url_for, asset_path, etc. all rely on different mechanisms.

Anyway, you can use the attached trait like this:

class ExampleController < ApplicationController
  does 'host_enforcement', :for => 'some_action'
end

Short explanation:

  • asset_host is used for links to stylesheets and javascripts
  • asset_host belongs to ActionController::Base -- changes are persistent and will not be reset after a request
  • rewrite_options is used by the ..._path methods in the views
    ^...

Spreewald: Content-Disposition not set when testing a download's filename

Precondition

  • You are not using javascript tests
  • The file is served from a public folder (not via controller)

Problem description

If you deliver files from a public folder it might be that the Content-Disposition header is not set. That's why the following spreewald step might raise an error:

Then I should get a download with filename "..."
expected: /filename="some.pdf"$/
     got: nil (using =~) (RSpec::Expectations::ExpectationNotMetError)

Solution

One solution...

Clean up application servers when deploying

Our development process makes us deploy very often. As the number of releases grows, junk clogs up the hard drive of our application servers:

  • Old release code
  • Old tmp folders with compiled view templates etc.
  • Precompiled assets (Javascripts, images...) that no longer exist. When using the asset pipeline, Capistrano will symlink the public/assets directory to shared/assets. This is cool since we can still serve previous assets after a new release, in the window where browser caches might still have references to old assets. But i...

Drag'n'drop in trees: I went to town

For my Gem Session project Holly I ran the Ironman of drag'n'drop implementations:

  • Dragging in nested lists
  • User-definable order of items
  • Complicated item elements with super-custom CSS and other Javascript functionality
  • Items that can be both leaves and containers of other items
  • has_ancestry on the server side

Things I learned:

  • Be ready to write a lot of CSS. You need to indicate what is being dragged, where it will be dropped, if it is dropped above, below o...

Pre-releasing a Ruby gem

When a Ruby version gem has a letter in its version number, it is considered a pre-release:

  • 1.0.0.rc1
  • 2.3.0.alpha2
  • 3.0.0.beta3
  • 4.0.0.pre.rc2

Even if a pre-release gem has the highest version number, it is never installed unless the user explictily requested the version:

gem install foobar --version="=2.3.0.alpha2"

Also bundle update will never update a stable version to a pre-release version unless the user explicitly requests it in the Gemfile:

gem 'foobar', '=2.3.0.alpha2'

A note on Semanti...

CSS: How to force background images to scale to the container, ignoring aspect ratio

You can scale background images in CSS to the container size using background-size (Demo).

Commonly, we use contain or cover because we want to preserve the image's aspect ratio.
If you do not want to do that, simply provide scaling values for X and Y:

background-size: 100% 100%

(a simple 100% would mean 100% auto and respect the image's aspect ratio)

SVGs with a viewBox will force their aspect ratio

The above may not work for you when ...

Rails 3: Make "link_to :remote => true" replace HTML elements with jQuery

In Rails 2, you could use link_to_remote ... :update => 'id' to automatically replace the content of $('#id').

To do the same in Rails 3, include usual rails-ujs JavaScript, and put this into your application.js:

$(function() {
  $('[data-remote][data-replace]')
    .data('type', 'html')
    .live('ajax:success', function(event, data) {
      var $this = $(this);
      $($this.data('replace')).html(data);
      $this.trigger('ajax:replaced');...

Clicking issues with chromedriver

ChromeDriver clicking works by simulating a mouse click in the middle of the element's first client rect (or bounding client rect if it doesn't have a first client rect). (Clicking issues)

So if you are trying to click on an element, chromedriver tries to click at the position where it first finds that element.

This can lead to some problems and unexpected behavior like:

  • Element is not clickable at point ... erros
  • Another element which is now located at...

How to build a fully custom TinyMCE 5 dialog

TinyMCE is a WYSIWYG editor which is quite customizable.


  1. Add a custom button to the tinyMCE toolbar and tell tinyMCE to open a dialog with the route to your dialog's view.
tinymce.init({
  // ...
  toolbar: 'myCustomButton',
  setup: function(editor) {
      editor.ui.registry.addButton('myCustom Button', {
        ...

Make jQuery.animate run smoother with almost no code changes

jQuery comes with .animate() that lets you transition some CSS selectors:

function floatIn($element) {
  $element.css({ 'opacity': 0, 'margin-top': 200px });
  $element.animate({ 'opacity': 1, 'margin-top': 0 }, { duration: 500 });
}

The animation is implemented using setInterval and Javascript. This works great, but it's not as smooth as a CSS transition.

Fortunately the animate API can be mapped almo...

Cucumber: Wait until CKEditor is loaded

I had to deal with JavaScript Undefined Error while accessing a specific CKEditor instance to fill in text.

Ensure everything is loaded with

patiently do
  page.execute_script("return isCkeditorLoaded('#{selector}');").should be_true
end

Example

The fill in text snippet for Cucumber:

When /^I fill in the "([^\"]+)" WYSIWYG editor with:$/ do |selector, html|
  patiently do
    page.execute_script("return isCkeditorLoaded('#{selector}');").should be_true
  end
  html.gsub!(/\n+/, "") # otherwise: unterminated string lit...

Bookmarklet to generate a commit message with Pivotal Tracker story ID and title

For clarity and traceability, your commit messages should include the ID and title of the Pivotal Tracker story you're working on. For example:

[#12345] Add Google Maps to user profiles
Optional further commit messages in the body

Also see Howto: Write a proper git commit message

To quickly generate such commit messages, add a new link "Commit" to your bookmarks and use the following Javascript as the link URL:

javascript:(function() { ...

How to open a new tab with Selenium

Until recently, you could open a new tab via window.open when using execute_script in Selenium tests. It no longer works in Chrome (will show a "popup blocked" notification).

This is because browsers usually block window.open unless the user interacted with an element for security reasons. I am not sure why it did work via Selenium before.

Here is an approach that will insert a link into the page, and have Selenium click it:

path = "/your/path/here"
id = "helper_#{SecureRandom.hex(8)}"
execute_script <<-JAVASCRIPT
  ...

Looping through iterators in Coffeescript

Some modern Javascript APIs return iterators instead of arrays.

In plain Javascript you can loop through an iterator using
for...of:

var iterator = ...;

for (var value of iterator) {
   console.log(value); 
}

While there is a for...of construct in Coffeescript, it iterates through property/value pairs and **...

Check that an element is hidden via CSS with Spreewald

If you have content inside a page that is hidden by CSS, the following will work with Selenium, but not when using the Rack::Test driver. The Selenium driver correctly only considers text that is actually visible to a user.

Then I should not see "foobear"

This is because the Rack::Test driver does not know if an element is visible, and only looks at the DOM.

Spreewald offers steps to check that an element is hidden by CSS:

Then "foo" should be hidden

You can also check that an el...

A few recent CSS properties

  • Feature Queries (Edge 12+): Similar to @media queries, @supports blocks can be scoped to browsers that support a given declaration. There is CSS.supports() to do the equivalent in Javascript.

  • backdrop-filter (Edge 17+, but not FF): Applying filters to what is visible through an element.

  • [touch-action](https://d...

Bookmarklet to generate a Pivotal Tracker story from Zammad Ticket

This is a bookmarklet you can add to Chrome or Firefox which will allow you to create a story in Pivotal Tracker from a Zammad ticket. This might come in handy when creating stories for SWAT Teams.

But first you will have to set two variables in the script below:

  • pt_project_id: the ID of the Pivotal Tracker Project you want to add stories to. This can be found as part of the URL of the project (https://www.pivotaltracker.com/n/projects/<pt_project_id>)
  • pt_token: the Pivotal Tracker token used for authentication. Can be found in y...

IE7 not properly redrawing elements with position: relative

If you manipulate the DOM with JavaScript and your page contains nested elements with position: relative, chances are Internet Explorer 7 will not properly move to the right position.

I have not found a fool-proof workaround, but these are options that have worked for me:

  • Try to lose some position: relative attributes, so they do not nest. This is often impossible, though.
  • Force the correct redrawing of the elements using JavaScript. Adding a bogus class ($(element).addClass('touch')) seems to suffice.
  • Try to give the off...

IIFEs in Coffeescript

In JavaScript we often use Immediately Invoked Function Expessions (or IIFEs) to prevent local variables from bleeding into an outside scope:

(function() {
  var foo = "value"; // foo is scoped to this IIFE
})();

In Coffeescript an IIFE looks like this:

(->
  foo = "value" # foo is scoped to this IIFE
)()

There is also a shorthand syntax with do:

do ->
  foo = "value" # foo is scoped to this IIFE

You can also use do with arguments t...

Webpack: Automatically generating an icon font from .svg files

Over the years we have tried several solution to have vector icons in our applications. There are many ways to achieve this, from SVGs inlined into the HTML, SVGs inlined in CSS, JavaScript-based solutions, to icon fonts.

Out of all these options, the tried and true icon font seems to have the most advantages, since

  • icon fonts are supported everywhere
  • they perform well and require no JavaScript at all
  • their icons align nicely with text
  • their icons automatically inherit color and size of the surrounding text

The big issue used to b...

Skip Selenium scenarios in a Cucumber run

Cucumber scenarios that are tagged with @javascript so they run with Selenium are very slow. You might not want to run them every time.

In order to skip all Selenium scenarios, call Cucumber like this:

cucumber --tags ~@javascript

Note that you should still run all the scenarios, including the Selenium ones, before you push a change.