Delicious Sample App
The Delicious Sample App:
- Uses a message.read event listener to enhance email message whenever the user opens email with a particular subject line.
- Modifies the content of the email.
- Makes the email more interactive.
- Calls an external web service to expand content available within the email.
Try It Out
See Quick Start to learn how to get the full source for this example, install it, and run it in Yahoo! Mail.
Create Your Delicious Skeleton App
Below, we create an app based on the Delicious Skeleton sample.
> ymdt create delicious -S 'Delicious Skeleton' Yahoo! Mail Development Tool Version 1.11 Developer server: developer.mail.yahoo.com create: Getting sample 'Delicious Skeleton' into delicious: changed appid to 6ce36cpgmacpi. downloading delicious/config.xml. . . downloading delicious/auth.xml. . . downloading delicious/views/about.html. . . downloading delicious/assets/enhance.css. . . downloading delicious/assets/enhance.js. . . Done all gets for delicious. Application '38d1kcgsm4d9g' created. uploading delicious/skeleton/auth.xml. . . uploading delicious/skeleton/config.xml. . . uploading delicious/assets/enhance.css. . . uploading delicious/assets/enhance.js. . . uploading delicious/skeleton/views/about.html. . . Done all puts for delicious.
> ymdt create delicious -S 'Delicious Skeleton' Yahoo! Mail Development Tool Version 1.11 Developer server: developer.mail.yahoo.com create: Getting sample 'Delicious Skeleton' into delicious: changed appid to 6ce36cpgmacpi. downloading delicious/config.xml. . . downloading delicious/auth.xml. . . downloading delicious/views/about.html. . . downloading delicious/assets/enhance.css. . . downloading delicious/assets/enhance.js. . . Done all gets for delicious. Application '38d1kcgsm4d9g' created. uploading delicious/skeleton/auth.xml. . . uploading delicious/skeleton/config.xml. . . uploading delicious/assets/enhance.css. . . uploading delicious/assets/enhance.js. . . uploading delicious/skeleton/views/about.html. . . Done all puts for delicious.
Install and Run Your App in the latest Yahoo! Mail
After logging into Yahoo! Mail, notice the Applications Pane in
the lower left corner of the page. Click the
in the upper right-hand corner of the Applications Pane.
This launches the Yahoo! Mail Gallery. Find the app named 'Delicious Skeleton (private)' with your Yahoo! ID listed as the developer. It is marked '(private)' because only you can see it while it's under development.
Click the 'Add' button, then reload Yahoo! Mail. You should now see a 'Delicious Skeleton (private)' app in the Applications Pane. Click its icon and the app should show its 'about' dialog. Send yourself a message with "tasty" in the subject line and some URLs in the message body. When the message arrives, click on in it in your inbox, and this app will add the word 'Delicious-ified!' in red at the top of the message body and will enhance the message by making all of the URLs green.
That's just a simple example of how an app can enhance the appearance of a message.
Make Changes: Edit, Upload, Refresh
The files comprising your app are located in the local 'delicious' directory
that was populated when you ran ymdt create. To get an idea for
how apps are put together, have a look at the files in that directory.
The contents of config.xml tell Yahoo! Mail to launch the app's
"about.html" view in a dialog when the app's icon is clicked and to enhance email
messages with enhance.js and enhance.css when a message
with "tasty" in the subject is clicked. enhance.js uses the
openmail.Application.setListener function to register
afterRender handler. The handler is invoked after message rendering
with the current message's data, so that the app can use that data to modify the
message content.
Let's make a simple change to the app. Edit the
file delicious/config.xml. Change the <name>
field to "Delicious".
Upload your changed config.xml to the Yahoo! Mail developer server using ymdt put.
> ymdt put delicious/config.xml Yahoo! Mail Development Tool Version 1.11 Developer server: developer.mail.yahoo.com put: uploading delicious/config.xml. . .
> ymdt put delicious/config.xml
Yahoo! Mail Development Tool Version 1.11
Developer server: developer.mail.yahoo.com
put:
uploading delicious/config.xml. . .You should be able to see your changes by clearing your browser's cache and reloading Yahoo! Mail. Notice that the name in the Applications pane has changed from "Delicious Skeleton (private)" to "Delicious (private)".
Now, edit delicious/assets/enhance.css and replace "color: green"
with "color: purple". Upload the change
with ymdt put delicious/assets/enhance.css.
To see your change, instead of refreshing your browser like you did above,
just click the
next to your application in the Applications
pane. Then, click another message and then your "tasty" message to see the
changed coloring. You can use this shortcut whenever you change any of your
app's files except for its config.xml.
Instead of typing ymdt put every time you edit a file, try using
ymdt's dev-o-matic mode: ymdt dev delicious.
Adding Tooltip to Links
Let's say we wanted to make it so that the user gets a tooltip when they mouseover a link in the message body. In this step, we'll show how to add event listeners to DOM nodes.
Here are the contents of enhance.js:
function enhance(args) { args.YUI({ doc: args.view.doc }).use('node', function(Y) { var tooltip; /** * Display delicious tags when mouseover link. */ function onMouseoverLink(evt) { var node = evt.target; // Reposition the tooltip var r = node.get('region'); tooltip.setStyles({ top: r.bottom, left: r.left, display: 'block' }); tooltip._node.innerHTML = 'standby for true flavah!'; } //add intro text Y.one('.msg-body').prepend('<div class="delicious-intro">Delicious-ified! [hover on any URL.]<br/><br/></div>'); //make all anchor tags have 'delicious-link' class Y.all('.msg-body a').each(function(node){ node.addClass('delicious-link'); }); //create our tooltip (just a floating div) var tooltip = Y.Node.create('<div id="tastytooltip" class="delicious-tastymenu"></div>'); Y.one('body').append(tooltip); //hook up UI tooltip.on('mouseout', function() { tooltip.setStyle('display', 'none'); } ); Y.on('mouseover', onMouseoverLink, 'a.delicious-link'); }); // YUI.use } // enhance //listen to afterRender event openmail.Application.setListener({event:'message.read'}, { afterRender: enhance });
function enhance(args) { args.YUI({ doc: args.view.doc }).use('node', function(Y) { var tooltip; /** * Display delicious tags when mouseover link. */ function onMouseoverLink(evt) { var node = evt.target; // Reposition the tooltip var r = node.get('region'); tooltip.setStyles({ top: r.bottom, left: r.left, display: 'block' }); tooltip._node.innerHTML = 'standby for true flavah!'; } //add intro text Y.one('.msg-body').prepend('<div class="delicious-intro">Delicious-ified! [hover on any URL.]<br/><br/></div>'); //make all anchor tags have 'delicious-link' class Y.all('.msg-body a').each(function(node){ node.addClass('delicious-link'); }); //create our tooltip (just a floating div) var tooltip = Y.Node.create('<div id="tastytooltip" class="delicious-tastymenu"></div>'); Y.one('body').append(tooltip); //hook up UI tooltip.on('mouseout', function() { tooltip.setStyle('display', 'none'); } ); Y.on('mouseover', onMouseoverLink, 'a.delicious-link'); }); // YUI.use } // enhance //listen to afterRender event openmail.Application.setListener({event:'message.read'}, { afterRender: enhance });
The comments here reveal most of what you need to know. We also need to add css
to style the tooltip. We'll put a new css rule ".delicious-tastymenu" in
delicious/assets/enhance.css.
.delicious-intro { color: red; } .delicious-link { color: purple; } .delicious-tastymenu { display: none; background: #3274D0; color: #fff; position: absolute; padding: 10px; }
.delicious-intro { color: red; } .delicious-link { color: purple; } .delicious-tastymenu { display: none; background: #3274D0; color: #fff; position: absolute; padding: 10px; }
You can now upload all the changes we've made by typing ymdt put
delicious. (Or, if you have ymdt in dev-o-matic mode, it's been
uploading changes as you've made them.) Now, the delicious-enhanced messages
should have tooltips that read "standby for true flavah!" when you mouseover the
links.
Calling a Web Service
You can use our Application.callWebService
API to call third party webservices with both GET and POST methods. Upon a GET, this method takes the values of URL and
parameters and combines them into a single GET request. Upon a POST, it takes the values of URL and
parameters and creates a POST request with the post-body containing encodeURIComponent() parameters.
For example:
url:"http://api.flickr.com/services/rest?api_key=1234",
parameters:{"function":"flickr.photos.search","tags":"pb & j"}GET http://api.flickr.com/services/rest?api_key=1234&function=flickr.photos.search&tags=pb%20%26%20jPOST http://api.flickr.com/services/rest?api_key=1234
postBody: function=flickr.photos.search&tags=pb%20%26%20j
function enhance(args) { args.YUI({ doc: args.view.doc, modules: { 'gallery-crypto-md5': { // pull in local gallery for use over ssl fullpath: '/yahoo/mail/assets/' + 'gallery-crypto/gallery-crypto-md5-min.js' } } }).use('gallery-crypto-md5', 'node', 'json-parse', function(Y) { var md5 = Y.Crypto.MD5; var tooltip; var cache = []; var protocol = openmail.Application.isSSL && openmail.Application.isSSL() ? 'https://' : 'http://'; /** * Handle the delicious web service response to our query for a URL's tags. */ function handleTags(response) { var items = []; var results = response.data && Y.JSON.parse(response.data); var txt = 'delicious tags: '; if (results && results.query && results.query.results) { results = results.query.results.json; for(var tag in results.top_tags){ items.push(tag); } if (results.title) { txt = '<b>' + results.title + '</b><br/>' + txt; } } else { txt = ''; items.push('<div class="error">an error or no results found for url.</div>'); } // Cache web service responses by the md5 hash of the URL. // Using the md5 hash for the key since the normalized URL might not be // exactly the same as what was requested. txt += items.join(', '); if (results) { cache[results.hash] = txt; } tooltip._node.innerHTML = txt; } /** * Populate the tooltip with tags relevant to the URL. */ function onRenderTooltip(url){ var hash; //URL for asking delicious for information about this URL is of form: //http://feeds.delicious.com/v2/json/urlinfo/{md5 of url}" if(url.substr(0, 7) != 'http://') url = 'http://' + url; if(url.substr(url.length - 1) != '/') url = url + '/'; hash = md5(url); //Use the cached tags if we have them, otherwise we'll query delicious. if(cache[hash]){ tooltip._node.innerHTML = cache[hash]; return; } // We'll use YQL tables to access delicious feeds since most // sites do not allow direct access across domains. url = 'http://feeds.delicious.com/v2/json/urlinfo/' + hash; openmail.Application.callWebService( { url: protocol + "query.yahooapis.com/v1/public/yql", method: "GET", parameters: { q : 'select * from json where url="'+url+'"', env: 'store://datatables.org/alltableswithkeys', format: 'json', diagnostics: false, callback: '' } }, handleTags); } /** * Display delicious tags when mouseover link. */ function onMouseoverLink(evt) { var node = evt.target; // Reposition the tooltip var r = node.get('region'); tooltip.setStyles({ top: r.bottom, left: r.left, display: 'block' }); tooltip._node.innerHTML = 'Loading...'; onRenderTooltip(node._node.innerHTML) } //add intro text Y.one('.msg-body').prepend('<div class="delicious-intro">Delicious-ified! [hover on any URL.]<br/><br/></div>'); //make all anchor tags have 'delicious-link' class Y.all('.msg-body a').each(function(node){ node.addClass('delicious-link'); }); //create our tooltip (just a floating div) var tooltip = Y.Node.create('<div id="tastytooltip" class="delicious-tastymenu"></div>'); Y.one('body').append(tooltip); //hook up UI tooltip.on('mouseout', function() { tooltip.setStyle('display', 'none'); } ); Y.on('mouseover', onMouseoverLink, 'a.delicious-link'); }); // YUI.use } // enhance //listen to afterRender event openmail.Application.setListener({event:'message.read'}, { afterRender: enhance });
function enhance(args) { args.YUI({ doc: args.view.doc, modules: { 'gallery-crypto-md5': { // pull in local gallery for use over ssl fullpath: '/yahoo/mail/assets/' + 'gallery-crypto/gallery-crypto-md5-min.js' } } }).use('gallery-crypto-md5', 'node', 'json-parse', function(Y) { var md5 = Y.Crypto.MD5; var tooltip; var cache = []; var protocol = openmail.Application.isSSL && openmail.Application.isSSL() ? 'https://' : 'http://'; /** * Handle the delicious web service response to our query for a URL's tags. */ function handleTags(response) { var items = []; var results = response.data && Y.JSON.parse(response.data); var txt = 'delicious tags: '; if (results && results.query && results.query.results) { results = results.query.results.json; for(var tag in results.top_tags){ items.push(tag); } if (results.title) { txt = '<b>' + results.title + '</b><br/>' + txt; } } else { txt = ''; items.push('<div class="error">an error or no results found for url.</div>'); } // Cache web service responses by the md5 hash of the URL. // Using the md5 hash for the key since the normalized URL might not be // exactly the same as what was requested. txt += items.join(', '); if (results) { cache[results.hash] = txt; } tooltip._node.innerHTML = txt; } /** * Populate the tooltip with tags relevant to the URL. */ function onRenderTooltip(url){ var hash; //URL for asking delicious for information about this URL is of form: //http://feeds.delicious.com/v2/json/urlinfo/{md5 of url}" if(url.substr(0, 7) != 'http://') url = 'http://' + url; if(url.substr(url.length - 1) != '/') url = url + '/'; hash = md5(url); //Use the cached tags if we have them, otherwise we'll query delicious. if(cache[hash]){ tooltip._node.innerHTML = cache[hash]; return; } // We'll use YQL tables to access delicious feeds since most // sites do not allow direct access across domains. url = 'http://feeds.delicious.com/v2/json/urlinfo/' + hash; openmail.Application.callWebService( { url: protocol + "query.yahooapis.com/v1/public/yql", method: "GET", parameters: { q : 'select * from json where url="'+url+'"', env: 'store://datatables.org/alltableswithkeys', format: 'json', diagnostics: false, callback: '' } }, handleTags); } /** * Display delicious tags when mouseover link. */ function onMouseoverLink(evt) { var node = evt.target; // Reposition the tooltip var r = node.get('region'); tooltip.setStyles({ top: r.bottom, left: r.left, display: 'block' }); tooltip._node.innerHTML = 'Loading...'; onRenderTooltip(node._node.innerHTML) } //add intro text Y.one('.msg-body').prepend('<div class="delicious-intro">Delicious-ified! [hover on any URL.]<br/><br/></div>'); //make all anchor tags have 'delicious-link' class Y.all('.msg-body a').each(function(node){ node.addClass('delicious-link'); }); //create our tooltip (just a floating div) var tooltip = Y.Node.create('<div id="tastytooltip" class="delicious-tastymenu"></div>'); Y.one('body').append(tooltip); //hook up UI tooltip.on('mouseout', function() { tooltip.setStyle('display', 'none'); } ); Y.on('mouseover', onMouseoverLink, 'a.delicious-link'); }); // YUI.use } // enhance //listen to afterRender event openmail.Application.setListener({event:'message.read'}, { afterRender: enhance });
You can get the complete source for the Delicious app we just built by running ymdt get -S 'Delicious'.