var requirejs, require, define;
(function (global, undef) {
var topReq, dataMain, src, subPath, bootstrapConfig = requirejs || require, hasOwn = Object.prototype.hasOwnProperty, contexts = {}, queue = [], currDirRegExp = /^\.\//, urlRegExp = /^\/|\:|\?|\.js$/, commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/gm, cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, jsSuffixRegExp = /\.js$/;
if (typeof requirejs === 'function') {
return;
}
function hasProp(obj, prop) {
return hasOwn.call(obj, prop);
}
function getOwn(obj, prop) {
return obj && hasProp(obj, prop) && obj[prop];
}
function eachProp(obj, func) {
var prop;
for (prop in obj) {
if (hasProp(obj, prop)) {
if (func(obj[prop], prop)) {
break;
}
}
}
}
function mixin(target, source, force, deepStringMixin) {
if (source) {
eachProp(source, function (value, prop) {
if (force || !hasProp(target, prop)) {
if (deepStringMixin && typeof value === 'object' && value && !Array.isArray(value) && typeof value !== 'function' && !(value instanceof RegExp)) {
if (!target[prop]) {
target[prop] = {};
}
mixin(target[prop], value, force, deepStringMixin);
} else {
target[prop] = value;
}
}
});
}
return target;
}
function getGlobal(value) {
if (!value) {
return value;
}
var g = global;
value.split('.').forEach(function (part) {
g = g[part];
});
return g;
}
function newContext(contextName) {
var req, main, makeMap, callDep, handlers, checkingLater, load, context, defined = {}, waiting = {}, config = {
waitSeconds: 7,
baseUrl: './',
paths: {},
bundles: {},
pkgs: {},
shim: {},
config: {}
}, mapCache = {}, requireDeferreds = [], deferreds = {}, calledDefine = {}, calledPlugin = {}, loadCount = 0, startTime = new Date().getTime(), errCount = 0, trackedErrors = {}, urlFetched = {}, bundlesMap = {};
var nextMicroTaskPass;
(function () {
'use strict';
var waitingResolving, waiting = [];
function callWaiting() {
waitingResolving = null;
var w = waiting;
waiting = [];
while (w.length) {
w.shift()();
}
}
nextMicroTaskPass = function (fn) {
waiting.push(fn);
if (!waitingResolving) {
waitingResolving = new Promise(function (resolve, reject) {
resolve();
}).then(callWaiting).catch(delayedError);
}
};
}());
function trimDots(ary) {
var i, part, length = ary.length;
for (i = 0; i < length; i++) {
part = ary[i];
if (part === '.') {
ary.splice(i, 1);
i -= 1;
} else if (part === '..') {
if (i === 1 && (ary[2] === '..' || ary[0] === '..')) {
break;
} else if (i > 0) {
ary.splice(i - 1, 2);
i -= 2;
}
}
}
}
function normalize(name, baseName, applyMap) {
var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex, foundMap, foundI, foundStarMap, starI, baseParts = baseName && baseName.split('/'), normalizedBaseParts = baseParts, map = config.map, starMap = map && map['*'];
if (name && name.charAt(0) === '.') {
if (baseName) {
normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
name = name.split('/');
lastIndex = name.length - 1;
if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
}
name = normalizedBaseParts.concat(name);
trimDots(name);
name = name.join('/');
} else if (name.indexOf('./') === 0) {
name = name.substring(2);
}
}
if (applyMap && map && (baseParts || starMap)) {
nameParts = name.split('/');
outerLoop:
for (i = nameParts.length; i > 0; i -= 1) {
nameSegment = nameParts.slice(0, i).join('/');
if (baseParts) {
for (j = baseParts.length; j > 0; j -= 1) {
mapValue = getOwn(map, baseParts.slice(0, j).join('/'));
if (mapValue) {
mapValue = getOwn(mapValue, nameSegment);
if (mapValue) {
foundMap = mapValue;
foundI = i;
break outerLoop;
}
}
}
}
if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) {
foundStarMap = getOwn(starMap, nameSegment);
starI = i;
}
}
if (!foundMap && foundStarMap) {
foundMap = foundStarMap;
foundI = starI;
}
if (foundMap) {
nameParts.splice(0, foundI, foundMap);
name = nameParts.join('/');
}
}
pkgMain = getOwn(config.pkgs, name);
return pkgMain ? pkgMain : name;
}
function makeShimExports(value) {
function fn() {
var ret;
if (value.init) {
ret = value.init.apply(global, arguments);
}
return ret || value.exports && getGlobal(value.exports);
}
return fn;
}
function takeQueue(anonId) {
var i, id, args, shim;
for (i = 0; i < queue.length; i += 1) {
if (typeof queue[i][0] !== 'string') {
if (anonId) {
queue[i].unshift(anonId);
anonId = undef;
} else {
break;
}
}
args = queue.shift();
id = args[0];
i -= 1;
if (!hasProp(defined, id) && !hasProp(waiting, id)) {
if (hasProp(deferreds, id)) {
main.apply(undef, args);
} else {
waiting[id] = args;
}
}
}
if (anonId) {
shim = getOwn(config.shim, anonId) || {};
main(anonId, shim.deps || [], shim.exportsFn);
}
}
function makeRequire(relName, topLevel) {
var req = function (deps, callback, errback, alt) {
var name, cfg;
if (topLevel) {
takeQueue();
}
if (typeof deps === 'string') {
if (handlers[deps]) {
return handlers[deps](relName);
}
name = makeMap(deps, relName, true).id;
if (!hasProp(defined, name)) {
throw new Error('Not loaded: ' + name);
}
return defined[name];
} else if (deps && !Array.isArray(deps)) {
cfg = deps;
deps = undef;
if (Array.isArray(callback)) {
deps = callback;
callback = errback;
errback = alt;
}
if (topLevel) {
return req.config(cfg)(deps, callback, errback);
}
}
callback = callback || function () {
};
nextMicroTaskPass(function () {
takeQueue();
main(undef, deps || [], callback, errback, relName);
});
return req;
};
req.isBrowser = typeof document !== 'undefined' && typeof navigator !== 'undefined';
req.nameToUrl = function (moduleName, ext, skipExt) {
var paths, syms, i, parentModule, url, parentPath, bundleId, pkgMain = getOwn(config.pkgs, moduleName);
if (pkgMain) {
moduleName = pkgMain;
}
bundleId = getOwn(bundlesMap, moduleName);
if (bundleId) {
return req.nameToUrl(bundleId, ext, skipExt);
}
if (urlRegExp.test(moduleName)) {
url = moduleName + (ext || '');
} else {
paths = config.paths;
syms = moduleName.split('/');
for (i = syms.length; i > 0; i -= 1) {
parentModule = syms.slice(0, i).join('/');
parentPath = getOwn(paths, parentModule);
if (parentPath) {
if (Array.isArray(parentPath)) {
parentPath = parentPath[0];
}
syms.splice(0, i, parentPath);
break;
}
}
url = syms.join('/');
url += ext || (/^data\:|\?/.test(url) || skipExt ? '' : '.js');
url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url;
}
return config.urlArgs ? url + ((url.indexOf('?') === -1 ? '?' : '&') + config.urlArgs) : url;
};
req.toUrl = function (moduleNamePlusExt) {
var ext, index = moduleNamePlusExt.lastIndexOf('.'), segment = moduleNamePlusExt.split('/')[0], isRelative = segment === '.' || segment === '..';
if (index !== -1 && (!isRelative || index > 1)) {
ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length);
moduleNamePlusExt = moduleNamePlusExt.substring(0, index);
}
return req.nameToUrl(normalize(moduleNamePlusExt, relName), ext, true);
};
req.defined = function (id) {
return hasProp(defined, makeMap(id, relName, true).id);
};
req.specified = function (id) {
id = makeMap(id, relName, true).id;
return hasProp(defined, id) || hasProp(deferreds, id);
};
return req;
}
function resolve(name, d, value) {
if (name) {
defined[name] = value;
if (requirejs.onResourceLoad) {
requirejs.onResourceLoad(context, d.map, d.deps);
}
}
d.finished = true;
d.resolve(value);
}
function reject(d, err) {
d.finished = true;
d.rejected = true;
d.reject(err);
}
function makeNormalize(relName) {
return function (name) {
return normalize(name, relName, true);
};
}
function defineModule(d) {
var name = d.map.id, ret = d.factory.apply(defined[name], d.values);
if (name) {
if (ret === undef) {
if (d.cjsModule) {
ret = d.cjsModule.exports;
} else if (d.usingExports) {
ret = defined[name];
}
}
} else {
requireDeferreds.splice(requireDeferreds.indexOf(d), 1);
}
resolve(name, d, ret);
}
function depFinished(val, i) {
if (!this.rejected && !this.depDefined[i]) {
this.depDefined[i] = true;
this.depCount += 1;
this.values[i] = val;
if (!this.depending && this.depCount === this.depMax) {
defineModule(this);
}
}
}
function makeDefer(name) {
var d = {};
d.promise = new Promise(function (resolve, reject) {
d.resolve = resolve;
d.reject = reject;
});
d.map = name ? makeMap(name, null, true) : {};
d.depCount = 0;
d.depMax = 0;
d.values = [];
d.depDefined = [];
d.depFinished = depFinished;
if (d.map.pr) {
d.deps = [makeMap(d.map.pr)];
}
return d;
}
function getDefer(name) {
var d;
if (name) {
d = hasProp(deferreds, name) && deferreds[name];
if (!d) {
d = deferreds[name] = makeDefer(name);
}
} else {
d = makeDefer();
requireDeferreds.push(d);
}
return d;
}
function makeErrback(d, name) {
return function (err) {
if (!d.rejected) {
if (!err.dynaId) {
err.dynaId = 'id' + (errCount += 1);
err.requireModules = [name];
}
reject(d, err);
}
};
}
function waitForDep(depMap, relName, d, i) {
d.depMax += 1;
callDep(depMap, relName).then(function (val) {
d.depFinished(val, i);
}, makeErrback(d, depMap.id)).catch(makeErrback(d, d.map.id));
}
function makeLoad(id) {
var fromTextCalled;
function load(value) {
if (!fromTextCalled) {
resolve(id, getDefer(id), value);
}
}
load.error = function (err) {
getDefer(id).reject(err);
};
load.fromText = function (text, textAlt) {
var d = getDefer(id), map = makeMap(makeMap(id).n), plainId = map.id;
fromTextCalled = true;
d.factory = function (p, val) {
return val;
};
if (textAlt) {
text = textAlt;
}
if (hasProp(config.config, id)) {
config.config[plainId] = config.config[id];
}
try {
req.exec(text);
} catch (e) {
reject(d, new Error('fromText eval for ' + plainId + ' failed: ' + e));
}
takeQueue(plainId);
d.deps = [map];
waitForDep(map, null, d, d.deps.length);
};
return load;
}
load = typeof importScripts === 'function' ? function (map) {
var url = map.url;
if (urlFetched[url]) {
return;
}
urlFetched[url] = true;
getDefer(map.id);
importScripts(url);
takeQueue(map.id);
} : function (map) {
var script, id = map.id, url = map.url;
if (urlFetched[url]) {
return;
}
urlFetched[url] = true;
script = document.createElement('script');
script.setAttribute('data-requiremodule', id);
script.type = config.scriptType || 'text/javascript';
script.charset = 'utf-8';
script.async = true;
loadCount += 1;
script.addEventListener('load', function () {
loadCount -= 1;
takeQueue(id);
}, false);
script.addEventListener('error', function () {
loadCount -= 1;
var err, pathConfig = getOwn(config.paths, id), d = getOwn(deferreds, id);
if (pathConfig && Array.isArray(pathConfig) && pathConfig.length > 1) {
script.parentNode.removeChild(script);
pathConfig.shift();
d.map = makeMap(id);
load(d.map);
} else {
err = new Error('Load failed: ' + id + ': ' + script.src);
err.requireModules = [id];
getDefer(id).reject(err);
}
}, false);
script.src = url;
document.head.appendChild(script);
};
function callPlugin(plugin, map, relName) {
plugin.load(map.n, makeRequire(relName), makeLoad(map.id), {});
}
callDep = function (map, relName) {
var args, bundleId, name = map.id, shim = config.shim[name];
if (hasProp(waiting, name)) {
args = waiting[name];
delete waiting[name];
main.apply(undef, args);
} else if (!hasProp(deferreds, name)) {
if (map.pr) {
if (bundleId = getOwn(bundlesMap, name)) {
map.url = req.nameToUrl(bundleId);
load(map);
} else {
return callDep(makeMap(map.pr)).then(function (plugin) {
var newMap = makeMap(name, relName, true), newId = newMap.id, shim = getOwn(config.shim, newId);
if (!hasProp(calledPlugin, newId)) {
calledPlugin[newId] = true;
if (shim && shim.deps) {
req(shim.deps, function () {
callPlugin(plugin, newMap, relName);
});
} else {
callPlugin(plugin, newMap, relName);
}
}
return getDefer(newId).promise;
});
}
} else if (shim && shim.deps) {
req(shim.deps, function () {
load(map);
});
} else {
load(map);
}
}
return getDefer(name).promise;
};
function splitPrefix(name) {
var prefix, index = name ? name.indexOf('!') : -1;
if (index > -1) {
prefix = name.substring(0, index);
name = name.substring(index + 1, name.length);
}
return [
prefix,
name
];
}
makeMap = function (name, relName, applyMap) {
if (typeof name !== 'string') {
return name;
}
var plugin, url, parts, prefix, result, cacheKey = name + ' & ' + (relName || '') + ' & ' + !!applyMap;
parts = splitPrefix(name);
prefix = parts[0];
name = parts[1];
if (!prefix && hasProp(mapCache, cacheKey)) {
return mapCache[cacheKey];
}
if (prefix) {
prefix = normalize(prefix, relName, applyMap);
plugin = hasProp(defined, prefix) && defined[prefix];
}
if (prefix) {
if (plugin && plugin.normalize) {
name = plugin.normalize(name, makeNormalize(relName));
} else {
name = normalize(name, relName, applyMap);
}
} else {
name = normalize(name, relName, applyMap);
parts = splitPrefix(name);
prefix = parts[0];
name = parts[1];
url = req.nameToUrl(name);
}
result = {
id: prefix ? prefix + '!' + name : name,
n: name,
pr: prefix,
url: url
};
if (!prefix) {
mapCache[cacheKey] = result;
}
return result;
};
handlers = {
require: function (name) {
return makeRequire(name);
},
exports: function (name) {
var e = defined[name];
if (typeof e !== 'undefined') {
return e;
} else {
return defined[name] = {};
}
},
module: function (name) {
return {
id: name,
uri: '',
exports: handlers.exports(name),
config: function () {
return getOwn(config.config, name) || {};
}
};
}
};
function breakCycle(d, traced, processed) {
var id = d.map.id;
traced[id] = true;
if (!d.finished && d.deps) {
d.deps.forEach(function (depMap) {
var depId = depMap.id, dep = !hasProp(handlers, depId) && getDefer(depId);
if (dep && !dep.finished && !processed[depId]) {
if (hasProp(traced, depId)) {
d.deps.forEach(function (depMap, i) {
if (depMap.id === depId) {
d.depFinished(defined[depId], i);
}
});
} else {
breakCycle(dep, traced, processed);
}
}
});
}
processed[id] = true;
}
function check(d) {
var err, notFinished = [], waitInterval = config.waitSeconds * 1000, expired = waitInterval && startTime + waitInterval < new Date().getTime();
if (loadCount === 0) {
if (d) {
if (!d.finished) {
breakCycle(d, {}, {});
}
} else if (requireDeferreds.length) {
requireDeferreds.forEach(function (d) {
breakCycle(d, {}, {});
});
}
}
if (expired) {
eachProp(deferreds, function (d) {
if (!d.finished) {
notFinished.push(d.map.id);
}
});
err = new Error('Timeout for modules: ' + notFinished);
err.requireModules = notFinished;
req.onError(err);
} else if (loadCount || requireDeferreds.length) {
if (!checkingLater) {
checkingLater = true;
setTimeout(function () {
checkingLater = false;
check();
}, 70);
}
}
}
function delayedError(e) {
setTimeout(function () {
if (!e.dynaId || !trackedErrors[e.dynaId]) {
trackedErrors[e.dynaId] = true;
req.onError(e);
}
});
}
main = function (name, deps, factory, errback, relName) {
if (name && hasProp(calledDefine, name)) {
return;
}
calledDefine[name] = true;
var d = getDefer(name);
if (deps && !Array.isArray(deps)) {
factory = deps;
deps = [];
}
d.promise.catch(errback || delayedError);
relName = relName || name;
if (typeof factory === 'function') {
if (!deps.length && factory.length) {
factory.toString().replace(commentRegExp, '').replace(cjsRequireRegExp, function (match, dep) {
deps.push(dep);
});
deps = (factory.length === 1 ? ['require'] : [
'require',
'exports',
'module'
]).concat(deps);
}
d.factory = factory;
d.deps = deps;
d.depending = true;
deps.forEach(function (depName, i) {
var depMap;
deps[i] = depMap = makeMap(depName, relName, true);
depName = depMap.id;
if (depName === 'require') {
d.values[i] = handlers.require(name);
} else if (depName === 'exports') {
d.values[i] = handlers.exports(name);
d.usingExports = true;
} else if (depName === 'module') {
d.values[i] = d.cjsModule = handlers.module(name);
} else if (depName === undefined) {
d.values[i] = undefined;
} else {
waitForDep(depMap, relName, d, i);
}
});
d.depending = false;
if (d.depCount === d.depMax) {
defineModule(d);
}
} else if (name) {
resolve(name, d, factory);
}
startTime = new Date().getTime();
if (!name) {
check(d);
}
};
req = makeRequire(null, true);
req.config = function (cfg) {
if (cfg.context && cfg.context !== contextName) {
return newContext(cfg.context).config(cfg);
}
mapCache = {};
if (cfg.baseUrl) {
if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') {
cfg.baseUrl += '/';
}
}
var shim = config.shim, objs = {
paths: true,
bundles: true,
config: true,
map: true
};
eachProp(cfg, function (value, prop) {
if (objs[prop]) {
if (!config[prop]) {
config[prop] = {};
}
mixin(config[prop], value, true, true);
} else {
config[prop] = value;
}
});
if (cfg.bundles) {
eachProp(cfg.bundles, function (value, prop) {
value.forEach(function (v) {
if (v !== prop) {
bundlesMap[v] = prop;
}
});
});
}
if (cfg.shim) {
eachProp(cfg.shim, function (value, id) {
if (Array.isArray(value)) {
value = { deps: value };
}
if ((value.exports || value.init) && !value.exportsFn) {
value.exportsFn = makeShimExports(value);
}
shim[id] = value;
});
config.shim = shim;
}
if (cfg.packages) {
cfg.packages.forEach(function (pkgObj) {
var location, name;
pkgObj = typeof pkgObj === 'string' ? { name: pkgObj } : pkgObj;
name = pkgObj.name;
location = pkgObj.location;
if (location) {
config.paths[name] = pkgObj.location;
}
config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main').replace(currDirRegExp, '').replace(jsSuffixRegExp, '');
});
}
if (cfg.deps || cfg.callback) {
req(cfg.deps, cfg.callback);
}
return req;
};
req.onError = function (err) {
throw err;
};
context = {
id: contextName,
defined: defined,
waiting: waiting,
config: config,
deferreds: deferreds
};
contexts[contextName] = context;
return req;
}
requirejs = topReq = newContext('_');
if (typeof require !== 'function') {
require = topReq;
}
topReq.exec = function (text) {
return eval(text);
};
topReq.contexts = contexts;
define = function () {
queue.push([].slice.call(arguments, 0));
};
define.amd = { jQuery: true };
if (bootstrapConfig) {
topReq.config(bootstrapConfig);
}
if (topReq.isBrowser && !contexts._.config.skipDataMain) {
dataMain = document.querySelectorAll('script[data-main]')[0];
dataMain = dataMain && dataMain.getAttribute('data-main');
if (dataMain) {
dataMain = dataMain.replace(jsSuffixRegExp, '');
if (!bootstrapConfig || !bootstrapConfig.baseUrl) {
src = dataMain.split('/');
dataMain = src.pop();
subPath = src.length ? src.join('/') + '/' : './';
topReq.config({ baseUrl: subPath });
}
topReq([dataMain]);
}
}
}(this));
define('alameda', function () {
});
(function () {
function consoleHelper() {
var msg = arguments[0] + ':';
for (var i = 1; i < arguments.length; i++) {
msg += ' ' + arguments[i];
}
msg += '\x1B[0m\n';
dump(msg);
}
if ('mozTCPSocket' in window.navigator) {
window.console = {
log: consoleHelper.bind(null, '\x1B[32mLOG'),
error: consoleHelper.bind(null, '\x1B[31mERR'),
info: consoleHelper.bind(null, '\x1B[36mINF'),
warn: consoleHelper.bind(null, '\x1B[33mWAR')
};
}
window.onerror = function errHandler(msg, url, line) {
console.error('onerror reporting:', msg, '@', url, ':', line);
return false;
};
}());
define('console_hook', function () {
});
define('evt', [], function () {
var evt, slice = Array.prototype.slice, props = [
'_events',
'_pendingEvents',
'on',
'once',
'latest',
'latestOnce',
'removeListener',
'emitWhenListener',
'emit'
];
function Emitter() {
this._events = {};
this._pendingEvents = {};
}
Emitter.prototype = {
on: function (id, fn) {
var listeners = this._events[id], pending = this._pendingEvents[id];
if (!listeners) {
listeners = this._events[id] = [];
}
listeners.push(fn);
if (pending) {
pending.forEach(function (args) {
fn.apply(null, args);
});
delete this._pendingEvents[id];
}
return this;
},
once: function (id, fn) {
var self = this, fired = false;
function one() {
if (fired) {
return;
}
fired = true;
fn.apply(null, arguments);
setTimeout(function () {
self.removeListener(id, one);
});
}
return this.on(id, one);
},
latest: function (id, fn) {
if (this[id] && !this._pendingEvents[id]) {
fn(this[id]);
}
this.on(id, fn);
},
latestOnce: function (id, fn) {
if (this[id] && !this._pendingEvents[id]) {
fn(this[id]);
} else {
this.once(id, fn);
}
},
removeListener: function (id, fn) {
var i, listeners = this._events[id];
if (listeners) {
i = listeners.indexOf(fn);
if (i !== -1) {
listeners.splice(i, 1);
}
if (listeners.length === 0) {
delete this._events[id];
}
}
},
emitWhenListener: function (id) {
var listeners = this._events[id];
if (listeners) {
this.emit.apply(this, arguments);
} else {
if (!this._pendingEvents[id]) {
this._pendingEvents[id] = [];
}
this._pendingEvents[id].push(slice.call(arguments, 1));
}
},
emit: function (id) {
var args = slice.call(arguments, 1), listeners = this._events[id];
if (listeners) {
listeners.forEach(function (fn) {
try {
fn.apply(null, args);
} catch (e) {
setTimeout(function () {
throw e;
});
}
});
}
}
};
evt = new Emitter();
evt.Emitter = Emitter;
evt.mix = function (obj) {
var e = new Emitter();
props.forEach(function (prop) {
if (obj.hasOwnProperty(prop)) {
throw new Error('Object already has a property "' + prop + '"');
}
obj[prop] = e[prop];
});
return obj;
};
return evt;
});
define('app_self', [
'require',
'exports',
'module',
'evt'
], function (require, exports, module) {
var evt = require('evt');
var appSelf = evt.mix({}), mozApps = navigator.mozApps, oldLatest = appSelf.latest, loaded = false;
if (!mozApps) {
appSelf.self = {};
loaded = true;
}
function loadSelf() {
mozApps.getSelf().onsuccess = function (event) {
loaded = true;
var app = event.target.result;
appSelf.self = app;
appSelf.emit('self', appSelf.self);
};
}
appSelf.latest = function (id) {
if (!loaded) {
loadSelf();
}
if (id !== 'self') {
throw new Error(module.id + ' only supports "self" property');
}
return oldLatest.apply(this, arguments);
};
return appSelf;
});
define('l10n', {
load: function (id, require, onload, config) {
if (config.isBuild) {
return onload();
}
require(['l10nbase'], function () {
navigator.mozL10n.once(function () {
var dir = navigator.mozL10n.language.direction, htmlNode = document.querySelector('html');
if (htmlNode.getAttribute('dir') !== dir) {
console.log('email l10n updating html dir to ' + dir);
htmlNode.setAttribute('dir', dir);
}
onload(navigator.mozL10n);
});
});
}
});
(function (window) {
'use strict';
window.NotificationHelper = {
getIconURI: function nh_getIconURI(app, entryPoint) {
var icons = app.manifest.icons;
if (entryPoint) {
icons = app.manifest.entry_points[entryPoint].icons;
}
if (!icons) {
return null;
}
var sizes = Object.keys(icons).map(function parse(str) {
return parseInt(str, 10);
});
sizes.sort(function (x, y) {
return y - x;
});
var HVGA = document.documentElement.clientWidth < 480;
var index = sizes[HVGA ? sizes.length - 1 : 0];
return app.installOrigin + icons[index];
},
send: function nh_send(titleL10n, options) {
return Promise.all([
titleL10n,
options.bodyL10n
].map(getL10n)).then(([title, body]) => {
if (body) {
options.body = body;
}
options.lang = document.documentElement.getAttribute('lang');
var notification = new window.Notification(title, options);
if (options.closeOnClick !== false) {
notification.addEventListener('click', function nh_click() {
notification.removeEventListener('click', nh_click);
notification.close();
});
}
return notification;
});
}
};
function getL10n(l10nAttrs) {
if (!l10nAttrs) {
return;
}
if (typeof l10nAttrs === 'string') {
return document.l10n.formatValue(l10nAttrs);
}
if (typeof l10nAttrs.raw === "string") {
return l10nAttrs.raw;
}
return document.l10n.formatValue(l10nAttrs.id, l10nAttrs.args);
}
}(this));
define('shared/js/notification_helper', function (global) {
return function () {
var ret, fn;
return ret || global.NotificationHelper;
};
}(this));
define('sync', [
'require',
'app_self',
'evt',
'l10n!',
'shared/js/notification_helper'
], function (require) {
var cronSyncStartTime, appSelf = require('app_self'), evt = require('evt'), mozL10n = require('l10n!'), notificationHelper = require('shared/js/notification_helper');
var notificationDataVersion = '1';
return function syncInit(model, api) {
var hasBeenVisible = !document.hidden, waitingOnCron = {};
if (hasBeenVisible) {
api.setInteractive();
}
document.addEventListener('visibilitychange', function onVisibilityChange() {
if (!document.hidden) {
hasBeenVisible = true;
api.setInteractive();
}
}, false);
function makeAccountKey(accountIds) {
return 'id' + accountIds.join(' ');
}
var sendNotification;
if (typeof Notification !== 'function') {
console.log('email: notifications not available');
sendNotification = function () {
};
} else {
sendNotification = function (notificationId, titleL10n, bodyL10n, iconUrl, data, behavior) {
console.log('Notification sent for ' + notificationId);
if (Notification.permission !== 'granted') {
console.log('email: notification skipped, permission: ' + Notification.permission);
return;
}
data = data || {};
var notificationOptions = {
bodyL10n: bodyL10n,
icon: iconUrl,
tag: notificationId,
data: data,
mozbehavior: { noscreen: true },
closeOnClick: false
};
if (behavior) {
Object.keys(behavior).forEach(function (key) {
notificationOptions.mozbehavior[key] = behavior[key];
});
}
notificationHelper.send(titleL10n, notificationOptions).then(function (notification) {
notification.onclick = function () {
evt.emit('notification', {
clicked: true,
imageURL: iconUrl,
tag: notificationId,
data: data
});
};
});
};
}
api.oncronsyncstart = function (accountIds) {
console.log('email oncronsyncstart: ' + accountIds);
cronSyncStartTime = Date.now();
var accountKey = makeAccountKey(accountIds);
waitingOnCron[accountKey] = true;
};
function fetchNotificationsData(ntype) {
if (typeof Notification !== 'function' || !Notification.get) {
return Promise.resolve({});
}
return Notification.get().then(function (notifications) {
var result = {};
notifications.forEach(function (notification) {
var data = notification.data;
if (!data.v || data.v !== notificationDataVersion) {
notification.close();
} else if (data.ntype === ntype) {
data.notification = notification;
result[data.accountId] = data;
}
});
return result;
}, function (err) {
console.error('email notification.get call failed: ' + err);
return {};
});
}
function getSyncEnv(fn) {
appSelf.latest('self', function (app) {
model.latestOnce('account', function (currentAccount) {
fetchNotificationsData('sync').then(function (existingNotificationsData) {
mozL10n.formatValue('senders-separation-sign').then(function (separator) {
var localized = { separator };
mozL10n.formatValue('notification-no-subject').then(function (noSubject) {
localized.noSubject = noSubject;
fn(app, currentAccount, existingNotificationsData, localized);
});
});
});
});
});
}
function topUniqueFromNames(latestInfos, oldFromNames) {
var names = [], maxCount = 3;
latestInfos.sort(function (a, b) {
return b.date - a.date;
});
latestInfos.some(function (info) {
if (names.length > maxCount) {
return true;
}
if (names.indexOf(info.from) === -1) {
names.push(info.from);
}
});
oldFromNames.some(function (name) {
if (names.length > maxCount) {
return true;
}
if (names.indexOf(name) === -1) {
names.push(name);
}
});
return names;
}
api.oncronsyncstop = function (accountsResults) {
console.log('email oncronsyncstop: ' + accountsResults.accountIds);
function finishSync() {
evt.emit('cronSyncStop', accountsResults.accountIds);
var accountKey = makeAccountKey(accountsResults.accountIds);
waitingOnCron[accountKey] = false;
var stillWaiting = Object.keys(waitingOnCron).some(function (key) {
return !!waitingOnCron[key];
});
if (!hasBeenVisible && !stillWaiting) {
console.log('sync completed in ' + (Date.now() - cronSyncStartTime) / 1000 + ' seconds, closing mail app');
window.close();
}
}
if (!accountsResults.updates) {
finishSync();
return;
}
getSyncEnv(function (app, currentAccount, existingNotificationsData, localized) {
var iconUrl = notificationHelper.getIconURI(app);
accountsResults.updates.forEach(function (result) {
if (currentAccount.id === result.id && !document.hidden) {
model.notifyInboxMessages(result);
return;
}
if (!model.getAccount(result.id).notifyOnNew || typeof Notification !== 'function') {
return;
}
var dataObject, subjectL10n, bodyL10n, behavior, count = result.count, oldFromNames = [];
var existingData = existingNotificationsData[result.id];
if (existingData) {
if (existingData.count) {
count += parseInt(existingData.count, 10);
}
if (existingData.fromNames) {
oldFromNames = existingData.fromNames;
}
}
if (count > 1) {
var newFromNames = topUniqueFromNames(result.latestMessageInfos, oldFromNames);
dataObject = {
v: notificationDataVersion,
ntype: 'sync',
type: 'message_list',
accountId: result.id,
count: count,
fromNames: newFromNames
};
if (existingData && existingData.count) {
behavior = {
soundFile: 'does-not-exist-to-simulate-silent',
vibrationPattern: [1]
};
}
if (model.getAccountCount() === 1) {
subjectL10n = {
id: 'new-emails-notify-one-account',
args: { n: count }
};
} else {
subjectL10n = {
id: 'new-emails-notify-multiple-accounts',
args: {
n: count,
accountName: result.address
}
};
}
bodyL10n = { raw: newFromNames.join(localized.separator) };
} else {
var info = result.latestMessageInfos[0];
dataObject = {
v: notificationDataVersion,
ntype: 'sync',
type: 'message_reader',
accountId: info.accountId,
messageSuid: info.messageSuid,
count: 1,
fromNames: [info.from]
};
var rawSubject = info.subject || localized.noSubject;
if (model.getAccountCount() === 1) {
subjectL10n = { raw: rawSubject };
bodyL10n = { raw: info.from };
} else {
subjectL10n = {
id: 'new-emails-notify-multiple-accounts',
args: {
n: count,
accountName: result.address
}
};
bodyL10n = {
id: 'new-emails-notify-multiple-accounts-body',
args: {
from: info.from,
subject: rawSubject
}
};
}
}
sendNotification(result.id, subjectL10n, bodyL10n, iconUrl, dataObject, behavior);
});
finishSync();
});
};
var BACKGROUND_SEND_NOTIFICATION_ID = 'backgroundSendFailed';
var sentAudio = null;
api.onbackgroundsendstatus = function (data) {
console.log('outbox: Message', data.suid, 'status =', JSON.stringify({
state: data.state,
err: data.err,
sendFailures: data.sendFailures,
emitNotifications: data.emitNotifications
}));
var descId;
switch (data.state) {
case 'pending':
descId = 'background-send-pending';
break;
case 'sending':
descId = 'background-send-sending';
break;
case 'success':
descId = 'background-send-success';
break;
case 'error':
if (data.badAddresses && data.badAddresses.length || data.err === 'bad-recipient') {
descId = 'background-send-error-recipients';
} else {
descId = 'background-send-error';
}
break;
case 'syncDone':
break;
default:
console.error('No state description for background send state "' + data.state + '"');
return;
}
if (data.state === 'success') {
model.latestOnce('acctsSlice', function () {
var account = model.getAccount(data.accountId);
if (!account) {
console.error('Invalid account ID', data.accountId, 'for a background send notification.');
return;
}
if (account.playSoundOnSend) {
if (!sentAudio) {
sentAudio = new Audio('/sounds/firefox_sent.opus');
sentAudio.mozAudioChannelType = 'notification';
}
sentAudio.play();
}
}.bind(this));
}
if (!document.hidden) {
mozL10n.formatValue(descId).then(function (localizedDescription) {
data.localizedDescription = localizedDescription;
model.notifyBackgroundSendStatus(data);
});
} else if (data.state === 'error' && data.emitNotifications) {
appSelf.latest('self', function (app) {
var iconUrl = notificationHelper.getIconURI(app);
var dataObject = {
v: notificationDataVersion,
ntype: 'outbox',
type: 'message_reader',
folderType: 'outbox',
accountId: data.accountId,
messageSuid: data.suid
};
sendNotification(BACKGROUND_SEND_NOTIFICATION_ID, 'background-send-error-title', descId, iconUrl, dataObject);
});
}
};
evt.on('inboxShown', function (accountId) {
fetchNotificationsData('sync').then(function (notificationsData) {
if (notificationsData.hasOwnProperty(accountId)) {
notificationsData[accountId].notification.close();
}
});
});
};
});
define('model_init', [
'require',
'sync',
'evt',
'l10n!'
], function (require) {
return function modelInit(model, api) {
require('sync')(model, api);
var evt = require('evt'), mozL10n = require('l10n!');
api.onbadlogin = function (account, problem, whichSide) {
evt.emitWhenListener('apiBadLogin', account, problem, whichSide);
};
api.useLocalizedStrings({
wrote: mozL10n.get('reply-quoting-wrote'),
originalMessage: mozL10n.get('forward-original-message'),
forwardHeaderLabels: {
subject: mozL10n.get('forward-header-subject'),
date: mozL10n.get('forward-header-date'),
from: mozL10n.get('forward-header-from'),
replyTo: mozL10n.get('forward-header-reply-to'),
to: mozL10n.get('forward-header-to'),
cc: mozL10n.get('forward-header-cc')
},
folderNames: {
inbox: mozL10n.get('folder-inbox'),
outbox: mozL10n.get('folder-outbox'),
sent: mozL10n.get('folder-sent'),
drafts: mozL10n.get('folder-drafts'),
trash: mozL10n.get('folder-trash'),
queue: mozL10n.get('folder-queue'),
junk: mozL10n.get('folder-junk'),
archives: mozL10n.get('folder-archives'),
localdrafts: mozL10n.get('folder-localdrafts')
}
});
};
});
define('model_create', [
'require',
'evt',
'model_init'
], function (require) {
var evt = require('evt'), modelInit = require('model_init');
function dieOnFatalError(msg) {
console.error('FATAL:', msg);
throw new Error(msg);
}
function saveHasAccount(acctsSlice) {
localStorage.setItem('data_has_account', acctsSlice.items.length ? 'yes' : 'no');
console.log('WRITING LOCAL STORAGE ITEM: ' + 'data_has_account', acctsSlice.items.length ? 'yes' : 'no');
}
function Model() {
evt.Emitter.call(this);
}
Model.prototype = {
acctsSlice: null,
account: null,
foldersSlice: null,
folder: null,
_callEmit: function (id) {
this.emit(id, this[id]);
},
inited: false,
hasAccount: function () {
return this.getAccountCount() > 0;
},
getAccount: function (id) {
if (!this.acctsSlice || !this.acctsSlice.items) {
throw new Error('No acctsSlice available');
}
var targetAccount;
this.acctsSlice.items.some(function (account) {
if (account.id === id) {
return !!(targetAccount = account);
}
});
return targetAccount;
},
getAccountCount: function () {
var count = 0;
if (this.acctsSlice && this.acctsSlice.items && this.acctsSlice.items.length) {
count = this.acctsSlice.items.length;
}
return count;
},
init: function (showLatest, callback) {
require(['api'], function (api) {
if (this === modelCreate.defaultModel) {
modelInit(this, api);
}
this.api = api;
this.die();
var acctsSlice = api.viewAccounts(false);
acctsSlice.oncomplete = function () {
this.acctsSlice = acctsSlice;
saveHasAccount(acctsSlice);
if (acctsSlice.items.length) {
var account = showLatest ? acctsSlice.items.slice(-1)[0] : acctsSlice.defaultAccount;
this.changeAccount(account, callback);
} else if (callback) {
callback();
}
this.inited = true;
this._callEmit('acctsSlice');
if (this === modelCreate.defaultModel) {
evt.emitWhenListener('metrics:apiDone');
}
}.bind(this);
acctsSlice.onchange = function () {
saveHasAccount(acctsSlice);
}.bind(this);
}.bind(this));
},
changeAccount: function (account, callback) {
if (this.account && this.account.id === account.id) {
if (callback) {
callback();
}
return;
}
this._dieFolders();
this.account = account;
this._callEmit('account');
var foldersSlice = this.api.viewFolders('account', account);
foldersSlice.oncomplete = function () {
this.foldersSlice = foldersSlice;
this.foldersSlice.onchange = this.notifyFoldersSliceOnChange.bind(this);
this.selectInbox(callback);
this._callEmit('foldersSlice');
}.bind(this);
},
changeAccountFromId: function (accountId, callback) {
if (!this.acctsSlice || !this.acctsSlice.items.length) {
throw new Error('No accounts available');
}
this.acctsSlice.items.some(function (account) {
if (account.id === accountId) {
this.changeAccount(account, callback);
return true;
}
}.bind(this));
},
changeFolder: function (folder) {
if (folder && (!this.folder || folder.id !== this.folder.id)) {
this.folder = folder;
this._callEmit('folder');
}
},
selectInbox: function (callback) {
this.selectFirstFolderWithType('inbox', callback);
},
selectFirstFolderWithType: function (folderType, callback) {
if (!this.foldersSlice) {
throw new Error('No foldersSlice available');
}
var folder = this.foldersSlice.getFirstFolderWithType(folderType);
if (!folder) {
dieOnFatalError('We have an account without a folderType ' + folderType + '!', this.foldersSlice.items);
}
if (this.folder && this.folder.id === folder.id) {
if (callback) {
callback();
}
} else {
if (callback) {
this.once('folder', callback);
}
this.changeFolder(folder);
}
},
notifyInboxMessages: function (accountUpdate) {
if (accountUpdate.id === this.account.id) {
this.emit('newInboxMessages', accountUpdate.count);
}
},
notifyFoldersSliceOnChange: function (folder) {
this.emit('foldersSliceOnChange', folder);
},
notifyBackgroundSendStatus: function (data) {
this.emit('backgroundSendStatus', data);
},
_dieFolders: function () {
if (this.foldersSlice) {
this.foldersSlice.die();
}
this.foldersSlice = null;
this.folder = null;
},
die: function () {
if (this.acctsSlice) {
this.acctsSlice.die();
}
this.acctsSlice = null;
this.account = null;
this._dieFolders();
}
};
evt.mix(Model.prototype);
function modelCreate() {
return new Model();
}
modelCreate.defaultModel = new Model();
return modelCreate;
});
var MimeMapper = {
_typeToExtensionMap: {
'image/jpeg': 'jpg',
'image/png': 'png',
'image/gif': 'gif',
'image/bmp': 'bmp',
'audio/mpeg': 'mp3',
'audio/mp4': 'm4a',
'audio/ogg': 'ogg',
'audio/webm': 'webm',
'audio/3gpp': '3gp',
'audio/amr': 'amr',
'video/mp4': 'mp4',
'video/mpeg': 'mpg',
'video/ogg': 'ogg',
'video/webm': 'webm',
'video/3gpp': '3gp',
'video/3gpp2': '3g2',
'application/pdf': 'pdf',
'application/vcard': 'vcf',
'text/vcard': 'vcf',
'text/x-vcard': 'vcf'
},
_extensionToTypeMap: {
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'jpe': 'image/jpeg',
'png': 'image/png',
'gif': 'image/gif',
'bmp': 'image/bmp',
'mp3': 'audio/mpeg',
'm4a': 'audio/mp4',
'm4b': 'audio/mp4',
'm4p': 'audio/mp4',
'm4r': 'audio/mp4',
'aac': 'audio/aac',
'opus': 'audio/ogg',
'amr': 'audio/amr',
'mp4': 'video/mp4',
'mpeg': 'video/mpeg',
'mpg': 'video/mpeg',
'ogv': 'video/ogg',
'ogx': 'video/ogg',
'webm': 'video/webm',
'3gp': 'video/3gpp',
'3g2': 'video/3gpp2',
'ogg': 'video/ogg',
'pdf': 'application/pdf',
'vcf': 'text/vcard'
},
_parseExtension: function (filename) {
var array = filename.split('.');
return array.length > 1 ? array.pop() : '';
},
isSupportedType: function (mimetype) {
return mimetype in this._typeToExtensionMap;
},
isSupportedExtension: function (extension) {
return extension in this._extensionToTypeMap;
},
isFilenameMatchesType: function (filename, mimetype) {
var extension = this._parseExtension(filename);
var guessedType = this.guessTypeFromExtension(extension);
return guessedType == mimetype;
},
guessExtensionFromType: function (mimetype) {
return this._typeToExtensionMap[mimetype];
},
guessTypeFromExtension: function (extension) {
return this._extensionToTypeMap[extension];
},
guessTypeFromFileProperties: function (filename, mimetype) {
var extension = this._parseExtension(filename);
var type = this.isSupportedType(mimetype) ? mimetype : this.guessTypeFromExtension(extension);
return type || '';
},
ensureFilenameMatchesType: function (filename, mimetype) {
if (!this.isFilenameMatchesType(filename, mimetype)) {
var guessedExt = this.guessExtensionFromType(mimetype);
if (guessedExt) {
filename += '.' + guessedExt;
}
}
return filename;
}
};
define('shared/js/mime_mapper', function (global) {
return function () {
var ret, fn;
return ret || global.MimeMapper;
};
}(this));
define('attachment_name', [
'require',
'l10n!',
'shared/js/mime_mapper'
], function (require) {
var mozL10n = require('l10n!'), mapper = require('shared/js/mime_mapper');
var attachmentName = {
ensureName: function (blob, name, count) {
if (!name) {
count = count || 1;
var suffix = mapper.guessExtensionFromType(blob.type);
name = mozL10n.get('default-attachment-filename', { n: count }) + (suffix ? '.' + suffix : '');
}
return name;
},
ensureNameList: function (blobs, names) {
for (var i = 0; i < blobs.length; i++) {
names[i] = attachmentName.ensureName(blobs[i], names[i], i + 1);
}
}
};
return attachmentName;
});
define('query_uri', [], function () {
'use strict';
function decode(value) {
try {
return decodeURIComponent(value);
} catch (err) {
console.error('Skipping "' + value + '", decodeURIComponent error: ' + err);
return '';
}
}
function queryURI(uri) {
function addressesToArray(addresses) {
if (!addresses) {
return [];
}
addresses = addresses.split(/[,;]/);
var addressesArray = addresses.filter(function notEmpty(addr) {
return addr.trim() !== '';
});
return addressesArray;
}
var mailtoReg = /^mailto:(.*)/i;
var obj = {};
if (uri && uri.match(mailtoReg)) {
uri = uri.match(mailtoReg)[1];
var parts = uri.split('?');
var subjectReg = /(?:^|&)subject=([^\&]*)/i, bodyReg = /(?:^|&)body=([^\&]*)/i, ccReg = /(?:^|&)cc=([^\&]*)/i, bccReg = /(?:^|&)bcc=([^\&]*)/i;
obj.to = parts[0] ? addressesToArray(decode(parts[0])) : [];
if (parts.length == 2) {
var data = parts[1];
if (data.match(subjectReg)) {
obj.subject = decode(data.match(subjectReg)[1]);
}
if (data.match(bodyReg)) {
obj.body = decode(data.match(bodyReg)[1]);
}
if (data.match(ccReg)) {
obj.cc = addressesToArray(decode(data.match(ccReg)[1]));
}
if (parts[1].match(bccReg)) {
obj.bcc = addressesToArray(decode(data.match(bccReg)[1]));
}
}
}
return obj;
}
return queryURI;
});
define('activity_composer_data', [
'require',
'exports',
'module',
'attachment_name',
'query_uri'
], function (require, exports, module) {
var attachmentName = require('attachment_name'), queryUri = require('query_uri');
return function activityComposeR(rawActivity) {
var source = rawActivity.source;
var data = source.data;
var activityName = source.name;
var dataType = data.type;
var url = data.url || data.URI;
var attachData;
if (dataType === 'url' && activityName === 'share') {
attachData = { body: url };
} else {
attachData = queryUri(url);
attachData.attachmentBlobs = data.blobs || [];
attachData.attachmentNames = data.filenames || [];
attachmentName.ensureNameList(attachData.attachmentBlobs, attachData.attachmentNames);
}
return {
onComposer: function (composer, composeCard) {
var attachmentBlobs = attachData.attachmentBlobs;
if (attachData.to) {
composer.to = attachData.to;
}
if (attachData.subject) {
composer.subject = attachData.subject;
}
if (attachData.body) {
composer.body = { text: attachData.body + composer.body.text };
}
if (attachData.cc) {
composer.cc = attachData.cc;
}
if (attachData.bcc) {
composer.bcc = attachData.bcc;
}
if (attachmentBlobs) {
var attachmentsToAdd = [];
for (var iBlob = 0; iBlob < attachmentBlobs.length; iBlob++) {
attachmentsToAdd.push({
name: attachData.attachmentNames[iBlob],
blob: attachmentBlobs[iBlob]
});
}
composeCard.addAttachmentsSubjectToSizeLimits(attachmentsToAdd);
}
}
};
};
});
define('cards_init', ['require'], function (require) {
return function cardsInit(cards) {
cards.pushDefaultCard = function (onPushed) {
require(['model_create'], function (modelCreate) {
cards.pushCard('message_list', 'none', {
model: modelCreate.defaultModel,
onPushed: onPushed
}, 'left');
});
};
};
});
define('html_cache', [
'require',
'exports',
'module',
'l10n!'
], function (require, exports) {
var mozL10n = require('l10n!');
exports.cloneAsInertNodeAvoidingCustomElementHorrors = function (node) {
var templateNode = document.createElement('template');
var cacheDoc = templateNode.content.ownerDocument;
return cacheDoc.importNode(node, true);
};
exports.save = function htmlCacheSave(moduleId, html) {
var id = exports.moduleIdToKey(moduleId);
var langDir = document.querySelector('html').getAttribute('dir');
html = window.HTML_CACHE_VERSION + (langDir ? ',' + langDir : '') + ':' + html;
localStorage.setItem('html_cache_' + id, html);
console.log('htmlCache.save ' + id + ': ' + html.length + ', lang dir: ' + langDir);
};
exports.reset = function () {
localStorage.clear();
var expiry = Date.now() + 20 * 365 * 24 * 60 * 60 * 1000;
expiry = new Date(expiry).toUTCString();
for (var i = 0; i < 40; i++) {
document.cookie = 'htmlc' + i + '=; expires=' + expiry;
}
console.log('htmlCache reset');
};
window.addEventListener('languagechange', exports.reset);
exports.moduleIdToKey = function moduleIdToKey(moduleId) {
return moduleId.replace(/^cards\//, '').replace(/-/g, '_');
};
exports.nodeToKey = function nodeToKey(node) {
return node.nodeName.toLowerCase().replace(/^cards-/, '').replace(/-/g, '_');
};
exports.cloneAndSave = function cloneAndSave(moduleId, node) {
var cachedNode = exports.cloneAsInertNodeAvoidingCustomElementHorrors(node);
mozL10n.translateFragment(cachedNode);
cachedNode.dataset.cached = 'cached';
exports.delayedSaveFromNode(moduleId, cachedNode);
};
exports.saveFromNode = function saveFromNode(moduleId, node) {
var cl = node.classList;
cl.remove('before');
cl.remove('after');
cl.add('center');
var nodes = node.querySelectorAll('.email-ce');
for (var i = 0; i < nodes.length; i++) {
nodes[i].dataset.cached = 'cached';
}
if (node.classList.contains('email-ce')) {
node.dataset.cached = 'cached';
}
var html = node.outerHTML;
exports.save(moduleId, html);
};
var delayedSaveId = 0;
var delayedNode = '';
exports.delayedSaveFromNode = function delayedSaveFromNode(moduleId, node) {
delayedNode = node;
if (!delayedSaveId) {
delayedSaveId = setTimeout(function () {
delayedSaveId = 0;
exports.saveFromNode(moduleId, delayedNode);
delayedNode = null;
}, 500);
}
};
});
define('tmpl', ['l10n!'], function (mozL10n) {
var tmpl = {
pluginBuilder: './tmpl_builder',
toDom: function (text) {
var temp = document.createElement('div');
temp.innerHTML = text;
var node = temp.children[0];
mozL10n.translateFragment(node);
return node;
},
load: function (id, require, onload, config) {
require(['text!' + id], function (text) {
var node = tmpl.toDom(text);
onload(node);
});
}
};
return tmpl;
});
define('tmpl!cards/toaster.html', ['tmpl'], function (tmpl) {
return tmpl.toDom('\n');
});
define('transition_end', [
'require',
'exports',
'module'
], function (require, exports, module) {
return function transitionEnd(node, fn, capturing) {
function asyncFn(event) {
Promise.resolve().then(function () {
fn(event);
}).catch(function (error) {
console.error(error);
});
}
node.addEventListener('transitionend', asyncFn, capturing);
return asyncFn;
};
});
define('toaster', [
'require',
'l10n!',
'tmpl!./cards/toaster.html',
'transition_end'
], function (require) {
var mozL10n = require('l10n!');
var toasterNode = require('tmpl!./cards/toaster.html');
var transitionEnd = require('transition_end');
var toaster = {
defaultTimeout: 5000,
_previousActionClass: undefined,
init: function (parentEl) {
this.el = toasterNode;
parentEl.appendChild(this.el);
this.text = this.el.querySelector('.toaster-text');
this.actionButton = this.el.querySelector('.toaster-action');
this.el.addEventListener('click', this.hide.bind(this));
transitionEnd(this.el, this.hide.bind(this));
this.el.querySelector('.toaster-action-target').addEventListener('click', this.onAction.bind(this));
this.currentToast = null;
},
toastOperation: function (op) {
if (!op || !op.affectedCount) {
return;
}
var type = op.operation;
var canUndo = op.undo && type !== 'move' && type !== 'delete';
this.toast({
text: mozL10n.get('toaster-message-' + type, { n: op.affectedCount }),
actionLabel: mozL10n.get('toaster-undo'),
actionClass: 'undo',
action: canUndo && op.undo.bind(op)
});
},
onAction: function () {
var actionFunction = this.currentToast && this.currentToast.action;
this.hide();
if (actionFunction) {
actionFunction();
}
},
toast: function (opts) {
opts = opts || {};
console.log('Showing toast:', JSON.stringify(opts));
this.hide();
this.currentToast = opts;
this.text.textContent = opts.text;
this.actionButton.textContent = opts.actionLabel;
if (this._previousActionClass) {
this.actionButton.classList.remove(this._previousActionClass);
this._previousActionClass = undefined;
}
if (opts.actionClass) {
this._previousActionClass = opts.actionClass;
this.actionButton.classList.add(this._previousActionClass);
}
this.el.classList.toggle('actionable', !opts.action);
this.actionButton.disabled = !opts.action;
this.el.classList.remove('collapsed');
this._fadeTimeout = setTimeout(function () {
this.el.classList.add('fadeout');
}.bind(this), opts.timeout || this.defaultTimeout);
},
isShowing: function () {
return !this.el.classList.contains('collapsed');
},
hide: function () {
this.currentToast = null;
this.el.classList.add('collapsed');
this.el.classList.remove('fadeout');
window.clearTimeout(this._fadeTimeout);
this._fadeTimeout = null;
}
};
return toaster;
});
define('input_areas', [
'require',
'exports',
'module'
], function (require, exports) {
var slice = Array.prototype.slice;
return function hookupInputAreaResetButtons(e) {
var selector = 'form p input + button[type="reset"],' + 'form p textarea + button[type="reset"]';
var resetButtons = slice.call(e.querySelectorAll(selector));
resetButtons.forEach(function (resetButton) {
resetButton.addEventListener('mousedown', function (e) {
e.preventDefault();
});
resetButton.addEventListener('click', function (e) {
e.target.previousElementSibling.value = '';
e.preventDefault();
});
});
};
});
define('cards', [
'require',
'exports',
'module',
'cards_init',
'html_cache',
'l10n!',
'evt',
'toaster',
'transition_end',
'input_areas'
], function (require, exports, module) {
var cardsInit = require('cards_init'), htmlCache = require('html_cache'), mozL10n = require('l10n!'), evt = require('evt'), toaster = require('toaster'), transitionEnd = require('transition_end'), hookupInputAreaResetButtons = require('input_areas');
function addClass(domNode, name) {
if (domNode) {
domNode.classList.add(name);
}
}
function removeClass(domNode, name) {
if (domNode) {
domNode.classList.remove(name);
}
}
var cards = {
_cardDefs: {},
_cardStack: [],
activeCardIndex: -1,
_pendingPush: null,
_zIndex: 0,
_rootNode: null,
_containerNode: null,
_cardsNode: null,
_animatingDeadDomNodes: [],
_transitionCount: 0,
_startupEventsEmitted: false,
_popupActive: null,
_eatingEventsUntilNextCard: false,
init: function () {
this._rootNode = document.body;
this._containerNode = document.getElementById('cardContainer');
this._cardsNode = document.getElementById('cards');
this._statusColorMeta = document.querySelector('meta[name="theme-color"]');
toaster.init(this._containerNode);
this._containerNode.addEventListener('click', this._onMaybeIntercept.bind(this), true);
transitionEnd(this._cardsNode, this._onTransitionEnd.bind(this), false);
document.addEventListener('visibilitychange', function (evt) {
var card = this._cardStack[this.activeCardIndex];
if (card && card.onCurrentCardDocumentVisibilityChange) {
card.onCurrentCardDocumentVisibilityChange(document.hidden);
}
}.bind(this));
cardsInit(this);
},
_onMaybeIntercept: function (event) {
if (this._eatingEventsUntilNextCard) {
event.stopPropagation();
event.preventDefault();
return;
}
if (this._popupActive) {
event.stopPropagation();
event.preventDefault();
this._popupActive.close();
return;
}
var cardNode = event.target;
for (cardNode = event.target; cardNode; cardNode = cardNode.parentElement) {
if (cardNode.classList.contains('card')) {
break;
}
}
},
pushCard: function (type, showMethod, args, placement) {
var cardDef = this._cardDefs[type];
args = args || {};
if (!cardDef) {
var cbArgs = Array.slice(arguments);
this._pendingPush = [type];
if (showMethod !== 'none') {
this.eatEventsUntilNextCard();
}
require(['element!cards/' + type], function (Ctor) {
this._cardDefs[type] = Ctor;
this.pushCard.apply(this, cbArgs);
}.bind(this));
return;
}
this._pendingPush = null;
console.log('pushCard for type: ' + type);
var domNode = args.cachedNode || new cardDef();
if (args && domNode.onArgs) {
domNode.onArgs(args);
}
var cardIndex, insertBuddy;
if (!placement) {
cardIndex = this._cardStack.length;
insertBuddy = null;
domNode.classList.add(cardIndex === 0 ? 'before' : 'after');
} else if (placement === 'left') {
cardIndex = this.activeCardIndex++;
insertBuddy = this._cardsNode.children[cardIndex];
domNode.classList.add('before');
} else if (placement === 'right') {
cardIndex = this.activeCardIndex + 1;
if (cardIndex >= this._cardStack.length) {
insertBuddy = null;
} else {
insertBuddy = this._cardsNode.children[cardIndex];
}
domNode.classList.add('after');
}
this._cardStack.splice(cardIndex, 0, domNode);
if (!args.cachedNode) {
this._cardsNode.insertBefore(domNode, insertBuddy);
}
hookupInputAreaResetButtons(domNode);
if (!domNode.callHeaderFontSize) {
window.dispatchEvent(new CustomEvent('lazyload', { detail: domNode }));
}
if ('postInsert' in domNode) {
domNode.postInsert();
}
if (showMethod !== 'none') {
if (!args.cachedNode) {
domNode.clientWidth;
}
this._showCard(cardIndex, showMethod, 'forward');
}
if (args.onPushed) {
args.onPushed(domNode);
}
},
pushOrTellCard: function (type, showMethod, args, placement) {
var query = type;
if (this.hasCard(query)) {
this.tellCard(query, args);
return false;
} else {
this.pushCard.apply(this, Array.slice(arguments));
return true;
}
},
setStatusColor: function (element) {
var color;
if (!element) {
element = this._cardStack[this.activeCardIndex];
}
var statusElement = element.dataset.statuscolor ? element : element.querySelector('[data-statuscolor]');
if (statusElement) {
color = statusElement.dataset.statuscolor;
if (color === 'default') {
color = null;
} else if (color === 'background') {
color = getComputedStyle(statusElement).backgroundColor;
}
} else {
color = getComputedStyle(element).backgroundColor;
}
if (color && color.indexOf('rgb') !== 0 && color.indexOf('#') !== 0) {
color = null;
}
color = color || this._statusColorMeta.dataset.statuscolor;
var existingColor = this._statusColorMeta.getAttribute('content');
if (color !== existingColor) {
this._statusColorMeta.setAttribute('content', color);
}
},
_findCardUsingType: function (type) {
for (var i = 0; i < this._cardStack.length; i++) {
var domNode = this._cardStack[i];
if (htmlCache.nodeToKey(domNode) === type) {
return i;
}
}
},
_findCard: function (query, skipFail) {
var result;
if (typeof query === 'string') {
result = this._findCardUsingType(query, skipFail);
} else if (typeof query === 'number') {
result = query;
} else {
result = this._cardStack.indexOf(query);
}
if (result > -1) {
return result;
} else if (!skipFail) {
throw new Error('Unable to find card with query:', query);
} else {
return undefined;
}
},
hasCard: function (query) {
if (this._pendingPush && this._pendingPush === query) {
return true;
}
return this._findCard(query, true) > -1;
},
isVisible: function (domNode) {
return !!(domNode && domNode.classList.contains('center'));
},
findCardObject: function (query) {
return this._cardStack[this._findCard(query)];
},
getCurrentCardType: function () {
var result = null, card = this._cardStack[this.activeCardIndex];
if (this._pendingPush) {
result = this._pendingPush;
} else if (card) {
result = htmlCache.nodeToKey(card);
}
return result;
},
folderSelector: function (model, callback, filter) {
var self = this;
require(['value_selector'], function (ValueSelector) {
if (!self.folderPrompt) {
var selectorTitle = mozL10n.get('messages-folder-select');
self.folderPrompt = new ValueSelector(selectorTitle);
}
model.latestOnce('foldersSlice', function (foldersSlice) {
var folders = foldersSlice.items;
folders.forEach(function (folder) {
var isMatch = !filter || filter(folder);
if (folder.neededForHierarchy || isMatch) {
self.folderPrompt.addToList(folder.name, folder.depth, isMatch, function (folder) {
return function () {
self.folderPrompt.hide();
callback(folder);
};
}(folder));
}
});
self.folderPrompt.show();
});
});
},
moveToCard: function (query, showMethod) {
this._showCard(this._findCard(query), showMethod || 'animate');
},
tellCard: function (query, what) {
var cardIndex = this._findCard(query), domNode = this._cardStack[cardIndex];
if (!('told' in domNode)) {
console.warn('Tried to tell a card that\'s not listening!', query, what);
} else {
domNode.told(what);
}
},
removeCardAndSuccessors: function (cardDomNode, showMethod, numCards, nextCardSpec, skipDefault) {
if (!this._cardStack.length) {
return;
}
if (cardDomNode && this._cardStack.length === 1 && !skipDefault) {
return cards.pushDefaultCard(function () {
this.removeCardAndSuccessors(cardDomNode, showMethod, numCards, nextCardSpec);
}.bind(this));
}
var firstIndex, iCard, domNode;
if (cardDomNode === undefined) {
throw new Error('undefined is not a valid card spec!');
} else if (cardDomNode === null) {
firstIndex = 0;
this._zIndex = 0;
} else {
for (iCard = this._cardStack.length - 1; iCard >= 0; iCard--) {
domNode = this._cardStack[iCard];
if (domNode === cardDomNode) {
firstIndex = iCard;
break;
}
}
if (firstIndex === undefined) {
throw new Error('No card represented by that DOM node');
}
}
if (!numCards) {
numCards = this._cardStack.length - firstIndex;
}
if (showMethod === 'none') {
if (cardDomNode && cardDomNode.classList.contains('anim-overlay')) {
this._zIndex -= 10;
}
} else {
var nextCardIndex = -1;
if (nextCardSpec) {
nextCardIndex = this._findCard(nextCardSpec);
} else if (this._cardStack.length) {
nextCardIndex = Math.min(firstIndex - 1, this._cardStack.length - 1);
}
if (nextCardIndex > -1) {
this._showCard(nextCardIndex, showMethod, 'back');
}
}
if (firstIndex <= this.activeCardIndex) {
this.activeCardIndex -= numCards;
if (this.activeCardIndex < -1) {
this.activeCardIndex = -1;
}
}
var deadDomNodes = this._cardStack.splice(firstIndex, numCards);
for (iCard = 0; iCard < deadDomNodes.length; iCard++) {
domNode = deadDomNodes[iCard];
try {
domNode.die();
} catch (ex) {
console.warn('Problem cleaning up card:', ex, '\n', ex.stack);
}
switch (showMethod) {
case 'animate':
case 'immediate':
this._animatingDeadDomNodes.push(domNode);
break;
case 'none':
domNode.parentNode.removeChild(domNode);
break;
}
}
this._setScreenReaderVisibility();
},
removeAllCards: function () {
return this.removeCardAndSuccessors(null, 'none');
},
_showCard: function (cardIndex, showMethod, navDirection) {
if (cardIndex === this.activeCardIndex) {
return;
}
var activeElement = document.activeElement;
if (activeElement && activeElement.blur) {
activeElement.blur();
}
if (cardIndex > this._cardStack.length - 1) {
cardIndex = this._cardStack.length - 1;
}
if (this.activeCardIndex > this._cardStack.length - 1) {
this.activeCardIndex = -1;
}
if (this.activeCardIndex === -1) {
this.activeCardIndex = cardIndex === 0 ? cardIndex : cardIndex - 1;
}
var domNode = cardIndex !== null ? this._cardStack[cardIndex] : null;
var beginNode = this._cardStack[this.activeCardIndex];
var endNode = this._cardStack[cardIndex];
var isForward = navDirection === 'forward';
if (this._cardStack.length === 1) {
this._zIndex = 0;
}
if (isForward && endNode.classList.contains('anim-overlay')) {
beginNode = null;
this._zIndex += 10;
}
if (beginNode && beginNode.classList.contains('anim-overlay')) {
if (isForward) {
if (showMethod !== 'immediate') {
if (beginNode.classList.contains('anim-vertical')) {
removeClass(beginNode, 'anim-vertical');
addClass(beginNode, 'disabled-anim-vertical');
} else if (beginNode.classList.contains('anim-fade')) {
removeClass(beginNode, 'anim-fade');
addClass(beginNode, 'disabled-anim-fade');
}
}
} else {
this.setStatusColor(endNode);
endNode = null;
this._zIndex -= 10;
}
}
if (endNode && isForward && this._zIndex) {
endNode.style.zIndex = this._zIndex;
}
var cardsNode = this._cardsNode;
if (endNode) {
this.setStatusColor(endNode);
}
if (showMethod === 'immediate') {
addClass(beginNode, 'no-anim');
addClass(endNode, 'no-anim');
cardsNode.clientWidth;
this._eatingEventsUntilNextCard = false;
} else if (showMethod === 'none') {
} else {
this._transitionCount = beginNode && endNode ? 2 : 1;
this._eatingEventsUntilNextCard = true;
}
if (this.activeCardIndex === cardIndex) {
removeClass(beginNode, 'before');
removeClass(beginNode, 'after');
addClass(beginNode, 'center');
} else if (this.activeCardIndex > cardIndex) {
removeClass(beginNode, 'center');
addClass(beginNode, 'after');
removeClass(endNode, 'before');
addClass(endNode, 'center');
} else {
removeClass(beginNode, 'center');
addClass(beginNode, 'before');
removeClass(endNode, 'after');
addClass(endNode, 'center');
}
if (showMethod === 'immediate') {
cardsNode.clientWidth;
removeClass(beginNode, 'no-anim');
removeClass(endNode, 'no-anim');
this._onCardVisible(domNode);
}
toaster.hide();
this.activeCardIndex = cardIndex;
this._setScreenReaderVisibility();
},
_setScreenReaderVisibility: function () {
this._cardStack.forEach(function (card, index) {
card.setAttribute('aria-hidden', index !== this.activeCardIndex);
}, this);
},
_onTransitionEnd: function (event) {
if (!event.target.classList.contains('card')) {
return;
}
var activeCard = this._cardStack[this.activeCardIndex];
if (!activeCard) {
return;
}
if (this._transitionCount > 0) {
this._transitionCount -= 1;
}
if (this._transitionCount === 0) {
if (this._eatingEventsUntilNextCard) {
this._eatingEventsUntilNextCard = false;
}
if (this._animatingDeadDomNodes.length) {
setTimeout(function () {
this._animatingDeadDomNodes.forEach(function (domNode) {
if (domNode.parentNode) {
domNode.parentNode.removeChild(domNode);
}
});
this._animatingDeadDomNodes = [];
}.bind(this), 100);
}
var endNode = activeCard;
if (endNode.classList.contains('disabled-anim-vertical')) {
removeClass(endNode, 'disabled-anim-vertical');
addClass(endNode, 'anim-vertical');
} else if (endNode.classList.contains('disabled-anim-fade')) {
removeClass(endNode, 'disabled-anim-fade');
addClass(endNode, 'anim-fade');
}
if (this._afterTransitionAction) {
var afterTransitionAction = this._afterTransitionAction;
this._afterTransitionAction = null;
afterTransitionAction();
}
this._onCardVisible(activeCard);
var nextCards = activeCard.nextCards;
if (nextCards) {
console.log('Preloading cards: ' + nextCards);
require(nextCards.map(function (id) {
return 'cards/' + id;
}));
}
}
},
_onCardVisible: function (domNode) {
if (domNode.onCardVisible) {
domNode.onCardVisible();
}
this._emitStartupEvents(domNode.skipEmitContentEvents);
},
_emitStartupEvents: function (skipEmitContentEvents) {
if (!this._startupEventsEmitted) {
if (window.startupCacheEventsSent) {
window.performance.mark('contentInteractive');
} else {
window.performance.mark('navigationLoaded');
}
window.performance.mark('navigationInteractive');
if (!skipEmitContentEvents) {
evt.emit('metrics:contentDone');
}
this._startupEventsEmitted = true;
}
},
eatEventsUntilNextCard: function () {
this._eatingEventsUntilNextCard = true;
},
stopEatingEvents: function () {
this._eatingEventsUntilNextCard = false;
},
assertNoCards: function () {
if (this._cardStack.length) {
throw new Error('There are ' + this._cardStack.length + ' cards but' + ' there should be ZERO');
}
}
};
return cards;
});
define('array', ['require'], function (require) {
var array = {
indexOfGeneric: function (array, callback, thisObject) {
var result = -1;
array.some(function (value, index) {
if (callback.call(thisObject, value)) {
result = index;
return true;
}
});
return result;
}
};
return array;
});
define('header_cursor', [
'require',
'array',
'evt'
], function (require) {
var array = require('array'), evt = require('evt');
function makeListener(type, obj) {
return function () {
var args = Array.slice(arguments);
this.emit.apply(this, ['messages_' + type].concat(args));
}.bind(obj);
}
function HeaderCursor(model) {
evt.Emitter.call(this);
this.model = model;
this.onLatestFolder = this.onLatestFolder.bind(this);
this.searchMode = 'nonsearch';
}
HeaderCursor.prototype = evt.mix({
currentMessage: null,
messagesSlice: null,
expectingMessageSuid: null,
sliceEvents: [
'splice',
'change',
'status',
'remove',
'complete'
],
_inited: false,
init: function () {
this._inited = true;
this.on('messages_splice', this.onMessagesSplice.bind(this));
this.on('messages_remove', this.onMessagesSpliceRemove.bind(this));
this.on('messages_complete', function () {
if (this.messagesSlice) {
this.messagesSlice.oncomplete = makeListener('complete', this);
}
}.bind(this));
this.model.latest('folder', this.onLatestFolder);
},
advance: function (direction) {
var index = this.indexOfMessageById(this.currentMessage.header.id);
switch (direction) {
case 'previous':
index -= 1;
break;
case 'next':
index += 1;
break;
}
var messages = this.messagesSlice.items;
if (index < 0 || index >= messages.length) {
return;
}
this.setCurrentMessageByIndex(index);
},
setCurrentMessageBySuid: function (messageSuid) {
this.expectingMessageSuid = messageSuid;
this.checkExpectingMessageSuid();
},
checkExpectingMessageSuid: function (eventIfNotFound) {
var messageSuid = this.expectingMessageSuid;
var model = this.model;
if (!messageSuid || !model.folder || model.folder.type !== 'inbox') {
return;
}
var index = this.indexOfMessageById(messageSuid);
if (index > -1) {
this.expectingMessageSuid = null;
return this.setCurrentMessageByIndex(index);
}
if (eventIfNotFound) {
console.error('header_cursor could not find messageSuid ' + messageSuid + ', emitting messageSuidNotFound');
this.emit('messageSuidNotFound', messageSuid);
}
},
setCurrentMessage: function (header) {
if (!header) {
return;
}
this.setCurrentMessageByIndex(this.indexOfMessageById(header.id));
},
setCurrentMessageByIndex: function (index) {
var messages = this.messagesSlice.items;
if (index === -1 || index > messages.length - 1) {
return;
}
var header = messages[index];
if ('header' in header) {
header = header.header;
}
var currentMessage = new CurrentMessage(header, {
hasPrevious: index !== 0,
hasNext: index !== messages.length - 1
});
this.emit('currentMessage', currentMessage, index);
this.currentMessage = currentMessage;
},
indexOfMessageById: function (id) {
var messages = this.messagesSlice && this.messagesSlice.items || [];
return array.indexOfGeneric(messages, function (message) {
var other = 'header' in message ? message.header.id : message.id;
return other === id;
});
},
onLatestFolder: function (folder) {
if (!this.model.foldersSlice) {
return;
}
this.freshMessagesSlice();
},
startSearch: function (phrase, whatToSearch) {
this.searchMode = 'search';
this.bindToSlice(this.model.api.searchFolderMessages(this.model.folder, phrase, whatToSearch));
},
endSearch: function () {
this.resetMessageSlice();
this.searchMode = 'nonsearch';
this.freshMessagesSlice();
},
freshMessagesSlice: function () {
this.bindToSlice(this.model.api.viewFolderMessages(this.model.folder));
},
bindToSlice: function (messagesSlice) {
this.resetMessageSlice();
this.messagesSlice = messagesSlice;
this.sliceEvents.forEach(function (type) {
messagesSlice['on' + type] = makeListener(type, this);
}.bind(this));
},
onMessagesSplice: function (index, howMany, addedItems, requested, moreExpected) {
if (!this.messagesSlice) {
return;
}
if (this.messagesSlice.atTop && this.expectingMessageSuid && this.messagesSlice.items && this.messagesSlice.items.length) {
this.checkExpectingMessageSuid(true);
}
},
onMessagesSpliceRemove: function (removedHeader, removedFromIndex) {
if (this.currentMessage !== removedHeader) {
return this.setCurrentMessage(this.currentMessage);
}
var messages = this.messagesSlice.items;
if (messages.length === 0) {
return this.currentMessage = null;
}
var index = Math.min(removedFromIndex, messages.length - 1);
var message = this.messagesSlice.items[index];
this.setCurrentMessage(message);
},
resetMessageSlice: function () {
if (this.messagesSlice) {
this.messagesSlice.die();
this.messagesSlice = null;
}
this.currentMessage = null;
},
die: function () {
this.model.removeListener('folder', this.onLatestFolder);
this.resetMessageSlice();
}
});
var oldOn = HeaderCursor.prototype.on;
HeaderCursor.prototype.on = function () {
if (!this._inited) {
this.init();
}
return oldOn.apply(this, arguments);
};
function CurrentMessage(header, siblings) {
this.header = header;
this.siblings = siblings;
}
CurrentMessage.prototype = {
header: null,
siblings: null
};
HeaderCursor.CurrentMessage = CurrentMessage;
return HeaderCursor;
});
(function (exports) {
'use strict';
const HEADER_SIZES = [
16,
17,
18,
19,
20,
21,
22,
23
];
var FontSizeUtils = {
_cachedContexts: {},
_getCachedContext: function (fontSize, fontFamily, fontStyle) {
fontStyle = fontStyle || 'italic';
var cache = this._cachedContexts;
var ctx = cache[fontSize] && cache[fontSize][fontFamily] ? cache[fontSize][fontFamily][fontStyle] : null;
if (!ctx) {
var canvas = document.createElement('canvas');
canvas.setAttribute('moz-opaque', 'true');
canvas.setAttribute('width', '1');
canvas.setAttribute('height', '1');
ctx = canvas.getContext('2d', { willReadFrequently: true });
ctx.font = fontStyle + ' ' + fontSize + 'px ' + fontFamily;
if (!cache[fontSize]) {
cache[fontSize] = {};
}
if (!cache[fontSize][fontFamily]) {
cache[fontSize][fontFamily] = {};
}
cache[fontSize][fontFamily][fontStyle] = ctx;
}
return ctx;
},
resetCache: function () {
this._cachedContexts = {};
},
_textChangeObserver: null,
_handleTextChanges: function (mutations) {
for (var i = 0; i < mutations.length; i++) {
this._reformatHeaderText(mutations[i].target);
}
},
_getTextChangeObserver: function () {
if (!this._textChangeObserver) {
this._textChangeObserver = new MutationObserver(this._handleTextChanges.bind(this));
}
return this._textChangeObserver;
},
_observeHeaderChanges: function (element) {
var observer = this._getTextChangeObserver();
observer.observe(element, { childList: true });
},
_reformatHeaderText: function (header) {
if (header.textContent.trim() === '') {
return;
}
this.resetCentering(header);
var style = this.getStyleProperties(header);
style.textWidth = this.autoResizeElement(header, style);
this.centerTextToScreen(header, style);
},
_registerHeadersInSubtree: function (domNode) {
if (!domNode) {
return;
}
var headers = domNode.querySelectorAll('header > h1');
for (var i = 0; i < headers.length; i++) {
window.requestAnimationFrame(function (header) {
this._reformatHeaderText(header);
this._observeHeaderChanges(header);
}.bind(this, headers[i]));
}
},
getFontWidth: function (string, fontSize, fontFamily, fontStyle) {
var ctx = this._getCachedContext(fontSize, fontFamily, fontStyle);
return ctx.measureText(string).width;
},
getMaxFontSizeInfo: function (string, allowedSizes, fontFamily, maxWidth) {
var fontSize;
var resultWidth;
var i = allowedSizes.length - 1;
do {
fontSize = allowedSizes[i];
resultWidth = this.getFontWidth(string, fontSize, fontFamily);
i--;
} while (resultWidth > maxWidth && i >= 0);
return {
fontSize: fontSize,
overflow: resultWidth > maxWidth,
textWidth: resultWidth
};
},
_overflowCountCache: -1,
getOverflowCount: function (string, fontSize, fontFamily, maxWidth) {
var substring;
var resultWidth;
var overflowCount = -1;
if (string.length > this._overflowCountCache) {
substring = string.substr(0, string.length - this._overflowCountCache);
resultWidth = this.getFontWidth(substring, fontSize, fontFamily);
if (resultWidth > maxWidth) {
overflowCount = this._overflowCountCache;
}
}
do {
overflowCount++;
substring = string.substr(0, string.length - overflowCount);
resultWidth = this.getFontWidth(substring, fontSize, fontFamily);
} while (substring.length > 0 && resultWidth > maxWidth);
this._overflowCountCache = overflowCount;
return overflowCount;
},
getAllowedSizes: function (element) {
if (element.tagName === 'H1' && element.parentNode.tagName === 'HEADER') {
return HEADER_SIZES;
}
return [];
},
getContentWidth: function (style) {
var width = parseInt(style.width, 10);
if (style.boxSizing === 'border-box') {
width -= parseInt(style.paddingRight, 10) + parseInt(style.paddingLeft, 10);
}
return width;
},
getStyleProperties: function (element) {
var style = window.getComputedStyle(element);
var contentWidth = this.getContentWidth(style);
if (isNaN(contentWidth)) {
contentWidth = 0;
}
return {
fontFamily: style.fontFamily,
contentWidth: contentWidth,
paddingRight: parseInt(style.paddingRight, 10),
paddingLeft: parseInt(style.paddingLeft, 10),
offsetLeft: element.offsetLeft
};
},
autoResizeElement: function (element, styleOptions) {
var allowedSizes = this.getAllowedSizes(element);
if (allowedSizes.length === 0) {
return 0;
}
var contentWidth = styleOptions.contentWidth || this.getContentWidth(element);
var fontFamily = styleOptions.fontFamily || getComputedStyle(element).fontFamily;
var info = this.getMaxFontSizeInfo(element.textContent.trim(), allowedSizes, fontFamily, contentWidth);
element.style.fontSize = info.fontSize + 'px';
return info.textWidth;
},
resetCentering: function (element) {
element.style.marginLeft = element.style.marginRight = '0';
},
centerTextToScreen: function (element, styleOptions) {
var minHeaderWidth = styleOptions.textWidth + styleOptions.paddingRight + styleOptions.paddingLeft;
var sideSpaceLeft = styleOptions.offsetLeft;
var sideSpaceRight = this.getWindowWidth() - sideSpaceLeft - styleOptions.contentWidth - styleOptions.paddingRight - styleOptions.paddingLeft;
if (sideSpaceLeft === sideSpaceRight) {
return;
}
var margin = Math.max(sideSpaceLeft, sideSpaceRight);
if (minHeaderWidth + margin * 2 < this.getWindowWidth() - 1) {
element.style.marginLeft = element.style.marginRight = margin + 'px';
}
},
_initHeaderFormatting: function () {
if (navigator.mozL10n) {
navigator.mozL10n.once(function () {
this._registerHeadersInSubtree(document.body);
}.bind(this));
} else {
this._registerHeadersInSubtree(document.body);
}
},
init: function () {
window.addEventListener('lazyload', function (evt) {
this._registerHeadersInSubtree(evt.detail);
}.bind(this));
if (document.readyState === 'loading') {
window.addEventListener('DOMContentLoaded', function () {
this._initHeaderFormatting();
}.bind(this));
} else {
this._initHeaderFormatting();
}
},
getWindowWidth: function () {
return window.innerWidth;
}
};
FontSizeUtils.init();
exports.FontSizeUtils = FontSizeUtils;
}(this));
define('font_size_utils', function () {
});
define('metrics', [
'require',
'evt'
], function (require) {
var evt = require('evt'), apiDone = false, contentDone = false;
function checkAppLoaded() {
if (apiDone && contentDone) {
window.performance.mark('fullyLoaded');
}
}
evt.once('metrics:apiDone', function onApiDone() {
apiDone = true;
checkAppLoaded();
});
evt.once('metrics:contentDone', function () {
contentDone = true;
if (!window.startupCacheEventsSent) {
window.performance.mark('visuallyLoaded');
window.performance.mark('contentInteractive');
}
checkAppLoaded();
});
});
define('wake_locks', [
'require',
'evt'
], function (require) {
'use strict';
var lockTimeouts = {}, evt = require('evt'), allLocks = {}, dataOps = {}, dataOpsTimeoutId = 0, maxLockInterval = 45000, dataOpsTimeout = 5000;
function close() {
dataOps = {};
dataOpsTimeoutId = 0;
if (document.hidden) {
console.log('email: cronsync wake locks expired, force closing app');
window.close();
} else {
console.log('email: cronsync wake locks expired, but app visible, ' + 'not force closing');
Object.keys(allLocks).forEach(function (accountKey) {
clearLocks(accountKey);
});
}
}
function closeIfNoDataOps() {
var dataOpsKeys = Object.keys(dataOps);
if (!dataOpsKeys.length) {
return close();
}
console.log('email: cronsync wake lock force shutdown waiting on email ' + 'data operations: ' + dataOpsKeys.join(', '));
dataOpsTimeoutId = setTimeout(close, dataOpsTimeout);
}
evt.on('uiDataOperationStart', function (dataId) {
dataOps[dataId] = true;
});
evt.on('uiDataOperationStop', function (dataId) {
delete dataOps[dataId];
if (dataOpsTimeoutId && !Object.keys(dataOps).length) {
clearTimeout(dataOpsTimeoutId);
close();
}
});
function clearLocks(accountKey) {
console.log('email: clearing wake locks for "' + accountKey + '"');
var lockTimeoutId = lockTimeouts[accountKey];
if (lockTimeoutId) {
clearTimeout(lockTimeoutId);
}
lockTimeouts[accountKey] = 0;
var locks = allLocks[accountKey];
allLocks[accountKey] = null;
if (locks) {
locks.forEach(function (lock) {
lock.unlock();
});
}
}
function makeAccountKey(accountIds) {
return 'id' + accountIds.join(' ');
}
function onCronStop(accountIds) {
clearLocks(makeAccountKey(accountIds));
}
evt.on('cronSyncWakeLocks', function (accountKey, locks) {
if (lockTimeouts[accountKey]) {
clearLocks(accountKey);
}
allLocks[accountKey] = locks;
lockTimeouts[accountKey] = setTimeout(closeIfNoDataOps, maxLockInterval);
});
evt.on('cronSyncStop', onCronStop);
});
define('mail_app', [
'require',
'exports',
'module',
'l10n!',
'activity_composer_data',
'cards',
'evt',
'model_create',
'header_cursor',
'html_cache',
'font_size_utils',
'metrics',
'wake_locks'
], function (require, exports, module) {
var mozL10n = require('l10n!'), activityComposerData = require('activity_composer_data'), cards = require('cards'), evt = require('evt'), model = require('model_create').defaultModel, HeaderCursor = require('header_cursor'), htmlCache = require('html_cache'), waitingRawActivity, activityCallback;
require('font_size_utils');
require('metrics');
require('wake_locks');
var started = false;
function pushStartCard(id, addedArgs) {
var args = { model: model };
if (addedArgs) {
Object.keys(addedArgs).forEach(function (key) {
args[key] = addedArgs[key];
});
}
if (!started) {
var cachedNode = cards._cardsNode.children[0];
if (cachedNode && id === htmlCache.nodeToKey(cachedNode)) {
mozL10n.translateFragment(cachedNode);
args.cachedNode = cachedNode;
}
document.body.classList.add('content-visible');
}
cards.pushCard(id, 'immediate', args);
started = true;
}
document.addEventListener('visibilitychange', function onVisibilityChange() {
if (!document.hidden && !started && startupData && startupData.entry === 'alarm') {
pushStartCard('message_list');
}
}, false);
function isCurrentCardMessageList() {
var cardType = cards.getCurrentCardType();
return cardType && cardType === 'message_list';
}
evt.on('addAccount', function () {
cards.removeAllCards();
pushStartCard('setup_account_info', { allowBack: true });
});
function resetApp() {
activityCallback = waitingRawActivity = undefined;
cards.removeAllCards();
model.init(false, function () {
var cardId = model.hasAccount() ? 'message_list' : 'setup_account_info';
pushStartCard(cardId);
});
}
evt.on('accountDeleted', resetApp);
evt.on('resetApp', resetApp);
evt.on('setupAccountCanceled', function (fromCard) {
if (waitingRawActivity) {
waitingRawActivity.postError('cancelled');
}
if (!model.foldersSlice) {
evt.emit('resetApp');
} else {
cards.removeCardAndSuccessors(fromCard, 'animate', 1);
}
});
evt.on('showLatestAccount', function () {
cards.removeAllCards();
model.latestOnce('acctsSlice', function (acctsSlice) {
var account = acctsSlice.items[acctsSlice.items.length - 1];
model.changeAccount(account, function () {
pushStartCard('message_list', {
onPushed: function () {
if (activityCallback) {
var activityCb = activityCallback;
activityCallback = null;
activityCb();
return true;
}
return false;
}
});
});
});
});
evt.on('apiBadLogin', function (account, problem, whichSide) {
switch (problem) {
case 'bad-user-or-pass':
cards.pushCard('setup_fix_password', 'animate', {
account: account,
whichSide: whichSide,
restoreCard: cards.activeCardIndex
}, 'right');
break;
case 'imap-disabled':
case 'pop3-disabled':
cards.pushCard('setup_fix_gmail', 'animate', {
account: account,
restoreCard: cards.activeCardIndex
}, 'right');
break;
case 'needs-app-pass':
cards.pushCard('setup_fix_gmail_twofactor', 'animate', {
account: account,
restoreCard: cards.activeCardIndex
}, 'right');
break;
case 'needs-oauth-reauth':
cards.pushCard('setup_fix_oauth2', 'animate', {
account: account,
restoreCard: cards.activeCardIndex
}, 'right');
break;
}
});
cards.init();
if (!model.inited) {
model.init();
}
var startupData = globalOnAppMessage({
activity: function (rawActivity) {
if (!isCurrentCardMessageList()) {
cards.removeAllCards();
}
function activityCompose() {
var cardArgs = {
activity: rawActivity,
composerData: activityComposerData(rawActivity)
};
pushStartCard('compose', cardArgs);
}
if (globalOnAppMessage.hasAccount()) {
activityCompose();
} else {
activityCallback = activityCompose;
waitingRawActivity = rawActivity;
pushStartCard('setup_account_info', {
allowBack: true,
launchedFromActivity: true
});
}
},
notification: function (data) {
data = data || {};
var type = data.type || '';
var folderType = data.folderType || 'inbox';
model.latestOnce('foldersSlice', function latestFolderSlice() {
function onCorrectFolder() {
if (!isCurrentCardMessageList()) {
cards.removeAllCards();
}
if (type === 'message_list') {
pushStartCard('message_list', {});
} else if (type === 'message_reader') {
var headerCursor = new HeaderCursor(model);
headerCursor.setCurrentMessageBySuid(data.messageSuid);
pushStartCard(type, {
messageSuid: data.messageSuid,
headerCursor: headerCursor
});
} else {
console.error('unhandled notification type: ' + type);
}
}
var acctsSlice = model.acctsSlice, accountId = data.accountId;
if (model.account.id === accountId) {
return model.selectFirstFolderWithType(folderType, onCorrectFolder);
} else {
var newAccount;
acctsSlice.items.some(function (account) {
if (account.id === accountId) {
newAccount = account;
return true;
}
});
if (newAccount) {
model.changeAccount(newAccount, function () {
model.selectFirstFolderWithType(folderType, onCorrectFolder);
});
}
}
});
}
});
console.log('startupData: ' + JSON.stringify(startupData, null, ' '));
if (startupData.entry === 'default' || startupData.entry === 'alarm' && !document.hidden) {
pushStartCard(startupData.view);
}
});
if (typeof TestUrlResolver === 'undefined') {
requirejs.config({
waitSeconds: 0,
baseUrl: 'js',
paths: {
l10nbase: '../shared/js/l10n',
moz_intl: '../shared/js/moz_intl',
style: '../style',
shared: '../shared'
},
map: { '*': { 'api': 'ext/main-frame-setup' } },
shim: {
moz_intl: {
deps: ['l10nbase'],
exports: 'mozIntl'
},
'shared/js/mime_mapper': { exports: 'MimeMapper' },
'shared/js/notification_helper': { exports: 'NotificationHelper' },
'shared/js/accessibility_helper': { exports: 'AccessibilityHelper' },
'shared/js/gesture_detector': { exports: 'GestureDetector' }
},
config: {
template: {
tagToId: function (tag) {
return tag.replace(/^cards-/, 'cards/').replace(/^lst-/, 'cards/lst/').replace(/^msg-/, 'cards/msg/').replace(/^cmp-/, 'cards/cmp/').replace(/-/g, '_');
}
},
element: {
idToTag: function (id) {
return id.toLowerCase().replace(/^cards\/lst\//, 'lst-').replace(/^cards\/msg\//, 'msg-').replace(/^cards\/cmp\//, 'cmp-').replace(/[^a-z]/g, '-');
}
}
},
definePrim: 'prim'
});
}
if (navigator.mozAudioChannelManager) {
navigator.mozAudioChannelManager.volumeControlChannel = 'notification';
}
if (window.startupOnModelLoaded) {
requirejs([
'console_hook',
'model_create'
], function (hook, modelCreate) {
var model = modelCreate.defaultModel;
model.init();
window.startupOnModelLoaded(model, function () {
require(['mail_app']);
});
});
} else {
requirejs([
'console_hook',
'mail_app'
]);
}
;
define('config', function () {
});
(function (window, undefined) {
'use strict';
function L10nError(message, id, loc) {
this.name = 'L10nError';
this.message = message;
this.id = id;
this.loc = loc;
}
L10nError.prototype = Object.create(Error.prototype);
L10nError.prototype.constructor = L10nError;
var io = {
_load: function (type, url, callback, sync) {
var xhr = new XMLHttpRequest();
var needParse;
if (xhr.overrideMimeType) {
xhr.overrideMimeType(type);
}
xhr.open('GET', url, !sync);
if (type === 'application/json') {
if (sync) {
needParse = true;
} else {
xhr.responseType = 'json';
}
}
xhr.addEventListener('load', function io_onload(e) {
if (e.target.status === 200 || e.target.status === 0) {
var res = e.target.response || e.target.responseText;
callback(null, needParse ? JSON.parse(res) : res);
} else {
callback(new L10nError('Not found: ' + url));
}
});
xhr.addEventListener('error', callback);
xhr.addEventListener('timeout', callback);
try {
xhr.send(null);
} catch (e) {
if (e.name === 'NS_ERROR_FILE_NOT_FOUND') {
callback(new L10nError('Not found: ' + url));
} else {
throw e;
}
}
},
load: function (url, callback, sync) {
return io._load('text/plain', url, callback, sync);
},
loadJSON: function (url, callback, sync) {
return io._load('application/json', url, callback, sync);
}
};
function EventEmitter() {
}
EventEmitter.prototype.emit = function ee_emit() {
if (!this._listeners) {
return;
}
var args = Array.prototype.slice.call(arguments);
var type = args.shift();
if (!this._listeners[type]) {
return;
}
var typeListeners = this._listeners[type].slice();
for (var i = 0; i < typeListeners.length; i++) {
typeListeners[i].apply(this, args);
}
};
EventEmitter.prototype.addEventListener = function ee_add(type, listener) {
if (!this._listeners) {
this._listeners = {};
}
if (!(type in this._listeners)) {
this._listeners[type] = [];
}
this._listeners[type].push(listener);
};
EventEmitter.prototype.removeEventListener = function ee_rm(type, listener) {
if (!this._listeners) {
return;
}
var typeListeners = this._listeners[type];
var pos = typeListeners.indexOf(listener);
if (pos === -1) {
return;
}
typeListeners.splice(pos, 1);
};
function getPluralRule(lang) {
var locales2rules = {
'af': 3,
'ak': 4,
'am': 4,
'ar': 1,
'asa': 3,
'az': 0,
'be': 11,
'bem': 3,
'bez': 3,
'bg': 3,
'bh': 4,
'bm': 0,
'bn': 3,
'bo': 0,
'br': 20,
'brx': 3,
'bs': 11,
'ca': 3,
'cgg': 3,
'chr': 3,
'cs': 12,
'cy': 17,
'da': 3,
'de': 3,
'dv': 3,
'dz': 0,
'ee': 3,
'el': 3,
'en': 3,
'eo': 3,
'es': 3,
'et': 3,
'eu': 3,
'fa': 0,
'ff': 5,
'fi': 3,
'fil': 4,
'fo': 3,
'fr': 5,
'fur': 3,
'fy': 3,
'ga': 8,
'gd': 24,
'gl': 3,
'gsw': 3,
'gu': 3,
'guw': 4,
'gv': 23,
'ha': 3,
'haw': 3,
'he': 2,
'hi': 4,
'hr': 11,
'hu': 0,
'id': 0,
'ig': 0,
'ii': 0,
'is': 3,
'it': 3,
'iu': 7,
'ja': 0,
'jmc': 3,
'jv': 0,
'ka': 0,
'kab': 5,
'kaj': 3,
'kcg': 3,
'kde': 0,
'kea': 0,
'kk': 3,
'kl': 3,
'km': 0,
'kn': 0,
'ko': 0,
'ksb': 3,
'ksh': 21,
'ku': 3,
'kw': 7,
'lag': 18,
'lb': 3,
'lg': 3,
'ln': 4,
'lo': 0,
'lt': 10,
'lv': 6,
'mas': 3,
'mg': 4,
'mk': 16,
'ml': 3,
'mn': 3,
'mo': 9,
'mr': 3,
'ms': 0,
'mt': 15,
'my': 0,
'nah': 3,
'naq': 7,
'nb': 3,
'nd': 3,
'ne': 3,
'nl': 3,
'nn': 3,
'no': 3,
'nr': 3,
'nso': 4,
'ny': 3,
'nyn': 3,
'om': 3,
'or': 3,
'pa': 3,
'pap': 3,
'pl': 13,
'ps': 3,
'pt': 3,
'rm': 3,
'ro': 9,
'rof': 3,
'ru': 11,
'rwk': 3,
'sah': 0,
'saq': 3,
'se': 7,
'seh': 3,
'ses': 0,
'sg': 0,
'sh': 11,
'shi': 19,
'sk': 12,
'sl': 14,
'sma': 7,
'smi': 7,
'smj': 7,
'smn': 7,
'sms': 7,
'sn': 3,
'so': 3,
'sq': 3,
'sr': 11,
'ss': 3,
'ssy': 3,
'st': 3,
'sv': 3,
'sw': 3,
'syr': 3,
'ta': 3,
'te': 3,
'teo': 3,
'th': 0,
'ti': 4,
'tig': 3,
'tk': 3,
'tl': 4,
'tn': 3,
'to': 0,
'tr': 0,
'ts': 3,
'tzm': 22,
'uk': 11,
'ur': 3,
've': 3,
'vi': 0,
'vun': 3,
'wa': 4,
'wae': 3,
'wo': 0,
'xh': 3,
'xog': 3,
'yo': 0,
'zh': 0,
'zu': 3
};
function isIn(n, list) {
return list.indexOf(n) !== -1;
}
function isBetween(n, start, end) {
return typeof n === typeof start && start <= n && n <= end;
}
var pluralRules = {
'0': function () {
return 'other';
},
'1': function (n) {
if (isBetween(n % 100, 3, 10)) {
return 'few';
}
if (n === 0) {
return 'zero';
}
if (isBetween(n % 100, 11, 99)) {
return 'many';
}
if (n === 2) {
return 'two';
}
if (n === 1) {
return 'one';
}
return 'other';
},
'2': function (n) {
if (n !== 0 && n % 10 === 0) {
return 'many';
}
if (n === 2) {
return 'two';
}
if (n === 1) {
return 'one';
}
return 'other';
},
'3': function (n) {
if (n === 1) {
return 'one';
}
return 'other';
},
'4': function (n) {
if (isBetween(n, 0, 1)) {
return 'one';
}
return 'other';
},
'5': function (n) {
if (isBetween(n, 0, 2) && n !== 2) {
return 'one';
}
return 'other';
},
'6': function (n) {
if (n === 0) {
return 'zero';
}
if (n % 10 === 1 && n % 100 !== 11) {
return 'one';
}
return 'other';
},
'7': function (n) {
if (n === 2) {
return 'two';
}
if (n === 1) {
return 'one';
}
return 'other';
},
'8': function (n) {
if (isBetween(n, 3, 6)) {
return 'few';
}
if (isBetween(n, 7, 10)) {
return 'many';
}
if (n === 2) {
return 'two';
}
if (n === 1) {
return 'one';
}
return 'other';
},
'9': function (n) {
if (n === 0 || n !== 1 && isBetween(n % 100, 1, 19)) {
return 'few';
}
if (n === 1) {
return 'one';
}
return 'other';
},
'10': function (n) {
if (isBetween(n % 10, 2, 9) && !isBetween(n % 100, 11, 19)) {
return 'few';
}
if (n % 10 === 1 && !isBetween(n % 100, 11, 19)) {
return 'one';
}
return 'other';
},
'11': function (n) {
if (isBetween(n % 10, 2, 4) && !isBetween(n % 100, 12, 14)) {
return 'few';
}
if (n % 10 === 0 || isBetween(n % 10, 5, 9) || isBetween(n % 100, 11, 14)) {
return 'many';
}
if (n % 10 === 1 && n % 100 !== 11) {
return 'one';
}
return 'other';
},
'12': function (n) {
if (isBetween(n, 2, 4)) {
return 'few';
}
if (n === 1) {
return 'one';
}
return 'other';
},
'13': function (n) {
if (isBetween(n % 10, 2, 4) && !isBetween(n % 100, 12, 14)) {
return 'few';
}
if (n !== 1 && isBetween(n % 10, 0, 1) || isBetween(n % 10, 5, 9) || isBetween(n % 100, 12, 14)) {
return 'many';
}
if (n === 1) {
return 'one';
}
return 'other';
},
'14': function (n) {
if (isBetween(n % 100, 3, 4)) {
return 'few';
}
if (n % 100 === 2) {
return 'two';
}
if (n % 100 === 1) {
return 'one';
}
return 'other';
},
'15': function (n) {
if (n === 0 || isBetween(n % 100, 2, 10)) {
return 'few';
}
if (isBetween(n % 100, 11, 19)) {
return 'many';
}
if (n === 1) {
return 'one';
}
return 'other';
},
'16': function (n) {
if (n % 10 === 1 && n !== 11) {
return 'one';
}
return 'other';
},
'17': function (n) {
if (n === 3) {
return 'few';
}
if (n === 0) {
return 'zero';
}
if (n === 6) {
return 'many';
}
if (n === 2) {
return 'two';
}
if (n === 1) {
return 'one';
}
return 'other';
},
'18': function (n) {
if (n === 0) {
return 'zero';
}
if (isBetween(n, 0, 2) && n !== 0 && n !== 2) {
return 'one';
}
return 'other';
},
'19': function (n) {
if (isBetween(n, 2, 10)) {
return 'few';
}
if (isBetween(n, 0, 1)) {
return 'one';
}
return 'other';
},
'20': function (n) {
if ((isBetween(n % 10, 3, 4) || n % 10 === 9) && !(isBetween(n % 100, 10, 19) || isBetween(n % 100, 70, 79) || isBetween(n % 100, 90, 99))) {
return 'few';
}
if (n % 1000000 === 0 && n !== 0) {
return 'many';
}
if (n % 10 === 2 && !isIn(n % 100, [
12,
72,
92
])) {
return 'two';
}
if (n % 10 === 1 && !isIn(n % 100, [
11,
71,
91
])) {
return 'one';
}
return 'other';
},
'21': function (n) {
if (n === 0) {
return 'zero';
}
if (n === 1) {
return 'one';
}
return 'other';
},
'22': function (n) {
if (isBetween(n, 0, 1) || isBetween(n, 11, 99)) {
return 'one';
}
return 'other';
},
'23': function (n) {
if (isBetween(n % 10, 1, 2) || n % 20 === 0) {
return 'one';
}
return 'other';
},
'24': function (n) {
if (isBetween(n, 3, 10) || isBetween(n, 13, 19)) {
return 'few';
}
if (isIn(n, [
2,
12
])) {
return 'two';
}
if (isIn(n, [
1,
11
])) {
return 'one';
}
return 'other';
}
};
var index = locales2rules[lang.replace(/-.*$/, '')];
if (!(index in pluralRules)) {
return function () {
return 'other';
};
}
return pluralRules[index];
}
var MAX_PLACEABLES = 100;
var PropertiesParser = {
patterns: null,
entryIds: null,
init: function () {
this.patterns = {
comment: /^\s*#|^\s*$/,
entity: /^([^=\s]+)\s*=\s*(.*)$/,
multiline: /[^\\]\\$/,
index: /\{\[\s*(\w+)(?:\(([^\)]*)\))?\s*\]\}/i,
unicode: /\\u([0-9a-fA-F]{1,4})/g,
entries: /[^\r\n]+/g,
controlChars: /\\([\\\n\r\t\b\f\{\}\"\'])/g,
placeables: /\{\{\s*([^\s]*?)\s*\}\}/
};
},
parse: function (ctx, source) {
if (!this.patterns) {
this.init();
}
var ast = [];
this.entryIds = Object.create(null);
var entries = source.match(this.patterns.entries);
if (!entries) {
return ast;
}
for (var i = 0; i < entries.length; i++) {
var line = entries[i];
if (this.patterns.comment.test(line)) {
continue;
}
while (this.patterns.multiline.test(line) && i < entries.length) {
line = line.slice(0, -1) + entries[++i].trim();
}
var entityMatch = line.match(this.patterns.entity);
if (entityMatch) {
try {
this.parseEntity(entityMatch[1], entityMatch[2], ast);
} catch (e) {
if (ctx) {
ctx._emitter.emit('parseerror', e);
} else {
throw e;
}
}
}
}
return ast;
},
parseEntity: function (id, value, ast) {
var name, key;
var pos = id.indexOf('[');
if (pos !== -1) {
name = id.substr(0, pos);
key = id.substring(pos + 1, id.length - 1);
} else {
name = id;
key = null;
}
var nameElements = name.split('.');
if (nameElements.length > 2) {
throw new L10nError('Error in ID: "' + name + '".' + ' Nested attributes are not supported.');
}
var attr;
if (nameElements.length > 1) {
name = nameElements[0];
attr = nameElements[1];
if (attr[0] === '$') {
throw new L10nError('Attribute can\'t start with "$"', id);
}
} else {
attr = null;
}
this.setEntityValue(name, attr, key, this.unescapeString(value), ast);
},
setEntityValue: function (id, attr, key, rawValue, ast) {
var pos, v;
var value = rawValue.indexOf('{{') > -1 ? this.parseString(rawValue) : rawValue;
if (attr) {
pos = this.entryIds[id];
if (pos === undefined) {
v = { $i: id };
if (key) {
v[attr] = {};
v[attr][key] = value;
} else {
v[attr] = value;
}
ast.push(v);
this.entryIds[id] = ast.length - 1;
return;
}
if (key) {
if (typeof ast[pos][attr] === 'string') {
ast[pos][attr] = {
$x: this.parseIndex(ast[pos][attr]),
$v: {}
};
}
ast[pos][attr].$v[key] = value;
return;
}
ast[pos][attr] = value;
return;
}
if (key) {
pos = this.entryIds[id];
if (pos === undefined) {
v = {};
v[key] = value;
ast.push({
$i: id,
$v: v
});
this.entryIds[id] = ast.length - 1;
return;
}
if (typeof ast[pos].$v === 'string') {
ast[pos].$x = this.parseIndex(ast[pos].$v);
ast[pos].$v = {};
}
ast[pos].$v[key] = value;
return;
}
ast.push({
$i: id,
$v: value
});
this.entryIds[id] = ast.length - 1;
},
parseString: function (str) {
var chunks = str.split(this.patterns.placeables);
var complexStr = [];
var len = chunks.length;
var placeablesCount = (len - 1) / 2;
if (placeablesCount >= MAX_PLACEABLES) {
throw new L10nError('Too many placeables (' + placeablesCount + ', max allowed is ' + MAX_PLACEABLES + ')');
}
for (var i = 0; i < chunks.length; i++) {
if (chunks[i].length === 0) {
continue;
}
if (i % 2 === 1) {
complexStr.push({
t: 'idOrVar',
v: chunks[i]
});
} else {
complexStr.push(chunks[i]);
}
}
return complexStr;
},
unescapeString: function (str) {
if (str.lastIndexOf('\\') !== -1) {
str = str.replace(this.patterns.controlChars, '$1');
}
return str.replace(this.patterns.unicode, function (match, token) {
return unescape('%u' + '0000'.slice(token.length) + token);
});
},
parseIndex: function (str) {
var match = str.match(this.patterns.index);
if (!match) {
throw new L10nError('Malformed index');
}
if (match[2]) {
return [
{
t: 'idOrVar',
v: match[1]
},
match[2]
];
} else {
return [{
t: 'idOrVar',
v: match[1]
}];
}
}
};
var KNOWN_MACROS = ['plural'];
var MAX_PLACEABLE_LENGTH = 2500;
var rePlaceables = /\{\{\s*(.+?)\s*\}\}/g;
var nonLatin1 = /[^\x01-\xFF]/;
var FSI = '\u2068';
var PDI = '\u2069';
function createEntry(node, env) {
var keys = Object.keys(node);
if (typeof node.$v === 'string' && keys.length === 2) {
return node.$v;
}
var attrs;
for (var i = 0, key; key = keys[i]; i++) {
if (key[0] === '$') {
continue;
}
if (!attrs) {
attrs = Object.create(null);
}
attrs[key] = createAttribute(node[key], env, node.$i + '.' + key);
}
return {
id: node.$i,
value: node.$v !== undefined ? node.$v : null,
index: node.$x || null,
attrs: attrs || null,
env: env,
dirty: false
};
}
function createAttribute(node, env, id) {
if (typeof node === 'string') {
return node;
}
return {
id: id,
value: node.$v || (node !== undefined ? node : null),
index: node.$x || null,
env: env,
dirty: false
};
}
function format(args, entity) {
if (typeof entity === 'string') {
return [
{},
entity
];
}
if (entity.dirty) {
throw new L10nError('Cyclic reference detected: ' + entity.id);
}
entity.dirty = true;
var rv;
try {
rv = resolveValue({}, args, entity.env, entity.value, entity.index);
} finally {
entity.dirty = false;
}
return rv;
}
function resolveIdentifier(args, env, id) {
if (KNOWN_MACROS.indexOf(id) > -1) {
return [
{},
env['__' + id]
];
}
if (args && args.hasOwnProperty(id)) {
if (typeof args[id] === 'string' || typeof args[id] === 'number' && !isNaN(args[id])) {
return [
{},
args[id]
];
} else {
throw new L10nError('Arg must be a string or a number: ' + id);
}
}
if (id in env && id !== '__proto__') {
return format(args, env[id]);
}
throw new L10nError('Unknown reference: ' + id);
}
function subPlaceable(locals, args, env, id) {
var res;
try {
res = resolveIdentifier(args, env, id);
} catch (err) {
return [
{ error: err },
'{{ ' + id + ' }}'
];
}
var value = res[1];
if (typeof value === 'number') {
return res;
}
if (typeof value === 'string') {
if (value.length >= MAX_PLACEABLE_LENGTH) {
throw new L10nError('Too many characters in placeable (' + value.length + ', max allowed is ' + MAX_PLACEABLE_LENGTH + ')');
}
if (locals.contextIsNonLatin1 || value.match(nonLatin1)) {
res[1] = FSI + value + PDI;
}
return res;
}
return [
{},
'{{ ' + id + ' }}'
];
}
function interpolate(locals, args, env, arr) {
return arr.reduce(function (prev, cur) {
if (typeof cur === 'string') {
return [
prev[0],
prev[1] + cur
];
} else if (cur.t === 'idOrVar') {
var placeable = subPlaceable(locals, args, env, cur.v);
return [
prev[0],
prev[1] + placeable[1]
];
}
}, [
locals,
''
]);
}
function resolveSelector(args, env, expr, index) {
var selectorName = index[0].v;
var selector = resolveIdentifier(args, env, selectorName)[1];
if (typeof selector !== 'function') {
return selector;
}
var argValue = index[1] ? resolveIdentifier(args, env, index[1])[1] : undefined;
if (selector === env.__plural) {
if (argValue === 0 && 'zero' in expr) {
return 'zero';
}
if (argValue === 1 && 'one' in expr) {
return 'one';
}
if (argValue === 2 && 'two' in expr) {
return 'two';
}
}
return selector(argValue);
}
function resolveValue(locals, args, env, expr, index) {
if (!expr) {
return [
locals,
expr
];
}
if (typeof expr === 'string' || typeof expr === 'boolean' || typeof expr === 'number') {
return [
locals,
expr
];
}
if (Array.isArray(expr)) {
locals.contextIsNonLatin1 = expr.some(function ($_) {
return typeof $_ === 'string' && $_.match(nonLatin1);
});
return interpolate(locals, args, env, expr);
}
if (index) {
var selector = resolveSelector(args, env, expr, index);
if (expr.hasOwnProperty(selector)) {
return resolveValue(locals, args, env, expr[selector]);
}
}
if ('other' in expr) {
return resolveValue(locals, args, env, expr.other);
}
throw new L10nError('Unresolvable value');
}
var Resolver = {
createEntry: createEntry,
format: format,
rePlaceables: rePlaceables
};
function walkContent(node, fn) {
if (typeof node === 'string') {
return fn(node);
}
if (node.t === 'idOrVar') {
return node;
}
var rv = Array.isArray(node) ? [] : {};
var keys = Object.keys(node);
for (var i = 0, key; key = keys[i]; i++) {
if (key === '$i' || key === '$x') {
rv[key] = node[key];
} else {
rv[key] = walkContent(node[key], fn);
}
}
return rv;
}
var reAlphas = /[a-zA-Z]/g;
var reVowels = /[aeiouAEIOU]/g;
var ACCENTED_MAP = 'ȦƁƇḒḖƑƓĦĪ' + 'ĴĶĿḾȠǾƤɊŘ' + 'ŞŦŬṼẆẊẎẐ' + '[\\]^_`' + 'ȧƀƈḓḗƒɠħī' + 'ĵķŀḿƞǿƥɋř' + 'şŧŭṽẇẋẏẑ';
var FLIPPED_MAP = '\u2200ԐↃpƎɟפHIſ' + 'Ӽ\u02E5WNOԀÒᴚS\u22A5\u2229Ʌ' + 'MXʎZ' + '[\\]ᵥ_,' + 'ɐqɔpǝɟƃɥıɾ' + 'ʞʅɯuodbɹsʇnʌʍxʎz';
function makeLonger(val) {
return val.replace(reVowels, function (match) {
return match + match.toLowerCase();
});
}
function replaceChars(map, val) {
return val.replace(reAlphas, function (match) {
return map.charAt(match.charCodeAt(0) - 65);
});
}
var reWords = /[^\W0-9_]+/g;
function makeRTL(val) {
return val.replace(reWords, function (match) {
return '\u202E' + match + '\u202C';
});
}
var reExcluded = /(%[EO]?\w|\{\s*.+?\s*\}|&[#\w]+;|<\s*.+?\s*>)/;
function mapContent(fn, val) {
if (!val) {
return val;
}
var parts = val.split(reExcluded);
var modified = parts.map(function (part) {
if (reExcluded.test(part)) {
return part;
}
return fn(part);
});
return modified.join('');
}
function Pseudo(id, name, charMap, modFn) {
this.id = id;
this.translate = mapContent.bind(null, function (val) {
return replaceChars(charMap, modFn(val));
});
this.name = this.translate(name);
}
var PSEUDO = {
'fr-x-psaccent': new Pseudo('fr-x-psaccent', 'Runtime Accented', ACCENTED_MAP, makeLonger),
'ar-x-psbidi': new Pseudo('ar-x-psbidi', 'Runtime Bidi', FLIPPED_MAP, makeRTL)
};
function Locale(id, ctx) {
this.id = id;
this.ctx = ctx;
this.isReady = false;
this.entries = Object.create(null);
this.entries.__plural = getPluralRule(this.isPseudo() ? this.ctx.defaultLocale : id);
}
Locale.prototype.isPseudo = function () {
return this.ctx.qps.indexOf(this.id) !== -1;
};
var bindingsIO = {
extra: function (id, ver, path, type, callback, errback) {
if (type === 'properties') {
type = 'text';
}
navigator.mozApps.getLocalizationResource(id, ver, path, type).then(callback.bind(null, null), errback);
},
app: function (id, ver, path, type, callback, errback, sync) {
switch (type) {
case 'properties':
io.load(path, callback, sync);
break;
case 'json':
io.loadJSON(path, callback, sync);
break;
}
}
};
Locale.prototype.build = function L_build(callback) {
var sync = !callback;
var ctx = this.ctx;
var self = this;
var l10nLoads = ctx.resLinks.length;
function onL10nLoaded(err) {
if (err) {
ctx._emitter.emit('fetcherror', err);
}
if (--l10nLoads <= 0) {
self.isReady = true;
if (callback) {
callback();
}
}
}
if (l10nLoads === 0) {
onL10nLoaded();
return;
}
function onJSONLoaded(err, json) {
if (!err && json) {
self.addAST(json);
}
onL10nLoaded(err);
}
function onPropLoaded(err, source) {
if (!err && source) {
var ast = PropertiesParser.parse(ctx, source);
self.addAST(ast);
}
onL10nLoaded(err);
}
var idToFetch = this.isPseudo() ? ctx.defaultLocale : this.id;
var appVersion = null;
var source = 'app';
if (typeof navigator !== 'undefined') {
source = navigator.mozL10n._config.localeSources[this.id] || 'app';
appVersion = navigator.mozL10n._config.appVersion;
}
for (var i = 0; i < ctx.resLinks.length; i++) {
var resLink = decodeURI(ctx.resLinks[i]);
var path = resLink.replace('{locale}', idToFetch);
var type = path.substr(path.lastIndexOf('.') + 1);
var cb;
switch (type) {
case 'json':
cb = onJSONLoaded;
break;
case 'properties':
cb = onPropLoaded;
break;
}
bindingsIO[source](this.id, appVersion, path, type, cb, onL10nLoaded, sync);
}
};
function createPseudoEntry(node, entries) {
return Resolver.createEntry(walkContent(node, PSEUDO[this.id].translate), entries);
}
Locale.prototype.addAST = function (ast) {
var createEntry = this.isPseudo() ? createPseudoEntry.bind(this) : Resolver.createEntry;
for (var i = 0; i < ast.length; i++) {
this.entries[ast[i].$i] = createEntry(ast[i], this.entries);
}
};
function Context(id) {
this.id = id;
this.isReady = false;
this.isLoading = false;
this.defaultLocale = 'en-US';
this.availableLocales = [];
this.supportedLocales = [];
this.qps = [];
this.resLinks = [];
this.locales = {};
this._emitter = new EventEmitter();
this._ready = new Promise(this.once.bind(this));
}
function reportMissing(id, err) {
this._emitter.emit('notfounderror', err);
return id;
}
function getWithFallback(id) {
var cur = 0;
var loc;
var locale;
while (loc = this.supportedLocales[cur]) {
locale = this.getLocale(loc);
if (!locale.isReady) {
locale.build(null);
}
var entry = locale.entries[id];
if (entry === undefined) {
cur++;
reportMissing.call(this, id, new L10nError('"' + id + '"' + ' not found in ' + loc + ' in ' + this.id, id, loc));
continue;
}
return entry;
}
throw new L10nError('"' + id + '"' + ' missing from all supported locales in ' + this.id, id);
}
function formatTuple(args, entity) {
try {
return Resolver.format(args, entity);
} catch (err) {
this._emitter.emit('resolveerror', err);
var locals = { error: err };
return [
locals,
entity.id
];
}
}
function formatValue(args, entity) {
if (typeof entity === 'string') {
return entity;
}
return formatTuple.call(this, args, entity)[1];
}
function formatEntity(args, entity) {
var entityTuple = formatTuple.call(this, args, entity);
var value = entityTuple[1];
var formatted = {
value: value,
attrs: null
};
if (entity.attrs) {
formatted.attrs = Object.create(null);
}
for (var key in entity.attrs) {
var attrTuple = formatTuple.call(this, args, entity.attrs[key]);
formatted.attrs[key] = attrTuple[1];
}
return formatted;
}
function formatAsync(fn, id, args) {
return this._ready.then(getWithFallback.bind(this, id)).then(fn.bind(this, args), reportMissing.bind(this, id));
}
Context.prototype.formatValue = function (id, args) {
return formatAsync.call(this, formatValue, id, args);
};
Context.prototype.formatEntity = function (id, args) {
return formatAsync.call(this, formatEntity, id, args);
};
function legacyGet(fn, id, args) {
if (!this.isReady) {
throw new L10nError('Context not ready');
}
var entry;
try {
entry = getWithFallback.call(this, id);
} catch (err) {
if (err.loc) {
throw err;
}
reportMissing.call(this, id, err);
return '';
}
return fn.call(this, args, entry);
}
Context.prototype.get = function (id, args) {
return legacyGet.call(this, formatValue, id, args);
};
Context.prototype.getEntity = function (id, args) {
return legacyGet.call(this, formatEntity, id, args);
};
Context.prototype.getLocale = function getLocale(code) {
var locales = this.locales;
if (locales[code]) {
return locales[code];
}
return locales[code] = new Locale(code, this);
};
function negotiate(available, requested, defaultLocale) {
var supportedLocale;
for (var i = 0; i < requested.length; i++) {
var locale = requested[i];
if (available.indexOf(locale) !== -1) {
supportedLocale = locale;
break;
}
}
if (!supportedLocale || supportedLocale === defaultLocale) {
return [defaultLocale];
}
return [
supportedLocale,
defaultLocale
];
}
function freeze(supported) {
var locale = this.getLocale(supported[0]);
if (locale.isReady) {
setReady.call(this, supported);
} else {
locale.build(setReady.bind(this, supported));
}
}
function setReady(supported) {
this.supportedLocales = supported;
this.isReady = true;
this._emitter.emit('ready');
}
Context.prototype.registerLocales = function (defLocale, available) {
if (defLocale) {
this.defaultLocale = defLocale;
}
this.availableLocales = [this.defaultLocale];
this.qps = Object.keys(PSEUDO);
if (available) {
for (var i = 0, loc; loc = available[i]; i++) {
if (this.availableLocales.indexOf(loc) === -1) {
this.availableLocales.push(loc);
var pos = this.qps.indexOf(loc);
if (pos !== -1) {
this.qps.splice(pos, 1);
}
}
}
}
};
Context.prototype.requestLocales = function requestLocales() {
if (this.isLoading && !this.isReady) {
throw new L10nError('Context not ready');
}
this.isLoading = true;
var requested = Array.prototype.slice.call(arguments);
if (requested.length === 0) {
throw new L10nError('No locales requested');
}
var supported = negotiate(this.availableLocales.concat(this.qps), requested, this.defaultLocale);
if (this.supportedLocales[0] !== supported[0]) {
freeze.call(this, supported);
}
};
Context.prototype.addEventListener = function (type, listener) {
this._emitter.addEventListener(type, listener);
};
Context.prototype.removeEventListener = function (type, listener) {
this._emitter.removeEventListener(type, listener);
};
Context.prototype.ready = function (callback) {
if (this.isReady) {
setTimeout(callback);
}
this.addEventListener('ready', callback);
};
Context.prototype.once = function (callback) {
if (this.isReady) {
setTimeout(callback);
return;
}
var callAndRemove = function () {
this.removeEventListener('ready', callAndRemove);
callback();
}.bind(this);
this.addEventListener('ready', callAndRemove);
};
var allowed = {
elements: [
'a',
'em',
'strong',
'small',
's',
'cite',
'q',
'dfn',
'abbr',
'data',
'time',
'code',
'var',
'samp',
'kbd',
'sub',
'sup',
'i',
'b',
'u',
'mark',
'ruby',
'rt',
'rp',
'bdi',
'bdo',
'span',
'br',
'wbr'
],
attributes: {
global: [
'title',
'aria-label',
'aria-valuetext',
'aria-moz-hint'
],
a: ['download'],
area: [
'download',
'alt'
],
input: [
'alt',
'placeholder'
],
menuitem: ['label'],
menu: ['label'],
optgroup: ['label'],
option: ['label'],
track: ['label'],
img: ['alt'],
textarea: ['placeholder'],
th: ['abbr']
}
};
var rtlList = [
'ar',
'he',
'fa',
'ps',
'ar-x-psbidi',
'ur'
];
var nodeObserver = null;
var pendingElements = null;
var moConfig = {
attributes: true,
characterData: false,
childList: true,
subtree: true,
attributeFilter: [
'data-l10n-id',
'data-l10n-args'
]
};
navigator.mozL10n = {
ctx: null,
get: function get(id, ctxdata) {
return navigator.mozL10n.ctx.get(id, ctxdata);
},
formatValue: function (id, ctxdata) {
return navigator.mozL10n.ctx.formatValue(id, ctxdata);
},
formatEntity: function (id, ctxdata) {
return navigator.mozL10n.ctx.formatEntity(id, ctxdata);
},
translateFragment: function (fragment) {
return translateFragment.call(navigator.mozL10n, fragment);
},
setAttributes: setL10nAttributes,
getAttributes: getL10nAttributes,
ready: function ready(callback) {
return navigator.mozL10n.ctx.ready(callback);
},
once: function once(callback) {
return navigator.mozL10n.ctx.once(callback);
},
get readyState() {
return navigator.mozL10n.ctx.isReady ? 'complete' : 'loading';
},
language: {
set code(lang) {
navigator.mozL10n.ctx.requestLocales(lang);
},
get code() {
return navigator.mozL10n.ctx.supportedLocales[0];
},
get direction() {
return getDirection(navigator.mozL10n.ctx.supportedLocales[0]);
}
},
qps: PSEUDO,
_config: {
appVersion: null,
localeSources: Object.create(null),
isPretranslated: false
},
_getInternalAPI: function () {
return {
Error: L10nError,
Context: Context,
Locale: Locale,
Resolver: Resolver,
getPluralRule: getPluralRule,
rePlaceables: rePlaceables,
translateDocument: translateDocument,
onMetaInjected: onMetaInjected,
PropertiesParser: PropertiesParser,
walkContent: walkContent,
buildLocaleList: buildLocaleList
};
}
};
function getDirection(lang) {
return rtlList.indexOf(lang) >= 0 ? 'rtl' : 'ltr';
}
var readyStates = {
loading: 0,
interactive: 1,
complete: 2
};
function whenInteractive(callback) {
if (readyStates[document.readyState] >= readyStates.interactive) {
callback();
return;
}
document.addEventListener('readystatechange', function l10n_onrsc() {
if (readyStates[document.readyState] >= readyStates.interactive) {
document.removeEventListener('readystatechange', l10n_onrsc);
callback();
}
});
}
function initObserver() {
nodeObserver = new MutationObserver(onMutations.bind(navigator.mozL10n));
nodeObserver.observe(document, moConfig);
}
function init(pretranslate) {
if (!pretranslate) {
initObserver();
}
initResources.call(navigator.mozL10n);
}
function initResources() {
var meta = {};
var nodes = document.head.querySelectorAll('link[rel="localization"],' + 'meta[name="availableLanguages"],' + 'meta[name="defaultLanguage"],' + 'meta[name="appVersion"],' + 'script[type="application/l10n"]');
for (var i = 0, node; node = nodes[i]; i++) {
var type = node.getAttribute('rel') || node.nodeName.toLowerCase();
switch (type) {
case 'localization':
this.ctx.resLinks.push(node.getAttribute('href'));
break;
case 'meta':
onMetaInjected.call(this, node, meta);
break;
case 'script':
onScriptInjected.call(this, node);
break;
}
}
var additionalLanguagesPromise;
if (navigator.mozApps && navigator.mozApps.getAdditionalLanguages) {
additionalLanguagesPromise = navigator.mozApps.getAdditionalLanguages().catch(function (e) {
console.error('Error while loading getAdditionalLanguages', e);
});
document.addEventListener('additionallanguageschange', function (evt) {
registerLocales.call(this, meta, evt.detail);
this.ctx.requestLocales.apply(this.ctx, navigator.languages || [navigator.language]);
}.bind(this));
} else {
additionalLanguagesPromise = Promise.resolve();
}
additionalLanguagesPromise.then(function (extraLangs) {
registerLocales.call(this, meta, extraLangs);
initLocale.call(this);
}.bind(this));
}
function registerLocales(meta, extraLangs) {
var locales = buildLocaleList.call(this, meta, extraLangs);
navigator.mozL10n._config.localeSources = locales[1];
this.ctx.registerLocales(locales[0], Object.keys(locales[1]));
}
function getMatchingLangpack(appVersion, langpacks) {
for (var i = 0, langpack; langpack = langpacks[i]; i++) {
if (langpack.target === appVersion) {
return langpack;
}
}
return null;
}
function buildLocaleList(meta, extraLangs) {
var loc, lp;
var localeSources = Object.create(null);
var defaultLocale = meta.defaultLanguage || this.ctx.defaultLocale;
if (meta.availableLanguages) {
for (loc in meta.availableLanguages) {
localeSources[loc] = 'app';
}
}
if (extraLangs) {
for (loc in extraLangs) {
lp = getMatchingLangpack(this._config.appVersion, extraLangs[loc]);
if (!lp) {
continue;
}
if (!(loc in localeSources) || !meta.availableLanguages[loc] || parseInt(lp.revision) > meta.availableLanguages[loc]) {
localeSources[loc] = 'extra';
}
}
}
if (!(defaultLocale in localeSources)) {
localeSources[defaultLocale] = 'app';
}
return [
defaultLocale,
localeSources
];
}
function splitAvailableLanguagesString(str) {
var langs = {};
str.split(',').forEach(function (lang) {
lang = lang.trim().split(':');
langs[lang[0]] = parseInt(lang[1]);
});
return langs;
}
function onMetaInjected(node, meta) {
switch (node.getAttribute('name')) {
case 'availableLanguages':
meta.availableLanguages = splitAvailableLanguagesString(node.getAttribute('content'));
break;
case 'defaultLanguage':
meta.defaultLanguage = node.getAttribute('content');
break;
case 'appVersion':
navigator.mozL10n._config.appVersion = node.getAttribute('content');
break;
}
}
function onScriptInjected(node) {
var lang = node.getAttribute('lang');
var locale = this.ctx.getLocale(lang);
locale.addAST(JSON.parse(node.textContent));
}
function initLocale() {
this.ctx.requestLocales.apply(this.ctx, navigator.languages || [navigator.language]);
window.addEventListener('languagechange', function l10n_langchange() {
this.ctx.requestLocales.apply(this.ctx, navigator.languages || [navigator.language]);
}.bind(this));
}
function localizeMutations(mutations) {
var mutation;
var targets = new Set();
for (var i = 0; i < mutations.length; i++) {
mutation = mutations[i];
if (mutation.type === 'childList') {
var addedNode;
for (var j = 0; j < mutation.addedNodes.length; j++) {
addedNode = mutation.addedNodes[j];
if (addedNode.nodeType !== Node.ELEMENT_NODE) {
continue;
}
targets.add(addedNode);
}
}
if (mutation.type === 'attributes') {
targets.add(mutation.target);
}
}
targets.forEach(function (target) {
if (target.childElementCount) {
translateFragment.call(this, target);
} else if (target.hasAttribute('data-l10n-id')) {
translateElement.call(this, target);
}
}, this);
}
function onMutations(mutations, self) {
self.disconnect();
localizeMutations.call(this, mutations);
self.observe(document, moConfig);
}
function onReady() {
if (!navigator.mozL10n._config.isPretranslated) {
translateDocument.call(this);
}
navigator.mozL10n._config.isPretranslated = false;
if (pendingElements) {
for (var i = 0, element; element = pendingElements[i]; i++) {
translateElement.call(this, element);
}
pendingElements = null;
}
if (!nodeObserver) {
initObserver();
}
fireLocalizedEvent.call(this);
}
function fireLocalizedEvent() {
var event = new CustomEvent('localized', {
'bubbles': false,
'cancelable': false,
'detail': { 'language': this.ctx.supportedLocales[0] }
});
window.dispatchEvent(event);
}
var reOverlay = /<|?\w+;/;
var reHtml = /[&<>]/g;
var htmlEntities = {
'&': '&',
'<': '<',
'>': '>'
};
function translateDocument() {
document.documentElement.lang = this.language.code;
document.documentElement.dir = this.language.direction;
translateFragment.call(this, document.documentElement);
}
function translateFragment(element) {
if (typeof element.hasAttribute === 'function' && element.hasAttribute('data-l10n-id')) {
translateElement.call(this, element);
}
var nodes = getTranslatableChildren(element);
for (var i = 0; i < nodes.length; i++) {
translateElement.call(this, nodes[i]);
}
}
function setL10nAttributes(element, id, args) {
element.setAttribute('data-l10n-id', id);
if (args) {
element.setAttribute('data-l10n-args', JSON.stringify(args));
}
}
function getL10nAttributes(element) {
return {
id: element.getAttribute('data-l10n-id'),
args: JSON.parse(element.getAttribute('data-l10n-args'))
};
}
function getTranslatableChildren(element) {
return element ? element.querySelectorAll('*[data-l10n-id]') : [];
}
function camelCaseToDashed(string) {
if (string === 'ariaValueText') {
return 'aria-valuetext';
}
return string.replace(/[A-Z]/g, function (match) {
return '-' + match.toLowerCase();
}).replace(/^-/, '');
}
function escapeL10nArgs(match) {
return htmlEntities[match];
}
function translateElement(element) {
if (!this.ctx.isReady) {
if (!pendingElements) {
pendingElements = [];
}
pendingElements.push(element);
return;
}
var l10nId = element.getAttribute('data-l10n-id');
if (!l10nId) {
return false;
}
var l10nArgs = element.getAttribute('data-l10n-args');
var entity = this.ctx.getEntity(l10nId, l10nArgs ? JSON.parse(l10nArgs.replace(reHtml, escapeL10nArgs)) : undefined);
var value = entity.value;
if (typeof value === 'string') {
if (!reOverlay.test(value)) {
element.textContent = value;
} else {
var translation = element.ownerDocument.createElement('template');
translation.innerHTML = value;
overlayElement(element, translation.content);
}
}
for (var key in entity.attrs) {
var attrName = camelCaseToDashed(key);
if (isAttrAllowed({ name: attrName }, element)) {
element.setAttribute(attrName, entity.attrs[key]);
}
}
}
function overlayElement(sourceElement, translationElement) {
var result = translationElement.ownerDocument.createDocumentFragment();
var k, attr;
var childElement;
while (childElement = translationElement.childNodes[0]) {
translationElement.removeChild(childElement);
if (childElement.nodeType === Node.TEXT_NODE) {
result.appendChild(childElement);
continue;
}
var index = getIndexOfType(childElement);
var sourceChild = getNthElementOfType(sourceElement, childElement, index);
if (sourceChild) {
overlayElement(sourceChild, childElement);
result.appendChild(sourceChild);
continue;
}
if (isElementAllowed(childElement)) {
var sanitizedChild = childElement.ownerDocument.createElement(childElement.nodeName);
overlayElement(sanitizedChild, childElement);
result.appendChild(sanitizedChild);
continue;
}
result.appendChild(document.createTextNode(childElement.textContent));
}
sourceElement.textContent = '';
sourceElement.appendChild(result);
if (translationElement.attributes) {
for (k = 0, attr; attr = translationElement.attributes[k]; k++) {
if (isAttrAllowed(attr, sourceElement)) {
sourceElement.setAttribute(attr.name, attr.value);
}
}
}
}
function isElementAllowed(element) {
return allowed.elements.indexOf(element.tagName.toLowerCase()) !== -1;
}
function isAttrAllowed(attr, element) {
var attrName = attr.name.toLowerCase();
var tagName = element.tagName.toLowerCase();
if (allowed.attributes.global.indexOf(attrName) !== -1) {
return true;
}
if (!allowed.attributes[tagName]) {
return false;
}
if (allowed.attributes[tagName].indexOf(attrName) !== -1) {
return true;
}
if (tagName === 'input' && attrName === 'value') {
var type = element.type.toLowerCase();
if (type === 'submit' || type === 'button' || type === 'reset') {
return true;
}
}
return false;
}
function getNthElementOfType(context, element, index) {
var nthOfType = 0;
for (var i = 0, child; child = context.children[i]; i++) {
if (child.nodeType === Node.ELEMENT_NODE && child.tagName === element.tagName) {
if (nthOfType === index) {
return child;
}
nthOfType++;
}
}
return null;
}
function getIndexOfType(element) {
var index = 0;
var child;
while (child = element.previousElementSibling) {
if (child.tagName === element.tagName) {
index++;
}
}
return index;
}
var DEBUG = false;
navigator.mozL10n.ctx = new Context(window.document ? document.URL : null);
navigator.mozL10n.ctx.ready(onReady.bind(navigator.mozL10n));
navigator.mozL10n.ctx.addEventListener('notfounderror', function reportMissingEntity(e) {
if (DEBUG || e.loc === 'en-US') {
console.warn(e.toString());
}
});
if (DEBUG) {
navigator.mozL10n.ctx.addEventListener('fetcherror', console.error.bind(console));
navigator.mozL10n.ctx.addEventListener('parseerror', console.error.bind(console));
navigator.mozL10n.ctx.addEventListener('resolveerror', console.error.bind(console));
}
if (window.document) {
navigator.mozL10n._config.isPretranslated = document.documentElement.lang === navigator.language;
var forcePretranslate = !navigator.mozL10n._config.isPretranslated;
whenInteractive(init.bind(navigator.mozL10n, forcePretranslate));
}
document.l10n = {
setAttributes: navigator.mozL10n.setAttributes,
getAttributes: navigator.mozL10n.getAttributes,
formatValue: function (id, args) {
return navigator.mozL10n.formatValue(id, args);
},
translateFragment: function (frag) {
return Promise.resolve(navigator.mozL10n.translateFragment(frag));
},
ready: new Promise(function (resolve) {
navigator.mozL10n.once(resolve);
}),
formatValues: function () {
var keys = arguments;
var resp = keys.map(function (key) {
if (Array.isArray(key)) {
return navigator.mozL10n.formatValue(key[0], key[1]);
}
return navigator.mozL10n.formatValue(key);
});
return Promise.all(resp);
},
requestLanguages: function (langs) {
navigator.mozL10n.ctx.requestLocales.apply(navigator.mozL10n.ctx, langs);
},
pseudo: {
'fr-x-psaccent': {
getName: function () {
return Promise.resolve(navigator.mozL10n.qps['fr-x-psaccent'].name);
},
processString: function (s) {
return Promise.resolve(navigator.mozL10n.qps['fr-x-psaccent'].translate(s));
}
},
'ar-x-psbidi': {
getName: function () {
return Promise.resolve(navigator.mozL10n.qps['ar-x-psbidi'].name);
},
processString: function (s) {
return Promise.resolve(navigator.mozL10n.qps['ar-x-psbidi'].translate(s));
}
}
}
};
navigator.mozL10n.ready(function () {
document.documentElement.setAttribute('langs', navigator.mozL10n.ctx.supportedLocales.join(' '));
});
navigator.mozL10n.once(function () {
window.addEventListener('localized', function () {
document.dispatchEvent(new CustomEvent('DOMRetranslated', {
bubbles: false,
cancelable: false
}));
});
});
}(this));
define('l10nbase', function () {
});
(function (global) {
'use strict';
global.mozIntl = {
formatList: function (list) {
return navigator.mozL10n.formatValue('listSeparator_middle').then(sep => list.join(sep));
},
DateTimeFormat: function (locales, options) {
const resolvedOptions = Object.assign({}, options);
if (resolvedOptions.dayperiod) {
if (resolvedOptions.hour === undefined) {
resolvedOptions.hour = 'numeric';
}
}
if (resolvedOptions.hour !== undefined && resolvedOptions.hour12 === undefined) {
resolvedOptions.hour12 = navigator.mozHour12;
}
if (resolvedOptions.dayperiod === undefined && resolvedOptions.hour12 === true) {
resolvedOptions.dayperiod = true;
}
var intlFormat = new Intl.DateTimeFormat(locales, resolvedOptions);
resolvedOptions.locale = intlFormat.resolvedOptions().locale;
resolvedOptions.hour12 = intlFormat.resolvedOptions().hour12;
var hourFormatter;
if (resolvedOptions.dayperiod !== undefined && resolvedOptions.hour12 === true) {
hourFormatter = Intl.DateTimeFormat(locales, {
hour: 'numeric',
hour12: false
});
}
return {
resolvedOptions() {
return resolvedOptions;
},
format: function (date, tokenFormats) {
var dayPeriod;
var string = intlFormat.format(date);
if (resolvedOptions.dayperiod === false && resolvedOptions.hour12 === true) {
dayPeriod = getDayPeriodTokenForDate(date, hourFormatter);
string = string.replace(dayPeriod, '').trim();
} else if (resolvedOptions.dayperiod === true && options.hour === undefined) {
dayPeriod = getDayPeriodTokenForDate(date, hourFormatter);
const hour = date.toLocaleString(navigator.languages, {
hour12: true,
hour: 'numeric'
}).replace(dayPeriod, '').trim();
string = string.replace(hour, '').trim();
}
for (var token in tokenFormats) {
if (token === 'dayperiod' && resolvedOptions.hour12 === false) {
continue;
}
const localOptions = { [token]: resolvedOptions[token] };
var formatter = global.mozIntl.DateTimeFormat(navigator.languages, localOptions);
var tokenString = formatter.format(date);
string = string.replace(tokenString, tokenFormats[token]);
}
return string;
}
};
},
calendarInfo: function (token) {
switch (token) {
case 'firstDayOfTheWeek':
return navigator.mozL10n.formatValue('firstDayOfTheWeek').then(firstDayOfTheWeek => parseInt(firstDayOfTheWeek) % 7);
default:
throw new Error('Unknown token: ' + token);
}
},
DurationFormat: function (locales = navigator.languages, options = {}) {
const resolvedOptions = Object.assign({
locale: locales[0],
maxUnit: 'hour',
minUnit: 'second'
}, options);
const numFormatter = Intl.NumberFormat(locales, {
style: 'decimal',
useGrouping: false,
minimumIntegerDigits: 2
});
const maxUnitIdx = getDurationUnitIdx(resolvedOptions.maxUnit, 0);
const minUnitIdx = getDurationUnitIdx(resolvedOptions.minUnit, durationFormatOrder.length - 1);
return navigator.mozL10n.formatValue('durationPattern').then(fmt => ({
resolvedOptions: function () {
return resolvedOptions;
},
format: function (input) {
const minValue = durationFormatElements[resolvedOptions.minUnit].value;
input = Math.round(input / minValue) * minValue;
const duration = splitIntoTimeUnits(input, maxUnitIdx, minUnitIdx);
var string = trimDurationPattern(fmt, resolvedOptions.maxUnit, resolvedOptions.minUnit);
for (var unit in duration) {
const token = durationFormatElements[unit].token;
string = string.replace(token, numFormatter.format(duration[unit]));
}
if (input < 0) {
return '-' + string;
}
return string;
}
}));
},
RelativeTimeFormat: function (locales, options) {
return {
resolvedOptions: function () {
return options;
},
format: function (x) {
const {unit, value} = relativeTimeFormatId(x, options);
return navigator.mozL10n.formatValue(unit, { value });
}
};
},
UnitFormat: function (locales, options) {
const unitGroup = getUnitFormatGroupName(options.unit);
if (unitGroup === undefined) {
throw new RangeError(`invalid value ${ options.unit } for option unit`);
}
if (!unitFormatGroups[unitGroup].styles.includes(options.style)) {
throw new RangeError(`invalid value ${ options.style } for option style`);
}
const unit = `${ unitGroup }-${ options.unit }-${ options.style }`;
return {
format: function (x) {
return document.l10n.formatValue(unit, { value: x });
}
};
},
_gaia: {
relativePart: function (milliseconds) {
const units = computeTimeUnits(milliseconds);
const unit = getBestMatchUnit(units);
return {
unit: unit + 's',
value: Math.abs(units[unit])
};
},
RelativeDate: function (locales, options) {
const style = options && options.style || 'long';
const maxFormatter = Intl.DateTimeFormat(locales, {
year: 'numeric',
month: 'numeric',
day: 'numeric'
});
const relativeFmtOptions = {
unit: 'bestFit',
style: style,
minUnit: 'minute'
};
return {
format: function (time, maxDiff) {
maxDiff = maxDiff || 86400 * 10;
const secDiff = (Date.now() - time) / 1000;
if (isNaN(secDiff)) {
return navigator.mozL10n.formatValue('incorrectDate');
}
if (secDiff > maxDiff) {
return Promise.resolve(maxFormatter.format(time));
}
const {unit, value} = relativeTimeFormatId(time, relativeFmtOptions);
return navigator.mozL10n.formatValue(unit, { value });
},
formatElement: function (element, time, maxDiff) {
maxDiff = maxDiff || 86400 * 10;
const secDiff = (Date.now() - time) / 1000;
if (isNaN(secDiff)) {
element.setAttribute('data-l10n-id', 'incorrectDate');
}
element.removeAttribute('data-l10n-id');
if (secDiff > maxDiff) {
element.textContent = maxFormatter.format(time);
}
const {unit, value} = relativeTimeFormatId(time, relativeFmtOptions);
navigator.mozL10n.setAttributes(element, unit, { value });
}
};
},
getFormattedUnit: function (type, style, v) {
if (!unitFormatData.hasOwnProperty(type)) {
throw new RangeError(`invalid type ${ type }`);
}
if (!unitFormatGroups[type].styles.includes(style)) {
throw new RangeError(`invalid style ${ style } for type ${ type }`);
}
var units = unitFormatData[type];
var scale = 0;
for (let i = 1; i < units.length; i++) {
if (v < units[i].value * unitFormatGroups[type].rounding) {
scale = i - 1;
break;
} else if (i === units.length - 1) {
scale = i;
}
}
var value = Math.round(v / units[scale].value * 100) / 100;
return global.mozIntl.UnitFormat(navigator.languages, {
unit: units[scale].name,
style: style
}).format(value);
}
}
};
const durationFormatOrder = [
'hour',
'minute',
'second',
'millisecond'
];
const durationFormatElements = {
'hour': {
value: 3600000,
token: 'hh'
},
'minute': {
value: 60000,
token: 'mm'
},
'second': {
value: 1000,
token: 'ss'
},
'millisecond': {
value: 10,
token: 'SS'
}
};
const unitFormatData = {
'duration': [
{
'name': 'second',
'value': 1
},
{
'name': 'minute',
'value': 60
},
{
'name': 'hour',
'value': 60 * 60
},
{
'name': 'day',
'value': 24 * 60 * 60
},
{
'name': 'month',
'value': 30 * 24 * 60 * 60
}
],
'digital': [
{
'name': 'byte',
'value': 1
},
{
'name': 'kilobyte',
'value': 1024
},
{
'name': 'megabyte',
'value': 1024 * 1024
},
{
'name': 'gigabyte',
'value': 1024 * 1024 * 1024
},
{
'name': 'terabyte',
'value': 1024 * 1024 * 1024 * 1024
}
]
};
const unitFormatGroups = {
'duration': {
'units': [
'second',
'minute',
'hour',
'day',
'month'
],
'styles': ['narrow'],
'rounding': 1
},
'digital': {
'units': [
'byte',
'kilobyte',
'megabyte',
'gigabyte',
'terabyte'
],
'styles': ['short'],
'rounding': 0.8
}
};
function getDurationUnitIdx(name, defaultValue) {
if (!name) {
return defaultValue;
}
const pos = durationFormatOrder.indexOf(name);
if (pos === -1) {
throw new Error('Unknown unit type: ' + name);
}
return pos;
}
function splitIntoTimeUnits(v, maxUnitIdx, minUnitIdx) {
const units = {};
var input = Math.abs(v);
for (var i = maxUnitIdx; i <= minUnitIdx; i++) {
const key = durationFormatOrder[i];
const {value} = durationFormatElements[key];
units[key] = i == minUnitIdx ? Math.round(input / value) : Math.floor(input / value);
input -= units[key] * value;
}
return units;
}
function trimDurationPattern(string, maxUnit, minUnit) {
const maxToken = durationFormatElements[maxUnit].token;
const minToken = durationFormatElements[minUnit].token;
string = string.substring(string.indexOf(maxToken), string.indexOf(minToken) + minToken.length);
return string;
}
function getDayPeriodTokenForDate(date, hourFormatter) {
const hourToken = hourFormatter.format(date);
const newDate = new Date(date);
newDate.setHours(parseInt(hourToken));
return new Intl.DateTimeFormat(navigator.language, {hour:'numeric', hour12:true}).formatToParts(newDate).find(part => part.type === 'dayPeriod').value;
}
function computeTimeUnits(v) {
const units = {};
const millisecond = Math.round(v);
const second = Math.round(millisecond / 1000);
const minute = Math.round(second / 60);
const hour = Math.round(minute / 60);
const day = Math.round(hour / 24);
const rawYear = day * 400 / 146097;
units.millisecond = millisecond;
units.second = second;
units.minute = minute;
units.hour = hour;
units.day = day;
units.week = Math.round(day / 7);
units.month = Math.round(rawYear * 12);
units.quarter = Math.round(rawYear * 4);
units.year = Math.round(rawYear);
return units;
}
function getBestMatchUnit(units) {
if (Math.abs(units.minute) < 45) {
return 'minute';
}
if (Math.abs(units.hour) < 22) {
return 'hour';
}
if (Math.abs(units.day) < 7) {
return 'day';
}
if (Math.abs(units.week) < 4) {
return 'week';
}
if (Math.abs(units.month) < 11) {
return 'month';
}
return 'year';
}
function relativeTimeFormatId(x, options) {
const ms = x - Date.now();
const units = computeTimeUnits(ms);
const unit = options.unit === 'bestFit' ? getBestMatchUnit(units) : options.unit;
const v = units[unit];
const tl = v < 0 ? '-ago' : '-until';
const style = options.style || 'long';
const entry = unit + 's' + tl + '-' + style;
return {
unit: entry,
value: Math.abs(v)
};
}
function getUnitFormatGroupName(unitName) {
for (let groupName in unitFormatGroups) {
if (unitFormatGroups[groupName].units.includes(unitName)) {
return groupName;
}
}
return undefined;
}
}(this));
define('moz_intl', ['l10nbase'], function (global) {
return function () {
var ret, fn;
return ret || global.mozIntl;
};
}(this));
define('text', {
load: function (name, req, onload, config) {
var url = req.toUrl(name), xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function (evt) {
var status, err;
if (xhr.readyState === 4) {
status = xhr.status;
if (status > 399 && status < 600) {
err = new Error(url + ' HTTP status: ' + status);
err.xhr = xhr;
onload.error(err);
} else {
onload(xhr.responseText);
}
}
};
xhr.responseType = 'text';
xhr.send(null);
}
});
define('folder_depth_classes', [], function () {
return [
'fld-folder-depth0',
'fld-folder-depth1',
'fld-folder-depth2',
'fld-folder-depth3',
'fld-folder-depth4',
'fld-folder-depth5',
'fld-folder-depthmax'
];
});
define('tmpl!cards/value_selector.html', ['tmpl'], function (tmpl) {
return tmpl.toDom('
');
});
define('tmpl!cards/vsl/item.html', ['tmpl'], function (tmpl) {
return tmpl.toDom('');
});
define('value_selector', [
'require',
'cards',
'folder_depth_classes',
'tmpl!cards/value_selector.html',
'tmpl!cards/vsl/item.html'
], function (require) {
'use strict';
var cards = require('cards'), FOLDER_DEPTH_CLASSES = require('folder_depth_classes'), formNode = require('tmpl!cards/value_selector.html'), itemTemplateNode = require('tmpl!cards/vsl/item.html');
function noop() {
}
function ValueSelector(title, list) {
var init, show, hide, render, setTitle, emptyList, addToList, data;
init = function () {
data = {
title: 'No Title',
list: [{
label: 'Dummy element',
callback: function () {
alert('Define an action here!');
}
}]
};
document.body.appendChild(formNode);
var btnCancel = formNode.querySelector('button');
btnCancel.addEventListener('click', function (event) {
event.stopPropagation();
event.preventDefault();
hide();
});
emptyList();
if (typeof title === 'string') {
setTitle(title);
}
if (Array.isArray(list)) {
data.list = list;
}
};
show = function () {
render();
cards.setStatusColor(formNode);
formNode.classList.remove('collapsed');
};
hide = function () {
cards.setStatusColor();
formNode.classList.add('collapsed');
emptyList();
};
render = function () {
var title = formNode.querySelector('h1'), list = formNode.querySelector('ol');
title.textContent = data.title;
list.innerHTML = '';
data.list.forEach(function (listItem) {
var node = itemTemplateNode.cloneNode(true);
node.querySelector('span').textContent = listItem.label;
var depthIdx = listItem.depth;
depthIdx = Math.min(FOLDER_DEPTH_CLASSES.length - 1, depthIdx);
node.classList.add(FOLDER_DEPTH_CLASSES[depthIdx]);
var callback = listItem.selectable ? listItem.callback : noop;
node.addEventListener('click', callback, false);
list.appendChild(node);
});
};
setTitle = function (str) {
data.title = str;
};
emptyList = function () {
data.list = [];
};
addToList = function (label, depth, selectable, callback) {
data.list.push({
label: label,
depth: depth,
selectable: selectable,
callback: callback
});
};
init();
return {
init: init,
show: show,
hide: hide,
setTitle: setTitle,
addToList: addToList,
List: list
};
}
return ValueSelector;
});
var GestureDetector = function () {
function GD(e, options) {
this.element = e;
this.options = options || {};
this.options.panThreshold = this.options.panThreshold || GD.PAN_THRESHOLD;
this.state = initialState;
this.timers = {};
}
GD.prototype.startDetecting = function () {
var self = this;
eventtypes.forEach(function (t) {
self.element.addEventListener(t, self);
});
};
GD.prototype.stopDetecting = function () {
var self = this;
eventtypes.forEach(function (t) {
self.element.removeEventListener(t, self);
});
};
GD.prototype.handleEvent = function (e) {
var handler = this.state[e.type];
if (!handler) {
return;
}
if (e.changedTouches) {
for (var i = 0; i < e.changedTouches.length; i++) {
handler(this, e, e.changedTouches[i]);
handler = this.state[e.type];
if (!handler) {
return;
}
}
} else {
handler(this, e);
}
};
GD.prototype.startTimer = function (type, time) {
this.clearTimer(type);
var self = this;
this.timers[type] = setTimeout(function () {
self.timers[type] = null;
var handler = self.state[type];
if (handler) {
handler(self, type);
}
}, time);
};
GD.prototype.clearTimer = function (type) {
if (this.timers[type]) {
clearTimeout(this.timers[type]);
this.timers[type] = null;
}
};
GD.prototype.switchTo = function (state, event, touch) {
this.state = state;
if (state.init) {
state.init(this, event, touch);
}
};
GD.prototype.emitEvent = function (type, detail) {
if (!this.target) {
console.error('Attempt to emit event with no target');
return;
}
var event = this.element.ownerDocument.createEvent('CustomEvent');
event.initCustomEvent(type, true, true, detail);
this.target.dispatchEvent(event);
};
GD.HOLD_INTERVAL = 1000;
GD.PAN_THRESHOLD = 20;
GD.DOUBLE_TAP_DISTANCE = 50;
GD.DOUBLE_TAP_TIME = 500;
GD.VELOCITY_SMOOTHING = 0.5;
GD.SCALE_THRESHOLD = 20;
GD.ROTATE_THRESHOLD = 22.5;
GD.THRESHOLD_SMOOTHING = 0.9;
var abs = Math.abs, floor = Math.floor, sqrt = Math.sqrt, atan2 = Math.atan2;
var PI = Math.PI;
var eventtypes = [
'touchstart',
'touchmove',
'touchend'
];
function eventTime(e) {
var ts = e.timeStamp;
if (ts > 2 * Date.now()) {
return Math.floor(ts / 1000);
} else {
return ts;
}
}
function coordinates(e, t) {
return Object.freeze({
screenX: t.screenX,
screenY: t.screenY,
clientX: t.clientX,
clientY: t.clientY,
timeStamp: eventTime(e)
});
}
function midpoints(e, t1, t2) {
return Object.freeze({
screenX: floor((t1.screenX + t2.screenX) / 2),
screenY: floor((t1.screenY + t2.screenY) / 2),
clientX: floor((t1.clientX + t2.clientX) / 2),
clientY: floor((t1.clientY + t2.clientY) / 2),
timeStamp: eventTime(e)
});
}
function between(c1, c2) {
var r = GD.THRESHOLD_SMOOTHING;
return Object.freeze({
screenX: floor(c1.screenX + r * (c2.screenX - c1.screenX)),
screenY: floor(c1.screenY + r * (c2.screenY - c1.screenY)),
clientX: floor(c1.clientX + r * (c2.clientX - c1.clientX)),
clientY: floor(c1.clientY + r * (c2.clientY - c1.clientY)),
timeStamp: floor(c1.timeStamp + r * (c2.timeStamp - c1.timeStamp))
});
}
function touchDistance(t1, t2) {
var dx = t2.screenX - t1.screenX;
var dy = t2.screenY - t1.screenY;
return sqrt(dx * dx + dy * dy);
}
function touchDirection(t1, t2) {
return atan2(t2.screenY - t1.screenY, t2.screenX - t1.screenX) * 180 / PI;
}
function touchRotation(d1, d2) {
var angle = d2 - d1;
if (angle > 180) {
angle -= 360;
} else if (angle <= -180) {
angle += 360;
}
return angle;
}
function isDoubleTap(lastTap, thisTap) {
var dx = abs(thisTap.screenX - lastTap.screenX);
var dy = abs(thisTap.screenY - lastTap.screenY);
var dt = thisTap.timeStamp - lastTap.timeStamp;
return dx < GD.DOUBLE_TAP_DISTANCE && dy < GD.DOUBLE_TAP_DISTANCE && dt < GD.DOUBLE_TAP_TIME;
}
var initialState = {
name: 'initialState',
init: function (d, e, t) {
d.target = null;
d.start = d.last = null;
d.touch1 = d.touch2 = null;
d.vx = d.vy = null;
d.startDistance = d.lastDistance = null;
d.startDirection = d.lastDirection = null;
d.lastMidpoint = null;
d.scaled = d.rotated = null;
if (e && t && e.type === 'touchstart') {
initialState.touchstart(d, e, t);
}
},
touchstart: function (d, e, t) {
d.switchTo(touchStartedState, e, t);
}
};
var touchStartedState = {
name: 'touchStartedState',
init: function (d, e, t) {
d.target = e.target;
d.touch1 = t.identifier;
d.start = d.last = coordinates(e, t);
if (d.options.holdEvents) {
d.startTimer('holdtimeout', GD.HOLD_INTERVAL);
}
},
touchstart: function (d, e, t) {
d.clearTimer('holdtimeout');
if (e.touches.length > 1) {
d.switchTo(transformState, e, t);
} else {
console.warn('Ignoring missing touchend event. See bug 1162771.');
d.switchTo(initialState, e, t);
}
},
touchmove: function (d, e, t) {
if (t.identifier !== d.touch1) {
return;
}
if (abs(t.screenX - d.start.screenX) > d.options.panThreshold || abs(t.screenY - d.start.screenY) > d.options.panThreshold) {
d.clearTimer('holdtimeout');
d.switchTo(panStartedState, e, t);
}
},
touchend: function (d, e, t) {
if (t.identifier !== d.touch1) {
return;
}
if (d.lastTap && isDoubleTap(d.lastTap, d.start)) {
d.emitEvent('tap', d.start);
d.emitEvent('dbltap', d.start);
d.lastTap = null;
} else {
d.emitEvent('tap', d.start);
d.lastTap = coordinates(e, t);
}
d.clearTimer('holdtimeout');
d.switchTo(initialState);
},
holdtimeout: function (d) {
d.switchTo(holdState);
}
};
var panStartedState = {
name: 'panStartedState',
init: function (d, e, t) {
d.start = d.last = between(d.start, coordinates(e, t));
if (e.type === 'touchmove') {
panStartedState.touchmove(d, e, t);
}
},
touchmove: function (d, e, t) {
if (t.identifier !== d.touch1) {
return;
}
var current = coordinates(e, t);
d.emitEvent('pan', {
absolute: {
dx: current.screenX - d.start.screenX,
dy: current.screenY - d.start.screenY
},
relative: {
dx: current.screenX - d.last.screenX,
dy: current.screenY - d.last.screenY
},
position: current
});
var dt = current.timeStamp - d.last.timeStamp;
var vx = (current.screenX - d.last.screenX) / dt;
var vy = (current.screenY - d.last.screenY) / dt;
if (d.vx == null) {
d.vx = vx;
d.vy = vy;
} else {
d.vx = d.vx * GD.VELOCITY_SMOOTHING + vx * (1 - GD.VELOCITY_SMOOTHING);
d.vy = d.vy * GD.VELOCITY_SMOOTHING + vy * (1 - GD.VELOCITY_SMOOTHING);
}
d.last = current;
},
touchend: function (d, e, t) {
if (t.identifier !== d.touch1) {
return;
}
var current = coordinates(e, t);
var dx = current.screenX - d.start.screenX;
var dy = current.screenY - d.start.screenY;
var angle = atan2(dy, dx) * 180 / PI;
if (angle < 0) {
angle += 360;
}
var direction;
if (angle >= 315 || angle < 45) {
direction = 'right';
} else if (angle >= 45 && angle < 135) {
direction = 'down';
} else if (angle >= 135 && angle < 225) {
direction = 'left';
} else if (angle >= 225 && angle < 315) {
direction = 'up';
}
d.emitEvent('swipe', {
start: d.start,
end: current,
dx: dx,
dy: dy,
dt: e.timeStamp - d.start.timeStamp,
vx: d.vx,
vy: d.vy,
direction: direction,
angle: angle
});
d.switchTo(initialState);
}
};
var holdState = {
name: 'holdState',
init: function (d) {
d.emitEvent('holdstart', d.start);
},
touchmove: function (d, e, t) {
var current = coordinates(e, t);
d.emitEvent('holdmove', {
absolute: {
dx: current.screenX - d.start.screenX,
dy: current.screenY - d.start.screenY
},
relative: {
dx: current.screenX - d.last.screenX,
dy: current.screenY - d.last.screenY
},
position: current
});
d.last = current;
},
touchend: function (d, e, t) {
var current = coordinates(e, t);
d.emitEvent('holdend', {
start: d.start,
end: current,
dx: current.screenX - d.start.screenX,
dy: current.screenY - d.start.screenY
});
d.switchTo(initialState);
}
};
var transformState = {
name: 'transformState',
init: function (d, e, t) {
d.touch2 = t.identifier;
var t1 = e.touches.identifiedTouch(d.touch1);
var t2 = e.touches.identifiedTouch(d.touch2);
d.startDistance = d.lastDistance = touchDistance(t1, t2);
d.startDirection = d.lastDirection = touchDirection(t1, t2);
d.scaled = d.rotated = false;
},
touchmove: function (d, e, t) {
if (t.identifier !== d.touch1 && t.identifier !== d.touch2) {
return;
}
var t1 = e.touches.identifiedTouch(d.touch1);
var t2 = e.touches.identifiedTouch(d.touch2);
var midpoint = midpoints(e, t1, t2);
var distance = touchDistance(t1, t2);
var direction = touchDirection(t1, t2);
var rotation = touchRotation(d.startDirection, direction);
if (!d.scaled) {
if (abs(distance - d.startDistance) > GD.SCALE_THRESHOLD) {
d.scaled = true;
d.startDistance = d.lastDistance = floor(d.startDistance + GD.THRESHOLD_SMOOTHING * (distance - d.startDistance));
} else {
distance = d.startDistance;
}
}
if (!d.rotated) {
if (abs(rotation) > GD.ROTATE_THRESHOLD) {
d.rotated = true;
} else {
direction = d.startDirection;
}
}
if (d.scaled || d.rotated) {
d.emitEvent('transform', {
absolute: {
scale: distance / d.startDistance,
rotate: touchRotation(d.startDirection, direction)
},
relative: {
scale: distance / d.lastDistance,
rotate: touchRotation(d.lastDirection, direction)
},
midpoint: midpoint
});
d.lastDistance = distance;
d.lastDirection = direction;
d.lastMidpoint = midpoint;
}
},
touchend: function (d, e, t) {
if (t.identifier === d.touch2) {
d.touch2 = null;
} else if (t.identifier === d.touch1) {
d.touch1 = d.touch2;
d.touch2 = null;
} else {
return;
}
if (d.scaled || d.rotated) {
d.emitEvent('transformend', {
absolute: {
scale: d.lastDistance / d.startDistance,
rotate: touchRotation(d.startDirection, d.lastDirection)
},
relative: {
scale: 1,
rotate: 0
},
midpoint: d.lastMidpoint
});
}
d.switchTo(afterTransformState);
}
};
var afterTransformState = {
name: 'afterTransformState',
touchstart: function (d, e, t) {
d.switchTo(transformState, e, t);
},
touchend: function (d, e, t) {
if (t.identifier === d.touch1) {
d.switchTo(initialState);
}
}
};
return GD;
}();
define('shared/js/gesture_detector', function (global) {
return function () {
var ret, fn;
return ret || global.GestureDetector;
};
}(this));
define('iframe_shims', ['shared/js/gesture_detector'], function (GestureDetector) {
var DEFAULT_STYLE_TAG = '';
var iframeShimsOpts = {
zoomDelayMS: 200,
initialScale: null,
resizeLimit: 4,
initialResizePollIntervalMS: 200,
noResizePollIntervalMS: 250,
didResizePollIntervalMS: 300,
pictureDelayPollIntervalMS: 200
};
function createAndInsertIframeForContent(htmlStr, scrollContainer, parentNode, beforeNode, interactiveMode, clickHandler) {
var scrollPad = 0;
var viewportWidth = parentNode.offsetWidth - scrollPad;
var viewport = document.createElement('div');
viewport.setAttribute('style', 'padding: 0; border-width: 0; margin: 0; ' + 'overflow: hidden;');
viewport.style.width = viewportWidth + 'px';
var iframe = document.createElement('iframe');
iframe.setAttribute('sandbox', 'allow-same-origin');
iframe.setAttribute('style', 'padding: 0; border-width: 0; margin: 0; ' + 'overflow: hidden; ' + 'transform-origin: top ' + (document.documentElement.dir === 'rtl' ? 'right' : 'left') + '; ' + 'pointer-events: none;');
if (iframeShimsOpts.tapTransform) {
iframe.style.transform = 'scale(1)';
}
iframe.style.width = viewportWidth + 'px';
viewport.appendChild(iframe);
parentNode.insertBefore(viewport, beforeNode);
iframe.contentDocument.open();
iframe.contentDocument.write('');
iframe.contentDocument.write(DEFAULT_STYLE_TAG);
iframe.contentDocument.write('');
iframe.contentDocument.write(htmlStr);
iframe.contentDocument.write('');
iframe.contentDocument.close();
var iframeBody = iframe.contentDocument.body;
var scrollWidth = iframeBody.scrollWidth;
var scrollHeight = iframeBody.scrollHeight;
var baseScale = Math.min(1, viewportWidth / scrollWidth), lastRequestedScale = iframeShimsOpts.initialScale || baseScale, scale = lastRequestedScale, lastDoubleTapScale = scale, scaleMode = 0;
viewport.style.width = Math.ceil(scrollWidth * scale) + 'px';
viewport.style.height = Math.ceil(scrollHeight * scale) + 'px';
iframe.style.width = scrollWidth + 'px';
var resizeFrame = function (why) {
if (why === 'initial' || why === 'poll') {
scrollWidth = iframeBody.scrollWidth;
scrollHeight = iframeBody.scrollHeight;
var oldBaseScale = baseScale;
baseScale = Math.min(1, viewportWidth / scrollWidth);
if (scale === oldBaseScale) {
scale = baseScale;
}
iframe.style.width = scrollWidth + 'px';
console.log('iframe_shims: recalculating height / width because', why, 'sw', scrollWidth, 'sh', scrollHeight, 'bs', baseScale);
}
console.log('iframe_shims: scale:', scale);
iframe.style.transform = 'scale(' + scale + ')';
iframe.style.height = scrollHeight * Math.max(1, scale) + scrollPad + 'px';
viewport.style.width = Math.ceil(scrollWidth * scale) + 'px';
viewport.style.height = Math.ceil(scrollHeight * scale) + scrollPad + 'px';
};
resizeFrame('initial');
var activeZoom = false, lastCenterX, lastCenterY;
var zoomFrame = function (newScale, centerX, centerY) {
if (newScale === scale) {
return;
}
lastRequestedScale = newScale;
lastCenterX = centerX;
lastCenterY = centerY;
if (activeZoom) {
return;
}
activeZoom = true;
var iframeScrolledTop = scrollContainer.scrollTop - extraHeight, iframeScrolledLeft = scrollContainer.scrollLeft;
var ix = centerX + iframeScrolledLeft, iy = centerY + iframeScrolledTop;
var scaleDelta = newScale / scale;
var vertScrollDelta = Math.ceil(iy * scaleDelta), horizScrollDelta = Math.ceil(ix * scaleDelta);
scale = newScale;
resizeFrame('zoom');
scrollContainer.scrollTop = vertScrollDelta + extraHeight - centerY;
scrollContainer.scrollLeft = horizScrollDelta - centerX;
window.setTimeout(clearActiveZoom, iframeShimsOpts.zoomDelayMS);
};
var clearActiveZoom = function () {
activeZoom = false;
if (scale !== lastRequestedScale) {
window.requestAnimationFrame(function () {
console.log('delayed zoomFrame timeout, probably causing a mem-spike');
zoomFrame(lastRequestedScale, lastCenterX, lastCenterY);
});
}
};
var resizePollerTimeout = null;
var resizePollCount = 0;
var pollResize = function () {
var opts = iframeShimsOpts;
var desiredScrollWidth = iframeBody.scrollWidth;
var desiredScrollHeight = iframeBody.scrollHeight;
var resized = false;
if (desiredScrollWidth > scrollWidth || desiredScrollHeight > scrollHeight) {
resizeFrame('poll');
resized = true;
}
if (++resizePollCount < opts.resizeLimit) {
resizePollerTimeout = window.setTimeout(pollResize, resized ? opts.didResizePollIntervalMS : opts.noResizePollIntervalMS);
} else {
resizePollerTimeout = null;
}
};
resizePollerTimeout = window.setTimeout(pollResize, iframeShimsOpts.initialResizePollIntervalMS);
var iframeShims = {
iframe: iframe,
resizeHandler: function () {
resizePollCount = 0;
if (resizePollerTimeout) {
window.clearTimeout(resizePollerTimeout);
}
resizePollerTimeout = window.setTimeout(pollResize, iframeShimsOpts.pictureDelayPollIntervalMS);
}
};
if (interactiveMode !== 'interactive') {
return iframeShims;
}
var detectorTarget = viewport;
var detector = new GestureDetector(detectorTarget);
detector.startDetecting();
if (clickHandler) {
viewport.removeEventListener('click', clickHandler);
bindSanitizedClickHandler(viewport, clickHandler, null, iframe);
}
var title = document.getElementsByClassName('msg-reader-header')[0];
var header = document.getElementsByClassName('msg-envelope-bar')[0];
var extraHeight = title.clientHeight + header.clientHeight;
detectorTarget.addEventListener('dbltap', function (e) {
var newScale = scale;
if (lastDoubleTapScale === lastRequestedScale) {
scaleMode = (scaleMode + 1) % 3;
switch (scaleMode) {
case 0:
newScale = baseScale;
break;
case 1:
newScale = 1;
break;
case 2:
newScale = 2;
break;
}
console.log('already in double-tap, deciding on new scale', newScale);
} else {
if (lastRequestedScale > 1) {
newScale = lastDoubleTapScale;
scaleMode = 0;
} else {
newScale = 2;
scaleMode = 2;
}
console.log('user was not in double-tap switching to double-tap with', newScale);
}
lastDoubleTapScale = newScale;
try {
zoomFrame(newScale, e.detail.clientX, e.detail.clientY);
} catch (ex) {
console.error('zoom bug!', ex, '\n', ex.stack);
}
});
var transformDone = false;
detectorTarget.addEventListener('transformend', function (e) {
transformDone = false;
});
detectorTarget.addEventListener('transform', function (e) {
if (transformDone) {
return;
}
var scaleFactor = e.detail.absolute.scale;
var newScale = lastRequestedScale;
if (scaleFactor > 1.15) {
transformDone = true;
if (lastRequestedScale < 1) {
newScale = 1;
} else if (lastRequestedScale < 1.5) {
newScale = 1.5;
} else if (lastRequestedScale < 2) {
newScale = 2;
} else {
return;
}
} else if (scaleFactor < 0.9) {
transformDone = true;
if (lastRequestedScale > 1.5) {
newScale = 1.5;
} else if (lastRequestedScale > 1) {
newScale = 1;
} else if (lastRequestedScale > baseScale) {
newScale = baseScale;
} else {
return;
}
} else {
return;
}
zoomFrame(newScale, e.detail.midpoint.clientX, e.detail.midpoint.clientY);
});
return iframeShims;
}
function bindSanitizedClickHandler(target, clickHandler, topNode, iframe) {
var eventType, node;
var root, title, header, attachmentsContainer, msgBodyContainer, titleHeight, headerHeight, attachmentsHeight, msgBodyMarginTop, msgBodyMarginLeft, attachmentsMarginTop, iframeDoc, inputStyle, loadBar, loadBarHeight;
if (iframe) {
root = document.getElementsByClassName('scrollregion-horizontal-too')[0];
title = document.getElementsByClassName('msg-reader-header')[0];
header = document.getElementsByClassName('msg-envelope-bar')[0];
attachmentsContainer = document.getElementsByClassName('msg-attachments-container')[0];
loadBar = document.getElementsByClassName('msg-reader-load-infobar')[0];
msgBodyContainer = document.getElementsByClassName('msg-body-container')[0];
inputStyle = window.getComputedStyle(msgBodyContainer);
msgBodyMarginTop = parseInt(inputStyle.marginTop);
msgBodyMarginLeft = parseInt(inputStyle.marginLeft);
titleHeight = title.clientHeight;
headerHeight = header.clientHeight;
eventType = 'tap';
iframeDoc = iframe.contentDocument;
} else {
eventType = 'click';
}
target.addEventListener(eventType, function clicked(event) {
if (iframe) {
loadBarHeight = loadBar.clientHeight;
attachmentsHeight = attachmentsContainer.clientHeight;
inputStyle = window.getComputedStyle(attachmentsContainer);
attachmentsMarginTop = attachmentsHeight ? parseInt(inputStyle.marginTop) : 0;
var dx, dy;
var transform = iframe.style.transform || 'scale(1)';
var scale = transform.match(/(\d|\.)+/g)[0];
if (document.dir === 'rtl') {
dx = event.detail.clientX - msgBodyMarginLeft + root.scrollWidth + root.scrollLeft - root.clientWidth;
} else {
dx = event.detail.clientX + root.scrollLeft - msgBodyMarginLeft;
}
dy = event.detail.clientY + root.scrollTop - titleHeight - headerHeight - loadBarHeight - attachmentsHeight - attachmentsMarginTop - msgBodyMarginTop;
node = iframeDoc.elementFromPoint(dx / scale, dy / scale);
} else {
node = event.originalTarget;
}
while (node !== topNode) {
if (node.nodeName === 'A') {
if (node.hasAttribute('ext-href')) {
clickHandler(event, node, node.getAttribute('ext-href'), node.textContent);
event.preventDefault();
event.stopPropagation();
return;
}
}
node = node.parentNode;
}
});
}
return {
createAndInsertIframeForContent: createAndInsertIframeForContent,
bindSanitizedClickHandler: bindSanitizedClickHandler,
iframeShimsOpts: iframeShimsOpts
};
});
define('cards/editor_mixins', ['require'], function (require) {
return {
_bindEditor: function (textNode) {
this._editorNode = textNode;
textNode.addEventListener('paste', function (event) {
event.preventDefault();
var text = event.clipboardData.getData('text/plain');
if (text) {
document.execCommand('insertText', false, text);
}
});
},
populateEditor: function (value) {
var lines = value.split('\n');
var frag = document.createDocumentFragment();
for (var i = 0, len = lines.length; i < len; i++) {
if (i) {
frag.appendChild(document.createElement('br'));
}
if (lines[i]) {
frag.appendChild(document.createTextNode(lines[i]));
}
}
if (!frag.childNodes.length) {
frag.appendChild(document.createTextNode(''));
}
this._editorNode.appendChild(frag);
},
fromEditor: function (value) {
var content = '';
var len = this._editorNode.childNodes.length;
for (var i = 0; i < len; i++) {
var node = this._editorNode.childNodes[i];
if (node.nodeName === 'BR' && node.getAttribute('type') !== '_moz') {
content += '\n';
} else {
content += node.textContent;
}
}
return content;
}
};
});
define('date', [
'require',
'moz_intl'
], function (require) {
const mozIntl = require('moz_intl');
var shortRelativeDateFmt, longRelativeDateFmt;
function setRelativeDateFormatters() {
shortRelativeDateFmt = mozIntl._gaia.RelativeDate(navigator.languages, { style: 'short' });
longRelativeDateFmt = mozIntl._gaia.RelativeDate(navigator.languages, { style: 'long' });
}
setRelativeDateFormatters();
window.addEventListener('languagechange', setRelativeDateFormatters);
var date = {
relativeDateElement: function (element, time, useCompactFormat) {
if (time) {
var f = useCompactFormat ? shortRelativeDateFmt : longRelativeDateFmt;
f.formatElement(element, time);
} else {
element.textContent = '';
}
},
setPrettyNodeDate: function (node, timestamp) {
if (timestamp) {
node.dataset.time = timestamp.valueOf();
node.dataset.compactFormat = true;
date.relativeDateElement(node, timestamp, true);
} else {
node.textContent = '';
node.removeAttribute('data-time');
}
}
};
return date;
});
define('cards/lst/default_vscroll_data', {
'isPlaceholderData': true,
'id': 'INVALID',
'author': {
'name': '\u2583\u2583\u2583\u2583\u2583\u2583\u2583\u2583',
'address': '',
'contactId': null
},
'to': [{
'name': ' ',
'address': ' ',
'contactId': null
}],
'cc': null,
'bcc': null,
'date': '0',
'hasAttachments': false,
'snippet': '\u2583\u2583\u2583\u2583\u2583\u2583\u2583\u2583' + '\u2583\u2583\u2583\u2583\u2583\u2583\u2583\u2583' + '\u2583\u2583\u2583\u2583\u2583\u2583\u2583\u2583',
'isRead': true,
'isStarred': false,
'sendStatus': {},
'subject': '\u2583\u2583\u2583\u2583\u2583\u2583\u2583\u2583' + '\u2583\u2583\u2583\u2583\u2583\u2583\u2583\u2583' + '\u2583\u2583\u2583\u2583\u2583\u2583\u2583\u2583'
});
define('message_list_topbar', [
'require',
'exports',
'module',
'l10n!',
'transition_end'
], function (require, exports, module) {
var mozL10n = require('l10n!'), transitionEnd = require('transition_end');
var proto = {
domNode: null,
_scrollContainer: null,
_vScroll: null,
_delayedState: null,
_newEmailCount: 0,
_scrollTop: 0,
_thresholdMultiplier: 2,
visibleOffset: 0,
createdCallback: function () {
this.domNode.addEventListener('click', this._onClick.bind(this));
transitionEnd(this.domNode, this._onTransitionEnd.bind(this));
},
resetNodeForCache: function (node) {
node.classList.remove('closing');
node.textContent = '';
node.dataset.state = '';
this.domNode.style.left = '';
},
bindToElements: function (scrollContainer, vScroll) {
this._scrollContainer = scrollContainer;
this._scrollContainer.addEventListener('scroll', this._onScroll.bind(this));
this._scrollTop = this._scrollContainer.scrollTop;
this._vScroll = vScroll;
},
showNewEmailCount: function (newEmailCount) {
if (this._scrollTop <= this.visibleOffset) {
return;
}
this._newEmailCount = newEmailCount;
this._showState('message');
},
_getState: function () {
return this.domNode.dataset.state;
},
_showState: function (state) {
var nodeState = this._getState();
if (nodeState === state) {
return;
} else if (!nodeState || !state) {
this._animateState(state);
} else if (nodeState !== state && (nodeState !== 'message' || state !== 'top')) {
this._delayedState = state;
if (!this._animating) {
this._animateState('');
}
}
},
_animateState: function (state) {
this._animating = true;
if (!state && this._getState()) {
this.domNode.classList.add('closing');
} else {
this.domNode.classList.add('no-anim');
this.domNode.classList.toggle('horiz-message', state === 'message');
this.domNode.classList.toggle('horiz-top', state === 'top');
this.domNode.clientWidth;
if (state === 'message') {
mozL10n.setAttributes(this.domNode, 'new-messages', { n: this._newEmailCount });
} else if (state === 'top') {
mozL10n.setAttributes(this.domNode, 'message-list-top-action');
}
this.domNode.classList.remove('no-anim');
this.domNode.clientWidth;
this.domNode.dataset.state = state;
}
},
_onTransitionEnd: function (evt) {
this._animating = false;
if (this.domNode.classList.contains('closing')) {
this.domNode.classList.remove('closing');
this.domNode.dataset.state = '';
this.domNode.style.left = '';
}
if (this._delayedState) {
this._animateState(this._delayedState);
this._delayedState = null;
}
},
_onScroll: function (evt) {
if (!this._topThreshold) {
var rect = this._scrollContainer.getBoundingClientRect();
this._topThreshold = rect.height * this._thresholdMultiplier;
this.domNode.style.top = rect.top + 'px';
}
if (this._vScroll.lastScrollTopSetTime && this._vScroll.lastScrollTopSetTime + 500 > Date.now()) {
return;
}
var scrollTop = this._scrollContainer.scrollTop, scrollingDown = scrollTop > this._scrollTop, nodeState = this._getState();
if (scrollTop !== this._scrollTop) {
if (scrollTop <= this.visibleOffset) {
this._showState('');
} else if (scrollingDown) {
this._showState('');
} else if (nodeState !== 'top' && scrollTop > this._topThreshold) {
this._showState('top');
}
}
this._scrollTop = scrollTop;
},
_onClick: function (evt) {
if (this._vScroll) {
this._vScroll.jumpToIndex(0);
this._showState('');
}
}
};
function MessageListTopBar(domNode) {
this.domNode = domNode;
this.createdCallback();
}
MessageListTopBar.prototype = proto;
return MessageListTopBar;
});
define('message_display', [
'require',
'l10n!'
], function (require) {
var mozL10n = require('l10n!');
return {
subject: function (subjectNode, message) {
var subject = message.subject && message.subject.trim();
if (subject) {
subjectNode.textContent = subject;
subjectNode.classList.remove('msg-no-subject');
subjectNode.removeAttribute('data-l10n-id');
} else {
mozL10n.setAttributes(subjectNode, 'message-no-subject');
subjectNode.classList.add('msg-no-subject');
}
}
};
});
define('cards/lst/peep_dom', [], function () {
return {
update: function (peep) {
peep.element.textContent = peep.name || peep.address;
}
};
});
define('vscroll', [
'require',
'exports',
'module',
'evt'
], function (require, exports, module) {
var evt = require('evt'), slice = Array.prototype.slice, useTransform = false;
function setTop(node, value) {
if (useTransform) {
node.style.transform = 'translateY(' + value + 'px)';
} else {
node.style.top = value + 'px';
}
}
function VScroll(container, scrollingContainer, template, defaultData) {
evt.Emitter.call(this);
this.container = container;
this.scrollingContainer = scrollingContainer;
this.template = template;
this.defaultData = defaultData;
this._inited = false;
this._capturedScreenMetrics = false;
this.firstRenderedIndex = 0;
this._limited = false;
this.nodes = [];
this.nodesDataIndices = {};
this.nodesIndex = -1;
this.scrollTop = 0;
this.visibleOffset = 0;
this.oldListSize = 0;
this._lastEventTime = 0;
this.onEvent = this.onEvent.bind(this);
this.onChange = this.onChange.bind(this);
this._scrollTimeoutPoll = this._scrollTimeoutPoll.bind(this);
}
VScroll.nodeClassName = 'vscroll-node';
VScroll.trimMessagesForCache = function (container, itemLimit) {
var nodes = slice.call(container.querySelectorAll('.' + VScroll.nodeClassName));
nodes.forEach(function (node) {
var index = parseInt(node.dataset.index, 10);
delete node.dataset.index;
if (index > itemLimit - 1) {
container.removeChild(node);
}
});
};
VScroll.prototype = {
eventRateLimitMillis: 0,
itemsPerScreen: undefined,
prerenderScreens: 3,
prefetchScreens: 2,
retainExtraRenderedScreens: 3,
recalculatePaddingScreens: 1.5,
lastScrollTopSetTime: 0,
prerenderItemCount: undefined,
prefetchItemCount: undefined,
recalculatePaddingItemCount: undefined,
itemDefaultDataClass: 'default-data',
prepareData: function (highAbsoluteIndex) {
},
bindData: function (model, node) {
},
setData: function (list) {
this.list = list;
if (this._inited) {
if (!this.waitingForRecalculate) {
this._recalculate(0);
}
this.emit('dataChanged');
} else {
this._init();
this.renderCurrentPosition();
}
},
updateDataBind: function (index, dataList, removedCount) {
if (!this._inited) {
return;
}
if (this.oldListSize !== this.list.size() || removedCount) {
if (!this.waitingForRecalculate) {
this.waitingForRecalculate = true;
this.once('scrollStopped', function () {
this._recalculate(index);
}.bind(this));
}
return;
}
for (var i = 0; i < dataList.length; i++) {
var absoluteIndex = index + i;
var node = this._getNodeFromDataIndex(absoluteIndex);
if (node) {
this.bindData(dataList[i], node);
}
}
},
onEvent: function () {
this._lastEventTime = Date.now();
if (!this.eventRateLimitMillis) {
this.onChange();
return;
}
if (this._limited) {
return;
}
this._limited = true;
setTimeout(this.onChange, this.eventRateLimitMillis);
},
onChange: function () {
this._limited = false;
if (!this._inited) {
return;
}
if (this.lastScrollTopSetTime) {
if (this.lastScrollTopSetTime + 1000 < Date.now()) {
this.lastScrollTopSetTime = 0;
}
}
var startIndex, endIndex, scrollTop = this.scrollingContainer.scrollTop, scrollingDown = scrollTop >= this.scrollTop;
this.scrollTop = scrollTop;
var visibleRange = this.getVisibleIndexRange();
if (scrollingDown) {
startIndex = visibleRange[0];
endIndex = visibleRange[1] + this.prerenderItemCount;
this.prepareData(endIndex + this.prefetchItemCount);
} else {
startIndex = visibleRange[0] - this.prerenderItemCount;
endIndex = visibleRange[1];
}
this._render(startIndex, endIndex);
this._startScrollStopPolling();
},
nowVisible: function () {
if (!this._inited && this.list) {
this._init();
this.onChange();
}
},
renderCurrentPosition: function () {
if (!this._inited) {
return;
}
var scrollTop = this.scrollingContainer.scrollTop;
this.scrollTop = scrollTop;
var visibleRange = this.getVisibleIndexRange();
var startIndex = visibleRange[0] - this.recalculatePaddingItemCount;
var endIndex = visibleRange[1] + this.recalculatePaddingItemCount;
this._render(startIndex, endIndex);
this.prepareData(endIndex);
},
indexAtScrollPosition: function (position) {
var top = position - this.visibleOffset;
if (top < 0) {
top = 0;
}
return this.itemHeight ? Math.floor(top / this.itemHeight) : 0;
},
getVisibleIndexRange: function () {
if (this.itemHeight === undefined) {
return undefined;
}
var top = this.scrollTop;
return [
this.indexAtScrollPosition(top),
this.indexAtScrollPosition(top + this.innerHeight)
];
},
jumpToIndex: function (index) {
this._setContainerScrollTop(index * this.itemHeight + this.visibleOffset);
},
clearDisplay: function () {
this.container.innerHTML = '';
this.container.style.height = '0px';
this.oldListSize = 0;
},
destroy: function () {
this.scrollingContainer.removeEventListener('scroll', this.onEvent);
if (this._scrollTimeoutPoll) {
clearTimeout(this._scrollTimeoutPoll);
this._scrollTimeoutPoll = 0;
}
},
_setContainerScrollTop: function (value) {
this.scrollingContainer.scrollTop = value;
this.lastScrollTopSetTime = Date.now();
},
_render: function (startIndex, endIndex) {
var i, listSize = this.list.size();
if (startIndex < 0) {
startIndex = 0;
}
if (endIndex >= listSize) {
endIndex = listSize - 1;
}
this.firstRenderedIndex = startIndex;
if (!this._inited) {
this._init();
}
for (i = startIndex; i <= endIndex; i++) {
if (this._getNodeFromDataIndex(i)) {
continue;
}
var node = this._nextAvailableNode(startIndex, endIndex), data = this.list(i);
if (!data) {
data = this.defaultData;
}
if (node.parentNode) {
node.parentNode.removeChild(node);
}
setTop(node, i * this.itemHeight);
this._setNodeDataIndex(this.nodesIndex, i);
this.bindData(data, node);
this.container.appendChild(node);
}
},
_setNodeDataIndex: function (nodesIndex, dataIndex) {
var oldDataIndex = this.nodes[nodesIndex].vScrollDataIndex;
if (oldDataIndex > -1) {
this.nodesDataIndices[oldDataIndex] = -1;
}
var node = this.nodes[nodesIndex];
node.vScrollDataIndex = dataIndex;
node.dataset.index = dataIndex;
this.nodesDataIndices[dataIndex] = nodesIndex;
},
_getNodeFromDataIndex: function (dataIndex) {
var index = this.nodesDataIndices[dataIndex];
if (index === undefined) {
index = -1;
}
return index === -1 ? null : this.nodes[index];
},
captureScreenMetrics: function () {
if (this._capturedScreenMetrics) {
return;
}
this.innerHeight = this.scrollingContainer.getBoundingClientRect().height;
if (this.innerHeight > 0) {
this._capturedScreenMetrics = true;
}
},
_init: function () {
if (this._inited) {
return;
}
this.container.innerHTML = '';
var node = this.template.cloneNode(true);
this.container.appendChild(node);
this.itemHeight = node.clientHeight;
this.container.removeChild(node);
this.captureScreenMetrics();
if (!this.itemHeight || !this.innerHeight) {
return;
}
this.scrollingContainer.addEventListener('scroll', this.onEvent);
this.itemsPerScreen = Math.ceil(this.innerHeight / this.itemHeight);
this.prerenderItemCount = Math.ceil(this.itemsPerScreen * this.prerenderScreens);
this.prefetchItemCount = Math.ceil(this.itemsPerScreen * this.prefetchScreens);
this.recalculatePaddingItemCount = Math.ceil(this.itemsPerScreen * this.recalculatePaddingScreens);
this.nodeCount = this.itemsPerScreen + this.prerenderItemCount + Math.ceil(this.retainExtraRenderedScreens * this.itemsPerScreen);
for (var i = 0; i < this.nodeCount; i++) {
node = this.template.cloneNode(true);
node.classList.add(VScroll.nodeClassName);
setTop(node, -1 * this.itemHeight);
this.nodes.push(node);
this._setNodeDataIndex(i, -1);
}
this._calculateTotalHeight();
this._inited = true;
this.emit('inited');
},
_nextAvailableNode: function (beginIndex, endIndex) {
var i, node, vScrollDataIndex, count = 0;
for (i = this.nodesIndex + 1; count < this.nodes.length; count++, i++) {
if (i > this.nodes.length - 1) {
i = 0;
}
node = this.nodes[i];
vScrollDataIndex = node.vScrollDataIndex;
if (vScrollDataIndex < beginIndex || vScrollDataIndex > endIndex) {
this.nodesIndex = i;
break;
}
}
return node;
},
_recalculate: function (refIndex) {
if (!this._inited) {
return;
}
var node, index = this.indexAtScrollPosition(this.scrollTop), remainder = this.scrollTop % this.itemHeight, sizeDiff = this.list.size() - this.oldListSize;
if (refIndex && refIndex < index && sizeDiff > 0 && this.oldListSize !== 0 && index !== 0) {
index += sizeDiff;
}
console.log('VSCROLL scrollTop: ' + this.scrollTop + ', RECALCULATE: ' + index + ', ' + remainder);
this._calculateTotalHeight();
for (var i = 0; i < this.nodeCount; i++) {
node = this.nodes[i];
setTop(node, -1 * this.itemHeight);
this._setNodeDataIndex(i, -1);
}
this.waitingForRecalculate = false;
this._setContainerScrollTop(this.itemHeight * index + remainder);
this.renderCurrentPosition();
this.emit('recalculated', index === 0, refIndex);
},
_calculateTotalHeight: function () {
var newListSize = this.list.size();
if (this.oldListSize !== newListSize || parseInt(this.container.style.height, 10) === 0) {
this.totalHeight = this.itemHeight * newListSize;
this.container.style.height = this.totalHeight + 'px';
this.oldListSize = newListSize;
}
},
_scrollTimeoutPoll: function () {
this._scrollStopTimeout = 0;
if (Date.now() > this._lastEventTime + 300) {
this.emit('scrollStopped');
} else {
this._scrollStopTimeout = setTimeout(this._scrollTimeoutPoll, 300);
}
},
_startScrollStopPolling: function () {
if (!this._scrollStopTimeout) {
this._scrollStopTimeout = setTimeout(this._scrollTimeoutPoll, 300);
}
}
};
evt.mix(VScroll.prototype);
var originalOn = VScroll.prototype.on;
VScroll.prototype.on = function (id, fn) {
if (id === 'scrollStopped') {
this._startScrollStopPolling();
}
return originalOn.apply(this, slice.call(arguments));
};
return VScroll;
});
define('cards/mixins/data-prop', [], function () {
return {
templateInsertedCallback: function () {
var node, parent, nodes = this.querySelectorAll('[data-prop]'), length = nodes.length;
for (var i = 0; i < length; i++) {
node = nodes[i];
parent = node;
while (parent = parent.parentNode) {
if (parent.nodeName.indexOf('-') !== -1) {
if (parent !== this) {
node = null;
}
break;
}
}
if (node && parent) {
this[node.dataset.prop] = nodes[i];
}
}
}
};
});
define('cards/mixins/data-event', [], function () {
var slice = Array.prototype.slice;
return {
templateInsertedCallback: function () {
slice.call(this.querySelectorAll('[data-event]')).forEach(function (node) {
var parent = node;
while (parent = parent.parentNode) {
if (parent.nodeName.indexOf('-') !== -1) {
if (parent !== this) {
return;
}
break;
}
}
if (!parent) {
return;
}
node.dataset.event.split(',').forEach(function (pair) {
var evtName, method, parts = pair.split(':');
if (!parts[1]) {
parts[1] = parts[0];
}
evtName = parts[0].trim();
method = parts[1].trim();
if (typeof this[method] !== 'function') {
throw new Error('"' + method + '" is not a function, cannot bind with data-event');
}
node.addEventListener(evtName, function (evt) {
evt.stopPropagation();
return this[method](evt);
}.bind(this), false);
}.bind(this));
}.bind(this));
}
};
});
define('cards/base', [
'require',
'evt',
'./mixins/data-prop',
'./mixins/data-event'
], function (require) {
var Emitter = require('evt').Emitter;
return function base(templateMixins) {
return [
templateMixins ? templateMixins : {},
require('./mixins/data-prop'),
require('./mixins/data-event'),
Emitter.prototype,
{
createdCallback: function () {
this.classList.add('email-ce');
Emitter.call(this);
},
emitDomEvent: function (eventName, detail) {
this.dispatchEvent(new CustomEvent(eventName, { detail: detail }));
}
}
];
};
});
define('cards/base_card', [
'require',
'date',
'l10n!',
'./base'
], function (require) {
var date = require('date'), mozL10n = require('l10n!'), base = require('./base');
(function () {
var updatePrettyDate = function updatePrettyDate() {
var labels = [...document.querySelectorAll('[data-time]')];
labels.forEach(label => {
date.relativeDateElement(label, label.dataset.time);
});
};
var timer = setInterval(updatePrettyDate, 60 * 1000);
function updatePrettyDateOnEvent() {
clearInterval(timer);
updatePrettyDate();
timer = setInterval(updatePrettyDate, 60 * 1000);
}
mozL10n.ready(updatePrettyDateOnEvent);
document.addEventListener('visibilitychange', function () {
if (document && !document.hidden) {
updatePrettyDateOnEvent();
} else {
clearInterval(timer);
}
});
}());
return function baseCard(templateMixins) {
return [
base(templateMixins),
{
createdCallback: function () {
if (this.extraClasses) {
this.classList.add.apply(this.classList, this.extraClasses);
}
this.classList.add('card');
},
batchAddClass: function (searchClass, classToAdd) {
var nodes = this.getElementsByClassName(searchClass);
for (var i = 0; i < nodes.length; i++) {
nodes[i].classList.add(classToAdd);
}
}
}
];
};
});
define('element', [
'require',
'exports',
'module'
], function (require, exports, module) {
'use strict';
var slice = Array.prototype.slice, callbackSuffix = 'Callback', callbackSuffixLength = callbackSuffix.length, charRegExp = /[^a-z]/g, idToTag = function (id) {
return id.toLowerCase().replace(charRegExp, '-');
}, moduleConfig = module.config();
if (moduleConfig.hasOwnProperty('idToTag')) {
idToTag = moduleConfig.idToTag;
}
function makePropName(attrName) {
var parts = attrName.split('-');
for (var i = 1; i < parts.length; i++) {
parts[i] = parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
}
return parts.join('');
}
function setPropFromAttr(instance, attrName, attrValue) {
var proto = Object.getPrototypeOf(instance), propName = makePropName(attrName), descriptor = Object.getOwnPropertyDescriptor(proto, propName);
if (descriptor && descriptor.set) {
instance[propName] = attrValue;
}
}
function makePropFn(prop) {
return function () {
var i, ret, args = slice.call(arguments), fns = this._element.props[prop];
for (i = 0; i < fns.length; i++) {
ret = fns[i].apply(this, args);
}
return ret;
};
}
function mixFnProp(proto, prop, value, operation) {
if (proto.hasOwnProperty(prop)) {
var existing = proto._element.props[prop];
if (!existing) {
existing = proto._element.props[prop] = [proto[prop]];
proto[prop] = makePropFn(prop);
}
operation = operation || 'push';
existing[operation](value);
} else {
proto[prop] = value;
}
}
function mix(proto, mixin) {
if (Array.isArray(mixin)) {
mixin.forEach(function (mixin) {
mix(proto, mixin);
});
return;
}
Object.keys(mixin).forEach(function (key) {
var suffixIndex, descriptor = Object.getOwnPropertyDescriptor(mixin, key);
suffixIndex = key.indexOf(callbackSuffix);
if (suffixIndex > 0 && suffixIndex === key.length - callbackSuffixLength) {
mixFnProp(proto, key, descriptor.value);
} else {
Object.defineProperty(proto, key, descriptor);
}
});
}
var element = {
load: function (id, req, onload, config) {
req([id], function (mod) {
if (config.isBuild || !mod || typeof mod === 'function') {
return onload();
}
var proto = Object.create(HTMLElement.prototype);
Object.defineProperty(proto, '_element', {
enumerable: false,
configurable: false,
writable: false,
value: {}
});
proto._element.props = {};
mix(proto, mod);
mixFnProp(proto, 'createdCallback', function attrCreated() {
var i, item, attrs = this.attributes;
for (i = 0; i < attrs.length; i++) {
item = attrs.item(i);
setPropFromAttr(this, item.nodeName, item.value);
}
}, 'unshift');
mixFnProp(proto, 'attributeChangedCallback', function attrChanged(name, oldValue, newValue) {
setPropFromAttr(this, name, newValue);
}, 'unshift');
var tagId = idToTag(id);
onload(document.registerElement(tagId, { prototype: proto }));
});
}
};
return element;
});
define('template', [
'require',
'exports',
'module',
'element'
], function (require, exports, module) {
var template, fetchText, isReady = false, readyQueue = [], tagRegExp = /<(\w+-[\w-]+)(\s|>)/g, commentRegExp = //g, attrIdRegExp = /\s(hrefid|srcid)="([^"]+)"/g, buildProtocol = 'build:', moduleConfig = module.config(), depPrefix = 'element!', buildMap = {}, tagToId = function (tag) {
return tag;
};
require('element');
if (moduleConfig.hasOwnProperty('depPrefix')) {
depPrefix = moduleConfig.depPrefix;
}
if (moduleConfig.hasOwnProperty('tagToId')) {
tagToId = moduleConfig.tagToId;
}
function onReady() {
isReady = true;
var bodyTemplate = document.querySelector('template#body');
if (bodyTemplate) {
bodyTemplate.parentNode.removeChild(bodyTemplate);
document.body.innerHTML = bodyTemplate.innerHTML;
}
readyQueue.forEach(function (fn) {
fn();
});
readyQueue = [];
}
function makeFullId(id, refId) {
if (id.indexOf('.') === 0 && refId) {
var parts = refId.split('/');
parts.pop();
refId = parts.join('/');
id = (refId ? refId + '/' : '') + id;
}
return id;
}
function templateCreatedCallback() {
if (this.dataset.cached === 'cached' || this.template) {
if (this.dataset.cached !== 'cached' && this.template) {
this.innerHTML = '';
this.appendChild(this.template());
}
if (this.templateInsertedCallback) {
this.templateInsertedCallback();
}
}
}
if (typeof XMLHttpRequest !== 'undefined') {
fetchText = function (url, onload, onerror) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
var status, err;
if (xhr.readyState === 4) {
status = xhr.status;
if (status > 399 && status < 600) {
err = new Error(url + ' HTTP status: ' + status);
err.xhr = xhr;
onerror(err);
} else {
onload(xhr.responseText);
}
}
};
xhr.responseType = 'text';
xhr.send(null);
};
} else {
fetchText = function (url, onload) {
onload(requirejs._readFile(url));
};
}
template = {
fetchText: fetchText,
ready: function (fn) {
if (isReady) {
setTimeout(fn);
} else {
readyQueue.push(fn);
}
},
makeFullId: makeFullId,
makeTemplateFn: function (text) {
return function () {
var e, frag = document.createDocumentFragment(), templateDiv = document.createElement('div');
templateDiv.innerHTML = text;
while (e = templateDiv.firstChild) {
frag.appendChild(e);
}
return frag;
};
},
idsToUrls: function (text, refId) {
text = text.replace(attrIdRegExp, function (match, type, id) {
id = makeFullId(id, refId);
var attr = type === 'hrefid' ? 'href' : 'src';
return ' ' + attr + '="' + require.toUrl(id) + '"';
});
return text;
},
depsFromText: function (text) {
var match, noCommentText, deps = [];
noCommentText = text.replace(commentRegExp, '');
tagRegExp.lastIndex = 0;
while (match = tagRegExp.exec(noCommentText)) {
deps.push(depPrefix + tagToId(match[1]));
}
return deps;
},
textToTemplate: function (text, id, skipTranslateIds) {
var obj, deps = template.depsFromText(text);
obj = {
id: id,
deps: deps,
text: text
};
if (!skipTranslateIds) {
obj.text = template.idsToUrls(text, id);
obj.fn = template.makeTemplateFn(obj.text);
}
return obj;
},
objToFn: function (obj) {
var text = template.idsToUrls(obj.text, obj.id);
return template.makeTemplateFn(text);
},
templateCreatedCallback: templateCreatedCallback,
load: function (id, req, onload, config) {
var isBuild = config.isBuild;
if (id.indexOf(buildProtocol) === 0 && isBuild) {
id = id.substring(buildProtocol.length);
var idList = id.split(','), count = 0, buildIdDone = function () {
count += 1;
if (count === idList.length) {
onload();
}
};
buildIdDone.__requireJsBuild = true;
id.split(',').forEach(function (moduleId) {
var path = req.toUrl(moduleId);
require(template.depsFromText(requirejs._readFile(path)), buildIdDone);
});
} else {
fetchText(req.toUrl(id), function (text) {
var templateObj = template.textToTemplate(text, id, isBuild);
req(templateObj.deps, function () {
if (isBuild) {
buildMap[id] = templateObj;
}
onload({
createdCallback: templateCreatedCallback,
template: templateObj.fn
});
});
}, onload.error);
}
},
write: function (pluginName, id, write) {
if (buildMap.hasOwnProperty(id)) {
var obj = buildMap[id], depString = JSON.stringify(obj.deps);
depString = depString.replace(/^\s*\[/, '').replace(/\]\s*$/, '').trim();
if (depString) {
depString = ', ' + depString;
}
write.asModule(pluginName + '!' + id, 'define([\'' + module.id + '\'' + depString + '], function(template) { return {\n' + 'createdCallback: template.templateCreatedCallback,\n' + 'template: template.objToFn(' + JSON.stringify(buildMap[id]) + ')}; });\n');
}
}
};
if (typeof document !== 'undefined') {
var onDom, onDomDone = false;
onDom = function () {
if (onDomDone) {
return;
}
onDomDone = true;
var converted = template.textToTemplate(document.body.innerHTML);
require(converted.deps, onReady);
};
if (document.readyState === 'interactive' || document.readyState === 'complete') {
onDom();
} else {
window.addEventListener('DOMContentLoaded', onDom);
}
}
return template;
});
define('template!cards/lst/edit_header.html', ['template'], function (template) {
return {
createdCallback: template.templateCreatedCallback,
template: template.objToFn({
'id': 'cards/lst/edit_header.html',
'deps': [],
'text': '\n'
})
};
});
define('cards/mixins/dom_evt', [], function () {
return {
domEvt: function (event) {
var target = event.currentTarget, eventName = target.dataset.domevtName, eventData = {}, eventDetail = target.dataset.domevtDetail;
event.preventDefault();
event.stopPropagation();
if (!eventName) {
throw new Error('No data-domevt-name on element <' + target.nodeName + ' class="' + target.className + '">');
}
if (eventDetail) {
eventData.detail = eventDetail;
}
this.dispatchEvent(new CustomEvent(eventName, eventData));
}
};
});
define('cards/lst/edit_header', [
'require',
'exports',
'module',
'l10n!',
'../base',
'template!./edit_header.html',
'../mixins/dom_evt'
], function (require, exports) {
var mozL10n = require('l10n!');
return [
require('../base')(require('template!./edit_header.html')),
require('../mixins/dom_evt'),
{
updateDomHeaderCount: function (count) {
mozL10n.setAttributes(this.headerNode, 'message-multiedit-header', { n: count });
}
}
];
});
define('container_listen', [], function () {
return function containerListen(containerNode, eventName, func) {
containerNode.addEventListener(eventName, function (event) {
var node = event.target;
if (node === containerNode) {
return;
}
while (node && node.parentNode !== containerNode) {
node = node.parentNode;
}
if (node) {
func(node, event);
}
}, false);
};
});
define('tmpl!cards/msg/header_item.html', ['tmpl'], function (tmpl) {
return tmpl.toDom('\n');
});
define('template!cards/lst/msg_vscroll.html', ['template'], function (template) {
return {
createdCallback: template.templateCreatedCallback,
template: template.objToFn({
'id': 'cards/lst/msg_vscroll.html',
'deps': [],
'text': '\n\n\n'
})
};
});
define('cards/lst/msg_vscroll', [
'require',
'exports',
'module',
'cards',
'container_listen',
'l10n!',
'tmpl!../msg/header_item.html',
'toaster',
'vscroll',
'../base',
'template!./msg_vscroll.html'
], function (require, exports) {
var cards = require('cards'), containerListen = require('container_listen'), mozL10n = require('l10n!'), msgHeaderItemNode = require('tmpl!../msg/header_item.html'), toaster = require('toaster'), VScroll = require('vscroll');
var sliceEvents = [
'splice',
'change',
'status',
'complete'
];
var MINIMUM_ITEMS_FOR_SCROLL_CALC = 10;
var MAXIMUM_MS_BETWEEN_SNIPPET_REQUEST = 6000;
var MAXIMUM_BYTES_PER_MESSAGE_DURING_SCROLL = 4 * 1024;
return [
require('../base')(require('template!./msg_vscroll.html')),
{
createdCallback: function () {
this.setAttribute('role', 'listbox');
this.setAttribute('aria-multiselectable', 'true');
mozL10n.setAttributes(this.messageEmptyText, this.dataset.emptyL10nId);
containerListen(this.vScrollContainer, 'click', this.onClickMessage.bind(this));
},
init: function (scrollContainer, bindData, defaultVScrollData) {
this.scrollContainer = scrollContainer;
var listFunc = function (index) {
return this.headerCursor.messagesSlice.items[index];
}.bind(this);
listFunc.size = function () {
var slice = this.headerCursor.messagesSlice;
return Math.max(slice.headerCount || 0, slice.items.length);
}.bind(this);
this.listFunc = listFunc;
this.waitingOnChunk = true;
this.desiredHighAbsoluteIndex = 0;
this._needVScrollData = false;
this.vScroll = new VScroll(this.vScrollContainer, this.scrollContainer, msgHeaderItemNode, defaultVScrollData);
this.vScroll.bindData = bindData;
this.vScroll.prepareData = function (highAbsoluteIndex) {
var items = this.headerCursor.messagesSlice && this.headerCursor.messagesSlice.items, headerCount = this.headerCursor.messagesSlice.headerCount;
if (!items || !headerCount) {
return;
}
if (highAbsoluteIndex > headerCount - 1) {
highAbsoluteIndex = headerCount - 1;
}
if (highAbsoluteIndex < items.length) {
return;
}
this.loadNextChunk(highAbsoluteIndex);
}.bind(this);
},
setHeaderCursor: function (headerCursor) {
if (this.headerCursor) {
throw new Error('headerCursor already set');
}
this.headerCursor = headerCursor;
sliceEvents.forEach(function (type) {
var name = 'messages_' + type;
this[name] = this[name].bind(this);
headerCursor.on(name, this[name]);
}.bind(this));
this.onCurrentMessage = this.onCurrentMessage.bind(this);
headerCursor.on('currentMessage', this.onCurrentMessage);
var parent = this;
while (parent = parent.parentNode) {
if (parent.classList.contains('card')) {
break;
}
}
this.cardParent = parent;
this._onVScrollStopped = this._onVScrollStopped.bind(this);
this.vScroll.on('scrollStopped', this._onVScrollStopped);
},
removeMessagesHtml: function () {
this.vScrollContainer.innerHTML = '';
},
onClickMessage: function (node, event) {
this.emitDomEvent('messageClick', node);
},
_onVScrollStopped: function () {
if (!this.headerCursor.messagesSlice || this.headerCursor.messagesSlice.pendingRequestCount) {
return;
}
if (cards.isVisible(this.cardParent) && !this._hasSnippetRequest()) {
this._requestSnippets();
}
},
onGetMoreMessages: function () {
if (!this.headerCursor.messagesSlice) {
return;
}
this.vScroll.once('recalculated', function (calledFromTop, refIndex) {
this.vScrollContainer.querySelector('[data-index="' + refIndex + '"]').focus();
}.bind(this));
this.headerCursor.messagesSlice.requestGrowth(1, true);
},
isEmpty: function () {
return this.headerCursor.messagesSlice.items.length === 0;
},
showEmptyLayout: function () {
this.messageEmptyContainer.classList.remove('collapsed');
this.emit('emptyLayoutShown');
},
hideEmptyLayout: function () {
this.messageEmptyContainer.classList.add('collapsed');
this.emit('emptyLayoutHidden');
},
messages_status: function (newStatus) {
var syncInProgress = true;
if (newStatus === 'synchronizing' || newStatus === 'syncblocked') {
this.syncingNode.classList.remove('collapsed');
this.syncMoreNode.classList.add('collapsed');
this.hideEmptyLayout();
} else if (newStatus === 'syncfailed' || newStatus === 'synced') {
syncInProgress = false;
if (newStatus === 'syncfailed') {
toaster.toast({ text: mozL10n.get('toaster-retryable-syncfailed') });
}
this.syncingNode.classList.add('collapsed');
}
this.emit('syncInProgress', syncInProgress);
},
messages_complete: function (newEmailCount) {
var headerCursor = this.headerCursor;
console.log('message_list complete:', headerCursor.messagesSlice.items.length, 'items of', headerCursor.messagesSlice.headerCount, 'alleged known headers. canGrow:', headerCursor.messagesSlice.userCanGrowDownwards);
if (headerCursor.messagesSlice.userCanGrowDownwards && headerCursor.messagesSlice.headerCount) {
this.syncMoreNode.classList.remove('collapsed');
} else {
this.syncMoreNode.classList.add('collapsed');
}
if (headerCursor.messagesSlice.items.length === 0) {
this.showEmptyLayout();
}
this.waitingOnChunk = false;
if (this.desiredHighAbsoluteIndex) {
this.loadNextChunk(this.desiredHighAbsoluteIndex);
this.desiredHighAbsoluteIndex = 0;
}
this.vScroll.updateDataBind(0, [], 0);
this.emit('messagesComplete', newEmailCount);
},
messages_splice: function (index, howMany, addedItems, requested, moreExpected) {
var headerCursor = this.headerCursor;
if (index === 0 && howMany === 0 && !addedItems.length) {
return;
}
this.emit('messagesSpliceStart', index, howMany, addedItems, requested, moreExpected);
if (this._needVScrollData) {
this.vScroll.setData(this.listFunc);
this._needVScrollData = false;
}
this.vScroll.updateDataBind(index, addedItems, howMany);
if (addedItems.length > 0) {
this.hideEmptyLayout();
}
if (!headerCursor.messagesSlice.headerCount) {
this.vScroll.once('scrollStopped', function () {
if (!headerCursor.messagesSlice.headerCount) {
this.showEmptyLayout();
}
}.bind(this));
}
this.emit('messagesSpliceEnd', index, howMany, addedItems, requested, moreExpected);
},
messages_change: function (message, index) {
this.emit('messagesChange', message, index);
},
loadNextChunk: function (desiredHighAbsoluteIndex) {
if (this.vScroll.waitingForRecalculate) {
return;
}
if (this.waitingOnChunk) {
this.desiredHighAbsoluteIndex = desiredHighAbsoluteIndex;
return;
}
var headerCursor = this.headerCursor;
if (desiredHighAbsoluteIndex >= headerCursor.messagesSlice.headerCount) {
desiredHighAbsoluteIndex = headerCursor.messagesSlice.headerCount - 1;
}
var items = headerCursor.messagesSlice.items;
var curHighAbsoluteIndex = items.length - 1;
var amount = desiredHighAbsoluteIndex - curHighAbsoluteIndex;
if (amount > 0) {
console.log('message_list loadNextChunk growing', amount, amount === 1 ? '(will get boosted to 15!) to' : 'to', desiredHighAbsoluteIndex + 1, 'items out of', headerCursor.messagesSlice.headerCount, 'alleged known');
headerCursor.messagesSlice.requestGrowth(amount, false);
this.waitingOnChunk = true;
}
},
onCurrentMessage: function (currentMessage, index) {
if (!currentMessage) {
return;
}
var visibleIndices = this.vScroll.getVisibleIndexRange();
if (visibleIndices && (index < visibleIndices[0] || index > visibleIndices[1])) {
this.vScroll.jumpToIndex(index);
}
},
_hasSnippetRequest: function () {
var max = MAXIMUM_MS_BETWEEN_SNIPPET_REQUEST;
var now = Date.now();
var beforeTimeout = this._lastSnippetRequest + max > now;
if (this._snippetRequestPending && beforeTimeout) {
return true;
}
return false;
},
_pendingSnippetRequest: function () {
this._snippetRequestPending = true;
this._lastSnippetRequest = Date.now();
},
_clearSnippetRequest: function () {
this._snippetRequestPending = false;
},
_requestSnippets: function () {
var headerCursor = this.headerCursor;
var items = headerCursor.messagesSlice.items;
var len = items.length;
if (!len) {
return;
}
var clearSnippets = this._clearSnippetRequest.bind(this);
var options = { maximumBytesToFetch: MAXIMUM_BYTES_PER_MESSAGE_DURING_SCROLL };
if (len < MINIMUM_ITEMS_FOR_SCROLL_CALC) {
this._pendingSnippetRequest();
headerCursor.messagesSlice.maybeRequestBodies(0, MINIMUM_ITEMS_FOR_SCROLL_CALC - 1, options, clearSnippets);
return;
}
var visibleIndices = this.vScroll.getVisibleIndexRange();
if (visibleIndices) {
this._pendingSnippetRequest();
headerCursor.messagesSlice.maybeRequestBodies(visibleIndices[0], visibleIndices[1], options, clearSnippets);
}
},
die: function () {
sliceEvents.forEach(function (type) {
var name = 'messages_' + type;
this.headerCursor.removeListener(name, this[name]);
}.bind(this));
this.headerCursor.removeListener('currentMessage', this.onCurrentMessage);
this.vScroll.destroy();
}
}
];
});
define('template!cards/lst/edit_toolbar.html', ['template'], function (template) {
return {
createdCallback: template.templateCreatedCallback,
template: template.objToFn({
'id': 'cards/lst/edit_toolbar.html',
'deps': [],
'text': '\n\n - \n \n
\n - \n \n
\n - \n \n
\n - \n \n
\n
'
})
};
});
define('cards/lst/edit_toolbar', [
'require',
'exports',
'module',
'l10n!',
'../base',
'template!./edit_toolbar.html',
'../mixins/dom_evt'
], function (require, exports) {
var mozL10n = require('l10n!'), toolbarEditButtonNames = [
'starBtn',
'readBtn',
'deleteBtn',
'moveBtn'
];
return [
require('../base')(require('template!./edit_toolbar.html')),
require('../mixins/dom_evt'),
{
updateDomFolderType: function (folderType) {
this.moveBtn.classList.toggle('collapsed', folderType === 'localdrafts' || folderType === 'outbox');
this.starBtn.classList.toggle('collapsed', folderType === 'outbox');
this.readBtn.classList.toggle('collapsed', folderType === 'outbox');
},
updateDomStartButton: function (isStarred) {
mozL10n.setAttributes(this.starBtn, isStarred ? 'message-star-button' : 'message-unstar-button');
},
updateDomReadButton: function (hasUnread) {
this.readBtn.classList.toggle('unread', hasUnread);
mozL10n.setAttributes(this.readBtn, hasUnread ? 'message-mark-unread-button' : 'message-mark-read-button');
},
updateDomEditButtons: function (hasMessages) {
toolbarEditButtonNames.forEach(function (key) {
this[key].disabled = !hasMessages;
}.bind(this));
}
}
];
});
define('template!cards/message_list.html', [
'template',
'element!cards/lst/edit_header',
'element!cards/lst/msg_vscroll',
'element!cards/lst/edit_toolbar'
], function (template) {
return {
createdCallback: template.templateCreatedCallback,
template: template.objToFn({
'id': 'cards/message_list.html',
'deps': [
'element!cards/lst/edit_header',
'element!cards/lst/msg_vscroll',
'element!cards/lst/edit_toolbar'
],
'text': '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n'
})
};
});
define('template!cards/confirm_dialog.html', ['template'], function (template) {
return {
createdCallback: template.templateCreatedCallback,
template: template.objToFn({
'id': 'cards/confirm_dialog.html',
'deps': [],
'text': '\n'
})
};
});
define('cards/confirm_dialog', [
'require',
'cards',
'./base_card',
'template!./confirm_dialog.html'
], function (require) {
var cards = require('cards');
return [
require('./base_card')(require('template!./confirm_dialog.html')),
{
onArgs: function (args) {
var dialogBodyNode = args.dialogBodyNode, confirm = args.confirm, cancel = args.cancel, callback = args.callback;
if (dialogBodyNode) {
this.appendChild(dialogBodyNode);
} else {
dialogBodyNode = this.querySelector('.confirm-dialog-form');
dialogBodyNode.querySelector('.confirm-dialog-message').textContent = args.message;
dialogBodyNode.classList.remove('collapsed');
confirm = {
handler: function () {
callback(true);
}
};
cancel = {
handler: function () {
callback(false);
}
};
}
dialogBodyNode.addEventListener('submit', function (evt) {
evt.preventDefault();
evt.stopPropagation();
this.hide();
var target = evt.explicitOriginalTarget, targetId = target.id, isOk = target.classList.contains('confirm-dialog-ok'), isCancel = target.classList.contains('confirm-dialog-cancel');
if ((isOk || targetId === confirm.id) && confirm.handler) {
confirm.handler();
} else if ((isCancel || targetId === cancel.id) && cancel.handler) {
cancel.handler();
}
}.bind(this));
},
hide: function () {
cards.removeCardAndSuccessors(this, 'immediate', 1, null, true);
},
die: function () {
}
}
];
});
define('confirm_dialog', [
'require',
'exports',
'module',
'cards',
'element!cards/confirm_dialog'
], function (require, exports) {
var cards = require('cards'), ConfirmDialog = require('element!cards/confirm_dialog');
ConfirmDialog.show = function (message, callback, cancel) {
var dialogBodyNode;
if (typeof message !== 'string') {
dialogBodyNode = message;
message = null;
}
cards.pushCard('confirm_dialog', 'immediate', {
dialogBodyNode: dialogBodyNode,
message: message,
confirm: callback,
callback: callback,
cancel: cancel
}, 'right');
};
return ConfirmDialog;
});
define('tmpl!cards/msg/delete_confirm.html', ['tmpl'], function (tmpl) {
return tmpl.toDom('');
});
define('cards/lst/edit_controller', [
'require',
'exports',
'module',
'cards',
'confirm_dialog',
'tmpl!../msg/delete_confirm.html',
'l10n!',
'toaster'
], function (require, exports) {
var cards = require('cards'), ConfirmDialog = require('confirm_dialog'), deleteConfirmMsgNode = require('tmpl!../msg/delete_confirm.html'), mozL10n = require('l10n!'), toaster = require('toaster');
return {
createdCallback: function () {
this.editMode = false;
this.selectedMessages = null;
this.editModeEnabled = false;
},
setEditMode: function (editMode) {
if (!this.editModeEnabled) {
return;
}
this._setEditMode(editMode);
},
_setEditMode: function (editMode) {
var i;
this.editMode = editMode;
if (editMode) {
this.classList.add('show-edit');
this.selectedMessages = [];
this.updateDomEditControls();
} else {
this.classList.remove('show-edit');
this.selectedMessages = null;
}
var msgNodes = this.msgVScroll.querySelectorAll('.msg-header-item');
for (i = 0; i < msgNodes.length; i++) {
this.updateDomMessageChecked(msgNodes[i], false);
}
if (this.editModeChanged) {
this.editModeChanged(editMode);
}
},
setEditModeStart: function () {
this.setEditMode(true);
},
editHeaderClose: function () {
this.setEditMode(false);
},
toggleSelection: function (msgNode) {
var header = msgNode.message;
var idx = this.selectedMessages.indexOf(header);
if (idx !== -1) {
this.selectedMessages.splice(idx, 1);
} else {
this.selectedMessages.push(header);
}
this.updateDomMessageChecked(msgNode, idx === -1);
this.updateDomEditControls();
},
updateDomSelectState: function (msgNode, message) {
if (this.editMode) {
this.updateDomMessageChecked(msgNode, this.selectedMessages.indexOf(message) !== -1);
} else {
msgNode.removeAttribute('aria-selected');
}
},
updateDomMessageChecked: function (msgNode, checked) {
var checkbox = msgNode.querySelector('input[type=checkbox]');
checkbox.checked = checked;
msgNode.setAttribute('aria-selected', checked);
},
updateDomEditControls: function () {
this.editHeader.updateDomHeaderCount(this.selectedMessages.length);
this.editToolbar.updateDomEditButtons(this.selectedMessages.length > 0);
var numStarred = 0, numRead = 0;
for (var i = 0; i < this.selectedMessages.length; i++) {
var msg = this.selectedMessages[i];
if (msg.isStarred) {
numStarred++;
}
if (msg.isRead) {
numRead++;
}
}
this.setAsStarred = !(numStarred && numStarred === this.selectedMessages.length);
this.editToolbar.updateDomStartButton(this.setAsStarred);
this.setAsRead = !!this.selectedMessages.length && numRead === 0;
this.editToolbar.updateDomReadButton(numRead > 0);
},
onDeleteMessages: function () {
if (this.selectedMessages.length === 0) {
return this.setEditMode(false);
}
var dialog = deleteConfirmMsgNode.cloneNode(true);
var content = dialog.getElementsByTagName('p')[0];
mozL10n.setAttributes(content, 'message-multiedit-delete-confirm', { n: this.selectedMessages.length });
ConfirmDialog.show(dialog, {
id: 'msg-delete-ok',
handler: function () {
var op = this.model.api.deleteMessages(this.selectedMessages);
toaster.toastOperation(op);
this.setEditMode(false);
}.bind(this)
}, {
id: 'msg-delete-cancel',
handler: null
});
},
onStarMessages: function () {
var op = this.model.api.markMessagesStarred(this.selectedMessages, this.setAsStarred);
this.setEditMode(false);
toaster.toastOperation(op);
},
onMarkMessagesRead: function () {
var op = this.model.api.markMessagesRead(this.selectedMessages, this.setAsRead);
this.setEditMode(false);
toaster.toastOperation(op);
},
onMoveMessages: function () {
cards.folderSelector(this.model, function (folder) {
var op = this.model.api.moveMessages(this.selectedMessages, folder);
toaster.toastOperation(op);
this.setEditMode(false);
}.bind(this), function (folder) {
return folder.isValidMoveTarget;
});
}
};
});
define('tmpl!cards/msg/large_message_confirm.html', ['tmpl'], function (tmpl) {
return tmpl.toDom('\n');
});
define('cards/lst/msg_click', [
'require',
'exports',
'module',
'cards',
'confirm_dialog',
'tmpl!../msg/large_message_confirm.html'
], function (require, exports) {
var cards = require('cards'), ConfirmDialog = require('confirm_dialog'), largeMsgConfirmMsgNode = require('tmpl!../msg/large_message_confirm.html');
function showLargeMessageWarning(size, cb) {
var dialog = largeMsgConfirmMsgNode.cloneNode(true);
ConfirmDialog.show(dialog, {
id: 'msg-large-message-ok',
handler: function () {
cb(true);
}
}, {
id: 'msg-large-message-cancel',
handler: function () {
cb(false);
}
});
}
return {
onClickMessage: function (event) {
var messageNode = event.detail;
if (this.curFolder && this.curFolder.type === 'outbox' && this.outboxSyncInProgress) {
return;
}
var header = messageNode.message;
if (header && header.isPlaceholderData) {
return;
}
if (this.editMode) {
this.toggleSelection(messageNode);
return;
}
if (this.curFolder && this.curFolder.type === 'localdrafts') {
var composer = header.editAsDraft(() => {
cards.pushCard('compose', 'animate', {
model: this.model,
composer
});
});
return;
}
if (this.curFolder && this.curFolder.type === 'outbox') {
if (header.sendStatus.state === 'sending') {
return;
}
var draftsFolder = this.model.foldersSlice.getFirstFolderWithType('localdrafts');
console.log('outbox: Moving message to localdrafts.');
this.model.api.moveMessages([header], draftsFolder, moveMap => {
header.id = moveMap[header.id];
console.log('outbox: Editing message in localdrafts.');
var composer = header.editAsDraft(() => {
cards.pushCard('compose', 'animate', {
model: this.model,
composer
});
});
});
return;
}
var model = this.model, headerCursor = this.headerCursor;
function pushMessageCard() {
cards.pushCard('message_reader', 'animate', {
model,
headerCursor,
header,
messageSuid: messageNode.dataset.id
});
}
if (header) {
this.headerCursor.setCurrentMessage(header);
} else if (messageNode.dataset.id) {
this.headerCursor.setCurrentMessageBySuid(messageNode.dataset.id);
} else {
return;
}
var LARGE_MESSAGE_SIZE = 1 * 1024 * 1024;
if (header && header.bytesToDownloadForBodyDisplay > LARGE_MESSAGE_SIZE) {
showLargeMessageWarning(header.bytesToDownloadForBodyDisplay, function (result) {
if (result) {
pushMessageCard();
} else {
}
});
} else {
pushMessageCard();
}
}
};
});
define('cards/message_list', [
'require',
'exports',
'module',
'cards',
'date',
'./lst/default_vscroll_data',
'evt',
'toaster',
'header_cursor',
'html_cache',
'l10n!',
'message_list_topbar',
'message_display',
'./lst/peep_dom',
'vscroll',
'./base_card',
'template!./message_list.html',
'./lst/edit_controller',
'./lst/msg_click'
], function (require, exports, module) {
var cards = require('cards'), date = require('date'), defaultVScrollData = require('./lst/default_vscroll_data'), evt = require('evt'), toaster = require('toaster'), HeaderCursor = require('header_cursor'), htmlCache = require('html_cache'), mozL10n = require('l10n!'), MessageListTopBar = require('message_list_topbar'), messageDisplay = require('message_display'), updatePeepDom = require('./lst/peep_dom').update, VScroll = require('vscroll');
return [
require('./base_card')(require('template!./message_list.html')),
require('./lst/edit_controller'),
require('./lst/msg_click'),
{
createdCallback: function () {
this._needsSizeLastSync = true;
this.updateLastSynced();
this.curFolder = null;
this.isIncomingFolder = true;
this._hideSearchBoxByScrolling = this._hideSearchBoxByScrolling.bind(this);
this._folderChanged = this._folderChanged.bind(this);
this.onNewMail = this.onNewMail.bind(this);
this.onFoldersSliceChange = this.onFoldersSliceChange.bind(this);
this.usingCachedNode = this.dataset.cached === 'cached';
this.msgVScroll.on('messagesSpliceStart', function (index, howMany, addedItems, requested, moreExpected) {
this._clearCachedMessages();
}.bind(this));
this.msgVScroll.on('messagesSpliceEnd', function (index, howMany, addedItems, requested, moreExpected) {
if (addedItems.length || howMany) {
this._considerCacheDom(index);
}
}.bind(this));
this.msgVScroll.on('messagesChange', function (message, index) {
this.onMessagesChange(message, index);
}.bind(this));
this._emittedContentEvents = false;
this.msgVScroll.on('messagesComplete', function (newEmailCount) {
this.onNewMail(newEmailCount);
if (!this._emittedContentEvents) {
evt.emit('metrics:contentDone');
this._emittedContentEvents = true;
}
}.bind(this));
var oldMessagesStatus = this.msgVScroll.messages_status;
this.msgVScroll.messages_status = function (newStatus) {
if (!this.curFolder || this.curFolder.type === 'outbox') {
return;
}
return oldMessagesStatus.call(this.msgVScroll, newStatus);
}.bind(this);
this.msgVScroll.on('emptyLayoutShown', function () {
this._clearCachedMessages();
if (this.curFolder.type === 'outbox') {
this.refreshBtn.disabled = true;
}
this.editBtn.disabled = true;
this._hideSearchBoxByScrolling();
}.bind(this));
this.msgVScroll.on('emptyLayoutHidden', function () {
this.editBtn.disabled = false;
this.refreshBtn.disabled = false;
}.bind(this));
this.msgVScroll.on('syncInProgress', function (syncInProgress) {
if (syncInProgress) {
this.setRefreshState(true);
} else {
this.setRefreshState(false);
this._manuallyTriggeredSync = false;
}
}.bind(this));
var vScrollBindData = function bindNonSearch(model, node) {
model.element = node;
node.message = model;
this.updateMessageDom(model);
}.bind(this);
this.msgVScroll.init(this.scrollContainer, vScrollBindData, defaultVScrollData);
this.msgVScroll.vScroll.on('inited', this._hideSearchBoxByScrolling);
this.msgVScroll.vScroll.on('dataChanged', this._hideSearchBoxByScrolling);
this.msgVScroll.vScroll.on('recalculated', function (calledFromTop) {
if (calledFromTop) {
this._hideSearchBoxByScrolling();
}
}.bind(this));
this._topBar = new MessageListTopBar(this.querySelector('.message-list-topbar'));
this._topBar.bindToElements(this.scrollContainer, this.msgVScroll.vScroll);
this.onFolderPickerClosing = this.onFolderPickerClosing.bind(this);
evt.on('folderPickerClosing', this.onFolderPickerClosing);
},
onArgs: function (args) {
var model = this.model = args.model;
var headerCursor = this.headerCursor = args.headerCursor || new HeaderCursor(model);
this.msgVScroll.setHeaderCursor(headerCursor);
model.latest('folder', this._folderChanged);
model.on('newInboxMessages', this.onNewMail);
model.on('backgroundSendStatus', this.onBackgroundSendStatus.bind(this));
model.on('foldersSliceOnChange', this.onFoldersSliceChange);
if (this.curFolder) {
var items = headerCursor.messagesSlice && headerCursor.messagesSlice.items;
if (items && items.length) {
this.msgVScroll.messages_splice(0, 0, items);
this.msgVScroll.messages_complete(0);
}
}
},
skipEmitContentEvents: true,
postInsert: function () {
this._hideSearchBoxByScrolling();
this.msgVScroll.vScroll.visibleOffset = this.searchBar.getBoundingClientRect().height;
this._topBar.visibleOffset = this.msgVScroll.vScroll.visibleOffset;
this.msgVScroll.vScroll.captureScreenMetrics();
},
onSearchButton: function () {
if (!this.curFolder) {
return;
}
cards.pushCard('message_list_search', 'animate', {
model: this.model,
folder: this.curFolder
});
},
_hideSearchBoxByScrolling: function () {
var searchBar = this.searchBar, scrollContainer = this.scrollContainer;
if (searchBar.classList.contains('collapsed')) {
searchBar.classList.remove('collapsed');
scrollContainer.scrollTop += searchBar.offsetHeight;
}
if (scrollContainer.scrollTop === 0) {
scrollContainer.scrollTop = searchBar.offsetHeight;
}
},
onShowFolders: function () {
cards.pushCard('folder_picker', 'immediate', {
model: this.model,
onPushed: function () {
this.headerMenuNode.classList.add('transparent');
}.bind(this)
});
},
onCompose: function () {
cards.pushCard('compose', 'animate', { model: this.model });
},
sizeLastSync: function () {
if (this._needsSizeLastSync && this.lastSyncedLabel.scrollWidth) {
var label = this.lastSyncedLabel;
var overHalf = label.scrollWidth > label.parentNode.clientWidth / 2;
label.parentNode.classList[overHalf ? 'add' : 'remove']('long');
this._needsSizeLastSync = false;
}
},
updateLastSynced: function (value) {
var method = value ? 'remove' : 'add';
this.lastSyncedLabel.classList[method]('collapsed');
date.setPrettyNodeDate(this.lastSyncedAtNode, value);
this.sizeLastSync();
},
updateUnread: function (num) {
var content = '';
if (num > 0) {
content = num > 999 ? mozL10n.get('messages-folder-unread-max') : num;
}
this.folderUnread.textContent = content;
this.folderUnread.classList.toggle('collapsed', !content);
this.callHeaderFontSize();
},
onFoldersSliceChange: function (folder) {
if (folder === this.curFolder) {
this.updateUnread(folder.unread);
this.updateLastSynced(folder.lastSyncedAt);
}
},
callHeaderFontSize: function (node) {
requestAnimationFrame(function () {
FontSizeUtils._reformatHeaderText(this.folderLabel);
}.bind(this));
},
showFolder: function (folder, forceNewSlice) {
if (folder === this.curFolder && !forceNewSlice) {
return false;
}
if (!this.usingCachedNode) {
this.msgVScroll.vScroll.clearDisplay();
}
this.msgVScroll._needVScrollData = true;
this.curFolder = folder;
this.editModeEnabled = true;
switch (folder.type) {
case 'drafts':
case 'localdrafts':
case 'outbox':
case 'sent':
this.isIncomingFolder = false;
break;
default:
this.isIncomingFolder = true;
break;
}
this.folderNameNode.textContent = folder.name;
this.updateUnread(folder.unread);
this.msgVScroll.setAttribute('aria-label', folder.name);
this.msgVScroll.hideEmptyLayout();
this.refreshBtn.classList.toggle('collapsed', folder.type === 'localdrafts');
this.editToolbar.updateDomFolderType(folder.type);
this.updateLastSynced(folder.lastSyncedAt);
if (forceNewSlice) {
this.msgVScroll._snippetRequestPending = false;
this.headerCursor.freshMessagesSlice();
}
this.onFolderShown();
return true;
},
setEditMode: function (editMode) {
if (!this.editModeEnabled) {
return;
}
if (this.curFolder.type === 'outbox') {
if (editMode && this.outboxSyncInProgress) {
return;
}
var model = this.model;
model.api.setOutboxSyncEnabled(model.account, !editMode, function () {
this._setEditMode(editMode);
}.bind(this));
} else {
this._setEditMode(editMode);
}
},
setRefreshState: function (syncing) {
if (syncing) {
this.refreshBtn.dataset.state = 'synchronizing';
this.refreshBtn.setAttribute('role', 'progressbar');
mozL10n.setAttributes(this.refreshBtn, 'messages-refresh-progress');
} else {
this.refreshBtn.dataset.state = 'synchronized';
this.refreshBtn.removeAttribute('role');
mozL10n.setAttributes(this.refreshBtn, 'messages-refresh-button');
}
},
onNewMail: function (newEmailCount) {
var inboxFolder = this.model.foldersSlice.getFirstFolderWithType('inbox');
if (inboxFolder.id === this.curFolder.id && newEmailCount && newEmailCount > 0) {
if (!cards.isVisible(this)) {
this._whenVisible = this.onNewMail.bind(this, newEmailCount);
return;
}
if (this._manuallyTriggeredSync) {
this.msgVScroll.vScroll.jumpToIndex(0);
} else {
this._topBar.showNewEmailCount(newEmailCount);
}
}
},
onBackgroundSendStatus: function (data) {
if (this.curFolder.type === 'outbox') {
if (data.state === 'sending') {
this.toggleOutboxSyncingDisplay(true);
} else if (data.state === 'syncDone') {
this.toggleOutboxSyncingDisplay(false);
}
}
if (data.emitNotifications) {
toaster.toast({ text: data.localizedDescription });
}
},
_cacheListLimit: 7,
_cacheDomTimeoutId: 0,
_isCacheableCardState: function () {
return this.cacheableFolderId === this.curFolder.id && !this.editMode;
},
_cacheDom: function () {
this._cacheDomTimeoutId = 0;
if (!this._isCacheableCardState()) {
return;
}
var cacheNode = htmlCache.cloneAsInertNodeAvoidingCustomElementHorrors(this);
cacheNode.querySelector('menu[type="toolbar"]').classList.remove('transparent');
var removableCacheNode = cacheNode.querySelector('.msg-search-tease-bar');
if (removableCacheNode) {
removableCacheNode.classList.add('collapsed');
}
removableCacheNode = cacheNode.querySelector('.message-list-topbar');
if (removableCacheNode) {
this._topBar.resetNodeForCache(removableCacheNode);
}
var tempNode = cacheNode.querySelector('.msg-last-synced-label');
if (tempNode) {
tempNode.classList.add('collapsed');
}
tempNode = cacheNode.querySelector('.msg-last-synced-value');
if (tempNode) {
tempNode.innerHTML = '';
}
VScroll.trimMessagesForCache(cacheNode.querySelector('.msg-vscroll-container'), this._cacheListLimit);
htmlCache.saveFromNode(module.id, cacheNode);
},
_considerCacheDom: function (index) {
if (!this._cacheDomTimeoutId && this._isCacheableCardState() && this.msgVScroll.vScroll.firstRenderedIndex === 0 && (index || index === 0) && index < this._cacheListLimit) {
this._cacheDomTimeoutId = setTimeout(this._cacheDom.bind(this), 600);
}
},
_clearCachedMessages: function () {
if (this.usingCachedNode) {
this.msgVScroll.removeMessagesHtml();
this.usingCachedNode = false;
}
},
onMessagesChange: function (message, index) {
this.updateMessageDom(message);
this._considerCacheDom(index);
},
updateMessageDom: function (message) {
var msgNode = message.element;
if (!msgNode) {
return;
}
var classAction = message.isPlaceholderData ? 'add' : 'remove';
var defaultDataClass = this.msgVScroll.vScroll.itemDefaultDataClass;
msgNode.classList[classAction](defaultDataClass);
msgNode.dataset.id = message.id;
var dateNode = msgNode.querySelector('.msg-header-date');
var subjectNode = msgNode.querySelector('.msg-header-subject');
var snippetNode = msgNode.querySelector('.msg-header-snippet');
var listPerson;
if (this.isIncomingFolder) {
listPerson = message.author;
} else if (message.to && message.to.length) {
listPerson = message.to[0];
} else if (message.cc && message.cc.length) {
listPerson = message.cc[0];
} else if (message.bcc && message.bcc.length) {
listPerson = message.bcc[0];
} else {
listPerson = message.author;
}
listPerson.element = msgNode.querySelector('.msg-header-author');
listPerson.onchange = updatePeepDom;
listPerson.onchange(listPerson);
var dateTime = dateNode.dataset.time = message.date.valueOf();
date.relativeDateElement(dateNode, dateTime);
messageDisplay.subject(msgNode.querySelector('.msg-header-subject'), message);
var attachmentsNode = msgNode.querySelector('.msg-header-attachments');
attachmentsNode.classList.toggle('msg-header-attachments-yes', message.hasAttachments);
snippetNode.classList.toggle('icon-short', message.hasAttachments);
snippetNode.textContent = message.snippet;
msgNode.classList.toggle('unread', !message.isRead);
var starNode = msgNode.querySelector('.msg-header-star');
starNode.classList.toggle('msg-header-star-starred', message.isStarred);
subjectNode.classList.toggle('icon-short', message.isStarred);
var syncNode = msgNode.querySelector('.msg-header-syncing-section');
var sendState = message.sendStatus && message.sendStatus.state;
syncNode.classList.toggle('msg-header-syncing-section-syncing', sendState === 'sending');
syncNode.classList.toggle('msg-header-syncing-section-error', sendState === 'error');
if (sendState) {
mozL10n.setAttributes(syncNode, 'message-header-state-' + sendState);
} else {
syncNode.removeAttribute('data-l10n-id');
}
this.updateDomSelectState(msgNode, message);
},
onFolderPickerClosing: function () {
this.headerMenuNode.classList.remove('transparent');
},
onFolderShown: function () {
var model = this.model, account = model.account, foldersSlice = model.foldersSlice;
if (!document.hidden && account && foldersSlice && this.curFolder) {
var inboxFolder = foldersSlice.getFirstFolderWithType('inbox');
if (inboxFolder === this.curFolder) {
evt.emit('inboxShown', account.id);
}
if (document.activeElement === this.searchTextTease) {
this.onSearchButton();
}
}
},
onCurrentCardDocumentVisibilityChange: function () {
this.onFolderShown();
},
onCardVisible: function () {
if (this._whenVisible) {
var fn = this._whenVisible;
this._whenVisible = null;
fn();
}
this.msgVScroll.vScroll.nowVisible();
this.sizeLastSync();
},
toggleOutboxSyncingDisplay: function (syncing) {
if (syncing === this._outboxSyncing) {
return;
}
this._outboxSyncing = syncing;
var i;
var items = this.msgVScroll.getElementsByClassName('msg-header-syncing-section');
if (syncing) {
for (i = 0; i < items.length; i++) {
items[i].classList.add('msg-header-syncing-section-syncing');
items[i].classList.remove('msg-header-syncing-section-error');
}
this.editBtn.disabled = true;
} else {
this.editBtn.disabled = this.msgVScroll.isEmpty();
for (i = 0; i < items.length; i++) {
items[i].classList.remove('msg-header-syncing-section-syncing');
}
}
this.setRefreshState(syncing);
},
onRefresh: function () {
var headerCursor = this.headerCursor;
if (!headerCursor.messagesSlice) {
return;
}
if (this.curFolder.type === 'outbox') {
this.toggleOutboxSyncingDisplay(true);
} else {
switch (headerCursor.messagesSlice.status) {
case 'new':
case 'synchronizing':
break;
case 'synced':
this._manuallyTriggeredSync = true;
headerCursor.messagesSlice.refresh();
break;
case 'syncfailed':
if (headerCursor.messagesSlice.items.length) {
headerCursor.messagesSlice.refresh();
} else {
this.showFolder(this.curFolder, true);
}
break;
}
}
this.model.api.sendOutboxMessages(this.model.account);
},
_folderChanged: function (folder) {
if (!this.model.foldersSlice) {
return;
}
var model = this.model;
var inboxFolder = model.foldersSlice.getFirstFolderWithType('inbox');
this.cacheableFolderId = model.account === model.acctsSlice.defaultAccount ? inboxFolder.id : null;
if (this.showFolder(folder)) {
this._hideSearchBoxByScrolling();
}
},
die: function () {
this.msgVScroll.die();
evt.removeListener('folderPickerClosing', this.onFolderPickerClosing);
if (this.headerCursor) {
this.headerCursor.die();
}
var model = this.model;
model.removeListener('folder', this._folderChanged);
model.removeListener('newInboxMessages', this.onNewMail);
model.removeListener('foldersSliceOnChange', this.onFoldersSliceChange);
}
}
];
});