Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
230 lines
6 KiB
JavaScript
Executable file
230 lines
6 KiB
JavaScript
Executable file
(function(global) {
|
|
'use strict';
|
|
/* global mozIntl */
|
|
|
|
/*************************
|
|
|
|
IntlHelper.define('shortDate', 'datetime', {
|
|
year: 'numeric',
|
|
month: 'long',
|
|
});
|
|
|
|
function setValue() {
|
|
const formatter = IntlHelper.get('shortDate');
|
|
console.log(formatter.format(new Date()));
|
|
}
|
|
|
|
setValue();
|
|
|
|
IntlHelper.observe('shortDate', setValue);
|
|
|
|
****************************/
|
|
const helperCache = new Map();
|
|
|
|
const knownObjects = {
|
|
datetime: {
|
|
create: function(options) {
|
|
const customOptions = Object.assign({}, options);
|
|
if (options.hour) {
|
|
customOptions.hour12 = navigator.mozHour12;
|
|
}
|
|
return Intl.DateTimeFormat(
|
|
navigator.languages,
|
|
customOptions
|
|
);
|
|
},
|
|
isAffected: function(obj, reason) {
|
|
if (['languagechange', 'moztimechange'].includes(reason)) {
|
|
return true;
|
|
}
|
|
if (reason === 'timeformatchange') {
|
|
return 'hour' in obj.options;
|
|
}
|
|
return false;
|
|
}
|
|
},
|
|
mozdatetime: {
|
|
create: function(options) {
|
|
const customOptions = Object.assign({}, options);
|
|
if (options.hour) {
|
|
customOptions.hour12 = navigator.mozHour12;
|
|
}
|
|
return mozIntl.DateTimeFormat(
|
|
navigator.languages,
|
|
customOptions
|
|
);
|
|
},
|
|
isAffected: function(obj, reason) {
|
|
if (['languagechange', 'moztimechange'].includes(reason)) {
|
|
return true;
|
|
}
|
|
if (reason === 'timeformatchange') {
|
|
return 'hour' in obj.options;
|
|
}
|
|
return false;
|
|
}
|
|
},
|
|
mozduration: {
|
|
create: function(options) {
|
|
return mozIntl.DurationFormat(
|
|
navigator.languages,
|
|
options
|
|
);
|
|
},
|
|
isAffected: function(obj, reason) {
|
|
return reason === 'languagechange';
|
|
}
|
|
},
|
|
number: {
|
|
create: function(options) {
|
|
return Intl.NumberFormat(
|
|
navigator.languages,
|
|
options
|
|
);
|
|
},
|
|
isAffected: function(obj, reason) {
|
|
return reason === 'languagechange';
|
|
}
|
|
},
|
|
collator: {
|
|
create: function(options) {
|
|
return Intl.Collator(
|
|
navigator.languages,
|
|
options
|
|
);
|
|
},
|
|
isAffected: function(obj, reason) {
|
|
return reason === 'languagechange';
|
|
}
|
|
},
|
|
};
|
|
|
|
global.IntlHelper = {
|
|
define: function(name, type, options) {
|
|
if (!knownObjects.hasOwnProperty(type)) {
|
|
throw new Error('Unknown type: ' + type);
|
|
}
|
|
|
|
if (helperCache.has(name)) {
|
|
throw new Error(
|
|
'Intl Object with name "' + name + '" already exists');
|
|
}
|
|
|
|
helperCache.set(name, {
|
|
options,
|
|
type,
|
|
intlObject: undefined,
|
|
listeners: new Set()
|
|
});
|
|
},
|
|
|
|
get: function(name) {
|
|
if (!helperCache.has(name)) {
|
|
throw new Error('Intl Object with name "' + name + '" is not defined');
|
|
}
|
|
const obj = helperCache.get(name);
|
|
|
|
if (!knownObjects.hasOwnProperty(obj.type)) {
|
|
throw new Error('Unknown type: ' + obj.type);
|
|
}
|
|
|
|
if (obj.intlObject === undefined) {
|
|
obj.intlObject = knownObjects[obj.type].create(obj.options);
|
|
}
|
|
return obj.intlObject;
|
|
},
|
|
|
|
observe: function(names, cb) {
|
|
if (typeof names === 'string') {
|
|
names = [names];
|
|
}
|
|
for (var name of names) {
|
|
if (!helperCache.has(name)) {
|
|
throw new Error(
|
|
'Intl Object with name "' + name + '" is not defined');
|
|
}
|
|
|
|
const obj = helperCache.get(name);
|
|
|
|
obj.listeners.add(cb);
|
|
}
|
|
},
|
|
|
|
unobserve: function(name, cb) {
|
|
const obj = helperCache.get(name);
|
|
obj.listeners.delete(cb);
|
|
},
|
|
|
|
_fireObservers: function(affectedObjects, ...args) {
|
|
const affectedCallbacks = new Set();
|
|
|
|
affectedObjects.forEach(obj =>
|
|
obj.listeners.forEach(cb => affectedCallbacks.add(cb))
|
|
);
|
|
|
|
affectedCallbacks.forEach(cb => {
|
|
try {
|
|
cb(...args);
|
|
} catch(e) {
|
|
console.error('Error in callback: ' + e.toString());
|
|
console.error(e.stack);
|
|
}
|
|
});
|
|
},
|
|
|
|
handleEvent: function(evt) {
|
|
// This is a temporary hack to help handling l10n.js' event
|
|
const affectedObjects = resetObjects(evt.type);
|
|
|
|
if (evt.type === 'languagechange' && navigator.mozL10n) {
|
|
// We will fire observers only on DOMLocalized
|
|
// We're using DOMLocalized here instead of languagechange because
|
|
// mozIntl formatters would restart before new language is loaded
|
|
// which means they'd be using old strings
|
|
//
|
|
// Once we switch completely to l20n.js, we will remove this
|
|
// limitation which will make IntlHelper useful without mozL10n API
|
|
// We will only have to make sure that mozL10n reacted to
|
|
// languagechange before we fired our observers here.
|
|
waitOnDOMLocalized(() =>
|
|
global.IntlHelper._fireObservers(affectedObjects)
|
|
);
|
|
} else {
|
|
global.IntlHelper._fireObservers(affectedObjects);
|
|
}
|
|
},
|
|
|
|
_resetObjectCache: function() {
|
|
helperCache.clear();
|
|
}
|
|
};
|
|
|
|
function waitOnDOMLocalized(cb) {
|
|
const resolver = () => {
|
|
document.removeEventListener('DOMRetranslated', resolver);
|
|
window.removeEventListener('localized', resolver);
|
|
cb();
|
|
};
|
|
document.addEventListener('DOMRetranslated', resolver, false);
|
|
|
|
// Support for l10n.js until we can remove it
|
|
window.addEventListener('localized', resolver, false);
|
|
}
|
|
|
|
function resetObjects(reason) {
|
|
const affectedObjects = new Set();
|
|
|
|
helperCache.forEach((obj, name) => {
|
|
if (knownObjects[obj.type].isAffected(obj, reason)) {
|
|
obj.intlObject = undefined;
|
|
affectedObjects.add(obj);
|
|
}
|
|
});
|
|
|
|
return affectedObjects;
|
|
}
|
|
|
|
window.addEventListener('timeformatchange', global.IntlHelper, false);
|
|
window.addEventListener('languagechange', global.IntlHelper, false);
|
|
window.addEventListener('moztimechange', global.IntlHelper, false);
|
|
})(this);
|