Blog

Something I have been asked today:

Keypress, stacking of events, control of values and setTimeout

Choan, my dear, guapetón of my soul… I have two fields on a form and I want that one to reflect the changes in the other… and controlling keypress I don’t have the updated value

Not really, because you don’t have it. Solution: control the execution stack. The trick: use a timeout for the method that updates the values to run after finishing with the management of events. Something like this:

// in the handler of the keypress event
setTimeout(updateValue, 0); // <-- the key is in the zero

Now, to pick up the updated value based on keypress it is not enough, because there are a thousand ways to fill in the field without pressing a key (copipegar menus, bar code readers, I-that-sés…)

So my advice is to defend yourself by controlling also the event blur. I put it in terms of jQuery, assuming that the field that control takes the id source and the destination is known as a destination.

(function($) {

  var init_copy_values = function() {
    $('#source').bind('keypress blur', function(e) {
      if (e.type == 'keypress') setTimeout(updateValue, 0);
      else updateValue();
    });
  },
  updateValue = function() {
    $('#destination').val($('#source').val());
  };

  $(init_copy_values);

}(jQuery));

Well, nothing, already, a post without insulting anyone.

Vilaweb and the developer stupid bean

I counted out there today has released the new version of Vilaweb. The culmination of the best practices in HTML markup (yes, that is sarcasm).

Attention to the following fragment, taken from the website on November 24, 2009:

<div class="zona-imatge">
  <img alt="Detingut el batlle de Polop per la mort del seu predecessor<br/>" src="/media/portada/81/1259046000.jpg" width="299" height="254"/>
  <div class="params" style="display: none">
    <div class="w">299</div>
    <div class="h">254</div>
    <div class="file">/media/portada/81/1259046000.jpg</div>
  </div>
</div>

My comment (brief version): asshole.

My comment (extended version): are you so stupid that every time you are checking, instead of improve create a detriment. What the hell are those “parameters” putting noise into the HTML? How did you happen, perchance, to apply a sort of “non-intrusiveness” born by your great brain?

I guess that you will be thinking: are you okay, Choan, despotricas and don’t say anything. This is, in effect. If you know what is wrong (fatal, punishable with death) in that piece of code you don’t need to count anything. And if you do not know, follow in your holy ignorance, seeks a multinational company in the to succeed as a programmer mediocre, and make your mother feel proud of you. Fool.

(There are other pearls, but the more desire he has given me of myself faltón has been this. And yes, the design also seems to me like a shit.)

NOT-SO-RECENT

Number of days in a month

See a cow on top of a shed is nothing compared to the things that one is found in the scripts that run through these worlds of god.

One of the fields in which any mindundi is able to apply the most of your ignorance of javascript is the calculation of days in a month. Use a dozen conditional is loose.

The ultimate: when you need to take accounts, about anything, using the tools of the language. Do not accounts of the old.

The code. Function that receives a one-month (in numeric notation-human) and a year (optional, if not received we take the current year and returns the number of days in the month. No conditional was damaged during the experiment.

function daysInMonth(humanMonth, year) {
  return new Date(year || new Date().getFullYear(), humanMonth, 0).getDate();
}
daysInMonth(2, 2009); // 28
daysInMonth(2, 2008); // 29

Keys:

ECMAScript manages internally dates as numeric data (see Unix Time)
in ECMAScript, the months are numbered from 0 to 11 and days from 1 to N. We feed the function with a month human. So the date is created (d) correspond to day 0 of the month following the month that we seek. The algorithm of creation of dates is tolerant (basically the sum of what we give and get an integer, so the day 0 of a month is always the last day of the previous month).

In the end, that all of this is that the machine is cleverer than you are and your knuckles. And all the bloggers together, including the menda.

ADDENDUM

We can use a stratagem similar to to know if the year is a leap year:

function isLeapYear(year) {
  return new Date(year, 0, 366).getFullYear() == year;
}

Working with jQuery: timeline an inflatable, espaguetismo to the plugin

In the chapter today, we will see how to convert a spaghetti-code in a nice plugin reusable.

Target widget: allow to scroll horizontally by dragging (skewer, drag, loose) a block of content. If you are in a hurry to see what just the thing, go straight to the demo.

Elements: a container (with overflow: auto) and their content. For our example:

<div id="#timeline">
  <ul>
    <li>...</li>
    <li>...</li>
  </ul>
</div>

With a few styles such that:

#timeline {
 width: 600px;
 height: 400px;
 overflow: auto;
}

