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:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,314 @@
'use strict';
/* global LazyLoader, fb, contacts, utils */
/* exported DatastoreMigration */
var DatastoreMigration = function(db) {
var active = false;
var ongoingMigration = false;
var migrator;
var idleObserver;
var self = this;
// Only really needed for testing purposes
Object.defineProperty(this, 'db', {
set: function(val) {
if (migrator) {
migrator.db = val;
}
}
});
var IdleObserver = function() {
var self = this;
this._files = [
'/shared/js/fb/fb_request.js',
'/shared/js/contacts/import/facebook/fb_data.js'
];
// 3 seconds idle time
this.time = 3;
this.onidle = function() {
LazyLoader.load(self._files, function() {
onidle();
});
};
this.onactive = function() {
LazyLoader.load(self._files, function() {
onactive();
});
};
};
function DatastoreMigrator(db) {
var IDB_NAME = 'Gaia_Facebook_Friends';
var objStoreName;
var SLICE_SIZE = 5;
var initialized = false;
var database = db;
var self = this;
var existsDatabase = true;
this.saveErrors = false;
// Only really needed for testing purposes
Object.defineProperty(this, 'db', {
set: function(val) {
database = val;
initialized = true;
}
});
function sliceEnd(hasMore) {
if (!hasMore) {
// No more records present
window.console.info('FB Migration: All records have been migrated');
// Close the database
database.close();
if (!self.saveErrors) {
var req = window.indexedDB.deleteDatabase(IDB_NAME);
req.onsuccess = function() {
window.console.info('FB Migration: ',
'Now the indexedDB has been deleted');
};
req.onerror = function() {
window.console.error('FB Migration: ',
'Error while deleting indexedDB: ',
req.error.name);
};
}
else {
window.console.warn('There are save errors. ', 'DB not deleted');
}
}
if (typeof self.onfinished === 'function') {
self.onfinished(hasMore, self.saveErrors);
}
}
this.migrateSlice = function migrateSlice() {
var toBeMigrated = [];
var pendingRecords = true;
var self = this;
openIndexedDB(function() {
var transaction = database.transaction([objStoreName], 'readwrite');
var objectStore = transaction.objectStore(objStoreName);
var req = objectStore.openCursor();
req.onsuccess = function(e) {
var cursor = e.target.result;
if (cursor && toBeMigrated.length < SLICE_SIZE) {
var obj = cursor.value;
toBeMigrated.push(obj);
cursor.continue();
}
else {
if (!cursor) {
pendingRecords = false;
}
migrateRecords(toBeMigrated, function() {
sliceEnd(pendingRecords);
});
}
};
req.onerror = function() {
window.console.error('FB Migration: ',
'Error while requesting a cursor: ',
req.error.name);
if (typeof self.onerror === 'function') {
self.onerror();
}
};
});
};
this.reset = function() {
existsDatabase = true;
initialized = false;
this.saveErrors = false;
database = null;
};
function migrateRecords(list, cb) {
if (list.length === 0) {
cb();
return;
}
var numResponses = 0;
var migrateCb = function migratCb() {
numResponses++;
if (numResponses === list.length) {
cb();
}
};
for (var j = 0; j < list.length; j++) {
migrateRecord(list[j], migrateCb);
}
}
function removeRecord(obj, cb) {
// Now deleting from indexedDB
var trans = database.transaction([objStoreName], 'readwrite');
var objectStore = trans.objectStore(objStoreName);
var req = objectStore.delete(obj.uid);
req.onsuccess = cb;
req.onerror = function() {
window.console.error('FB Migration: Error while deleting indexedDB: ',
obj.uid, ': ', req.error.name);
cb();
};
}
function migrateRecord(obj, cb) {
var req = fb.contacts.save(obj);
req.onsuccess = function() {
window.console.info('FB Migration: indexed DB Record migrated: ',
obj.uid);
contacts.List.refreshFb(obj.uid);
removeRecord(obj, cb);
};
req.onerror = function() {
window.console.error('FB Migration: Error while saving to datastore: ',
obj.uid, req.error.name);
if (req.error.name === 'AlreadyExists') {
// Then that record was already migrated and it is safe to delete it
removeRecord(obj, cb);
}
else {
self.saveErrors = true;
cb();
}
};
}
function openIndexedDB(cb) {
if (database) {
cb();
return;
}
var req = window.indexedDB.open(IDB_NAME);
req.onupgradeneeded = function() {
existsDatabase = false;
};
req.onsuccess = function() {
if (!existsDatabase) {
// Delete it as indexedDB will have created it
window.console.warn('FB Migration: The database does not exist');
window.indexedDB.deleteDatabase(IDB_NAME);
if (typeof self.onfinished === 'function') {
self.onfinished(false);
}
return;
}
database = req.result;
objStoreName = database.objectStoreNames[0];
cb();
};
req.onerror = function() {
window.console.error('Indexed DB cannot be opened: ', req.error.name);
if (typeof self.onerror === 'function') {
self.onerror();
}
};
}
}
var start = function start() {
idleObserver = new IdleObserver();
navigator.addIdleObserver(idleObserver);
};
this.start = start;
var stop = function stop() {
navigator.removeIdleObserver(idleObserver);
if (migrator) {
migrator.reset();
}
if (typeof self.onmigrationdone === 'function') {
self.onmigrationdone();
}
};
this.stop = stop;
function onactive() {
active = true;
}
function sliceFinished(hasMore, saveErrors) {
if (!hasMore && !saveErrors) {
// And annotate in a cookie that migration was done
utils.cookie.update({
fbMigrated: true
});
stop();
}
else if (saveErrors && !hasMore) {
// The process is finished for the moment though there are errors
if (typeof self.onmigrationdone === 'function') {
self.onmigrationdone(true);
}
ongoingMigration = false;
}
else {
if (typeof self.onslicemigrated === 'function') {
self.onslicemigrated();
}
if (!active) {
ongoingMigration = true;
// If still inactive next slice is migrated
migrator.migrateSlice();
}
else {
ongoingMigration = false;
}
}
}
var onidle = function onidle() {
active = false;
if (ongoingMigration) {
return;
}
window.console.info('Idle event!!!. FB Migration about to start');
ongoingMigration = true;
if (!migrator) {
migrator = new DatastoreMigrator(db);
migrator.onfinished = sliceFinished;
migrator.onerror = function() {
ongoingMigration = false;
migrator.reset();
if (typeof self.onerror === 'function') {
self.onerror({
name: 'Migration Error'
});
}
};
}
migrator.saveErrors = false;
migrator.migrateSlice();
};
// Mainly needed for testing purposes
this.onidle = onidle;
};

