SlideShare a Scribd company logo
1 of 116
Making Ajax Sexy
A Look at Sequential Ajax Patterns
View source
http://github.com/furf/Sexy
Go to website…
http://sexyjs.com
NSFW?
?SFW
But really, “assholes?”
WARNING
This presentation contains a very bawdy joke.
WARNING
If very bawdy jokes are the sort of thing that
offend you or make you uncomfortable in the
    trousers, you might want to leave now.
David Furfero
@furf   UI Engineer, MLB.com
SERIOUSLY
  Go, now.
YOU  DATA
Ajax makes killer web apps possible.
Sequential Ajax makes Ajax killer.
Sequential Ajax makes Ajax killer.
Sexy.js makes Sequential Ajax…Sexy!
Sexy.js makes Sequential Ajax…Sexy!
Sequential Ajax (Sajax)
An Ajax pattern for handling multiple
asynchronous requests while maintaining
proper callback execution order.
Goals of Sequential Ajax (Sajax)

• Facilitate combination and manipulation of data from
  multiple sources

• Speed loading of data, scripts, stylesheets using parallel
  asynchronous HTTP requests

• Speed execution of callbacks by allowing them to fire as
  soon as all previous data is available

• Fun
Three Flavors of Sajax

• Serial request / serial response
• Parallel request / random response
• Parallel request / serial response
In a perfect world…
In a world, where time waits for
no man…
or his data…
Sequential Ajax Anti-Patterns
Serial Request / Serial Response
// Request an article
$.getJSON('/article/1.json', function (article) {

  // Request the related comments
  $.getJSON('/article/1/comments.json', function (comments) {

   // Join the data
   article.comments = comments;

    // Render the article and comments
    render(article);
  });
});
Serial Request /
Serial Response

• Pros

  • Relatively readable

• Cons

  • Slower

  • Nested calls don’t
    scale
Parallel Request / Random Response
var i = 0, n = 2, article, comments;

$.getJSON('/article/1.json', function (data) {
  article = data;
  if (++i == n) {
    handleComplete();
  }
});

$.getJSON('/article/1/comments.json', function (data) {
  comments = data;
  if (++i == n) {
    handleComplete();
  }
});

function handleComplete () {
  article.comments = comments;
  render(article);
}
Parallel Request /
Random Response

• Pros

  • Faster

• Cons

  • Heavier

  • Uglier

  • Really doesn’t scale
Do those patterns look familiar?
Meet Sexy.js…
Sexy is a jQuery plugin
A lightweight JavaScript library which provides parallel
request/serial response Sequential Ajax (Sajax) functionality
and a sleek facade to jQuery’s native Ajax methods.
Sexy is a standalone library
A lightweight JavaScript library which provides parallel
request/serial response Sequential Ajax (Sajax) functionality
and a sleek facade to jQuery’s native Ajax methods
JavaScript.
Sexy is a standalone library
A lightweight JavaScript library which provides parallel
request/serial response Sequential Ajax (Sajax) functionality
and a sleek facade to jQuery’s native Ajax methods
JavaScript (using jQuery’s native Ajax methods).
Sexy is classy
new Sexy() creates a Sexy instance
Sexy is classy
var s = new Sexy();

s.json(articleUrl, callback)
 .json(commentsUrl, callback);
Sexy is the new Sexy
Sexy() also creates a Sexy instance
Sexy is the new Sexy
Sexy()
  .json(articleUrl, callback)
  .json(commentsUrl, callback);
Naked Sexy
/**
 * Constructs a new Sexy instance
 */
function Sexy (cfg) {

    /**
      * Allow instantiation without new keyword
      */
    if (!(this instanceof Sexy)) {
       return new Sexy(cfg);
    }

    this.cfgs = [];
    this.setup(cfg);
}
Sexy is full of surprises
Sexy.fn() also creates a Sexy instance!
Sexy is full of surprises
Sexy
  .json(articleUrl, callback)
  .json(commentsUrl, callback);
Naked Sexy
/**
  * Add sexier static methods
  */
function addStaticMethod (method) {
   Sexy[method] = function () {
      return Sexy.prototype[method].apply(new Sexy(), arguments);
   };
}

for (i in Sexy.prototype) {
  addStaticMethod(i);
}
I WASN’T KIDDING!
There is a bawdy joke coming up VERY soon!
I WASN’T KIDDING!
   If you are pregnant, have a serious heart
condition, or giggle uncontrollably at the word
 “titillate”, you may want to hurry to Track A.
Expressive, Chainable API
Sexy is Your Syntactic Sugar Mama

An expressive method for each supported data type:
html          (url, callback)
json          (url, callback)
jsonp         (url, callback)
script / js   (url, callback)
text          (url, callback)
xml           (url, callback)
style / css   (url, callback)
Sexy is Patient

