Mail Application Development Platform API Reference

/**
 * This JavaScript API for Yahoo! Mail Applications is currently available to application developers.  We expect this API list to grow substantially over time.<br />
 * <br /><br />
 * <b>General Information</b>
 * <ul class="bullet">
 * <li>Many code samples are taken from the "Hello World" and "Saved Searches"
 * applications. These applications can be found in the Application Gallery,
 * where you can add them and view the source code.</li>
 * <li>Very few API calls are synchronous.  When data is returned, it's returned through a handler function.</li>
 * </ul>
 * <b>Methods and Response Objects</b>
 * <ul class="bullet">
 * <li>
 *    This is the most common signature for API methods:
 *    <code>
 *    <pre>
 *        errorCode:int APICall(args:object, handler:function)
 *    </pre>
 *    </code>
 *        <br/><b>Note:</b> Some calls don't take arguments, some don't take a handler, and some don't take either.
 * </li>
 * <li>
 *    Handlers always have the following signature:
 *    <code>
 *    <pre>
 *        void handler(response:object)
 *    </pre>
 *    </code>
 * </li>
 * <li>
 *    The possible members for the response object are listed below. The
 *    response object, however, can only have either the error member or the
 *    warning member. If an error occurs, the response object will probably not
 *    have a data object.
 * <br />
 *    <pre>
 *        {
 *        <pre>
 *            error:int,
 *            warning:int,
 *            errorMsg:String,
 *            errorObj:Object,
 *            data:Object
 *        </pre>
 *        }
 * </pre>
 *    <ul>
 *        <li>
 *            If the call succeeds, <code>error</code> will be absent or
 *            <code>ERR_NONE</code>.  <code>ERR_NONE</code> is defined as 0, so
 *            success is indicated by <code>!error</code>.
 *        </li>
 *        <li>
 *            General error codes are defined in the class <code>openmail</code>.  Individual classes can define their own specific error codes.
 *        </li>
 *        <li>
 *            An error code can be returned in <code>warning</code> if the call
 *            succeeded but had some issue.  Only one of either
 *            <code>error</code> or <code>warning</code>
 *            can be present.  If <code>warning</code> is present,
 *            <code>errorMsg</code> and <code>errorObj</code> may be present to provide additional information
 *            about the warning.
 *        </li>
 *        <li>
 *            <code>errorMsg</code> is an informational message intended for debugging only.
 *        </li>
 *        <li>
 *            <code>errorObj</code> may contain rich error information defined by the individual method that was called.
 *        </li>
 *        <li>
 *            <code>data</code> contains the data returned by the individual
 *            method that was called.  If <code>data</code> is present, it is guaranteed to be an
 *            object.  The members of the object are defined by the individual method called.
 *        </li>
 *        <li>
 *            Additional members can be defined by the method called.
 *        </li>
 *    </ul>
 * </li>
 * </ul>
 * <b>General Error Information</b>
 * <ul class="bullet">
 * <li>
 *    The data type for all error codes is <code>int</code> because errors are
 *    actually enumerations and not numbers. 
 * </li>
 * <li>
 *    Only simple errors can be detected and returned synchronously.  Expect most errors to be communicated through the handler.
 * </li>
 * <li>
 *    Any error which is returned synchronously will also be passed to the handler.  The handler will most likely be called before
 *  the function returns.
 * </li>
 * </ul>
 * <b>API Versions and Backward Compatability</b>
 * <ul class="bullet">
 * <li>
 *    We will strive to maintain backward compatibility.  Methods may be
 *    extended (an <code>args</code> object may accept additional data, or
 *    <code>response.data</code>
 *  may return additional information), but calls made against an older version of the API will continue to work.
 * </li>
 * <li>
 *    In the cases where we are unable to maintain backward compatibility,
 *    deprecated methods will remain in the API but will return
 *    <code>ERR_DELETED</code>.
 * </li>
 * <li>
 *    You can figure out the API version number by the included JS URL. For example, the 
 *    version number of the following URL is 0.1.2: http://redirect.corp.yahoo.com/?url=http://mail.yimg.com/a/lib/om/om_api_public/0.1.2/om_api_public.js.
 * </li>
 * </ul>
 * @module openmail
 * @requires dom, event, json, crossframe
 */

if (typeof(YAHOO) == 'undefined')
    YAHOO = { };

/**
 * All API methods are in following namespaces: <code>Application</code>,
 * <code>AuthService</code>, <code>Calendar</code>, and <code>Mail</code>.
 * The <code>openmail</code> class only contains general error codes.  Namespaces
 * can define their own specific error codes in these ranges:<br /><br />
 * <table border="1" frame="box" rules="groups">
 * <colgroup span="2" width="100"></colgroup>
 * <thead>
 *    <tr><th>Namespace   </th><th> Range Start </th>
 * </thead>
 * <tr><td>General     </td><td> 0             </td>
 * <tr><td>Application </td><td> 1000        </td>
 * <tr><td>Mail        </td><td> 2000        </td>
 * <tr><td>AuthService </td><td> 3000        </td>
 * <tr><td>Calendar    </td><td> 4000        </td>
 * </table>
 *
 * @class openmail
 */
