Array#extremes Useful JavaScript Game Extension #30

###*
 * Returns an object containing the extremes of this array.
 *
 * @param {Function} [fn] An optional funtion used to evaluate
 * each element to calculate its value for determining extremes.
 * @returns {min: minElement, max: maxElement}
 * @type Object
###
Array::extremes = (fn) ->
  fn ||= (n) -> n

  min = max = undefined
  minResult = maxResult = undefined

  this.each (object) ->
    result = fn(object)

    if min?
      if result  maxResult
        max = object
        maxResult = result
    else
      max = object
      maxResult = result

  min: min
  max: max

Usage:

[-1, 3, 0].extremes() # => {min: -1, max: 3}

test "#extremes", ->
  array = [-7, 1, 11, 94]

  extremes = array.extremes()

  equals extremes.min, -7, "Min is -7"
  equals extremes.max, 94, "Max is 94"

  extremes = array.extremes (value) ->
    value.mod 11

  equals extremes.min, 11
  equals extremes.max, 94

Array#wrap Useful JavaScript Game Extension #29

/**
 * Pretend the array is a circle and grab a new array containing length elements.
 * If length is not given return the element at start, again assuming the array
 * is a circle.
 *
 * @param {Number} start The index to start wrapping at, or the index of the
 * sole element to return if no length is given.
 * @param {Number} [length] Optional length determines how long result
 * array should be.
 * @returns The element at start mod array.length, or an array of length elements,
 * starting from start and wrapping.
 * @type Object or Array
 */
Array.prototype.wrap = function(start, length) {
  if(length != null) {
    var end = start + length;
    var result = [];

    for(var i = start; i < end; i++) {
      result.push(this[i.mod(this.length)]);
    }

    return result;
  } else {
    return this[start.mod(this.length)];
  }
};

Simple use:

[1, 2, 3].wrap(-1) => 3

[1, 2, 3].wrap(6) => 1

Or get fancy and tile your kitchen:

["w", "o", "o", "o"].wrap(0, 16) => ["w", "o", "o", "o", "w", "o", "o", "o", "w", "o", "o", "o", "w", "o", "o", "o"]

Array#clear Useful JavaScript Game Extension #28

Here’s a useful little method that empties out an array. Sure you could type myArray.length = 0; but that is kind of weird. For some reason myArray.clear() just makes more sense to me.

/**
* Empties the array of it's contents. It is modified in place.
*
* @type Array
* @returns this, now emptied.
*/
Array.prototype.clear = function() {
  this.length = 0;
  return this;
};

The series continues…

Capistrano Rails: A group writable tmp directory

Maybe you’ve run into [out :: …] rm: cannot remove `/…/releases/20110221192810/tmp/restart.txt’: Permission denied

I did. The reason was because we were deploying with multiple users and when the tmp directory was being created it was not set to group writable.

Fortunately it is an easy fix, just add this into your cap deploy script:

after :deploy do
  run "chmod -R g+w #{release_path}/tmp"
end

Javascript String#constantize and String#parse – Useful Extensions 26 and 27

Here are two extensions to the String class that I’ve found quite useful. constantize and parse. They are in CoffeeScript because JavaScript fills me with a growing disgust.

String::constantize = () ->
  if this.match /[A-Z][A-z]*/
    eval("var that = #{this}")
    that
  else
    undefined

String::parse = () ->
  try
    return JSON.parse(this)
  catch e
    return this

constantize is based on the ActiveSupport method. It transforms a string that represents the name of a class into a reference to that class. It uses eval to accomplish this, but until JavaScript gets more extensive reflection capabilities it’s probably the best we can do.

Parse is a useful way to convert the string into a raw JavaScript type if possible, otherwise it returns just the string itself. For example if I have a string "false", "false".parse() will return the JavaScript value false. Likewise '{"a": 7}'.parse() will return a JavaScript object with a property a that has the value 7.

Pretty sweet.

jQuery Drag Image From Desktop Plugin

Here’s the CoffeeScript for a jQuery plugin I wrote that makes accepting images dragged in from the desktop super easy. The event.fix part at the beginning is because jQuery currently doesn’t pass on the dataTransfer attribute of events. Once that’s taken care of we create the plugin.

(($) ->
  $.event.fix = ((originalFix) ->
    (event) ->
      event = originalFix.apply(this, arguments)

      if event.type.indexOf('drag') == 0 || event.type.indexOf('drop') == 0
        event.dataTransfer = event.originalEvent.dataTransfer

      event

  )($.event.fix)

  $.fn.dropImageReader = (callback) ->
    stopFn = (event) ->
      event.stopPropagation()
      event.preventDefault()

    this.each () ->
      element = this
      $this = $(this)

      $this.bind 'dragenter dragover dragleave', stopFn

      $this.bind 'drop', (event) ->
        stopFn(event)

        Array.prototype.forEach.call event.dataTransfer.files, (file) ->
          imageType = /image.*/
          if !file.type.match(imageType)
            return

          reader = new FileReader()

          reader.onload = (evt) ->
            callback.call(element, file, evt)

          reader.readAsDataURL(file)

)(jQuery)

The plugin takes a callback that will be called when any of the matched elements receive an image file via the drop event. Here’s an example usage:

$(".tiles").dropImageReader (file, event) ->
  img = $ "",
    alt: file.name
    src: event.target.result
    title: file.name

  $(this).append img

For image drops all you really care about is the file name and the data url, but if for some reason you need different file results here’s the line to modify: reader.readAsDataURL(file). You can also extend or alter what is passed to the callback if this is too mundane for your needs.

And here’s the JS version for anyone eager to copy/paste:

  (function() {
    $.event.fix = (function(originalFix) {
      return function(event) {
        event = originalFix.apply(this, arguments);

        if (event.type.indexOf('drag') === 0 || event.type.indexOf('drop') === 0) {
          event.dataTransfer = event.originalEvent.dataTransfer;
        }

        return event;
      };
    })($.event.fix);

    $.fn.dropImageReader = function(callback) {
      var stopFn;

      stopFn = function(event) {
        event.stopPropagation();
        event.preventDefault();
      };

      return this.each(function() {
        var $this, element;

        element = this;
        $this = $(this);

        $this.bind('dragenter dragover dragleave', stopFn);

        $this.bind('drop', function(event) {
          stopFn(event);

          Array.prototype.forEach.call(event.dataTransfer.files, function(file) {
            var imageType, reader;

            imageType = /image.*/;
            if (!file.type.match(imageType)) {
              return;
            }

            reader = new FileReader();

            reader.onload = function(evt) {
              return callback.call(element, file, evt);
            };

            reader.readAsDataURL(file);
          });
        });
      });
    };
  })(jQuery);

Happy image dropping!

Number#snap Useful JavaScript Game Extension #25

Well my friends, it’s been quite some time. So without further ado back to the series!

/**
 * Returns the the nearest grid resolution less than or equal to the number.
 *
 *   EX:
 *    (7).snap(8) => 0
 *    (4).snap(8) => 0
 *    (12).snap(8) => 8
 *
 * @param {Number} resolution The grid resolution to snap to.
 * @returns The nearest multiple of resolution lower than the number.
 * @type Number
 */
Number.prototype.snap = function(resolution) {
  return (this / resolution).floor() * resolution;
};

This handy method is useful for when you need to snap input to a grid (like in a tile editor).

I hope you enjoyed this installment of the wonderful 256 part series on JavaScript game extensions.