Requests can be deferred using an optional 2nd argument.
html          (url, defer, callback)
json          (url, defer, callback)
jsonp         (url, defer, callback)
script / js   (url, defer, callback)
text          (url, defer, callback)
xml           (url, defer, callback)
style / css   (url, defer, callback)
Sexy is Flexible

Each method can also take a jQuery settings object.
html          (settings)
json          (settings)
jsonp         (settings)
script / js   (settings)
text          (settings)
xml           (settings)
style / css   (settings)
callback (data, previous, next, status)

Each callback receives four arguments:
data          response data of the current request
previous      return value of the previous callback
next          Ajax settings of the next request
status        result status of the current request
Sexy Sequential Ajax Patterns
Sexy
Sexy
  .json({
     url: '/article/1.json',
     success: function (article) {
       return article;
     }
  })
  .json({
     url: '/article/1/comments.json',
     success: function (comments, article) {
       article.comments = comments;
       render(article);
     }
  });
Passing Data with Callbacks
Sexy
  .json({
     url: '/article/1.json',
     success: function (article, previous, next, status) {
       return article;
     }
  })
  .json({
     url: '/article/1/comments.json',
     success: function (comments, article, next, status) {
       article.comments = comments;
       render(article);
     }
  });
Sexy-er
Sexy
  .json('/article/1.json', function (article) {
     return article;
  })
  .json('/article/1/comments.json', function (comments, article) {
     article.comments = comments;
     render(article);
  });
Passing Data with Callbacks
Sexy
  .json('/article/1.json', function (article) {
     return article;
  })
  .json('/article/1/comments.json', function (comments, article) {
     article.comments = comments;
     render(article);
  });
Sexy-est
Sexy
  .json('/article/1.json')
  .json('/article/1/comments.json', function (comments, article) {
     article.comments = comments;
     render(article);
  });
Passing Data with Implicit Callbacks
Sexy
  .json('/article/1.json')
  .json('/article/1/comments.json', function (comments, article) {
     article.comments = comments;
     render(article);
  });
Passing Data with Implicit Callbacks
Sexy
  .json('/article/1.json')
  .js('jquery.js')
  .json('/article/1/comments.json', function (comments, article) {
     article.comments = comments;
     render(article);
  });
Slow it Down
Sexy
  // Authenticate user
  .json({
     type: 'POST',
     url: 'login',
     data: { username: 'furf', password: '2s3xy4u' }
  })
  // GET article
  .json('article/1', true, function (article, previous, nextCfg) {
     article.author = 'furf';
     nextCfg.data = article;
  })
  // POST article
  .json({
     defer: true,
     type: 'POST',
     url: 'article/1'
  })
  // De-authenticate user
  .json('logout', true);
Manage <script> Dependencies
Sexy
  .js('jquery.js')
  .js('jquery-ui.js')
  .js('jquery-ui-i18n.js')
  .js('jquery.template.js')
  .js('mlb.js')
  .js('mlb.tracking.js')
  .js('mlb.datagrid.js')
  .js('mlb.stats.js')
  .js('mlb.stats.util.js')
  .js('mlb.stats.widget.js')
  .js('mlb.stats.sortableplayers.js', function () {
     mlb.stats.SortablePlayers.init();
  });
A Sexy Cinderella Story
Old and Busted
var i = 0, n = 2, article, comments;

$.getJSON('/article/1.json', function (data) {
  article = data;
  if (++i == n) {
    handleComplete();
  }
});

$.getJSON('/article/1/comments.json', function (data) {
  comments = data;
  if (++i == n) {
    handleComplete();
  }
});

function handleComplete () {
  article.comments = comments;
  render(article);
}
Your Royal Hotness
Sexy
  .json('/article/1.json')
  .json('/article/1/comments.json', function (comments, article) {
     article.comments = comments;
     render(article);
  });
The 4th Stage of Grief
          ACCEPTANCE
  There’s no way out now. Strap yourself in.
Accept your fate. The bawdy joke is on its way.
Additional Methods
setup (settings)

• Configure subsequent requests of a Sexy instance

• Settings can be unset with setup()
setup (settings)
Sexy
  .setup({
     beforeSend: showLoadingIndicator
     complete:   hideLoadingIndicator
  })
  .json('article.json', diplayArticle)

 .setup()
 .json('comments.json', displayComments);
Sexy (settings)
Sexy({
     beforeSend: showLoadingIndicator
     complete:   hideLoadingIndicator
  })
  .json('article.json', diplayArticle)

 .setup()
 .json('comments.json', displayComments);
bundle (url1, url2, ..., callback)

• Client-side JavaScript packaging

• Multiple file, single <script> injection

• Sequence is preserved
Why not Sexy.package?
JavaScript was too reserved.
*groan*
:)
I told you it was bawdy!
bundle (url1, url2, ..., callback)

