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,123 @@
'use strict';
//
// Utility API for manipulating config stored in local cookie.
//
(function() {
var utils = window.utils = window.utils || {};
utils.cookie = {};
// Bump this number if the format of the cookie changes
var COOKIE_VERSION = 6;
// Default values for items stored in the configuration
var COOKIE_DEFAULTS = {
order: false,
viewHeight: -1,
rowsPerPage: -1,
fbMigrated: false,
fbScheduleDone: false,
defaultImage: true,
accessTokenMigrated: false,
fbCleaningInProgress: 0
};
// Only allow these properties to be stored in the config
var COOKIE_PROPS = Object.keys(COOKIE_DEFAULTS);
// Set as a constant in the far future to avoid calling new Date
var EXPIRATION_DATE = 'Fri, 31 Dec 9999 23:59:59 GMT';
var COOKIE_NAME = 'preferences';
var COOKIE_NAME_EQ = COOKIE_NAME + '=';
var COOKIE_NAME_LENGHT = COOKIE_NAME_EQ.length;
// Parses the cookie returning the corresponding object
// Returns null if the cookie string does not conform with expected format
function _parseCookie(cookie) {
// Expecting "preferences=<JSON value>"
var index = cookie.indexOf(COOKIE_NAME_EQ);
if (index === -1) {
return null;
}
var cookieVal = cookie.substring(index + COOKIE_NAME_LENGHT);
if (!cookieVal) {
return null;
}
// Setup default value for cookie with version that will trigger update
var result = {
version: -1
};
try {
result = JSON.parse(decodeURIComponent(cookieVal));
} catch (error) {
console.warn('Could not parse current cookie, rebuilding it');
}
return result;
}
// Load and return the cookie config if present. Returns null if the
// cookie is missing, has an incorrect format or versions mismatch.
utils.cookie.load = function() {
if (!document.cookie) {
return null;
}
var cookie = _parseCookie(document.cookie);
// If the cookie is out-of-date, then we need to update it
// and re-parse since the format might have changed.
if (cookie && cookie.version !== COOKIE_VERSION) {
_updateCookie(cookie);
cookie = _parseCookie(document.cookie);
}
return cookie;
};
// Update the current cookie config with the given set of configuration
// properties. The passed in configuration properties can be a subset of
// the values stored in the cookie. Unspecified values will be unchanged.
utils.cookie.update = function(cfg) {
_updateCookie(utils.cookie.load(), cfg);
};
// Internal helper function that writes out a new cookie based on a set
// of existing value, a set of new values, and defaults.
function _updateCookie(oldCookie, cfg) {
oldCookie = oldCookie || {};
cfg = cfg || {};
var newCookie = {version: COOKIE_VERSION};
// For each allowed property in the config, pick the right value given
// our input. This intentionally removes old properties in the cookie
// that may no longer be in our allowed COOKIE_PROPS.
for (var i = 0, n = COOKIE_PROPS.length; i < n; ++i) {
var prop = COOKIE_PROPS[i];
// Prefer configuration overrides provided
if (prop in cfg) {
newCookie[prop] = cfg[prop];
// Otherwise use previously set values
} else if (prop in oldCookie) {
newCookie[prop] = oldCookie[prop];
// Finally, fall back to defaults
} else {
newCookie[prop] = COOKIE_DEFAULTS[prop];
}
}
document.cookie = COOKIE_NAME + '=' +
encodeURIComponent(JSON.stringify(newCookie)) +
';expires=' + EXPIRATION_DATE;
}
utils.cookie.getDefault = function(prop) {
return COOKIE_DEFAULTS[prop];
};
utils.cookie.COOKIE_DEFAULTS = COOKIE_DEFAULTS;
utils.cookie.COOKIE_VERSION = COOKIE_VERSION;
})();

View file