#timeline ul {
 width: 1000px;
 margin: 0;
 padding: 0;
}

#timeline li {
 display: block;
 width: 200px;
 padding: 10px;
 margin: 0;
 float: left;
}

On these stones we will build our church. Let’s step through the code necessary to make the thing move. We will put in place the machinery when clicking on the container:

$('#timeline')
  .bind('mousedown', function(e) {
    // aquí vendrá nuestro código
  });

In order to avoid the use of global variables, we use data, a method in jQuery that allows us to maintain relationships between values and DOM elements. We will maintain the following data: current position of the scroll element to which it affects and the position of the pointer at the time of the mousedown.

$(document)
  .data('timeline', {
    element: this,
    scroll: this.scrollLeft,
    x : e.clientX
  });

Notice that we are preserving the data in the document. In every moment, there will be a maximum of one element in displacement.

Well, it has begun the action… but still we are not following the movements of the pointer. Assign a couple of event handlers for the events mousemove and mouseup from document.

Why to document and not for the container with which we are dealing? Even if the pointer leaves the container during the drag, we want to continue moving the contents. And if you go out and not controláramos mouseup at the document level, our script will never abandon him the way we are “dragging stuff”.

As we are cool and modern, we will take advantage of the events.with-space-of-names that jQuery kindly put at our disposal. For mousemove, we retrieve the data we stored in mousedown and modify the property scrollLeft of the container plus the difference between the current mouse position and the stored. In mouseup, we clean the data and remove event handlers from the space timeline assigned to document.

jQuery(document)
  .bind('mousemove.timeline', function(e) {
    var data = jQuery(this).data('timeline');
    data.element.scrollLeft = data.scroll + data.x - e.clientX;
  })
  .bind('mouseup.timeline', function(e) {
    jQuery(this)
      .removeData('timeline')
      .unbind('.timeline');
  });

To finish off, we will cancel the default action of mousedown for not selecting text as you drag:

e.preventDefault();

Putting it all together and within a call to ready, we have the thingy working:

jQuery(document).ready(function() {

  jQuery('#timeline')
    .bind('mousedown', function(e) {

      jQuery(document)
        .data('timeline', {
          element: this,
          scroll: this.scrollLeft,
          x : e.clientX
        })
        .bind('mousemove.timeline', function(e) {
          var data = jQuery(this).data('timeline');
          data.element.scrollLeft = data.scroll + data.x - e.clientX;
        })
        .bind('mouseup.timeline', function(e) {
          jQuery(this)
            .removeData('timeline')
            .unbind('.timeline');
        });

      e.preventDefault();

    });
});

Great! With this we already have the desired behavior, but… does not seem very reusable. Pluguinifiquemos.

BUILDING A PLUGIN

When we invoke the function jQuery (or its alias habitual, $), we retrieve a object. That object has a prototype. To add methods to the object returned byjQuery, add them to your prototype. So something such that:

jQuery.prototype.miMetodo = function() {

};

We would go shooting. But there is a language more jotacueriesco to say the same thing. Of step add a return this our method to allow chaining of calls:

jQuery.extend(jQuery.fn, {
  miMetodo : function() {
    return this;
  }
});

With this we can already give us the pleasure of writing:

jQuery('#miCosa')
  .miMetodo()
  .etcetera();

But to write jQuery (that is what we must do if we want to make sure that our plugin is compatible with other libraries), time and time again is to write a lot. We’re going to put all the code of our plugin into an anonymous function that will allow us to have an alias-safe jQuery.

(function($) {

})(jQuery);

And we’re going to fill it bit by bit.

(function($) {
  $.extend($.fn, {
    timeline : function() {
      this
        .bind('mousedown', handleMouseDown);
      return this;
    }
  });

  function handleMouseDown(e) {
    // bla bla
  }

})(jQuery);

Note: we extract the code handling the event mousedown. Why? We don’t need any data, local and therefore we do not need the functional closure for access to that data (or generate a function on the fly for each widget). We will do the same for the handles mousemove and mouseup:

(function($) {
  $.extend($.fn, {
    timeline : function() {
      this
        .bind('mousedown', handleMouseDown);
      return this;
    }
  });

  function handleMouseDown(e) {
    $(document)
      .data('timeline', {
        element: this,
        scroll: this.scrollLeft,
        x : e.clientX
      })
      .bind('mousemove.timeline', handleMouseMove)
      .bind('mouseup.timeline', handleMouseUp);
    e.preventDefault();
  }

  function handleMouseMove(e) {
    var data = $(this).data('timeline');
    data.element.scrollLeft = data.scroll + data.x - e.clientX;
  }

  function handleMouseUp(e) {
    $(this)
      .removeData('timeline')
      .unbind('.timeline');
  }

})(jQuery);

With this our spaghetti-code as it seems a little more ordenadito. But a plugin without options little. We will consider the possibility of setting the ratio of displacement/drag and decide whether to hide or not the scroll bar native. By parts:

$.extend($.fn, {
  timeline : function(opts) {
    var config = $.extend({
      speed: 1.5,
      accesible: true
    }, opts || {});

    this
      .bind('mousedown', config, handleMouseDown);

    if (!config.accesible) {
      this.css('overflow', 'hidden');
    }

    return this;
  }
});

The method now timeline accepts an argument that corresponds to a configuration object. We use jQuery.extend to apply the values on a default configuration. And when you assign the handler to mousedown pass it as the argument. If this is new to you, what you need to know is that with an assignment of this type, the handler will receive this extra data in the property data the object that represents the event. Let’s modify the handler to transmit the configuration:

function handleMouseDown(e) {
  $(document)
    .data('timeline', {
      element: this,
      scroll: this.scrollLeft,
      x : e.clientX,
      config : e.data
    })
    .bind('mousemove.timeline', handleMouseMove)
    .bind('mouseup.timeline', handleMouseUp);
  e.preventDefault();
}

Use the settings in handleMouseMove:

function handleMouseMove(e) {
  var data = $(this).data('timeline');
  data.element.scrollLeft = data.scroll + (data.x - e.clientX) * data.config.speed;
}

And ready to go. We already have a plugin pretty decent. To get you started:

jQuery(document)
  .ready(function() {
    $('el.selector-de-dios')
      .timeline({ speed: 2, accesible: false })
  });

BALL EXTRA 1: DEFAULT SETTINGS

If our user is always going to prefer the option that is not accessible from the plugin, not for being an idiot (or obey) we are going to force to pass the settings time and time again.

$.extend($.fn, {
  timeline : function(opts) {
    var config = $.extend({}, $.fn.timeline.defaults, opts || {});
    // ...
    return this;
  }
});

$.fn.timeline.defaults = {
  speed: 1.5,
  accesible: true
};

In this way we allow the user to configure options globally by manipulating jQuery.fn.timeline.defaults at your whim.

BALL EXTRA 2: MOUSEWHEEL

The plugin mousewheel allows you to control the movements of the wheel of the mouse.

$.extend($.fn, {
    timeline : function(opts) {
      // ...
      if ($.fn.mousewheel) {
        this
          .mousewheel(function(e, delta) {
            this.scrollLeft -= delta * config.mousewheelSpeed;
            e.preventDefault();
          });
      }
      return this;
    }
  });

You can try the combination of all these elements in the demo.

CODE

Choan does not invent anything new, what is covered here comes to be a combination of my skills, tastes and experience and what is explained in these two articles:

Fun with overflows, Remy Sharp.
A Plugin Development Pattern by Mike Alsup.

Keep in mind that the proposed architecture for this plugin is not always the most appropriate —we’ll talk about it— and that the sample plugin is not published or official, or extra officially, and no one will give you support.

jShoulda and JsUnitTest, unit testing in JavaScript-style Shoulda

We were talking the other day of QUnit, today we will talk about JsUnitTest –other system of testing– and of jShoulda, a layer of abstraction that a server has molded with his own hands, and in the image and likeness of Shoulda.

Principiemos talking about JsUnitTest, a port with no dependencies of the test system developed to test the prototype and script.aculo.us.

WHAT DO WE NEED?

jsunittest.js, the library of testing;
an HTML document that references.

Something like this will suffice:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <title>Minimal JsUnitTest</title>
  <script src="/js/jsunittest.js" type="text/javascript"></script>
  <link rel="stylesheet" href="/css/unittest.css" type="text/css" media="screen"/>
</head>

<body>
  <h1>Minimal JsUnitTest</h1>

  <div id="testlog">
    Los resultados de las pruebas se muestran, por defecto
    en el elemento con id=testlog
  </div>

