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:
Microsoft Internet Explorer for Windows, version 6.0 and higher
Mozilla Firefox 1.0/Mozilla 1.7 and higher
Apple Safari 1.2 and higher
The Prototype Library is broken up into 10 seperate subcomponents, we will look
at each one individually.
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.
Class: The class object is used to create classes. So, when
defining a new class, we'd use the following form.
var PeriodicalExecuter = Class.create();
Abstract: Used to create an abstract class. Really this is
just an empty Object.
Try: A helper object for iterating through a list of functions
that may or may not throw exceptions, and returning the result from the first
function that runs successfully. Useful for things like
var transport = Try.these(
function() {return new ActiveXObject('Msxml2.XMLHTTP')},
function() {return new ActiveXObject('Microsoft.XMLHTTP')},
function() {return new XMLHttpRequest()}
);
Object.extend(destination, source): Useful for creating
inherited classes. All this does is copy all of the properties, fields, and
methods from the source class (the one we're inheriting from) to the
destination class. A simple example use of this function is as follows.
This setOptions function creates a default set of options, this.options, and
then uses the extend function to add any other options that were passed to the
function. This is a really simple example, but should show how this function
can make code much more readable.
Function.prototype.bind(object): Used to bind a function
to an object.
Function.prototype.bindAsEventListener(object): Used to
bind a function to an object as an event listener.
Number.prototype.toColorPart(): Converts a number into its
two digit hexadecimal equivalent (ie. 255 => FF, 10 => 0A, etc.).
PeriodicalExecuter: Used to call a specific function at
regular intervals.
$(): This is a helper function. It ensures that we are always
dealing with references to elements, and not their names. It can be passed a
single object, or an array of objects, and it will return the proper element
references. The parameters can be either strings, specifying the id's of the
elements, in which case the result will be that of document.getElementById(),
or the actual elements themselves, in which case the result will just be the
same element that was passed in.
The Compat subcomponent contains a few methods to help with cross-browser
compatibility. At the moment it contains only the two functions.
Array.prototype.push
: Allows the push method on arrays, which simply append an element, or an array
of elements, to the end of the array.
Function.prototype.apply(object, parameters): Allows a
function to be called on an object. Binds the function to the object, so that
'this' references in the function will resolve correctly.
The String subcomponent adds the following methods to the String object
stripTags(): Strips all HTML tags. Does not handle encoded
tags, and just calls the following regular expression:
this.replace(/<\/?[^>]+>/gi, '');
escapeHTML() and unescapeHTML(): These two
functions should be obvious. It performs these functions in a rather
clever, interesting way, and the code for these functions is as follows:
escapeHTML: function() {
var div = document.createElement('div');
var text = document.createTextNode(this);
div.appendChild(text);
return div.innerHTML;
}
unescapeHTML: function() {
var div = document.createElement('div');
div.innerHTML = this.stripTags();
return div.childNodes[0].nodeValue;
}
parseQuery(): Simply returns an associative array of the
parameters and values in string that it is called on. The source string must be
of the form name1=value1&name2=value2.
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
});
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.
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:
Ajax.Base: A basic Ajax from which the other Ajax objects are
extended from. The following methods are supported:
setOptions(options): Used to set the values for the options,
method
which is either 'get' or 'post', asynchronous
which is either true or false, and parameters
which is a string of name=value pairs to be sent with the request. The options
are just an associative array.
responseIsSuccess() and responseIsFailure():
For determining whether the previous request was successful.
Ajax.Request: Inherits from Ajax.Base. Adds more functionality
required for making basic requests. Includes the following methods:
initialize(url, options): Peforms a request on the given
url, using the options as specified.
request(url): Performs a request on the given url. Assumes
that the options have been previously set.
setRequestHeaders(): Sets the request headers to match those
found in the options.requestHeaders
array, which is of the form ['name1', 'value1', 'name2', 'value2', ...].
onStateChange(): Used internally to handle the stateChange
events, calls the respondToReadyState function if the
readyState is 1.
evalJSON(): If there is a response header named X-JSON,
then this method eval's the value of that header and returns the result.
respondToReadyState(readyState): calls evalJSON,
then calls any events that have been set in the options. The way to specify a
handler in the options would be to have an entry like {'onSuccess' : myFunction
} in the options, which is just an associative array.
Ajax.Updater: Inherits from AjaxRequest. Includes
additional functionality so that two containers can be set, a success container
and a failure container. The containers are HTML elements, and then when the
response is received from the server, the element's innerHTML is set to be the
reponse, or if the options.insertion
function is set to be a function of the Insertion type defined in the DOM
subcomponent, then the response is inserted into the container using that type
of Insertion. Optionally, if the options.evalScripts property is set to true,
then any <script> tags that are included in the response will be
evaluated.
Ajax.PeriodicalUpdater: Inherits from AjaxBase. Allows
for an Ajax.Updater object to be called at regular intervals, as specified by
the options.frequency property. Also has start()
The DOM subcomponent provides a number of methods and objects for making it
easier to work with the DOM. They are as follows:
document.getElementsByClassName(className): Just like
getElementsByTagName, but this method returns all of the elements in the
document which have the specified name defined in their className property
(which is the value of the class attribute for the element).
Element: The element class extends the Object object to add a
few more very useful helper methods, which are as follows:
toggle(): This function toggles the value of the style.display
attribute for an element, or an array of elements (or element id's, as the
$()
function is applied to the arguments.
hide(): This function sets the value of the style.display
attribute for an element, or an array of elements, to 'none'.
show(): This function sets the value of the style.display
attribute for an element, or an array of elements, to ''.
remove(element): Removes the element, by calling removeChild
on the elements' parentNode.
getHeight(element): Returns the offsetHeight for the
element.
hasClassName(element, className): Returns whether the
element has that name in its className property.
addClassName(element, className): Adds a class name to the
className property of the element.
removeClassName(element, className): Removes a class name
from the className property of the element.
cleanWhitespace(element): Removes all whitespace-only text
node children.
Insertion: Allows for insertion of Content into an Element at
a certain point. Insertion points are 'beforeBegin', 'afterBegin', 'beforeEnd',
and 'afterEnd'. It defines the Insertion.Before, Insertion.Top, Insertion.Bottom, and Insertion.After objects
to handle each of these Insertion points respectively.
The Form subcomponent provides a useful wrapper for HTML Forms, and their
fields. It defines the following objects:
Field: A field represents any form element. The following
methods are defined:
clear(): Sets the value
of the field to be ''. Can also take an array of fields, in which case it will
clear each field in the array.
focus(element): Sets the focus to the element.
present(): Pass this an array of form elements, and it'll
determine whether all of their values are anything other than an empty
string.
select(element): Calls select on the form element.
activate(element): Calls focus, and then select
on the form element.
Form: A JavaScript object defined to provide additional
functionality for working with HTML Forms. The following methods are defined:
serialize(form): Serializes the form to a string, in the
form name1=value1&name2=value2&...
getElements(form): Returns all of the Form.Element objects
for a form.
getInputs(form, typeName, name): Returns all of the input
elements from a form, optionally specifying a typeName and name to filter by.
disable(form): Calls blur on each element in
the form and sets their disabled
property to true.
enable(form): Sets the disabled
property on every element in the form to be ''.
focusFirstElement(form): Sets the focus to the first
element in the form.
reset(form): Calls reset on the form.
Form.Element: Represents an element in an HTML Form. Seems to
be used for serializing form elements. The methods supported are serialize(element),
and getValue(element). Also, the function $F()
is defined as a shortcut to the getValue.
Form.Element.Serializers: Four serializers are defined. input
is used for basic input types, which are submit, hidden, password, text, checkbox,
and radio. inputSelector is used for
checkboxes and radio inputs. textarea is used for text areas.
select
is used for select boxes.
Abstract.TimedObserver: An abstract class, used for
attaching a function to an element, where the function will be called at
regular intervals.
Form.Element.Observer and Form.Observer:
Inherit from the Abstract.TimedObserver
class. Used to bind a function to a form element or a form, such that the
function will be called at regular intervals.
Abstract.EventObserver: An abstract class used to attach a
callback function to an element such that the callback function will be called
on an event. The events that will trigger the callback are the onclick
event for checkboxes or radio inputs, and the onchange
event for password boxes, text boxes, textareas, and select boxes.
Form.Element.EventObserver and Form.EventObserver:
Inherits from the Abstract.EventObserver class. Used to bind
callback functions to form elements or forms.
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.
element(event): Returns the source of the event, using either
event.target, or event.srcElement.
isLeftClick(event): Returns whether the event was
triggered by a left click.
pointerX(event) and pointerY(event):
Return the X or Y coordinates of the mouse pointer.
stop(event): Stops the event from bubbling.
findElement(event, tagName): Finds the first node with the
given tagName, starting from the node the event was triggered on and moving up
the DOM.
_observeAndCache(element, name, observer, useCapture):
"Private" method, used to add an event listener to an element. Keeps track of
the observers so that they can later be deleted in order to clean up for memory
leaks in IE.
unloadCache: Clears all of the observers.
observe(element, name, observer, useCapture): Used to
attach an event handler (or observer) to an element.
stopObserving(element, name, observer, useCapture):
Disconnects an event handler (or observer) from an element.
To prevent memory leaks in IE, this subcomponent does the following:
The Position subcomponent defines the Position class which provides the
following methods to assist with the positioning of elements.
prepare(): This method must be called before calling withinIncludingScrolloffset
if the page has been scrolled since the last time it was called.
realOffset(element): Returns an array [left, top] where
left and top are the sum of the scrollLeft and scrollTop values between the
element and its parentNode, and the parentNode and its parentNode, all the way
up the DOM tree.
cumulativeOffset(element): Returns an array [left, top]
where left and top are the sum of the offsetLeft and offsetTop values between
the element and its offsetParent, and the offsetParent and its offsetParent,
all the way up the DOM tree.
within(element, x, y): Determines whether the point (x, y)
lies within the element.
withinIncludingScrolloffsets(element, x, y): Same as
above, but seems to be required when draggable elements are contained within a
scrollable element. Causes major Firefox performance problems.
overlap(mode, element): Mode is either 'horizontal' or
'vertical'. Must call within directly before this function.
Determines the amount that the point passed to within overlaps
the element, either horizontally or vertically, as set by
the mode.
clone(source, target): Sets the top, left, width, and height
for the source element to be identical to that of the target element. Uses
absolute positioning to place the source target.