Prototype Stuart Halloway
[email protected]
Prototype’s Role
What is Prototype? provides core support for Ajax hides browser oddities powers other libraries (Scriptaculous, Rico) keeps things simple works with any server stack integrates with Rails
Prototype’s Class: OO for JavaScript
Ajax.Request = Class.create(); Ajax.Request.Events = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; Ajax.Request.prototype = Object.extend(new Ajax.Base(), { initialize: function(url, options) { this.transport = Ajax.getTransport(); this.setOptions(options); this.request(url); }, // ...snip ... }
create a class
Ajax.Request = Class.create(); Ajax.Request.Events = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; Ajax.Request.prototype = Object.extend(new Ajax.Base(), { initialize: function(url, options) { this.transport = Ajax.getTransport(); this.setOptions(options); this.request(url); }, // ...snip ... }
class-level properties
Ajax.Request = Class.create(); Ajax.Request.Events = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; Ajax.Request.prototype = Object.extend(new Ajax.Base(), { initialize: function(url, options) { this.transport = Ajax.getTransport(); this.setOptions(options); this.request(url); }, // ...snip ... }
extend another class
Ajax.Request = Class.create(); Ajax.Request.Events = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; Ajax.Request.prototype = Object.extend(new Ajax.Base(), { initialize: function(url, options) { this.transport = Ajax.getTransport(); this.setOptions(options); this.request(url); }, // ...snip ... }
constructor
Ajax.Request = Class.create(); Ajax.Request.Events = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; Ajax.Request.prototype = Object.extend(new Ajax.Base(), { initialize: function(url, options) { this.transport = Ajax.getTransport(); this.setOptions(options); this.request(url); }, // ...snip ... }
instance-level properties
Ajax.Request = Class.create(); Ajax.Request.Events = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; Ajax.Request.prototype = Object.extend(new Ajax.Base(), { initialize: function(url, options) { this.transport = Ajax.getTransport(); this.setOptions(options); this.request(url); }, // ...snip ... }
beware the comma
var Class = { create: function() { return function() { this.initialize.apply(this, arguments); } } }
Class.create
Object.extend = function(destination, source) { for (var property in source) { destination[property] = source[property]; } return destination; }
Object.extend
OO in Prototype • Classes are language library features • Inheritance is a language library feature • Classes and inheritance are flexible ideas
Augmenting Built-in Classes
Object.extend(String.prototype, { stripTags: function() { return this.replace(/<\/?[^>]+>/gi, ''); }, stripScripts: function() { return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); }, // ... snip ... }
augmenting String
$A(element.childNodes).each(function(node){ element.removeChild(node) });
enumeration: each
toQueryString: function() { return this.map(function(pair) { return pair.map(encodeURIComponent).join('='); }).join('&'); },
enumeration: map
compact: function() { return this.select(function(value) { return value != undefined || value != null; }); },
enumeration: select
Augmenting Classes
• functions have sensible homes • collection traversal is trivial
xhr Support
new Ajax.Request(this.validateAction, { method: 'get', parameters: e.name + "=" + e.value, onSuccess: (function(req) { this.reportValidation(e, req.responseText); }).bind(this), onFailure: function(req) {alert(req.responseText)}, onException: function(t,e) {alert(e);} });
URL to invoke
new Ajax.Request(this.validateAction, { method: 'get', parameters: e.name + "=" + e.value, onSuccess: (function(req) { this.reportValidation(e, req.responseText); }).bind(this), onFailure: function(req) {alert(req.responseText)}, onException: function(t,e) {alert(e);} });
HTTP verb
new Ajax.Request(this.validateAction, { method: 'get', parameters: e.name + "=" + e.value, onSuccess: (function(req) { this.reportValidation(e, req.responseText); }).bind(this), onFailure: function(req) {alert(req.responseText)}, onException: function(t,e) {alert(e);} });
query string
new Ajax.Request(this.validateAction, { method: 'get', parameters: e.name + "=" + e.value, onSuccess: (function(req) { this.reportValidation(e, req.responseText); }).bind(this), onFailure: function(req) {alert(req.responseText)}, onException: function(t,e) {alert(e);} });
success callback
new Ajax.Request(this.validateAction, { method: 'get', parameters: e.name + "=" + e.value, onSuccess: (function(req) { this.reportValidation(e, req.responseText); }).bind(this), onFailure: function(req) {alert(req.responseText)}, onException: function(t,e) {alert(e);} });
bind if you need this
new Ajax.Request(this.validateAction, { method: 'get', parameters: e.name + "=" + e.value, onSuccess: (function(req) { this.reportValidation(e, req.responseText); }).bind(this), onFailure: function(req) {alert(req.responseText)}, onException: function(t,e) {alert(e);} });
failure callback
new Ajax.Request(this.validateAction, { method: 'get', parameters: e.name + "=" + e.value, onSuccess: (function(req) { this.reportValidation(e, req.responseText); }).bind(this), onFailure: function(req) {alert(req.responseText)}, onException: function(t,e) {alert(e);} });
exception callback
Ajax.Request • hides browser oddities • takes a slew of options • performs callbacks asynchronously
xhr Example: Polling and Updating
new PeriodicalExecuter(function() { new Ajax.Updater('items', 'http://localhost:3000/retrieve_chats', {evalScripts:true}); }, 10);
call periodically
new PeriodicalExecuter(function() { new Ajax.Updater('items', 'http://localhost:3000/retrieve_chats', {evalScripts:true}); }, 10);
update a page element
new PeriodicalExecuter(function() { new Ajax.Updater('items', 'http://localhost:3000/retrieve_chats', {evalScripts:true}); }, 10);
URL to invoke
new PeriodicalExecuter(function() { new Ajax.Updater('items', 'http://localhost:3000/retrieve_chats', {evalScripts:true}); }, 10);
evaluate response scripts
new PeriodicalExecuter(function() { new Ajax.Updater('items', 'http://localhost:3000/retrieve_chats', {evalScripts:true}); }, 10);
interval (seconds)
xhr • a method for every season • Ajax.Request, Ajax.Updater • methods share common options
DOM Support
clear: function() { for (var i = 0; i < arguments.length; i++) $(arguments[i]).value = ''; },
$
function $() { var results = [], element; for (var i = 0; i < arguments.length; i++) { element = arguments[i]; if (typeof element == 'string') element = document.getElementById(element); results.push(Element.extend(element)); } return results.length < 2 ? results[0] : results; }
$ implementation
$$("h1").each(function(e) { Element.addClassName(e, "blueborder"); }); $$("li .showme").each(function(e) { Effect.Pulsate(e); });
$$
insertions
log: function(type, message) { new Insertion.Bottom(this.element, '
' + message.escapeHTML() + ' |
'); Element.scrollTo(this.form); },
insertion example
//from pragforms sample app new Form.Element.Observer( 'search', 0.5, function(element, value) { Element.show('spinner'); new Ajax.Updater( 'ajaxWrapper', 'http://localhost:3000/user/search', { asynchronous:true, evalScripts:true, onComplete:function(request) { Element.hide('spinner') }, parameters:'search=' + encodeURIComponent(value) } ) } )
observe an element
//from pragforms sample app new Form.Element.Observer( 'search',
0.5, function(element, value) { Element.show('spinner'); new Ajax.Updater( 'ajaxWrapper', 'http://localhost:3000/user/search', { asynchronous:true, evalScripts:true, onComplete:function(request) { Element.hide('spinner') }, parameters:'search=' + encodeURIComponent(value) } ) } )
interval (seconds)
//from pragforms sample app new Form.Element.Observer( 'search', 0.5,
function(element, value) { Element.show('spinner'); new Ajax.Updater( 'ajaxWrapper', 'http://localhost:3000/user/search', { asynchronous:true, evalScripts:true, onComplete:function(request) { Element.hide('spinner') }, parameters:'search=' + encodeURIComponent(value) } ) } )
handler
<%= observe_field :search, :frequency => 0.5, :update => 'ajaxWrapper', :complete=>"Element.hide('spinner')", :before=>"Element.show('spinner')", :with=>"'search=' + encodeURIComponent(value)", :url=>{:action=>'search'} %>
let Rails write the code
DOM Support • find elements by id with $ • find by class with $$ • update elements with insertions • grab user input with observers
Resources and Samples Prototype: prototype.conio.net
About Relevance, LLC: www.relevancellc.com/about Pragforms App: www.relevancellc.com/code
Rails Samples: www.pragmaticprogrammer.com/titles/fr_rails4java/