Overview of the Prototype Javascript Library

The following is a brief overview of what functionality is provided by the Prototype Javascript Library. It contains a number of useful functions, fixes for browser bugs, and I think it is also a good model for how a javascript library should be written. The homepage for the Prototype Library is http://prototype.conio.net/, and the latest version of the Prototype Library can be found at http://dev.conio.net/repos/prototype/dist/prototype.js. Unfortunately the documentation on the site for this library is completely non-existant, and therefore this document is necessary in avoiding having to read through the source code to figure out what is available. The licensing of the Prototype library allows free, unrestricted use of the code as far as I can tell, but I'm not a lawyer so...

The claim is that the Protoype Library supports the following browsers:

The Prototype Library is broken up into 10 seperate subcomponents, we will look at each one individually.

Base

The base subcomponent contains a few useful objects and methods for using JavaScript in a more Object-Oriented manner, as well as a few helper functions.

Compat

The Compat subcomponent contains a few methods to help with cross-browser compatibility. At the moment it contains only the two functions.

String

The String subcomponent adds the following methods to the String object

Enumerable

The Enumerable subcomponent contains the definition for the Enumerable object, which is an extremely useful object for working with collections. It is also an excellent example of how the Object.extend method should be used, and also contains a really interesting use of the exception handling ability of JavaScript, with the _break and _continue objects. Therefore, I'm just including the entire source code of this subcomponent.

var _break    = new Object();
var _continue = new Object();

var Enumerable = {
  each: function(iterator) {
    var index = 0;
    try {
      this._each(function(value) {
        try {
          iterator(value, index++);
        } catch (e) {
          if (e != _continue) throw e;
        }
      });
    } catch (e) {
      if (e != _break) throw e;
    }
  },
  
  all: function(iterator) {
    var result = true;
    this.each(function(value, index) {
      if (!(result &= (iterator || Prototype.K)(value, index))) 
        throw _break;
    });
    return result;
  },
  
  any: function(iterator) {
    var result = true;
    this.each(function(value, index) {
      if (result &= (iterator || Prototype.K)(value, index)) 
        throw _break;
    });
    return result;
  },
  
  collect: function(iterator) {
    var results = [];
    this.each(function(value, index) {
      results.push(iterator(value, index));
    });
    return results;
  },
  
  detect: function (iterator) {
    var result;
    this.each(function(value, index) {
      if (iterator(value, index)) {
        result = value;
        throw _break;
      }
    });
    return result;
  },
  
  findAll: function(iterator) {
    var results = [];
    this.each(function(value, index) {
      if (iterator(value, index))
        results.push(value);
    });
    return results;
  },
  
  grep: function(pattern, iterator) {
    var results = [];
    this.each(function(value, index) {
      var stringValue = value.toString();
      if (stringValue.match(pattern))
        results.push((iterator || Prototype.K)(value, index));
    })
    return results;
  },
  
  include: function(object) {
    var found = false;
    this.each(function(value) {
      if (value == object) {
        found = true;
        throw _break;
      }
    });
    return found;
  },
  
  inject: function(memo, iterator) {
    this.each(function(value, index) {
      memo = iterator(memo, value, index);
    });
    return memo;
  },
  
  invoke: function(method) {
    var args = $A(arguments).slice(1);
    return this.collect(function(value) {
      return value[method].apply(value, args);
    });
  },
  
  max: function(iterator) {
    var result;
    this.each(function(value, index) {
      value = (iterator || Prototype.K)(value, index);
      if (value >= (result || value))
        result = value;
    });
    return result;
  },
  
  min: function(iterator) {
    var result;
    this.each(function(value, index) {
      value = (iterator || Prototype.K)(value, index);
      if (value <= (result || value))
        result = value;
    });
    return result;
  },
  
  partition: function(iterator) {
    var trues = [], falses = [];
    this.each(function(value, index) {
      ((iterator || Prototype.K)(value, index) ? 
        trues : falses).push(value);
    });
    return [trues, falses];
  },
  
  pluck: function(property) {
    var results = [];
    this.each(function(value, index) {
      results.push(value[property]);
    });
    return results;
  },
  
  reject: function(iterator) {
    var results = [];
    this.each(function(value, index) {
      if (!iterator(value, index))
        results.push(value);
    });
    return results;
  },
  
  sortBy: function(iterator) {
    return this.collect(function(value, index) {
      return {value: value, criteria: iterator(value, index)};
    }).sort(function(left, right) {
      var a = left.criteria, b = right.criteria;
      return a < b ? -1 : a > b ? 1 : 0;
    }).pluck('value');
  },
  
  toArray: function() {
    return this.collect(Prototype.K);
  },
  
  zip: function() {
    var iterator = Prototype.K, args = $A(arguments);
    if (typeof args.last() == 'function')
      iterator = args.pop();

    var collections = [this].concat(args).map($A);
    return this.map(function(value, index) {
      iterator(value = collections.pluck(index));
      return value;
    });
  }
}

Object.extend(Enumerable, {
  map:     Enumerable.collect,
  find:    Enumerable.detect,
  select:  Enumerable.findAll,
  member:  Enumerable.include,
  entries: Enumerable.toArray
});

Array

The Array subcomponent contains the $A() function which converts a regular Javascript Array into an Enumerable object (as defined in the Enumerable subcomponent). It also adds the functions _each(iterator), first(), and last() to the standard Array object.

Ajax

The Ajax subcomponent contains a simple object, called Ajax, which essentially provides a cross-browser solution for using the XMLHttpRequest object. There are a number of Ajax objects:

DOM

The DOM subcomponent provides a number of methods and objects for making it easier to work with the DOM. They are as follows:

Form

The Form subcomponent provides a useful wrapper for HTML Forms, and their fields. It defines the following objects:

Event

The Event subcomponent extends the standard Event object with much more functionality. Constants for various keystrokes are defined, such as KEY_BACKSPACE, KEY_TAB, KEY_RETURN, KEY_ESC, KEY_LEFT, KEY_UP, KEY_RIGHT, KEY_DOWN, and KEY_DELETE. The following methods are added to the Event object.

To prevent memory leaks in IE, this subcomponent does the following:

Event.observe(window, 'unload', Event.unloadCache, false);

Position

The Position subcomponent defines the Position class which provides the following methods to assist with the positioning of elements.