YAHOO.openmail = openmail = function()
{
    /**
     * Current version of the API
     * @property apiVersion
     * @private
     */
    var apiVersion  = "@BUILD_NUM@";

    /**
     * @property xdmProxyVer
     * @private
     */
    var xdmProxyVer = window.location.href.match( /bn=([^&#]*)/i );

    /*
     * @property proxyHost
     * @private
     */
    var proxyHost   = window.location.href.match( /proxyHost=([^&#]*)/i );

    /*
     * @property appSig
     * @private
     */
    var appSig      = window.location.href.match( /sig=([^&#]*)/i )[1];

    /*
     * @property proxy
     * @private
     */
    var proxy       = proxyHost ? ("http://" + proxyHost[1] + "/om/xdm_proxy?" + xdmProxyVer[0] ) : null;

    /*
     * @property handlerMap
     * @private
     */
    var handlerMap  = {};

    /*
     * @property id_counter
     * @private
     */
    var id_counter  = 0;

    YAHOO.yap.Iframe.listen({
                                pattern: /^cmd=(.*)$/,
                                handler: receiveHandler,
                                filter: new RegExp("^http://" + proxyHost[1]),
								sig: appSig
                            });

    /*
     * Register a callback function
     * @method registerHandler
     * @private
     */
    function registerHandler(handler)
    {
        var id = "z" + Math.floor(Math.random()*1e9) + "_" + (id_counter++);
        handlerMap[id] = handler;
        return id;
    }

    /*
     * Process a message received from the host application
     * @method receiveHandler
     * @private
     */
    function receiveHandler(iframe, cmd)
    {
        var args = YAHOO.lang.JSON.parse(decodeURIComponent(cmd));
        var id = args.$cbid;
        var f = handlerMap[id];
        if (!f) {
            return; // no handlers setup for this message
        }

        if (args.reason == "data/message") {
            if (args.data && args.data.date) {
                var d = new Date(args.data.date);
                args.data.date = d;
            }
        }

        // event listeners can emit multiple callbacks, so $keep them around.
        if (!args.$keep) {
            delete handlerMap[id];
            delete args.$cbid;
            delete args.$apiVersion;
        }

        f(args);
    }

    /*
     * @method escapeUnicodeChar
     * @private
     */
    var escapeUnicodeChar = (function() {
        // These chars are additional to what YUI 2 JSON replaces
        var chars = /[\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
        var cache = {};

        return function(s) {
            return s.replace(chars, function(c) {
                return !cache[c] ? cache[c] = '\\u' + ('0000' + (+(c.charCodeAt(0))).toString(16)).slice(-4) : cache[c];
            });
        };
    })();

    /*
     * Send a message to the host application
     * @method send
     * @private
     */
    function send(cmd, args, handler)
    {
        if (proxy)        // (else, we don't know where to send the command)
        {
            args.$cmd = cmd;
            if (handler) {
                args.$cbid = registerHandler(handler);
            }

            args.$apiVersion = apiVersion;
            YAHOO.yap.Iframe.send(
                proxy,
                "parent",
                "cmd="+encodeURIComponent(escapeUnicodeChar(YAHOO.lang.JSON.stringify(args))),
                appSig
            );
        }
    }

    // This object gets returned & assigned to 'openmail' - this is the openmail API object the user gets.
    var omApi =
    {
        /**
         * ERR_NONE will be zero for this version and future versions.
         *
         * @property ERR_NONE
         * @static
         * @type int
         */
         ERR_NONE:    0,

        /**
         * The arguments to the function were bad.  If <code>ERR_BAD_ARG</code> is returned synchronously, it indicates that a sanity
         * check on the arguments failed and the function wasn't called (the handler will still be called with
         * <code>{ error: ERR_BAD_ARG }</code>, and possibly additional information).
         * When <code>ERR_BAD_ARG</code> is received by a handler, it indicates
         * either a warning or an error.
         * @property ERR_BAD_ARG
         * @static
         * @type int
         */
         ERR_BAD_ARG: 1,

        /**
         * The method has been deprecated.  This is a warning, never an error.  If this is passed to a handler,
         * <code>errorMsg</code> will contain useful information about the action you should take.
         *
         * @property ERR_DEPRECATED
         * @static
         * @type int
         */
         ERR_DEPRECATED: 2,

        /**
         * The method has been deleted.  This is always an error.  When passed to the handler, <code>errorMsg</code> may
         * contain information for debugging.
         */
         ERR_DELETED: 3,

        /**
         * A cross-frame communication failure occurred.
         */
         ERR_XFRAME: 4
    };

    /*
     * Do some simple validation on a call's arguments
     * @method validate
     * @private
     */
    function validate(args, constraints, handler)
    {
        var e;
        var n;
        if (constraints) {
            n = "'" + (constraints.name||"args") + "'";
        }

        // Passing a handler that's not a function is a pretty basic mistake.  We could define a
        // special error code for this, but it doesn't seem worthwhile.
        if (handler && (typeof handler != "function")) {
            return omApi.ERR_BAD_ARG;
        }

        if (!args) {
            if (!constraints)        // No constraints means no args required
                return;

            e = { error: omApi.ERR_BAD_ARG, errorMsg: n + " is required" };
        }
        else {
            if (typeof args != "object") {
                e = { error: omApi.ERR_BAD_ARG, errorMsg: n + " must be an object" };
            }
            else if (constraints && constraints.required) {
                var r = constraints.required;
                for (var i = 0; i < r.length; i++) {
                    if (typeof args[r[i]] == "undefined") {
                        e = { error: omApi.ERR_BAD_ARG, errorMsg: "Missing required member '" + r[i] + "'" };
                        break;
                    }
                }
            }
        }

        if (e)
        {
            if (handler) {
                handler(e);
            }

            return e.error;
        }
    }

    /**
     * Openmail functions that are related to the host application (Yahoo! Mail).
     * @class Mail
     */
    omApi.Mail =
    {
		// Private method - not documented for jsdoc.
		// Uninstall an application.  Used by the gallery to tell CG to run the app's uninstall action.
		uninstall: function(app, handler)			{ send("uninstall", app, handler); },

        /**
         * This method is the strict equivalent of having the user type the query in the search
         * box, then pressing "go". The search will open in a new tab.
         * <br /><br />
         *    <b>Sample Use:</b>
         * <br /><br />
         *    As used by the "Saved Searches" sample application.
         * <br />
         * <code>
         * <pre>
         *  var state = [
         * <pre>
         *      {label:"Sample 1", query:"test"},
         *      {label:"Sample 2", query:"openmail"}
         * </pre>
         *  ];
         *
         *  function buttonClick(btn)
         *  {
         * <pre>
         *      var a = btn.id.split("_");
         *      var action=a[0], index=a[1];
         *      switch (action)
         *      {
         * <pre>
         *          case "run":
         * <pre>
         *              openmail.Mail.search(state[index].query);
         *              break;
         * </pre></pre></pre>
         * ...
         * </pre>
         * </code>
         * @param { Object } query
         * <br />
         * <dd>
         *    <code>query</code> includes the following member:
         *    <dl><dd>
         *        <code>query: String </code>The query to execute (<b>Required</b>) <br />
         *    </dd></dl>
         * </dd>
         * <br />
         * @return { int }   An error code.
         * @method search
         */
        search:                 function(query) {
            var e = validate(query, { name: "query", required: ["query"] });
            if (e) {
                return e;
            }

            send("search", query );
        },

        /**
         * This method opens a compose tab. Every field is optional. The "body" field
         * accepts both plain text and HTML. If it's HTML, the <code>html</code> flag should be set to true.
         * <br /><br />
         * HTML content is processed by a Web service to avoid XSS vulnerabilities.
         * <br /><br />
         *  <b>Sample Use:</b>
         * <br /><br />
         *    As used by the "Hello World" sample application.
         * <br />
         * <code>
         * <pre>
         * ...
         * Compose an email to joe:
         * &lt;input id="msgbody">
         * &lt;button onclick="openmail.Mail.compose({to: 'joe@joe.joe', subject: 'hi joe',
         * <pre>
         *                                            body: document.getElementById('msgbody').value})">
         * </pre>
         *    Compose
         * &lt;/button>
         * ...
         * </pre>
         * </code>
         * @param {Object} msg  Optional object used to pre-populate the new message.
         *
         * <p><br/>
         * <code>msg</code> may include the following:<br />
         *        <code>to: String </code>A comma-separated list of valid email addresses <br />
         *        <code>subject: String </code>An email subject  <br />
         *        <code>body: String </code>A plain-text or HTML email body  <br />
         *        <code>cc: String </code>A comma-separated list of valid email addresses  <br />
         *        <code>bcc: String </code>A comma-separated list of valid email addresses  <br />
         *        <code>html: boolean </code>Set to true if the body is HTML, false if the body is plain text<br />
         *
         * </p>
         * @return { int }   An error code.
         * @method compose
         */
        compose:             function(msg) {
            var e = validate(msg);
            if (e) {
                return e;
            }

            send("compose", msg);
        },

        /**
         * Requests the mail container to refetch the list of applications
         * and redraw the application folders.
         *
         * @return { int }   An error code.
         * @method refreshApplications
         */
        refreshApplications: function(handler)    { send("refreshApplications", {}, handler); }
    };

    /**
     * Openmail functions that are related to the openmail application
     * @class Application
     */
    omApi.Application = {

        /**
         * This method opens a new view for the current application.
         * <br /><br />
         * <b>Sample Use:</b>
         * <br /><br />
         * As used in the "Hello World" sample application.
         * <br /><br />
         * Open another tab with different content:
         * <code>
         * <pre>
         * ...
         * &lt;button onclick='openmail.Application.openView({
         * <pre><pre>
         *         id:"otherTab", view:"full", target:"tab", title:"Y! Groups"
         * </pre></pre>
         * })'>
         * <pre>
         *      Open Tab
         * </pre>
         * &lt;/button>
         * </pre>
         * Open a dialog:
         * <pre>
         * &lt;button onclick='openmail.Application.openView({
         * <pre><pre>
         *            id:"otherDialog", view:"full", target:"dialog", title:"Bunny!"
         * </pre></pre>
         * })'>
         * <pre>
         * Open Dialog
         * </pre>
         * &lt;/button>
         * ...
         * </pre>
         * </code>
         * @param { Object } view
         * <br />
         * <dd>
         *    <code>view</code> may include the following:
         *    <dl><dd>
         *        <code>id: String </code>         An application-defined id used to reference the view in other API calls <br />
         *      <code>view: String </code>         The name of a view to load (e.g. "full" ) as created in the Application Editor. <br />
         *      <code>target: String </code>       A valid target for the view to load that supports the following views: "tab", "dialog", and "hidden". <br />
         *      <code>parameters: Object </code>   A map of values available to the loaded view through the <code>getParameters()</code> API call. <br />
         *      <code>context: Object </code>      A value specifying the context of the opened view that can be retrieved through the
         *      <code>getParameters()</code> API call. <br />
         *      <code>width: Number </code>        "Dialog" view only. The default width of the dialog view is 400 px. <br />
         *      <code>height: Number </code>       "Dialog" view only. The default height of the dialog view is 400 px. <br />
         *      <code>ttl: Number </code>          "Hidden" view only. The "time to live" of the view in seconds. The view will be forcibly
         * closed afterward. A <code>ttl</code> of zero means the view will never be forcibly closed.
         * The default value for <code>ttl</code> is 60 seconds. <br />
         *      <code>title: String </code>        "Tab" and "dialog" only. The title is shown in the tab or dialog header area. <br />
         *    </dd></dl>
         * </dd>
         * <br />
         * @return { int }   An error code.
         * @method openView
         */
        openView:            function(view) {
            var e = validate(view, { name:"view" });
            if (e) {
                return e;
            }

            // Set default values if they're not present
            view.width  = view.width  || 400;
            view.height = view.height || 400;
            view.ttl    = view.ttl    || 60;
            view.title  = view.title  || null;

            send("openView", view);
        },

        /**
         * Closes a currently opened view. To close the view initiating the
         * call, pass <code>null</code> for <code>view</code>.
         * <br /><br />
         * <b>Sample Use:</b>
         * <br /><br />
         * Open another tab with different content:
         * <code>
         * <pre>
         * ...
         * &lt;button onclick='openmail.Application.openView({
         * <pre><pre>
         * id:"otherTab", view:"full", target:"tab", title:"Y! Groups"
         * </pre></pre>
         * })'>
         * <pre>
         * Open Tab
         * </pre>
         * &lt;/button><br />
         * </pre>
         * Open a dialog:
         * <pre>
         * &lt;button onclick='openmail.Application.openView({
         * <pre><pre>
         * id:"otherDialog", view:"full", target:"dialog", title:"Bunny!"
         * </pre></pre>
         * })'>
         * <pre>
         * Open Dialog
         * </pre>
         * &lt;/button>
         * ...
         * &lt;button onclick='openmail.Application.closeView({ id: "otherTab" })'>
         * <pre>
         * Close the tab
         * </pre>
         * &lt;/button>
         * &lt;button onclick='openmail.Application.closeView({ id: "otherDialog" })'>
         * <pre>
         * Close the Dialog
         * </pre>
         * &lt;/button>&lt;br>
         * ...
         * </pre>
         * </code>
         * @param { Object | null } view
         * <p><br />
         * If <code>view</code> is not <code>null</code>, it must be an object that includes the following:<br />
         *        <code>id: String </code> The application-defined id passed
         *        to the <code>openView()</code> call (<b>Required</b>)<br />
         * @return { int }   An error code.
         * @method closeView
         */
        closeView: function(view) {
            view = view || { id: "" };

            var e = validate(view, { name:"view", required: ["id"] });
            if (e) {
                return e;
            }

            send("closeView", view);
        },

        /**
         * Retrieves properties associated with the view that invoked the function.  These properties include
         * the user, the reason the view was launched, application-defined view context, and action-dependent data.
         *
         * If the view was opened by a call to <code>openView()</code>, the data property is the same as the
         * <code>parameters</code> argument passed to <code>openView()</code>.  For example, in the
         * "Hello World" example application, if an email message is dragged and dropped onto the application, then
         * the data is the JSON representation of the email message.
         * <br /><br />
         * <b>Sample Use:</b>
         * <br /><br />
         * Code for this view includes a button that opens another instance of itself, with
         * application-defined context and additional parameters.  The view's load handler uses
         * <code>getParameters()</code> to retrieve the <code>context</code> and <code>parameters</code>
         * arguments originally passed to <code>openView()</code>.
         * <code>
         * <pre>
         * &lt;body>
         * ...
         * &lt;button onclick='openmail.Application.openView({
         * <pre>
         *        id:"otherDialog", view:"full", target:"dialog", title:"Bunny!",
         *        context:"button-launched view",
         *        parameters:{ my: 1; custom: "JSON"; stuff:null }
         * </pre>
         * })'>
         * <pre>
         * Open Dialog
         * </pre>
         * &lt;/button>
         * &lt;/body>
         * &lt;script>
         *
         * <pre>
         * ...
         * //load handler
         * function refresh() {
         * <pre>
         *   openmail.Application.getParameters(function(args){
         *       // if view opened by button above:
         *       //    args.context is "button-launched view"
         *       //    args.data is { my: 1; custom: "JSON"; stuff:null }
         *
         *       var ta = document.createElement("textarea");
         *       ta.value = YAHOO.lang.JSON.stringify(args);
         *       ta.style.width="100%";
         *       ta.style.height="100px";
         *       document.getElementById("params").appendChild(ta);
         * </pre>
         *   });
         * </pre>
         * }
         * ...
         * </pre>
         * &lt;/script>
         * </pre>
         * </code>
         * @param { function } handler  The callback function that receives the response.<br />
         * <p> <br />
         * @return { int }   An error code.
         * </dl><br/>
         * <dl><dt>Callback: <code>response &lt; Object &gt;</code></dt>
         *    <dd>
         *        <code>response.user</code> is the user object.  The user object has three members:
         *              <dl><dd>
         *                  <code>guid</code> unique user identifier as string, not human-friendly. <br />
         *                  <code>intl</code> internationalization domain (e.g. "us") 
	 *                  <br />
         *                  <code>language</code> user's language (e.g. "eng-us") 
	 *                  <br />
         *              </dd></dl>
         *        <br />
         *        The remaining response members depend on the user action. <br /><br />
         *        For a message drag-and-drop action, they are:  <br />
         *    <dl><dd>
         *       <code>response.context</code>: the value of the <code>context</code> node in the configuration XML of the application <br />
         *       <code>response.data</code>: message JSON object  <br />
         *       <code>response.reason</code>: "data/message" <br />
         *    </dd></dl><br />
         * For click action:  <br />
         *    <dl><dd>
         *       <code>response.context</code>: the value of the <code>context</code> node in the configuration XML of the application  <br />
         *       <code>response.data</code>: <code>null</code>  <br />
         *       <code>response.reason</code>: "events/launch" <br />
         *    </dd></dl><br />
         *
         * For <code>openView()</code> call:  <br />
         *    <dl><dd>
         *       <code>response.context</code>: the value passed to <code>context</code> argument of <code>openView()</code>  <br />
         *       <code>response.data</code>: the value passed to <code>parameters</code> argument of <code>openView()</code>  <br />
         *       <code>response.reason</code>: "api/openView" <br />
         *    </dd></dl>
         * </dd>
         * @method getParameters
         */
        getParameters:        function(handler) {
            send("getParameters", {}, handler);
        },

        /**
         * This method allows you to customize the appearance of an application's folder, including changing its tooltip, count and subfolders, or extending its label. The root label provided in this configuration is appended to the default label. The default label is the application's name. The subfolder labels are defined in the <code>config</code> object.
         * <br /><br />
         * Short of giving a full specification of the JSON format accepted for <code>config</code>, here's an example: <br />
         * <code>
         * <pre>
         * {
         * <pre>
         *     label: " - Beta",
         *     tooltip: "click me",
         *     subFolders: [{
         * <pre>
         *         label: "eden_people",
         *         tooltip: "sub1 tt",
         * </pre>
         *     },
         *     {
         * <pre>
         *         label: "flexcoders",
         *         tooltip: "sub2 tt",
         * </pre>
         *     },
         *     {
         * <pre>
         *         label: "shoop da woop",
         *         tooltip: "blah"
         * </pre>
         *     }]
         * </pre>
         * }
         * </pre>
         * <b>Sample Use:</b>
         * <br /><br />
         * In this excerpt from the "Saved Searches" sample application, each saved search is set up as a separate subfolder.
         * <br />
         * <pre>
         * ...
         * &lt;script>
         * <pre>
         * var state = [
         * <pre>
         *   {label:"Sample 1", query:"test"},
         *   {label:"Sample 2", query:"openmail"}
         * </pre>
         * ];
         * ...
         * function updateFolders(){
         * <pre>
         *   var obj = {
         * <pre>
         *     subFolders:[]
         * </pre>
         *   }
         *   for (var i=0;i&lt;state.length;i++) {
         * <pre>
         *     var s = state[i];
         *     obj.subFolders.push(s);
         * </pre>
         *   }
         *   openmail.Application.setFolderConfig(obj);
         * </pre>
         * }
         * </pre>
         * &lt;/script>
         * ...
         * </pre>
         * </code>
         * @param { Object } config   An object that describes the folder configuration.
         * <dd>
         * <code>config</code> can include the following:
         *    <dl><dd>
         *        <code>label: String </code>Text to be appended to the application's folder<br />
         *        <code>tooltip: String </code>Tooltip to display for the application<br />
         *        <code>subFolders: Array of Objects</code> An array of objects.
         *   <br /><br />Each object in the <code>subFolders</code> array can include the following:<br />
         *            <dl><dd>
         *                <code>label: String </code>Name of the subfolder (<b>Required</b>)<br />
         *                <code>tooltip: String </code>Tooltip for the subfolder<br />
         *            </dd></dl>
         *    </dd></dl>
         * </dd>
         * <br />
         * @return { int }   An error code.
         * @method setFolderConfig
         */
        setFolderConfig:    function(config) {
            var e = validate(config, { name:"config" });
            if (e) {
                return e;
            }

            send("setFolderConfig", config);
        },

        /**
         * Calls a 3rd-party Web service. This requires the 3rd-party
         * service to implement one of the cross-domain mechanisms supported by
         * Flash, such as a <code>crossdomain.xml</code> policy file. For
         * examples of <code>crossdomain.xml</code> policies, see Flickr or Digg.
         * <br /><br />
         * <b>Sample Use:</b>
         * <br /><br />
         * This code sample comes from the "Hello World" sample application.
         *
         * <pre>
         * ...
         * &lt;script>
         * <pre>
         * var diggURL = "http://services.digg.com/stories/diggs";
         * function loadDiggStuff() {
         * <pre>
         *     openmail.Application.callWebService( { url: diggURL , method: "GET",
         * <pre>
         *         parameters: {appkey:"http://apidoc.digg.com"} }, function(args) {
         * <pre>
         *         alert(["got data: ", args.data].join('')); 
         * </pre></pre>
         *     });  
         * </pre>
         * } 
         * </pre>
         *
         * &lt;/script>
         *
         * ...
         * &lt;button onclick='loadDiggStuff()'>Load some Digg API stuff&lt;/button>&lt;br>
         * ...
         * </pre>
         *
         * @param { Object } service        An object describing the Web service to call
         * @param { function } handler        A callback function <br />
         * <p><br />
         * <code>service</code> may include the following:<br />
         * <code>url: String </code>           Target URL<br />
         * <code>method: String </code>       "GET" or "POST" <br />
         * <code>parameters: Object </code>       A map of key/value pairs to use as the query string or the POST body <br />
         * </p>
         * @return { int }   An error code.
         * <br/><br/></dl>
         * <b>Callback:</b> <code>response</code> &lt; Object &gt;
         * <dd>
         *    On success: <code>response.data</code> contains the HTTP body returned by the Web service. <br />
         *    On failure: <code>response.error</code> contains an error message describing the failure. <br />
         *
         * </dd></dl>
         * @method callWebService
         */
        callWebService:        function(service, handler) {
            var e = validate(service, { name:"service", required: ["url"] }, handler);
            if (e) {
                return e;
            }

            send("callWebService", service, handler);
        },

        /**
         * Registers an event handler for a particular event. Only one handler will be
         * called for a given application event.
         * <br />
         * <br />
         * <b>Note:</b> Setting a handler for an event will override any previous handlers for that event.
         * <br /><br />
         * <b>Sample Use:</b>
         * <br /><br />
         * The "Saved Searches" example application uses <code>setListener()</code> to run a search for a subfolder.
         * <pre>
         * &lt;script>
         * <pre>
         * var state = [
         * <pre>
         *   {label:"Sample 1", query:"test"},
         *   {label:"Sample 2", query:"openmail"}
         * </pre>
         * ];
         * ...
         * function loadHandler() {
         * <pre>
         *   updateFolders();
         *   openmail.Application.setListener("subfolder.click", function(args) {
         * <pre>
         *     openmail.Mail.search(state[args.index].query);
         * </pre>
         *   });
         * </pre>
         * }
         * ...
         * </pre>
         * &lt;/script>
         * </pre>
         *
         * @param { Object } event
         * <dd>
         * <code>event</code> should include the following:
         *    <dl><dd>
         * <code>event: String </code> The name of the event to listen for. Currently, we only support "subfolder.click".
         *    </dd></dl>
         * </dd>
         * @param { function } handler    A callback function
         * <p><br />
         * @return { int }   An error code.
         * </dl><br/>
         * <dl><dt>Callback: <code>response &lt; Object &gt;</code></dt>
         *    <dd>
         *        When a subfolder is clicked on, an object will be passed to the callback.
         *        <dl><dd>
         *            <code>index: Number </code>    The index of the subfolder that was clicked on.
         *        </dd></dl>
         *    </dd>
         * @method setListener
         */
        setListener:        function(event, handler) {
            var e = validate(event, { name:"event", required: ["event"] }, handler);
            if (e) {
                return e;
            }

            send("setListener", event, handler);
        },

        /**
         * Saves key/value set in a per-application-per-user persistent store.
         * <br /><br />
         * <b>Sample Use:</b>
         * <br /><br />
         * This is how the "Saved Searches" example could use <code>setData()</code> to store the user's saved searches.
         * <pre>
         * &lt;script>
         * <pre>
         * ...
         * function buttonClick(btn) {
         * <pre>
         *   var a = btn.id.split("_");
         *   var action=a[0], index=a[1];
         *   switch (action) {
         * <pre>
         *     ...
         *     case "save":
         * <pre>
         *       state[index].label = $("labelinput_"+index).value;
         *       state[index].query = $("queryinput_"+index).value;
         *       stateStr = YAHOO.lang.JSON.stringify(state);
         *       openmail.Application.setData({ keys: { state: stateStr } });
         *       render();
         *       updateFolders();
         * </pre>
         *     ...
         *      case "delete":
         * <pre>
         *       if(confirm("Are you sure you want to delete this saved search?")) {
         * <pre>
         *         state.splice(index,1);
         *         stateStr = YAHOO.lang.JSON.stringify(state);
         *         openmail.Application.setData({ keys: { state: stateStr } });
         *         render();
         *         updateFolders();
         * </pre>
         *       }
         *       break;
         * </pre>
         *     case "create":
         * <pre>
         *       state.push({label:"New Search", query:"from:bob"});
         *       stateStr = YAHOO.lang.JSON.stringify(state);
         *       openmail.Application.setData({ keys: { state: stateStr} });
         *       render();
         *       updateFolders();
         *       break;
         * </pre></pre>
         *   }
         * </pre>
         * }
         * </pre>
         * &lt;/script>
         * ...
         * </pre>
         *
         * @param { Object } data        An object describing the data to set.
         * @param { function } handler    A callback function
         * <br />
         * <dd>
         * <code>data</code> can include the following:
         *    <dl><dd>
         *        <code>keys: Object </code>The key/value pairs to store (<b>Required</b>)<br />
         *        <code>ttl: Object </code>key/ttl pairs.  Member names should
         *        match the keys in <code>keys</code>.  The value is the time-to-live of that data.
         *    </dd></dl>
         * </dd>
         * <br />
         * @return { int }   An error code.
         * </dl><br/>
         * <dl><dt>Callback: <code>response &lt; Object &gt;</code></dt>
         *    <dd>
         *        An object containing the requested data.<br /><br />The object has
         *        the following members:<br />
         *        <dl><dd>
         *            <code>data: Object </code>The data requested.  Member names are the keys requested, values are the stored data.<br/>
         *            <code>version: Object </code>Version number of
         *            application instance. Members names are the keys
         *            requested, values are version numbers.<br/>
         *            <code>totalCount: Number </code>The number of items in the returned data set. <br/>
         *        </dd></dl>
         *    </dd>
         * @method setData
         */
        setData:            function(data, handler) {
            var e = validate(data, { name:"data", required: ["keys"] }, handler);
            if (e) {
                return e;
            }

            send("setData", data, handler);
        },

        /**
         * Retrieves stored data by key from the per-app-per-user persistent store.
         * <br /><br />
         * <b>Sample Use:</b>
         * <br /><br />
         * Here is how this version of <code>getData()</code> might be used in the "Saved Searches" example application to retrieve the user's stored searches.
         * <pre>
         * &lt;script>
         * <pre>
         * ...
         * var state = [
         * <pre>
         *   {label:"Sample 1", query:"test"},
         *   {label:"Sample 2", query:"openmail"}
         * </pre>
         * ];
         *
         * openmail.Application.getParameters(function(args){
         * <pre>
         *   openmail.Application.getData({ keys: ["state"] }, function(result){
         * <pre>
         *     var stateKeyStr = result.data["state"];
         *     var stateKey=null;
         *     try { stateKey = YAHOO.lang.JSON.parse(stateKeyStr); }
         *     catch(e) { alert("Failed to convert stored data to JSON");}
         *     state = stateKey||state;
         *     if (args.context == "load") {
         * <pre>
         *       loadHandler();
         * </pre>
         *     } else {
         * <pre>
         *       render();
         * </pre>
         *     }
         * </pre>
         *   });
         * </pre>
         * });
         * ...
         * </pre>
         * &lt;/script>
         * </pre>
         * <br />
         * Here is another example of using the wildcard "*" as the key name:
         * <pre>
         * &lt;script>
         * <pre>
         * function getAllData() {
         * <pre>
         *     openmail.Application.getData({ keys: [ "*" ] },
         * <pre>
         *         function( result ) {
         * <pre>
         *             var s = "";
         *             for ( var keyName in result.data) {
         * <pre>
         *                 s += keyName + "=" + result.data[keyName] = "\n";
         * </pre>
         *             }
         *             alert( s );
         * </pre>
         *         }
         * </pre>
         * );
         * </pre>
         * }
         * </pre>
         * &lt;/script>
         * </pre>
         *
         * @param { Object } keys         The key names of the data to retrieve.  This object must have a single data member
         *                                named <code>keys</code>; this is an array of key names to retrieve.  The wildcards "*" (any
         *                                arbitrary string) and "?" (any single character) are supported.
         * @param { function } handler    A callback function
         * <p><br />
         *
         * @return { int }   An error code.
         * </dl><br/>
         * <dl><dt>Callback: <code>response &lt; Object &gt;</code></dt>
         *    <dd>
         *        An object containing the requested data.<br /><br />The object has
         *        the following members:<br />
         *        <dl><dd>
         *            <code>data: Object </code>The data requested.  Member names are the keys requested, values are the stored data.<br/>
         *            <code>version: Object </code>Version number of
         *            application instance. Members names are the keys
         *            requested, values are version numbers.<br/>
         *            <code>totalCount: Number </code>The number of items in the returned data set. <br/>
         *        </dd></dl>
         *    </dd>
         * @method getData
         */
        getData:            function(keys, handler) {
            var e = validate(keys, { name:"keys", required: ["keys"] }, handler);
            if (e) {
                return e;
            }

            send("getData", keys, handler);
        },

        /**
         * Constructs and returns an object to interact with a service requiring authorization.
         * <br /><br />
         * <b>Sample Use:</b>
         * <br /><br />
         * Here is how <code>getAuthService()</code> is used to get an object to
         * a profile "oauth" (which was created together with the application configuration in the development tool):
         * <pre>
         * &lt;script>
         * ...
         * var oauth = openmail.Application.getAuthService({profile:'oauth'});
         * ...
         * &lt;/script>
         * </pre>
         * <br />
         *
         * @param { Object } conf        An object containing the configuration
         * of the authorization service requested.<br /><br />The object has the following members:<br/>
         * <dl><dt><dd>
         * <code>profile</code>:   The name of the stored profile<br/>
         * <code>url</code>:       Url of the target service - only needed if service is used with http proxy<br/>
         * <code>proxy</code>:     Url of the proxy - only needed if service is used with http<br/>
         * </dd></dt></dl>
         * <p><br />
         * @return { Object }            An authorization object to interact with the Web service specified in the profile.
         */
        getAuthService:     function(config) {
            /**
             * Open Mail's API to interact with authorized web services.
             * @class AuthService
             */
            var omAuthApi = {
                profile:config.profile,
                rewrite:{url:config.url,proxy:config.proxy},

                /**
                 * ERR_AUTH is the generic authorization error.
                 *
                 * @property ERR_AUTH
                 * @static
                 * @type int
                 */
                ERR_AUTH:    3000,

                /**
                 * Retrieves the current authorization status of the user with respect to the Web service.
                 * <br /><br />
                 * <b>Sample Use:</b>
                 * <br /><br />
                 * Here is how <code>getStatus()</code> is used to decide whether logging in is required.
                 * <pre>
                 * &lt;script>
                 * ...
                 * var oauth = openmail.Application.getAuthService({profile:'oauth'});
                 * oauth.getStatus( function(args) {
                 * <pre>
                 *     if (args.error) {
                 * <pre>
                 *         // handle error
                 * </pre>
                 *     }
                 *
                 *     if (args.data.status == 'ok') {
                 * <pre>
                 *         // ready to make calls
                 * </pre>
                 *     } else if (args.data.status == 'authorization required') {
                 * <pre>
                 *         // kick off authorization flow
                 * </pre>
                 *     }
                 * </pre>
                 * ...
                 * &lt;/script>
                 * </pre>
                 * <br />
                 *
                 * @param { function } handler        A callback function
                 *
                 * @return { Object }            An object containing
                 * information about the authorization status.<br />The object has the following members:<br/>
                 * <code>status</code> : One of following: 'ok', 'failed', or 'authorization required'. <br />
                 * <code>info</code> : If <code>status</code> is 'ok', depending on the profile type, data holds some additional information.
                 *
                 * @method getStatus
                 */
                getStatus:function(handler) {
                    send("getAuthStatus", {profile:this.profile}, handler);
                },

                /**
                 * Retrieves the redirection URL that is needed by the user to continue the authorization process.
                 * <br /><br />
                 * <b>Sample Use:</b>
                 * <br /><br />
                 * Here is how <code>getUserAuthorizationURL()</code> is used to 'redirect' the user.
                 * <pre>
                 * &lt;script>
                 * ...
                 * // handleSignIn needs to provide a way for the user to signal that she has completed
                 * // authorization on the 3rd party site.
                 * function handleSignIn() {...}
                 * ...
                 * var oauth = openmail.Application.getAuthService({profile:'oauth'});
                 * oauth.getStatus( function(args) {
                 * <pre>
                 *     if (args.error) {//handle error}
                 *     if (args.data.status == 'ok') {
                 * <pre>
                 *         // ready to make calls
                 * </pre>
                 *     } else if (args.data.status == 'authorization required') {
                 * <pre>
                 *         oauth.getUserAuthorizationURL({request:{},authorization:{permissions:'write'}},
                 *         function(response) {
                 * <pre>
                 *             if (response.error) {...} //handle errror
                 *             YAHOO.util.Dom.get("oauth_signin").href = response.data.url;
                 *             YAHOO.util.Event.addListener("oauth_signin", "click", handleSignIn);
                 * </pre>
                 *         });
                 * </pre>
                 *     }
                 * </pre>
                 * ...
                 * &lt;/script>
                 * ...
                 * &lt;a href="javascript:void(0);" id="oauth_signin" target="_blank">Authorize!&lt;/a>
                 * </pre>
                 * <br />
                 *
                 * @param { Object } params        An object containing the configuration of the auth service requested. The object has the following members:<br/>
                 * <dl><dt><dd>
                 * <code>request</code>:   The properties of the request object will be added to the call for the request token<br/>
                 * <code>authorization</code>:       The properties of the request object will be added to the authorization url<br/>
                 * </dd></dt></dl>
                 * @param { function } handler        A callback function
                 *
                 * @return { Object }            <br />An object containing the authorization URL and the following members:<br />
                 * <code>url</code> : The authorization URL.
                 *
                 * @method getUserAuthorizationURL
                 */
                getUserAuthorizationURL:function(params, handler) {
                    send("getUserAuthorizationUrl", {params:params, profile:this.profile}, handler);
                },

                /**
                 * Tells the authorization service that the user has completed the authorization process. This will initiate the retrieval of the authorization token from the 3rd-party service.
                 * <br /><br />
                 * <b>Sample Use:</b>
                 * <br /><br />
                 * Here is how <code>notifyAuthorizationReceived()</code> is used to continue the authorization flow.
                 * <pre>
                 * &lt;script>
                 * ...
                 * var oauth = openmail.Application.getAuthService({profile:'oauth'});
                 * ...
                 * function completeAuth() {
                 * <pre>
                 *     oauth.notifyAuthorizationReceived(function (response) {
                 * <pre>
                 *         if (response.error) {
                 * <pre>
                 *             // handle error
                 * </pre>
                 *         } else {
                 * <pre>
                 *             // handle completion. 
                 *             //response.data.info contains additional data from the service
                 * </pre>
                 *         }
                 * </pre>
                 *      });
                 * </pre>
                 * ...
                 * &lt;/script>
                 * &lt;button onclick='completeAuth()'>Authorization Complete!&lt;/button>
                 * </pre>
                 * <br />
                 *
                 * @param { function } handler        A callback function
                 * <p><br />
                 *
                 * @return { Object }            <br />An object contains the status of the token retrieval and the following members:<br />
                 * <code>info</code> : Additional information is returned by the service.<br />
                 *
                 * @method notifyAuthorizationReceived
                 */
                notifyAuthorizationReceived:function(handler) {
                    send("notifyAuthorizationComplete", {profile:this.profile}, handler);
                },

                /**
                 * Removes all authorization tokens and reverts back to initial state.
                 * <br /><br />
                 * <b>Sample Use:</b>
                 * <br /><br />
                 * Here is how <code>removeAuthorization()</code> is used to log out.
                 * <pre>
                 * &lt;script>
                 * ...
                 * var oauth = openmail.Application.getAuthService({profile:'oauth'});
                 * ...
                 * function logout() {
                 * <pre>
                 *     oauth.removeAuthorization(function (response) {
                 * <pre>
                 *         if (response.error) {//handle error}
                 * </pre>
                 *      });
                 * </pre>
                 * ...
                 * &lt;/script>
                 * &lt;button onclick='logout()'>Log out!&lt;/button>
                 * </pre>
                 * <br />
                 *
                 * @param { function } handler        A callback function
                 *
                 * @return { Object }            An empty object.
                 */
                removeAuthorization:function(handler) {
                    send("removeAuthorization", {profile:this.profile}, handler);
                },

                /**
                 * Call a 3rd-party Web service using an authorized (i.e., signed) call. This is similar to <code>openmail.Application.callWebService</code>, but will add any authorization information needed to make the call.
                 * <br /><br />
                 * <b>Sample Use:</b>
                 * <br /><br />
                 * Here is an example of how to call an OAuth service:
                 * <pre>
                 * ...
                 * &lt;script>
                 * var statusURL = "http://social.yahooapis.com/v1/user/{guid}/presence/presence";
                 * var oauth = openmail.Application.getAuthService({profile:'oauth'});
                 *
                 * function loadStatus() {
                 * <pre>
                 *     oauth.callWebService( 
                 * <pre>
                 *         { 
                 * <pre>
                 *             url: statusURL.replace('{guid}',guid),
                 *             method: "GET",
                 *             parameters: {format: 'json'}
                 * </pre>
                 *         },
                 *         function (response) {
                 * <pre>
                 *             alert(response.data);
                 * </pre>
                 *         });
                 * </pre>
                 * }
                 * </pre>
                 * &lt;/script>
                 * ...
                 * &lt;button onclick='loadStatus()'>load status&lt;/button>
                 * ...
                 * </pre>
                 * @param { Object } service
                 * <p><br />
                 * <code>service</code> may include the following:<br />
                 * <code>url: String </code>           Target URL<br />
                 * <code>method: String </code>       "GET" or "POST" <br />
                 * <code>parameters: Object </code>       A map of key/value pairs to use as the query string or the POST body <br />
                 * <code>handler: function </code>    A callback function <br />
                 * </p>
                 * @return { Object }            If call is successful, the <code>Object</code> contains the HTTP body returned by the Web service. <br />
                 *                                If call fails, the <code>Object</code> contains an error message describing the failure. <br />
                 *                                <br /><b>Note:</b> The return value is always passed to the handler function. <br />
                 * @method callWebService
                 */
                callWebService:    function(service, handler) {
                    service.profile = this.profile;
                    if (!service.rewrite) {
                        service.rewrite = this.rewrite;
                    }
                    send("callAuthWebService", service, handler);
                }
            }
            return omAuthApi;
        }
    };

    /**
     * Open mail's calendar API
     * @class Calendar
     */
    omApi.Calendar ={
        /**
         * <p>Opens the UI to add an event to the user's calendar.</p>
         *
         * <p>Passing <code>eventParms</code> is optional, as is every member of that object. Any values
         * present are used to initialize the UI presented to the user.</p>
         *
         * <p>Only pass one of <code>starts</code> and <code>isoStart</code>. Likewise, only pass one of <code>ends</code> and
         * <code>isoEnd</code>. These are alternative representations of the start and end time.
         * Using an alternative representation may be preferrable to using a JavaScript Date object because
         * you must set the time and date for a JavaScript Date object.
         * </p>
         *
         * <p>If a start time is given (either in <code>starts</code> or <code>isoStart</code>), no end time is given,
         * and 'untimed' isn't set, 'untimed' will be set to 'true'.</p>
         * <b>Sample Use:</b>
         * <pre>
         * &lt;script>
         * <pre>
         * ...
         * evt = {
         * <pre>
         *   title: "Dentist Appt",
         *   location: "dentist's office",
         *   notes: "it's gonna hurt...",
         *   starts: new Date(2008, 9, 22, 9, 00),
         *   ends: new Date(2008, 9, 22, 9, 30)
         * </pre>
         * };
         *
         * openmail.Calendar.addEvent(evt);
         * ...
         * </pre>
         * &lt;/script>
         * </pre>
         *
         * @return { int }   An error code.
         * @method addEvent
         * @param { calendarEvent } eventParms    Optional object used to pre-populate the UI.
         *
         * <p><br/>
         * All members of <code>calendarEvent</code> are optional.
         * <br />Here are the possible members:<br />
         *        <code>title: String </code> The event title<br />
         *        <code>starts: Date </code> The start date and time of the event<br />
         *        <code>isoStart: String </code>    The start date and/or time of the event<br />
         *        <code>ends: Date </code> The date and time the event ends<br />
         *        <code>isoEnd: String </code> The date and/or time the event ends<br />
         *        <code>location: String </code> The location of the event<br />
         *        <code>notes: String </code> Notes<br />
         *        <code>untimed: boolean </code> Set to true if the event is untimed<br />
         * </p>
         */
        addEvent: function(eventParms) { send("calendar.addevent", eventParms); }
    };
    return omApi;
}();

Copyright © 2009 Yahoo! Inc. All rights reserved.