<script type="text/javascript">
  // aquí irá el código de nuestras pruebas,
  // también podemos hacer referencia a un script externo
</script>

</body>
</html>

HOW TO WRITE TESTS WITH JSUNITTEST

To define and run the tests, we create an instance of Test.Unit.Runner:

new Test.Unit.Runner({});

In the configuration object that we pass as an argument, we define as properties the tests to be performed:

new Test.Unit.Runner({
  testNombreDelTest : function() {
    this.assert(true);
  },
  testOtroTest : function() {
    this.assert(true);
  }
});

The names of the properties corresponding to tests must start with “test”. If we want to run functions of preparation and cleaning before and after each of the tests, we use the properties setup and teardown for defining them:

new Test.Unit.Runner({
  setup : function() {
    this.foo = 1;
  },
  testNombreDelTest : function() {
    this.assert(true);
  },
  testFoo : function() {
    this.assertEqual(1, this.foo);
  }
});

Notes: both the functions of preparation as the test is run in the context of an instance of Test.Unit.Testcase. The possible assertions are methods of that instance (that is why we use this).

  • assert
  • assertEqual
  • assertNotEqual
  • assertEnumEqual
  • assertEnumNotEqual
  • assertHashEqual
  • assertHashNotEqual
  • assertIdentical
  • assertNotIdentical
  • assertNull
  • assertNotNull
  • assertUndefined
  • assertNotUndefined

Check what its name seems to indicate. An important detail: in JavaScript, two arrays with the same content (or two objects-used-as-hashes) can’t be compared by equality. Therefore, if you want to compare two arrays you need to use assertEnumEqual. And to compare two objects, assertHashEqual.

AUTOMATION

drnic, the human behind JsUnitTest, is also the father of newjs, a gem (if you don’t know what is a gem, ignore this section until you find out) designed to facilitate the development of JavaScript libraries. Its virtues:

  • includes JsUnitTest;
  • simplifies the creation of new tests;
  • automates the execution of the tests.

Without going into details. Or what you see, or not see:

$ newjs milib
      create
      create  config
      create  lib
      create  src
      create  script
      create  tasks
      create  test/assets
      create  test/unit
      create  test/functional
      create  test/assets/unittest.css
      create  test/assets/jsunittest.js
      # etcetera
  dependency  install_rubigen_scripts
      exists    script
      create    script/generate
      create    script/destroy
$ cd milib
$ script/generate unit_test basic
      exists  test/unit
      create  test/unit/basic_test.html
$ rake test_units
(in /Users/choan/Current/tirame/milib)

Started tests in Safari
.
Finished in 2 seconds.
1 tests, 1 assertions, 0 failures, 0 errors

Started tests in Firefox
.
Finished in 1 seconds.
1 tests, 1 assertions, 0 failures, 0 errors

Skipping Internet Explorer, not supported on this OS

Skipping Konqueror, not supported on this OS

Started tests in Opera
.
Finished in 3 seconds.
1 tests, 1 assertions, 0 failures, 0 errors

Yes, my friend, by running a rake task we run the tests on each of the browsers available on our system and we collected the results. Something that Chuck Norris is not capable of doing.

JSHOULDA

But let’s go with jShoulda, I’ve come here to talk about my book.

jShoulda is an abstraction layer for JsUnitTest that allows you to write our tests in the most pure style Shoulda. Something like this:

context('A context', {},
  should('run a test', function() {
    this.assert('Yay!');
  }),
)();

And what advantage does this have? For a test vulgar, no, frankly. But if your tests require initializations complex, it gets cool.

context('A context', {
  setup: function() {
    this.foo = 1;
  }
  },
  should('run its setup function', function() {
    this.assertEqual(1, this.foo);
  }),
  context('which is a "nested" context', {
    setup: function() {
      this.foo +=1;
    }
    },
    should('run both setup functions', function() {
      this.assertEqual(2, this.foo);
    })
  )
)();

To use jShoulda you need to… jshoulda.js. And a HTML document as the previous one, to which you will add the reference to the library.

But to prove it you do not need to download anything. Head over to the project page. The results of tests that you will find in it (assuming that you visit with JavaScript enabled) correspond to the test of example executed in vivo. If you want to tinker, doblecliquea in the code, edit it to your liking and press the button. Suggestion: make fail the tests. Mola.