1 /** 2 * Copyright (c) 2011, salesforce.com, inc. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without modification, are permitted provided 6 * that the following conditions are met: 7 * 8 * Redistributions of source code must retain the above copyright notice, this list of conditions and the 9 * following disclaimer. 10 * 11 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 12 * the following disclaimer in the documentation and/or other materials provided with the distribution. 13 * 14 * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to endorse or 15 * promote products derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 19 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 21 * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 23 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 * POSSIBILITY OF SUCH DAMAGE. 25 */ 26 /** 27 *@namespace Sfdc.canvas.client 28 *@name Sfdc.canvas.client 29 */ 30 (function ($$) { 31 32 "use strict"; 33 34 var pversion, cversion = "29.0"; 35 36 var module = (function() /**@lends module */ { 37 38 var purl; 39 40 function startsWithHttp(u, d) { 41 return $$.isNil(u) ? u : (u.substring(0, 4) === "http") ? u : d; 42 } 43 44 // returns the url of the Parent Window 45 function getTargetOrigin(to) { 46 var h; 47 if (to === "*") {return to;} 48 if (!$$.isNil(to)) { 49 h = $$.stripUrl(to); 50 purl = startsWithHttp(h, purl); 51 if (purl) {return purl;} 52 } 53 // This relies on the parent passing it in. This may not be there as the client can do a redirect. 54 h = $$.document().location.hash; 55 if (h) { 56 h = decodeURIComponent(h.replace(/^#/, '')); 57 purl = startsWithHttp(h, purl); 58 } 59 return purl; 60 } 61 62 // The main cross domain callback handler 63 function xdCallback(data) { 64 if (data) { 65 if (submodules[data.type]) { 66 submodules[data.type].callback(data); 67 } 68 // Just ignore... 69 } 70 } 71 72 var submodules = (function () { 73 74 var cbs = [], seq = 0, autog = true; 75 76 // Functions common to submodules... 77 78 function postit(clientscb, message) { 79 var wrapped, to, c; 80 81 // need to keep a mapping from request to callback, otherwise 82 // wrong callbacks get called. Unfortunately, this is the only 83 // way to handle this as postMessage acts more like topic/queue. 84 // limit the sequencers to 100 avoid out of memory errors 85 seq = (seq > 100) ? 0 : seq + 1; 86 cbs[seq] = clientscb; 87 wrapped = {seq : seq, src : "client", clientVersion : cversion, parentVersion: pversion, body : message}; 88 89 c = message && message.config && message.config.client; 90 to = getTargetOrigin($$.isNil(c) ? null : c.targetOrigin); 91 if ($$.isNil(to)) { 92 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."; 93 } 94 $$.xd.post(wrapped, to, parent); 95 } 96 97 function validateClient(client, cb) { 98 var msg; 99 100 client = client || $$.oauth && $$.oauth.client(); 101 102 if ($$.isNil(client) || $$.isNil(client.oauthToken)) { 103 msg = {status : 401, statusText : "Unauthorized" , parentVersion : pversion, payload : "client or client.oauthToken not supplied"}; 104 } 105 if ($$.isNil(client.instanceId) || $$.isNil(client.targetOrigin)) { 106 msg = {status : 400, statusText : "Bad Request" , parentVersion : pversion, payload : "client.instanceId or client.targetOrigin not supplied"}; 107 } 108 if (!$$.isNil(msg)) { 109 if ($$.isFunction(cb)) { 110 cb(msg); 111 return false; 112 } 113 else { 114 throw msg; 115 } 116 } 117 return true; 118 } 119 120 // Submodules... 121 122 var event = (function() { 123 var subscriptions = {}, STR_EVT = "sfdc.streamingapi"; 124 125 function validName(name, res) { 126 var msg, r = $$.validEventName(name, res); 127 if (r !== 0) { 128 msg = {1 : "Event names can only contain one namespace", 129 2 : "Namespace has already been reserved", 130 3 : "Event name contains invalid characters" 131 }; 132 throw msg[r]; 133 } 134 } 135 136 function findSubscription(event) { 137 var s, name = event.name; 138 139 if (name === STR_EVT) { 140 s = subscriptions[name][event.params.topic]; 141 } else { 142 s = subscriptions[name]; 143 } 144 145 if (!$$.isNil(s) && $$.isFunction(s.onData)) { 146 return s; 147 } 148 149 return null; 150 } 151 152 return { 153 callback : function (data) { 154 var event = data.payload, 155 subscription = findSubscription(event), 156 func; 157 158 if (!$$.isNil(subscription)) { 159 if (event.method === "onData") { 160 func = subscription.onData; 161 } else if (event.method === "onComplete") { 162 func = subscription.onComplete; 163 } 164 165 if (!$$.isNil(func) && $$.isFunction(func)) { 166 func(event.payload); 167 } 168 } 169 }, 170 171 /** 172 * @description Subscribes to parent or custom events. Events 173 * with the namespaces 'canvas', 'sfdc', 'force', 'salesforce', and 'chatter' are reserved by Salesforce. 174 * Developers can choose their own namespace and event names. 175 * Event names must be in the form <code>namespace.eventname</code>. 176 * @public 177 * @name Sfdc.canvas.client#subscribe 178 * @function 179 * @param {client} client The object from the signed request 180 * @param {Object} s The subscriber object or array of objects with name and callback functions 181 * @example 182 * // Subscribe to the parent window onscroll event. 183 * Sfdc.canvas(function() { 184 * sr = JSON.parse('<%=signedRequestJson%>'); 185 * // Capture the onScrolling event of the parent window. 186 * Sfdc.canvas.client.subscribe(sr.client, 187 * {name : 'canvas.scroll', onData : function (event) { 188 * console.log("Parent's contentHeight; " + event.heights.contentHeight); 189 * console.log("Parent's pageHeight; " + event.heights.pageHeight); 190 * console.log("Parent's scrollTop; " + event.heights.scrollTop); 191 * console.log("Parent's contentWidth; " + event.widths.contentWidth); 192 * console.log("Parent's pageWidth; " + event.widths.pageWidth); 193 * console.log("Parent's scrollLeft; " + event.widths.scrollLeft); 194 * }} 195 * ); 196 * }); 197 * 198 * @example 199 * // Subscribe to a custom event. 200 * Sfdc.canvas(function() { 201 * sr = JSON.parse('<%=signedRequestJson%>'); 202 * Sfdc.canvas.client.subscribe(sr.client, 203 * {name : 'mynamespace.someevent', onData : function (event) { 204 * console.log("Got custom event ", event); 205 * }} 206 * ); 207 * }); 208 * 209 * @example 210 * // Subscribe to multiple events 211 * Sfdc.canvas(function() { 212 * sr = JSON.parse('<%=signedRequestJson%>'); 213 * Sfdc.canvas.client.subscribe(sr.client, [ 214 * {name : 'mynamespace.someevent1', onData : handler1}, 215 * {name : 'mynamespace.someevent2', onData : handler2}, 216 * ]); 217 * }); 218 * 219 * @example 220 * //Subscribe to Streaming API events. 221 * //The PushTopic to subscribe to must be passed in. 222 * //The 'onComplete' method may be defined, 223 * //and will fire when the subscription is complete. 224 * Sfdc.canvas(function() { 225 * sr = JSON.parse('<%=signedRequestJson%>'); 226 * var handler1 = function(){ console.log("onData done");}, 227 * handler2 = function(){ console.log("onComplete done");}; 228 * Sfdc.canvas.client.subscribe(sr.client, 229 * {name : 'sfdc.streamingapi', params:{topic:"/topic/InvoiceStatements"}}, 230 * onData : handler1, onComplete : handler2} 231 * ); 232 * }); 233 * 234 * 235 */ 236 subscribe : function(client, s) { 237 var subs = {}; 238 239 if ($$.isNil(s) || (!validateClient(client))) { 240 throw "precondition fail"; 241 } 242 243 $$.each($$.isArray(s) ? s : [s], function (v) { 244 if (!$$.isNil(v.name)) { 245 validName(v.name, ["canvas", "sfdc"]); 246 247 if (v.name === STR_EVT) { 248 if (!$$.isNil(v.params) && !$$.isNil(v.params.topic)) { 249 if ($$.isNil(subscriptions[v.name])) { 250 subscriptions[v.name] = {}; 251 } 252 subscriptions[v.name][v.params.topic] = v; 253 } else { 254 throw "[" +STR_EVT +"] topic is missing"; 255 } 256 } else { 257 subscriptions[v.name] = v; 258 } 259 260 subs[v.name] = { 261 params : v.params 262 }; 263 } else { 264 throw "subscription does not have a 'name'"; 265 } 266 }); 267 if (!client.isVF) { 268 postit(null, {type : "subscribe", config : {client : client}, subscriptions : subs}); 269 } 270 }, 271 /** 272 * @description Unsubscribes from parent or custom events. 273 * @public 274 * @name Sfdc.canvas.client#unsubscribe 275 * @function 276 * @param {client} client The object from the signed request 277 * @param {Object} s The events to unsubscribe from 278 * @example 279 * //Unsubscribe from the canvas.scroll method. 280 * Sfdc.canvas(function() { 281 * sr = JSON.parse('<%=signedRequestJson%>'); 282 * Sfdc.canvas.client.unsubscribe(sr.client, "canvas.scroll"); 283 *}); 284 * 285 * @example 286 * //Unsubscribe from the canvas.scroll method by specifying the object name. 287 * Sfdc.canvas(function() { 288 * sr = JSON.parse('<%=signedRequestJson%>'); 289 * Sfdc.canvas.client.unsubscribe(sr.client, {name : "canvas.scroll"}); 290 *}); 291 * 292 * @example 293 * //Unsubscribe from multiple events. 294 * Sfdc.canvas(function() { 295 * sr = JSON.parse('<%=signedRequestJson%>'); 296 * Sfdc.canvas.client.unsubscribe(sr.client, ['canvas.scroll', 'foo.bar']); 297 *}); 298 * 299 * @example 300 * //Unsubscribe from Streaming API events. 301 * //The PushTopic to unsubscribe from must be passed in. 302 * Sfdc.canvas(function() { 303 * sr = JSON.parse('<%=signedRequestJson%>'); 304 * Sfdc.canvas.client.unsubscribe(sr.client, {name : 'sfdc.streamingapi', 305 * params:{topic:"/topic/InvoiceStatements"}}); 306 *}); 307 */ 308 unsubscribe : function(client, s) { 309 // client can pass in the handler object or just the name 310 var subs = {}; 311 312 if ($$.isNil(s) || !validateClient(client)) { 313 throw "PRECONDITION FAIL: need fo supply client and event name"; 314 } 315 316 if ($$.isString(s)) { 317 subs[s] = {}; 318 delete subscriptions[s]; 319 } 320 else { 321 $$.each($$.isArray(s) ? s : [s], function (v) { 322 var name = v.name ? v.name : v; 323 validName(name, ["canvas", "sfdc"]); 324 subs[name] = { 325 params : v.params 326 }; 327 if (name === STR_EVT) { 328 if(!$$.isNil(subscriptions[name])) { 329 if (!$$.isNil(subscriptions[name][v.params.topic])) { 330 delete subscriptions[name][v.params.topic]; 331 } 332 if ($$.size(subscriptions[name]) <= 0) { 333 delete subscriptions[name]; 334 } 335 } 336 } else { 337 delete subscriptions[name]; 338 } 339 }); 340 } 341 if (!client.isVF) { 342 postit(null, {type : "unsubscribe", config : {client : client}, subscriptions : subs}); 343 } 344 }, 345 /** 346 * @description Publishes a custom event. Events are published to all subscribing canvas applications 347 * on the same page, regardless of domain. Choose a unique namespace so the event doesn't collide with other 348 * application events. Events can have payloads of arbitrary JSON objects. 349 * @public 350 * @name Sfdc.canvas.client#publish 351 * @function 352 * @param {client} client The object from the signed request 353 * @param {Object} e The event to publish 354 * @example 355 * // Publish the foo.bar event with the specified payload. 356 * Sfdc.canvas(function() { 357 * sr = JSON.parse('<%=signedRequestJson%>'); 358 * Sfdc.canvas.client.publish(sr.client, 359 * {name : "foo.bar", payload : {some : 'stuff'}}); 360 *}); 361 */ 362 publish : function(client, e) { 363 if (!$$.isNil(e) && !$$.isNil(e.name)) { 364 validName(e.name); 365 if (validateClient(client)) { 366 postit(null, {type : "publish", config : {client : client}, event : e}); 367 } 368 } 369 } 370 }; 371 }()); 372 373 var callback = (function() { 374 return { 375 callback : function (data) { 376 // If the server is telling us the access_token is invalid, wipe it clean. 377 if (data.status === 401 && 378 $$.isArray(data.payload) && 379 data.payload[0].errorCode && 380 data.payload[0].errorCode === "INVALID_SESSION_ID") { 381 // Session has expired logout. 382 if ($$.oauth) {$$.oauth.logout();} 383 } 384 // Find appropriate client callback an invoke it. 385 if ($$.isFunction(cbs[data.seq])) { 386 if (!$$.isFunction(cbs[data.seq])) { 387 alert("not function"); 388 } 389 cbs[data.seq](data); 390 } 391 else { 392 // This can happen when the user switches out canvas apps real quick, 393 // before the request from the last canvas app have finish processing. 394 // We will ignore any of these results as the canvas app is no longer active to 395 // respond to the results. 396 } 397 } 398 }; 399 }()); 400 401 var services = (function() { 402 403 var sr; 404 405 return { 406 /** 407 * @description Performs a cross-domain, asynchronous HTTP request. 408 <br>Note: this method shouldn't be used for same domain requests. 409 * @param {String} url The URL to which the request is sent 410 * @param {Object} settings A set of key/value pairs to configure the request 411 <br>The success setting is required at minimum and should be a callback function 412 * @config {String} [client] The required client context {oauthToken: "", targetOrigin : "", instanceId : ""} 413 * @config {String} [contentType] "application/json" 414 * @config {String} [data] The request body 415 * @config {String} [headers] request headers 416 * @config {String} [method="GET"] The type of AJAX request to make 417 * @config {Function} [success] Callback for all responses from the server (failure and success). Signature: success(response); interesting fields: [response.data, response.responseHeaders, response.status, response.statusText} 418 * @config {Boolean} [async=true] Asynchronous: only <code>true</code> is supported. 419 * @name Sfdc.canvas.client#ajax 420 * @function 421 * @throws An error if the URL is missing or the settings object doesn't contain a success callback function. 422 * @example 423 * //Posting to a Chatter feed: 424 * var sr = JSON.parse('<%=signedRequestJson%>'); 425 * var url = sr.context.links.chatterFeedsUrl+"/news/" 426 * +sr.context.user.userId+"/feed-items"; 427 * var body = {body : {messageSegments : [{type: "Text", text: "Some Chatter Post"}]}}; 428 * Sfdc.canvas.client.ajax(url, 429 * {client : sr.client, 430 * method: 'POST', 431 * contentType: "application/json", 432 * data: JSON.stringify(body), 433 * success : function(data) { 434 * if (201 === data.status) { 435 * alert("Success" 436 * } 437 * } 438 * }); 439 * @example 440 * // Gets a list of Chatter users: 441 * // Paste the signed request string into a JavaScript object for easy access. 442 * var sr = JSON.parse('<%=signedRequestJson%>'); 443 * // Reference the Chatter user's URL from Context.Links object. 444 * var chatterUsersUrl = sr.context.links.chatterUsersUrl; 445 * 446 * // Make an XHR call back to Salesforce through the supplied browser proxy. 447 * Sfdc.canvas.client.ajax(chatterUsersUrl, 448 * {client : sr.client, 449 * success : function(data){ 450 * // Make sure the status code is OK. 451 * if (data.status === 200) { 452 * // Alert with how many Chatter users were returned. 453 * alert("Got back " + data.payload.users.length + 454 * " users"); // Returned 2 users 455 * } 456 * })}; 457 */ 458 ajax : function (url, settings) { 459 460 var ccb, config, defaults; 461 462 if (!url) { 463 throw "PRECONDITION ERROR: url required with AJAX call"; 464 } 465 if (!settings || !$$.isFunction(settings.success)) { 466 throw "PRECONDITION ERROR: function: 'settings.success' missing."; 467 } 468 if (! validateClient(settings.client, settings.success)) { 469 return; 470 } 471 472 ccb = settings.success; 473 defaults = { 474 method: 'GET', 475 async: true, 476 contentType: "application/json", 477 headers: {"Authorization" : "OAuth " + settings.client.oauthToken, 478 "Accept" : "application/json"}, 479 data: null 480 }; 481 config = $$.extend(defaults, settings || {}); 482 483 // Remove any listeners as functions cannot get marshaled. 484 config.success = undefined; 485 config.failure = undefined; 486 // Don't allow the client to set "*" as the target origin. 487 if (config.client.targetOrigin === "*") { 488 config.client.targetOrigin = null; 489 } 490 else { 491 // We need to set this here so we can validate the origin when we receive the call back 492 purl = startsWithHttp(config.targetOrigin, purl); 493 } 494 postit(ccb, {type : "ajax", url : url, config : config}); 495 }, 496 /** 497 * @description Returns the context for the current user and organization. 498 * @public 499 * @name Sfdc.canvas.client#ctx 500 * @function 501 * @param {Function} clientscb The callback function to run when the call to ctx completes 502 * @param {Object} client The signedRequest.client. 503 * @example 504 * // Gets context in the canvas app. 505 * 506 * function callback(msg) { 507 * if (msg.status !== 200) { 508 * alert("Error: " + msg.status); 509 * return; 510 * } 511 * alert("Payload: ", msg.payload); 512 * } 513 * var ctxlink = Sfdc.canvas.byId("ctxlink"); 514 * var client = Sfdc.canvas.oauth.client(); 515 * ctxlink.onclick=function() { 516 * Sfdc.canvas.client.ctx(callback, client)}; 517 * } 518 */ 519 ctx : function (clientscb, client) { 520 if (validateClient(client, clientscb)) { 521 postit(clientscb, {type : "ctx", accessToken : client.oauthToken, config : {client : client}}); 522 } 523 }, 524 /** 525 * @description Stores or gets the oauth token in a local javascript variable. Note, if longer term 526 * (survive page refresh) storage is needed store the oauth token on the server side. 527 * @param {String} t oauth token, if supplied it will be stored in a volatile local JS variable. 528 * @returns {Object} the oauth token. 529 */ 530 token : function(t) { 531 return $$.oauth && $$.oauth.token(t); 532 }, 533 /** 534 * @description Returns the current version of the client. 535 * @returns {Object} {clientVersion : "29.0", parentVersion : "29.0"}. 536 */ 537 version : function() { 538 return {clientVersion: cversion, parentVersion : pversion}; 539 }, 540 /** 541 * @description Temporary storage for the signed request. An alternative for users storing SR in 542 * a global variable. 543 * @param {Object} s signedrequest to be temporarily stored in Sfdc.canvas.client object. 544 * @returns {Object} the value previously stored 545 */ 546 signedrequest : function(s) { 547 if (arguments.length > 0) { 548 sr = s; 549 } 550 return sr; 551 } 552 }; 553 }()); 554 555 var frame = (function() { 556 return { 557 /** 558 * @public 559 * @name Sfdc.canvas.client#size 560 * @function 561 * @description Returns the current size of the iFrame. 562 * @return {Object}<br> 563 * <code>heights.contentHeight</code>: the height of the virtual iFrame, all content, not just visible content.<br> 564 * <code>heights.pageHeight</code>: the height of the visible iFrame in the browser.<br> 565 * <code>heights.scrollTop</code>: the position of the scroll bar measured from the top.<br> 566 * <code>widths.contentWidth</code>: the width of the virtual iFrame, all content, not just visible content.<br> 567 * <code>widths.pageWidth</code>: the width of the visible iFrame in the browser.<br> 568 * <code>widths.scrollLeft</code>: the position of the scroll bar measured from the left. 569 * @example 570 * //get the size of the iFrame and print out each component. 571 * var sizes = Sfdc.canvas.client.size(); 572 * console.log("contentHeight; " + sizes.heights.contentHeight); 573 * console.log("pageHeight; " + sizes.heights.pageHeight); 574 * console.log("scrollTop; " + sizes.heights.scrollTop); 575 * console.log("contentWidth; " + sizes.widths.contentWidth); 576 * console.log("pageWidth; " + sizes.widths.pageWidth); 577 * console.log("scrollLeft; " + sizes.widths.scrollLeft); 578 */ 579 size : function() { 580 var docElement = $$.document().documentElement; 581 var contentHeight = docElement.scrollHeight, 582 pageHeight = docElement.clientHeight, 583 scrollTop = (docElement && docElement.scrollTop) || $$.document().body.scrollTop, 584 contentWidth = docElement.scrollWidth, 585 pageWidth = docElement.clientWidth, 586 scrollLeft = (docElement && docElement.scrollLeft) || $$.document().body.scrollLeft; 587 588 return {heights : {contentHeight : contentHeight, pageHeight : pageHeight, scrollTop : scrollTop}, 589 widths : {contentWidth : contentWidth, pageWidth : pageWidth, scrollLeft : scrollLeft}}; 590 }, 591 /** 592 * @public 593 * @name Sfdc.canvas.client#resize 594 * @function 595 * @description Informs the parent window to resize the canvas iFrame. If no parameters are specified, 596 * the parent window attempts to determine the height of the canvas app based on the 597 * content and then sets the iFrame width and height accordingly. To explicitly set the dimensions, 598 * pass in an object with height and/or width properties. 599 * @param {Client} client The object from the signed request 600 * @param {size} size The optional height and width information 601 * @example 602 * //Automatically determine the size 603 * Sfdc.canvas(function() { 604 * sr = JSON.parse('<%=signedRequestJson%>'); 605 * Sfdc.canvas.client.resize(sr.client); 606 * }); 607 * 608 * @example 609 * //Set the height and width explicitly 610 * Sfdc.canvas(function() { 611 * sr = JSON.parse('<%=signedRequestJson%>'); 612 * Sfdc.canvas.client.resize(sr.client, {height : "1000px", width : "900px"}); 613 * }); 614 * 615 * @example 616 * //Set only the height 617 * Sfdc.canvas(function() { 618 * sr = JSON.parse('<%=signedRequestJson%>'); 619 * Sfdc.canvas.client.resize(sr.client, {height : "1000px"}); 620 * }); 621 * 622 */ 623 resize : function(client, size) { 624 var sh, ch, sw, cw, s = {height : "", width : ""}, 625 docElement = $$.document().documentElement; 626 627 // If the size was not supplied, adjust window 628 if ($$.isNil(size)) { 629 sh = docElement.scrollHeight; 630 ch = docElement.clientHeight; 631 if (ch !== sh) { 632 s.height = sh + "px"; 633 } 634 sw = docElement.scrollWidth; 635 cw = docElement.clientWidth; 636 if (sw !== cw) { 637 s.width = sw + "px"; 638 } 639 } 640 else { 641 if (!$$.isNil(size.height)) { 642 s.height = size.height; 643 } 644 if (!$$.isNil(size.width)) { 645 s.width = size.width; 646 } 647 } 648 if (!$$.isNil(s.height) || !$$.isNil(s.width)) { 649 postit(null, {type : "resize", config : {client : client}, size : s}); 650 } 651 }, 652 /** 653 * @public 654 * @name Sfdc.canvas.client#autogrow 655 * @function 656 * @description Starts or stops a timer which checks the content size of the iFrame and 657 * adjusts the frame accordingly. 658 * Use this function when you know your content is changing size, but you're not sure when. There's a delay as 659 * the resizing is done asynchronously. Therfore, if you know when your content changes size, you should 660 * explicitly call the resize() method and save browser CPU cycles. 661 * Note: you should turn off scrolling before this call, otherwise you might get a flicker. 662 * @param {client} client The object from the signed request 663 * @param {boolean} b Whether it's turned on or off; defaults to <code>true</code> 664 * @param {Integer} interval The interval used to check content size; default timeout is 300ms. 665 * @example 666 * 667 * // Turn on auto grow with default settings. 668 * Sfdc.canvas(function() { 669 * sr = JSON.parse('<%=signedRequestJson%>'); 670 * Sfdc.canvas.client.autogrow(sr.client); 671 * }); 672 * 673 * // Turn on auto grow with a polling interval of 100ms (milliseconds). 674 * Sfdc.canvas(function() { 675 * sr = JSON.parse('<%=signedRequestJson%>'); 676 * Sfdc.canvas.client.autogrow(sr.client, true, 100); 677 * }); 678 * 679 * // Turn off auto grow. 680 * Sfdc.canvas(function() { 681 * sr = JSON.parse('<%=signedRequestJson%>'); 682 * Sfdc.canvas.client.autogrow(sr.client, false); 683 * }); 684 */ 685 autogrow : function(client, b, interval) { 686 var ival = ($$.isNil(interval)) ? 300 : interval; 687 autog = ($$.isNil(b)) ? true : b; 688 if (autog === false) { 689 return; 690 } 691 setTimeout(function () { 692 submodules.frame.resize(client); 693 submodules.frame.autogrow(client, autog); 694 },ival); 695 } 696 }; 697 }()); 698 699 return { 700 services : services, 701 frame : frame, 702 event : event, 703 callback : callback 704 }; 705 }()); 706 707 $$.xd.receive(xdCallback, getTargetOrigin); 708 709 return { 710 ctx : submodules.services.ctx, 711 ajax : submodules.services.ajax, 712 token : submodules.services.token, 713 version : submodules.services.version, 714 resize : submodules.frame.resize, 715 size : submodules.frame.size, 716 autogrow : submodules.frame.autogrow, 717 subscribe : submodules.event.subscribe, 718 unsubscribe : submodules.event.unsubscribe, 719 publish : submodules.event.publish, 720 signedrequest : submodules.services.signedrequest 721 }; 722 }()); 723 724 $$.module('Sfdc.canvas.client', module); 725 726 }(Sfdc.canvas)); 727