@ -0,0 +1,18 @@
'use strict';
var utils = window.utils || {};
if (!utils.extractParams) {
utils.extractParams = function extractParams(url) {
if (!url) {
return -1;
}
var ret = {};
var params = url.split('&');
for (var i = 0; i < params.length; i++) {
var currentParam = params[i].split('=');
ret[currentParam[0]] = currentParam[1];
}
return ret;
};
}

View file

@ -0,0 +1,95 @@
/* exported IccHandler */
//
// IccManager Handler, abstract the new IccManager Multi SIM
// API.
// Generates code and handles listener for multiple SIMs
//
'use strict';
var IccHandler = function IccHandler() {
var iccManager = null;
var isSingleSIMApi = false;
var iccs = {};
var sims = [];
var mobileConnections = null;
var init = function init(domGenerator, cb) {
iccManager = navigator.mozIccManager;
// Avoid any icc operation in devices that don't
// have icc (desktop, tablets, etc.)
if (!iccManager) {
if (typeof cb === 'function') {
cb();
}
return;
}
mobileConnections = navigator.mozMobileConnections || [];
isSingleSIMApi = typeof iccManager.getIccById !== 'function';
if (isSingleSIMApi) {
iccs['default'] = iccManager;
} else {
iccManager.iccIds.forEach(function(iccId) {
iccs[iccId] = iccManager.getIccById(iccId);
});
for (var i = 0; i < mobileConnections.length; i++) {
sims[i] = iccs[mobileConnections[i].iccId] || null;
}
}
domGenerator.setIccList(sims);
domGenerator.generateDOM();
subscribeToChanges(cb);
};
// Make all the SIMS to listen to the same change observer
var subscribeToChanges = function subscribeToChanges(cb) {
if (!navigator.mozIccManager) {
return;
}
Object.keys(iccs).forEach(function onIccId(iccId) {
var icc = iccs[iccId];
if (icc !== null) {
icc.oncardstatechange = cb;
}
});
};
// Get the status of all the SIMs, not just the status, also
// the proper icc object
var getStatus = function getStatus() {
var status = [];
Object.keys(iccs).forEach(function onIccId(iccId) {
var icc = iccs[iccId];
status.push({
'iccId': iccId,
'icc': icc,
'cardState': icc.cardState
});
});
return status;
};
// Returns the specific icc depending on id.
// If id is null will try to get the one named 'default'
var getIccById = function getIccById(id) {
if (!id) {
id = 'default';
}
return iccs[id];
};
return {
'getStatus': getStatus,
'getIccById': getIccById,
'init': init
};
}();

View file

