3232 * console, go to Your Name | Setup | Security Controls | Remote Site Settings
3333 */
3434
35- /*jslint browser: true*/
36- /*global alert, Blob, $, jQuery */
35+ /*jslint browser: true, plusplus: true */
36+ /*global alert, Blob*/
3737
3838var forcetk = window . forcetk ;
3939
@@ -96,38 +96,44 @@ if (forcetk.Client === undefined) {
9696 */
9797 forcetk . Client . prototype . refreshAccessToken = function ( callback , error ) {
9898 'use strict' ;
99- var that = this ,
100- url = this . loginUrl + '/services/oauth2/token' ;
101- return $ . ajax ( {
102- type : 'POST' ,
103- url : ( this . proxyUrl !== null && ! this . visualforce ) ? this . proxyUrl : url ,
104- cache : false ,
105- processData : false ,
106- data : 'grant_type=refresh_token&client_id=' + this . clientId + '&refresh_token=' + this . refreshToken ,
107- success : callback ,
108- error : error ,
109- dataType : "json" ,
110- beforeSend : function ( xhr ) {
111- if ( that . proxyUrl !== null && ! this . visualforce ) {
112- xhr . setRequestHeader ( 'SalesforceProxy-Endpoint' , url ) ;
99+ var xhr = new XMLHttpRequest ( ) ,
100+ url = this . loginUrl + '/services/oauth2/token' ,
101+ payload = 'grant_type=refresh_token&client_id=' + this . clientId + '&refresh_token=' + this . refreshToken ;
102+
103+ xhr . onreadystatechange = function ( ) {
104+ if ( xhr . readyState === 4 ) {
105+ if ( xhr . status > 199 && xhr . status < 300 ) {
106+ if ( callback ) {
107+ callback ( xhr . responseText ? JSON . parse ( xhr . responseText ) : undefined ) ;
108+ }
109+ } else {
110+ console . error ( xhr . responseText ) ;
111+ if ( error ) {
112+ error ( xhr ) ;
113+ }
113114 }
114115 }
115- } ) ;
116+ } ;
117+
118+ xhr . open ( 'POST' , url , true ) ;
119+ xhr . setRequestHeader ( "Accept" , "application/json" ) ;
120+ xhr . setRequestHeader ( 'X-User-Agent' , 'salesforce-toolkit-rest-javascript/' + this . apiVersion ) ;
121+ xhr . send ( payload ) ;
116122 } ;
117123
118124 /**
119125 * Set a session token and the associated metadata in the client.
120126 * @param sessionId a salesforce.com session ID. In a Visualforce page,
121127 * use '{!$Api.sessionId}' to obtain a session ID.
122- * @param [apiVersion="v29 .0"] Force.com API version
128+ * @param [apiVersion="v36 .0"] Force.com API version
123129 * @param [instanceUrl] Omit this if running on Visualforce; otherwise
124130 * use the value from the OAuth token.
125131 */
126132 forcetk . Client . prototype . setSessionToken = function ( sessionId , apiVersion , instanceUrl ) {
127133 'use strict' ;
128134 this . sessionId = sessionId ;
129135 this . apiVersion = ( apiVersion === undefined || apiVersion === null )
130- ? 'v29 .0' : apiVersion ;
136+ ? 'v36 .0' : apiVersion ;
131137 if ( instanceUrl === undefined || instanceUrl === null ) {
132138 this . visualforce = true ;
133139
@@ -150,49 +156,73 @@ if (forcetk.Client === undefined) {
150156 }
151157 } ;
152158
159+ var nonce = + ( new Date ( ) ) ;
160+ var rquery = ( / \? / ) ;
161+
153162 /*
154163 * Low level utility function to call the Salesforce endpoint.
155164 * @param path resource path relative to /services/data
156165 * @param callback function to which response will be passed
157166 * @param [error=null] function to which jqXHR will be passed in case of error
158167 * @param [method="GET"] HTTP method for call
159- * @param [payload=null] payload for POST/PATCH etc
168+ * @param [payload=null] string payload for POST/PATCH etc
160169 */
161170 forcetk . Client . prototype . ajax = function ( path , callback , error , method , payload , retry ) {
162171 'use strict' ;
163- var that = this ,
164- url = ( this . visualforce ? '' : this . instanceUrl ) + '/services/data' + path ;
165-
166- return $ . ajax ( {
167- type : method || "GET" ,
168- async : this . asyncAjax ,
169- url : ( this . proxyUrl !== null && ! this . visualforce ) ? this . proxyUrl : url ,
170- contentType : method === "DELETE" ? null : 'application/json' ,
171- cache : false ,
172- processData : false ,
173- data : payload ,
174- success : callback ,
175- error : ( ! this . refreshToken || retry ) ? error : function ( jqXHR , textStatus , errorThrown ) {
176- if ( jqXHR . status === 401 ) {
177- that . refreshAccessToken ( function ( oauthResponse ) {
178- that . setSessionToken ( oauthResponse . access_token , null ,
179- oauthResponse . instance_url ) ;
180- that . ajax ( path , callback , error , method , payload , true ) ;
181- } ,
182- error ) ;
183- } else {
184- error ( jqXHR , textStatus , errorThrown ) ;
185- }
186- } ,
187- dataType : "json" ,
188- beforeSend : function ( xhr ) {
189- if ( that . proxyUrl !== null && ! that . visualforce ) {
190- xhr . setRequestHeader ( 'SalesforceProxy-Endpoint' , url ) ;
172+
173+ // dev friendly API: Add leading '/' if missing so url + path concat always works
174+ if ( path . charAt ( 0 ) !== '/' ) {
175+ path = '/' + path ;
176+ }
177+
178+ var xhr = new XMLHttpRequest ( ) ,
179+ url = ( this . visualforce ? '' : this . instanceUrl ) + '/services/data' + path ,
180+ that = this ;
181+
182+ method = method || 'GET' ;
183+
184+ // Cache-busting logic inspired by jQuery
185+ url = url + ( rquery . test ( url ) ? "&" : "?" ) + "_=" + nonce ++ ;
186+
187+ if ( this . asyncAjax ) {
188+ xhr . onreadystatechange = function ( ) {
189+ if ( xhr . readyState === 4 ) {
190+ if ( xhr . status > 199 && xhr . status < 300 ) {
191+ if ( callback ) {
192+ callback ( xhr . responseText ? JSON . parse ( xhr . responseText ) : undefined ) ;
193+ }
194+ } else if ( xhr . status === 401 && that . refresh_token ) {
195+ if ( retry ) {
196+ console . error ( xhr . responseText ) ;
197+ error ( xhr ) ;
198+ } else {
199+ that . refreshAccessToken ( function ( oauthResponse ) {
200+ that . setSessionToken ( oauthResponse . access_token , null ,
201+ oauthResponse . instance_url ) ;
202+ that . ajax ( path , callback , error , method , payload , true ) ;
203+ } ,
204+ error ) ;
205+ }
206+ } else {
207+ console . error ( xhr . responseText ) ;
208+ if ( error ) {
209+ error ( xhr ) ;
210+ }
211+ }
191212 }
192- xhr . setRequestHeader ( that . authzHeader , "Bearer " + that . sessionId ) ;
193- xhr . setRequestHeader ( 'X-User-Agent' , 'salesforce-toolkit-rest-javascript/' + that . apiVersion ) ;
194- }
195- } ) ;
213+ } ;
214+ }
215+
216+ xhr . open ( method , url , this . asyncAjax ) ;
217+ xhr . setRequestHeader ( "Accept" , "application/json" ) ;
218+ xhr . setRequestHeader ( this . authzHeader , "Bearer " + this . sessionId ) ;
219+ xhr . setRequestHeader ( 'X-User-Agent' , 'salesforce-toolkit-rest-javascript/' + this . apiVersion ) ;
220+ if ( method !== "DELETE" ) {
221+ xhr . setRequestHeader ( "Content-Type" , 'application/json' ) ;
222+ }
223+ xhr . send ( payload ) ;
224+
225+ return this . asyncAjax ? null : JSON . parse ( xhr . responseText ) ;
196226 } ;
197227
198228 /**
@@ -374,6 +404,19 @@ if (forcetk.Client === undefined) {
374404 '?_HttpMethod=PATCH' , fields , filename , payloadField , payload , callback , error , retry ) ;
375405 } ;
376406
407+ var param = function ( data ) {
408+ 'use strict' ;
409+ var r20 = / % 2 0 / g,
410+ s = [ ] ,
411+ key ;
412+ for ( key in data ) {
413+ if ( data . hasOwnProperty ( key ) ) {
414+ s [ s . length ] = encodeURIComponent ( key ) + "=" + encodeURIComponent ( data [ key ] ) ;
415+ }
416+ }
417+ return s . join ( "&" ) . replace ( r20 , "+" ) ;
418+ } ;
419+
377420 /*
378421 * Low level utility function to call the Salesforce endpoint specific for Apex REST API.
379422 * @param path resource path relative to /services/apexrest
@@ -386,16 +429,24 @@ if (forcetk.Client === undefined) {
386429 */
387430 forcetk . Client . prototype . apexrest = function ( path , callback , error , method , payload , paramMap , retry ) {
388431 'use strict' ;
389- var that = this ,
390- url = this . instanceUrl + '/services/apexrest' + path ;
391432
392- method = method || "GET" ;
433+ // dev friendly API: Add leading '/' if missing so url + path concat always works
434+ if ( path . charAt ( 0 ) !== '/' ) {
435+ path = '/' + path ;
436+ }
437+
438+ var xhr = new XMLHttpRequest ( ) ,
439+ that = this ,
440+ url = this . instanceUrl + '/services/apexrest' + path ,
441+ paramName ;
442+
443+ method = method || 'GET' ;
393444
394445 if ( method === "GET" ) {
395446 // Handle proxied query params correctly
396447 if ( this . proxyUrl && payload ) {
397448 if ( typeof payload !== 'string' ) {
398- payload = $ . param ( payload ) ;
449+ payload = param ( payload ) ;
399450 }
400451 url += "?" + payload ;
401452 payload = null ;
@@ -407,47 +458,64 @@ if (forcetk.Client === undefined) {
407458 }
408459 }
409460
410- return $ . ajax ( {
411- type : method ,
412- async : this . asyncAjax ,
413- url : this . proxyUrl || url ,
414- contentType : 'application/json' ,
415- cache : false ,
416- processData : false ,
417- data : payload ,
418- success : callback ,
419- error : ( ! this . refreshToken || retry ) ? error : function ( jqXHR , textStatus , errorThrown ) {
420- if ( jqXHR . status === 401 ) {
421- that . refreshAccessToken ( function ( oauthResponse ) {
422- that . setSessionToken ( oauthResponse . access_token , null ,
423- oauthResponse . instance_url ) ;
424- that . apexrest ( path , callback , error , method , payload , paramMap , true ) ;
425- } , error ) ;
426- } else {
427- error ( jqXHR , textStatus , errorThrown ) ;
428- }
429- } ,
430- dataType : "json" ,
431- beforeSend : function ( xhr ) {
432- var paramName ;
433- if ( that . proxyUrl !== null ) {
434- xhr . setRequestHeader ( 'SalesforceProxy-Endpoint' , url ) ;
435- }
436- //Add any custom headers
437- if ( paramMap === null ) {
438- paramMap = { } ;
439- }
440- for ( paramName in paramMap ) {
441- if ( paramMap . hasOwnProperty ( paramName ) ) {
442- xhr . setRequestHeader ( paramName , paramMap [ paramName ] ) ;
461+ // Cache-busting logic inspired by jQuery
462+ url = url + ( rquery . test ( url ) ? "&" : "?" ) + "_=" + nonce ++ ;
463+
464+ if ( this . asyncAjax ) {
465+ xhr . onreadystatechange = function ( ) {
466+ if ( xhr . readyState === 4 ) {
467+ if ( xhr . status > 199 && xhr . status < 300 ) {
468+ if ( callback ) {
469+ callback ( xhr . responseText ? JSON . parse ( xhr . responseText ) : undefined ) ;
470+ }
471+ } else if ( xhr . status === 401 && that . refresh_token ) {
472+ if ( retry ) {
473+ console . error ( xhr . responseText ) ;
474+ error ( xhr ) ;
475+ } else {
476+ that . refreshAccessToken ( function ( oauthResponse ) {
477+ that . setSessionToken ( oauthResponse . access_token , null ,
478+ oauthResponse . instance_url ) ;
479+ that . apexrest ( path , callback , error , method , payload , paramMap , true ) ;
480+ } ,
481+ error ) ;
482+ }
483+ } else {
484+ console . error ( xhr . responseText ) ;
485+ if ( error ) {
486+ error ( xhr ) ;
487+ }
443488 }
444489 }
445- xhr . setRequestHeader ( that . authzHeader , "Bearer " + that . sessionId ) ;
446- xhr . setRequestHeader ( 'X-User-Agent' , 'salesforce-toolkit-rest-javascript/' + that . apiVersion ) ;
490+ } ;
491+ }
492+
493+ xhr . open ( method , this . proxyUrl || url , this . asyncAjax ) ;
494+ xhr . setRequestHeader ( "Accept" , "application/json" ) ;
495+ xhr . setRequestHeader ( this . authzHeader , "Bearer " + this . sessionId ) ;
496+ xhr . setRequestHeader ( 'X-User-Agent' , 'salesforce-toolkit-rest-javascript/' + this . apiVersion ) ;
497+ xhr . setRequestHeader ( "Content-Type" , 'application/json' ) ;
498+
499+ //Add any custom headers
500+ if ( paramMap === null ) {
501+ paramMap = { } ;
502+ }
503+ for ( paramName in paramMap ) {
504+ if ( paramMap . hasOwnProperty ( paramName ) ) {
505+ xhr . setRequestHeader ( paramName , paramMap [ paramName ] ) ;
447506 }
448- } ) ;
507+ }
508+
509+ if ( that . proxyUrl !== null ) {
510+ xhr . setRequestHeader ( 'SalesforceProxy-Endpoint' , url ) ;
511+ }
512+
513+ xhr . send ( payload ) ;
514+
515+ return this . asyncAjax ? null : JSON . parse ( xhr . responseText ) ;
449516 } ;
450517
518+
451519 /*
452520 * Lists summary information about each Salesforce.com version currently
453521 * available, including the version, label, and a link to each version's
0 commit comments