1 /** 2 *@namespace Sfdc.canvas.client 3 *@name Sfdc.canvas.client 4 */ 5 (function ($$) { 6 7 "use strict"; 8 9 var pversion, cversion = "27.0"; 10 11 var module = (function() /**@lends module */ { 12 13 var purl, cbs = [], seq = 0; 14 15 function startsWithHttp(u, d) { 16 return $$.isNil(u) ? u : (u.substring(0, 4) === "http") ? u : d; 17 } 18 /** 19 * @description 20 * @function 21 * @returns The url of the Parent Window 22 */ 23 function getTargetOrigin(to) { 24 25 var h; 26 27 if (to === "*") {return to;} 28 29 if (!$$.isNil(to)) { 30 h = $$.stripUrl(to); 31 purl = startsWithHttp(h, purl); 32 if (purl) {return purl;} 33 } 34 35 // This relies on the parent passing it in. This may not be there as the client can do a redirect. 36 h = document.location.hash; 37 if (h) { 38 h = decodeURIComponent(h.replace(/^#/, '')); 39 purl = startsWithHttp(h, purl); 40 } 41 return purl; 42 } 43 44 function callbacker(data) { 45 46 if (data) { 47 // If the server is telling us the access_token is invalid, wipe it clean. 48 if (data.status === 401 && 49 $$.isArray(data.payload) && 50 data.payload[0].errorCode && 51 data.payload[0].errorCode === "INVALID_SESSION_ID") { 52 // Session has expired logout. 53 $$.oauth.logout(); 54 } 55 if ($$.isFunction(cbs[data.seq])) { 56 cbs[data.seq](data); 57 } 58 else { 59 // This can happen when the user switches out canvas apps real quick, 60 // before the request from the last canvas app have finish processing. 61 // We will ignore any of these results as the canvas app is no longer active to 62 // respond to the results. 63 } 64 } 65 } 66 67 function postit(clientscb, message) { 68 69 var wrapped, to, c; 70 71 // need to keep a mapping from request to callback, otherwise 72 // wrong callbacks get called. Unfortunately, this is the only 73 // way to handle this as postMessage acts more like topic/queue. 74 // limit the sequencers to 100 avoid out of memory errors 75 76 seq = (seq > 100) ? 0 : seq + 1; 77 cbs[seq] = clientscb; 78 wrapped = {seq : seq, src : "client", clientVersion : cversion, parentVersion: pversion, body : message}; 79 80 c = message && message.config && message.config.client; 81 to = getTargetOrigin($$.isNil(c) ? null : c.targetOrigin); 82 if ($$.isNil(to)) { 83 throw "ERROR: targetOrigin was not supplied and was not found on the hash tag, this can result from a redirect or link to another page. " + 84 "Try setting the targetOrgin (example: targetOrigin : sr.context.environment.targetOrigin) " + 85 "when making an ajax request."; 86 } 87 $$.xd.post(wrapped, to, parent); 88 } 89 90 function validateClient(client, cb) { 91 92 var msg; 93 94 if ($$.isNil(client) || $$.isNil(client.oauthToken)) { 95 msg = {status : 401, statusText : "Unauthorized" , parentVersion : pversion, payload : "client or client.oauthToken not supplied"}; 96 } 97 if ($$.isNil(client.instanceId) || $$.isNil(client.targetOrigin)) { 98 msg = {status : 400, statusText : "Bad Request" , parentVersion : pversion, payload : "client.instanceId or client.targetOrigin not supplied"}; 99 } 100 if (!$$.isNil(msg)) { 101 if ($$.isFunction(cb)) { 102 cb(msg); 103 return false; 104 } 105 else { 106 throw msg; 107 } 108 } 109 return true; 110 } 111 112 /** 113 * @description Get the context for the current user and organization 114 * @public 115 * @name Sfdc.canvas.client#ctx 116 * @function 117 * @param {Function} clientscb Callback function to run when the call to ctx is complete 118 * @param {String} token OAuth token to send. 119 * @example 120 * // Gets context in the canvas app. 121 * 122 * function callback(msg) { 123 * if (msg.status !== 200) { 124 * alert("Error: " + msg.status); 125 * return; 126 * } 127 * alert("Payload: ", msg.payload); 128 * } 129 * var ctxlink = connect.byId("ctxlink"); 130 * var oauthtoken = connect.oauth.token(); 131 * ctxlink.onclick=function() { 132 * connect.client.ctx(callback, oauthtoken)}; 133 * } 134 */ 135 function ctx(clientscb, client) { 136 client = client || $$.oauth.client(); 137 if (validateClient(client, clientscb)) { 138 var token = client.oauthToken; 139 postit(clientscb, {type : "ctx", accessToken : token, config : {client : client}}); 140 } 141 } 142 143 /** 144 * @description Perform a cross-domain, asynchronous HTTP request. 145 <br>Note: this should not be used for same domain requests. 146 * @param {String} url The URL to which the request is sent 147 * @param {Object} settings A set of key/value pairs to configure the request. 148 <br>The success setting is required at minimum and should be a callback function 149 * @config {String} [client] required client context {oauthToken: "", targetOrigin : "", instanceId : ""} 150 * @config {String} [contentType] "application/json" 151 * @config {String} [data] request body 152 * @config {String} [headers] request headers 153 * @config {String} [method="GET"] The type of Ajax Request to make 154 * @config {Function} [success] Callback for all responses from server (failure and success) . Signature: success(response); intersting fields: [response.data, response.responseHeaders, response.status, response.statusText} 155 * @config {Boolean} [async=true] Asyncronous: true is only supported at this time. 156 * @name Sfdc.canvas.client#ajax 157 * @function 158 * @throws illegalArgumentException if the URL is missing or the settings object does not contain a success callback function. 159 * @example 160 * //Posting To a Chatter Feed: 161 * var sr = JSON.parse('<%=signedRequestJson%>'); 162 * var url = sr.context.links.chatterFeedsUrl+"/news/" 163 * +sr.context.user.userId+"/feed-items"; 164 * var body = {body : {messageSegments : [{type: "Text", text: "Some Chatter Post"}]}}; 165 * connect.client.ajax(url, 166 * {client : sr.client, 167 * method: 'POST', 168 * contentType: "application/json", 169 * data: JSON.stringify(body), 170 * success : function(data) { 171 * if (201 === data.status) { 172 * alert("Success" 173 * } 174 * } 175 * }); 176 * @example 177 * // Gets a List of Chatter Users: 178 * // Paste the signed request string into a JavaScript object for easy access. 179 * var sr = JSON.parse('<%=signedRequestJson%>'); 180 * // Reference the Chatter user's URL from Context.Links object. 181 * var chatterUsersUrl = sr.context.links.chatterUsersUrl; 182 * 183 * // Make an XHR call back to salesforce through the supplied browser proxy. 184 * connect.client.ajax(chatterUsersUrl, 185 * {client : sr.client, 186 * success : function(data){ 187 * // Make sure the status code is OK. 188 * if (data.status === 200) { 189 * // Alert with how many Chatter users were returned. 190 * alert("Got back " + data.payload.users.length + 191 * " users"); // Returned 2 users 192 * } 193 * })}; 194 */ 195 function ajax(url, settings) { 196 197 var ccb, config, defaults; 198 199 if (!url) { 200 throw "PRECONDITION ERROR: url required with AJAX call"; 201 } 202 if (!settings || !$$.isFunction(settings.success)) { 203 throw "PRECONDITION ERROR: function: 'settings.success' missing."; 204 } 205 if (! validateClient(settings.client, settings.success)) { 206 return; 207 } 208 209 210 ccb = settings.success; 211 defaults = { 212 method: 'GET', 213 async: true, 214 contentType: "application/json", 215 headers: {"Authorization" : "OAuth " + settings.client.oauthToken, 216 "Accept" : "application/json"}, 217 data: null 218 }; 219 config = $$.extend(defaults, settings || {}); 220 221 // Remove any listeners as functions cannot get marshaled. 222 config.success = undefined; 223 config.failure = undefined; 224 // Don't allow the client to set "*" as the target origin. 225 if (config.client.targetOrigin === "*") { 226 config.client.targetOrigin = null; 227 } 228 else { 229 // We need to set this here so we can validate the origin when we receive the call back 230 purl = startsWithHttp(config.targetOrigin, purl); 231 } 232 postit(ccb, {type : "ajax", accessToken : token, url : url, config : config}); 233 } 234 235 /** 236 * @description Stores or gets the oauth token in a local javascript variable. Note, if longer term 237 * (survive page refresh) storage is needed store the oauth token on the server side. 238 * @param {String} t oauth token, if supplied it will be stored in a volatile local JS variable. 239 * @returns {Object} the oauth token. 240 */ 241 242 function token(t) { 243 return $$.oauth.token(t); 244 } 245 246 function version() { 247 return {clientVersion: cversion}; 248 } 249 250 $$.xd.receive(callbacker, getTargetOrigin); 251 252 return { 253 ctx : ctx, 254 ajax : ajax, 255 token : token, 256 version : version 257 }; 258 }()); 259 260 $$.module('Sfdc.canvas.client', module); 261 262 }(Sfdc.canvas)); 263