• Client-side JavaScript packaging

• Multiple file, single <script> injection

• Sequence is preserved
bundle (url1, url2, ..., callback)
Sexy.bundle(
   'jquery.js',
   'jquery-ui.js',
   'jquery-ui-i18n.js',
   'jquery.template.js',
   'mlb.js',
   'mlb.tracking.js',
   'mlb.datagrid.js',
   'mlb.stats.js',
   'mlb.stats.util.js',
   'mlb.stats.widget.js',
   'mlb.stats.app.sortableplayers.js',
   function () {
     mlb.stats.app.SortablePlayers.init();
   }
);
The Kama Sutra of Sexy.js
var s = Sexy
  .js('jquery.min.js')
  .js('jquery.template.min.js')
  .css('friends.css')
  .text('friend.tpl')
  .json('friends.json', function (data, tpl) {

    // Send a new request for each friend (executed simultaneously)
    $.each(data.friends, function (i, friend) {
      s.json(friend.username + '.json', function (friend, friends) {

       // Push onto the previously returned array
       friends.push(friend);

        // Until the last friend, return the augmented array to the next callback
        if (i < data.friends.length - 1) {
          return friends;

       // When we reach the last friend, render the data and insert into the DOM
       } else {
         $.template(tpl, friends).appendTo('#friends');
       }
     });
   });

   // Instantiate the friends array
   return [];
 });
Known Issues

• Unable to detect error events for remote requests

  • jsonp (remoteUrl)

  • script (remoteUrl)

  • style (remoteUrl)

• style (remoteUrl) fires the success callback even if the
  request was unsuccessful (ie. 404)
Parallel Request / Serial Response

• Pros

  • Faster

  • Cleaner

  • Prettier

• Cons

  • Sexy.js adds
    1.4Kb–4.6Kb to
    your JavaScript
    library
Parallel Request / Serial Response

• Pros

  • Faster

  • Cleaner

  • Prettier

• Cons

  • Sexy.js adds
    1.4Kb–4.6Kb to
    your JavaScript
    library
Can’t get enough?

website      http://sexyjs.com
slideshow    http://www.slideshare.net/furf
download     http://github.com/furf/Sexy
twitter      @sexyjs or @furf
email        furf@furf.com
@thanks!
jsconf, voodootikigod, jquery, jeresig,
jaubourg, dimak25, subchild, brandonaaron,
yoni, jsconfeu, getify, maraksquires,
attricetta, and you!

More Related Content

What's hot

Unit Testing in SilverStripe
Unit Testing in SilverStripeUnit Testing in SilverStripe
Unit Testing in SilverStripeIngo Schommer
 
SilverStripe CMS JavaScript Refactoring
SilverStripe CMS JavaScript RefactoringSilverStripe CMS JavaScript Refactoring
SilverStripe CMS JavaScript RefactoringIngo Schommer
 
Getting Started with React
Getting Started with ReactGetting Started with React
Getting Started with ReactNathan Smith
 
Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101Roy Yu
 
How to write easy-to-test JavaScript
How to write easy-to-test JavaScriptHow to write easy-to-test JavaScript
How to write easy-to-test JavaScriptYnon Perek
 
Art of Javascript
Art of JavascriptArt of Javascript
Art of JavascriptTarek Yehia
 
JavaScript Promises
JavaScript PromisesJavaScript Promises
JavaScript PromisesTomasz Bak
 
Javascript Promises/Q Library
Javascript Promises/Q LibraryJavascript Promises/Q Library
Javascript Promises/Q Libraryasync_io
 
Hidden Treasures in Project Wonder
Hidden Treasures in Project WonderHidden Treasures in Project Wonder
Hidden Treasures in Project WonderWO Community
 
React.js & Om: A hands-on walkthrough of better ways to build web UIs
React.js & Om: A hands-on walkthrough of better ways to build web UIsReact.js & Om: A hands-on walkthrough of better ways to build web UIs
React.js & Om: A hands-on walkthrough of better ways to build web UIsAdam Solove
 
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KZepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KThomas Fuchs
 
Jasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishyJasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishyIgor Napierala
 
Scala ActiveRecord
Scala ActiveRecordScala ActiveRecord
Scala ActiveRecordscalaconfjp
 
Beyond Profilers: Tracing Node.js Transactions
Beyond Profilers: Tracing Node.js TransactionsBeyond Profilers: Tracing Node.js Transactions
Beyond Profilers: Tracing Node.js TransactionsTerral R Jordan
 

What's hot (20)

Unit Testing in SilverStripe
Unit Testing in SilverStripeUnit Testing in SilverStripe
Unit Testing in SilverStripe
 
SilverStripe CMS JavaScript Refactoring
SilverStripe CMS JavaScript RefactoringSilverStripe CMS JavaScript Refactoring
SilverStripe CMS JavaScript Refactoring
 
