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.

  1. > ymdt create delicious -S 'Delicious Skeleton'
  2. Yahoo! Mail Development Tool Version 1.11
  3. Developer server: developer.mail.yahoo.com
  4. create:
  5. Getting sample 'Delicious Skeleton' into delicious:
  6. changed appid to 6ce36cpgmacpi.
  7. downloading delicious/config.xml. . .
  8. downloading delicious/auth.xml. . .
  9. downloading delicious/views/about.html. . .
  10. downloading delicious/assets/enhance.css. . .
  11. downloading delicious/assets/enhance.js. . .
  12. Done all gets for delicious.
  13. Application '38d1kcgsm4d9g' created.
  14. uploading delicious/skeleton/auth.xml. . .
  15. uploading delicious/skeleton/config.xml. . .
  16. uploading delicious/assets/enhance.css. . .
  17. uploading delicious/assets/enhance.js. . .
  18. uploading delicious/skeleton/views/about.html. . .
  19. 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 gear icon 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.

  1. > ymdt put delicious/config.xml
  2. Yahoo! Mail Development Tool Version 1.11
  3. Developer server: developer.mail.yahoo.com
  4. put:
  5. 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 refresh icon 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:

  1. function enhance(args) {
  2. args.YUI({
  3. doc: args.view.doc
  4. }).use('node', function(Y) {
  5.  
  6. var tooltip;
  7.  
  8. /**
  9.   * Display delicious tags when mouseover link.
  10.   */
  11. function onMouseoverLink(evt) {
  12. var node = evt.target;
  13.  
  14. // Reposition the tooltip
  15. var r = node.get('region');
  16. tooltip.setStyles({
  17. top: r.bottom,
  18. left: r.left,
  19. display: 'block'
  20. });
  21.  
  22. tooltip._node.innerHTML = 'standby for true flavah!';
  23. }
  24.  
  25. //add intro text
  26. Y.one('.msg-body').prepend('<div class="delicious-intro">Delicious-ified! [hover on any URL.]<br/><br/></div>');
  27.  
  28. //make all anchor tags have 'delicious-link' class
  29. Y.all('.msg-body a').each(function(node){
  30. node.addClass('delicious-link');
  31. });
  32.  
  33. //create our tooltip (just a floating div)
  34. var tooltip = Y.Node.create('<div id="tastytooltip" class="delicious-tastymenu"></div>');
  35. Y.one('body').append(tooltip);
  36.  
  37. //hook up UI
  38. tooltip.on('mouseout', function() { tooltip.setStyle('display', 'none'); } );
  39. Y.on('mouseover', onMouseoverLink, 'a.delicious-link');
  40.  
  41. }); // YUI.use
  42. } // enhance
  43.  
  44. //listen to afterRender event
  45. openmail.Application.setListener({event:'message.read'}, {
  46. afterRender: enhance
  47. });
  48.  
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.

  1. .delicious-intro {
  2. color: red;
  3. }
  4.  
  5. .delicious-link {
  6. color: purple;
  7. }
  8.  
  9. .delicious-tastymenu {
  10. display: none;
  11. background: #3274D0;
  12. color: #fff;
  13. position: absolute;
  14. padding: 10px;
  15. }
  16.  
