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,4 @@
;'use strict';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,51 @@
;'use strict';var fb=window.fb||{};if(!fb.link){(function(document){var link=fb.link={};var UI=link.ui={};link.CID_PARAM='contactid';var contactid;var cdata;var access_token;var numQueries=0;var friendUidToLink;var SEARCH_QUERY=['SELECT uid, name, email from user ',' WHERE uid IN (SELECT uid1 FROM friend WHERE uid2=me()) ',' AND (',null,')',' ORDER BY name'];var MAIL_COND,CELL_COND;MAIL_COND=['strpos(email, ',"'",null,"'",') >= 0'];CELL_COND=['strpos(cell, ',"'",null,"'",') >= 0'];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;var currentNetworkRequest=null;var state;var imgLoader;var completedCb;function notifyParent(message){parent.postMessage({type:message.type||'',data:message.data||''},fb.CONTACTS_APP_ORIGIN);}
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 ');});}
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;}
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);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);}
function proposalReadyMultiple(done,error,response){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});}
function doGetRemoteProposal(acc_tk,contactData,query,isAll){var theQuery=query;var callback=proposalReadyMultiple.bind(null,fb.link.proposalReady,fb.link.errorHandler);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);}
function cancelCb(shouldNotifyParent){if(currentNetworkRequest){currentNetworkRequest.cancel();currentNetworkRequest=null;}
Curtain.hide(shouldNotifyParent?notifyParent.bind(null,{type:'abort'}):null);}
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);}
link.getProposal=function(cid,acc_tk){link.getRemoteProposal(acc_tk,cid);};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{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;if(numQueries===2){sortedByName.push(item);Object.keys(SEARCH_ACCENTS_FIELDS).forEach(function(field){searchAccentsArrays[field]=searchAccentsArrays[field]||[];if(item[field]){var words=item[field].split(/[ ]+/);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}});}});});}};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;}
searchFields.forEach(function(searchField){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]){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;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');};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{Curtain.onretry=handleTokenError;}
Curtain.show('error','friends');}};function setCurtainHandlers(){Curtain.oncancel=function curtain_cancel(){cancelCb(true);};}
link.start=function(contactId,acc_tk,endCb){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;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();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.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,3 @@
;'use strict';(function(document){var cid=window.location.search.substring(fb.link.CID_PARAM.length+2);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();document.querySelector('#view-all').onclick=fb.link.ui.viewAllFriends;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);
});
}