@ -0,0 +1,272 @@
'use strict';
/* global Cache */
/* global ICEStore */
/**
* Module to handle ICE data across different parts of the application
* This module just know about the internal representation and how
* to deal with share ICE information (which has different format).
*/
(function ICEData(exports) {
if (exports.ICEData) {
return;
}
var localIceContacts = [];
var ICE_CONTACTS_KEY = 'ice-contacts';
var onChangeCallbacks = [];
var onChangeAttached = false;
var pendingChanges = [];
var performingChange = false;
/**
* Loads the local formated data into an internal
* representation.
* @return (Promise) fullfill when the load finish.
*/
function load() {
var iceContactsIds = [
{
id: undefined,
active: false
},
{
id: undefined,
active: false
}
];
return new Promise(function(resolve) {
window.asyncStorage.getItem(ICE_CONTACTS_KEY, function(data) {
if (data) {
if (data[0]) {
iceContactsIds[0] = data[0];
}
if (data[1]) {
iceContactsIds[1] = data[1];
}
}
localIceContacts = iceContactsIds;
resolve(localIceContacts);
});
});
}
/**
* Set the value of an ICE contact, both in local and in the
* datastore
* @param id (string) contact id
* @param pos (int) current position (0,1)
* @param active (boolean) ice contact is active or not
*/
function setICEContact(id, pos, active) {
active = active || false;
Cache.evict();
// Save locally
localIceContacts[pos] = {
id: id,
active: active
};
return setIceContactsItem(localIceContacts).then(() => {
// Save in the datastore
return modifyICEInDS();
});
}
/**
* Set the values for ICE contacts, both in local and in the
* datastore
* @param iceContactList the list with ids of ICE Contacts to be set
*/
function setICEContacts(iceContactList) {
if (!Array.isArray(iceContactList)) {
return Promise.resolve();
}
// Save locally
localIceContacts = [
{
id: iceContactList[0],
active: !!iceContactList[0]
},
{
id: iceContactList[1],
active: !!iceContactList
}
];
return setIceContactsItem(localIceContacts).then(() => {
// Save in the datastore
return modifyICEInDS();
});
}
/**
* Clone the current local ice contacts into the datastore,
* considering the active flag.
* @return (Promise) fullfill when data saved in DS
*/
function modifyICEInDS() {
var contacts = [];
localIceContacts.forEach(function(iceContact) {
if (iceContact.id && iceContact.active) {
contacts.push(iceContact.id);
}
});
return ICEStore.setContacts(contacts);
}
/**
* Given a specific contact id removes it from the
* ice list, if present or disabled
* @param id (String) contact id
* @return (Promise) fullfill when data modified in DS
*/
function removeICEContact(id) {
var contact = localIceContacts.find(function(x) {
return x.id === id;
});
if (!contact) {
return Promise.resolve();
}
return new Promise(function(resolve, reject) {
var index = localIceContacts.indexOf(contact);
localIceContacts[index].id = null;
localIceContacts[index].active = false;
setIceContactsItem(localIceContacts).then(function() {
return modifyICEInDS().then(() => {
notifyCallbacks(localIceContacts);
});
}).then(resolve, reject);
});
}
function setIceContactsItem(iceContactsList) {
return new Promise(function(resolve, reject) {
window.asyncStorage.setItem(ICE_CONTACTS_KEY, iceContactsList,
resolve, reject);
});
}
/**
* Adds an observer to be called when an ice contact
* changed. We can add multiple listeners, since we will
* need to react in several parts of the app to this changes.
* Also the listener removes a contact id in case we are performing
* a delete. In case of modification we don't do anythying as we
* store just ids.
* Listener will be attached just once, but we can keep adding
* callbacks.
* @param fn (Function) callback to call when ice contact change
*/
function listenForChanges(fn) {
return new Promise(function(resolve) {
if (!onChangeAttached) {
load().then(function() {
document.addEventListener('contactChanged', onChangeEvent);
onChangeAttached = true;
});
}
if (typeof fn === 'function') {
onChangeCallbacks.push(fn);
}
resolve();
});
}
function notifyCallbacks(data) {
onChangeCallbacks.forEach(function(fn) {
fn(data);
});
}
function handleChangeEvent(evt) {
// Figure out if we got a change in a ICE contact
var contact = localIceContacts.find(function(x) {
return x.id === evt.detail.contactID;
});
if (!contact) {
return Promise.resolve();
}
// If it's a delete update all the storages
if (evt.detail.reason === 'remove') {
return removeICEContact(contact.id);
} else {
notifyCallbacks(localIceContacts);
return Promise.resolve();
}
}
function onChangeEvent(evt) {
// We should refresh the iceContacts before determining if it is necessary
// to notify or not
pendingChanges.push(evt);
if (!performingChange) {
performChange();
}
}
// We perform any change sequentially to avoid any race condition while
// saving to disc the ICE state.
function performChange() {
performingChange = true;
var evt = pendingChanges.pop();
if (!evt) {
performingChange = false;
return;
}
function callback() {
handleChangeEvent(evt).then(performChange);
}
load().then(callback, callback);
}
function getActiveIceContacts() {
return new Promise(function(resolve, reject) {
load().then(function() {
var result = [];
localIceContacts.forEach(function(aContact) {
if (aContact.active && aContact.id) {
result.push(aContact.id);
}
resolve(result);
});
}, reject);
});
}
function stopListenForChanges() {
document.removeEventListener('contactChanged', onChangeEvent);
onChangeCallbacks = [];
onChangeAttached = false;
}
exports.ICEData = {
load: load,
setICEContact: setICEContact,
setICEContacts: setICEContacts,
removeICEContact: removeICEContact,
get iceContacts() { return localIceContacts; },
getActiveIceContacts: getActiveIceContacts,
listenForChanges: listenForChanges,
stopListenForChanges: stopListenForChanges
};
})(window);