View file

@ -0,0 +1,54 @@
'use strict';
/* global utils */
var fb = window.fb || {};
var config = window.config || {};
if (typeof fb.init === 'undefined') {
(function() {
var initialized = false;
var initializing = false;
var EV_FB_INIT = 'fb_init_initialized';
fb.isEnabled = false;
fb.init = function(callback) {
if (initialized) {
callback();
return;
}
if (initializing) {
document.addEventListener(EV_FB_INIT, function handler(e) {
initializing = false;
document.removeEventListener(EV_FB_INIT, handler);
callback();
});
return;
}
initializing = true;
utils.config.load('/contacts/config.json').then(
function cLoaded(configData) {
if (configData.facebookEnabled === true) {
fb.isEnabled = true;
}
fb.operationsTimeout = config.operationsTimeout =
configData.operationsTimeout;
fb.logLevel = configData.logLevel || 'none';
fb.syncPeriod = configData.facebookSyncPeriod || 24;
fb.testToken = configData.testToken;
initialized = true;
document.dispatchEvent(new CustomEvent(EV_FB_INIT));
callback();
}, function loadError(err) {
console.error('Error while loading config.json', err);
});
};
})();
}

View file

@ -0,0 +1,677 @@
'use strict';
/* global Curtain, FacebookConnector, ImageLoader, Normalizer, utils,
ImportStatusData, oauth2 */
var fb = window.fb || {};
if (!fb.link) {
(function(document) {
// parameter id for the linking contact
var link = fb.link = {};
var UI = link.ui = {};
link.CID_PARAM = 'contactid';
// Contact id that the user wants to be linked to
var contactid;
// The data of such a contact
var cdata;
// Access token
var access_token;
// Used to control the number of queries
var numQueries = 0;
// The uid of the friend to be linked
var friendUidToLink;
// Base query to search for contacts
var SEARCH_QUERY = ['SELECT uid, name, email from user ',
' WHERE uid IN (SELECT uid1 FROM friend WHERE uid2=me()) ',
' AND (', null, ')', ' ORDER BY name'
];
// Conditions
var MAIL_COND, CELL_COND;
/* jshint ignore:start */
MAIL_COND = ['strpos(email, ' , "'", null, "'", ') >= 0'];
CELL_COND = ['strpos(cell, ' , "'", null, "'", ') >= 0'];
/* jshint ignore:end */
var ALL_QUERY = ['SELECT uid, name, last_name, first_name,',
' middle_name, email from user ',
' WHERE uid IN (SELECT uid1 FROM friend WHERE uid2=me()) ',
' ORDER BY name'
];
var COUNT_QUERY = 'SELECT uid FROM user WHERE uid IN ' +
'(SELECT uid1 FROM friend WHERE uid2=me())';
var SEARCH_ACCENTS_FIELDS = {
'last_name': 'familyName',
'first_name': 'givenName',
'middle_name': 'givenName'
};
var friendsList;
var viewButton, mainSection;
var currentRecommendation = null;
var allFriends = null;
// Enables cancelation
var currentNetworkRequest = null;
// State can be proposal or view All
var state;
var imgLoader;
// Only needed for testing purposes
var completedCb;
function notifyParent(message) {
parent.postMessage({
type: message.type || '',
data: message.data || ''
}, fb.CONTACTS_APP_ORIGIN);
}
// Builds the first query for finding a contact to be linked to
function buildQuery(contact) {
var filter = [];
if (contact.tel && contact.tel.length > 0) {
contact.tel.forEach(function(tel) {
CELL_COND[2] = tel.value.trim();
filter.push(CELL_COND.join(''));
filter.push(' OR ');
});
}
if (contact.email && contact.email.length > 0) {
contact.email.forEach(function(email) {
MAIL_COND[2] = email.value.trim();
filter.push(MAIL_COND.join(''));
filter.push(' OR ');
});
}
// Remove the last OR
if (filter.length > 0) {
filter[filter.length - 1] = null;
}
var filterStr = filter.join('');
var out;
if (filterStr) {
SEARCH_QUERY[3] = filterStr;
out = SEARCH_QUERY.join('');
}
else {
out = null;
}
return out;
}
// entry point for obtaining a remote proposal
link.getRemoteProposal = function(acc_tk, contid) {
var cid = contid || contactid;
var req = fb.utils.getContactData(cid);
req.onsuccess = function() {
if (req.result) {
cdata = req.result;
numQueries = 1;
currentRecommendation = null;
var query = buildQuery(cdata);
// Check whether we have enough info for the first query
// otherwise launch the getAll query
if (query === null) {
getRemoteProposalAll(acc_tk);
return;
}
doGetRemoteProposal(acc_tk, cdata, query);
}
else {
throw new Error(
'FB: Contact to be linked not found in mozContacts: ' + cid);
}
};
req.onerror = function(e) {
window.console.error('FB: Error while retrieving contact data: ', cid);
throw e;
};
};
function getRemoteProposalAll(acc_tk) {
numQueries++;
doGetRemoteProposal(acc_tk, null, ALL_QUERY.join(''), true);
}
// This function deals with the response for a proposal
// It takes care whether a response with the number of friends is present
// and updates the cache accordingly
function proposalReadyMultiple(done, error, response) {
// If there is an error we just pass it upstream
if (response.error) {
done(response);
return;
} else if (!Array.isArray(response.data)) {
error({
name: 'QueryResponseError'
});
return;
}
var friendList, totalFriends;
if (response.data.length > 0 &&
Array.isArray(response.data[0].fql_result_set)) {
friendList = response.data[0].fql_result_set;
if (response.data[1] &&
Array.isArray(response.data[1].fql_result_set)) {
totalFriends = response.data[1].fql_result_set.length;
}
}
else {
friendList = response.data;
totalFriends = friendList.length;
}
if (typeof totalFriends !== 'undefined') {
fb.utils.setCachedNumFriends(totalFriends);
}
done({
data: friendList
});
}
// Performs all the work to obtain the remote proposal
// the "isAll" parameter indicates that the query will get all friends
function doGetRemoteProposal(acc_tk, contactData, query, isAll) {
/*
Phone.lookup was analysed but we were not happy about how it worked
var searchService = 'https://api.facebook.com/method/phonebook.lookup' +
'?include_non_fb=false';
var entries = [];
entries.push(getEntry(contactData));
var sentries = JSON.stringify(entries);
*/
var theQuery = query;
var callback = proposalReadyMultiple.bind(null, fb.link.proposalReady,
fb.link.errorHandler);
// If the query is not going to provide a full list of friends
// We count all in order to refresh the total number of friends
if (!isAll) {
theQuery = JSON.stringify({
query1: query,
query2: COUNT_QUERY
});
}
state = 'proposal';
currentNetworkRequest = fb.utils.runQuery(theQuery, {
success: callback,
error: fb.link.errorHandler,
timeout: fb.link.timeoutHandler
}, acc_tk);
}
// Invoked when remoteAll is canceled
function cancelCb(shouldNotifyParent) {
if (currentNetworkRequest) {
currentNetworkRequest.cancel();
currentNetworkRequest = null;
}
Curtain.hide(shouldNotifyParent ? notifyParent.bind(
null, {type: 'abort'}) : null);
}
// Obtains a proposal with all friends
function getRemoteAll() {
Curtain.oncancel = cancelCb;
Curtain.show('wait', 'friends');
state = 'friends';
currentNetworkRequest = fb.utils.runQuery(ALL_QUERY.join(''), {
success: fb.link.friendsReady,
error: fb.link.errorHandler,
timeout: fb.link.timeoutHandler
}, access_token);
}
// Obtains a linking proposal to be shown to the user
link.getProposal = function(cid, acc_tk) {
link.getRemoteProposal(acc_tk, cid);
};
// Executed when the server response is available
link.proposalReady = function(response) {
if (typeof response.error !== 'undefined') {
window.console.error('FB: Error while retrieving link data',
response.error.code, response.error.message);
if (response.error.code != 190) {
setCurtainHandlersErrorProposal();
Curtain.show('error', 'proposal');
}
else {
// access token problem. A new OAuth 2.0 flow has to start
handleTokenError();
}
return;
}
if (response.data.length === 0 && numQueries === 1) {
getRemoteProposalAll(access_token);
}
else {
var data = response.data;
currentRecommendation = data;
var sortedByName = [];
var numFriendsProposed = data.length;
var searchAccentsArrays = {};
var index = 0;
data.forEach(function(item) {
if (!item.email) {
item.email = '';
}
var box = utils.misc.getPreferredPictureBox();
item.picwidth = box.width;
item.picheight = box.height;
// Only do this if we need to prepare the search accents phase
if (numQueries === 2) {
// Saving the original order
sortedByName.push(item);
// Populate the arrays for doing the accents related search
Object.keys(SEARCH_ACCENTS_FIELDS).forEach(function(field) {
searchAccentsArrays[field] = searchAccentsArrays[field] || [];
if (item[field]) {
// The different words for each item
var words = item[field].split(/[ ]+/);
// The whole word itself is added
words.push(item[field]);
words.forEach(function(word) {
var obj = {
originalIndex: index
};
obj[field] = Normalizer.toAscii(word).toLowerCase();
searchAccentsArrays[field].push(obj);
});
}
});
index++;
}
});
if (numQueries === 2) {
var accentsProposal = searchAccents(searchAccentsArrays, cdata);
if (accentsProposal.length === 0) {
data = sortedByName;
mainSection.classList.add('no-proposal');
numFriendsProposed = 0;
}
else {
currentRecommendation = [];
accentsProposal.forEach(function(proposalIndex) {
currentRecommendation.push(sortedByName[proposalIndex]);
});
numFriendsProposed = currentRecommendation.length;
}
} else {
viewButton.setAttribute('data-l10n-id', 'viewAll');
viewButton.onclick = UI.viewAllFriends;
}
utils.templates.append('#friends-list', currentRecommendation);
imgLoader.reload();
if (typeof completedCb === 'function') {
completedCb();
}
Curtain.hide(function onCurtainHide() {
sendReadyEvent();
window.addEventListener('message', function linkOnViewPort(e) {
if (e.origin !== fb.CONTACTS_APP_ORIGIN) {
return;
}
var data = e.data;
if (data && data.type === 'dom_transition_end') {
window.removeEventListener('message', linkOnViewPort);
utils.status.show({
id: 'linkProposal',
args: {
numFriends: numFriendsProposed
}
});
}
});
});
}
};
// This function needs to be called because link proposals through FB Query
// do not work properly with words with special characters (bug 796714)
function searchAccents(searchArrays, contactData) {
var out = [];
var searchFields = Object.keys(SEARCH_ACCENTS_FIELDS);
function compareItems(target, item) {
var out;
if (typeof target === 'string') {
out = target.localeCompare(item);
if (out !== 0) {
if (item.indexOf(target) === 0) {
out = 0;
}
}
}
return out;
} // compareItems Function
searchFields.forEach(function(searchField) {
// The array is ordered according to the search field
// this enables an efficient binary search
var searchArray = searchArrays[searchField].sort(function(a, b) {
return a[searchField].localeCompare(b[searchField]);
});
var fieldToSearch = contactData[SEARCH_ACCENTS_FIELDS[searchField]];
if (fieldToSearch && fieldToSearch[0]) {
// Splitting in the different words
var dataToSearch = fieldToSearch[0].trim().split(/[ ]+/);
dataToSearch.forEach(function(aData) {
var targetString = Normalizer.toAscii(aData).toLowerCase();
var searchResult = utils.binarySearch(targetString, searchArray, {
arrayField: searchField,
compareFunction: compareItems
});
searchResult.forEach(function(aResult) {
var candidate = searchArray[aResult].originalIndex;
// Avoiding to show two times the same result element
if (out.indexOf(candidate) === -1) {
out.push(candidate);
}
});
});
}
});
return out;
}
function sendReadyEvent() {
parent.postMessage({
type: 'ready', data: ''
}, fb.CONTACTS_APP_ORIGIN);
}
function setCurtainHandlersErrorProposal() {
Curtain.oncancel = function() {
cancelCb(true);
};
Curtain.onretry = function getRemoteProposal() {
Curtain.oncancel = function() {
cancelCb(true);
};
Curtain.show('wait', state);
link.getRemoteProposal(access_token, contactid);
};
}
link.baseHandler = function(type) {
if (state === 'friends') {
Curtain.onretry = getRemoteAll;
Curtain.oncancel = Curtain.hide;
} else if (state === 'proposal') {
setCurtainHandlersErrorProposal();
}
else if (state === 'linking') {
Curtain.onretry = function retry_link() {
Curtain.show('message', 'linking');
UI.selected({
target: {
dataset: {
uuid: friendUidToLink
}
}
});
};
Curtain.oncancel = Curtain.hide;
}
Curtain.show(type, state);
};
link.timeoutHandler = function() {
link.baseHandler('timeout');
};
link.errorHandler = function() {
link.baseHandler('error');
};
/**
* Clears the list of contacts
*
*/
function clearList() {
if (!friendsList) {
friendsList = document.querySelector('#friends-list');
}
var template = friendsList.querySelector('[data-template]');
utils.dom.removeChildNodes(friendsList);
friendsList.appendChild(template);
}
link.friendsReady = function(response) {
if (typeof response.error === 'undefined' && response.data) {
viewButton.setAttribute('data-l10n-id', 'viewRecommend');
viewButton.onclick = UI.viewRecommended;
allFriends = response;
fb.utils.setCachedNumFriends(response.data.length);
response.data.forEach(function(item) {
if (!item.email) {
item.email = '';
}
});
clearList();
var fragment = document.createDocumentFragment();
utils.templates.append(friendsList, response.data, fragment);
friendsList.appendChild(fragment);
imgLoader.reload();
Curtain.hide();
}
else {
window.console.error('FB: Error while retrieving friends',
response.error.code, response.error.message);
if (response.error.code !== 190) {
Curtain.onretry = getRemoteAll;
Curtain.oncancel = Curtain.hide;
}
else {
// Error with the access token. It is very unlikely that this happens
Curtain.onretry = handleTokenError;
}
Curtain.show('error', 'friends');
}
};
function setCurtainHandlers() {
Curtain.oncancel = function curtain_cancel() {
cancelCb(true);
};
}
link.start = function(contactId, acc_tk, endCb) {
// Only needed for testing purposes
completedCb = endCb;
access_token = acc_tk;
contactid = contactId;
setCurtainHandlers();
clearList();
imgLoader = new ImageLoader('#mainContent',
'li:not([data-uuid="#uid#"])');
if (!acc_tk) {
oauth2.getAccessToken(function proposal_new_token(new_acc_tk) {
access_token = new_acc_tk;
link.getProposal(contactId, new_acc_tk);
}, 'proposal', 'facebook');
}
else {
link.getProposal(contactId, acc_tk);
}
};
link.init = function() {
mainSection = document.querySelector('#main');
viewButton = document.querySelector('#view-all');
};
function retryOnErrorCb() {
UI.selected({
target: {
dataset: {
uuid: friendUidToLink
}
}
});
}
function handleTokenError() {
Curtain.hide(notifyParent.bind(null, {
type: 'token_error'
}));
var cb = function() {
allFriends = null;
link.start(contactid);
};
ImportStatusData.remove(fb.utils.TOKEN_DATA_KEY).then(cb);
}
UI.selected = function(event) {
Curtain.show('message', 'linking');
var element = event.target.parentNode;
friendUidToLink = element.dataset.uuid;
// First it is needed to check whether is an already imported friend
var req = fb.contacts.get(friendUidToLink);
req.onsuccess = function() {
if (req.result) {
window.setTimeout(function delay() {
Curtain.hide(notifyParent.bind(null, {
type: 'item_selected',
data: {
uid: friendUidToLink
}
}));
}, 1000);
}
else {
state = 'linking';
var callbacks = { };
callbacks.success = function(data) {
Curtain.hide(notifyParent.bind(null, {
type: 'item_selected',
data: data
}));
};
callbacks.error = function(e) {
var error = e.target.error;
window.console.error('FB: Error while importing friend data ',
JSON.stringify(error));
Curtain.oncancel = Curtain.hide;
if (error.code != 190) {
Curtain.onretry = retryOnErrorCb;
}
else {
Curtain.onretry = handleTokenError;
}
Curtain.show('error', 'linking');
};
callbacks.timeout = function() {
link.baseHandler('timeout');
};
imgLoader.unload(); // Removing listeners
FacebookConnector.importContact(friendUidToLink, access_token,
callbacks, 'not_match');
}
};
req.onerror = function() {
window.console.error('FB: Error while importing friend data');
Curtain.oncancel = Curtain.hide;
Curtain.onretry = retryOnErrorCb;
Curtain.show('error', 'linking');
};
};
UI.end = function(event) {
var msg = {
type: 'window_close',
data: ''
};
parent.postMessage(msg, fb.CONTACTS_APP_ORIGIN);
};
UI.viewAllFriends = function(event) {
if (!allFriends) {
getRemoteAll();
}
else {
link.friendsReady(allFriends);
}
return false;
};
UI.viewRecommended = function(event) {
// event.target === viewButton
event.target.onclick = UI.viewAllFriends;
event.target.setAttribute('data-l10n-id', 'viewAll');
clearList();
utils.templates.append(friendsList, currentRecommendation);
imgLoader.reload();
return false;
};
})(document);
}

