The Package is designed permit safe use of the Prototype javascript framework along side arbitrary 3rd party scripts (i.e, advertisements). The Prototype package adds many utility functions to the native javascript Array, String, and Number classes. Due to some peculiarities of the javascript language, these additions will show up when ever a script tries to iterate (using the "for x in l" syntax) over the values in any instance of an Array (or over the characters in a String). This may have dire consequences for an unsuspecting script.
One solution would be to simply discard the Prototype package, but, in addition to the loss of a powerful set of tools, this would also eliminate other packages like Script.acoul.us and Moo.fx. Disposing of the third party code is also undesirable, because it's often what's paying the bills.
Prototypify solves this dilemma by removing prototype functions by default. It then allows the programmer to proxy the functions that are expecting Prototype functionality so that the functions are restored prior to execution and removed again when execution completes.
To use Prototypify, load prototypify.js immediately after loading Prototype:
<script type="text/javascript" language="javascript" src="prototype-1.4.0.js"></script>
<script type="text/javascript" language="javascript" src="prototypify.js"></script>
This will remove the prototype functions so that 3rd party or legacy code will run correctly. Note that,
after loading the Prototypify package, a good deal of Prototype functionality will still work (everything
that does not rely on array methods). You'll need to examine the Prototype code to determine which functions
will work and which won't.
If you would like to use the prototype functionality (or call some code that does) you must proxy
your function. For a single function you use the Prototype.proxy function. The following
produces and calls a proxied function:
function unproxiedListToCommaSeparatedString( list ) {
var out = ''
list.each( function(x,i) { out += ( i == 0 ? x : ',' + x ) } );
return out;
}
listToCommaSeparatedString = Prototypify.proxy( unproxiedListToCommaSeparatedString );
alert( listToCommaSeparatedString(['apples','bananas','oranges']);
You may simplify this to:
listToCommaSeparatedString = Prototypify.proxy( function( list ) {
var out = ''
list.each( function(x,i) { out += ( i == 0 ? x : ',' + x ) } );
return out;
} );
alert( listToCommaSeparatedString(['apples','bananas','oranges']);
Prototypify's instrument function proxies an entire class. The nice
thing about this function is that it not only proxies each method within the class, but does
so for every instance of the class created thereafter. To use it,
simply call Prototypify.instrument on the class constructor function. The
following code does essentially the same thing as the last example in an object-oriented
fashion.
FruitLister = Class.create();
FruitLister.prototype = {
init:function( fruit ) {
this.fruit = fruit;
},
listFruit = function() {
var out = ''
this.fruit.each( function(x,i) { out += ( i == 0 ? x : ',' + x ) } );
return out;
} );
}
Prototypify.instrument( FruitLister );
var lister = new FruitLister( ['apples','bananas','oranges'] );
alert( lister.listFruit() );
Note that the instrument method does not proxy super classes, so if FruitLister
extends AbstractLister you will need to call Prototypify.instrument on both
classes to get all of FruitLister's methods.
Prototypify's instrumentStatic function works similarly to the instrument
function but works on functions added to the constructor function itself (static functions), rather
than functions added to the class's prototype (instance methods). You use it as follows:
FruitLister = Class.create();
FruitLister.listFruit = function( fruit ) {
var out = ''
fruit.each( function(x,i) { out += ( i == 0 ? x : ',' + x ) } );
return out;
}
Prototypify.instrumentStatic( FruitLister );
alert( FruitLister.listFruit(['apples','bananas','oranges']) );
The proxy, instrument, and instrumentStatic functions all support
an optional 'proxyArguments' parameter. If this parameter is set to true, all
callback functions passed as parameters to the proxied function or methods will be automatically proxied
as well. For example:
Prototypify.instrumentStatic(Event, true); // note the second parameter
var fruit = ['apples','bananas','oranges'];
Event.observe( 'listButton', 'click', function() {
var out = ''
fruit.each( function(x,i) { out += ( i == 0 ? x : ',' + x ) } );
alert( out );
} );
will list the fruit when 'listButton' is pressed. This is really just a convenience, since the same
effect could be achieved by putting a Prototypify.proxy call around the callback function.
This test calls the function displayPrototypeValues that iterates through an array,
a string, and a number to demostrate that additional properties added by the Prototype package
have been removed. There should be no values displayed in the text area.
This test uses the Prototypify.proxy function to create a proxy of the displayPrototypeValues function
called proxiedDisplayPrototypeValues. There should be many function values under each of the three
object types.
This test calls proxiedDisplayTransportationAndFruit which in turn calls proxiedDisplayTransportation.
Both of these methods use Prototype's Array.each method to iterate over an array.
This confirms that the cleanup (removal of prototype functions) only happens when the outermost nested function
exits. After calling these functions, the test calls displayPrototypeValues to confirm the cleanup has
happened. You should see a Transportation and Fruit message, a Transportation message, some vehicle types,
A Transportation and Fruit message, some fruit types, and then the output of test 1.
This test creates an instance of the Fruit class and calls methods on the instance that use
the Array.each method. The Fruit class has had all its methods proxied using the
Prototypify.instrument function. This is testing that after instrumenting a class, all
instances of that class will have their methods proxied (and thus able to use all prototype functionality).
You should see the output of two methods.
This test first sets an event handler on the test button using Prototype's Event.observe function.
Clicking on the button then executes displayPrototypeValues in the textarea. The Event
class has been prototypified using the Prototypify.instrument function with the optional proxyArguments
parameter set to true. The callback function passed to Event.observe has not been prototyped, so the
presence of prototype functions in the text area indicates that the proxyArguments has caused the callback to be
prototypified.
Author: Alex Cruikshank
07.27.2006
Copyright (c) 2006 CNET Networks, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.