View file

@ -0,0 +1,149 @@
'use strict';
/* exported MergeHelper */
/* globals LazyLoader, ICEData, contacts, ContactsService */
/**
*
* Merge helper file. Allows to perform a merge while respecting
* all the ICE Settings
*
*
*/
var MergeHelper = (function() {
var DEPENDENCIES = [
'/shared/js/contacts/contacts_merger.js',
'/shared/js/contacts/utilities/image_thumbnail.js',
'/shared/js/contacts/utilities/ice_store.js',
'/contacts/js/utilities/ice_data.js',
'/shared/js/async_storage.js'
];
var eventHandler;
// Set the new ICE Contacts being contactId the id of the contact which
// is now the merged contact
function setNewIceContacts(merged, original, contactId) {
if (!merged) {
return Promise.resolve();
}
// If we still don't know the contact id
// (this happens when merge is done from 'add new contact')
if (!contactId || contactId === 'undefined') {
eventHandler = onContactAdded.bind(null, merged, original);
ContactsService.addListener('contactchange', eventHandler);
}
return ICEData.setICEContacts(merged);
}
function onContactAdded(mergedIceContacts, originalIceContacts, event) {
if (event.reason !== 'create') {
return;
}
ContactsService.removeListener('contactchange', eventHandler);
var id = event.contactID;
// Let's determine what ICE Contact is changing
var mergedICEContact1 = false;
if (originalIceContacts[0] && mergedIceContacts[0] !==
originalIceContacts[0]) {
mergedIceContacts[0] = id;
mergedICEContact1 = true;
}
// This condition avoids to set ICE Contact 2 to the same contact as
// ICE Contact 1. This may occur if ICE Contact 1 and ICE Contact 2 both
// match an incoming (manually added contact)
if (originalIceContacts[1] &&
mergedIceContacts[1] !== originalIceContacts[1] && !mergedICEContact1) {
mergedIceContacts[1] = id;
}
ICEData.setICEContacts(mergedIceContacts);
}
function getMergedIceContacts(mergeList, contactId) {
return new Promise(function(resolve) {
ICEData.load().then(function() {
var iceContacts = ICEData.iceContacts.map((contact) => {
return contact.active ? contact.id : null;
});
var mergeListWithIds = mergeList.map((matching) => {
return matching.matchingContact.id;
});
var mergedIceContacts;
var isThereMerge = false;
mergedIceContacts = iceContacts.map((iceContact) => {
if(iceContact && mergeListWithIds.indexOf(iceContact) !== -1) {
isThereMerge = true;
return contactId;
}
return iceContact;
});
// If there is no merge null value is returned
if (!isThereMerge) {
resolve([null, iceContacts]);
return;
}
// Here we filter out duplicates
mergedIceContacts = mergedIceContacts.map((id, index) => {
if(id && mergedIceContacts.indexOf(id) === index) {
return id;
}
return null;
});
resolve([mergedIceContacts, iceContacts]);
}, function(error) {
console.error('Error while obtaining ICE Contacts: ', error);
resolve([]);
});
});
}
function merge(contact, mergeList) {
return new Promise(function(resolve, reject) {
LazyLoader.load(DEPENDENCIES, function loaded() {
getMergedIceContacts(mergeList, contact.id).then(
function(result) {
var mergedIceContacts = result[0], originalIceContacts = result[1];
var callbacks = {
success: resolve,
error: function(e) {
console.error('Failed merging duplicate contacts: ', e.name);
// If there is an error in the merge then we just
// keep the old ICE Contacts
ICEData.setICEContacts(originalIceContacts);
ContactsService.removeListener('contactchange', eventHandler);
reject(e);
}
};
var mergerFunction = contacts.Merger.merge.bind(null,
contact, mergeList, callbacks);
// We set the new ICEContact as the merged one
// As a result, the contactChange events associated to the
// Contacts merged (which are deleted) will be ignored
// This is safer than relying on events as
// there could race conditions
setNewIceContacts(mergedIceContacts, originalIceContacts,
contact.id).then(mergerFunction, mergerFunction);
});
});
});
}
return {
'merge': merge
};
}());

