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
|
|
@ -0,0 +1 @@
|
|||
;'use strict';window.addEventListener('DOMContentLoaded',function(){LazyLoader.load(['/shared/js/l10n.js']).then(function(){});});window.onload=function(){var dependencies=['/shared/js/contact_photo_helper.js','/shared/js/sanitizer.js','/shared/js/contacts/import/utilities/misc.js','/contacts/views/matching/js/matching_ui.js'];window.screen.mozLockOrientation('portrait-primary');LazyLoader.load(dependencies).then(function(){MatchingUI.init();});};
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
;'use strict';(function(exports){var CONTACTS_APP_ORIGIN=location.origin;var contact=null,matchings={};var type='matching';var parentWindow=null;function start(cid){if(!cid){type='listing';window.addEventListener('message',duplicateContactsHandler);parentWindow.postMessage({type:'duplicate_contacts_loaded'},CONTACTS_APP_ORIGIN);return;}
|
||||
Curtain.show('wait','duplicateContacts');var callbacks={onmatch:showUI,onmismatch:function(){Curtain.show('alert','noDuplicateContacts');Curtain.onok=abort;}};Curtain.oncancel=function oncancel(){callbacks.onmatch=callbacks.onmismatch=null;abort();};var matcherDependencies=['/shared/js/text_normalizer.js','/shared/js/simple_phone_matcher.js','/shared/js/contacts/contacts_matcher.js'];LazyLoader.load(matcherDependencies,function loaded(){setTimeout(function(){parentWindow.ContactsService.get(cid,function success(mContact){contact=mContact;Matcher.match(contact,'active',callbacks);},abort);},1000);});}
|
||||
function duplicateContactsHandler(e){if(e.origin!==CONTACTS_APP_ORIGIN){return;}
|
||||
var data=e.data;if(data&&data.type==='show_duplicate_contacts'){window.removeEventListener('message',duplicateContactsHandler);contact={name:[data.data.name]};var duplicateContacts=data.data.duplicateContacts;var total=Object.keys(duplicateContacts).length;var matchingReady=function(){if(--total===0){window.addEventListener('message',contactsMergedHandler);showUI(duplicateContacts);}};Object.keys(duplicateContacts).forEach(function(cid){parentWindow.ContactsService.get(cid,function success(contact){duplicateContacts[cid]={matchingContact:contact,matchings:duplicateContacts[cid].matchings};matchingReady();},matchingReady);});}else if(data&&data.type==='sync'){parentWindow=e.source;navigator.mozL10n.once(function localized(evt){start(window.location.search.substring('contactId'.length+2));});}}
|
||||
function contactsMergedHandler(e){if(e.origin!==CONTACTS_APP_ORIGIN){return;}
|
||||
if(e.data&&e.data.type==='duplicate_contacts_merged'){window.removeEventListener('message',contactsMergedHandler);Curtain.hide(function(){parentWindow.postMessage({type:'window_close',data:''},CONTACTS_APP_ORIGIN);});}}
|
||||
function showUI(results){matchings=results;window.dispatchEvent(new CustomEvent('initUI',{'detail':{'type':type,'contact':contact,'results':results}}));window.addEventListener('UIReady',function fn(){window.removeEventListener('UIReady',fn);if(type==='matching'){Curtain.hide(sendReadyEvent,CONTACTS_APP_ORIGIN);}else{sendReadyEvent();}});}
|
||||
function sendReadyEvent(){parentWindow.postMessage({type:'ready',data:''},CONTACTS_APP_ORIGIN);}
|
||||
function abort(){var notifyParent=function cmc_notifyParent(){parentWindow.postMessage({type:'abort',data:''},CONTACTS_APP_ORIGIN);};Curtain.hide(notifyParent);}
|
||||
function merge(checkedContacts){Curtain.show('wait','mergingDuplicateContacts');Curtain.hideMenu();if(type==='listing'){parentWindow.postMessage({type:'merge_duplicate_contacts',data:checkedContacts},CONTACTS_APP_ORIGIN);return;}
|
||||
var MERGE_DEPS=['/contacts/js/utilities/merge_helper.js'];LazyLoader.load(MERGE_DEPS,function loaded(){var cb=function cb(){Curtain.hide(function(){parentWindow.postMessage({type:'window_close',data:''},CONTACTS_APP_ORIGIN);});};var list=[];Object.keys(checkedContacts).forEach(function(id){list.push(matchings[id]);});MergeHelper.merge(contact,list).then(cb,cb);});}
|
||||
window.addEventListener('message',duplicateContactsHandler);window.addEventListener('merge',evt=>{merge(evt.detail.checkedContacts);});})(window);
|
||||
|
|
@ -0,0 +1,304 @@
|
|||
'use strict';
|
||||
/* global ContactPhotoHelper */
|
||||
/* global ImageLoader */
|
||||
/* global LazyLoader */
|
||||
/* global Sanitizer */
|
||||
/* jshint nonew: false */
|
||||
|
||||
/**
|
||||
* This object handles every interaction between the user and the UI of the
|
||||
* matching contacts view
|
||||
*/
|
||||
(function(exports) {
|
||||
|
||||
var CONTACTS_APP_ORIGIN = location.origin;
|
||||
|
||||
var listDependencies = [
|
||||
'/shared/js/contacts/utilities/image_loader.js'
|
||||
];
|
||||
|
||||
var MatchingUI = {
|
||||
_: null,
|
||||
|
||||
// Counter for checked list items
|
||||
checked: 0,
|
||||
|
||||
// Hash contains identifiers of checked contacts
|
||||
checkedContacts: {},
|
||||
|
||||
// Field order when showing matching details main reason
|
||||
fieldOrder: ['tel', 'email', 'name'],
|
||||
|
||||
init: function() {
|
||||
this._ = navigator.mozL10n.get;
|
||||
this.mergeButton = document.getElementById('merge-action');
|
||||
if (!this.mergeButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.duplicateMessage = document.querySelector('#duplicate-msg > p');
|
||||
this.contactsList =
|
||||
document.querySelector('#contacts-list-container > ol');
|
||||
|
||||
this.contactsList.addEventListener('click', this.onClick.bind(this)
|
||||
, true);
|
||||
this.mergeButton.addEventListener('click', this.onMerge.bind(this));
|
||||
|
||||
window.addEventListener('initUI', evt => {
|
||||
this.load(evt.detail.type, evt.detail.contact, evt.detail.results);
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
* Loads the UI that implements the merge of duplicate contacts
|
||||
*
|
||||
* @param{Object} Master contact
|
||||
*
|
||||
* @param{Object} Hash of matching contacts from Matcher module
|
||||
*
|
||||
*/
|
||||
load: function(type, contact, results) {
|
||||
this.matchingResults = results;
|
||||
|
||||
document.body.dataset.mode = type;
|
||||
var contactName = this.getDisplayName(contact);
|
||||
var params = { name: Sanitizer.escapeHTML `${contactName}` };
|
||||
|
||||
if (type === 'matching') {
|
||||
// "Suggested duplicate contacts for xxx"
|
||||
navigator.mozL10n.setAttributes(this.duplicateMessage,
|
||||
'suggestedDuplicatesMessage',
|
||||
params);
|
||||
} else {
|
||||
navigator.mozL10n.setAttributes(document.querySelector('title'),
|
||||
'duplicatesFoundTitle');
|
||||
// "xxx duplicates information in the following contacts"
|
||||
navigator.mozL10n.setAttributes(this.duplicateMessage,
|
||||
'foundDuplicatesMessage',
|
||||
params);
|
||||
}
|
||||
|
||||
// Rendering the duplicate contacts list
|
||||
this.renderList(results);
|
||||
},
|
||||
|
||||
templateDuplicateContact: function(contact) {
|
||||
return Sanitizer.createSafeHTML `<li data-uuid="${contact.id}"
|
||||
class="block-item">
|
||||
<label class="pack-checkbox">
|
||||
<input type="checkbox" checked>
|
||||
<span></span>
|
||||
</label>
|
||||
<aside class="pack-end">
|
||||
<img data-src="${contact.thumb}">
|
||||
</aside>
|
||||
<p>
|
||||
<bdi class="ellipsis-dir-fix">${contact.displayName}</bdi>
|
||||
</p>
|
||||
<p class="match-main-reason">
|
||||
<bdi class="ellipsis-dir-fix">${contact.mainReason}</bdi>
|
||||
</p>
|
||||
</li>`;
|
||||
},
|
||||
|
||||
renderList: function(contacts) {
|
||||
LazyLoader.load(listDependencies, () => {
|
||||
// For each contact in the list
|
||||
var contactsKeys = Object.keys(contacts);
|
||||
contactsKeys.forEach(id => {
|
||||
// New contact appended
|
||||
this.checkedContacts[id] = id;
|
||||
var contact = this.cookContact(contacts[id]);
|
||||
|
||||
// Use a template node so we can append it and also access the node
|
||||
var item = document.createElement('template');
|
||||
item.innerHTML =
|
||||
Sanitizer.unwrapSafeHTML(this.templateDuplicateContact(contact));
|
||||
|
||||
this.contactsList.appendChild(item.content);
|
||||
|
||||
if (contact.email1 === '') {
|
||||
var emailField = item.content.querySelector('p:last-child');
|
||||
emailField && emailField.parentNode.removeChild(emailField);
|
||||
}
|
||||
});
|
||||
|
||||
this.checked = contactsKeys.length;
|
||||
this.checkMergeButton();
|
||||
|
||||
new ImageLoader('#main', 'li');
|
||||
setTimeout(() => {
|
||||
window.dispatchEvent(new CustomEvent('UIReady'));
|
||||
// Remove the hidden ui
|
||||
document.body.classList.remove('ui-not-ready');
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/*
|
||||
* It creates a new copy becase it is forbidden to add new attributes to
|
||||
* original contacts provided by the API
|
||||
*
|
||||
* @param(Object) Contact object provided by Gecko
|
||||
*/
|
||||
cookContact: function(matching) {
|
||||
var contact = matching.matchingContact;
|
||||
var reasons = matching.matchings || {};
|
||||
|
||||
var out = {};
|
||||
|
||||
this.populate(contact, out, Object.getOwnPropertyNames(contact));
|
||||
this.populate(contact, out,
|
||||
Object.getOwnPropertyNames(Object.getPrototypeOf(contact)));
|
||||
|
||||
out.displayName = this.getDisplayName(contact);
|
||||
out.mainReason = this.selectMainReason(reasons);
|
||||
var photo = ContactPhotoHelper.getThumbnail(out);
|
||||
if (photo) {
|
||||
out.thumb = window.URL.createObjectURL(photo);
|
||||
}
|
||||
|
||||
return out;
|
||||
},
|
||||
|
||||
getCompleteName: function(contact) {
|
||||
var givenName = Array.isArray(contact.givenName) ?
|
||||
contact.givenName[0] : '';
|
||||
|
||||
var familyName = Array.isArray(contact.familyName) ?
|
||||
contact.familyName[0] : '';
|
||||
|
||||
var completeName = givenName && familyName ?
|
||||
givenName + ' ' + familyName :
|
||||
givenName || familyName;
|
||||
|
||||
return completeName;
|
||||
},
|
||||
|
||||
// Fills the contact data to display if no givenName and familyName
|
||||
getDisplayName: function(contact) {
|
||||
if (this.hasName(contact)) {
|
||||
return this.getCompleteName(contact);
|
||||
}
|
||||
|
||||
var name = [];
|
||||
if (Array.isArray(contact.name) && contact.name[0] &&
|
||||
contact.name[0].trim()) {
|
||||
name.push(contact.name[0]);
|
||||
} else if (contact.org && contact.org[0] && contact.org[0].trim()) {
|
||||
name.push(contact.org[0]);
|
||||
} else if (contact.tel && contact.tel[0]) {
|
||||
name.push(contact.tel[0].value);
|
||||
} else if (contact.email && contact.email[0]) {
|
||||
name.push(contact.email[0].value);
|
||||
} else {
|
||||
name.push(this._('noName'));
|
||||
}
|
||||
|
||||
return name[0];
|
||||
},
|
||||
|
||||
hasName: function(contact) {
|
||||
return (Array.isArray(contact.givenName) && contact.givenName[0] &&
|
||||
contact.givenName[0].trim()) ||
|
||||
(Array.isArray(contact.familyName) && contact.familyName[0] &&
|
||||
contact.familyName[0].trim());
|
||||
},
|
||||
|
||||
selectMainReason: function(matchings) {
|
||||
var out = '';
|
||||
|
||||
for (var j = 0; j < this.fieldOrder.length; j++) {
|
||||
var aField = this.fieldOrder[j];
|
||||
var theMatchings = matchings[aField];
|
||||
if (Array.isArray(theMatchings) && theMatchings[0]) {
|
||||
out = theMatchings[0].matchedValue;
|
||||
if (out) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return out;
|
||||
},
|
||||
|
||||
populate: function(source, target, propertyNames) {
|
||||
propertyNames.forEach(function(property) {
|
||||
var propertyValue = source[property];
|
||||
if (propertyValue) {
|
||||
target[property] = propertyValue;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onClose: function(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
parent.postMessage({
|
||||
type: 'window_close',
|
||||
data: ''
|
||||
}, CONTACTS_APP_ORIGIN);
|
||||
},
|
||||
|
||||
onClick: function(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
if (e.target.tagName === 'INPUT') {
|
||||
return false;
|
||||
}
|
||||
|
||||
var li = e.target.closest('li');
|
||||
var uuid = li.dataset.uuid;
|
||||
var el = li.querySelector('input');
|
||||
|
||||
if (el.checked) {
|
||||
--this.checked;
|
||||
delete this.checkedContacts[uuid];
|
||||
} else {
|
||||
++this.checked;
|
||||
this.checkedContacts[uuid] = uuid;
|
||||
}
|
||||
|
||||
// Change manually the status as we prevented this
|
||||
el.checked = !el.checked;
|
||||
|
||||
this.checkMergeButton();
|
||||
},
|
||||
|
||||
isMatch: function(matchings, aField, fieldValue) {
|
||||
var noMatch = true;
|
||||
if (matchings[aField]) {
|
||||
// Using every to short circuit the checking when a match is found.
|
||||
noMatch = matchings[aField].every(function(obj) {
|
||||
var val = fieldValue.value || fieldValue;
|
||||
if (obj.matchedValue === val) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
return !noMatch;
|
||||
},
|
||||
|
||||
checkMergeButton: function() {
|
||||
navigator.mozL10n.setAttributes(this.mergeButton,
|
||||
'mergeActionButtonLabel',
|
||||
{ n: this.checked });
|
||||
this.mergeButton.disabled = (this.checked === 0);
|
||||
},
|
||||
|
||||
onMerge: function(e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
window.dispatchEvent(new CustomEvent('merge', {
|
||||
'detail': {
|
||||
'checkedContacts': this.checkedContacts
|
||||
}
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
exports.MatchingUI = MatchingUI;
|
||||
})(window);
|
||||
Loading…
Reference in a new issue