Promise pattern
Promise patternPromise pattern
Promise pattern
 
Getting Started with React
Getting Started with ReactGetting Started with React
Getting Started with React
 
Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101Javascript Testing with Jasmine 101
Javascript Testing with Jasmine 101
 
How to write easy-to-test JavaScript
How to write easy-to-test JavaScriptHow to write easy-to-test JavaScript
How to write easy-to-test JavaScript
 
Art of Javascript
Art of JavascriptArt of Javascript
Art of Javascript
 
JavaScript Promises
JavaScript PromisesJavaScript Promises
JavaScript Promises
 
JavaScript Promises
JavaScript PromisesJavaScript Promises
JavaScript Promises
 
Javascript Promises/Q Library
Javascript Promises/Q LibraryJavascript Promises/Q Library
Javascript Promises/Q Library
 
Hidden Treasures in Project Wonder
Hidden Treasures in Project WonderHidden Treasures in Project Wonder
Hidden Treasures in Project Wonder
 
React.js & Om: A hands-on walkthrough of better ways to build web UIs
React.js & Om: A hands-on walkthrough of better ways to build web UIsReact.js & Om: A hands-on walkthrough of better ways to build web UIs
React.js & Om: A hands-on walkthrough of better ways to build web UIs
 
Discover React
Discover ReactDiscover React
Discover React
 
JavaScript Promise
JavaScript PromiseJavaScript Promise
JavaScript Promise
 
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2KZepto.js, a jQuery-compatible mobile JavaScript framework in 2K
Zepto.js, a jQuery-compatible mobile JavaScript framework in 2K
 
Jasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishyJasmine - why JS tests don't smell fishy
Jasmine - why JS tests don't smell fishy
 
Promises, Promises
Promises, PromisesPromises, Promises
Promises, Promises
 
Scala ActiveRecord
Scala ActiveRecordScala ActiveRecord
Scala ActiveRecord
 
CDI: How do I ?
CDI: How do I ?CDI: How do I ?
CDI: How do I ?
 
Beyond Profilers: Tracing Node.js Transactions
Beyond Profilers: Tracing Node.js TransactionsBeyond Profilers: Tracing Node.js Transactions
Beyond Profilers: Tracing Node.js Transactions
 

Viewers also liked

'I'm Too Sexy'
'I'm Too Sexy''I'm Too Sexy'
'I'm Too Sexy'John *
 
yui3 is Sexy - 使用 YUI 3 的 Sexy Part !
yui3 is Sexy - 使用 YUI 3 的 Sexy Part !yui3 is Sexy - 使用 YUI 3 的 Sexy Part !
yui3 is Sexy - 使用 YUI 3 的 Sexy Part !Joseph Chiang
 
Al desnudo el desarrollo del agro del meta en manos ajenas
Al desnudo el desarrollo del agro del meta en manos ajenasAl desnudo el desarrollo del agro del meta en manos ajenas
Al desnudo el desarrollo del agro del meta en manos ajenasEmilio Garcia Gutierrez
 
Delfina Gerez, sensual
Delfina Gerez, sensualDelfina Gerez, sensual
Delfina Gerez, sensualxixiro
 
The Tugler Show Episode 8
The Tugler Show   Episode 8The Tugler Show   Episode 8
The Tugler Show Episode 8thetuglershow
 
Sexy Car Babes (Pp Tminimizer)
Sexy Car Babes (Pp Tminimizer)Sexy Car Babes (Pp Tminimizer)
Sexy Car Babes (Pp Tminimizer)Marco Belzoni
 
Human sexual response -nw
Human sexual response -nwHuman sexual response -nw
Human sexual response -nwNyunt Wai
 
12 Steps To Condom Use
12 Steps To Condom Use12 Steps To Condom Use
12 Steps To Condom Usemypeeps
 
Sexpositions
SexpositionsSexpositions
Sexpositionsjohnjoon
 
Just for woman show final
Just for woman show finalJust for woman show final
Just for woman show finalDr. Basim walle
 
Reproductive systems of male & female
Reproductive systems of male & femaleReproductive systems of male & female
Reproductive systems of male & femaleMohit Singla
 
Buen Humor Www[1].Diapositivas Eroticas.Com
Buen Humor Www[1].Diapositivas Eroticas.ComBuen Humor Www[1].Diapositivas Eroticas.Com
Buen Humor Www[1].Diapositivas Eroticas.Commaria jose
 

Viewers also liked (18)

'I'm Too Sexy'
'I'm Too Sexy''I'm Too Sexy'
'I'm Too Sexy'
 