View file

@ -0,0 +1,36 @@
'use strict';
(function(exports) {
exports = exports || {};
var mozContactUtils = {
// Checks if an object fields are empty, by empty means
// field is null and if it's an array it's length is 0
haveEmptyFields: function (obj, fields) {
if (obj === null ||
typeof(obj) != 'object' ||
!fields || !fields.length) {
return true;
}
var attr;
for (var i = 0; i < fields.length; i++) {
attr = fields[i];
if (obj[attr]) {
if (Array.isArray(obj[attr])) {
if (obj[attr].length > 0) {
return false;
}
} else {
return false;
}
}
}
return true;
}
};
exports.mozContact = mozContactUtils;
})(window.utils);

View file

@ -0,0 +1,68 @@
'use strict';
var utils = window.utils || {};
if (!utils.time) {
(function() {
utils.time = {};
var _ = navigator.mozL10n.get;
utils.time.pretty = function(time, now) {
// 'now' attribute for test purposes.
var prettyDate = '';
if (!time) {
return prettyDate;
}
function roundToDay(date) {
var rounded = new Date(date);
rounded.setHours(0);
rounded.setMinutes(0);
rounded.setSeconds(0);
rounded.setMilliseconds(0);
return rounded;
}
now = now || Date.now();
var day_diff = (roundToDay(now) - roundToDay(time)) / 86400000;
if (!isNaN(day_diff)) {
// Woohh we are on the future here
if (day_diff < 0) {
prettyDate = (new Date(time)).toLocaleString(navigator.languages, {
year: '2-digit',
month: '2-digit',
day: '2-digit'
});
} else {
if (day_diff === 0) {
prettyDate = _('today');
} else if (day_diff === 1) {
prettyDate = _('yesterday');
} else if (day_diff < 6) {
prettyDate = (new Date(time)).toLocaleString(navigator.languages, {
weekday: 'long'
});
} else {
prettyDate = (new Date(time)).toLocaleString(navigator.languages, {
year: '2-digit',
month: '2-digit',
day: '2-digit'
});
}
}
}
var timeString = (new Date(time)).toLocaleString(navigator.languages, {
hour12: navigator.mozHour12,
hour: 'numeric',
minute: 'numeric'
});
//XXX: This order should not be hardcoded. Change once we have
// relative date/time in Intl
return prettyDate + ' ' + timeString;
};
})();
}

View file

@ -0,0 +1,37 @@
'use strict';
/**
* Utility class to fire events needed to measure the performance
* of the application.
* For an explanation of what means each event go here:
* https://bugzilla.mozilla.org/show_bug.cgi?id=996038
* For an explanation of how are adapted to the specific needs
* in the contacts app please go here:
* https://bugzilla.mozilla.org/show_bug.cgi?id=1015388#c8
*/
(function(){
window.utils = window.utils || {};
var PerformanceHelper = {
domLoaded: function() {
window.performance.mark('navigationLoaded');
},
chromeInteractive: function() {
window.performance.mark('navigationInteractive');
},
visuallyComplete: function() {
window.performance.mark('visuallyLoaded');
},
contentInteractive: function() {
window.performance.mark('contentInteractive');
},
loadEnd: function() {
window.performance.mark('fullyLoaded');
}
};
window.utils.PerformanceHelper = PerformanceHelper;
})();

