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
383
gaia/shared/js/contacts/import/facebook/facebook_connector.js
Executable file
383
gaia/shared/js/contacts/import/facebook/facebook_connector.js
Executable file
|
|
@ -0,0 +1,383 @@
|
|||
'use strict';
|
||||
|
||||
/* global fb, utils */
|
||||
/* global Matcher */
|
||||
|
||||
|
||||
if (!window.FacebookConnector) {
|
||||
window.FacebookConnector = (function() {
|
||||
|
||||
var nextUpdateTime;
|
||||
window.fb = window.fb || {};
|
||||
fb.operationsTimeout = parent.config ?
|
||||
parent.config.operationsTimeout : null;
|
||||
|
||||
// Only one instance in order to reuse it for saving friends in the
|
||||
// database. If the instance is not released between transactions, new
|
||||
// objects will be created.
|
||||
var reusedFbContact = new fb.Contact();
|
||||
reusedFbContact.ready = true;
|
||||
|
||||
function createFbImporter(clist, access_token) {
|
||||
/* jshint validthis:true */
|
||||
var out = new window.ContactsImporter(clist, access_token, this);
|
||||
|
||||
out.persist = persistFbData;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
function persistFbData(data, successCb, errorCb, options) {
|
||||
var saveCbs = {
|
||||
success: function(e) {
|
||||
if (options === 'not_match') {
|
||||
successCb();
|
||||
return;
|
||||
}
|
||||
|
||||
var cbs = {
|
||||
onmatch: function(matches) {
|
||||
// For each of the matching contacts link with the just imported
|
||||
Object.keys(matches).forEach(function(aMatchId) {
|
||||
var fbContact = new
|
||||
fb.Contact(matches[aMatchId].matchingContact);
|
||||
|
||||
var mozContReq = fb.utils.getMozContact(data.uid);
|
||||
|
||||
mozContReq.onsuccess = function() {
|
||||
var linkParams = {
|
||||
uid: data.uid,
|
||||
mozContact: mozContReq.result
|
||||
};
|
||||
if (Array.isArray(data.photo) && data.photo[0]) {
|
||||
linkParams.photoUrl = data.pic_big;
|
||||
}
|
||||
var req = fbContact.linkTo(linkParams);
|
||||
|
||||
req.onsuccess = successCb;
|
||||
|
||||
req.onerror = function error(e) {
|
||||
window.console.error('Error while automatically linking : ',
|
||||
aMatchId, data.uid, e.target.name);
|
||||
successCb();
|
||||
};
|
||||
};
|
||||
mozContReq.onerror = function(e) {
|
||||
window.console.error(
|
||||
'Error getting mozContact data in automatic linking: ',
|
||||
e.target.error.name);
|
||||
successCb();
|
||||
};
|
||||
});
|
||||
},
|
||||
onmismatch: successCb
|
||||
};
|
||||
// Try to match and if so merge is performed
|
||||
Matcher.match(data, 'passive', cbs);
|
||||
},
|
||||
error: errorCb
|
||||
};
|
||||
saveFbContact(data, saveCbs.success, saveCbs.error);
|
||||
}
|
||||
|
||||
|
||||
function saveFbContact(data, successCb, errorCb) {
|
||||
var fbContact, successWrapperCb;
|
||||
|
||||
if (reusedFbContact.ready) {
|
||||
reusedFbContact.ready = null;
|
||||
fbContact = reusedFbContact;
|
||||
successWrapperCb = function onsuccess(e) {
|
||||
reusedFbContact.ready = true;
|
||||
successCb(e);
|
||||
};
|
||||
} else {
|
||||
fbContact = new fb.Contact();
|
||||
successWrapperCb = successCb;
|
||||
}
|
||||
|
||||
fbContact.setData(data);
|
||||
|
||||
var req = fbContact.save();
|
||||
req.onsuccess = successWrapperCb;
|
||||
req.onerror = errorCb;
|
||||
}
|
||||
|
||||
function setInfraForSync(existingContacts, friendsImported, callback) {
|
||||
// From the FTU the infra for sync will not be loaded
|
||||
if (!fb.sync) {
|
||||
(typeof callback === 'function') && callback();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check wether we need to set the update alarm
|
||||
window.asyncStorage.getItem(fb.utils.ALARM_ID_KEY, function(data) {
|
||||
if (!data || (existingContacts && existingContacts.length === 0) &&
|
||||
!friendsImported) {
|
||||
// This is the first contact imported
|
||||
fb.utils.setLastUpdate(nextUpdateTime, function() {
|
||||
var req = fb.sync.scheduleNextSync();
|
||||
if (typeof callback === 'function') {
|
||||
req.onsuccess = callback;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var UID_FILTER_IDX = 5;
|
||||
var acc_tk;
|
||||
|
||||
function buildFriendQuery(uid) {
|
||||
var aquery1 = [].concat(FRIENDS_QUERY);
|
||||
aquery1[UID_FILTER_IDX] = '= ' + uid;
|
||||
|
||||
return aquery1.join('');
|
||||
}
|
||||
|
||||
// Query that retrieves the information about friends
|
||||
var FRIENDS_QUERY = [
|
||||
'SELECT uid, name, first_name, last_name, pic_big, current_location, ' ,
|
||||
'middle_name, birthday_date, email, profile_update_time, ' ,
|
||||
' work, phones, hometown_location' ,
|
||||
' FROM user' ,
|
||||
' WHERE uid ',
|
||||
'IN (SELECT uid1 FROM friend WHERE uid2=me())' ,
|
||||
' ORDER BY ',
|
||||
'first_name'
|
||||
];
|
||||
|
||||
var friendsQueryStr = FRIENDS_QUERY.join('');
|
||||
|
||||
function contactDataLoaded(options, response) {
|
||||
if (!response.error) {
|
||||
// Just in case this is the first contact imported
|
||||
nextUpdateTime = Date.now();
|
||||
/* jshint validthis:true */
|
||||
var successCb = this.success;
|
||||
|
||||
var friend = response.data[0];
|
||||
if (friend) {
|
||||
var out1 = self.adaptDataForShowing(friend);
|
||||
var photoCbs = {};
|
||||
|
||||
photoCbs.success = function(blobPicture) {
|
||||
if (!blobPicture) {
|
||||
pictureLoaded(out1, options, successCb);
|
||||
return;
|
||||
}
|
||||
utils.thumbnailImage(blobPicture,
|
||||
function gotThumbnail(thumbnail) {
|
||||
if (blobPicture !== thumbnail) {
|
||||
out1.photo = [blobPicture, thumbnail];
|
||||
} else {
|
||||
out1.photo = [blobPicture];
|
||||
}
|
||||
pictureLoaded(out1, options, successCb);
|
||||
});
|
||||
}; // successCb
|
||||
|
||||
self.downloadContactPicture(friend, acc_tk, photoCbs);
|
||||
} // if friend
|
||||
else {
|
||||
window.console.error('FB: No Friend data found');
|
||||
/* jshint validthis:true */
|
||||
this.error('No friend data found');
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* jshint validthis:true */
|
||||
this.error(response.error);
|
||||
}
|
||||
}
|
||||
|
||||
function pictureLoaded(friend, options, success) {
|
||||
var data = self.adaptDataForSaving(friend);
|
||||
/* jshint validthis:true */
|
||||
persistFbData(data, function() {
|
||||
success({
|
||||
uid: friend.uid,
|
||||
url: friend.pic_big
|
||||
});
|
||||
}, function error(err) {
|
||||
console.error('Error while persisting: ', err);
|
||||
}, options);
|
||||
|
||||
// If there is no an alarm set it has to be set
|
||||
window.asyncStorage.getItem(fb.utils.ALARM_ID_KEY, function(data) {
|
||||
if (!data) {
|
||||
fb.utils.setLastUpdate(nextUpdateTime,
|
||||
fb.sync.scheduleNextSync);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function FacebookConnector() { }
|
||||
|
||||
FacebookConnector.prototype = {
|
||||
listAllContacts: function(access_token, callbacks, options) {
|
||||
if (options && options.orderBy === 'lastName') {
|
||||
FRIENDS_QUERY[FRIENDS_QUERY.length] = 'last_name';
|
||||
friendsQueryStr = FRIENDS_QUERY.join('');
|
||||
}
|
||||
return fb.utils.runQuery(friendsQueryStr, {
|
||||
success: callbacks.success,
|
||||
error: callbacks.error,
|
||||
timeout: callbacks.timeout
|
||||
},access_token);
|
||||
},
|
||||
|
||||
listDeviceContacts: function(callbacks) {
|
||||
// Dummy implementation for the time being
|
||||
var req = fb.utils.getAllFbContacts();
|
||||
|
||||
req.onsuccess = function successHandler() {
|
||||
callbacks.success(req.result);
|
||||
};
|
||||
req.onerror = callbacks.error;
|
||||
},
|
||||
|
||||
getImporter: function(contactsList, access_token) {
|
||||
return createFbImporter.bind(this)(contactsList, access_token);
|
||||
},
|
||||
|
||||
// Imports a Contact to FB indexedDB private database
|
||||
importContact: function(uid, access_token, callbacks, options) {
|
||||
acc_tk = access_token;
|
||||
var oneFriendQuery = buildFriendQuery(uid);
|
||||
var auxCallbacks = {
|
||||
success: contactDataLoaded.bind(callbacks, options),
|
||||
error: callbacks.error,
|
||||
timeout: callbacks.timeout
|
||||
};
|
||||
fb.utils.runQuery(oneFriendQuery, auxCallbacks, access_token);
|
||||
},
|
||||
|
||||
cleanContacts: function(contactsList, mode, cb) {
|
||||
if (mode === 'update') {
|
||||
var cleaner = new fb.utils.FbContactsCleaner(contactsList, mode);
|
||||
window.setTimeout(cleaner.start, 0);
|
||||
cb(cleaner);
|
||||
}
|
||||
else {
|
||||
// Mode === clear
|
||||
var req = fb.utils.clearFbData();
|
||||
req.onsuccess = function() {
|
||||
cb(req.result);
|
||||
};
|
||||
|
||||
req.onerror = function(e) {
|
||||
window.console.error('Error while starting cleaning: ',
|
||||
e.target.error.name);
|
||||
// Notifying the UI importer to show an error to the user
|
||||
// As we cannot get a cleaner the param is set to null
|
||||
cb(null);
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
adaptDataForShowing: function(source) {
|
||||
|
||||
var box = utils.misc.getPreferredPictureBox();
|
||||
var picWidth = box.width;
|
||||
var picHeight = box.height;
|
||||
|
||||
var out = fb.friend2mozContact(source);
|
||||
out.contactPictureUri = 'https://graph.facebook.com/' +
|
||||
source.uid + '/picture?type=square' +
|
||||
'&width=' + picWidth +
|
||||
'&height=' + picHeight +
|
||||
'&t=' + Date.now();
|
||||
return out;
|
||||
},
|
||||
|
||||
adaptDataForSaving: function fbAdapt(cfdata) {
|
||||
var worksAt = fb.getWorksAt(cfdata);
|
||||
var address = fb.getAddresses(cfdata);
|
||||
|
||||
var birthDate = null;
|
||||
if (cfdata.birthday_date && cfdata.birthday_date.length > 0) {
|
||||
birthDate = fb.getBirthDate(cfdata.birthday_date);
|
||||
}
|
||||
|
||||
var fbInfo = {
|
||||
bday: birthDate,
|
||||
org: [worksAt]
|
||||
};
|
||||
|
||||
if (address) {
|
||||
fbInfo.adr = address;
|
||||
}
|
||||
|
||||
// This is the short telephone number to enable indexing
|
||||
if (cfdata.shortTelephone) {
|
||||
fbInfo.shortTelephone = cfdata.shortTelephone;
|
||||
delete cfdata.shortTelephone;
|
||||
}
|
||||
|
||||
// Check whether we were able to get the photo or not
|
||||
fbInfo.url = [];
|
||||
|
||||
if (cfdata.photo) {
|
||||
fbInfo.photo = cfdata.photo;
|
||||
if (cfdata.pic_big) {
|
||||
// The URL is stored for synchronization purposes
|
||||
fb.setFriendPictureUrl(fbInfo, cfdata.pic_big);
|
||||
}
|
||||
delete cfdata.photo;
|
||||
}
|
||||
// Facebook info is set and then contact is saved
|
||||
cfdata.fbInfo = fbInfo;
|
||||
|
||||
return cfdata;
|
||||
},
|
||||
|
||||
// It would allow to know the UID of a service contact already imported
|
||||
// on the device. That is needed by the generic importer, for live
|
||||
// a dummy implementation as we are currently not supporting updates
|
||||
getContactUid: function(deviceContact) {
|
||||
return fb.getFriendUid(deviceContact);
|
||||
},
|
||||
|
||||
get name() {
|
||||
return 'facebook';
|
||||
},
|
||||
|
||||
get automaticLogout() {
|
||||
return false;
|
||||
},
|
||||
|
||||
downloadContactPicture: function(contact, access_token, callbacks) {
|
||||
return fb.utils.getFriendPicture(contact.uid, callbacks.success,
|
||||
access_token, utils.misc.getPreferredPictureDetail());
|
||||
},
|
||||
|
||||
oncontactsloaded: function(lfriends) {
|
||||
// This is the timestamp for later syncing as it set at the time
|
||||
// when data was ready
|
||||
nextUpdateTime = Date.now();
|
||||
// Now caching the number
|
||||
fb.utils.setCachedNumFriends(lfriends.length);
|
||||
},
|
||||
|
||||
oncontactsimported: function(existingContacts, friendsImported, cb) {
|
||||
setInfraForSync(existingContacts, friendsImported, cb);
|
||||
fb.contacts.flush();
|
||||
},
|
||||
|
||||
startSync: function(existingContacts, myFriendsByUid, syncFinished) {
|
||||
var callbacks = {
|
||||
success: syncFinished
|
||||
};
|
||||
fb.sync.startWithData(existingContacts, myFriendsByUid, callbacks);
|
||||
},
|
||||
|
||||
scheduleNextSync: function() {
|
||||
fb.sync.scheduleNextSync();
|
||||
}
|
||||
};
|
||||
var self = new FacebookConnector();
|
||||
|
||||
return self;
|
||||
})();
|
||||
}
|
||||
659
gaia/shared/js/contacts/import/facebook/fb_contact.js
Executable file
659
gaia/shared/js/contacts/import/facebook/fb_contact.js
Executable file
|
|
@ -0,0 +1,659 @@
|
|||
'use strict';
|
||||
|
||||
/* global utils */
|
||||
|
||||
var fb = window.fb || {};
|
||||
|
||||
// Encapsulates all the logic to obtain the data for a FB contact
|
||||
fb.Contact = function(deviceContact, cid) {
|
||||
var contactData;
|
||||
var devContact = deviceContact;
|
||||
var contactid = cid;
|
||||
|
||||
function doGetFacebookUid(data) {
|
||||
return fb.getFriendUid(data);
|
||||
}
|
||||
|
||||
function getFacebookUid() {
|
||||
return doGetFacebookUid(deviceContact);
|
||||
}
|
||||
|
||||
function setFacebookUid(value) {
|
||||
doSetFacebookUid(deviceContact, value);
|
||||
}
|
||||
|
||||
function doSetFacebookUid(dcontact, value) {
|
||||
if (!dcontact.category) {
|
||||
dcontact.category = [];
|
||||
}
|
||||
|
||||
if (dcontact.category.indexOf(fb.CATEGORY) === -1) {
|
||||
markAsFb(dcontact);
|
||||
}
|
||||
|
||||
var idx = dcontact.category.indexOf(fb.CATEGORY);
|
||||
|
||||
dcontact.category[idx + 2] = value;
|
||||
}
|
||||
|
||||
function markAsFb(dcontact) {
|
||||
if (!dcontact.category) {
|
||||
dcontact.category = [];
|
||||
}
|
||||
|
||||
if (dcontact.category.indexOf(fb.CATEGORY) === -1) {
|
||||
dcontact.category.push(fb.CATEGORY);
|
||||
dcontact.category.push(fb.NOT_LINKED);
|
||||
}
|
||||
|
||||
return dcontact;
|
||||
}
|
||||
|
||||
// Mark a mozContact (deviceContact) as linked to a FB contact (uid)
|
||||
function markAsLinked(dcontact, uid) {
|
||||
if (!dcontact.category) {
|
||||
dcontact.category = [];
|
||||
}
|
||||
|
||||
if (dcontact.category.indexOf(fb.LINKED) === -1) {
|
||||
dcontact.category.push(fb.CATEGORY);
|
||||
dcontact.category.push(fb.LINKED);
|
||||
dcontact.category.push(uid);
|
||||
}
|
||||
|
||||
return dcontact;
|
||||
}
|
||||
|
||||
function promoteToLinked(dcontact) {
|
||||
var idx = dcontact.category.indexOf(fb.NOT_LINKED);
|
||||
|
||||
if (idx != -1) {
|
||||
dcontact.category[idx] = fb.LINKED;
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the data for an imported FB Contact
|
||||
this.setData = function(data) {
|
||||
contactData = data;
|
||||
};
|
||||
|
||||
Object.defineProperty(this, 'uid', {
|
||||
get: getFacebookUid,
|
||||
set: setFacebookUid,
|
||||
enumerable: true,
|
||||
});
|
||||
|
||||
Object.defineProperty(this, 'mozContact', {
|
||||
get: getDevContact
|
||||
});
|
||||
|
||||
function getDevContact() {
|
||||
return devContact;
|
||||
}
|
||||
|
||||
// For saving an imported FB contact
|
||||
this.save = function() {
|
||||
var outReq = new fb.utils.Request();
|
||||
|
||||
if (contactData && navigator.mozContacts) {
|
||||
window.setTimeout(function save_do() {
|
||||
var photoList = contactData.fbInfo.photo;
|
||||
if (photoList && photoList.length > 0 && photoList[0]) {
|
||||
utils.squareImage(photoList[0], function squared(squared_image) {
|
||||
photoList[0] = squared_image;
|
||||
doSave(outReq);
|
||||
});
|
||||
}
|
||||
else {
|
||||
doSave(outReq);
|
||||
}
|
||||
},0);
|
||||
}
|
||||
else {
|
||||
throw 'Data or mozContacts not available';
|
||||
}
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
function doSave(outReq) {
|
||||
// Info to be saved on mozContacts
|
||||
var contactInfo = {};
|
||||
|
||||
// Copying names to the mozContact
|
||||
copyNames(contactData, contactInfo);
|
||||
// URL (photo, etc) is stored also with the Device contact
|
||||
if (contactData.fbInfo.url) {
|
||||
contactInfo.url = contactData.fbInfo.url;
|
||||
}
|
||||
|
||||
doSetFacebookUid(contactInfo, contactData.uid);
|
||||
|
||||
var fbReq = persistToFbCache(contactData);
|
||||
|
||||
fbReq.onsuccess = function() {
|
||||
var mozContactsReq = navigator.mozContacts.save(
|
||||
utils.misc.toMozContact(contactInfo));
|
||||
mozContactsReq.onsuccess = function(e) {
|
||||
outReq.done(fbReq.result);
|
||||
};
|
||||
mozContactsReq.onerror = function(e) {
|
||||
window.console.error('FB: Error while saving on mozContacts',
|
||||
e.target.error);
|
||||
outReq.failed(e.target.error);
|
||||
}; // fbReq.onsuccess
|
||||
};
|
||||
fbReq.onerror = function() {
|
||||
window.console.error('FB: Error while saving on datastore',
|
||||
fbReq.error && fbReq.error.name);
|
||||
outReq.failed(fbReq.error);
|
||||
};
|
||||
}
|
||||
|
||||
// Persists FB Friend Data to the FB cache
|
||||
function persistToFbCache(contactData, mUpdate) {
|
||||
var isUpdate = (mUpdate === true ? mUpdate : false);
|
||||
|
||||
var outReq = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function persist_fb_do() {
|
||||
// now saving the FB-originated data to the "private cache area"
|
||||
var data = Object.create(contactData.fbInfo);
|
||||
|
||||
data.tel = contactData.tel || [];
|
||||
data.email = contactData.email || [];
|
||||
data.uid = contactData.uid;
|
||||
|
||||
Object.keys(contactData.fbInfo).forEach(function(prop) {
|
||||
data[prop] = contactData.fbInfo[prop];
|
||||
});
|
||||
|
||||
// Names are also stored on datastore
|
||||
// thus restoring the contact (if unlinked) will be trivial
|
||||
copyNames(contactData, data);
|
||||
|
||||
var updaterFn = (isUpdate === true ?
|
||||
fb.contacts.update : fb.contacts.save);
|
||||
var fbReq = updaterFn(data);
|
||||
|
||||
fbReq.onsuccess = function() {
|
||||
outReq.done(fbReq.result);
|
||||
};
|
||||
fbReq.onerror = function() {
|
||||
window.console.error('FB: Error while saving Fb data on the Datastore',
|
||||
fbReq.error && fbReq.error.name);
|
||||
outReq.failed(fbReq.error);
|
||||
};
|
||||
},0);
|
||||
|
||||
return outReq;
|
||||
}
|
||||
|
||||
// Updates a FB Contact
|
||||
this.update = function(contactData) {
|
||||
|
||||
// Auxiliary function to do all the work for saving to the FB cache
|
||||
function auxDoUpdate(contactData, outReq) {
|
||||
// If the photo was not updated it is needed to save it
|
||||
if (!contactData.fbInfo.photo) {
|
||||
var dataReq = fb.contacts.get(contactData.uid);
|
||||
dataReq.onsuccess = function() {
|
||||
contactData.fbInfo.photo = dataReq.result.photo;
|
||||
auxCachePersist(contactData, outReq, true);
|
||||
};
|
||||
dataReq.onerror = function() {
|
||||
window.console.error('Error while retrieving existing photo for ',
|
||||
contactData.uid, dataReq.error);
|
||||
outReq.failed(dataReq.error);
|
||||
};
|
||||
}
|
||||
else {
|
||||
utils.squareImage(contactData.fbInfo.photo[0],
|
||||
function sq_img(squaredImg) {
|
||||
contactData.fbInfo.photo[0] = squaredImg;
|
||||
auxCachePersist(contactData, outReq, true);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Persist the data to the FB Cache
|
||||
function auxCachePersist(contactData, outReq, isUpdate) {
|
||||
var fbReq = persistToFbCache(contactData, isUpdate);
|
||||
|
||||
fbReq.onsuccess = function() {
|
||||
outReq.done(fbReq.result);
|
||||
};
|
||||
fbReq.onerror = function() {
|
||||
window.console.error('FB: Error while saving to FB cache: ',
|
||||
contactData.uid, fbReq.error);
|
||||
outReq.failed(fbReq.error);
|
||||
};
|
||||
}
|
||||
|
||||
// Code starts here
|
||||
var outReq = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function update_do() {
|
||||
if (!fb.isFbLinked(devContact)) {
|
||||
copyNames(contactData, devContact);
|
||||
} else {
|
||||
// We are going to update names if they are propagated
|
||||
revisitPropagatedNames(contactData, devContact);
|
||||
}
|
||||
|
||||
// Check whether the photo has changed
|
||||
if (contactData.fbInfo.photo) {
|
||||
devContact.url = contactData.fbInfo.url;
|
||||
}
|
||||
|
||||
var auxReq = new fb.utils.Request();
|
||||
auxReq.onsuccess = function() {
|
||||
var mozContactsReq = navigator.mozContacts.save(
|
||||
utils.misc.toMozContact(devContact));
|
||||
mozContactsReq.onsuccess = function(e) {
|
||||
outReq.done();
|
||||
};
|
||||
|
||||
mozContactsReq.onerror = function(e) {
|
||||
window.console.error('FB: Error while saving mozContact: ',
|
||||
devContact.id, e.target.error);
|
||||
outReq.failed(e.target.error);
|
||||
};
|
||||
}; // auxReq.onsuccess
|
||||
|
||||
auxReq.onerror = function(e) {
|
||||
outReq.failed(e.target.error);
|
||||
}; // auxReq.onerror
|
||||
|
||||
// And now doing the update
|
||||
auxDoUpdate(contactData, auxReq);
|
||||
},0);
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
function asArrayOfValues(value) {
|
||||
return Array.isArray(value) ? value : [value];
|
||||
}
|
||||
|
||||
function copyNames(source, destination) {
|
||||
destination.name = asArrayOfValues(source.name);
|
||||
destination.givenName = asArrayOfValues(source.givenName);
|
||||
destination.familyName = asArrayOfValues(source.familyName);
|
||||
destination.additionalName = asArrayOfValues(source.additionalName);
|
||||
}
|
||||
|
||||
// Merges mozContact data with Facebook data
|
||||
this.merge = function(fbdata) {
|
||||
return fb.mergeContact(devContact, fbdata);
|
||||
};
|
||||
|
||||
|
||||
// Gets the data
|
||||
this.getData = function() {
|
||||
return fb.getData(devContact);
|
||||
};
|
||||
|
||||
this.getDataAndValues = function() {
|
||||
var outReq = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function do_getData() {
|
||||
var uid = doGetFacebookUid(devContact);
|
||||
|
||||
if (uid) {
|
||||
var fbreq = fb.contacts.get(uid);
|
||||
|
||||
fbreq.onsuccess = (function() {
|
||||
var fbdata = fbreq.result;
|
||||
|
||||
var out1 = this.merge(fbdata);
|
||||
|
||||
var out2 = {};
|
||||
|
||||
var duplicates = {};
|
||||
Object.keys(fbdata).forEach(function(key) {
|
||||
var dataElement = fbdata[key];
|
||||
|
||||
if (dataElement && Array.isArray(dataElement) && key !== 'photo') {
|
||||
dataElement.forEach(function(item) {
|
||||
if (item.value && item.value.length > 0) {
|
||||
// Check for duplicates. Those duplicates are annotated to
|
||||
// be later removed from the out2 array
|
||||
var dupList = fb.checkDuplicates(key, item, devContact[key],
|
||||
fbdata.shortTelephone);
|
||||
dupList.forEach(function(aDup) {
|
||||
duplicates[aDup] = true;
|
||||
});
|
||||
|
||||
out2[item.value] = true;
|
||||
}
|
||||
else if (typeof item === 'string' && item.length > 0) {
|
||||
out2[item] = true;
|
||||
}
|
||||
else if (key === 'adr') {
|
||||
if (item.locality) {
|
||||
out2[item.locality] = true;
|
||||
}
|
||||
if (item.countryName) {
|
||||
out2[item.countryName] = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (key === 'photo') {
|
||||
out2.hasPhoto = true;
|
||||
}
|
||||
else if (dataElement) {
|
||||
out2[dataElement] = true;
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(duplicates).forEach(function(aDup) {
|
||||
delete out2[aDup];
|
||||
});
|
||||
|
||||
outReq.done([out1, out2, fbdata]);
|
||||
|
||||
}).bind(this);
|
||||
|
||||
fbreq.onerror = function() {
|
||||
outReq.failed(fbreq.error);
|
||||
};
|
||||
}
|
||||
else {
|
||||
outReq.done([devContact, {}]);
|
||||
}
|
||||
}.bind(this), 0);
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
this.promoteToLinked = function() {
|
||||
promoteToLinked(devContact);
|
||||
};
|
||||
|
||||
this.linkTo = function(fbFriend) {
|
||||
var out = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function do_linkTo() {
|
||||
if (!devContact) {
|
||||
// We need to get the Contact data
|
||||
var req = fb.utils.getContactData(contactid);
|
||||
|
||||
req.onsuccess = function() {
|
||||
devContact = req.result;
|
||||
doLink(devContact, fbFriend, out);
|
||||
}; // req.onsuccess
|
||||
|
||||
req.onerror = function() {
|
||||
throw 'FB: Error while retrieving contact data';
|
||||
};
|
||||
} // devContact
|
||||
else {
|
||||
doLink(devContact, fbFriend, out);
|
||||
}
|
||||
},0);
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
function propagateField(field, from, to) {
|
||||
var copied = false;
|
||||
|
||||
// The field is copied when it is undefined in the target object
|
||||
if (!Array.isArray(to[field]) || !to[field][0] || !to[field][0].trim()) {
|
||||
to[field] = from[field];
|
||||
copied = true;
|
||||
}
|
||||
|
||||
return copied;
|
||||
}
|
||||
|
||||
function createName(contact) {
|
||||
contact.name = [];
|
||||
|
||||
if (Array.isArray(contact.givenName)) {
|
||||
contact.name[0] = contact.givenName[0] + ' ';
|
||||
}
|
||||
|
||||
if (Array.isArray(contact.familyName)) {
|
||||
contact.name[0] += contact.familyName[0];
|
||||
}
|
||||
}
|
||||
|
||||
function propagateNames(from, to) {
|
||||
var isGivenNamePropagated = propagateField('givenName', from, to);
|
||||
var isFamilyNamePropagated = propagateField('familyName', from, to);
|
||||
|
||||
if (isGivenNamePropagated || isFamilyNamePropagated) {
|
||||
// We are going to mark the propagation in the category field
|
||||
if (isGivenNamePropagated) {
|
||||
fb.setPropagatedFlag('givenName', to);
|
||||
}
|
||||
|
||||
if (isFamilyNamePropagated) {
|
||||
fb.setPropagatedFlag('familyName', to);
|
||||
}
|
||||
|
||||
createName(to);
|
||||
}
|
||||
}
|
||||
|
||||
// This method copies names to a contact when they are propagated from fb
|
||||
function revisitPropagatedNames(from, to) {
|
||||
if (fb.isPropagated('givenName', to)) {
|
||||
to.givenName = from.givenName;
|
||||
}
|
||||
|
||||
if (fb.isPropagated('familyName', to)) {
|
||||
to.familyName = from.familyName;
|
||||
}
|
||||
|
||||
createName(to);
|
||||
}
|
||||
|
||||
function doLink(contactdata, fbFriend, out) {
|
||||
if (contactdata) {
|
||||
if (fbFriend.uid) {
|
||||
// When marking as linked is needed to store a reference to the profile
|
||||
// picture URL
|
||||
markAsLinked(contactdata, fbFriend.uid);
|
||||
if (fbFriend.photoUrl) {
|
||||
fb.setFriendPictureUrl(contactdata, fbFriend.photoUrl);
|
||||
}
|
||||
else if (fbFriend.mozContact) {
|
||||
contactdata.url = fbFriend.mozContact.url;
|
||||
}
|
||||
}
|
||||
else if (fbFriend.mozContact) {
|
||||
markAsLinked(contactdata, doGetFacebookUid(fbFriend.mozContact));
|
||||
contactdata.url = fbFriend.mozContact.url;
|
||||
}
|
||||
|
||||
propagateNames(fbFriend.mozContact, contactdata);
|
||||
var mozContactsReq = navigator.mozContacts.save(
|
||||
utils.misc.toMozContact(contactdata));
|
||||
|
||||
mozContactsReq.onsuccess = function(e) {
|
||||
// The FB contact on mozContacts needs to be removed
|
||||
if (fbFriend.mozContact && !fb.isFbLinked(fbFriend.mozContact)) {
|
||||
var deleteReq = navigator.mozContacts.remove(
|
||||
utils.misc.toMozContact(fbFriend.mozContact));
|
||||
|
||||
deleteReq.onsuccess = function(e) {
|
||||
out.done(e.target.result);
|
||||
};
|
||||
|
||||
deleteReq.onerror = function(e) {
|
||||
window.console.error('FB: Error while linking');
|
||||
out.failed(e.target.error);
|
||||
};
|
||||
}
|
||||
else {
|
||||
out.done(e.target.result);
|
||||
}
|
||||
|
||||
}; // mozContactsReq.onsuccess
|
||||
|
||||
mozContactsReq.onerror = function(e) {
|
||||
out.failed(e.target.error);
|
||||
}; // mozContactsReq.onerror
|
||||
} // if(dev.contact)
|
||||
else {
|
||||
throw 'FB: Contact data not defined';
|
||||
}
|
||||
}
|
||||
|
||||
// if type === 'hard' the FB Friend is removed from the cache
|
||||
this.unlink = function(type) {
|
||||
var out = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function do_unlink() {
|
||||
if (!devContact) {
|
||||
// We need to get the Contact data
|
||||
var req = fb.utils.getContactData(contactid);
|
||||
|
||||
req.onsuccess = function() {
|
||||
devContact = req.result;
|
||||
doUnlink(devContact, out, type);
|
||||
}; // req.onsuccess
|
||||
|
||||
req.onerror = function() {
|
||||
throw 'FB: Error while retrieving contact data';
|
||||
};
|
||||
} // devContact
|
||||
else {
|
||||
doUnlink(devContact, out, type);
|
||||
}
|
||||
}, 0);
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
// Reset givenName and familyName if it is needed after unlinking
|
||||
function resetNames(dContact) {
|
||||
fb.resetNames(dContact);
|
||||
}
|
||||
|
||||
function doUnlink(dContact, out, type) {
|
||||
var theType = type || 'soft';
|
||||
var uid = doGetFacebookUid(dContact);
|
||||
|
||||
resetNames(dContact);
|
||||
fb.markAsUnlinked(dContact);
|
||||
var req = navigator.mozContacts.save(utils.misc.toMozContact(dContact));
|
||||
|
||||
req.onsuccess = function(e) {
|
||||
if (theType !== 'hard') {
|
||||
|
||||
// Then the original FB imported contact is restored
|
||||
var fbNumReq = fb.utils.getNumberMozContacts(uid);
|
||||
|
||||
fbNumReq.onsuccess = function() {
|
||||
// Because we have already unlinked one
|
||||
if (fbNumReq.result >= 1) {
|
||||
out.done();
|
||||
} else {
|
||||
var fbDataReq = fb.contacts.get(uid);
|
||||
|
||||
fbDataReq.onsuccess = function() {
|
||||
var imported = fbDataReq.result;
|
||||
|
||||
var data = {};
|
||||
copyNames(imported, data);
|
||||
data.url = imported.url;
|
||||
doSetFacebookUid(data, uid);
|
||||
|
||||
// The FB contact is restored
|
||||
var aMozContact = utils.misc.toMozContact(data);
|
||||
var reqRestore = navigator.mozContacts.save(aMozContact);
|
||||
|
||||
reqRestore.onsuccess = function(e) {
|
||||
out.done(aMozContact.id);
|
||||
};
|
||||
|
||||
reqRestore.onerror = function(e) {
|
||||
out.failed(e.target.error);
|
||||
};
|
||||
};
|
||||
|
||||
fbDataReq.onerror = function() {
|
||||
window.console.error('FB: Error while unlinking contact data');
|
||||
out.failed(fbDataReq.error);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
fbNumReq.onerror = function() {
|
||||
window.console.error('FB: Error while unlinking contact data');
|
||||
out.failed(fbNumReq.error);
|
||||
};
|
||||
}
|
||||
else {
|
||||
// FB Data is removed from the cache
|
||||
var removeReq = fb.contacts.remove(uid, true);
|
||||
|
||||
removeReq.onsuccess = function() {
|
||||
out.done(removeReq.result);
|
||||
};
|
||||
|
||||
removeReq.onerror = function() {
|
||||
out.failed(removeReq.error);
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
req.onerror = function(e) {
|
||||
out.failed(e.target.error);
|
||||
};
|
||||
}
|
||||
|
||||
this.remove = function(pforceFlush) {
|
||||
var forceFlush = (pforceFlush === true) ? pforceFlush : false;
|
||||
|
||||
var out = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function do_remove() {
|
||||
var uid = doGetFacebookUid(devContact);
|
||||
|
||||
var fbNumReq = fb.utils.getNumberMozContacts(uid);
|
||||
fbNumReq.onsuccess = function num_onsuccess() {
|
||||
var removeReq;
|
||||
// If there is only one Device Contact associated
|
||||
// Then corresponding FB Data is removed otherwise only
|
||||
// the device contact is removed
|
||||
if (fbNumReq.result === 1) {
|
||||
removeReq = navigator.mozContacts.remove(
|
||||
utils.misc.toMozContact(devContact));
|
||||
removeReq.onsuccess = function(e) {
|
||||
var fbReq = fb.contacts.remove(uid, forceFlush);
|
||||
fbReq.onsuccess = function() {
|
||||
out.done(fbReq.result);
|
||||
};
|
||||
|
||||
fbReq.onerror = function() {
|
||||
out.failed(fbReq.error);
|
||||
};
|
||||
};
|
||||
removeReq.onerror = function(e) {
|
||||
out.failed(e.target.error);
|
||||
};
|
||||
}
|
||||
else {
|
||||
removeReq = navigator.mozContacts.remove(
|
||||
utils.misc.toMozContact(devContact));
|
||||
removeReq.onsuccess = function(e) {
|
||||
out.done();
|
||||
};
|
||||
removeReq.onerror = function(e) {
|
||||
out.failed(e.target.error);
|
||||
};
|
||||
}
|
||||
};
|
||||
}, 0);
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
}; // fb.Contact
|
||||
260
gaia/shared/js/contacts/import/facebook/fb_contact_utils.js
Executable file
260
gaia/shared/js/contacts/import/facebook/fb_contact_utils.js
Executable file
|
|
@ -0,0 +1,260 @@
|
|||
'use strict';
|
||||
|
||||
/* global fb, utils */
|
||||
|
||||
/* jshint ignore:start */
|
||||
var fb = this.fb || {};
|
||||
/* jshint ignore:end */
|
||||
|
||||
fb.PROPAGATED_PREFIX = 'fb_propagated_';
|
||||
|
||||
// Types of URLs for FB Information
|
||||
fb.PROFILE_PHOTO_URI = 'fb_profile_photo';
|
||||
fb.FRIEND_URI = 'fb_friend';
|
||||
fb.DEFAULT_PHONE_TYPE = 'other';
|
||||
fb.DEFAULT_EMAIL_TYPE = 'other';
|
||||
// This year indicates that the year can be ignored (yearless date)
|
||||
fb.FLAG_YEAR_IGNORED = 9996;
|
||||
|
||||
fb.CONTACTS_APP_ORIGIN = location.origin;
|
||||
|
||||
fb.isPropagated = function fcu_isPropagated(field, devContact) {
|
||||
return (devContact.category && devContact.category.
|
||||
indexOf(fb.PROPAGATED_PREFIX + field) !== -1);
|
||||
};
|
||||
|
||||
fb.removePropagatedFlag = function fcu_removePropagatedFlag(field, devContact) {
|
||||
var idx = devContact.category.indexOf(fb.PROPAGATED_PREFIX + field);
|
||||
if (idx !== -1) {
|
||||
devContact.category.splice(idx, 1);
|
||||
}
|
||||
};
|
||||
|
||||
fb.setPropagatedFlag = function fcu_setPropagatedFlag(field, devContact) {
|
||||
var idx = devContact.category.indexOf(fb.PROPAGATED_PREFIX + field);
|
||||
if (idx === -1) {
|
||||
devContact.category.push(fb.PROPAGATED_PREFIX + field);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
fb.getFriendPictureUrl = function(devContact) {
|
||||
var out;
|
||||
|
||||
var urls = devContact.url;
|
||||
|
||||
if (urls) {
|
||||
for (var c = 0; c < urls.length; c++) {
|
||||
var aurl = urls[c];
|
||||
if (aurl.type.indexOf(fb.PROFILE_PHOTO_URI) !== -1) {
|
||||
out = aurl.value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
fb.setFriendPictureUrl = function(devContact, url) {
|
||||
var urls = devContact.url || [];
|
||||
|
||||
urls.push({
|
||||
type: [fb.PROFILE_PHOTO_URI],
|
||||
value: url
|
||||
});
|
||||
|
||||
devContact.url = urls;
|
||||
};
|
||||
|
||||
// Adapts data to the mozContact format names
|
||||
fb.friend2mozContact = function(f) {
|
||||
|
||||
function normalizeFbPhoneNumber(phone) {
|
||||
var out = phone.number;
|
||||
if (phone.country_code && out.indexOf('+') !== 0) {
|
||||
out = '+' + phone.country_code + out;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Check whether this has been already normalized to mozContact
|
||||
if (Array.isArray(f.familyName)) {
|
||||
return f;
|
||||
}
|
||||
|
||||
// givenName is put as name but it should be f.first_name
|
||||
f.familyName = [f.last_name ? f.last_name.trim() : (f.last_name || '')];
|
||||
var middleName = f.middle_name ? f.middle_name.trim() : (f.middle_name || '');
|
||||
f.additionalName = middleName;
|
||||
var firstName = f.first_name ? f.first_name.trim() : (f.first_name || '');
|
||||
f.givenName = [(firstName + ' ' + middleName).trim()];
|
||||
|
||||
delete f.last_name;
|
||||
delete f.middle_name;
|
||||
delete f.first_name;
|
||||
|
||||
if (f.email) {
|
||||
f.email1 = f.email;
|
||||
f.email = [{
|
||||
type: [fb.DEFAULT_EMAIL_TYPE],
|
||||
value: f.email
|
||||
}];
|
||||
}
|
||||
else {
|
||||
f.email1 = '';
|
||||
}
|
||||
|
||||
if (Array.isArray(f.phones) && f.phones.length > 0) {
|
||||
f.tel = [];
|
||||
f.shortTelephone = [];
|
||||
f.phones.forEach(function(aphone) {
|
||||
f.tel.push({
|
||||
type: [fb.DEFAULT_PHONE_TYPE],
|
||||
value: normalizeFbPhoneNumber(aphone)
|
||||
});
|
||||
// Enabling to find FB phones by short number
|
||||
f.shortTelephone.push(aphone.number);
|
||||
});
|
||||
}
|
||||
|
||||
delete f.phones;
|
||||
|
||||
f.uid = f.uid.toString();
|
||||
|
||||
return f;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Auxiliary function to know where a contact works
|
||||
*
|
||||
*/
|
||||
fb.getWorksAt = function(fbdata) {
|
||||
var out = '';
|
||||
if (fbdata.work && fbdata.work.length > 0) {
|
||||
// It is assumed that first is the latest
|
||||
out = fbdata.work[0].employer.name;
|
||||
}
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
/**
|
||||
* Facebook dates are MM/DD/YYYY
|
||||
*
|
||||
* Returns the birth date
|
||||
*
|
||||
*/
|
||||
fb.getBirthDate = function getBirthDate(sbday) {
|
||||
var out = new Date(0);
|
||||
|
||||
var imonth = sbday.indexOf('/');
|
||||
var smonth = sbday.substring(0, imonth);
|
||||
|
||||
var iyear = sbday.lastIndexOf('/');
|
||||
if (iyear === imonth) {
|
||||
iyear = sbday.length;
|
||||
}
|
||||
var sday = sbday.substring(imonth + 1, iyear);
|
||||
|
||||
var syear = sbday.substring(iyear + 1, sbday.length);
|
||||
|
||||
out.setUTCDate(parseInt(sday, 10));
|
||||
out.setUTCMonth(parseInt(smonth, 10) - 1, parseInt(sday, 10));
|
||||
|
||||
if (syear && syear.length > 0) {
|
||||
out.setUTCFullYear(parseInt(syear, 10));
|
||||
}
|
||||
else {
|
||||
// 9996 is the year that flags a not known year
|
||||
out.setUTCFullYear(fb.FLAG_YEAR_IGNORED);
|
||||
}
|
||||
|
||||
out.setUTCHours(0);
|
||||
out.setUTCMinutes(0);
|
||||
out.setUTCSeconds(0);
|
||||
out.setUTCMilliseconds(0);
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
fb.getAddresses = function(fbdata) {
|
||||
|
||||
function fillAddress(fbAddress, type) {
|
||||
var outAddr = {};
|
||||
|
||||
outAddr.type = [type];
|
||||
outAddr.locality = fbAddress.city || '';
|
||||
outAddr.region = fbAddress.state || '';
|
||||
outAddr.countryName = fbAddress.country || '';
|
||||
|
||||
return outAddr;
|
||||
}
|
||||
|
||||
var out = [];
|
||||
var addrTypes = {
|
||||
'home': 'hometown_location',
|
||||
'current': 'current_location'
|
||||
};
|
||||
|
||||
Object.keys(addrTypes).forEach(function onAddressType(type) {
|
||||
var addrObj = fbdata[addrTypes[type]];
|
||||
if (addrObj) {
|
||||
out.push(fillAddress(addrObj, type));
|
||||
}
|
||||
});
|
||||
return out;
|
||||
};
|
||||
|
||||
// The contact is now totally unlinked
|
||||
// [...,facebook, fb_not_linked, 123456,....]
|
||||
fb.markAsUnlinked = function(devContact) {
|
||||
var category = devContact.category;
|
||||
var updatedCategory = [];
|
||||
|
||||
if (category) {
|
||||
var idx = category.indexOf(fb.CATEGORY);
|
||||
if (idx !== -1) {
|
||||
for (var c = 0; c < idx; c++) {
|
||||
updatedCategory.push(category[c]);
|
||||
}
|
||||
// The facebook category, the linked mark and the UID are skipped
|
||||
for (c = idx + 3; c < category.length; c++) {
|
||||
updatedCategory.push(category[c]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
devContact.category = updatedCategory;
|
||||
|
||||
return devContact;
|
||||
};
|
||||
|
||||
// Unlinks a FB Contact. This must only be called from the clean all process
|
||||
fb.unlinkClearAll = function(devContact) {
|
||||
fb.resetNames(devContact);
|
||||
fb.markAsUnlinked(devContact);
|
||||
};
|
||||
|
||||
// Reset givenName and familyName if it is needed after unlinking
|
||||
fb.resetNames = function resetNames(dContact) {
|
||||
if (fb.isPropagated('givenName', dContact)) {
|
||||
dContact.givenName = [''];
|
||||
fb.removePropagatedFlag('givenName', dContact);
|
||||
}
|
||||
|
||||
if (fb.isPropagated('familyName', dContact)) {
|
||||
dContact.familyName = [''];
|
||||
fb.removePropagatedFlag('familyName', dContact);
|
||||
}
|
||||
|
||||
dContact.name = [dContact.givenName[0] + ' ' + dContact.familyName[0]];
|
||||
};
|
||||
|
||||
// Marks that FB Cleaning is in progress or not
|
||||
fb.markFbCleaningInProgress = function(value) {
|
||||
utils.cookie.update({
|
||||
fbCleaningInProgress: value
|
||||
});
|
||||
};
|
||||
380
gaia/shared/js/contacts/import/facebook/fb_data.js
Executable file
380
gaia/shared/js/contacts/import/facebook/fb_data.js
Executable file
|
|
@ -0,0 +1,380 @@
|
|||
'use strict';
|
||||
|
||||
/* global LazyLoader, TelIndexer, SimplePhoneMatcher */
|
||||
|
||||
// WARNING: This file lazy loads:
|
||||
// 'shared/js/fb/fb_data_reader.js
|
||||
// 'shared/js/fb/fb_tel_index.js'
|
||||
// 'shared/js/binary_search.js'
|
||||
// 'shared/js/simple_phone_matcher.js' (will be removed once bug 938265 lands)
|
||||
|
||||
var fb = window.fb || {};
|
||||
|
||||
(function() {
|
||||
var contacts = fb.contacts || {};
|
||||
fb.contacts = contacts;
|
||||
|
||||
var Reader;
|
||||
var readerLoaded = false;
|
||||
|
||||
// Record Id for the index
|
||||
var INDEX_ID = 1;
|
||||
var isIndexDirty = false;
|
||||
var READER_LOADED_EV = 'reader_loaded';
|
||||
|
||||
var TEL_INDEXER_JS = '/shared/js/fb/fb_tel_index.js';
|
||||
var PHONE_MATCHER_JS = '/shared/js/simple_phone_matcher.js';
|
||||
var FB_READER_JS = '/shared/js/fb/fb_data_reader.js';
|
||||
var BINARY_SEARCH_JS = '/shared/js/binary_search.js';
|
||||
|
||||
contacts.UID_NOT_FOUND = 'UIDNotFound';
|
||||
contacts.ALREADY_EXISTS = 'AlreadyExists';
|
||||
|
||||
// This is needed for having proxy methods setted and ready before
|
||||
// the real reader methods (in fb_data_reader) are loaded
|
||||
if (!contacts.init) {
|
||||
var proxyMethods = ['get', 'getLength', 'getByPhone', 'search',
|
||||
'refresh', 'init', 'restart'];
|
||||
proxyMethods.forEach(function(aMethod) {
|
||||
contacts[aMethod] = defaultFunction.bind(null, aMethod);
|
||||
});
|
||||
LazyLoader.load(FB_READER_JS, onreaderLoaded);
|
||||
}
|
||||
else {
|
||||
onreaderLoaded();
|
||||
}
|
||||
|
||||
function onreaderLoaded() {
|
||||
readerLoaded = true;
|
||||
Reader = fb.contacts;
|
||||
document.dispatchEvent(new CustomEvent(READER_LOADED_EV));
|
||||
}
|
||||
|
||||
function setIndex(index) {
|
||||
Reader.dsIndex = index;
|
||||
isIndexDirty = false;
|
||||
}
|
||||
|
||||
function datastore() {
|
||||
return Reader.datastore;
|
||||
}
|
||||
|
||||
function index() {
|
||||
return Reader.dsIndex;
|
||||
}
|
||||
|
||||
function defaultFunction(target) {
|
||||
var args = [];
|
||||
for (var j = 1; j < arguments.length; j++) {
|
||||
args.push(arguments[j]);
|
||||
}
|
||||
if (!readerLoaded) {
|
||||
document.addEventListener(READER_LOADED_EV, function rd_loaded() {
|
||||
document.removeEventListener(READER_LOADED_EV, rd_loaded);
|
||||
Reader[target].apply(this, args);
|
||||
});
|
||||
}
|
||||
else {
|
||||
// As the reader load will overwrite those functions probably this
|
||||
// will never be called
|
||||
/*jshint validthis:true */
|
||||
Reader[target].apply(this, args);
|
||||
}
|
||||
}
|
||||
|
||||
function initError(outRequest, error) {
|
||||
outRequest.failed(error);
|
||||
}
|
||||
|
||||
// Ensures a proper error object is returned
|
||||
function safeError(err) {
|
||||
if (err && err.name) {
|
||||
return err;
|
||||
}
|
||||
return {
|
||||
name: 'UnknownError'
|
||||
};
|
||||
}
|
||||
|
||||
// Creates a default handler for errors
|
||||
function defaultError(request) {
|
||||
return defaultErrorCb.bind(null, request);
|
||||
}
|
||||
|
||||
// Creates a default handler for success
|
||||
function defaultSuccess(request) {
|
||||
return defaultSuccessCb.bind(null, request);
|
||||
}
|
||||
|
||||
function defaultErrorCb(request, error) {
|
||||
request.failed(safeError(error));
|
||||
}
|
||||
|
||||
function defaultSuccessCb(request, result) {
|
||||
request.done(result);
|
||||
}
|
||||
|
||||
function doSave(obj, outRequest) {
|
||||
LazyLoader.load([TEL_INDEXER_JS, PHONE_MATCHER_JS,
|
||||
BINARY_SEARCH_JS] , function() {
|
||||
var uid = obj.uid;
|
||||
datastore().add(obj, uid).then(function success() {
|
||||
indexByPhone(obj, uid);
|
||||
isIndexDirty = true;
|
||||
outRequest.done();
|
||||
}, function error(err) {
|
||||
if (err.name === 'ConstraintError') {
|
||||
err = {
|
||||
name: contacts.ALREADY_EXISTS
|
||||
};
|
||||
}
|
||||
outRequest.failed(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function indexByPhone(obj, newId) {
|
||||
// Update index by tel
|
||||
// As this is populated by FB importer we don't need to have
|
||||
// extra checks
|
||||
if (Array.isArray(obj.tel)) {
|
||||
obj.tel.forEach(function(aTel) {
|
||||
var variants = SimplePhoneMatcher.generateVariants(aTel.value);
|
||||
|
||||
variants.forEach(function(aVariant) {
|
||||
index().byTel[aVariant] = newId;
|
||||
});
|
||||
// To avoid the '+' char
|
||||
TelIndexer.index(index().treeTel, aTel.value.substring(1), newId);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function reIndexByPhone(oldObj, newObj, dsId) {
|
||||
removePhoneIndex(oldObj);
|
||||
indexByPhone(newObj, dsId);
|
||||
TelIndexer.orderTree(index().treeTel);
|
||||
}
|
||||
|
||||
function removePhoneIndex(deletedFriend) {
|
||||
// Need to update the tel indexes
|
||||
if (Array.isArray(deletedFriend.tel)) {
|
||||
deletedFriend.tel.forEach(function(aTel) {
|
||||
TelIndexer.remove(index().treeTel, aTel.value.substring(1));
|
||||
|
||||
var variants = SimplePhoneMatcher.generateVariants(aTel.value);
|
||||
variants.forEach(function(aVariant) {
|
||||
delete index().byTel[aVariant];
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to save FB Friend Information
|
||||
*
|
||||
*/
|
||||
contacts.save = function(obj) {
|
||||
var retRequest = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function save() {
|
||||
contacts.init(function() {
|
||||
doSave(obj, retRequest);
|
||||
},
|
||||
function(err) {
|
||||
initError(retRequest, err);
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return retRequest;
|
||||
};
|
||||
|
||||
/**
|
||||
* Allows to update FB Friend Information
|
||||
*
|
||||
*
|
||||
*/
|
||||
contacts.update = function(obj) {
|
||||
var retRequest = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function save() {
|
||||
contacts.init(function() {
|
||||
doUpdate(obj, retRequest);
|
||||
},
|
||||
function(err) {
|
||||
initError(retRequest, err);
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return retRequest;
|
||||
};
|
||||
|
||||
function doUpdate(obj, outRequest) {
|
||||
LazyLoader.load([TEL_INDEXER_JS, PHONE_MATCHER_JS,
|
||||
BINARY_SEARCH_JS], function() {
|
||||
var uid = obj.uid;
|
||||
|
||||
var successCb = successUpdate.bind(null, outRequest);
|
||||
var errorCb = errorUpdate.bind(null, outRequest, uid);
|
||||
|
||||
// It is necessary to get the old object and delete old indexes
|
||||
datastore().get(uid).then(function success(oldObj) {
|
||||
if (!oldObj) {
|
||||
errorCb({
|
||||
name: contacts.UID_NOT_FOUND
|
||||
});
|
||||
return;
|
||||
}
|
||||
reIndexByPhone(oldObj, obj, uid);
|
||||
datastore().put(obj, uid).then(function success() {
|
||||
return datastore().put(index(), INDEX_ID);
|
||||
}, errorCb).then(successCb, errorCb);
|
||||
}, errorCb); // datastore.get
|
||||
});
|
||||
}
|
||||
|
||||
function successUpdate(outRequest) {
|
||||
outRequest.done();
|
||||
}
|
||||
|
||||
function errorUpdate(outRequest, uid, error) {
|
||||
window.console.error('Error while updating datastore for: ', uid);
|
||||
outRequest.failed(error);
|
||||
}
|
||||
|
||||
function doRemove(uid, outRequest, forceFlush) {
|
||||
LazyLoader.load([TEL_INDEXER_JS, PHONE_MATCHER_JS,
|
||||
BINARY_SEARCH_JS], function() {
|
||||
var errorCb = errorRemove.bind(null, outRequest, uid);
|
||||
var objToDelete;
|
||||
|
||||
datastore().get(uid).then(function success_get_remove(object) {
|
||||
objToDelete = object;
|
||||
if (!objToDelete) {
|
||||
errorRemove(outRequest, uid, {
|
||||
name: contacts.UID_NOT_FOUND
|
||||
});
|
||||
return;
|
||||
}
|
||||
datastore().remove(uid).then(function success_rm(removed) {
|
||||
successRemove(outRequest, objToDelete, forceFlush, removed);
|
||||
}, errorCb);
|
||||
}, errorCb);
|
||||
}); // LazyLoader.load
|
||||
}
|
||||
|
||||
// Needs to update the index data conveniently
|
||||
function successRemove(outRequest, deletedFriend, forceFlush, removed) {
|
||||
if (removed === true) {
|
||||
isIndexDirty = true;
|
||||
|
||||
removePhoneIndex(deletedFriend);
|
||||
if (forceFlush) {
|
||||
var flushReq = fb.contacts.flush();
|
||||
|
||||
flushReq.onsuccess = function() {
|
||||
isIndexDirty = false;
|
||||
outRequest.done(true);
|
||||
};
|
||||
flushReq.onerror = function() {
|
||||
outRequest.failed(flushReq.error);
|
||||
};
|
||||
}
|
||||
else {
|
||||
outRequest.done(true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
outRequest.done(false);
|
||||
}
|
||||
}
|
||||
|
||||
function errorRemove(outRequest, uid, error) {
|
||||
error = safeError(error);
|
||||
window.console.error('FB Data: Error while removing ', uid, ': ',
|
||||
error.name);
|
||||
outRequest.failed(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to remove FB contact from the DB
|
||||
*
|
||||
*/
|
||||
contacts.remove = function(uid, flush) {
|
||||
var hasToFlush = (flush === true ? flush : false);
|
||||
var retRequest = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function remove() {
|
||||
contacts.init(function() {
|
||||
doRemove(uid, retRequest, hasToFlush);
|
||||
},
|
||||
function(err) {
|
||||
initError(retRequest, err);
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return retRequest;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes all the FB Friends and the index
|
||||
*
|
||||
* The index is restored as empty
|
||||
*
|
||||
*/
|
||||
contacts.clear = function() {
|
||||
var outRequest = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function clear() {
|
||||
contacts.init(function() {
|
||||
doClear(outRequest);
|
||||
},
|
||||
function(err) {
|
||||
initError(outRequest, err);
|
||||
});
|
||||
}, 0);
|
||||
|
||||
return outRequest;
|
||||
};
|
||||
|
||||
function doClear(outRequest) {
|
||||
datastore().clear().then(function success() {
|
||||
setIndex(null);
|
||||
// TODO:
|
||||
// This is working but there are open questions on the mailing list
|
||||
datastore().put(index(), INDEX_ID).then(defaultSuccess(outRequest),
|
||||
function error(err) {
|
||||
err = safeError(err);
|
||||
window.console.error('Error while re-creating the index: ',
|
||||
err.name);
|
||||
outRequest.failed(err);
|
||||
}
|
||||
);
|
||||
}, defaultError(outRequest));
|
||||
}
|
||||
|
||||
/**
|
||||
* Persists the index on the datastore
|
||||
*
|
||||
*/
|
||||
contacts.flush = function() {
|
||||
var outRequest = new fb.utils.Request();
|
||||
|
||||
window.setTimeout(function do_Flush() {
|
||||
if (!(datastore()) || !isIndexDirty) {
|
||||
window.console.warn(
|
||||
'The datastore has not been initialized or is not dirty');
|
||||
outRequest.done();
|
||||
return;
|
||||
}
|
||||
|
||||
TelIndexer.orderTree(index().treeTel);
|
||||
datastore().put(index(), INDEX_ID).then(
|
||||
defaultSuccess(outRequest),
|
||||
defaultError(outRequest));
|
||||
}, 0);
|
||||
|
||||
return outRequest;
|
||||
};
|
||||
|
||||
})();
|
||||
163
gaia/shared/js/contacts/import/facebook/fb_query.js
Executable file
163
gaia/shared/js/contacts/import/facebook/fb_query.js
Executable file
|
|
@ -0,0 +1,163 @@
|
|||
'use strict';
|
||||
|
||||
/* global fb */
|
||||
|
||||
/* jshint ignore:start */
|
||||
var fb = this.fb || {};
|
||||
|
||||
// In an only worker execution context this would not be necessary
|
||||
var self = this;
|
||||
fb.utils = this.fb.utils || {};
|
||||
/* jshint ignore:end */
|
||||
|
||||
fb.DEFAULT_TIMEOUT = 30000;
|
||||
|
||||
// Runs a query against Facebook FQL. Callback is a string!!
|
||||
fb.utils.runQuery = function(query, callback, access_token) {
|
||||
// Auxiliary function for canceling a request
|
||||
function QueryRequest() {
|
||||
this.cancel = function() {
|
||||
if (typeof this.oncancel === 'function') {
|
||||
window.setTimeout(function() {
|
||||
this.oncancel();
|
||||
}.bind(this), 0);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
var outReq = new QueryRequest();
|
||||
|
||||
var queryService = 'https://graph.facebook.com/fql?q=';
|
||||
queryService += encodeURIComponent(query);
|
||||
|
||||
var params = ['access_token' + '=' + access_token,
|
||||
'format=json'];
|
||||
|
||||
var queryParams = params.join('&');
|
||||
|
||||
var remote = queryService + '&' + queryParams;
|
||||
|
||||
var xhr = new XMLHttpRequest({
|
||||
mozSystem: true
|
||||
});
|
||||
|
||||
// To enable xhr.abort if user cancels
|
||||
outReq.xhr = xhr;
|
||||
outReq.oncancel = function() {
|
||||
this.xhr.abort();
|
||||
};
|
||||
|
||||
xhr.open('GET', remote, true);
|
||||
xhr.responseType = 'json';
|
||||
|
||||
xhr.timeout = fb.operationsTimeout || fb.DEFAULT_TIMEOUT;
|
||||
|
||||
xhr.onload = function(e) {
|
||||
if (xhr.status === 200 || xhr.status === 400 || xhr.status === 0) {
|
||||
if (callback && typeof callback.success === 'function') {
|
||||
self.setTimeout(function() {
|
||||
callback.success(xhr.response);
|
||||
},0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
self.console.error('FB: HTTP error executing query. ',
|
||||
query, ' Status: ', xhr.status);
|
||||
if (callback && typeof callback.error === 'function') {
|
||||
self.setTimeout(callback.error, 0);
|
||||
}
|
||||
}
|
||||
}; // onload
|
||||
|
||||
xhr.ontimeout = function(e) {
|
||||
self.console.error('FB: Timeout!!! while executing query', query);
|
||||
if (callback && typeof callback.timeout === 'function') {
|
||||
self.setTimeout(callback.timeout, 0);
|
||||
}
|
||||
}; // ontimeout
|
||||
|
||||
xhr.onerror = function(e) {
|
||||
self.console.error('FB: Error while executing query: ', query,
|
||||
': ', e);
|
||||
if (callback && typeof callback.error === 'function') {
|
||||
self.setTimeout(function() {
|
||||
callback.error(e);
|
||||
},0);
|
||||
}
|
||||
}; // onerror
|
||||
|
||||
xhr.send();
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
/**
|
||||
* Obtains a img DOM Element with the Contact's img
|
||||
*
|
||||
*/
|
||||
fb.utils.getFriendPicture = function(uid, callback, access_token,
|
||||
targetPictureSize) {
|
||||
// Access token is necessary just in case the image is not public
|
||||
// When passing an access token to FB https must be used
|
||||
|
||||
var imgSrc = 'https://graph.facebook.com/' + uid + '/picture?';
|
||||
|
||||
var params = [
|
||||
'width=' + targetPictureSize,
|
||||
'access_token=' + access_token,
|
||||
't=' + Date.now()
|
||||
];
|
||||
|
||||
var imgService = imgSrc + params.join('&');
|
||||
|
||||
var xhr = new XMLHttpRequest({
|
||||
mozSystem: true
|
||||
});
|
||||
xhr.open('GET', imgService, true);
|
||||
xhr.responseType = 'blob';
|
||||
|
||||
xhr.timeout = fb.operationsTimeout || fb.DEFAULT_TIMEOUT;
|
||||
|
||||
xhr.onload = function(e) {
|
||||
if (xhr.status === 200 || xhr.status === 0) {
|
||||
var mblob = e.target.response;
|
||||
if (typeof callback === 'function') {
|
||||
self.setTimeout(function() {
|
||||
callback(mblob);
|
||||
},0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
self.console.error('FB: HTTP error retrieving img for uid: ',
|
||||
uid, ' Status: ', xhr.status);
|
||||
if (typeof callback === 'function') {
|
||||
self.setTimeout(function() {
|
||||
callback(null);
|
||||
},0);
|
||||
}
|
||||
}
|
||||
}; // onload
|
||||
|
||||
xhr.ontimeout = function(e) {
|
||||
self.console.error('FB: Timeout!!! while retrieving img for uid: ', uid);
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
self.setTimeout(function() {
|
||||
callback(null);
|
||||
},0);
|
||||
}
|
||||
}; // ontimeout
|
||||
|
||||
xhr.onerror = function(e) {
|
||||
self.console.error('FB: Error while retrieving img for uid: ', uid,
|
||||
'Error: ', e);
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
self.setTimeout(function() {
|
||||
callback(null);
|
||||
},0);
|
||||
}
|
||||
}; // onerror
|
||||
|
||||
xhr.send();
|
||||
};
|
||||
526
gaia/shared/js/contacts/import/facebook/fb_utils.js
Executable file
526
gaia/shared/js/contacts/import/facebook/fb_utils.js
Executable file
|
|
@ -0,0 +1,526 @@
|
|||
'use strict';
|
||||
|
||||
/* global ImportStatusData, oauthflow, utils */
|
||||
/* Ignoring function declaration in loops */
|
||||
/* jshint -W083 */
|
||||
|
||||
var fb = window.fb || {};
|
||||
window.fb = fb;
|
||||
|
||||
(function(document) {
|
||||
var Utils = fb.utils || {};
|
||||
fb.utils = Utils;
|
||||
|
||||
var FRIEND_COUNT_QUERY = 'select friend_count from user where uid=me()';
|
||||
|
||||
var CACHE_FRIENDS_KEY = Utils.CACHE_FRIENDS_KEY = 'numFacebookFriends';
|
||||
var LAST_UPDATED_KEY = Utils.LAST_UPDATED_KEY = 'lastUpdatedTime';
|
||||
Utils.ALARM_ID_KEY = 'nextAlarmId';
|
||||
Utils.SCHEDULE_SYNC_KEY = 'facebookShouldHaveScheduledAt';
|
||||
|
||||
var REDIRECT_LOGOUT_URI = window.oauthflow ?
|
||||
oauthflow.params.facebook.redirectLogout : '';
|
||||
var STORAGE_KEY = Utils.TOKEN_DATA_KEY = 'tokenData';
|
||||
|
||||
// For controlling data synchronization
|
||||
Utils.setLastUpdate = function(value, cb) {
|
||||
window.asyncStorage.setItem(LAST_UPDATED_KEY, {
|
||||
data: value
|
||||
}, cb);
|
||||
};
|
||||
|
||||
Utils.getLastUpdate = function(callback) {
|
||||
window.asyncStorage.getItem(LAST_UPDATED_KEY, function(obj) {
|
||||
var out = 0;
|
||||
if (obj) {
|
||||
out = obj.data || out;
|
||||
}
|
||||
if (typeof callback === 'function') {
|
||||
callback(out);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Utils.getNonCacheableUrl = function(url) {
|
||||
if (url.indexOf('?') === -1) {
|
||||
url += '?';
|
||||
}
|
||||
|
||||
return url + '&burst_cache=' + Date.now();
|
||||
};
|
||||
|
||||
|
||||
Utils.getContactData = function(cid) {
|
||||
var outReq = new Utils.Request();
|
||||
|
||||
var req = navigator.mozContacts.find({
|
||||
filterBy: ['id'],
|
||||
filterValue: cid,
|
||||
filterOp: 'equals'
|
||||
});
|
||||
|
||||
req.onsuccess = function(e) {
|
||||
if (e.target.result && e.target.result.length > 0) {
|
||||
outReq.done(e.target.result[0]);
|
||||
}
|
||||
else {
|
||||
outReq.done(null);
|
||||
}
|
||||
};
|
||||
|
||||
req.onerror = function(e) {
|
||||
outReq.failed(e.target.error);
|
||||
};
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
// Returns the mozContact associated to a UID in FB
|
||||
Utils.getMozContact = function(uid) {
|
||||
var outReq = new Utils.Request();
|
||||
|
||||
window.setTimeout(function get_mozContact_ByUid() {
|
||||
fb.getMozContactByUid(uid,
|
||||
function onsuccess(result) {
|
||||
if (Array.isArray(result) && result.length > 0) {
|
||||
outReq.done(result[0]);
|
||||
} else {
|
||||
outReq.done(null);
|
||||
}
|
||||
},
|
||||
function onerror(e) {
|
||||
outReq.failed(e.target.error);
|
||||
}
|
||||
);
|
||||
}, 0);
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
// Returns the number of mozContacts associated to a UID in FB
|
||||
Utils.getNumberMozContacts = function(uid) {
|
||||
var outReq = new Utils.Request();
|
||||
|
||||
window.setTimeout(function get_mozContact_ByUid() {
|
||||
fb.getMozContactByUid(uid,
|
||||
function onsuccess(result) {
|
||||
if (Array.isArray(result)) {
|
||||
outReq.done(result.length);
|
||||
} else {
|
||||
outReq.done(0);
|
||||
}
|
||||
},
|
||||
function onerror(error) {
|
||||
outReq.failed(error);
|
||||
}
|
||||
);
|
||||
},0);
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
Utils.getAllFbContacts = function() {
|
||||
var outReq = new Utils.Request();
|
||||
|
||||
window.setTimeout(function get_all_fb_contacts() {
|
||||
var filter = {
|
||||
filterValue: fb.CATEGORY,
|
||||
filterOp: 'contains',
|
||||
filterBy: ['category']
|
||||
};
|
||||
|
||||
var req = navigator.mozContacts.find(filter);
|
||||
|
||||
req.onsuccess = function(e) {
|
||||
outReq.done(e.target.result);
|
||||
};
|
||||
|
||||
req.onerror = function(e) {
|
||||
outReq.failed(e.target.error);
|
||||
};
|
||||
}, 0);
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
// On the device
|
||||
Utils.getNumFbContacts = function() {
|
||||
var outReq = new Utils.Request();
|
||||
|
||||
window.setTimeout(function get_num_fb_contacts() {
|
||||
var req = fb.contacts.getLength();
|
||||
|
||||
req.onsuccess = function() {
|
||||
outReq.done(req.result);
|
||||
};
|
||||
|
||||
req.onerror = function() {
|
||||
outReq.failed(req.error);
|
||||
};
|
||||
}, 0);
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
// Requests the number remotely
|
||||
Utils.getNumFbFriends = function(callback, access_token) {
|
||||
fb.utils.runQuery(FRIEND_COUNT_QUERY, callback, access_token);
|
||||
};
|
||||
|
||||
Utils.getCachedAccessToken = function(callback) {
|
||||
ImportStatusData.get(STORAGE_KEY).then(function(data) {
|
||||
var out = null;
|
||||
|
||||
if (data) {
|
||||
out = data.access_token || null;
|
||||
}
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback(out);
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
Utils.setCachedAccessToken = function(data, callback) {
|
||||
ImportStatusData.put(STORAGE_KEY, data).then(function done() {
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
}, function error() {
|
||||
console.error('Error while saving access token');
|
||||
if (typeof callback === 'function') {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Obtains the number locally
|
||||
Utils.getCachedNumFbFriends = function(callback) {
|
||||
ImportStatusData.get(CACHE_FRIENDS_KEY).then(function(data) {
|
||||
if (typeof callback === 'function' && typeof data === 'number') {
|
||||
callback(data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Utils.setCachedNumFriends = function(value, cb) {
|
||||
ImportStatusData.put(CACHE_FRIENDS_KEY, value).then(cb);
|
||||
};
|
||||
|
||||
// Removes cached number of friends from datastore to keep state of sync.
|
||||
Utils.removeCachedNumFriends = function(callback) {
|
||||
ImportStatusData.remove(CACHE_FRIENDS_KEY).then(function() {
|
||||
typeof callback === 'function' && callback();
|
||||
});
|
||||
};
|
||||
|
||||
Utils.getImportChecked = function(callback) {
|
||||
// If we have an access token Import should be checked
|
||||
Utils.getCachedAccessToken(function(access_token) {
|
||||
var out = 'logged-out';
|
||||
|
||||
if (access_token) {
|
||||
out = 'logged-in';
|
||||
}
|
||||
else {
|
||||
// In this case is needed to know whether the access_token has
|
||||
// been invalidated
|
||||
Utils.getCachedNumFbFriends(function(value) {
|
||||
if (value) {
|
||||
out = 'renew-pwd';
|
||||
if (typeof callback === 'function') {
|
||||
callback(out);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof callback === 'function') {
|
||||
callback(out);
|
||||
}
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Obtains the number locally (cached) and tries to get them remotely
|
||||
Utils.numFbFriendsData = function(callback) {
|
||||
var localCb = callback.local;
|
||||
var remoteCb = callback.remote;
|
||||
|
||||
Utils.getCachedNumFbFriends(localCb);
|
||||
|
||||
function auxCallback(response) {
|
||||
if (response.data && response.data[0] &&
|
||||
response.data[0].friend_count) {
|
||||
remoteCb(response.data[0].friend_count);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof remoteCb === 'function' && navigator.onLine === true) {
|
||||
var remoteCallbacks = {
|
||||
success: auxCallback,
|
||||
error: null,
|
||||
timeout: null
|
||||
};
|
||||
Utils.getCachedAccessToken(function(access_token) {
|
||||
if (access_token) {
|
||||
Utils.getNumFbFriends(remoteCallbacks, access_token);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Clears all Fb data (use with caution!!)
|
||||
Utils.clearFbData = function() {
|
||||
// First step all Contacts which are FB Friends are obtained
|
||||
// then those not linked are directly removed
|
||||
// Those linked are unlinked
|
||||
|
||||
var outReq = new Utils.Request();
|
||||
|
||||
window.setTimeout(function do_clearFbData() {
|
||||
// First a clear request is issued
|
||||
var ireq = fb.contacts.clear();
|
||||
|
||||
ireq.onsuccess = function() {
|
||||
var req = Utils.getAllFbContacts();
|
||||
|
||||
req.onsuccess = function() {
|
||||
var cleaner = new Utils.FbContactsCleaner(req.result, 'clear');
|
||||
// And now success notification is sent
|
||||
outReq.done(cleaner);
|
||||
// The cleaning activity should be starting immediately
|
||||
window.setTimeout(cleaner.start, 0);
|
||||
};
|
||||
|
||||
req.onerror = function() {
|
||||
window.console.error('FB Clean. Error retrieving FB Contacts');
|
||||
outReq.failed(req.error);
|
||||
};
|
||||
};
|
||||
|
||||
ireq.onerror = function(e) {
|
||||
window.console.error('Error while clearing the FB Cache');
|
||||
outReq.failed(ireq.error);
|
||||
};
|
||||
|
||||
},0);
|
||||
|
||||
return outReq;
|
||||
};
|
||||
|
||||
Utils.logout = function() {
|
||||
var outReq = new Utils.Request();
|
||||
|
||||
// Duration of the timer that checks window close by the user (1/2 sec)
|
||||
var WINDOW_TIMER_INTERVAL = 500;
|
||||
// Maximum time we are going to wait for a logout operation (5 secs)
|
||||
var LOGOUT_TIMEOUT = 5000;
|
||||
var MAX_TIMER_TICKS = LOGOUT_TIMEOUT / WINDOW_TIMER_INTERVAL;
|
||||
|
||||
window.setTimeout(function do_logout() {
|
||||
Utils.getCachedAccessToken(function getAccessToken(access_token) {
|
||||
if (access_token) {
|
||||
var logoutService = 'https://www.facebook.com/logout.php?';
|
||||
var params = [
|
||||
'next' + '=' + encodeURIComponent(REDIRECT_LOGOUT_URI),
|
||||
'access_token' + '=' + access_token
|
||||
];
|
||||
|
||||
var logoutParams = params.join('&');
|
||||
var logoutUrl = logoutService + logoutParams;
|
||||
var loggedOut = false;
|
||||
|
||||
var m_listen = function(e) {
|
||||
if (e.origin !== fb.CONTACTS_APP_ORIGIN) {
|
||||
return;
|
||||
}
|
||||
if (e.data === 'closed') {
|
||||
loggedOut = true;
|
||||
ImportStatusData.remove(STORAGE_KEY);
|
||||
outReq.done();
|
||||
}
|
||||
e.stopImmediatePropagation();
|
||||
window.removeEventListener('message', m_listen);
|
||||
};
|
||||
|
||||
window.addEventListener('message', m_listen);
|
||||
|
||||
var logoutWindow = window.open(logoutUrl, '', 'dialog');
|
||||
// This timer is run in order to monitor whether the window is
|
||||
// closed by the user during the logout process (bug 1084224).
|
||||
// This will have to be removed once bug 966216 is fixed
|
||||
var timerTicks = 0;
|
||||
var timerId = window.setInterval(function() {
|
||||
timerTicks++;
|
||||
var closed = (logoutWindow.closed === true);
|
||||
var timedOut = (timerTicks >= MAX_TIMER_TICKS);
|
||||
|
||||
if (loggedOut || closed || timedOut) {
|
||||
window.clearInterval(timerId);
|
||||
window.removeEventListener('message', m_listen);
|
||||
}
|
||||
|
||||
if (closed && !loggedOut) {
|
||||
outReq.failed('UserCancelled');
|
||||
}
|
||||
else if (timedOut && !loggedOut) {
|
||||
outReq.failed('Timeout');
|
||||
}
|
||||
}, WINDOW_TIMER_INTERVAL);
|
||||
} // if
|
||||
else {
|
||||
outReq.done();
|
||||
}
|
||||
}); // cachedToken
|
||||
}, 0); // setTimeout
|
||||
|
||||
return outReq;
|
||||
|
||||
}; // logout
|
||||
|
||||
|
||||
// FbContactsCleaner Object
|
||||
// Mode can be 'update' or 'cleanAll'
|
||||
Utils.FbContactsCleaner = function(contacts, pmode) {
|
||||
this.lcontacts = contacts;
|
||||
var total = contacts.length;
|
||||
var next = 0;
|
||||
var self = this;
|
||||
var CHUNK_SIZE = 5;
|
||||
var numResponses = 0;
|
||||
var mustUpdate = (pmode === 'update');
|
||||
var notifyClean = false;
|
||||
|
||||
var mustHold = false;
|
||||
var holded = false;
|
||||
var mustFinish = false;
|
||||
|
||||
this.start = function() {
|
||||
mustHold = holded = mustFinish = false;
|
||||
|
||||
if (total > 0) {
|
||||
cleanContacts(0);
|
||||
}
|
||||
else if (typeof self.onsuccess === 'function') {
|
||||
window.setTimeout(self.onsuccess);
|
||||
}
|
||||
};
|
||||
|
||||
this.hold = function() {
|
||||
mustHold = true;
|
||||
};
|
||||
|
||||
this.finish = function() {
|
||||
mustFinish = true;
|
||||
|
||||
if (holded) {
|
||||
notifySuccess();
|
||||
}
|
||||
};
|
||||
|
||||
this.resume = function() {
|
||||
mustHold = holded = mustFinish = false;
|
||||
|
||||
window.setTimeout(function resume_clean() {
|
||||
cleanContacts(next);
|
||||
});
|
||||
};
|
||||
|
||||
function successHandler(e) {
|
||||
if (notifyClean || typeof self.oncleaned === 'function') {
|
||||
notifyClean = true;
|
||||
// Avoiding race condition so the cleaned element is cached
|
||||
var cleaned = e.target.number;
|
||||
window.setTimeout(function() {
|
||||
self.oncleaned(cleaned);
|
||||
},0);
|
||||
}
|
||||
continueCb();
|
||||
}
|
||||
|
||||
function errorHandler(contactid, error) {
|
||||
if (typeof self.onerror === 'function') {
|
||||
self.onerror(contactid, error);
|
||||
}
|
||||
|
||||
continueCb();
|
||||
}
|
||||
|
||||
function cleanContacts(from) {
|
||||
for (var idx = from; idx < (from + CHUNK_SIZE) && idx < total; idx++) {
|
||||
var contact = contacts[idx];
|
||||
var number = idx;
|
||||
var req, fbContact;
|
||||
if (fb.isFbLinked(contact)) {
|
||||
if (mustUpdate) {
|
||||
fbContact = new fb.Contact(contact);
|
||||
req = fbContact.unlink('hard');
|
||||
}
|
||||
else {
|
||||
// Here we only need to mark this contact as unlinked
|
||||
// as the FB Data would have been already removed
|
||||
fb.unlinkClearAll(contact);
|
||||
req = navigator.mozContacts.save(
|
||||
utils.misc.toMozContact(contact));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (mustUpdate) {
|
||||
fbContact = new fb.Contact(contact);
|
||||
req = fbContact.remove();
|
||||
}
|
||||
else {
|
||||
req = navigator.mozContacts.remove(
|
||||
utils.misc.toMozContact(contact));
|
||||
}
|
||||
}
|
||||
req.number = number;
|
||||
req.onsuccess = successHandler;
|
||||
req.onerror = function(e) {
|
||||
errorHandler(contact.id, e.target.error);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function notifySuccess() {
|
||||
if (typeof self.onsuccess === 'function') {
|
||||
window.setTimeout(self.onsuccess);
|
||||
}
|
||||
}
|
||||
|
||||
function finishHandler() {
|
||||
// It is needed to flush in order to properly update the index
|
||||
var req = fb.contacts.flush();
|
||||
req.onsuccess = notifySuccess;
|
||||
req.onerror = function cleaner_flushError() {
|
||||
errorHandler(null, req.error);
|
||||
};
|
||||
}
|
||||
|
||||
function continueCb() {
|
||||
next++;
|
||||
numResponses++;
|
||||
if (next < total && numResponses === CHUNK_SIZE) {
|
||||
numResponses = 0;
|
||||
if (!mustHold && !mustFinish) {
|
||||
cleanContacts(next);
|
||||
}
|
||||
else if (mustFinish && !holded) {
|
||||
finishHandler();
|
||||
}
|
||||
|
||||
if (mustHold) {
|
||||
holded = true;
|
||||
}
|
||||
}
|
||||
else if (next >= total) {
|
||||
// End has been reached
|
||||
finishHandler();
|
||||
}
|
||||
} // function
|
||||
}; // FbContactsCleaner
|
||||
|
||||
})(document);
|
||||
Loading…
Reference in a new issue