facebook-stub 0.0.1.6 → 0.0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +2 -1
- data/bin/facebook-stub.js +752 -0
- data/lib/facebook_stub/rails/action_view_helper.rb +1 -1
- data/lib/facebook_stub/version.rb +1 -1
- data/test/server.rb +1 -1
- metadata +4 -3
data/Rakefile
CHANGED
@@ -5,13 +5,14 @@ require 'pathname'
|
|
5
5
|
ROOT = Pathname.new(File.expand_path('..',__FILE__))
|
6
6
|
LIB = ROOT + 'lib'
|
7
7
|
PKG = ROOT + 'pkg'
|
8
|
+
BIN = ROOT + 'bin'
|
8
9
|
|
9
10
|
task :build do
|
10
11
|
require 'sprockets'
|
11
12
|
environment = Sprockets::Environment.new
|
12
13
|
environment.append_path LIB.to_s
|
13
14
|
source = environment['facebook-stub.js'].source
|
14
|
-
|
15
|
+
BIN.join('facebook-stub.js').open('w'){|f| f.write source }
|
15
16
|
end
|
16
17
|
|
17
18
|
desc "run rspec"
|
@@ -0,0 +1,752 @@
|
|
1
|
+
(function(window, undefined) {
|
2
|
+
|
3
|
+
// var permissions = {
|
4
|
+
// "data": [
|
5
|
+
// {
|
6
|
+
// "installed": 1,
|
7
|
+
// "status_update": 1,
|
8
|
+
// "photo_upload": 1,
|
9
|
+
// "video_upload": 1,
|
10
|
+
// "email": 1,
|
11
|
+
// "create_note": 1,
|
12
|
+
// "share_item": 1,
|
13
|
+
// "publish_stream": 1,
|
14
|
+
// "publish_actions": 1
|
15
|
+
// }
|
16
|
+
// ]
|
17
|
+
// }
|
18
|
+
|
19
|
+
// two globals for creating the cookie
|
20
|
+
// FB Functions
|
21
|
+
function init(data) {
|
22
|
+
FBWorld.initialized = true;
|
23
|
+
state('appId', data.appId);
|
24
|
+
}
|
25
|
+
|
26
|
+
function missingPermissions(permissions){
|
27
|
+
var missing = [];
|
28
|
+
var perms = getPermissions();
|
29
|
+
var desired = permissions && permissions.split(',') || [];
|
30
|
+
for (var i=0; i<desired.length; i++){
|
31
|
+
if (!perms || !perms[desired[i]]) {
|
32
|
+
missing.push(desired[i]);
|
33
|
+
}
|
34
|
+
}
|
35
|
+
return missing;
|
36
|
+
}
|
37
|
+
|
38
|
+
// login
|
39
|
+
function login(callback, options) {
|
40
|
+
if (calledBeforeInit('login')) return;
|
41
|
+
if (FBWorld.state('loggedIn')) {
|
42
|
+
if (FBWorld.state('connected')) {
|
43
|
+
var missing = missingPermissions(options.scope);
|
44
|
+
if (!missing || missing.length === 0){
|
45
|
+
callback(getStatus());
|
46
|
+
}else{
|
47
|
+
options.missing_permissions = missing;
|
48
|
+
promptToAddPermissions(options, callback);
|
49
|
+
}
|
50
|
+
}else{
|
51
|
+
promptToConnect(options, callback);
|
52
|
+
}
|
53
|
+
}else{
|
54
|
+
promptToLogin(options, callback);
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
// simulate prompt to login
|
59
|
+
function promptToLogin(options, callback) {
|
60
|
+
FBWorld.beingPromptedToLogin = true;
|
61
|
+
FBWorld.beingPromptedToLoginOptions = options;
|
62
|
+
FBWorld.beingPromptedToLoginCallback = callback;
|
63
|
+
}
|
64
|
+
|
65
|
+
// simulates resolving a login prompt in one of three ways
|
66
|
+
function resolveLoginPrompt(successfull, facebook_uid) {
|
67
|
+
if (!FBWorld.beingPromptedToLogin) throw "you are not being prompted to login";
|
68
|
+
var
|
69
|
+
options = FBWorld.beingPromptedToLoginOptions,
|
70
|
+
callback = FBWorld.beingPromptedToLoginCallback;
|
71
|
+
|
72
|
+
// reset the FBWorld state
|
73
|
+
FBWorld.beingPromptedToLogin = false;
|
74
|
+
FBWorld.beingPromptedToLoginOptions = undefined;
|
75
|
+
FBWorld.beingPromptedToLoginCallback = undefined;
|
76
|
+
|
77
|
+
if (successfull){
|
78
|
+
FBWorld.setUid(facebook_uid);
|
79
|
+
FBWorld.loggedIn();
|
80
|
+
|
81
|
+
if (!FBWorld.state('connected')) {
|
82
|
+
promptToConnect(options, callback);
|
83
|
+
} else {
|
84
|
+
setPermissions(options.scope);
|
85
|
+
callback(getStatus());
|
86
|
+
}
|
87
|
+
|
88
|
+
} else {
|
89
|
+
FBWorld.notLoggedIn();
|
90
|
+
callback(getStatus());
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
function successfullyLogin(facebook_uid){
|
95
|
+
resolveLoginPrompt(true, facebook_uid);
|
96
|
+
}
|
97
|
+
|
98
|
+
function failToLogin(){
|
99
|
+
resolveLoginPrompt(false);
|
100
|
+
}
|
101
|
+
|
102
|
+
function cancelLogin(){
|
103
|
+
resolveLoginPrompt(false);
|
104
|
+
}
|
105
|
+
|
106
|
+
|
107
|
+
// connect to app
|
108
|
+
|
109
|
+
// simulate prompt to connect
|
110
|
+
function promptToConnect(options, callback) {
|
111
|
+
FBWorld.beingPromptedToConnect = true;
|
112
|
+
FBWorld.beingPromptedToConnectOptions = options;
|
113
|
+
FBWorld.beingPromptedToConnectCallback = callback;
|
114
|
+
}
|
115
|
+
|
116
|
+
function resolvePromptToConnect(approved) {
|
117
|
+
if (!FBWorld.beingPromptedToConnect) throw "you are not being prompted to connect";
|
118
|
+
var
|
119
|
+
options = FBWorld.beingPromptedToConnectOptions,
|
120
|
+
callback = FBWorld.beingPromptedToConnectCallback;
|
121
|
+
|
122
|
+
// reset the FBWorld state
|
123
|
+
FBWorld.beingPromptedToConnect = false;
|
124
|
+
FBWorld.beingPromptedToConnectOptions = undefined;
|
125
|
+
FBWorld.beingPromptedToConnectCallback = undefined;
|
126
|
+
|
127
|
+
if (approved){
|
128
|
+
FBWorld.connected();
|
129
|
+
setPermissions(options.scope);
|
130
|
+
} else {
|
131
|
+
FBWorld.notConnected();
|
132
|
+
}
|
133
|
+
|
134
|
+
callback(getStatus());
|
135
|
+
}
|
136
|
+
|
137
|
+
function acceptPromptToConnect() {
|
138
|
+
resolvePromptToConnect(true);
|
139
|
+
}
|
140
|
+
|
141
|
+
function denyPromptToConnect() {
|
142
|
+
resolvePromptToConnect(false);
|
143
|
+
}
|
144
|
+
|
145
|
+
function cancelPromptToConnect() {
|
146
|
+
resolvePromptToConnect(false);
|
147
|
+
}
|
148
|
+
|
149
|
+
// add permissions to app
|
150
|
+
|
151
|
+
// simulate prompt to add permissions
|
152
|
+
function promptToAddPermissions(options, callback) {
|
153
|
+
FBWorld.beingPromptedToAddPermissions = true;
|
154
|
+
FBWorld.beingPromptedToAddPermissionsOptions = options;
|
155
|
+
FBWorld.beingPromptedToAddPermissionsCallback = callback;
|
156
|
+
}
|
157
|
+
|
158
|
+
function resolvePromptToAddPermissions(approved, permissions) {
|
159
|
+
if (!FBWorld.beingPromptedToAddPermissions) throw "you are not being prompted to add permissions";
|
160
|
+
var
|
161
|
+
options = FBWorld.beingPromptedToAddPermissionsOptions,
|
162
|
+
callback = FBWorld.beingPromptedToAddPermissionsCallback;
|
163
|
+
|
164
|
+
// reset the FBWorld state
|
165
|
+
FBWorld.beingPromptedToAddPermissions = false;
|
166
|
+
FBWorld.beingPromptedToAddPermissionsOptions = undefined;
|
167
|
+
FBWorld.beingPromptedToAddPermissionsCallback = undefined;
|
168
|
+
|
169
|
+
if (approved){
|
170
|
+
if (permissions){
|
171
|
+
var existing_permissions = getPermissions();
|
172
|
+
if (existing_permissions){
|
173
|
+
var new_permissions = permissions.split(',');
|
174
|
+
for (var i=0; i<new_permissions.length; i++){
|
175
|
+
existing_permissions[new_permissions[i]] = 1;
|
176
|
+
}
|
177
|
+
setPermissions(Object.keys(existing_permissions).join(','));
|
178
|
+
}
|
179
|
+
else{
|
180
|
+
setPermissions(permissions);
|
181
|
+
}
|
182
|
+
}else{
|
183
|
+
setPermissions(options.scope);
|
184
|
+
}
|
185
|
+
}
|
186
|
+
|
187
|
+
callback(getStatus());
|
188
|
+
}
|
189
|
+
|
190
|
+
function acceptPromptToAddPermissions(permissions) {
|
191
|
+
resolvePromptToAddPermissions(true, permissions);
|
192
|
+
}
|
193
|
+
|
194
|
+
function skipPromptToAddPermissions() {
|
195
|
+
resolvePromptToAddPermissions(false);
|
196
|
+
}
|
197
|
+
|
198
|
+
function beingPromptedToAddThesePermissions(permissions){
|
199
|
+
if (FBWorld.beingPromptedToAddPermissionsOptions && FBWorld.beingPromptedToAddPermissionsOptions.missing_permissions) {
|
200
|
+
var missingOptions = FBWorld.beingPromptedToAddPermissionsOptions.missing_permissions;
|
201
|
+
return missingOptions.sort().toString() == permissions.split(',').sort().toString();
|
202
|
+
} else {
|
203
|
+
return false;
|
204
|
+
}
|
205
|
+
}
|
206
|
+
|
207
|
+
function hasPermissions(permissions) {
|
208
|
+
return missingPermissions(permissions).length === 0;
|
209
|
+
}
|
210
|
+
|
211
|
+
function logout(callback) {
|
212
|
+
if (calledBeforeInit('logout')) return;
|
213
|
+
if (!FBWorld.state('loggedIn')) console.log('FB.logout() called without a session.');
|
214
|
+
FBWorld.notLoggedIn();
|
215
|
+
callback(getStatus());
|
216
|
+
}
|
217
|
+
|
218
|
+
function getLoginStatus(callback, force) {
|
219
|
+
if (calledBeforeInit('getLoginStatus')) return;
|
220
|
+
callback(getStatus());
|
221
|
+
}
|
222
|
+
|
223
|
+
function getUserID() {
|
224
|
+
if (calledBeforeInit('getUserID')) return undefined;
|
225
|
+
return uid();
|
226
|
+
}
|
227
|
+
|
228
|
+
|
229
|
+
function getSession() {
|
230
|
+
if (calledBeforeInit('getSession')) return false;
|
231
|
+
return getStatus().session;
|
232
|
+
}
|
233
|
+
|
234
|
+
function api(location, callback) {
|
235
|
+
if (!FBWorld.state('connected')) {
|
236
|
+
callback(undefined);
|
237
|
+
} else if (location == '/me/friends') {
|
238
|
+
callback({data:FBWorld.friendList()});
|
239
|
+
} else if (location == '/me/permissions') {
|
240
|
+
var theState = FBWorld.state();
|
241
|
+
var perms;
|
242
|
+
if (theState && theState.perms && theState.perms.data) {
|
243
|
+
perms = {data:[theState.perms.data]};
|
244
|
+
}
|
245
|
+
callback( perms );
|
246
|
+
}
|
247
|
+
}
|
248
|
+
|
249
|
+
// FBWorld Functions
|
250
|
+
//3 states: loggedOut, loggedIn, connected
|
251
|
+
function state() {
|
252
|
+
var theState = JSON.parse(FBWorld.Helpers.makeMeACookie('fb-stub') || '{}');
|
253
|
+
if (arguments.length === 0) return theState;
|
254
|
+
if (arguments.length === 1) return theState[arguments[0]];
|
255
|
+
if (arguments.length === 2) {
|
256
|
+
theState[arguments[0]] = arguments[1];
|
257
|
+
FBWorld.Helpers.makeMeACookie('fb-stub', JSON.stringify(theState), cookieOptions);
|
258
|
+
return arguments[1];
|
259
|
+
}
|
260
|
+
if (arguments.length === 3) {
|
261
|
+
if(typeof(theState[arguments[0]]) == 'undefined') theState[arguments[0]] = {};
|
262
|
+
theState[arguments[0]][arguments[1]] = arguments[2];
|
263
|
+
FBWorld.Helpers.makeMeACookie('fb-stub', JSON.stringify(theState), cookieOptions);
|
264
|
+
return arguments[2];
|
265
|
+
}
|
266
|
+
}
|
267
|
+
|
268
|
+
function uid() {
|
269
|
+
return FBWorld.state('uid');
|
270
|
+
}
|
271
|
+
|
272
|
+
function setUid(newUid) {
|
273
|
+
return FBWorld.state('uid', newUid);
|
274
|
+
}
|
275
|
+
|
276
|
+
function setPermissions(newPermissions) {
|
277
|
+
return FBWorld.state('perms', 'data', convertPermsToData(newPermissions));
|
278
|
+
}
|
279
|
+
|
280
|
+
function setSecret(newSecret) {
|
281
|
+
return state('secret', newSecret);
|
282
|
+
}
|
283
|
+
|
284
|
+
function loggedIn() {
|
285
|
+
createConnectedCookie();
|
286
|
+
FBWorld.state('loggedIn', true);
|
287
|
+
FBWorld.state('perms', 'standard', '');
|
288
|
+
return true;
|
289
|
+
}
|
290
|
+
|
291
|
+
function notLoggedIn() {
|
292
|
+
deleteConnectedCookie();
|
293
|
+
FBWorld.state('loggedIn', false);
|
294
|
+
}
|
295
|
+
|
296
|
+
function connected() {
|
297
|
+
createConnectedCookie();
|
298
|
+
FBWorld.state('connected', true);
|
299
|
+
}
|
300
|
+
|
301
|
+
function notConnected() {
|
302
|
+
deleteConnectedCookie();
|
303
|
+
FBWorld.state('connected', false);
|
304
|
+
}
|
305
|
+
|
306
|
+
function addFriend(id, name) {
|
307
|
+
var friends = FBWorld.friendList();
|
308
|
+
friends.push({id: id, name: name});
|
309
|
+
FBWorld.Helpers.makeMeACookie('fb_friends', JSON.stringify(friends));
|
310
|
+
}
|
311
|
+
|
312
|
+
function friendList() {
|
313
|
+
return JSON.parse(FBWorld.Helpers.makeMeACookie('fb_friends') || '[]');
|
314
|
+
}
|
315
|
+
|
316
|
+
|
317
|
+
// sharing
|
318
|
+
|
319
|
+
function ui(options, callback) {
|
320
|
+
if (options.method === 'feed'){
|
321
|
+
FBWorld.beingPromptedToShare = true;
|
322
|
+
FBWorld.beingPromptedToShareOptions = options;
|
323
|
+
FBWorld.beingPromptedToShareCallback = callback;
|
324
|
+
}
|
325
|
+
}
|
326
|
+
|
327
|
+
// simulate closing the share prompt by either sharing or canceling
|
328
|
+
function resolveSharePrompt(way) {
|
329
|
+
response = {};
|
330
|
+
if (way === 'share') response.post_id = Math.floor(Math.random() * 100000);
|
331
|
+
//if (way === 'cancel');
|
332
|
+
|
333
|
+
if (typeof FBWorld.beingPromptedToShareCallback === 'function')
|
334
|
+
FBWorld.beingPromptedToShareCallback(response);
|
335
|
+
FBWorld.beingPromptedToShare = false;
|
336
|
+
FBWorld.beingPromptedToShareOptions = undefined;
|
337
|
+
FBWorld.beingPromptedToShareCallback = undefined;
|
338
|
+
}
|
339
|
+
|
340
|
+
function confirmSharePrompt(){
|
341
|
+
resolveSharePrompt('share');
|
342
|
+
}
|
343
|
+
|
344
|
+
function cancelSharePrompt(){
|
345
|
+
resolveSharePrompt('cancel');
|
346
|
+
}
|
347
|
+
|
348
|
+
var XFBML = {
|
349
|
+
parse: function(element, callback) {
|
350
|
+
callback();
|
351
|
+
}
|
352
|
+
};
|
353
|
+
|
354
|
+
FB = { // Emulates the FB API
|
355
|
+
getLoginStatus : getLoginStatus,
|
356
|
+
logout : logout,
|
357
|
+
login : login,
|
358
|
+
init : init,
|
359
|
+
getSession : getSession,
|
360
|
+
api : api,
|
361
|
+
XFBML : XFBML,
|
362
|
+
getUserID : getUserID,
|
363
|
+
ui : ui
|
364
|
+
};
|
365
|
+
|
366
|
+
FBWorld = { // used to set the state of Facebook
|
367
|
+
|
368
|
+
// the state of the Facebook World
|
369
|
+
state : state,
|
370
|
+
|
371
|
+
// Set the state of the Facebook World
|
372
|
+
loggedIn : loggedIn,
|
373
|
+
notLoggedIn : notLoggedIn,
|
374
|
+
setUid : setUid,
|
375
|
+
setSecret : setSecret,
|
376
|
+
uid : uid,
|
377
|
+
connected : connected,
|
378
|
+
notConnected : notConnected,
|
379
|
+
setPermissions : setPermissions,
|
380
|
+
getPermissions : getPermissions,
|
381
|
+
|
382
|
+
initialized : false,
|
383
|
+
|
384
|
+
// Simulate interactions with Facebook
|
385
|
+
|
386
|
+
// login
|
387
|
+
beingPromptedToLogin : false,
|
388
|
+
beingPromptedToLoginOptions : undefined,
|
389
|
+
beingPromptedToLoginCallback : undefined,
|
390
|
+
successfullyLogin : successfullyLogin,
|
391
|
+
failToLogin : failToLogin,
|
392
|
+
cancelLogin : cancelLogin,
|
393
|
+
|
394
|
+
// connecting
|
395
|
+
beingPromptedToConnect : false,
|
396
|
+
beingPromptedToConnectOptions : undefined,
|
397
|
+
beingPromptedToConnectCallback : undefined,
|
398
|
+
acceptPromptToConnect : acceptPromptToConnect,
|
399
|
+
denyPromptToConnect : denyPromptToConnect,
|
400
|
+
cancelPromptToConnect : cancelPromptToConnect,
|
401
|
+
|
402
|
+
// permissions
|
403
|
+
beingPromptedToAddPermissions : false,
|
404
|
+
beingPromptedToAddPermissionsOptions : undefined,
|
405
|
+
beingPromptedToAddPermissionsCallback : undefined,
|
406
|
+
beingPromptedToAddThesePermissions : beingPromptedToAddThesePermissions,
|
407
|
+
hasPermissions : hasPermissions,
|
408
|
+
acceptPromptToAddPermissions : acceptPromptToAddPermissions,
|
409
|
+
skipPromptToAddPermissions : skipPromptToAddPermissions,
|
410
|
+
|
411
|
+
//sharing
|
412
|
+
beingPromptedToShare : false,
|
413
|
+
beingPromptedToShareOptions : undefined,
|
414
|
+
beingPromptedToShareCallback : undefined,
|
415
|
+
confirmSharePrompt : confirmSharePrompt,
|
416
|
+
cancelSharePrompt : cancelSharePrompt,
|
417
|
+
|
418
|
+
//friends
|
419
|
+
addFriend : addFriend,
|
420
|
+
friendList : friendList
|
421
|
+
};
|
422
|
+
|
423
|
+
|
424
|
+
|
425
|
+
|
426
|
+
// PRIVATE FUNCTIONS
|
427
|
+
|
428
|
+
function getStatus() {
|
429
|
+
var theState = FBWorld.state();
|
430
|
+
|
431
|
+
// Connected
|
432
|
+
if (theState.loggedIn && theState.connected) {
|
433
|
+
var status = {
|
434
|
+
status: "connected",
|
435
|
+
authResponse: createConnectedCookie()
|
436
|
+
};
|
437
|
+
|
438
|
+
return status;
|
439
|
+
}
|
440
|
+
|
441
|
+
// not connected
|
442
|
+
if (theState.loggedIn && !theState.connected) {
|
443
|
+
return {
|
444
|
+
authResponse: null,
|
445
|
+
status: 'notConnected'
|
446
|
+
};
|
447
|
+
}
|
448
|
+
|
449
|
+
// not logged in
|
450
|
+
if (!theState.loggedIn) {
|
451
|
+
return {
|
452
|
+
authResponse: null,
|
453
|
+
status: 'unknown'
|
454
|
+
};
|
455
|
+
}
|
456
|
+
|
457
|
+
}
|
458
|
+
|
459
|
+
function getPermissions() {
|
460
|
+
var theState = FBWorld.state();
|
461
|
+
return theState.perms && theState.perms.data || undefined;
|
462
|
+
}
|
463
|
+
|
464
|
+
function calledBeforeInit(function_name) {
|
465
|
+
if (FBWorld.initialized) return false;
|
466
|
+
console.log("FB."+function_name+" called before FB.init");
|
467
|
+
return true;
|
468
|
+
}
|
469
|
+
|
470
|
+
function convertPermsToData(perms) {
|
471
|
+
var data = {};
|
472
|
+
perms = perms && perms.split(',') || [];
|
473
|
+
for (var i=0; i<perms.length; i++){
|
474
|
+
data[perms[i]] = 1;
|
475
|
+
}
|
476
|
+
return data;
|
477
|
+
}
|
478
|
+
|
479
|
+
var cookieOptions = { path: '/', domain: window.location.hostname.replace(/^www/, '')};
|
480
|
+
|
481
|
+
// cookie looks like this: (with the quotes): "access_token=theToken&base_domain=local-change.org&expires=0&secret=theSecret&session_key=theSessionKeysig=theSig-Hashed&uid=theUID"
|
482
|
+
function createConnectedCookie() {
|
483
|
+
var theState = {
|
484
|
+
user_id: state('uid'),
|
485
|
+
code: 'theAccessToken|hashData',
|
486
|
+
// We need to verify the timezone for this value. Traditionally FB uses PST8PDT, but it may be UTC.
|
487
|
+
issued_at: Math.floor(new Date().getTime() / 1000)
|
488
|
+
};
|
489
|
+
|
490
|
+
if (uid() !== null) {
|
491
|
+
theState.uid = uid();
|
492
|
+
}
|
493
|
+
|
494
|
+
var secret = state('secret');
|
495
|
+
if (!secret) throw "secret is not set. Use FBWorld.setSecret('secret')";
|
496
|
+
FBWorld.Helpers.makeMeACookie('fbsr_'+state('appId'), cookieToString(theState, secret), cookieOptions);
|
497
|
+
return theState;
|
498
|
+
}
|
499
|
+
|
500
|
+
function cookieToString(theState, secret) {
|
501
|
+
// Set the algorithm here, to keep any changes here.
|
502
|
+
theState.algorithm = 'HMAC-SHA256';
|
503
|
+
|
504
|
+
var payload = JSON.stringify(theState),
|
505
|
+
encodedPayload = FBWorld.Helpers.base64_encode(payload),
|
506
|
+
shaObj = new FBWorld.Helpers.jsSHA(encodedPayload, "ASCII"),
|
507
|
+
b64Signature = shaObj.getHMAC(secret, "ASCII", "SHA-256", "B64");
|
508
|
+
|
509
|
+
// jsSHA uses an odd Base64 encoder, which uses + where FB has -. For now we'll just replace them,
|
510
|
+
// but if we find other inconsistencies, we should use the HEX value and encode it ourselves.
|
511
|
+
b64Signature.replace('+', '-');
|
512
|
+
|
513
|
+
return b64Signature + '.' + encodedPayload;
|
514
|
+
}
|
515
|
+
|
516
|
+
function deleteConnectedCookie() {
|
517
|
+
FBWorld.Helpers.makeMeACookie('fbsr_'+state('appId'), null, cookieOptions);
|
518
|
+
}
|
519
|
+
|
520
|
+
|
521
|
+
})(this);
|
522
|
+
FBWorld.Helpers = {};
|
523
|
+
setTimeout(function() { if (typeof fbAsyncInit === 'function') fbAsyncInit(); }, 1);
|
524
|
+
/**
|
525
|
+
* Cookie plugin
|
526
|
+
*
|
527
|
+
* Copyright (c) 2006 Klaus Hartl (stilbuero.de)
|
528
|
+
* Dual licensed under the MIT and GPL licenses:
|
529
|
+
* http://www.opensource.org/licenses/mit-license.php
|
530
|
+
* http://www.gnu.org/licenses/gpl.html
|
531
|
+
*
|
532
|
+
*/
|
533
|
+
|
534
|
+
/**
|
535
|
+
* Create a cookie with the given name and value and other optional parameters.
|
536
|
+
*
|
537
|
+
* @example $.cookie('the_cookie', 'the_value');
|
538
|
+
* @desc Set the value of a cookie.
|
539
|
+
* @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
|
540
|
+
* @desc Create a cookie with all available options.
|
541
|
+
* @example $.cookie('the_cookie', 'the_value');
|
542
|
+
* @desc Create a session cookie.
|
543
|
+
* @example $.cookie('the_cookie', null);
|
544
|
+
* @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
|
545
|
+
* used when the cookie was set.
|
546
|
+
*
|
547
|
+
* @param String name The name of the cookie.
|
548
|
+
* @param String value The value of the cookie.
|
549
|
+
* @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
|
550
|
+
* @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
|
551
|
+
* If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
|
552
|
+
* If set to null or omitted, the cookie will be a session cookie and will not be retained
|
553
|
+
* when the the browser exits.
|
554
|
+
* @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
|
555
|
+
* @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
|
556
|
+
* @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
|
557
|
+
* require a secure protocol (like HTTPS).
|
558
|
+
* @type undefined
|
559
|
+
*
|
560
|
+
* @name $.cookie
|
561
|
+
* @cat Plugins/Cookie
|
562
|
+
* @author Klaus Hartl/klaus.hartl@stilbuero.de
|
563
|
+
*/
|
564
|
+
|
565
|
+
/**
|
566
|
+
* Get the value of a cookie with the given name.
|
567
|
+
*
|
568
|
+
* @example $.cookie('the_cookie');
|
569
|
+
* @desc Get the value of a cookie.
|
570
|
+
*
|
571
|
+
* @param String name The name of the cookie.
|
572
|
+
* @return The value of the cookie.
|
573
|
+
* @type String
|
574
|
+
*
|
575
|
+
* @name $.cookie
|
576
|
+
* @cat Plugins/Cookie
|
577
|
+
* @author Klaus Hartl/klaus.hartl@stilbuero.de
|
578
|
+
*/
|
579
|
+
|
580
|
+
// Modified to make it not use jquery
|
581
|
+
FBWorld.Helpers.makeMeACookie = function(name, value, options) {
|
582
|
+
if (typeof value != 'undefined') { // name and value given, set cookie
|
583
|
+
options = options || {};
|
584
|
+
if (value === null) {
|
585
|
+
value = '';
|
586
|
+
options.expires = -1;
|
587
|
+
} else {
|
588
|
+
options.expires = 100; // 100 days from now
|
589
|
+
}
|
590
|
+
var expires = '';
|
591
|
+
if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
|
592
|
+
var date;
|
593
|
+
if (typeof options.expires == 'number') {
|
594
|
+
date = new Date();
|
595
|
+
date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
|
596
|
+
} else {
|
597
|
+
date = options.expires;
|
598
|
+
}
|
599
|
+
expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
|
600
|
+
}
|
601
|
+
// CAUTION: Needed to parenthesize options.path and options.domain
|
602
|
+
// in the following expressions, otherwise they evaluate to undefined
|
603
|
+
// in the packed version for some reason...
|
604
|
+
var path = options.path ? '; path=' + (options.path) : '';
|
605
|
+
var domain = options.domain ? '; domain=' + (options.domain) : '';
|
606
|
+
var secure = options.secure ? '; secure' : '';
|
607
|
+
document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
|
608
|
+
} else { // only name given, get cookie
|
609
|
+
var cookieValue = null;
|
610
|
+
if (document.cookie && document.cookie != '') {
|
611
|
+
var cookies = document.cookie.split(';');
|
612
|
+
for (var i = 0; i < cookies.length; i++) {
|
613
|
+
var cookie = FBWorld.Helpers.trim(cookies[i]);
|
614
|
+
// Does this cookie string begin with the name we want?
|
615
|
+
if (cookie.substring(0, name.length + 1) == (name + '=')) {
|
616
|
+
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
617
|
+
break;
|
618
|
+
}
|
619
|
+
}
|
620
|
+
}
|
621
|
+
return cookieValue;
|
622
|
+
}
|
623
|
+
};
|
624
|
+
|
625
|
+
//Taken from jQuery
|
626
|
+
FBWorld.Helpers.trim = function( text ) {
|
627
|
+
return text == null ?
|
628
|
+
"" :
|
629
|
+
text.toString().replace( /^\s+/, "" ).replace( /\s+$/, "" );
|
630
|
+
};
|
631
|
+
/* A JavaScript implementation of the SHA family of hashes, as defined in FIPS
|
632
|
+
* PUB 180-2 as well as the corresponding HMAC implementation as defined in
|
633
|
+
* FIPS PUB 198a
|
634
|
+
*
|
635
|
+
* Version 1.3 Copyright Brian Turek 2008-2010
|
636
|
+
* Distributed under the BSD License
|
637
|
+
* See http://jssha.sourceforge.net/ for more information
|
638
|
+
*
|
639
|
+
* Several functions taken from Paul Johnson
|
640
|
+
*/
|
641
|
+
|
642
|
+
(function(){var charSize=8,b64pad="",hexCase=0,str2binb=function(a){var b=[],mask=(1<<charSize)-1,length=a.length*charSize,i;for(i=0;i<length;i+=charSize){b[i>>5]|=(a.charCodeAt(i/charSize)&mask)<<(32-charSize-(i%32))}return b},hex2binb=function(a){var b=[],length=a.length,i,num;for(i=0;i<length;i+=2){num=parseInt(a.substr(i,2),16);if(!isNaN(num)){b[i>>3]|=num<<(24-(4*(i%8)))}else{return"INVALID HEX STRING"}}return b},binb2hex=function(a){var b=(hexCase)?"0123456789ABCDEF":"0123456789abcdef",str="",length=a.length*4,i,srcByte;for(i=0;i<length;i+=1){srcByte=a[i>>2]>>((3-(i%4))*8);str+=b.charAt((srcByte>>4)&0xF)+b.charAt(srcByte&0xF)}return str},binb2b64=function(a){var b="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"+"0123456789+/",str="",length=a.length*4,i,j,triplet;for(i=0;i<length;i+=3){triplet=(((a[i>>2]>>8*(3-i%4))&0xFF)<<16)|(((a[i+1>>2]>>8*(3-(i+1)%4))&0xFF)<<8)|((a[i+2>>2]>>8*(3-(i+2)%4))&0xFF);for(j=0;j<4;j+=1){if(i*8+j*6<=a.length*32){str+=b.charAt((triplet>>6*(3-j))&0x3F)}else{str+=b64pad}}}return str},rotr=function(x,n){return(x>>>n)|(x<<(32-n))},shr=function(x,n){return x>>>n},ch=function(x,y,z){return(x&y)^(~x&z)},maj=function(x,y,z){return(x&y)^(x&z)^(y&z)},sigma0=function(x){return rotr(x,2)^rotr(x,13)^rotr(x,22)},sigma1=function(x){return rotr(x,6)^rotr(x,11)^rotr(x,25)},gamma0=function(x){return rotr(x,7)^rotr(x,18)^shr(x,3)},gamma1=function(x){return rotr(x,17)^rotr(x,19)^shr(x,10)},safeAdd_2=function(x,y){var a=(x&0xFFFF)+(y&0xFFFF),msw=(x>>>16)+(y>>>16)+(a>>>16);return((msw&0xFFFF)<<16)|(a&0xFFFF)},safeAdd_4=function(a,b,c,d){var e=(a&0xFFFF)+(b&0xFFFF)+(c&0xFFFF)+(d&0xFFFF),msw=(a>>>16)+(b>>>16)+(c>>>16)+(d>>>16)+(e>>>16);return((msw&0xFFFF)<<16)|(e&0xFFFF)},safeAdd_5=function(a,b,c,d,e){var f=(a&0xFFFF)+(b&0xFFFF)+(c&0xFFFF)+(d&0xFFFF)+(e&0xFFFF),msw=(a>>>16)+(b>>>16)+(c>>>16)+(d>>>16)+(e>>>16)+(f>>>16);return((msw&0xFFFF)<<16)|(f&0xFFFF)},coreSHA2=function(j,k,l){var a,b,c,d,e,f,g,h,T1,T2,H,lengthPosition,i,t,K,W=[],appendedMessageLength;if(l==="SHA-224"||l==="SHA-256"){lengthPosition=(((k+65)>>9)<<4)+15;K=[0x428A2F98,0x71374491,0xB5C0FBCF,0xE9B5DBA5,0x3956C25B,0x59F111F1,0x923F82A4,0xAB1C5ED5,0xD807AA98,0x12835B01,0x243185BE,0x550C7DC3,0x72BE5D74,0x80DEB1FE,0x9BDC06A7,0xC19BF174,0xE49B69C1,0xEFBE4786,0x0FC19DC6,0x240CA1CC,0x2DE92C6F,0x4A7484AA,0x5CB0A9DC,0x76F988DA,0x983E5152,0xA831C66D,0xB00327C8,0xBF597FC7,0xC6E00BF3,0xD5A79147,0x06CA6351,0x14292967,0x27B70A85,0x2E1B2138,0x4D2C6DFC,0x53380D13,0x650A7354,0x766A0ABB,0x81C2C92E,0x92722C85,0xA2BFE8A1,0xA81A664B,0xC24B8B70,0xC76C51A3,0xD192E819,0xD6990624,0xF40E3585,0x106AA070,0x19A4C116,0x1E376C08,0x2748774C,0x34B0BCB5,0x391C0CB3,0x4ED8AA4A,0x5B9CCA4F,0x682E6FF3,0x748F82EE,0x78A5636F,0x84C87814,0x8CC70208,0x90BEFFFA,0xA4506CEB,0xBEF9A3F7,0xC67178F2];if(l==="SHA-224"){H=[0xc1059ed8,0x367cd507,0x3070dd17,0xf70e5939,0xffc00b31,0x68581511,0x64f98fa7,0xbefa4fa4]}else{H=[0x6A09E667,0xBB67AE85,0x3C6EF372,0xA54FF53A,0x510E527F,0x9B05688C,0x1F83D9AB,0x5BE0CD19]}}j[k>>5]|=0x80<<(24-k%32);j[lengthPosition]=k;appendedMessageLength=j.length;for(i=0;i<appendedMessageLength;i+=16){a=H[0];b=H[1];c=H[2];d=H[3];e=H[4];f=H[5];g=H[6];h=H[7];for(t=0;t<64;t+=1){if(t<16){W[t]=j[t+i]}else{W[t]=safeAdd_4(gamma1(W[t-2]),W[t-7],gamma0(W[t-15]),W[t-16])}T1=safeAdd_5(h,sigma1(e),ch(e,f,g),K[t],W[t]);T2=safeAdd_2(sigma0(a),maj(a,b,c));h=g;g=f;f=e;e=safeAdd_2(d,T1);d=c;c=b;b=a;a=safeAdd_2(T1,T2)}H[0]=safeAdd_2(a,H[0]);H[1]=safeAdd_2(b,H[1]);H[2]=safeAdd_2(c,H[2]);H[3]=safeAdd_2(d,H[3]);H[4]=safeAdd_2(e,H[4]);H[5]=safeAdd_2(f,H[5]);H[6]=safeAdd_2(g,H[6]);H[7]=safeAdd_2(h,H[7])}switch(l){case"SHA-224":return[H[0],H[1],H[2],H[3],H[4],H[5],H[6]];case"SHA-256":return H;default:return[]}},jsSHA=function(a,b){this.sha224=null;this.sha256=null;this.strBinLen=null;this.strToHash=null;if("HEX"===b){if(0!==(a.length%2)){return"TEXT MUST BE IN BYTE INCREMENTS"}this.strBinLen=a.length*4;this.strToHash=hex2binb(a)}else if(("ASCII"===b)||('undefined'===typeof(b))){this.strBinLen=a.length*charSize;this.strToHash=str2binb(a)}else{return"UNKNOWN TEXT INPUT TYPE"}};jsSHA.prototype={getHash:function(a,b){var c=null,message=this.strToHash.slice();switch(b){case"HEX":c=binb2hex;break;case"B64":c=binb2b64;break;default:return"FORMAT NOT RECOGNIZED"}switch(a){case"SHA-224":if(null===this.sha224){this.sha224=coreSHA2(message,this.strBinLen,a)}return c(this.sha224);case"SHA-256":if(null===this.sha256){this.sha256=coreSHA2(message,this.strBinLen,a)}return c(this.sha256);default:return"HASH NOT RECOGNIZED"}},getHMAC:function(a,b,c,d){var e,keyToUse,i,retVal,keyBinLen,hashBitSize,keyWithIPad=[],keyWithOPad=[];switch(d){case"HEX":e=binb2hex;break;case"B64":e=binb2b64;break;default:return"FORMAT NOT RECOGNIZED"}switch(c){case"SHA-224":hashBitSize=224;break;case"SHA-256":hashBitSize=256;break;default:return"HASH NOT RECOGNIZED"}if("HEX"===b){if(0!==(a.length%2)){return"KEY MUST BE IN BYTE INCREMENTS"}keyToUse=hex2binb(a);keyBinLen=a.length*4}else if("ASCII"===b){keyToUse=str2binb(a);keyBinLen=a.length*charSize}else{return"UNKNOWN KEY INPUT TYPE"}if(64<(keyBinLen/8)){keyToUse=coreSHA2(keyToUse,keyBinLen,c);keyToUse[15]&=0xFFFFFF00}else if(64>(keyBinLen/8)){keyToUse[15]&=0xFFFFFF00}for(i=0;i<=15;i+=1){keyWithIPad[i]=keyToUse[i]^0x36363636;keyWithOPad[i]=keyToUse[i]^0x5C5C5C5C}retVal=coreSHA2(keyWithIPad.concat(this.strToHash),512+this.strBinLen,c);retVal=coreSHA2(keyWithOPad.concat(retVal),512+hashBitSize,c);return(e(retVal))}};window.FBWorld.Helpers.jsSHA=jsSHA}());
|
643
|
+
FBWorld.Helpers.base64_encode = function (data, utf8encode) {
|
644
|
+
// http://kevin.vanzonneveld.net
|
645
|
+
// + original by: Tyler Akins (http://rumkin.com)
|
646
|
+
// + improved by: Bayron Guevara
|
647
|
+
// + improved by: Thunder.m
|
648
|
+
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
649
|
+
// + bugfixed by: Pellentesque Malesuada
|
650
|
+
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
651
|
+
// + improved by: Rafał Kukawski (http://kukawski.pl)
|
652
|
+
// - depends on: utf8_encode
|
653
|
+
// * example 1: base64_encode('Kevin van Zonneveld');
|
654
|
+
// * returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
|
655
|
+
// mozilla has this native
|
656
|
+
// - but breaks in 2.0.0.12!
|
657
|
+
//if (typeof this.window['atob'] == 'function') {
|
658
|
+
// return atob(data);
|
659
|
+
//}
|
660
|
+
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
661
|
+
var o1, o2, o3, h1, h2, h3, h4, bits, i = 0,
|
662
|
+
ac = 0,
|
663
|
+
enc = "",
|
664
|
+
tmp_arr = [];
|
665
|
+
|
666
|
+
if (!data) {
|
667
|
+
return data;
|
668
|
+
}
|
669
|
+
|
670
|
+
// Only do this if forced
|
671
|
+
if (utf8encode)
|
672
|
+
data = this.utf8_encode(data + '');
|
673
|
+
|
674
|
+
do { // pack three octets into four hexets
|
675
|
+
o1 = data.charCodeAt(i++);
|
676
|
+
o2 = data.charCodeAt(i++);
|
677
|
+
o3 = data.charCodeAt(i++);
|
678
|
+
|
679
|
+
bits = o1 << 16 | o2 << 8 | o3;
|
680
|
+
|
681
|
+
h1 = bits >> 18 & 0x3f;
|
682
|
+
h2 = bits >> 12 & 0x3f;
|
683
|
+
h3 = bits >> 6 & 0x3f;
|
684
|
+
h4 = bits & 0x3f;
|
685
|
+
|
686
|
+
// use hexets to index into b64, and append result to encoded string
|
687
|
+
tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
|
688
|
+
} while (i < data.length);
|
689
|
+
|
690
|
+
enc = tmp_arr.join('');
|
691
|
+
|
692
|
+
var r = data.length % 3;
|
693
|
+
|
694
|
+
return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
|
695
|
+
|
696
|
+
};
|
697
|
+
FBWorld.Helpers.utf8_encode = function (argString) {
|
698
|
+
// http://kevin.vanzonneveld.net
|
699
|
+
// + original by: Webtoolkit.info (http://www.webtoolkit.info/)
|
700
|
+
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
701
|
+
// + improved by: sowberry
|
702
|
+
// + tweaked by: Jack
|
703
|
+
// + bugfixed by: Onno Marsman
|
704
|
+
// + improved by: Yves Sucaet
|
705
|
+
// + bugfixed by: Onno Marsman
|
706
|
+
// + bugfixed by: Ulrich
|
707
|
+
// + bugfixed by: Rafal Kukawski
|
708
|
+
// * example 1: utf8_encode('Kevin van Zonneveld');
|
709
|
+
// * returns 1: 'Kevin van Zonneveld'
|
710
|
+
|
711
|
+
if (argString === null || typeof argString === "undefined") {
|
712
|
+
return "";
|
713
|
+
}
|
714
|
+
|
715
|
+
var string = (argString + ''); // .replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
716
|
+
var utftext = "",
|
717
|
+
start, end, stringl = 0;
|
718
|
+
|
719
|
+
start = end = 0;
|
720
|
+
stringl = string.length;
|
721
|
+
for (var n = 0; n < stringl; n++) {
|
722
|
+
var c1 = string.charCodeAt(n);
|
723
|
+
var enc = null;
|
724
|
+
|
725
|
+
if (c1 < 128) {
|
726
|
+
end++;
|
727
|
+
} else if (c1 > 127 && c1 < 2048) {
|
728
|
+
enc = String.fromCharCode((c1 >> 6) | 192) + String.fromCharCode((c1 & 63) | 128);
|
729
|
+
} else {
|
730
|
+
enc = String.fromCharCode((c1 >> 12) | 224) + String.fromCharCode(((c1 >> 6) & 63) | 128) + String.fromCharCode((c1 & 63) | 128);
|
731
|
+
}
|
732
|
+
if (enc !== null) {
|
733
|
+
if (end > start) {
|
734
|
+
utftext += string.slice(start, end);
|
735
|
+
}
|
736
|
+
utftext += enc;
|
737
|
+
start = end = n + 1;
|
738
|
+
}
|
739
|
+
}
|
740
|
+
|
741
|
+
if (end > start) {
|
742
|
+
utftext += string.slice(start, stringl);
|
743
|
+
}
|
744
|
+
|
745
|
+
return utftext;
|
746
|
+
}
|
747
|
+
;
|
748
|
+
|
749
|
+
|
750
|
+
|
751
|
+
|
752
|
+
|
@@ -3,7 +3,7 @@ module FacebookStub
|
|
3
3
|
module ActionViewHelper
|
4
4
|
@@facebook_javascript_stub = nil
|
5
5
|
def include_facebook_stub
|
6
|
-
@@facebook_javascript_stub ||= javascript_tag File.read File.expand_path('../../../../
|
6
|
+
@@facebook_javascript_stub ||= javascript_tag File.read File.expand_path('../../../../bin/facebook-stub.js', __FILE__)
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
data/test/server.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: facebook-stub
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.1.
|
4
|
+
version: 0.0.1.7
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -152,6 +152,7 @@ files:
|
|
152
152
|
- Gemfile
|
153
153
|
- Rakefile
|
154
154
|
- Readme.md
|
155
|
+
- bin/facebook-stub.js
|
155
156
|
- facebook-stub.gemspec
|
156
157
|
- lib/facebook-stub.js
|
157
158
|
- lib/facebook-stub.rb
|
@@ -182,7 +183,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
182
183
|
version: '0'
|
183
184
|
segments:
|
184
185
|
- 0
|
185
|
-
hash:
|
186
|
+
hash: -521301745040137504
|
186
187
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
187
188
|
none: false
|
188
189
|
requirements:
|
@@ -191,7 +192,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
191
192
|
version: '0'
|
192
193
|
segments:
|
193
194
|
- 0
|
194
|
-
hash:
|
195
|
+
hash: -521301745040137504
|
195
196
|
requirements: []
|
196
197
|
rubyforge_project: facebook-stub
|
197
198
|
rubygems_version: 1.8.21
|