yui3 is Sexy - 使用 YUI 3 的 Sexy Part !
yui3 is Sexy - 使用 YUI 3 的 Sexy Part !yui3 is Sexy - 使用 YUI 3 的 Sexy Part !
yui3 is Sexy - 使用 YUI 3 的 Sexy Part !
 
Al desnudo el desarrollo del agro del meta en manos ajenas
Al desnudo el desarrollo del agro del meta en manos ajenasAl desnudo el desarrollo del agro del meta en manos ajenas
Al desnudo el desarrollo del agro del meta en manos ajenas
 
Delfina Gerez, sensual
Delfina Gerez, sensualDelfina Gerez, sensual
Delfina Gerez, sensual
 
Make mypenisbigger 01
Make mypenisbigger 01Make mypenisbigger 01
Make mypenisbigger 01
 
The Tugler Show Episode 8
The Tugler Show   Episode 8The Tugler Show   Episode 8
The Tugler Show Episode 8
 
Sexy Car Babes (Pp Tminimizer)
Sexy Car Babes (Pp Tminimizer)Sexy Car Babes (Pp Tminimizer)
Sexy Car Babes (Pp Tminimizer)
 
Human sexual response -nw
Human sexual response -nwHuman sexual response -nw
Human sexual response -nw
 
Female sexual response
Female sexual responseFemale sexual response
Female sexual response
 
Condoms
CondomsCondoms
Condoms
 
Sexpositions
SexpositionsSexpositions
Sexpositions
 
Girls´s
Girls´sGirls´s
Girls´s
 
12 Steps To Condom Use
12 Steps To Condom Use12 Steps To Condom Use
12 Steps To Condom Use
 
Male and Female condom use steps
Male and Female condom use  stepsMale and Female condom use  steps
Male and Female condom use steps
 
Sexpositions
SexpositionsSexpositions
Sexpositions
 
Just for woman show final
Just for woman show finalJust for woman show final
Just for woman show final
 
Reproductive systems of male & female
Reproductive systems of male & femaleReproductive systems of male & female
Reproductive systems of male & female
 
Buen Humor Www[1].Diapositivas Eroticas.Com
Buen Humor Www[1].Diapositivas Eroticas.ComBuen Humor Www[1].Diapositivas Eroticas.Com
Buen Humor Www[1].Diapositivas Eroticas.Com
 

Similar to Making Ajax Sexy, JSConf 2010

Sexy.js: A Sequential Ajax (Sajax) library for jQuery
Sexy.js: A Sequential Ajax (Sajax) library for jQuerySexy.js: A Sequential Ajax (Sajax) library for jQuery
Sexy.js: A Sequential Ajax (Sajax) library for jQueryDave Furfero
 
Adding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsAdding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsJeff Durta
 
What's new in jQuery 1.5
What's new in jQuery 1.5What's new in jQuery 1.5
What's new in jQuery 1.5Martin Kleppe
 
Build web application by express
Build web application by expressBuild web application by express
Build web application by expressShawn Meng
 
Avoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.jsAvoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.jscacois
 
Ajax Under The Hood
Ajax Under The HoodAjax Under The Hood
Ajax Under The HoodWO Community
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejsNick Lee
 
Ajax tutorial
Ajax tutorialAjax tutorial
Ajax tutorialKat Roque
 
JavaScript Fundamentals with Angular and Lodash
JavaScript Fundamentals with Angular and LodashJavaScript Fundamentals with Angular and Lodash
JavaScript Fundamentals with Angular and LodashBret Little
 
Matthew Eernisse, NodeJs, .toster {webdev}
Matthew Eernisse, NodeJs, .toster {webdev}Matthew Eernisse, NodeJs, .toster {webdev}
Matthew Eernisse, NodeJs, .toster {webdev}.toster
 
international PHP2011_Bastian Feder_jQuery's Secrets
international PHP2011_Bastian Feder_jQuery's Secretsinternational PHP2011_Bastian Feder_jQuery's Secrets
international PHP2011_Bastian Feder_jQuery's Secretssmueller_sandsmedia
 
Request dispacther interface ppt
Request dispacther interface pptRequest dispacther interface ppt
Request dispacther interface pptTaha Malampatti
 
Avinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPressAvinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPresswpnepal
 
JavaScript and the AST
JavaScript and the ASTJavaScript and the AST
JavaScript and the ASTJarrod Overson
 
Building Smart Async Functions For Mobile
Building Smart Async Functions For MobileBuilding Smart Async Functions For Mobile
Building Smart Async Functions For MobileGlan Thomas
 

Similar to Making Ajax Sexy, JSConf 2010 (20)

Sexy.js: A Sequential Ajax (Sajax) library for jQuery
Sexy.js: A Sequential Ajax (Sajax) library for jQuerySexy.js: A Sequential Ajax (Sajax) library for jQuery
Sexy.js: A Sequential Ajax (Sajax) library for jQuery
 
