mashing up javascript
TRANSCRIPT
Mashing up JavaScript
Bastian HofmannResearchGate GmbH
Wtf?
• JavaScript Apps
• CORS and OAuth2
• Local Storage
• OEmbed and Caja
• WebSockets, ActivityStrea.ms and PubsubHubbub
• OExchange
https://github.com/bashofmann/statusnet_js_mashup
Let‘s write a JS App
History & Bookmarking
API Access
Same Origin Policy
Cross-Origin Resource Sharing
Backendapi.twitter.com
Client client.net
AJAX
Access-Control-Allow-Origin: *
http://www.w3.org/TR/cors/
var html="<ul>";for (var i=0; i < viewers.length; i++) { html += "<li>" + viewers[i].displayName + "</li>";}html += "<ul>";document.getElementById("#div").innerHTML = html;
Where is the error?
Templates
Mustache.JS
https://github.com/janl/mustache.js
}
var feed = [];var app = Sammy('#main', function() { this.use(Sammy.Mustache, 'ms'); this.get('#/', function() { this.trigger('getFeed'); }); ...});
jQuery(function() { app.run('#/');});
var feed = [];var app = Sammy('#main', function() { this.use(Sammy.Mustache, 'ms'); this.get('#/', function() { this.trigger('getFeed'); }); ...});
jQuery(function() { app.run('#/');});
var feed = [];var app = Sammy('#main', function() { this.use(Sammy.Mustache, 'ms'); this.get('#/', function() { this.trigger('getFeed'); }); ...});
jQuery(function() { app.run('#/');});
var feed = [];var app = Sammy('#main', function() { this.use(Sammy.Mustache, 'ms'); this.get('#/', function() { this.trigger('getFeed'); }); ...});
jQuery(function() { app.run('#/');});
var feed = [];var app = Sammy('#main', function() { this.use(Sammy.Mustache, 'ms'); this.get('#/', function() { this.trigger('getFeed'); }); ...});
jQuery(function() { app.run('#/');});
this.bind('getFeed', function() { var that = this; $.ajax({ url: 'http://..._timeline.json', success: function(response) { feed = response; that.trigger('renderFeed'); } }); }); this.bind('renderFeed', function() { this.feed = feed; this.partial('js/templates/feed.ms'); });
this.bind('getFeed', function() { var that = this; $.ajax({ url: 'http://..._timeline.json', success: function(response) { feed = response; that.trigger('renderFeed'); } }); }); this.bind('renderFeed', function() { this.feed = feed; this.partial('js/templates/feed.ms'); });
this.bind('getFeed', function() { var that = this; $.ajax({ url: 'http://..._timeline.json', success: function(response) { feed = response; that.trigger('renderFeed'); } }); }); this.bind('renderFeed', function() { this.feed = feed; this.partial('js/templates/feed.ms'); });
this.bind('getFeed', function() { var that = this; $.ajax({ url: 'http://..._timeline.json', success: function(response) { feed = response; that.trigger('renderFeed'); } }); }); this.bind('renderFeed', function() { this.feed = feed; this.partial('js/templates/feed.ms'); });
this.bind('getFeed', function() { var that = this; $.ajax({ url: 'http://..._timeline.json', success: function(response) { feed = response; that.trigger('renderFeed'); } }); }); this.bind('renderFeed', function() { this.feed = feed; this.partial('js/templates/feed.ms'); });
this.bind('getFeed', function() { var that = this; $.ajax({ url: 'http://..._timeline.json', success: function(response) { feed = response; that.trigger('renderFeed'); } }); }); this.bind('renderFeed', function() { this.feed = feed; this.partial('js/templates/feed.ms'); });
<ul>{{#feed}} <li> <p>{{{statusnet_html}}}</p> <p>{{created_at}} {{#user}} {{name}} {{/user}} </p> </li>{{/feed}}</ul>
<ul>{{#feed}} <li> <p>{{{statusnet_html}}}</p> <p>{{created_at}} {{#user}} {{name}} {{/user}} </p> </li>{{/feed}}</ul>
<ul>{{#feed}} <li> <p>{{{statusnet_html}}}</p> <p>{{created_at}} {{#user}} {{name}} {{/user}} </p> </li>{{/feed}}</ul>
<ul>{{#feed}} <li> <p>{{{statusnet_html}}}</p> <p>{{created_at}} {{#user}} {{name}} {{/user}} </p> </li>{{/feed}}</ul>
DEMO
Authorization
+----------+ Client Identifier +----------------+ | |>---(A)-- & Redirection URI --->| | | | | | End <--+ - - - +----(B)-- User authenticates -->| Authorization | User | | | Server | | |<---(C)--- Redirect URI -------<| | | Client | with Access Token | | | in | in Fragment +----------------+ | Browser | | | +----------------+ | |>---(D)--- Redirect URI ------->| | | | without Fragment | Web Server | | | | with Client | | (F) |<---(E)--- Web Page with ------<| Resource | | Access | Script | | | Token | +----------------+ +----------+
OAuth 2
http://tools.ietf.org/html/draft-ietf-oauth-v2
this.bind('getFeed', function() { oauth2.retrieveAccessToken(); if (! oauth2.store['access_token']) { this.redirect('#Login'); return; } ... });
this.bind('getFeed', function() { oauth2.retrieveAccessToken(); if (! oauth2.store['access_token']) { this.redirect('#Login'); return; } ... });
this.get('#Login', function() { var consumerKey = 'abc....'; window.open('http://status.net/api/oauth2/authorize?response_toke=token&client_id=' + consumerKey);});
this.get('#Login', function() { var consumerKey = 'abc....'; window.open('http://status.net/api/oauth2/authorize?response_toke=token&client_id=' + consumerKey);});
<script type="text/javascript"> var fragment = location.hash.substr(1); opener.parent.oauth2.storeToken(fragment); window.close();</script>
<script type="text/javascript"> var fragment = location.hash.substr(1); opener.parent.oauth2.storeToken(fragment); window.close();</script>
<script type="text/javascript"> var fragment = location.hash.substr(1); opener.parent.oauth2.storeToken(fragment); window.close();</script>
<form action="#Entry" method="post"> <textarea name="comment"></textarea> <input type="submit" value="send"/></form>
<form action="#Entry" method="post"> <textarea name="comment"></textarea> <input type="submit" value="send"/></form>
<form action="#Entry" method="post"> <textarea name="comment"></textarea> <input type="submit" value="send"/></form>
this.post('#Entry', function() { var that = this; $.ajax({ url: 'http://status.net/.../update.json?oauth_token=' + oauth2.store['access_token'], type: 'POST', data: { 'status': that.params['comment'] }, success: function() { that.redirect('#/'); } }); });
this.post('#Entry', function() { var that = this; $.ajax({ url: 'http://status.net/.../update.json?oauth_token=' + oauth2.store['access_token'], type: 'POST', data: { 'status': that.params['comment'] }, success: function() { that.redirect('#/'); } }); });
this.post('#Entry', function() { var that = this; $.ajax({ url: 'http://status.net/.../update.json?oauth_token=' + oauth2.store['access_token'], type: 'POST', data: { 'status': that.params['comment'] }, success: function() { that.redirect('#/'); } }); });
this.post('#Entry', function() { var that = this; $.ajax({ url: 'http://status.net/.../update.json?oauth_token=' + oauth2.store['access_token'], type: 'POST', data: { 'status': that.params['comment'] }, success: function() { that.redirect('#/'); } }); });
this.post('#Entry', function() { var that = this; $.ajax({ url: 'http://status.net/.../update.json?oauth_token=' + oauth2.store['access_token'], type: 'POST', data: { 'status': that.params['comment'] }, success: function() { that.redirect('#/'); } }); });
this.post('#Entry', function() { var that = this; $.ajax({ url: 'http://status.net/.../update.json?oauth_token=' + oauth2.store['access_token'], type: 'POST', data: { 'status': that.params['comment'] }, success: function() { that.redirect('#/'); } }); });
Storing the access token
Local Storage
http://www.w3.org/TR/webstorage/
localStorage.setItem("key", value);
localStorage.getItem("key");
DEMO
Mash it up!
cool video:http://www.youtube.com/watch?v=OFzkTxiwziQ
http://www.youtube.com/watch?v=OyJd2qsRkNk
http://www.youtube.com/oembed?url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv
%3DOyJd2qsRkNk&maxwidth=500&format=json
{ "provider_url":"http:\/\/www.youtube.com\/", "title":"Jupiter Jones - Das Jahr in dem ich schlief (Musik Video)", "html":"\u003cobject width=\"500\" height=\"306\"\u003e\u003cparam name=\"movie\" value=\"http:\/\/www.youtube.com\/v\/OyJd2qsRkNk?version=3\"\u003e\u003c\/param\u003e\u003cparam name=\"allowFullScreen\" value=\"true\"\u003e\u003c\/param\u003e\u003cparam name=\"allowscriptaccess\" value=\"always\"\u003e\u003c\/param\u003e\u003cembed src=\"http:\/\/www.youtube.com\/v\/OyJd2qsRkNk?version=3\" type=\"application\/x-shockwave-flash\" width=\"500\" height=\"306\" allowscriptaccess=\"always\" allowfullscreen=\"true\"\u003e\u003c\/embed\u003e\u003c\/object\u003e", "author_name":"St182", "height":306, "thumbnail_width":480, "width":500, "version":"1.0", "author_url":"http:\/\/www.youtube.com\/user\/Stinkfist182", "provider_name":"YouTube", "thumbnail_url":"http:\/\/i4.ytimg.com\/vi\/OyJd2qsRkNk\/hqdefault.jpg", "type":"video", "thumbnail_height":360}
cool video:
Caja
http://code.google.com/p/google-caja/
html_sanitize(‘<script>alert(“foo“);</script>‘)
DEMO
Instant updates without reloading
PubSubHubbubretrieves Atom feed with Hub URL
Hub
posts sthpings everysubscriber
subscribes for feed
ackssubscription
http://code.google.com/p/pubsubhubbub/
<link rel="alternate"href="http://status.net.xyz:8061/index.php/api/statuses/user_timeline/3.atom"type="application/atom+xml" title="Notice feed for bastian (Atom)"/>
<entry> <activity:object-type>http://activitystrea.ms/schema/1.0/note</activity:object-type> <id>http://status.net.xyz:8061/index.php/notice/20</id> <title>hello from client</title> <content type="html">hello from client</content> <link rel="alternate" type="text/html" href="http://status.net.xyz:8061/index.php/notice/20"/> <activity:verb>http://activitystrea.ms/schema/1.0/post</activity:verb> <published>2011-05-23T21:07:33+00:00</published> <updated>2011-05-23T21:07:33+00:00</updated> <link rel="ostatus:conversation" href="http://status.net.xyz:8061/index.php/conversation/20"/> <georss:point>52.52437 13.41053</georss:point> <link rel="self" type="application/atom+xml"href="http://status.net.xyz:8061/index.php/api/statuses/show/20.atom"/> <link rel="edit" type="application/atom+xml"href="http://status.net.xyz:8061/index.php/api/statuses/show/20.atom"/> <statusnet:notice_info local_id="20" source="api" favorite="false"repeated="false"></statusnet:notice_info></entry>
<link href="http://status.net.xyz:8061/index.php/main/push/hub" rel="hub"/>
PubSubHubbubretrieves Atom feed with Hub URL
Hub
posts sthpings everysubscriber
subscribes for feed
ackssubscription
http://code.google.com/p/pubsubhubbub/
WebSockets
http://dev.w3.org/html5/websockets/
http://search.npmjs.org/#/socket.io
Browser Notifications
webkitNotifications.createNotification(image, title, text).show();
webkitNotifications.requestPermission();
retrieve Stream with Hub
Ajax: request Subscription
WebSockets:new Post
subscribe at hubchallenge
acknew post
Notification
new post
DEMO
Sharing
Nascar Problem
http://www.example.com/share.php?url={URI}&title={title for the content}&description={short description of the content}&ctype=flash&swfurl={SWF URI}&height={preferred SWF height}&width={preferred swf width}&screenshot={screenshot URI}
http://example.com/.well-known/host-meta
http://tools.ietf.org/html/draft-nottingham-site-meta
<?xml version='1.0' encoding='UTF-8'?><XRD xmlns='http://docs.oasis-open.org/ns/xri/xrd-1.0' xmlns:hm='http://host-meta.net/xrd/1.0'> <hm:Host>www.meinvz.net</hm:Host>
<Link rel="http://oexchange.org/spec/0.8/rel/resident-target" type="application/xrd+xml" href="http://www.example.com/oexchange.xrd" > </Link>
</XRD>
<?xml version='1.0' encoding='UTF-8'?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"> <Subject>http://www.example.com/linkeater</Subject> <Property type="http://www.oexchange.org/spec/0.8/prop/vendor"> Examples Inc.</Property> <Property type="http://www.oexchange.org/spec/0.8/prop/title"> A Link-Accepting Service</Property> <Link rel= "icon" href="http://www.example.com/favicon.ico" type="image/vnd.microsoft.icon" /> <Link rel= "http://www.oexchange.org/spec/0.8/rel/offer" href="http://www.example.com/linkeater/offer.php" type="text/html" /></XRD>
http://search.npmjs.org/#/oexchange
DEMO
h"p://twi"er.com/Bas2anHofmannh"p://profiles.google.com/bashofmannh"p://lanyrd.com/people/Bas2anHofmann/h"p://slideshare.net/bashofmann