.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%20j
POST http://api.flickr.com/services/rest?api_key=1234
postBody: function=flickr.photos.search&tags=pb%20%26%20j
Delicious has a 'urlinfo' web feed you can use to get summary information about a particular URL, including tags for that URL. For example, this link will return json about http://sports.yahoo.com/. Let's use the Application.callWebService API and that delicious web feed to fetch tags for links in our email body. Shown below, enhance.js is modified to populate the URL tooltips with tags fetched from Delicious.

  1. function enhance(args) {
  2. args.YUI({
  3. doc: args.view.doc,
  4. modules: {
  5. 'gallery-crypto-md5': {
  6. // pull in local gallery for use over ssl
  7. fullpath: '/yahoo/mail/assets/' +
  8. 'gallery-crypto/gallery-crypto-md5-min.js'
  9. }
  10. }
  11. }).use('gallery-crypto-md5', 'node', 'json-parse', function(Y) {
  12.  
  13. var md5 = Y.Crypto.MD5;
  14. var tooltip;
  15. var cache = [];
  16. var protocol = openmail.Application.isSSL && openmail.Application.isSSL() ? 'https://' : 'http://';
  17.  
  18. /**
  19.   * Handle the delicious web service response to our query for a URL's tags.
  20.   */
  21. function handleTags(response) {
  22. var items = [];
  23. var results = response.data && Y.JSON.parse(response.data);
  24. var txt = 'delicious tags: ';
  25.  
  26. if (results && results.query && results.query.results) {
  27. results = results.query.results.json;
  28. for(var tag in results.top_tags){
  29. items.push(tag);
  30. }
  31. if (results.title) {
  32. txt = '<b>' + results.title + '</b><br/>' + txt;
  33. }
  34. } else {
  35. txt = '';
  36. items.push('<div class="error">an error or no results found for url.</div>');
  37. }
  38.  
  39.  
  40. // Cache web service responses by the md5 hash of the URL.
  41. // Using the md5 hash for the key since the normalized URL might not be
  42. // exactly the same as what was requested.
  43. txt += items.join(', ');
  44. if (results) {
  45. cache[results.hash] = txt;
  46. }
  47.  
  48. tooltip._node.innerHTML = txt;
  49. }
  50.  
  51. /**
  52.   * Populate the tooltip with tags relevant to the URL.
  53.   */
  54. function onRenderTooltip(url){
  55. var hash;
  56.  
  57. //URL for asking delicious for information about this URL is of form:
  58. //http://feeds.delicious.com/v2/json/urlinfo/{md5 of url}"
  59. if(url.substr(0, 7) != 'http://')
  60. url = 'http://' + url;
  61. if(url.substr(url.length - 1) != '/')
  62. url = url + '/';
  63.  
  64. hash = md5(url);
  65.  
  66. //Use the cached tags if we have them, otherwise we'll query delicious.
  67. if(cache[hash]){
  68. tooltip._node.innerHTML = cache[hash];
  69. return;
  70. }
  71.  
  72. // We'll use YQL tables to access delicious feeds since most
  73. // sites do not allow direct access across domains.
  74. url = 'http://feeds.delicious.com/v2/json/urlinfo/' + hash;
  75. openmail.Application.callWebService( {
  76. url: protocol + "query.yahooapis.com/v1/public/yql",
  77. method: "GET",
  78. parameters: {
  79. q : 'select * from json where url="'+url+'"',
  80. env: 'store://datatables.org/alltableswithkeys',
  81. format: 'json',
  82. diagnostics: false,
  83. callback: ''
  84. }
  85. }, handleTags);
  86. }
  87.  
  88. /**
  89.   * Display delicious tags when mouseover link.
  90.   */
  91. function onMouseoverLink(evt) {
  92. var node = evt.target;
  93.  
  94. // Reposition the tooltip
  95. var r = node.get('region');
  96. tooltip.setStyles({
  97. top: r.bottom,
  98. left: r.left,
  99. display: 'block'
  100. });
  101.  
  102. tooltip._node.innerHTML = 'Loading...';
  103. onRenderTooltip(node._node.innerHTML)
  104. }
  105.  
  106. //add intro text
  107. Y.one('.msg-body').prepend('<div class="delicious-intro">Delicious-ified! [hover on any URL.]<br/><br/></div>');
  108.  
  109. //make all anchor tags have 'delicious-link' class
  110. Y.all('.msg-body a').each(function(node){
  111. node.addClass('delicious-link');
  112. });
  113.  
  114. //create our tooltip (just a floating div)
  115. var tooltip = Y.Node.create('<div id="tastytooltip" class="delicious-tastymenu"></div>');
  116. Y.one('body').append(tooltip);
  117.  
  118. //hook up UI
  119. tooltip.on('mouseout', function() { tooltip.setStyle('display', 'none'); } );
  120. Y.on('mouseover', onMouseoverLink, 'a.delicious-link');
  121.  
  122. }); // YUI.use
  123. } // enhance
  124.  
  125. //listen to afterRender event
  126. openmail.Application.setListener({event:'message.read'}, {
  127. afterRender: enhance
  128. });
  129.  
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'.