View file

@ -0,0 +1,43 @@
'use strict';
/* global fb, utils */
(function(document) {
var cid = window.location.search.substring(fb.link.CID_PARAM.length + 2);
// Getting the timeout config from the parent
if (parent.fb) {
fb.operationsTimeout = parent.fb.operationsTimeout;
}
utils.listeners.add({
'#link-header': [
{
event: 'action',
handler: fb.link.ui.end
}
],
'#friends-list': fb.link.ui.selected
});
fb.link.init();
// This event listener is added manually as it wil be changing dynamically
document.querySelector('#view-all').onclick = fb.link.ui.viewAllFriends;
// Module fb.contacts is initialized just in case we need it
fb.contacts.init(function fb_init() {
window.addEventListener('message', function getAccessToken(e) {
if (e.origin !== fb.CONTACTS_APP_ORIGIN) {
return;
}
window.removeEventListener('message', getAccessToken);
fb.link.start(cid, e.data.data);
});
parent.postMessage({
type: 'messaging_ready',
data: ''
}, fb.CONTACTS_APP_ORIGIN);
});
})(document);

View file

@ -0,0 +1,110 @@
'use strict';
/* global LazyLoader */
/* global oauth2 */
/* global oauthflow */
var fb = window.fb || {};
if (typeof fb.msg === 'undefined') {
LazyLoader.load('/shared/pages/import/js/parameters.js', () => {
(function(document) {
var Msg = fb.msg = {};
var to;
var message;
var params = oauthflow.params.facebook;
var appId = params.applicationId;
var redirectURI = params.redirectMsg;
Msg.CID_PARAM = 'contactid';
// Only needed if we decide to craft our own UI
Msg.wallPost = function(uid, msg) {
to = uid || to;
message = msg || message;
oauth2.getAccessToken(doWallPost, 'wallPost', 'facebook');
};
Msg.sendPrivate = function(uid, msg) {
// TODO: To be implemented (if we decide to craft our custom UI)
};
// This code is for posting to user's wall. Only will be necessary if
// we decide to craft our own UI for posting to the wall
function doWallPost(token) {
var msgWallService = 'https://graph.facebook.com/#/feed?method=POST';
msgWallService = msgWallService.replace(/#/, to);
var params = [
'access_token' + '=' + token,
'message' + '=' + message,
'callback' + '=' + 'fb.msg.ui.wallPosted'
];
var q = params.join('&');
var jsonp = document.createElement('script');
jsonp.src = msgWallService + '&' + q;
document.body.appendChild(jsonp);
}
var UI = Msg.ui = {};
function getFbContactUid(contactId, callback) {
var req = fb.utils.getContactData(contactId);
req.onsuccess = function() {
var fbContact = new fb.Contact(req.result);
callback(fbContact.uid);
};
req.onerror = function() {
window.console.error('Contacts: Contact not found!');
callback(null);
};
}
function openMsgDialog(dialogURI, uid) {
var params = [
'app_id' + '=' + appId,
'to' + '=' + uid,
'redirect_uri' + '=' + encodeURIComponent(redirectURI)
];
var target = dialogURI + params.join('&');
window.open(fb.utils.getNonCacheableUrl(target), '', 'dialog');
}
// Use the FB Dialogs functionality for posting to the wall
UI.wallPost = function(contactId) {
getFbContactUid(contactId, function ui_wallPost(uid) {
if (uid) {
var dialogURI = 'https://m.facebook.com/dialog/feed?';
openMsgDialog(dialogURI, uid);
}
});
};
// Use a Web view from Facebook for sending private messages
// TODO: Check with Facebook why the send message dialog seems not to be
// working on mobile
UI.sendPrivateMsg = function(contactId) {
getFbContactUid(contactId, function ui_sendMsg(uid) {
if (uid) {
var url = 'https://m.facebook.com/compose_message/?uid=' + uid;
window.open(fb.utils.getNonCacheableUrl(url),
'', 'dialog');
}
});
};
UI.wallPosted = function(result) {
UI.end();
window.console.log(JSON.stringify(result));
};
})(document);
});
}