1 // Sfdc.canvas.parent
  2 (function ($$) {
  3 
  4     "use strict";
  5 
  6     var module =   (function() {
  7 
  8         var listening = false,
  9             canvas = {},
 10             pversion, sid, salesforceBaseUrl;
 11 
 12         function getFrame(id) {
 13         	// We need to get the index of the iframe in case there
 14         	// are multiple iframes on the page.
 15         	// Referencing the iframe via frames['canvas-frame'] is 
 16         	// no good because the location is clobbered if you reference
 17         	// it in that way, which causes postMessage to break.
 18 
 19             var fn = "canvas-frame-" + id;
 20 
 21     		var ifs = window.document.getElementsByTagName('iframe');
 22 			for ( var i = 0, found = -1; ifs[i] && found == -1; i++){
 23 				try {
 24 					if (ifs[i].name === fn){
 25 						found = i;
 26 					}
 27 				} 
 28 				catch (e) {}
 29 			}
 30 			return found!=-1?frames[found]:frames[fn];
 31         }
 32 
 33         function callback(msgData, ctx) {
 34 
 35             if ($$.isNil(ctx.id)) {
 36                 throw "ERROR: Was expecting an id";
 37             }
 38 
 39             if (msgData) {
 40 
 41                 var type = msgData.body.type,
 42                     seq  = msgData.seq;
 43 
 44                 // Update the version of the client.
 45                 canvas[ctx.id].cversion = msgData.clientVersion;
 46 
 47                 // THis is our local callback handler.
 48                 var  lhandler = function(data, xmlHttp, config) {
 49 
 50                     var obj = {},
 51                         ct = xmlHttp.getResponseHeader("Content-Type");
 52 
 53                     // Got a response back from the server, send it (via xd) back to the client.
 54                     obj.seq = seq;
 55                     obj.parentVersion = pversion;
 56                     obj.clientVersion = canvas[ctx.id].cversion;
 57                     obj.payload = ct && data && (ct.indexOf("application/json") >= 0) ? Sfdc.JSON.parse(data) : data;
 58                     obj.status = xmlHttp.status;
 59                     obj.statusText = xmlHttp.statusText;
 60                     obj.responseHeaders = xmlHttp.getAllResponseHeaders();
 61                     $$.xd.post(obj, $$.stripUrl(canvas[ctx.id].url), getFrame(ctx.id));
 62                 };
 63 
 64                 var requestUrl = null, config = null;
 65                 if (type === 'ctx') {
 66                 	var frame = $$.byId("if-" + ctx.id);
 67                     var ver = ($$.isNil(canvas[ctx.id].cversion)) ? pversion : canvas[ctx.id].cversion;
 68                     requestUrl = "/services/data/v" + ver + "/platformconnect/canvascontext";
 69                     config = {
 70                         success : lhandler,
 71                         failure : lhandler,
 72                         headers: {Authorization : "OAuth " + msgData.body.accessToken, Accept : "application/json"},
 73                         contentType:"application/json",
 74                         method: "PUT",
 75                         data:Sfdc.JSON.stringify({
 76                             environment:{
 77                                 dimensions: {
 78                                     height: frame && frame.style.height || null,
 79                                     width:  frame && frame.style.width || null
 80                                 },
 81                                 parameters : canvas[ctx.id].options.parameters
 82                             }
 83                         })
 84                     };
 85 
 86                     requestUrl = applySalesforceProxyConfig(requestUrl, config);
 87                     Sfdc.Ajax.request(requestUrl, config);
 88                 }
 89                 else if (type === 'ajax') {
 90                     msgData.body.config.success = lhandler;
 91                     msgData.body.config.failure = lhandler;
 92                     requestUrl = msgData.body.url;
 93                     config = msgData.body.config;
 94                     requestUrl = applySalesforceProxyConfig(requestUrl, config);
 95                     Sfdc.Ajax.request(requestUrl, config);
 96                 }
 97             }
 98         }
 99 
100         function listen() {
101 
102             if (listening) {
103                 $$.xd.remove();
104             }
105 
106             $$.xd.receive(callback, function(o, d) {
107                 d = Sfdc.JSON.parse(d);
108                 if ($$.isNil(d.body.config.client)) {
109                     alert("It appears the canvas app is using an older version of the SDK. \n" +
110                           "In the future we will support backwards compatibility, but for now \n" +
111                           "Force.com Canvas is in Pilot and the canvas app developer must upgrade.");
112                     return false;
113                 }
114                 var c = canvas[d.body.config.client.instanceId];
115                 if (!$$.isNil(c)) {
116                     return {id : d.body.config.client.instanceId};
117                 }
118                 return false;
119             });
120             listening = true;
121         }
122 
123         function getSignedRequest(id, namespace, canvas, elem, uid, options) {
124 
125             var url = "/services/data/v" + pversion + "/platformconnect/signedrequest?canvas=" + canvas + ((!$$.isNil(namespace))? "&namespace="+namespace : "");
126             var config = {
127                 success: function(data) {
128                     var obj = Sfdc.JSON.parse(data);
129                     addSignedRequestFrame(id, obj.response, elem, uid, options);
130                 },
131                 headers: {Authorization : "OAuth " + sid, Accept : "application/json"},
132                 contentType: "application/json",
133                 method: "PUT",
134                 data:Sfdc.JSON.stringify({
135                     canvasRequest:{
136                         context:{
137                             environment:{
138                                 dimensions: {
139                                     height: options.height,
140                                     width:  options.width
141 								},
142                                 parameters: options.parameters
143                             }
144                         },
145                         client : {
146                             targetOrigin: document.location.protocol + "//" + document.location.host,
147                             instanceId : id
148                         }
149                     }
150                 })
151             };
152 
153             url = applySalesforceProxyConfig(url, config);
154 
155             Sfdc.Ajax.request(url, config);
156         }
157 
158         function applySalesforceProxyConfig(url, requestConfig) {
159             var endPointUrl = null;
160             var proxyUrl = url;
161             if (!$$.isNil(salesforceBaseUrl)) {
162                 endPointUrl = salesforceBaseUrl + url;
163                 proxyUrl = location.protocol + "//" + location.hostname + ":" + location.port + "/services/proxy";
164                 requestConfig.headers["SalesforceProxy-Endpoint"] = endPointUrl;
165             }
166             return proxyUrl;
167         }
168 
169         function trace(options){
170         	
171         	var theType = $$.isNil(options.type) ? null : options.type.toUpperCase();
172         	if ($$.isNil(theType) || ($$.indexOf(["FINISH","ERROR"],theType) === -1)){
173                 throw "PRECONDITION ERROR: Invalid event type (options.type).";
174             }
175         	
176         	if ($$.isNil(options.name)){
177                 throw "PRECONDITION ERROR: Event name is required (options.name).";
178         	}
179 
180         	if ($$.isNil(options.canvas)){
181                 throw "PRECONDITION ERROR: Canvas application name is required (options.canvas).";
182             }
183 
184             var url = "/services/data/v" + pversion + "/platformconnect/traceevent";
185 
186             var config = {
187                 headers :{
188                     Authorization : "OAuth " + sid,
189                     Accept : "application/json"
190                 },
191                 contentType: "application/json",
192                 data : Sfdc.JSON.stringify({
193                     eventName: options.name,
194                     eventType: theType,
195                     namespace: options.namespace,
196                     canvas: options.canvas,
197                     payload: options.payload,
198                     authType: options.authType,
199                     startTime: options.startTime,
200                     endTime: new Date().getTime(),
201                     parentVersion: pversion,
202                     clientVersion: options.cversion
203                 })
204             };
205 
206             url = applySalesforceProxyConfig(url, config, options);
207 
208             Sfdc.Ajax.post(url, function(data){}, config);
209         }
210 
211         function addSignedRequestFrame(id, sr, parent, uid, options) {
212 
213             var tmpForm, tmpInput, tmpSubmit;
214 
215             tmpForm = document.createElement('form');
216             tmpForm.setAttribute('name', "canvas-hidden-form");
217             tmpForm.setAttribute('style', "display:none");
218             tmpForm.setAttribute('action', canvas[id].url);
219             tmpForm.setAttribute('target', "canvas-frame-" + id);
220             tmpForm.setAttribute('method', "post");
221 
222             tmpInput = document.createElement('input');
223             tmpInput.setAttribute('type', "text");
224             tmpInput.setAttribute('name', "signed_request");
225             tmpInput.setAttribute('id', "signed_request");
226             tmpInput.value =sr;
227 
228             tmpSubmit = document.createElement('input');
229             tmpSubmit.setAttribute('type', "submit");
230             tmpSubmit.setAttribute('post', "post");
231 
232             tmpForm.appendChild(tmpInput);
233             tmpForm.appendChild(tmpSubmit);
234 
235             // Remove all child elements from the last time we rendered a canvas app
236             while ( parent.firstChild ) {parent.removeChild( parent.firstChild );}
237             parent.appendChild(tmpForm);
238             parent.appendChild(buildIFrame(id, uid,options));
239             document.forms["canvas-hidden-form"].submit();
240         }
241         
242         function buildIFrame(id, uid, options){
243         	var rv = document.createElement('iframe');
244             rv.setAttribute('id', uid);
245             rv.setAttribute('name', "canvas-frame-" + id);
246             rv.style.border=options.frameborder;
247             rv.style.width = options.width;
248             rv.style.height = options.height;
249             rv.style.scrolling = options.scrolling;
250             rv.onload = function(){
251             	if (options.startTime){
252                     trace({name:"render",
253 	               	     type:"FINISH",
254 	               	     namespace: canvas[id].app.namespace,
255 	               	     canvas: canvas[id].app.developerName,
256 	               	     startTime: canvas[id].app.startTime,
257 	               	     authType: canvas[id].app.authenticationType,
258 	               	     payload: {
259                    			canvasUrl: canvas[id].url
260 	               		 },
261                          cversion : canvas[id].cversion
262 	           		});
263             		options.startTime = null;
264             	}
265             };
266             return rv;
267         }
268         
269         function addFrame(id, parent, uid, options) {
270             var frameDoc, frameObj;
271 
272             // Remove all child elements from the last time we rendered a canvas app
273             while ( parent.firstChild ) {parent.removeChild( parent.firstChild );}
274 
275             frameObj = parent.appendChild(buildIFrame(id,uid,options));
276             if (frameObj.contentDocument) {
277                 frameDoc = frameObj.contentDocument;
278             }
279             else if (frameObj.contentWindow) {
280                 frameDoc = frameObj.contentWindow.document;
281             }
282             frameDoc.location.replace(canvas[id].url);
283         }
284 
285         function render(container, elem, options) {
286     		var iframeDefaults = {
287     				frameborder: '0',
288     				width: '800px',
289     				height: '900px',
290     				scrolling: 'no'
291 			};
292     		
293             options = $$.extend(iframeDefaults, options || {});
294 
295             var id, uid, q, parentUrl, curl, instanceLabel, tempElem;
296 
297             if ($$.isNil(container) || $$.isNil(container.app) || $$.isNil(container.app.canvasUrl) || $$.isNil(container.app.id)) {
298                 throw "ERROR: insufficient container and/or app parameter information passed in";
299             }
300             if ($$.isNil(container.version)) {
301                 throw "ERROR: Parent version information not supplied";
302             }
303             if ($$.isNil(container.sid)) {
304                 throw "ERROR: SID not supplied";
305             }
306 
307             try {
308                 instanceLabel = $$.isNil(options.instanceLabel) ? "" : "-" + options.instanceLabel;
309                 id  = container.app.id + instanceLabel;
310                 canvas[id] = {};
311                 canvas[id].app = container.app;
312                 canvas[id].cversion = null;
313                 canvas[id].options = options;
314 
315                 pversion = container.version;
316                 sid = container.sid;
317                 curl = decodeURIComponent(container.app.canvasUrl);
318 
319                  // Capture the start time (ms) for trace.
320                 options.startTime = new Date().getTime();
321 
322                 uid = "if-" + id;
323 
324                 tempElem = $$.isObject(elem) ? elem : $$.byId(elem);
325 
326                 if ($$.isNil(tempElem)) {
327                     alert("Container element not found [" + elem + "]");
328                 }
329                 else {
330 
331                     parentUrl = document.location.protocol + "//" + document.location.host;
332 
333                     salesforceBaseUrl = options.salesforceBaseUrl;
334 
335                     if (container.app.authenticationType === "OAUTH") {
336                         if (! $$.isNil(container.login) && "https://login.salesforce.com" !== container.login) {
337                             q = $$.param({loginUrl : container.login});
338                         }
339                         q += "#target_origin=" + encodeURIComponent(parentUrl) + "&instance_id=" + id ;
340                         canvas[id].url = $$.query(curl, q);
341                         addFrame(id, tempElem, uid, options);
342                     }
343                     else {
344                         canvas[id].url = curl;
345                         getSignedRequest(id, container.app.namespace,container.app.developerName, tempElem, uid, options);
346                     }
347                     listen(canvas[id].url);
348                 }
349             }
350             catch (e) {
351                 alert("An Error occurred: ", e);
352             }
353 
354         }
355 
356         return {
357             render : render
358         };
359     }());
360 
361     $$.module('Sfdc.canvas.parent', module);
362 
363 }(Sfdc.canvas));
364