Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
34
gaia/apps/communications/facebook/fb_sync.html
Executable file
34
gaia/apps/communications/facebook/fb_sync.html
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
<meta charset="UTF-8">
|
||||
<meta name="defaultLanguage" content="en-US">
|
||||
<meta name="availableLanguages" content="en-US">
|
||||
<link rel="localization" href="/shared/locales/import_contacts/import_contacts.{locale}.properties">
|
||||
<script type="text/javascript" src="/shared/js/l10n.js"></script>
|
||||
|
||||
<script src="/shared/js/async_storage.js"></script>
|
||||
<script src="/shared/js/contacts/import/import_status_data.js"></script>
|
||||
<script src="/shared/js/lazy_loader.js"></script>
|
||||
<script src="/shared/js/notification_helper.js"></script>
|
||||
<script src="/shared/pages/import/js/parameters.js"></script>
|
||||
<script src="/shared/js/contacts/import/utilities/config.js"></script>
|
||||
<script src="/shared/js/contacts/utilities/image_square.js"></script>
|
||||
<script src="/shared/js/contacts/utilities/image_thumbnail.js"></script>
|
||||
<script src="/shared/js/contacts/import/utilities/misc.js"></script>
|
||||
<script src="/shared/js/fb/fb_request.js"></script>
|
||||
<script src="/shared/js/contacts/import/facebook/fb_data.js"></script>
|
||||
<script src="/shared/js/contacts/import/facebook/fb_contact.js"></script>
|
||||
<script src="/shared/js/contacts/import/facebook/fb_utils.js"></script>
|
||||
<script src="/shared/js/fb/fb_reader_utils.js"></script>
|
||||
<script src="/shared/js/contacts/import/facebook/fb_contact_utils.js"></script>
|
||||
<script src="js/fb_sync.js"></script>
|
||||
<script src="/contacts/js/fb/fb_init.js"></script>
|
||||
<script src="js/fb_sync_init.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p id="status"></p>
|
||||
</body>
|
||||
</html>
|
||||
29
gaia/apps/communications/facebook/js/console.js
Executable file
29
gaia/apps/communications/facebook/js/console.js
Executable file
|
|
@ -0,0 +1,29 @@
|
|||
/* exported console */
|
||||
'use strict';
|
||||
|
||||
(function Console(exports) {
|
||||
|
||||
function getString(a) {
|
||||
var out = '';
|
||||
for (var c = 0; c < a.length; c++) {
|
||||
out += a[c];
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
exports.console = {
|
||||
'error': function() {
|
||||
self.postMessage({
|
||||
type: 'error',
|
||||
data: getString(arguments)
|
||||
});
|
||||
},
|
||||
'log': function() {
|
||||
self.postMessage({
|
||||
type: 'trace',
|
||||
data: getString(arguments)
|
||||
});
|
||||
}
|
||||
};
|
||||
})(self);
|
||||
479
gaia/apps/communications/facebook/js/fb_sync.js
Executable file
479
gaia/apps/communications/facebook/js/fb_sync.js
Executable file
|
|
@ -0,0 +1,479 @@
|
|||
/* global utils */
|
||||
'use strict';
|
||||
|
||||
var fb = window.fb || {};
|
||||
|
||||
if (!fb.sync) {
|
||||
(function() {
|
||||
var Sync = fb.sync = {};
|
||||
|
||||
var theWorker;
|
||||
// Facebook contacts currently under update process
|
||||
var fbContactsById;
|
||||
|
||||
var totalToChange = 0,
|
||||
changed = 0;
|
||||
|
||||
// Next timestamp to be set
|
||||
var nextTimestamp;
|
||||
|
||||
var completionCallback,
|
||||
errorCallback;
|
||||
|
||||
// Only makes sense when the data from FB is provided to the sync module
|
||||
// i.e. it is not the worker which obtains that data
|
||||
var fbFriendsDataByUid;
|
||||
|
||||
var logLevel = fb.logLevel || parent.fb.logLevel || 'DEBUG';
|
||||
var isDebug = (logLevel === 'DEBUG');
|
||||
|
||||
var alarmFrame = null,
|
||||
currentAlarmRequest = null;
|
||||
|
||||
// Delay in closing the iframe that schedules the alarm.
|
||||
// Avoiding console.log issues in the logcat
|
||||
var DELAY_ALARM_SCHED = 5000;
|
||||
|
||||
function debug() {
|
||||
if (isDebug) {
|
||||
var theArgs = ['<<FBSync>>'];
|
||||
for (var c = 0; c < arguments.length; c++) {
|
||||
theArgs.push(arguments[c]);
|
||||
}
|
||||
window.console.log.apply(window.console, theArgs);
|
||||
}
|
||||
}
|
||||
|
||||
// Starts the worker
|
||||
function startWorker() {
|
||||
theWorker = new Worker('/facebook/js/sync_worker.js');
|
||||
theWorker.onmessage = function(e) {
|
||||
workerMessage(e.data);
|
||||
};
|
||||
theWorker.onerror = function(e) {
|
||||
window.console.error('Worker Error', e.message, e.lineno, e.column);
|
||||
if (typeof errorCallback === 'function') {
|
||||
errorCallback({
|
||||
type: 'default_error'
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function workerMessage(m) {
|
||||
switch (m.type) {
|
||||
case 'query_error':
|
||||
var error = m.data || {};
|
||||
window.console.error('FB: Error reported by the worker',
|
||||
JSON.stringify(error));
|
||||
if (typeof errorCallback === 'function') {
|
||||
errorCallback({
|
||||
name: 'defaultError'
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'token_error':
|
||||
debug('FB: Token error reported by the worker');
|
||||
if (typeof errorCallback === 'function') {
|
||||
errorCallback({
|
||||
name: 'invalidToken'
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'timeout_error':
|
||||
debug('Timeout error reported by the worker');
|
||||
if (typeof errorCallback === 'function') {
|
||||
errorCallback({
|
||||
name: 'timeout'
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case 'trace':
|
||||
debug(m.data);
|
||||
break;
|
||||
|
||||
case 'friendRemoved':
|
||||
removeFbFriend(m.data.contactId);
|
||||
break;
|
||||
|
||||
case 'friendUpdated':
|
||||
updateFbFriend(m.data.contactId,
|
||||
fb.friend2mozContact(m.data.updatedFbData));
|
||||
break;
|
||||
|
||||
// Message with the totals
|
||||
case 'totals':
|
||||
changed = 0;
|
||||
totalToChange = m.data.totalToChange;
|
||||
nextTimestamp = m.data.queryTimestamp;
|
||||
var newFriendNumber = m.data.newFriendNumber;
|
||||
if (newFriendNumber) {
|
||||
debug('New friend number: ', newFriendNumber);
|
||||
fb.utils.setCachedNumFriends(newFriendNumber);
|
||||
}
|
||||
|
||||
debug('Total to be changed: ', totalToChange);
|
||||
|
||||
// If totals === 0 then the completion callback will be invoked
|
||||
checkTotals();
|
||||
break;
|
||||
|
||||
case 'friendImgReady':
|
||||
debug('Friend Img Data ready: ', m.data.contactId);
|
||||
updateFbFriendWhenImageReady(m.data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function updateFbFriendWhenImageReady(data) {
|
||||
var contact = fbContactsById[data.contactId];
|
||||
var uid = fb.getFriendUid(contact);
|
||||
var updatedFbData = fbFriendsDataByUid[uid];
|
||||
|
||||
if (!data.photo) {
|
||||
updateFbFriend(data.contactId, updatedFbData);
|
||||
return;
|
||||
}
|
||||
|
||||
utils.thumbnailImage(data.photo, function gotTumbnail(thumbnail) {
|
||||
var fbInfo = {};
|
||||
fbInfo.photo = [data.photo, thumbnail];
|
||||
fb.setFriendPictureUrl(fbInfo, updatedFbData.pic_big);
|
||||
updatedFbData.fbInfo = fbInfo;
|
||||
|
||||
updateFbFriend(data.contactId, updatedFbData);
|
||||
});
|
||||
}
|
||||
|
||||
function onsuccessCb() {
|
||||
changed++;
|
||||
checkTotals();
|
||||
}
|
||||
|
||||
// Updates the FB data from a friend
|
||||
function updateFbFriend(contactId, cfdata) {
|
||||
fb.friend2mozContact(cfdata);
|
||||
|
||||
cfdata.fbInfo = cfdata.fbInfo || {};
|
||||
|
||||
cfdata.fbInfo.org = [fb.getWorksAt(cfdata)];
|
||||
var birthDate = null;
|
||||
if (cfdata.birthday_date && cfdata.birthday_date.length > 0) {
|
||||
birthDate = fb.getBirthDate(cfdata.birthday_date);
|
||||
}
|
||||
cfdata.fbInfo.bday = birthDate;
|
||||
|
||||
var address = fb.getAddresses(cfdata);
|
||||
if (address) {
|
||||
cfdata.fbInfo.adr = address;
|
||||
}
|
||||
|
||||
if (cfdata.shortTelephone) {
|
||||
cfdata.fbInfo.shortTelephone = cfdata.shortTelephone;
|
||||
delete cfdata.shortTelephone;
|
||||
}
|
||||
|
||||
// Then the new data saved to the cache
|
||||
var fbContact = new fb.Contact(fbContactsById[contactId]);
|
||||
var fbReq = fbContact.update(cfdata);
|
||||
|
||||
// Nothing special
|
||||
fbReq.onsuccess = function() {
|
||||
debug('Friend updated correctly', cfdata.uid);
|
||||
onsuccessCb();
|
||||
};
|
||||
|
||||
// Error. mark the contact as pending to be synchronized
|
||||
fbReq.onerror = function() {
|
||||
window.console.error('FB: Error while saving contact data: ',
|
||||
cfdata.uid);
|
||||
changed++;
|
||||
checkTotals();
|
||||
};
|
||||
}
|
||||
|
||||
function removeFbFriend(contactId) {
|
||||
debug('Removing Friend: ', contactId);
|
||||
|
||||
var removedFriend = fbContactsById[contactId];
|
||||
|
||||
var fbContact = new fb.Contact(removedFriend);
|
||||
|
||||
if (fb.isFbLinked(removedFriend)) {
|
||||
debug('Friend is linked: ', contactId);
|
||||
// No care about what happens
|
||||
var req = fbContact.unlink('hard');
|
||||
req.onsuccess = onsuccessCb;
|
||||
|
||||
req.onerror = function() {
|
||||
window.console.error('FB. Error while hard unlinking friend: ',
|
||||
contactId);
|
||||
// The counter has to be increased anyway
|
||||
changed++;
|
||||
checkTotals();
|
||||
};
|
||||
}
|
||||
else {
|
||||
debug('Friend is not linked: ', contactId);
|
||||
var fbContactReq = fbContact.remove();
|
||||
fbContactReq.onsuccess = onsuccessCb;
|
||||
fbContactReq.onerror = function() {
|
||||
window.console.error('FB. Error while removing contact: ',
|
||||
contactId);
|
||||
// The counter has to be increased anyway
|
||||
changed++;
|
||||
checkTotals();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function checkTotals() {
|
||||
if (changed === totalToChange) {
|
||||
debug('Sync process finished!');
|
||||
|
||||
if (window.contacts && window.contacts.List) {
|
||||
window.setTimeout(window.contacts.List.load, 0);
|
||||
}
|
||||
|
||||
// Once sync has finished the last update date is set
|
||||
// Thus we ensure next will happen in the next <period> hours
|
||||
fb.utils.setLastUpdate(nextTimestamp, function sync_end() {
|
||||
completionCallback(totalToChange);
|
||||
});
|
||||
|
||||
if (theWorker) {
|
||||
theWorker.terminate();
|
||||
theWorker = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Starts a synchronization
|
||||
Sync.start = function(params) {
|
||||
if (params) {
|
||||
completionCallback = params.success;
|
||||
errorCallback = params.error;
|
||||
}
|
||||
|
||||
totalToChange = 0;
|
||||
changed = 0;
|
||||
|
||||
// First only take into account those Friends already on the device
|
||||
// This work has to be done here and not by the worker as it has no
|
||||
// access to the Web APIs
|
||||
var req = fb.utils.getAllFbContacts();
|
||||
|
||||
req.onsuccess = function() {
|
||||
var uids = {};
|
||||
var fbContacts = req.result;
|
||||
|
||||
if (fbContacts.length === 0) {
|
||||
debug('Nothing to be synchronized. No FB Contacts present');
|
||||
return;
|
||||
}
|
||||
|
||||
startWorker();
|
||||
|
||||
// Contacts by id are cached for later update
|
||||
fbContactsById = {};
|
||||
// Contacts for which an update will be forced
|
||||
var forceUpdate = {};
|
||||
|
||||
fbContacts.forEach(function(contact) {
|
||||
fbContactsById[contact.id] = contact;
|
||||
var pictureUrl = fb.getFriendPictureUrl(contact);
|
||||
var uid = fb.getFriendUid(contact);
|
||||
|
||||
uids[uid] = {
|
||||
contactId: contact.id,
|
||||
photoUrl: pictureUrl
|
||||
};
|
||||
|
||||
if (!pictureUrl) {
|
||||
forceUpdate[uid] = {
|
||||
contactId: contact.id
|
||||
// photoUrl is left undefined as it is not known
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
fb.utils.getLastUpdate(function run_worker(ts) {
|
||||
fb.utils.getCachedAccessToken(function(access_token) {
|
||||
// The worker must start
|
||||
theWorker.postMessage({
|
||||
type: 'start',
|
||||
data: {
|
||||
uids: uids,
|
||||
imgNeedsUpdate: forceUpdate,
|
||||
timestamp: ts,
|
||||
access_token: access_token,
|
||||
operationsTimeout: fb.operationsTimeout,
|
||||
targetPictureSize: utils.misc.getPreferredPictureDetail()
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
req.onerror = function() {
|
||||
window.console.error('FB: Error while getting friends on the device',
|
||||
req.error.name);
|
||||
if (typeof errorCallback === 'function') {
|
||||
errorCallback({
|
||||
name: 'defaultError'
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Schedules a next synchronization
|
||||
Sync.scheduleNextSync = function() {
|
||||
//
|
||||
currentAlarmRequest = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function() {
|
||||
// The alarm has to be scheduled by the same page that will handle it
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=800431
|
||||
alarmFrame = document.createElement('iframe');
|
||||
alarmFrame.src = '/facebook/fb_sync.html';
|
||||
alarmFrame.width = 1;
|
||||
alarmFrame.height = 1;
|
||||
alarmFrame.style.display = 'none';
|
||||
document.body.appendChild(alarmFrame);
|
||||
},0);
|
||||
|
||||
return currentAlarmRequest;
|
||||
};
|
||||
|
||||
function cleanAlarmSchedFrame() {
|
||||
alarmFrame.src = null;
|
||||
document.body.removeChild(alarmFrame);
|
||||
alarmFrame = null;
|
||||
}
|
||||
|
||||
Sync.onAlarmScheduled = function(date) {
|
||||
debug('Next synch scheduled at: ', date);
|
||||
if (alarmFrame) {
|
||||
window.setTimeout(cleanAlarmSchedFrame, DELAY_ALARM_SCHED);
|
||||
}
|
||||
currentAlarmRequest.done(date);
|
||||
};
|
||||
|
||||
|
||||
Sync.onAlarmError = function(e) {
|
||||
if (alarmFrame) {
|
||||
window.setTimeout(cleanAlarmSchedFrame, DELAY_ALARM_SCHED);
|
||||
}
|
||||
|
||||
window.console.error('<<FB Sync>> Error while scheduling a new sync: ',
|
||||
e);
|
||||
|
||||
currentAlarmRequest.failed(e);
|
||||
};
|
||||
|
||||
Sync.debug = function() {
|
||||
debug.apply(this, arguments);
|
||||
};
|
||||
|
||||
// Starts a synchronization with data coming from import / link
|
||||
Sync.startWithData = function(contactList, myFriendsByUid, callbacks) {
|
||||
completionCallback = callbacks.success;
|
||||
errorCallback = callbacks.error;
|
||||
nextTimestamp = Date.now();
|
||||
|
||||
changed = 0;
|
||||
// As it is not a priori known how many are going to needed a change
|
||||
totalToChange = Number.MAX_VALUE;
|
||||
|
||||
debug('Starting Synchronization with data');
|
||||
|
||||
fbFriendsDataByUid = myFriendsByUid;
|
||||
// Friends to be updated by the worker (those which profile img changed)
|
||||
var toBeUpdated = {};
|
||||
|
||||
fb.utils.getLastUpdate(function import_updates(lastUpdate) {
|
||||
var toBeChanged = 0;
|
||||
var lastUpdateTime = Math.round(lastUpdate / 1000);
|
||||
|
||||
debug('Last update time: ', lastUpdateTime);
|
||||
fbContactsById = {};
|
||||
|
||||
contactList.forEach(function(aContact) {
|
||||
fbContactsById[aContact.id] = aContact;
|
||||
|
||||
var uid = fb.getFriendUid(aContact);
|
||||
|
||||
var friendData = fbFriendsDataByUid[uid];
|
||||
if (friendData) {
|
||||
var friendUpdate = friendData.profile_update_time;
|
||||
debug('Friend update Time ', friendUpdate, 'for UID: ', uid);
|
||||
|
||||
var profileImgUrl = fb.getFriendPictureUrl(aContact);
|
||||
|
||||
if (friendUpdate > lastUpdateTime ||
|
||||
profileImgUrl !== friendData.pic_big) {
|
||||
debug('Friend changed!! : ', uid);
|
||||
|
||||
if (profileImgUrl !== friendData.pic_big) {
|
||||
debug('Profile img changed: ', profileImgUrl);
|
||||
|
||||
toBeUpdated[uid] = {
|
||||
contactId: aContact.id
|
||||
};
|
||||
}
|
||||
else {
|
||||
debug('Updating friend: ', friendData.uid);
|
||||
toBeChanged++;
|
||||
updateFbFriend(aContact.id, friendData);
|
||||
}
|
||||
}
|
||||
else {
|
||||
debug('Friend has not changed', uid);
|
||||
}
|
||||
}
|
||||
else {
|
||||
debug('Removing friend: ', aContact.id);
|
||||
toBeChanged++;
|
||||
removeFbFriend(aContact.id);
|
||||
}
|
||||
});
|
||||
|
||||
debug('First pass of Updates and removed finished');
|
||||
|
||||
// Those friends which image has changed will require help from the
|
||||
// worker
|
||||
var toBeUpdatedList = Object.keys(toBeUpdated);
|
||||
if (toBeUpdatedList.length > 0) {
|
||||
totalToChange = toBeChanged + toBeUpdatedList.length;
|
||||
|
||||
debug('Starting worker for updating img data');
|
||||
startWorker();
|
||||
|
||||
fb.utils.getCachedAccessToken(function(access_token) {
|
||||
|
||||
theWorker.postMessage({
|
||||
type: 'startWithData',
|
||||
data: {
|
||||
access_token: access_token,
|
||||
uids: toBeUpdated,
|
||||
operationsTimeout: fb.operationsTimeout,
|
||||
targetPictureSize: utils.misc.getPreferredPictureDetail()
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
else {
|
||||
totalToChange = toBeChanged;
|
||||
checkTotals();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
})();
|
||||
}
|
||||
272
gaia/apps/communications/facebook/js/fb_sync_init.js
Executable file
272
gaia/apps/communications/facebook/js/fb_sync_init.js
Executable file
|
|
@ -0,0 +1,272 @@
|
|||
/* globals NotificationHelper */
|
||||
|
||||
'use strict';
|
||||
|
||||
var fb = window.fb || {};
|
||||
var Sync = fb.sync || {};
|
||||
fb.sync = Sync;
|
||||
|
||||
(function() {
|
||||
var Alarm = Sync.alarm = {};
|
||||
|
||||
var ALARM_ID_KEY = fb.utils.ALARM_ID_KEY;
|
||||
var isSyncOngoing = false;
|
||||
|
||||
// This is the amount of hours to wait to retry a sync operation
|
||||
var DEFAULT_RETRY_PERIOD = 1;
|
||||
// Retry period after timeout (15 minutes)
|
||||
var RETRY_PERIOD_TIMEOUT = 0.25;
|
||||
// Delay in closing the window
|
||||
var CLOSE_DELAY = 5000;
|
||||
var FB_TRAY_ICON = '/contacts/style/images/f_logo.png';
|
||||
var FB_SYNC_ERROR_PARAM = 'isSyncError';
|
||||
|
||||
Alarm.init = function() {
|
||||
fb.init(function fb_alarm_init() {
|
||||
if (navigator.mozHasPendingMessage('alarm')) {
|
||||
fb.sync.debug('Alarm System Message Received!!!!!');
|
||||
navigator.mozSetMessageHandler('alarm', handleAlarm);
|
||||
}
|
||||
// Workaround for Gecko Bug. mozHasPendingMessage does not work
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=802876
|
||||
else if (parent.location === window.location) {
|
||||
fb.sync.debug('Fb Sync woke up. Alarm ok. mozHasPendingMsg failed');
|
||||
// Unfortunately this does not also work
|
||||
// navigator.mozSetMessageHandler('alarm', handleAlarm);
|
||||
window.setTimeout(function() {
|
||||
handleAlarm({
|
||||
data: {
|
||||
sync: true
|
||||
}
|
||||
});
|
||||
},0);
|
||||
}
|
||||
else {
|
||||
setNextAlarm(true, fb.syncPeriod);
|
||||
}
|
||||
}); // fb.init
|
||||
}; // Alarm.init
|
||||
|
||||
function syncSuccess() {
|
||||
fb.sync.debug('Sync finished ok at ', new Date());
|
||||
isSyncOngoing = false;
|
||||
|
||||
setNextAlarm(false, fb.syncPeriod, function() {
|
||||
closeApp();
|
||||
});
|
||||
} // syncSuccess function
|
||||
|
||||
function syncError(error) {
|
||||
isSyncOngoing = false;
|
||||
var theError = error;
|
||||
|
||||
if (!theError || theError.name === 'undefined') {
|
||||
theError = {
|
||||
name: 'defaultError'
|
||||
};
|
||||
}
|
||||
|
||||
switch (theError.name) {
|
||||
case 'invalidToken':
|
||||
fb.sync.debug('Invalid token!!!. Notifying the user');
|
||||
// A new alarm is not set. It will be set once the user
|
||||
// logs in Facebook one more time
|
||||
showNotification({
|
||||
title: 'facebook',
|
||||
body: 'notificationLogin',
|
||||
iconURL: FB_TRAY_ICON + '?' + FB_SYNC_ERROR_PARAM + '=1',
|
||||
callback: handleInvalidToken
|
||||
});
|
||||
break;
|
||||
|
||||
case 'timeout':
|
||||
fb.sync.debug('Timeout in sync. Next sync scheduled in 15 minutes');
|
||||
scheduleAt(RETRY_PERIOD_TIMEOUT, closeApp);
|
||||
break;
|
||||
|
||||
case 'defaultError':
|
||||
window.console.error('Error reported in synchronization: ',
|
||||
JSON.stringify(theError));
|
||||
showNotification({
|
||||
title: 'facebook',
|
||||
body: 'syncError',
|
||||
iconURL: FB_TRAY_ICON + '?' + FB_SYNC_ERROR_PARAM + '=1',
|
||||
callback: function() {
|
||||
scheduleAt(fb.syncPeriod, closeApp);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
} // syncError function
|
||||
|
||||
|
||||
function handleAlarm(message) {
|
||||
// First is checked if this is a sync alarm
|
||||
if (message.data && message.data.sync === true &&
|
||||
isSyncOngoing === false && navigator.onLine === true) {
|
||||
isSyncOngoing = true;
|
||||
fb.sync.debug('Starting sync at: ', new Date());
|
||||
|
||||
ackAlarm(function alarm_process() {
|
||||
fb.sync.start({
|
||||
success: syncSuccess,
|
||||
error: syncError
|
||||
});
|
||||
});
|
||||
}
|
||||
else if (isSyncOngoing === true) {
|
||||
fb.sync.debug('There is an ongoing synchronization. Trying it later');
|
||||
scheduleAt(DEFAULT_RETRY_PERIOD, closeApp);
|
||||
}
|
||||
else if (navigator.onLine === false) {
|
||||
fb.sync.debug('Navigator is not online. Setting an alarm for next hour');
|
||||
scheduleAt(DEFAULT_RETRY_PERIOD, closeApp);
|
||||
}
|
||||
else {
|
||||
fb.sync.debug('Alarm message but apparently was not a sync message');
|
||||
closeApp();
|
||||
}
|
||||
}
|
||||
|
||||
function alarmSetErrorCb(notifyParent, e) {
|
||||
if (notifyParent) {
|
||||
window.setTimeout(function() {
|
||||
parent.fb.sync.onAlarmError(e.target.error);
|
||||
},0);
|
||||
}
|
||||
else {
|
||||
window.console.error('<<FBSync>> Error while setting next alarm: ',
|
||||
e.target.error.name);
|
||||
}
|
||||
}
|
||||
|
||||
function scheduleAt(hours, callback) {
|
||||
var nextUpdate = Date.now() + hours * 60 * 60 * 1000;
|
||||
var scheduledDate = new Date(nextUpdate);
|
||||
|
||||
fb.sync.debug('Forcing scheduling an alarm at: ', scheduledDate);
|
||||
|
||||
var req = addAlarm(scheduledDate);
|
||||
|
||||
req.onsuccess = function() {
|
||||
fb.sync.debug('--> Next Sync forced to happen at: ', scheduledDate);
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
req.onerror = function(e) {
|
||||
alarmSetErrorCb(false, e);
|
||||
};
|
||||
}
|
||||
|
||||
function ackAlarm(callback) {
|
||||
// The next alarmid is removed
|
||||
// Continuation is enabled even if we cannot remove the id
|
||||
window.asyncStorage.removeItem(ALARM_ID_KEY, callback, callback);
|
||||
}
|
||||
|
||||
// Adds an alarm at the specified datetime ensuring any existing alarm
|
||||
// it is removed and ensuring the id is stored on the asyncStorage
|
||||
function addAlarm(at) {
|
||||
var outReq = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function do_add_alarm() {
|
||||
window.asyncStorage.getItem(ALARM_ID_KEY, function set_alarm(id) {
|
||||
if (id) {
|
||||
navigator.mozAlarms.remove(Number(id));
|
||||
}
|
||||
|
||||
var req = navigator.mozAlarms.add(at, 'honorTimezone', {
|
||||
sync: true
|
||||
});
|
||||
// Setting the new alarm
|
||||
req.onsuccess = function() {
|
||||
window.asyncStorage.setItem(ALARM_ID_KEY, String(req.result),
|
||||
function success_store() {
|
||||
outReq.done(req.result);
|
||||
},
|
||||
function error_store(e) {
|
||||
var errorParam = {
|
||||
target: {
|
||||
error: e || {
|
||||
name: 'AsyncStorageError'
|
||||
}
|
||||
}
|
||||
};
|
||||
outReq.failed(errorParam);
|
||||
});
|
||||
};
|
||||
|
||||
req.onerror = function(e) {
|
||||
outReq.failed(e);
|
||||
};
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return outReq;
|
||||
}
|
||||
|
||||
function setNextAlarm(notifyParent, hours, callback) {
|
||||
fb.utils.getLastUpdate(function(timestamp) {
|
||||
var nextUpdate = timestamp + hours * 60 * 60 * 1000;
|
||||
var scheduledDate = new Date(nextUpdate);
|
||||
|
||||
fb.sync.debug('Going to set a new alarm at: ', scheduledDate);
|
||||
|
||||
var req = addAlarm(scheduledDate);
|
||||
req.onsuccess = function() {
|
||||
if (notifyParent === true) {
|
||||
window.setTimeout(function() {
|
||||
parent.fb.sync.onAlarmScheduled(scheduledDate);
|
||||
},0);
|
||||
}
|
||||
|
||||
fb.sync.debug('--> Next Sync will happen at: ', scheduledDate);
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
req.onerror = function(e) {
|
||||
alarmSetErrorCb(notifyParent, e);
|
||||
};
|
||||
|
||||
}); // Get last update
|
||||
} // doSetNextAlarm
|
||||
|
||||
function showNotification(params) {
|
||||
navigator.mozApps.getSelf().onsuccess = function(evt) {
|
||||
var app = evt.target.result;
|
||||
var iconURL = app.installOrigin + params.iconURL;
|
||||
var options = {
|
||||
body: params.body,
|
||||
icon: iconURL
|
||||
};
|
||||
|
||||
/*jshint unused:false*/
|
||||
NotificationHelper.send(params.title, options)
|
||||
.then(notification => {
|
||||
if (typeof params.callback === 'function') {
|
||||
params.callback();
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
function closeApp() {
|
||||
// Wait some seconds to avoid any kind of race condition or console error
|
||||
window.setTimeout(window.close, CLOSE_DELAY);
|
||||
}
|
||||
|
||||
function handleInvalidToken() {
|
||||
// TODO: Reset token and other stuff in order to better inform the user
|
||||
closeApp();
|
||||
}
|
||||
|
||||
// Everything starts
|
||||
Alarm.init();
|
||||
|
||||
})();
|
||||
384
gaia/apps/communications/facebook/js/sync_worker.js
Executable file
384
gaia/apps/communications/facebook/js/sync_worker.js
Executable file
|
|
@ -0,0 +1,384 @@
|
|||
/* globals fb */
|
||||
'use strict';
|
||||
|
||||
importScripts('/shared/js/fb/fb_request.js',
|
||||
'/shared/js/contacts/import/facebook/fb_query.js',
|
||||
'/shared/js/contacts/import/facebook/fb_contact_utils.js',
|
||||
'/shared/js/fb/fb_reader_utils.js',
|
||||
'console.js');
|
||||
|
||||
(function(wutils) {
|
||||
|
||||
var uids,
|
||||
timestamp,
|
||||
access_token,
|
||||
forceUpdateUids,
|
||||
targetPictureSize;
|
||||
|
||||
wutils.addEventListener('message', processMessage);
|
||||
|
||||
debug('Worker up and running ....');
|
||||
|
||||
// Query to know what friends need to be updated
|
||||
var UPDATED_QUERY = [
|
||||
'SELECT uid, name, first_name, last_name, ' ,
|
||||
'middle_name, birthday_date, email, current_location, ' ,
|
||||
'work, phones, hometown_location, pic_big ' ,
|
||||
' FROM user' ,
|
||||
' WHERE uid ',
|
||||
' IN (SELECT uid1 FROM friend WHERE uid2=me()',
|
||||
' AND uid1 IN (',
|
||||
null,
|
||||
') )',
|
||||
' AND profile_update_time > ',
|
||||
null,
|
||||
' OR uid IN (',
|
||||
null,
|
||||
')'
|
||||
];
|
||||
|
||||
// Query to know what friends need to be removed
|
||||
var REMOVED_QUERY = [
|
||||
'SELECT target_id FROM ',
|
||||
'connection where source_id = me() ',
|
||||
' AND target_type = "user" AND is_deleted="true"',
|
||||
' AND target_id IN (',
|
||||
null,
|
||||
')'
|
||||
];
|
||||
|
||||
// Query to know the total amount of friends
|
||||
var COUNT_QUERY = 'SELECT uid FROM user WHERE uid IN ' +
|
||||
'(SELECT uid1 FROM friend WHERE uid2=me())';
|
||||
|
||||
// Indicates whether the worker started with or without data
|
||||
var withData = false;
|
||||
|
||||
function debug() {
|
||||
function getString(a) {
|
||||
var out = '';
|
||||
for (var c = 0; c < a.length; c++) {
|
||||
out += a[c];
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
self.console.log(getString(arguments));
|
||||
}
|
||||
|
||||
function errorQueryCb(e) {
|
||||
self.console.error('<<FB Sync>>: Error while trying to sync', e);
|
||||
postError({
|
||||
type: 'query_error',
|
||||
data: {}
|
||||
});
|
||||
}
|
||||
|
||||
function timeoutQueryCb(e) {
|
||||
postError({
|
||||
type: 'timeout_error'
|
||||
});
|
||||
}
|
||||
|
||||
function postError(e) {
|
||||
// Message type to propagate to the worker parent
|
||||
var type = 'query_error';
|
||||
if (e && e.code === 190) {
|
||||
self.console.log('This is a token error. Notifying worker parent');
|
||||
type = 'token_error';
|
||||
}
|
||||
else if (e && e.type === 'timeout_error') {
|
||||
self.console.log('This is a timeout error. Notifying worker parent');
|
||||
type = e.type;
|
||||
}
|
||||
wutils.postMessage({
|
||||
type: type,
|
||||
data: e
|
||||
});
|
||||
}
|
||||
|
||||
function buildQueries(ts, uids, forcedUids) {
|
||||
var uidsFilter = uids.join(',');
|
||||
|
||||
// The index at which the timestamp is set
|
||||
var IDX_TS = 10;
|
||||
UPDATED_QUERY[IDX_TS] = Math.round(ts / 1000);
|
||||
|
||||
// The index at which uids filter is set
|
||||
var IDX_UIDS = 7;
|
||||
UPDATED_QUERY[IDX_UIDS] = uidsFilter;
|
||||
|
||||
// The index at which those uids forced to be updated is set
|
||||
var IDX_FORCE = 12;
|
||||
var forceUpdateUidsFilter = '';
|
||||
|
||||
if (forcedUids && forcedUids.length > 0) {
|
||||
forceUpdateUidsFilter = forcedUids.join(',');
|
||||
}
|
||||
UPDATED_QUERY[IDX_FORCE] = forceUpdateUidsFilter;
|
||||
|
||||
var R_IDX_UIDS = 4;
|
||||
REMOVED_QUERY[R_IDX_UIDS] = uidsFilter;
|
||||
|
||||
// Two queries launched at the same time
|
||||
var outQueries = {
|
||||
query1: UPDATED_QUERY.join(''),
|
||||
query2: REMOVED_QUERY.join('')
|
||||
};
|
||||
|
||||
// In any sync without data (periodic one) we also request the total amount
|
||||
// of friends in order to update it
|
||||
// Please take into account that that cannot be done with a friend_count
|
||||
// query as it also returns those friends who are deactivated
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=838605
|
||||
if (!withData) {
|
||||
outQueries.query3 = COUNT_QUERY;
|
||||
}
|
||||
|
||||
return JSON.stringify(outQueries);
|
||||
}
|
||||
|
||||
function processMessage(e) {
|
||||
var message = e.data;
|
||||
|
||||
if (message.type === 'start') {
|
||||
withData = false;
|
||||
|
||||
uids = message.data.uids;
|
||||
access_token = message.data.access_token;
|
||||
timestamp = message.data.timestamp;
|
||||
forceUpdateUids = message.data.imgNeedsUpdate;
|
||||
fb.operationsTimeout = message.data.operationsTimeout;
|
||||
targetPictureSize = message.data.targetPictureSize;
|
||||
|
||||
debug('Worker acks contacts to check: ', Object.keys(uids).length);
|
||||
|
||||
if (forceUpdateUids && forceUpdateUids.length > 0) {
|
||||
debug('These friends are forced to be updated: ' ,
|
||||
JSON.stringify(forceUpdateUids));
|
||||
}
|
||||
|
||||
getFriendsToBeUpdated(Object.keys(uids), Object.keys(forceUpdateUids));
|
||||
}
|
||||
else if (message.type === 'startWithData') {
|
||||
withData = true;
|
||||
debug('worker Acks start with data');
|
||||
|
||||
fb.operationsTimeout = message.data.operationsTimeout;
|
||||
uids = message.data.uids;
|
||||
access_token = message.data.access_token;
|
||||
targetPictureSize = message.data.targetPictureSize;
|
||||
|
||||
getNewImgsForFriends(Object.keys(uids), access_token);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Launch a multiple query to obtain friends to be updated and deleted
|
||||
function getFriendsToBeUpdated(uids, forcedUids) {
|
||||
var query = buildQueries(timestamp, uids, forcedUids);
|
||||
var callbacks = {
|
||||
success: friendsReady,
|
||||
error: errorQueryCb,
|
||||
timeout: timeoutQueryCb
|
||||
};
|
||||
|
||||
fb.utils.runQuery(query, callbacks, access_token);
|
||||
}
|
||||
|
||||
// Callback executed when data is ready
|
||||
function friendsReady(response) {
|
||||
// Timestamp is captured right now to avoid problems
|
||||
// with updates in between
|
||||
var qts = Date.now();
|
||||
if (typeof response.error === 'undefined') {
|
||||
var updateList = response.data[0].fql_result_set;
|
||||
var removeList = response.data[1].fql_result_set;
|
||||
// removeList = [{target_id: '100001127136581'}];
|
||||
|
||||
var theData = {
|
||||
totalToChange: updateList.length + removeList.length,
|
||||
queryTimestamp: qts
|
||||
};
|
||||
|
||||
if (!withData) {
|
||||
if (response.data[2] &&
|
||||
Array.isArray(response.data[2].fql_result_set)) {
|
||||
var friendCount = response.data[2].fql_result_set.length;
|
||||
theData.newFriendNumber = friendCount;
|
||||
}
|
||||
}
|
||||
|
||||
wutils.postMessage({
|
||||
type: 'totals',
|
||||
data: theData
|
||||
});
|
||||
|
||||
syncUpdatedFriends(updateList);
|
||||
syncRemovedFriends(removeList);
|
||||
}
|
||||
else {
|
||||
postError(response.error);
|
||||
}
|
||||
}
|
||||
|
||||
function syncRemovedFriends(removedFriends) {
|
||||
debug('Friends to be removed: ', removedFriends.length);
|
||||
|
||||
// Simply an iteration over the collection is done and a message passed
|
||||
removedFriends.forEach(function(aremoved) {
|
||||
var removedRef = uids[aremoved.target_id];
|
||||
|
||||
if (removedRef) {
|
||||
wutils.postMessage({
|
||||
type: 'friendRemoved',
|
||||
data: {
|
||||
uid: aremoved.target_id,
|
||||
contactId: removedRef.contactId
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}); // forEach
|
||||
}
|
||||
|
||||
|
||||
function syncUpdatedFriends(updatedFriends) {
|
||||
debug('Friends to be updated: ', updatedFriends.length);
|
||||
|
||||
// Friends which image has to be updated
|
||||
var friendsImgToBeUpdated = {};
|
||||
|
||||
updatedFriends.forEach(function(afriend) {
|
||||
var friendInfo = forceUpdateUids[afriend.uid] || uids[afriend.uid];
|
||||
|
||||
if (!friendInfo) {
|
||||
debug('The uid provided is unknown. Doing nothing');
|
||||
return;
|
||||
}
|
||||
|
||||
if (afriend.pic_big !== friendInfo.photoUrl) {
|
||||
// Photo changed
|
||||
debug('Contact Photo Changed!!! for ', afriend.uid);
|
||||
friendsImgToBeUpdated[afriend.uid] = afriend;
|
||||
}
|
||||
else {
|
||||
debug('Contact Photo unchanged for ', afriend.uid);
|
||||
wutils.postMessage({
|
||||
type: 'friendUpdated',
|
||||
data: {
|
||||
updatedFbData: afriend,
|
||||
contactId: friendInfo.contactId
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
debug('Worker terminated processing updates');
|
||||
|
||||
var friendImgList = Object.keys(friendsImgToBeUpdated);
|
||||
|
||||
if (friendImgList.length > 0) {
|
||||
debug('Now starting synch for ', friendImgList.length, ' images');
|
||||
|
||||
// Now it is time to download the images needed
|
||||
var imgSync = new ImgSynchronizer(friendImgList);
|
||||
|
||||
imgSync.start();
|
||||
|
||||
// Once an image is ready friend update is notified
|
||||
imgSync.onimageready = function(uid, blob) {
|
||||
var friendData;
|
||||
if (blob) {
|
||||
debug('Image retrieved correctly for ', uid);
|
||||
|
||||
friendData = friendsImgToBeUpdated[uid];
|
||||
friendData.fbInfo = {};
|
||||
friendData.fbInfo.photo = [blob];
|
||||
fb.setFriendPictureUrl(friendData.fbInfo, friendData.pic_big);
|
||||
}
|
||||
else {
|
||||
self.console.error('Img for UID', uid, ' could not be retrieved ');
|
||||
// This friend has to be marked in a special state just to be
|
||||
// synced later on
|
||||
friendData = friendsImgToBeUpdated[uid];
|
||||
friendData.fbInfo = {};
|
||||
friendData.fbInfo.photo = [];
|
||||
// TODO: This should only remove the URL of the profile picture
|
||||
friendData.fbInfo.url = [];
|
||||
}
|
||||
|
||||
var contact = uids[uid] || forceUpdateUids[uid];
|
||||
|
||||
wutils.postMessage({
|
||||
type: 'friendUpdated',
|
||||
data: {
|
||||
updatedFbData: friendData,
|
||||
contactId: contact.contactId
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// For dealing with the case that only new imgs have to be retrieved
|
||||
function getNewImgsForFriends(friendList) {
|
||||
debug('Getting new imgs for friends', JSON.stringify(friendList));
|
||||
|
||||
var imgSync = new ImgSynchronizer(friendList);
|
||||
|
||||
imgSync.start();
|
||||
|
||||
// Once an image is ready friend update is notified
|
||||
imgSync.onimageready = function(uid, blob) {
|
||||
self.console.log('Img Ready from worker');
|
||||
|
||||
if (!blob) {
|
||||
self.console.error('Img for UID: ', uid, ' could not be retrieved ');
|
||||
}
|
||||
|
||||
wutils.postMessage({
|
||||
type: 'friendImgReady',
|
||||
data: {
|
||||
photo: blob,
|
||||
contactId: uids[uid].contactId
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
var ImgSynchronizer = function(friends) {
|
||||
var next = 0;
|
||||
var self = this;
|
||||
|
||||
this.friends = friends;
|
||||
|
||||
this.start = function() {
|
||||
retrieveImg(this.friends[next]);
|
||||
};
|
||||
|
||||
function imgRetrieved(blob) {
|
||||
if (typeof self.onimageready === 'function') {
|
||||
var uid = self.friends[next];
|
||||
|
||||
wutils.setTimeout(function() {
|
||||
self.onimageready(uid, blob);
|
||||
},0);
|
||||
}
|
||||
|
||||
// And lets go for the next
|
||||
next++;
|
||||
if (next < self.friends.length) {
|
||||
retrieveImg(self.friends[next]);
|
||||
}
|
||||
}
|
||||
|
||||
function retrieveImg(uid) {
|
||||
fb.utils.getFriendPicture(uid, imgRetrieved, access_token,
|
||||
targetPictureSize);
|
||||
}
|
||||
};
|
||||
|
||||
})(self);
|
||||
17
gaia/apps/communications/facebook/test/unit/.jshintrc
Executable file
17
gaia/apps/communications/facebook/test/unit/.jshintrc
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"extends": "../../../../../.jshintrc",
|
||||
"predef": [
|
||||
"assert",
|
||||
"suite",
|
||||
"test",
|
||||
"setup",
|
||||
"teardown",
|
||||
"suiteSetup",
|
||||
"suiteTeardown",
|
||||
"require",
|
||||
"requireApp",
|
||||
"sinon",
|
||||
"mocha",
|
||||
"loadBodyHTML"
|
||||
]
|
||||
}
|
||||
102
gaia/apps/communications/facebook/test/unit/facebook_connector_test.js
Executable file
102
gaia/apps/communications/facebook/test/unit/facebook_connector_test.js
Executable file
|
|
@ -0,0 +1,102 @@
|
|||
/* globals MockFbQuery, MockOauthflow*/
|
||||
'use strict';
|
||||
|
||||
require('/shared/js/contacts/import/facebook/fb_contact_utils.js');
|
||||
require('/shared/js/fb/fb_reader_utils.js');
|
||||
require('/shared/js/contacts/import/utilities/misc.js');
|
||||
require('/shared/js/contacts/import/facebook/fb_contact.js');
|
||||
require('/shared/js/contacts/import/facebook/facebook_connector.js');
|
||||
requireApp('communications/facebook/test/unit/mock_fb_graph_data.js');
|
||||
requireApp('communications/facebook/test/unit/mock_fb_query.js');
|
||||
requireApp('communications/contacts/test/unit/mock_oauthflow.js');
|
||||
|
||||
var realFbUtils,
|
||||
realOauthflow,
|
||||
subject;
|
||||
|
||||
if (!window.FacebookConnector) {
|
||||
window.FacebookConnector = null;
|
||||
}
|
||||
|
||||
if (!window.fb) {
|
||||
window.fb = null;
|
||||
}
|
||||
|
||||
if (!window.oauthflow) {
|
||||
window.oauthflow = null;
|
||||
}
|
||||
|
||||
suite('Facebook Connector Tests', function() {
|
||||
suiteSetup(function() {
|
||||
realFbUtils = window.fb.utils;
|
||||
window.fb.utils = MockFbQuery;
|
||||
|
||||
subject = window.FacebookConnector;
|
||||
|
||||
realOauthflow = window.oauthflow;
|
||||
window.oauthflow = MockOauthflow;
|
||||
});
|
||||
|
||||
|
||||
test('List all friends. Adapt data for showing', function(done) {
|
||||
subject.listAllContacts('fake_token', {
|
||||
success: function(data) {
|
||||
assert.isNotNull(data.data);
|
||||
assert.isTrue(Array.isArray(data.data));
|
||||
|
||||
var theData = data.data[0];
|
||||
var adapted = subject.adaptDataForShowing(theData);
|
||||
|
||||
assert.equal(adapted.givenName[0], 'Carlos Angel');
|
||||
assert.equal(adapted.familyName[0], 'Bureta');
|
||||
assert.equal(adapted.email1, 'ppburetaxyz@gmail.com');
|
||||
assert.equal(adapted.uid, 45677);
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('List all friends. Adapt Data for Saving', function(done) {
|
||||
subject.listAllContacts('fake_token', {
|
||||
success: function(data) {
|
||||
assert.isNotNull(data.data);
|
||||
assert.isTrue(Array.isArray(data.data));
|
||||
|
||||
var theData = data.data[0];
|
||||
var adapted = subject.adaptDataForSaving(theData);
|
||||
|
||||
assert.equal(adapted.givenName[0], 'Carlos Angel');
|
||||
assert.equal(adapted.familyName[0], 'Bureta');
|
||||
|
||||
assert.equal(adapted.email[0].type[0], 'other');
|
||||
assert.equal(adapted.email[0].value, 'ppburetaxyz@gmail.com');
|
||||
|
||||
assert.equal(adapted.tel[0].type[0], 'other');
|
||||
assert.equal(adapted.tel[0].value, '+34609274801');
|
||||
|
||||
assert.equal(adapted.fbInfo.org[0], 'Telefonica');
|
||||
|
||||
assert.equal(adapted.fbInfo.adr[0].type[0], 'home');
|
||||
assert.equal(adapted.fbInfo.adr[0].locality, 'Valladolid');
|
||||
assert.equal(adapted.fbInfo.adr[0].region, 'Castilla y Leon');
|
||||
assert.equal(adapted.fbInfo.adr[0].countryName, 'Spain');
|
||||
|
||||
assert.equal(adapted.fbInfo.adr[1].type[0], 'current');
|
||||
assert.equal(adapted.fbInfo.adr[1].locality, 'Greater London');
|
||||
assert.equal(adapted.fbInfo.adr[1].region, 'London');
|
||||
assert.equal(adapted.fbInfo.adr[1].countryName, 'United Kingdom');
|
||||
|
||||
assert.equal(adapted.fbInfo.bday.getUTCMonth(), 11);
|
||||
assert.equal(adapted.fbInfo.bday.getUTCDate(), 3);
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
suiteTeardown(function() {
|
||||
window.fb.utils = realFbUtils;
|
||||
window.oauthflow = realOauthflow;
|
||||
});
|
||||
});
|
||||
23
gaia/apps/communications/facebook/test/unit/mock_curtain.js
Executable file
23
gaia/apps/communications/facebook/test/unit/mock_curtain.js
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
'use strict';
|
||||
|
||||
/* exported MockCurtain */
|
||||
|
||||
var MockCurtain = {
|
||||
show: function() {
|
||||
return {
|
||||
update: function() {
|
||||
|
||||
},
|
||||
setTotal: function() {
|
||||
|
||||
},
|
||||
getValue: function() {
|
||||
|
||||
}
|
||||
};
|
||||
},
|
||||
hide: function() {
|
||||
|
||||
},
|
||||
hideMenu: function(){}
|
||||
};
|
||||
59
gaia/apps/communications/facebook/test/unit/mock_fb_graph_data.js
Executable file
59
gaia/apps/communications/facebook/test/unit/mock_fb_graph_data.js
Executable file
|
|
@ -0,0 +1,59 @@
|
|||
/* exported MockFbGraphData */
|
||||
'use strict';
|
||||
|
||||
var MockFbGraphData = {
|
||||
data: [
|
||||
{
|
||||
'uid': 45677,
|
||||
'name': 'Carlos Angel Bureta',
|
||||
'first_name': 'Carlos',
|
||||
'last_name': 'Bureta',
|
||||
'pic_big':
|
||||
'http://profile.ak.fbcdn.net/hprofile-ak-ash3/49136_779462559_2920_n.jpg',
|
||||
'hometown_location': {
|
||||
'city': 'Valladolid',
|
||||
'state': 'Castilla y Leon',
|
||||
'country': 'Spain',
|
||||
'zip': '',
|
||||
'id': 108236755863231,
|
||||
'name': 'Valladolid, Spain'
|
||||
},
|
||||
'current_location': {
|
||||
'city': 'Greater London',
|
||||
'state': 'London',
|
||||
'country': 'United Kingdom',
|
||||
'zip': '',
|
||||
'id': 902104567293783,
|
||||
'name': 'London, United Kingdom'
|
||||
},
|
||||
'middle_name': 'Angel',
|
||||
'birthday_date': '12/03/1984',
|
||||
'email': 'ppburetaxyz@gmail.com',
|
||||
'profile_update_time': 1360140477,
|
||||
'work': [
|
||||
{
|
||||
'employer': {
|
||||
'id': 115396748472536,
|
||||
'name': 'Telefonica'
|
||||
}
|
||||
},
|
||||
{
|
||||
'employer': {
|
||||
'id': 129653317102827,
|
||||
'name': 'Telefonica I+D'
|
||||
}
|
||||
}
|
||||
],
|
||||
'phones': [
|
||||
{
|
||||
'country_code': '34',
|
||||
'number': '609274801',
|
||||
'type': 'cell',
|
||||
'phone_type': 'Mobile',
|
||||
'is_verified': true,
|
||||
'is_sms_enabled': false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
12
gaia/apps/communications/facebook/test/unit/mock_fb_query.js
Executable file
12
gaia/apps/communications/facebook/test/unit/mock_fb_query.js
Executable file
|
|
@ -0,0 +1,12 @@
|
|||
/* global MockFbGraphData */
|
||||
/* exported MockFbQuery */
|
||||
'use strict';
|
||||
|
||||
var MockFbQuery = {
|
||||
runQuery: function(query, callbacks) {
|
||||
callbacks.success(MockFbGraphData);
|
||||
},
|
||||
getFriendPicture: function(uid, callbacks) {
|
||||
callbacks.success(null);
|
||||
}
|
||||
};
|
||||
Loading…
Reference in a new issue