Adding a modern twist to legacy web applications
Adding a modern twist to legacy web applicationsAdding a modern twist to legacy web applications
Adding a modern twist to legacy web applications
 
What's new in jQuery 1.5
What's new in jQuery 1.5What's new in jQuery 1.5
What's new in jQuery 1.5
 
Build web application by express
Build web application by expressBuild web application by express
Build web application by express
 
Backbone.js
Backbone.jsBackbone.js
Backbone.js
 
Avoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.jsAvoiding Callback Hell with Async.js
Avoiding Callback Hell with Async.js
 
Intro to Sail.js
Intro to Sail.jsIntro to Sail.js
Intro to Sail.js
 
Jasmine BDD for Javascript
Jasmine BDD for JavascriptJasmine BDD for Javascript
Jasmine BDD for Javascript
 
Ajax Under The Hood
Ajax Under The HoodAjax Under The Hood
Ajax Under The Hood
 
Understanding backbonejs
Understanding backbonejsUnderstanding backbonejs
Understanding backbonejs
 
Why Our Code Smells
Why Our Code SmellsWhy Our Code Smells
Why Our Code Smells
 
RingoJS
RingoJSRingoJS
RingoJS
 
Ajax tutorial
Ajax tutorialAjax tutorial
Ajax tutorial
 
JavaScript Fundamentals with Angular and Lodash
JavaScript Fundamentals with Angular and LodashJavaScript Fundamentals with Angular and Lodash
JavaScript Fundamentals with Angular and Lodash
 
Matthew Eernisse, NodeJs, .toster {webdev}
Matthew Eernisse, NodeJs, .toster {webdev}Matthew Eernisse, NodeJs, .toster {webdev}
Matthew Eernisse, NodeJs, .toster {webdev}
 
international PHP2011_Bastian Feder_jQuery's Secrets
international PHP2011_Bastian Feder_jQuery's Secretsinternational PHP2011_Bastian Feder_jQuery's Secrets
international PHP2011_Bastian Feder_jQuery's Secrets
 
Request dispacther interface ppt
Request dispacther interface pptRequest dispacther interface ppt
Request dispacther interface ppt
 
Avinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPressAvinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPress
 
JavaScript and the AST
JavaScript and the ASTJavaScript and the AST
JavaScript and the AST
 
Building Smart Async Functions For Mobile
Building Smart Async Functions For MobileBuilding Smart Async Functions For Mobile
Building Smart Async Functions For Mobile
 

Recently uploaded

From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .Alan Dix
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxLoriGlavin3
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxLoriGlavin3
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxLoriGlavin3
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek SchlawackFwdays
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxLoriGlavin3
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsNathaniel Shimoni
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embeddingZilliz
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 

Recently uploaded (20)

From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .
 
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptxPasskey Providers and Enabling Portability: FIDO Paris Seminar.pptx
Passkey Providers and Enabling Portability: FIDO Paris Seminar.pptx
 
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
 
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptxA Deep Dive on Passkeys: FIDO Paris Seminar.pptx
A Deep Dive on Passkeys: FIDO Paris Seminar.pptx
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptxUse of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
Use of FIDO in the Payments and Identity Landscape: FIDO Paris Seminar.pptx
 
Time Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directionsTime Series Foundation Models - current state and future directions
Time Series Foundation Models - current state and future directions
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embedding
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 