View file

@ -0,0 +1,150 @@
/* global _ */
'use strict';
// Generates the DOM for each button, depending
// on different variables
function generateButton(singleSim, sim, index) {
var button = document.createElement('button');
button.classList.add('icon', 'icon-sim');
var span = document.createElement('span');
if (singleSim) {
span.setAttribute('data-l10n-id', 'simCard');
} else {
var msisdn = sim.iccInfo && sim.iccInfo.msisdn;
var l10nId = msisdn ? 'simNumberWithMSISDN' : 'simNumberNoMSISDN';
var l10nArgs = {
number: index,
msisdn: msisdn
};
navigator.mozL10n.setAttributes(span, l10nId, l10nArgs);
}
button.appendChild(span);
return button;
}
var SimDomGenerator = function SimDomGenerator() {
};
SimDomGenerator.prototype.setIccList = function(list) {
this.sims = list;
this.singleSim = (list.length === 1);
};
SimDomGenerator.prototype.generateDOM = function() {
this.cleanDOM();
this.generateExportDOM();
this.generateImportDOM();
};
// Specific DOM generation for export options, generating the
// following code:
//<li id="export-sim-option-<id>" data-source="sim">
// <button class="icon icon-sim">
// <span data-l10n-id="importSim2">SIM <number> (optional : msisdn)</span>
// <p><span></span><time></time></p>
// </button>
// <p class="error-message" data-l10n-id="noSimMsgExport"></p>
//</li>
SimDomGenerator.prototype.generateExportDOM = function() {
var exportList = document.getElementById('export-options');
if (exportList === null) {
return;
}
var firstOption = exportList.firstChild;
var self = this;
this.sims.forEach(function onSim(sim, index) {
if (sim === null) {
return;
}
var iccId = sim.iccInfo.iccid;
var li = document.createElement('li');
li.id = 'export-sim-option-' + iccId;
li.dataset.source = 'sim';
li.dataset.iccid = iccId;
li.setAttribute('role', 'presentation');
var button = generateButton(self.singleSim, sim, index + 1);
button.setAttribute('role', 'option');
var p = document.createElement('p');
p.classList.add('error-message');
p.textContent = _('noSimMsgExport');
li.appendChild(button);
li.appendChild(p);
exportList.insertBefore(li, firstOption);
});
};
// Specific DOM generation for import options, generating the
// following code:
//<li id="import-sim-option-<id>" data-source="sim-<iccId>">
// <button class="icon icon-sim" data-l10n-id="importSim2">
// SIM <number> (optional : msisdn)
// <p><span></span><time></time></p>
// </button>
// <p class="error-message" data-l10n-id="noSimMsg"></p>
//</li>
SimDomGenerator.prototype.generateImportDOM = function() {
var importList = document.getElementById('import-options');
if (importList === null) {
return;
}
var firstOption = importList.firstChild;
var self = this;
this.sims.forEach(function onSim(sim, index) {
if (sim === null) {
return;
}
var iccId = sim.iccInfo.iccid;
var li = document.createElement('li');
li.dataset.source = 'sim-' + iccId;
li.dataset.iccid = iccId;
li.id = 'import-sim-option-' + iccId;
li.setAttribute('role', 'presentation');
var button = generateButton(self.singleSim, sim, index + 1);
button.setAttribute('role', 'option');
var pTime = document.createElement('p');
pTime.appendChild(document.createElement('span'));
pTime.appendChild(document.createElement('time'));
button.appendChild(pTime);
var p = document.createElement('p');
p.classList.add('error-message');
p.textContent = _('noSimMsg');
li.appendChild(button);
li.appendChild(p);
importList.insertBefore(li, firstOption);
});
};
// Clean any dom elements generated with this class
SimDomGenerator.prototype.cleanDOM = function() {
// Clean the generated DOM
function clean(selector) {
var items = Array.prototype.slice.call(
document.querySelectorAll(selector));
if (!items) {
return;
}
items.forEach(function(node) {
node.parentNode.removeChild(node);
});
}
// Import
clean('#import-options [data-iccid]');
// Export
clean('#export-options [data-iccid]');
};

