Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Wei Zhu
committed
Oct 26, 2009
1 parent
31a386c
commit cb9dbad
Showing
31 changed files
with
2,700 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
/** | ||
* @provides v2.FB.Api | ||
* @requires v2.FB.App v2.FB.Base v2.FB.Md5 v2.FB.Util v2.FB.Uri v2.FB.Dom | ||
* | ||
*/ | ||
|
||
/** | ||
* This module provides unified API access (both REST and UI) | ||
* @namespace FB.Api | ||
*/ | ||
FB.provide('Api', { | ||
/** | ||
* Calls the specified Facebook API method with given parameters, or | ||
* queues it on the Sequencer. | ||
* @param {String} method | ||
* API method to call | ||
* @param {Object} parameters | ||
* parameters corresponding to the method | ||
* @param callback | ||
* @param options | ||
* optional options | ||
*/ | ||
invoke: function(method, parameters, callback, options) { | ||
// Set an empty dictionary if the value is null | ||
// so later code doesn't have to check for null condition | ||
if (!parameters) { | ||
parameters = {}; | ||
} | ||
|
||
// Set an empty dictionary if the value is null | ||
// so later code doesn't have to check for null condition | ||
if (!options) { | ||
options = {}; | ||
} | ||
|
||
// This is rudimentary logic to perform automatically check for conditions | ||
// that requires for API calls to succeed. Currently, I just check for | ||
// methods that need a session. If a session is needed but not available, | ||
// the code will automatically invoke login UI, then continue the API call | ||
if ((FB.Api.mSessions[method] || options.requireSession) && | ||
!FB.App.session) { | ||
// Need to connect first | ||
FB.App.connect(function() { | ||
FB.Api.invoke(method, parameters, callback, options); | ||
}); | ||
return; | ||
} | ||
|
||
// Is this UI method? | ||
if (method.indexOf('ui.') == 0) { | ||
FB.Api._invokeUi(method, parameters, callback, options); | ||
} else { | ||
FB.Api._invokeRest(method, parameters, callback, options); | ||
} | ||
|
||
}, | ||
|
||
/** | ||
* Make a REST Api call | ||
* @param {String} method | ||
* API method to call | ||
* @param {Object} parameters | ||
* parameters corresponding to the method | ||
* @param callback | ||
* @param options | ||
* optional options | ||
* @private | ||
*/ | ||
_invokeRest: function(method, parameters, callback, options) { | ||
FB.Api._callId++; | ||
var session = FB.App.session, | ||
callback_fn = "fb_" + FB.Api._callId; | ||
|
||
// We need to encode parameters and a new stuff | ||
// Let's use a copy instead of modifying parameters | ||
var params = { | ||
method: method, | ||
api_key: FB.App.apiKey, | ||
format: 'JSON', | ||
call_id: FB.Api._callId, | ||
v: '1.0', | ||
session_key: (session ? session.session_key : ''), | ||
callback: 'FB.Api._callbacks.' + callback_fn | ||
}; | ||
|
||
FB.copy(params, parameters, true, FB.Api._encode); | ||
|
||
if (session && session.secret) { | ||
// Use session secret | ||
params['ss'] = 1; | ||
params['sig'] = FB.Md5.sign(params, session.secret); | ||
} | ||
|
||
// JSONP Callback | ||
var script; | ||
|
||
FB.Api._callbacks[callback_fn] = function(data) { | ||
delete FB.Api._callbacks[callback_fn]; | ||
FB.Dom.removeDom(script); | ||
callback(data); | ||
}; | ||
|
||
script = FB.Dom.addScript(FB.Util.getFacebookUrl('api') + 'restserver.php?' + | ||
FB.Uri.createQueryString(params)); | ||
}, | ||
|
||
_encode: function(obj) { | ||
return typeof(obj) === 'string' ? obj : FB.JSON.serialize(obj); | ||
}, | ||
|
||
|
||
/** | ||
* | ||
* Calls the specified Facebook UI method with given parameters | ||
* @param {String} method | ||
* API method to call | ||
* @param {Object} parameters | ||
* parameters corresponding to the method | ||
* @param {function} callback callback function. The API result will passed to | ||
* callback function when result is read. | ||
* @param {object} options [Optional] | ||
* @private | ||
*/ | ||
_invokeUi: function(method, parameters, callback, options) { | ||
// TODO: We don't have UI server ready yet. | ||
// The method is supposed to a path to a endpont url (sans .php) | ||
var url = FB.Util.getFacebookUrl('www') + method.substring(3) + '.php'; | ||
|
||
var dialog = new FB.UI.Dialog() | ||
|
||
// We need to encode parameters and a new stuff | ||
// Let's use a copy instead of modifying parameters | ||
var params = { | ||
in_iframe: 1, | ||
callback: dialog.createClosingUrl(), | ||
preview: 'true', | ||
api_key: FB.App.apiKey, | ||
channel_url: FB.XdComm.receiverUrl, | ||
session_key: FB.App.session ? FB.App.session.session_key : '' | ||
}; | ||
|
||
FB.copy(params, parameters); | ||
|
||
url += "?" + FB.Uri.createQueryString(params, FB.Api._encode); | ||
|
||
FB.Event.add(dialog, 'closed', callback); | ||
dialog.set(method, FB.Util.format( | ||
'<iframe name="{0}" src="{1}" frameborder="0"></iframe>', | ||
dialog.id, url)); | ||
}, | ||
|
||
/** | ||
* map of methods that requires session. | ||
* [TODO] Complete the list | ||
* @private | ||
*/ | ||
mSessions: { | ||
'friends.get': 1 | ||
}, | ||
|
||
/** | ||
* | ||
* @type Number | ||
*/ | ||
_callId: 0, | ||
_callbacks: {} | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,219 @@ | ||
/** | ||
* @provides v2.FB.App | ||
* @requires v2.FB.Base v2.FB.Event v2.FB.XdComm v2.FB.UI.PopupWin, v2.FB.UI.Dialog v2.FB.Dom | ||
* | ||
*/ | ||
|
||
|
||
/** | ||
* This module provides application settings, states (session states, permissions, etc.) and auth related | ||
* methods (connect, logout) | ||
* @namespace FB.App | ||
*/ | ||
FB.provide('App', { | ||
/** | ||
* A dictionary of advanced facebook settings. | ||
* Currently supported key/value are: | ||
* @type object | ||
*/ | ||
settings: { | ||
'checkStatusOnInit': true // By default, we will start checking connect status when FB.init is called | ||
}, | ||
|
||
/** | ||
* current session. | ||
* When session exists, it is standard session structure that contains session_key, | ||
* uid, session_secret, expire_time | ||
* Note we use undefined | ||
* to signal that we don't know yet | ||
* @type object | ||
*/ | ||
session: undefined, | ||
|
||
/** | ||
* current connect status. Note we use undefined | ||
* to signal that we don't know yet | ||
* @type string | ||
*/ | ||
status: undefined, | ||
|
||
/** | ||
* initialize an Facebook app. xdChannelUrl parameter may not be needed if stats shows postMessage and flash XdComm have enough coverage | ||
* @param {String} apiKey | ||
* API key for your Facebook application | ||
* @param {String} xdChannelUrl | ||
* Relative URL to the cross-domain | ||
* channel file located on your servers. | ||
* @param {Object} settings | ||
* An optional dictionary of other application settings. | ||
* Currently supported key/value are: | ||
*/ | ||
init: function(apiKey, xdChannelUrl, settings) { | ||
// Check if init is already called. | ||
if (FB.App.apiKey) { | ||
return; | ||
} | ||
|
||
FB.App.apiKey = apiKey; | ||
if (settings) { | ||
FB.copy(FB.App.settings, settings); | ||
} | ||
|
||
if (FB.XdComm) { | ||
FB.XdComm.setReceiverUrl(xdChannelUrl); | ||
} | ||
|
||
if (FB.App.settings.checkStatusOnInit) { | ||
FB.App._checkStatus(); | ||
// Also start a timer to check every 60 min because our session expires | ||
// every two hours. | ||
setInterval(FB.App._checkStatus, 60 * 60 * 1000); | ||
} | ||
|
||
FB.Event.fire(FB.App, 'init'); | ||
}, | ||
|
||
/** | ||
* Watch for changes in FB.App. | ||
* @param {string} name Name of event. possible values are 'session', 'status', 'init' | ||
* @param {function} callback A callback function. arguments may be passed to the callback. | ||
* If the callback function returns true, the event will be unsubscribed. | ||
* @param {bool} no_sync_callback By default, the callback will be invoked immediately before | ||
* the event is fired, unless this parameter specified a true value | ||
* @param {bool} auto_unsubscribe By default, the callback will be invoked whenever the event fires. | ||
* However, if this parameter value is true, the callback will be unsubscribed from the event after | ||
* it is fired once. | ||
*/ | ||
monitor: function(name, callback, /*optional*/ no_sync_callback, /*optional*/ auto_unmonitor) { | ||
FB.Event.monitor(FB.App, name, callback, no_sync_callback, auto_unmonitor); | ||
}, | ||
|
||
/** | ||
* Add connect method to FB.App | ||
* This method is in a separate component because App's don't always | ||
* need to call connect method. | ||
* @namespace FB.App | ||
* @param {function} callback Callback to be invoked after connect result is known | ||
* @param {object} options | ||
*/ | ||
connect: function(callback, options) { | ||
// Check if we have session already | ||
if (FB.App.session) { | ||
callback(FB.App.session); | ||
return; | ||
} | ||
|
||
// Create login window if it's not already | ||
// open | ||
if (!FB.App._loginWin) { | ||
// Create a popup window dialog | ||
var dlg = new FB.UI.PopupWin(), | ||
q_params = { | ||
return_session: 1, | ||
nochrome: 1, | ||
fbconnect: 1, | ||
extern: 0, | ||
display: 'popup', | ||
api_key: FB.App.apiKey , | ||
v: FB.App.version, | ||
next: dlg.createClosingUrl(), | ||
cancel_url: dlg.createClosingUrl(), | ||
'perms': (options && options.perms) || '' | ||
}, | ||
|
||
url = FB.Util.getFacebookUrl('www') + 'login.php?' + | ||
FB.Uri.createQueryString(q_params); | ||
|
||
// When the dialog is closed, we will get an possible session | ||
// string back. At that time, we need to process updates | ||
// to FB.App.session and FB.App.connectState, then | ||
// fire a 'logoutResult' event to notify all the callback | ||
// functions passed to FB.App.connect methods. | ||
FB.Event.add(dlg, 'closed', function(data) { | ||
FB.App._loginWin = null; | ||
FB.App._onStatus(data); | ||
FB.Event.fire(FB.App, 'loginResult', FB.App.session) | ||
}); | ||
dlg.set('Login', url, 448, 426); | ||
FB.App._loginWin = dlg; | ||
} | ||
|
||
// Listen to loginResult event, which will be fired when | ||
// login popup closes. | ||
// Note we use event here because multiple FB.App.connect | ||
// calls may be made while a single login popup is up. | ||
// With event module, it naturally takes cares of notifying | ||
// multiple callbacks | ||
FB.Event.once(FB.App, 'loginResult', callback); | ||
}, | ||
|
||
/** | ||
* Log out of current session and facebook if current session. | ||
* Note this method is a separate component because logout method | ||
* is not always needed. | ||
* @param {function} callback This function will be called when operation is completed | ||
*/ | ||
logout: function(callback) { | ||
// Check if we have session already | ||
if (!FB.App.session) { | ||
callback(); | ||
return; | ||
} | ||
|
||
// Construct query parameters to logout.php | ||
q_params = { | ||
api_key: FB.App.apiKey, | ||
session_key: FB.App.session.session_key, | ||
next: FB.XdComm.getUdp(function() { | ||
FB.App._onStatus(''); | ||
callback(); | ||
}, null, 'parent') | ||
}; | ||
|
||
// Create a hiden iframe to perform the logout | ||
url = FB.Util.getFacebookUrl('www') + 'logout.php?' + | ||
FB.Uri.createQueryString(q_params); | ||
FB.Dom.createHiddenIFrame(url); | ||
}, | ||
|
||
///////////////////////// Private Members /////////////////////////////////// | ||
|
||
/** | ||
* callback handler when status string returned from somewhere (login_status, | ||
* login.php, etc.) Our existing wire prototype is not clean (not JSON | ||
* encoded), so we basically do some simple parsing for now. | ||
* @private | ||
*/ | ||
_onStatus: function(data) { | ||
var sessionKey = 'session='; | ||
var i = data.indexOf(sessionKey); | ||
var value = 'no_user'; | ||
if (i >= 0) { | ||
var s = decodeURIComponent(data.substr(i + sessionKey.length)); | ||
value = 'connected'; | ||
FB.Event.setProperty(FB.App, 'session', FB.JSON.deserialize(s)); | ||
} else if (data.indexOf('not_connected') >= 0) { | ||
value = 'not_connected'; | ||
} | ||
|
||
FB.Event.setProperty(FB.App, 'status', value); | ||
}, | ||
|
||
|
||
/** | ||
* This function create an hidden iframe to login_status.lphp | ||
* @private | ||
*/ | ||
_checkStatus: function() { | ||
var q_params = { api_key: FB.App.apiKey, extern: 0, | ||
ok_session: FB.XdComm.getUdp(FB.App._onStatus, null, 'parent', true), | ||
no_session: FB.XdComm.getUdp(FB.App._onStatus, 'not_connected', 'parent', true), | ||
no_user: FB.XdComm.getUdp(FB.App._onStatus, 'no_user', 'parent', true) | ||
}; | ||
|
||
FB.Dom.createHiddenIFrame( | ||
FB.Util.getFacebookUrl('www') + 'extern/login_status.php?' + | ||
FB.Uri.createQueryString(q_params)); | ||
} | ||
}); | ||
|
Oops, something went wrong.