Making Ajax Sexy, JSConf 2010

  • 1. Making Ajax Sexy A Look at Sequential Ajax Patterns
  • 4.
  • 5.
  • 8.
  • 10.
  • 12. WARNING If very bawdy jokes are the sort of thing that offend you or make you uncomfortable in the trousers, you might want to leave now.
  • 13. David Furfero @furf UI Engineer, MLB.com
  • 16. Ajax makes killer web apps possible.
  • 17.
  • 18. Sequential Ajax makes Ajax killer.
  • 19. Sequential Ajax makes Ajax killer.
  • 20.
  • 21. Sexy.js makes Sequential Ajax…Sexy!
  • 22. Sexy.js makes Sequential Ajax…Sexy!
  • 23.
  • 24. Sequential Ajax (Sajax) An Ajax pattern for handling multiple asynchronous requests while maintaining proper callback execution order.
  • 25. Goals of Sequential Ajax (Sajax) • Facilitate combination and manipulation of data from multiple sources • Speed loading of data, scripts, stylesheets using parallel asynchronous HTTP requests • Speed execution of callbacks by allowing them to fire as soon as all previous data is available • Fun
  • 26. Three Flavors of Sajax • Serial request / serial response • Parallel request / random response • Parallel request / serial response
  • 27. In a perfect world…
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42. In a world, where time waits for no man…
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 60. Serial Request / Serial Response // Request an article $.getJSON('/article/1.json', function (article) { // Request the related comments $.getJSON('/article/1/comments.json', function (comments) { // Join the data article.comments = comments; // Render the article and comments render(article); }); });
  • 61. Serial Request / Serial Response • Pros • Relatively readable • Cons • Slower • Nested calls don’t scale
  • 62. Parallel Request / Random Response var i = 0, n = 2, article, comments; $.getJSON('/article/1.json', function (data) { article = data; if (++i == n) { handleComplete(); } }); $.getJSON('/article/1/comments.json', function (data) { comments = data; if (++i == n) { handleComplete(); } }); function handleComplete () { article.comments = comments; render(article); }
  • 63. Parallel Request / Random Response • Pros • Faster • Cons • Heavier • Uglier • Really doesn’t scale
  • 64. Do those patterns look familiar?
  • 66. Sexy is a jQuery plugin A lightweight JavaScript library which provides parallel request/serial response Sequential Ajax (Sajax) functionality and a sleek facade to jQuery’s native Ajax methods.
  • 67. Sexy is a standalone library A lightweight JavaScript library which provides parallel request/serial response Sequential Ajax (Sajax) functionality and a sleek facade to jQuery’s native Ajax methods JavaScript.
  • 68. Sexy is a standalone library A lightweight JavaScript library which provides parallel request/serial response Sequential Ajax (Sajax) functionality and a sleek facade to jQuery’s native Ajax methods JavaScript (using jQuery’s native Ajax methods).
  • 69. Sexy is classy new Sexy() creates a Sexy instance
  • 70. Sexy is classy var s = new Sexy(); s.json(articleUrl, callback) .json(commentsUrl, callback);
  • 71. Sexy is the new Sexy Sexy() also creates a Sexy instance
  • 72. Sexy is the new Sexy Sexy() .json(articleUrl, callback) .json(commentsUrl, callback);
  • 73. Naked Sexy /** * Constructs a new Sexy instance */ function Sexy (cfg) { /** * Allow instantiation without new keyword */ if (!(this instanceof Sexy)) { return new Sexy(cfg); } this.cfgs = []; this.setup(cfg); }
  • 74. Sexy is full of surprises Sexy.fn() also creates a Sexy instance!
  • 75. Sexy is full of surprises Sexy .json(articleUrl, callback) .json(commentsUrl, callback);
  • 76. Naked Sexy /** * Add sexier static methods */ function addStaticMethod (method) { Sexy[method] = function () { return Sexy.prototype[method].apply(new Sexy(), arguments); }; } for (i in Sexy.prototype) { addStaticMethod(i); }
  • 77. I WASN’T KIDDING! There is a bawdy joke coming up VERY soon!
  • 78. I WASN’T KIDDING! If you are pregnant, have a serious heart condition, or giggle uncontrollably at the word “titillate”, you may want to hurry to Track A.
  • 80. Sexy is Your Syntactic Sugar Mama An expressive method for each supported data type: html (url, callback) json (url, callback) jsonp (url, callback) script / js (url, callback) text (url, callback) xml (url, callback) style / css (url, callback)
  • 81. Sexy is Patient Requests can be deferred using an optional 2nd argument. html (url, defer, callback) json (url, defer, callback) jsonp (url, defer, callback) script / js (url, defer, callback) text (url, defer, callback) xml (url, defer, callback) style / css (url, defer, callback)
  • 82. Sexy is Flexible Each method can also take a jQuery settings object. html (settings) json (settings) jsonp (settings) script / js (settings) text (settings) xml (settings) style / css (settings)
  • 83. callback (data, previous, next, status) Each callback receives four arguments: data response data of the current request previous return value of the previous callback next Ajax settings of the next request status result status of the current request
  • 85. Sexy Sexy .json({ url: '/article/1.json', success: function (article) { return article; } }) .json({ url: '/article/1/comments.json', success: function (comments, article) { article.comments = comments; render(article); } });
  • 86. Passing Data with Callbacks Sexy .json({ url: '/article/1.json', success: function (article, previous, next, status) { return article; } }) .json({ url: '/article/1/comments.json', success: function (comments, article, next, status) { article.comments = comments; render(article); } });
  • 87. Sexy-er Sexy .json('/article/1.json', function (article) { return article; }) .json('/article/1/comments.json', function (comments, article) { article.comments = comments; render(article); });
  • 88. Passing Data with Callbacks Sexy .json('/article/1.json', function (article) { return article; }) .json('/article/1/comments.json', function (comments, article) { article.comments = comments; render(article); });
  • 89. Sexy-est Sexy .json('/article/1.json') .json('/article/1/comments.json', function (comments, article) { article.comments = comments; render(article); });
  • 90. Passing Data with Implicit Callbacks Sexy .json('/article/1.json') .json('/article/1/comments.json', function (comments, article) { article.comments = comments; render(article); });
  • 91. Passing Data with Implicit Callbacks Sexy .json('/article/1.json') .js('jquery.js') .json('/article/1/comments.json', function (comments, article) { article.comments = comments; render(article); });
  • 92. Slow it Down Sexy // Authenticate user .json({ type: 'POST', url: 'login', data: { username: 'furf', password: '2s3xy4u' } }) // GET article .json('article/1', true, function (article, previous, nextCfg) { article.author = 'furf'; nextCfg.data = article; }) // POST article .json({ defer: true, type: 'POST', url: 'article/1' }) // De-authenticate user .json('logout', true);
  • 93. Manage <script> Dependencies Sexy .js('jquery.js') .js('jquery-ui.js') .js('jquery-ui-i18n.js') .js('jquery.template.js') .js('mlb.js') .js('mlb.tracking.js') .js('mlb.datagrid.js') .js('mlb.stats.js') .js('mlb.stats.util.js') .js('mlb.stats.widget.js') .js('mlb.stats.sortableplayers.js', function () { mlb.stats.SortablePlayers.init(); });
  • 95. Old and Busted var i = 0, n = 2, article, comments; $.getJSON('/article/1.json', function (data) { article = data; if (++i == n) { handleComplete(); } }); $.getJSON('/article/1/comments.json', function (data) { comments = data; if (++i == n) { handleComplete(); } }); function handleComplete () { article.comments = comments; render(article); }
  • 96. Your Royal Hotness Sexy .json('/article/1.json') .json('/article/1/comments.json', function (comments, article) { article.comments = comments; render(article); });
  • 97. The 4th Stage of Grief ACCEPTANCE There’s no way out now. Strap yourself in. Accept your fate. The bawdy joke is on its way.
  • 99. setup (settings) • Configure subsequent requests of a Sexy instance • Settings can be unset with setup()
  • 100. setup (settings) Sexy .setup({ beforeSend: showLoadingIndicator complete: hideLoadingIndicator }) .json('article.json', diplayArticle) .setup() .json('comments.json', displayComments);
  • 101. Sexy (settings) Sexy({ beforeSend: showLoadingIndicator complete: hideLoadingIndicator }) .json('article.json', diplayArticle) .setup() .json('comments.json', displayComments);
  • 102. bundle (url1, url2, ..., callback) • Client-side JavaScript packaging • Multiple file, single <script> injection • Sequence is preserved
  • 104. JavaScript was too reserved.
  • 106.
  • 107. :) I told you it was bawdy!
  • 108. bundle (url1, url2, ..., callback) • Client-side JavaScript packaging • Multiple file, single <script> injection • Sequence is preserved
  • 109. bundle (url1, url2, ..., callback) Sexy.bundle( 'jquery.js', 'jquery-ui.js', 'jquery-ui-i18n.js', 'jquery.template.js', 'mlb.js', 'mlb.tracking.js', 'mlb.datagrid.js', 'mlb.stats.js', 'mlb.stats.util.js', 'mlb.stats.widget.js', 'mlb.stats.app.sortableplayers.js', function () { mlb.stats.app.SortablePlayers.init(); } );
  • 110. The Kama Sutra of Sexy.js
  • 111. var s = Sexy .js('jquery.min.js') .js('jquery.template.min.js') .css('friends.css') .text('friend.tpl') .json('friends.json', function (data, tpl) { // Send a new request for each friend (executed simultaneously) $.each(data.friends, function (i, friend) { s.json(friend.username + '.json', function (friend, friends) { // Push onto the previously returned array friends.push(friend); // Until the last friend, return the augmented array to the next callback if (i < data.friends.length - 1) { return friends; // When we reach the last friend, render the data and insert into the DOM } else { $.template(tpl, friends).appendTo('#friends'); } }); }); // Instantiate the friends array return []; });
  • 112. Known Issues • Unable to detect error events for remote requests • jsonp (remoteUrl) • script (remoteUrl) • style (remoteUrl) • style (remoteUrl) fires the success callback even if the request was unsuccessful (ie. 404)
  • 113. Parallel Request / Serial Response • Pros • Faster • Cleaner • Prettier • Cons • Sexy.js adds 1.4Kb–4.6Kb to your JavaScript library
  • 114. Parallel Request / Serial Response • Pros • Faster • Cleaner • Prettier • Cons • Sexy.js adds 1.4Kb–4.6Kb to your JavaScript library
  • 115. Can’t get enough? website http://sexyjs.com slideshow http://www.slideshare.net/furf download http://github.com/furf/Sexy twitter @sexyjs or @furf email furf@furf.com
  • 116. @thanks! jsconf, voodootikigod, jquery, jeresig, jaubourg, dimak25, subchild, brandonaaron, yoni, jsconfeu, getify, maraksquires, attricetta, and you!

Editor's Notes