View file

@ -0,0 +1,108 @@
(function(exports) {
/* global TAG_OPTIONS, ContactsTag, MainNavigation, LazyLoader */
/* exported TagSelector*/
'use strict';
var customTag, customTagReset, tagDone, tagHeader, lazyLoadedTagsDom = false;
var contactTag;
function handleCustomTagReset(ev) {
ev.preventDefault();
if (customTag) {
customTag.value = '';
}
}
function handleCustomTag(ev) {
if (ev.keyCode === 13) {
ev.preventDefault();
}
ContactsTag.touchCustomTag();
}
function handleBack(cb) {
MainNavigation.back(cb);
}
function handleSelectTagDone() {
var prevValue = contactTag.textContent;
ContactsTag.clickDone(function() {
var valueModifiedEvent = new CustomEvent('ValueModified', {
bubbles: true,
detail: {
prevValue: prevValue,
newValue: contactTag.textContent
}
});
contactTag.dispatchEvent(valueModifiedEvent);
handleBack();
});
}
function showSelectTag() {
var tagsList = document.getElementById('tags-list');
var selectedTagType = contactTag.dataset.taglist;
var options = TAG_OPTIONS[selectedTagType];
var type = selectedTagType.split('-')[0];
var isCustomTagVisible = (document.querySelector(
'[data-template]' + '.' + type + '-' +
'template').dataset.custom != 'false');
options = ContactsTag.filterTags(type, contactTag, options);
if (!customTag) {
customTag = document.querySelector('#custom-tag');
customTag.addEventListener('keydown', handleCustomTag);
customTag.addEventListener('touchend', handleCustomTag);
}
if (!customTagReset) {
customTagReset = document.getElementById('custom-tag-reset');
customTagReset.addEventListener('touchstart', handleCustomTagReset);
}
if (!tagDone) {
tagDone = document.querySelector('#settings-done');
tagDone.addEventListener('click', handleSelectTagDone);
}
if (!tagHeader) {
tagHeader = document.querySelector('#settings-header');
tagHeader.addEventListener('action', handleBack);
}
ContactsTag.setCustomTag(customTag);
// Set whether the custom tag is visible or not
// This is needed for dates as we only support bday and anniversary
// and not custom dates
ContactsTag.setCustomTagVisibility(isCustomTagVisible);
ContactsTag.fillTagOptions(tagsList, contactTag, options);
MainNavigation.go('view-select-tag', 'right-left');
if (document.activeElement) {
document.activeElement.blur();
}
}
var TagSelector = {
show: function goToSelectTag(pContactTag) {
contactTag = pContactTag;
var tagViewElement = document.getElementById('view-select-tag');
if (!lazyLoadedTagsDom) {
LazyLoader.load(tagViewElement, function() {
showSelectTag();
lazyLoadedTagsDom = true;
});
}
else {
showSelectTag();
}
}
};
exports.TagSelector = TagSelector;
}(window));

View file

@ -0,0 +1,45 @@
/*
Copyright 2015, Mozilla Foundation
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
'use strict';
/* global AdvancedTelemetryHelper */
/* global LazyLoader */
(function(exports) {
var namespace = 'telemetry_gaia_contacts';
var dependencies = LazyLoader.load(
['/shared/js/settings_listener.js',
'/shared/js/advanced_telemetry_helper.js']);
var Telemetry = {
logImportUsage: function(name) {
return dependencies.then(() => {
var histogramName = namespace + '_import_' + name;
var count = new AdvancedTelemetryHelper(
AdvancedTelemetryHelper.HISTOGRAM_COUNT,
histogramName);
count.add(1);
});
}
};
exports.Telemetry = Telemetry;
// For testing purposes
exports.TelemetryReady = dependencies;
})(window);