Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

View file

@ -0,0 +1,585 @@
/* global _, debug, Toolkit, Formatting, Common */
/* exported ChartUtils */
'use strict';
var ChartUtils = (function() {
var DEVICE_PIXEL_RATIO = window.devicePixelRatio || 1;
var DAY = 24 * 60 * 60 * 1000;
var NEVER_PERIOD = 30 * DAY;
var NEVER_ANCHOR = 21 * DAY;
var CHART_BG_RATIO = 0.87;
var FONT_SIZE = toDevicePixels(13);
var FONT_WEIGHT = '600';
var FONT_WEIGHT_AXIS = '400';
var WIFI_CHART_STROKE = '#8b9052';
var WIFI_CHART_FILL = '#cbd936';
var MOBILE_CHART_STROKE = '#762d4a';
var MOBILE_CHART_FILL = 'rgba(147, 21, 98, 0.7)';
function toDevicePixels(value) {
return value * DEVICE_PIXEL_RATIO;
}
function makeCSSFontString(fontSize, fontWeight) {
return fontWeight + ' ' + fontSize + 'px sans-serif';
}
function formatChartDate(date) {
return _('verbose-chart-date-format', {
'monthday-number': date.getDate(),
'em-month': _('month-' + date.getMonth() + '-short')
});
}
function getLimitInBytes(settings) {
var multiplier = 1000000; // MB
if (settings.dataLimitUnit === 'GB') {
multiplier = 1000000000; // GB
}
var value = settings.dataLimitValue;
return (value && value !== 0) ? value * multiplier : null;
}
function calculateUpperDate(settings) {
var today = new Date();
var trackingPeriod = settings.trackingPeriod;
var nextReset = settings.nextReset;
if (trackingPeriod !== 'never' && nextReset) {
return new Date(nextReset.getTime() - DAY);
}
var lastReset = settings.lastCompleteDataReset || today;
var offset = today.getTime() - lastReset.getTime();
var upperDate = new Date(lastReset.getTime() + NEVER_PERIOD);
if (offset >= NEVER_ANCHOR) {
upperDate = new Date(today.getTime() + (NEVER_PERIOD - NEVER_ANCHOR));
}
debug('Upper date:', upperDate);
return upperDate;
}
function calculateLowerDate(settings) {
var today = new Date();
var lowerDate = Toolkit.toMidnight(new Date());
var nextReset = settings.nextReset || lowerDate;
var trackingPeriod = settings.trackingPeriod;
if (trackingPeriod === 'weekly') {
lowerDate.setTime(nextReset.getTime() - (7 * DAY));
} else if (trackingPeriod === 'monthly') {
var monthDate = settings.resetTime;
var isFirstDayOfPeriod = today.getDate() == monthDate;
var isAfterFirstDayOfPeriod = today.getDate() > monthDate;
if (isAfterFirstDayOfPeriod) {
// lowerDate is in the current month
lowerDate.setDate(monthDate);
} else if (!isFirstDayOfPeriod) {
// lowerDate is on the previous month
var LAST_DAY_OF_PREVIOUS_MONTH = 0;
var newMonth = today.getMonth() - 1;
var newYear = today.getFullYear();
if (newMonth < 0) {
newMonth = 11;
newYear--;
}
lowerDate = Toolkit.toMidnight(new Date(newYear, newMonth, monthDate));
// The day of the month of lowerDate is different to settings.resetTime
// value when the resetTime day doesn't exists this month, (eg. 30th
// February), on this case, the lowerDate must be the last day of the
// previous month
if (lowerDate.getDate() != monthDate) {
lowerDate = Toolkit.toMidnight(new Date());
lowerDate.setDate(LAST_DAY_OF_PREVIOUS_MONTH);
}
}
} else {
var lastReset = settings.lastCompleteDataReset || lowerDate;
lowerDate = lastReset;
var offset = today.getTime() - lastReset.getTime();
if (offset >= NEVER_ANCHOR) {
lowerDate = new Date(today.getTime() - NEVER_ANCHOR);
}
}
debug('Lower date:', lowerDate);
return lowerDate;
}
// Expand the model with some computed values
function expandModel(base) {
// Update today
var today = Toolkit.toMidnight(new Date());
// Graphic settings
base.originY = Math.floor(base.height * CHART_BG_RATIO);
// Today value
base.axis.X.today = today;
// Normalize today
Toolkit.toMidnight(base.axis.X.today);
Toolkit.toMidnight(base.axis.X.lower);
Toolkit.toMidnight(base.axis.X.upper);
// X axis projection function to convert a value into a pixel value
var xLowerBound = base.axis.X.lower.getTime();
var xSize = base.axis.X.upper.getTime() - xLowerBound;
var realWidth = base.endX - base.originX;
base.axis.X.len = realWidth;
base.axis.X.get = function cc_dataToXPx(value) {
var projection = (value.getTime() - xLowerBound) / xSize;
return projection * realWidth + base.originX;
};
// Y max value
var limitEnabled = true; // XXX: model.limits.enabled;
base.axis.Y.maxValue = Math.max(limitEnabled ? base.limits.value : 0,
base.data.mobile.total,
base.data.wifi.total);
// Y axis projection function and automatic values
base.axis.Y.range = base.axis.Y.upper - base.axis.Y.lower;
base.axis.Y.upper = (1 + base.axis.Y.margin) * base.axis.Y.maxValue;
var yLowerBound = base.axis.Y.lower;
var ySize = base.axis.Y.upper - yLowerBound;
base.axis.Y.get = function cc_dataToYPx(value) {
var projection = (value - yLowerBound) / ySize;
return base.originY * (1 - projection);
};
base.axis.Y.step = base.axis.Y.get(base.axis.Y.maxValue);
// Limits
base.limits.warning = Common.DATA_USAGE_WARNING;
base.limits.warningValue = base.limits.value * Common.DATA_USAGE_WARNING;
}
// Check if the segment of the graph is inside chart area. If so, draw it
function clipAndDrawSegment(ctx, model, x0, y0, x1, y1) {
if (x0 >= model.originX && x1 <= model.endX) {
var x0Fixed = Math.floor(x0) - 0.5;
var x1Fixed = Math.floor(x1) + 0.5;
// Fill
ctx.globalCompositeOperation = 'destination-over';
ctx.beginPath();
ctx.lineTo(x1Fixed, y1);
ctx.lineTo(x1Fixed, model.originY);
ctx.lineTo(x0Fixed, model.originY);
ctx.lineTo(x0Fixed, y0);
ctx.moveTo(x1Fixed, y0);
ctx.fill();
// Stroke
ctx.globalCompositeOperation = 'source-over';
ctx.beginPath();
// It's necessary add 1 to the X coordinate to make up for the offset
// produced with the drawing of the previous line.
ctx.moveTo(x0Fixed + 1, y0);
ctx.lineTo(x1Fixed, y1);
ctx.stroke();
}
}
function drawTodayMark(ctx, x, y, color) {
ctx.save();
var radius = toDevicePixels(4);
ctx.fillStyle = color;
ctx.beginPath();
ctx.arc(x, y, radius, 0, 2 * Math.PI);
ctx.fill();
ctx.restore();
}
function drawBackgroundLayer(canvas, model, showMobile) {
canvas.height = model.height;
var width = canvas.width = model.width;
var ctx = canvas.getContext('2d');
ctx.save();
// White bg
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, width, model.originY);
// Horizontal lines every step
var step = model.axis.Y.step;
var limitY = model.axis.Y.get(model.limits.value);
ctx.strokeStyle = '#e0e0e0';
ctx.lineWidth = toDevicePixels(1);
var displayLimit = model.limits.enabled && showMobile;
for (var y = model.originY - step; y > step; y -= step) {
if (displayLimit && same(y, limitY, 0.1)) {
continue;
}
var drawY = Math.floor(y) - 0.5;
ctx.beginPath();
ctx.moveTo(model.originX, drawY);
ctx.lineTo(model.endX, drawY);
ctx.stroke();
}
// Vertical lines every day
var days = (model.axis.X.upper - model.axis.X.lower) / DAY;
step = model.axis.X.len / days;
ctx.strokeStyle = '#eeeeee';
ctx.lineWidth = toDevicePixels(1);
for (var x = model.originX; x <= model.endX; x += step) {
var drawX = Math.floor(x) + 0.5;
ctx.beginPath();
ctx.moveTo(drawX, model.originY);
ctx.lineTo(drawX, 0);
ctx.stroke();
// Ensure draw the last vertical line
if (((x + step) > model.endX) && (x !== model.endX)) {
// the 0.5 offset is needed to avoid drawing a double line.
drawX = model.endX - 0.5;
ctx.beginPath();
ctx.moveTo(drawX, model.originY);
ctx.lineTo(drawX, 0);
ctx.stroke();
}
}
ctx.restore();
// Return true if a and b not differ more than threshold
function same(a, b, threshold) {
threshold = threshold || 0;
return Math.abs(a - b) <= threshold;
}
}
function drawTodayLayer(canvas, model) {
canvas.height = model.height;
canvas.width = model.width;
var ctx = canvas.getContext('2d');
// Compute the X offset
var offsetX = model.axis.X.get(model.axis.X.today);
// Configure Centered today text
var marginTop = toDevicePixels(10);
var todayTag = formatChartDate(model.axis.X.today);
// Render the text
ctx.font = makeCSSFontString(FONT_SIZE, FONT_WEIGHT);
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
// Update measues of today label
var metrics = ctx.measureText(todayTag);
model.todayLabel.x0 = offsetX - metrics.width / 2;
model.todayLabel.x1 = offsetX + metrics.width / 2;
if (model.todayLabel.x1 > model.endX) {
ctx.textAlign = 'right';
}
ctx.fillStyle = 'black';
ctx.fillText(todayTag, offsetX, model.originY + marginTop);
setAccessibilityAttributes(canvas, 'today-layer', { today: todayTag });
}
function drawAxisLayer(canvas, model, showMobile) {
canvas.height = model.height;
canvas.width = model.width;
var ctx = canvas.getContext('2d');
// Start drawing Y axis
var step = model.axis.Y.step;
var dataStep = model.axis.Y.upper - model.axis.Y.maxValue;
var marginRight = 4;
var offsetX = model.originX - marginRight;
ctx.font = makeCSSFontString(FONT_SIZE, FONT_WEIGHT_AXIS);
ctx.textAlign = 'right';
var displayLimit = showMobile && model.limits.enabled;
var lastUnit;
for (var y = 0.5 + model.originY, value = 0;
y > step; y -= step, value += dataStep) {
// First X label for 0 is aligned with the bottom
if (value === 0) {
lastUnit = Formatting.smartRound(dataStep, 0)[1];
ctx.textBaseline = 'bottom';
ctx.fillStyle = '#6a6a6a';
ctx.fillText(Formatting.formatData([0, lastUnit]), offsetX, y - 2.5);
continue;
}
// Rest of labels are aligned with the middle
var rounded = Formatting.smartRound(value, -1);
var v = rounded[0];
var u = rounded[1];
var label = v;
if (lastUnit !== u) {
label = Formatting.formatData([v, u]);
}
lastUnit = u;
ctx.textBaseline = 'middle';
ctx.fillStyle = displayLimit &&
(value === model.limits.value) &&
model.limits.enabled ?
'#b50202' : '#6a6a6a';
ctx.fillText(label, offsetX, y);
}
// Now the X axis
ctx.fillStyle = '#6a6a6a';
var marginTop = toDevicePixels(10);
// Left tag
var leftTag = formatChartDate(model.axis.X.lower);
ctx.font = makeCSSFontString(FONT_SIZE, FONT_WEIGHT);
ctx.textBaseline = 'top';
ctx.textAlign = 'left';
var isBelowToday = model.todayLabel.x0 <=
model.originX + ctx.measureText(leftTag).width;
if (!isBelowToday) {
ctx.fillText(leftTag, model.originX, model.originY + marginTop);
}
// Right tag
var rightTag = formatChartDate(model.axis.X.upper);
ctx.textAlign = 'right';
isBelowToday = model.todayLabel.x1 >=
model.endX - ctx.measureText(rightTag).width;
if (!isBelowToday) {
ctx.fillText(rightTag, model.endX, model.originY + marginTop);
}
setAccessibilityAttributes(canvas, 'axis-layer',
{ from: leftTag, to: rightTag });
}
function setAccessibilityAttributes(element, identifier, data) {
if (identifier) {
element.setAttribute('data-l10n-id', identifier);
if (data) {
element.setAttribute('data-l10n-args', JSON.stringify(data));
}
} else {
element.removeAttribute('data-l10n-id');
element.removeAttribute('data-l10n-args');
element.removeAttribute('aria-label');
}
}
function drawLimits(canvas, model, showMobile) {
var set = model.limits.value;
var color = '#b50202';
canvas.height = model.height;
canvas.width = model.width;
var ctx = canvas.getContext('2d');
var displayLimit = showMobile && model.limits.enabled;
if (!displayLimit) {
return;
}
ctx.save();
var marginTop = 1;
var offsetY = set ? model.axis.Y.get(model.limits.value) :
FONT_SIZE + 2 * marginTop;
ctx.font = makeCSSFontString(FONT_SIZE, FONT_WEIGHT);
// The dashed limit line
var lineLength = toDevicePixels(15);
var gapLength = toDevicePixels(7);
ctx.strokeStyle = color;
ctx.lineWidth = toDevicePixels(1);
ctx.beginPath();
for (var x = model.originX, drawY = Math.floor(offsetY) - 0.5;
x < model.endX; x += gapLength) {
ctx.moveTo(x, drawY);
ctx.lineTo(Math.min(x += lineLength, model.endX), drawY);
}
ctx.stroke();
ctx.restore();
setAccessibilityAttributes(canvas,
'limit-layer-' + model.limits.dataLimitUnit,
{ value: model.limits.dataLimitValue });
}
function drawDataLayer(canvas, model, sampleType, style) {
var samples = (model.data[sampleType] || {}).samples;
if (!samples) {
return;
}
canvas.height = model.height;
canvas.width = model.width;
var ctx = canvas.getContext('2d');
// Style
ctx.fillStyle = style.fill;
ctx.strokeStyle = style.stroke;
ctx.lineWidth = toDevicePixels(2);
ctx.lineJoin = 'round';
ctx.moveTo(model.originX, model.originY);
var today = Toolkit.toMidnight(new Date());
// offset in milliseconds
var offset = today.getTimezoneOffset() * 60 * 1000;
var sum = 0; var x, y = model.originY;
var lastX = model.originX, lastY = model.axis.Y.get(sum);
// Only dealing with the use cases of negative UTC offset because, due to
// the database granularity, the returned samples are given in UTC days, and
// we don't know what part of the traffic sample values correspond to the
// current localtime day. The query for the samples is generated on the
// costcontrol module, and the case into which the returned samples only
// have one record with the data for the localtime day of yesterday is not
// possible.
for (var i = 0, len = samples.length; i < len; i++) {
var sample = samples[i];
var sampleLocalTime = sample.date.getTime() + offset;
var sampleUTCDate = Toolkit.toMidnight(new Date(sampleLocalTime));
var isToday = (today.getTime() === sampleUTCDate.getTime());
var isTomorrow = (today.getTime() + DAY === sampleUTCDate.getTime());
var thereIsATomorrowSample = (isToday && (i + 2 === len));
// Depends on the hour of the day and the offset, it is possible the
// networkStats API returns the current data mobile in the tomorrow
// sample, because on the UTC hour is another day.
if (thereIsATomorrowSample) {
// Join the value of the samples for today and tomorrow
var tomorrowSample = samples[i+1];
if (typeof sample.value === 'undefined') {
sample.value = tomorrowSample.value;
} else if (typeof tomorrowSample.value !== 'undefined') {
sample.value += tomorrowSample.value;
}
if (i === 0) {
lastX = model.axis.X.get(sample.date);
}
i++;
}
if (typeof sample.value === 'undefined') {
lastX = x = model.axis.X.get(sample.date);
ctx.moveTo(x, y);
} else {
if (i === 0) {
lastX = model.axis.X.get(sample.date);
}
sum += sample.value;
x = model.axis.X.get(sample.date);
y = model.axis.Y.get(sum);
clipAndDrawSegment(ctx, model, lastX, lastY, x, y);
lastX = x;
lastY = y;
}
var onlyExistTomorrowSample = (i===0 && isTomorrow);
var isXInsideTheGraph = (x >= model.originX);
if ((isToday || onlyExistTomorrowSample) && isXInsideTheGraph) {
drawTodayMark(ctx, x, y, style.stroke);
return;
}
}
if (!style.pattern) {
return;
}
var pattern = ctx.createPattern(style.pattern, 'repeat');
ctx.globalCompositeOperation = 'source-atop';
ctx.fillStyle = pattern;
ctx.fillRect(0, 0, model.width, model.originY);
}
function drawWarningLayer(canvas, model) {
canvas.height = model.height;
canvas.width = model.width;
var ctx = canvas.getContext('2d');
if (!model.limits.enabled || model.limits.value === null) {
setAccessibilityAttributes(canvas);
return;
}
// No problem here
var mobileUsage = model.data.mobile.total;
var mobileUsageDifference = mobileUsage - model.limits.value;
if (mobileUsage <= model.limits.warningValue) {
setAccessibilityAttributes(canvas);
return;
}
// Warning mode
if (mobileUsage <= model.limits.value) {
var limitValue = Math.floor(model.axis.Y.get(model.limits.value));
var warningValue =
Math.round(model.axis.Y.get(model.limits.warningValue));
ctx.beginPath();
ctx.fillStyle = 'rgba(255, 112, 0, 0.5)';
ctx.fillRect(
model.originX, limitValue,
model.axis.X.len + 0.5, warningValue - limitValue
);
setAccessibilityAttributes(canvas, 'warning-layer', {
value: Formatting.formatData(Formatting.roundData(
-mobileUsageDifference))
});
return;
}
// Limit exceeded
var limitValueExceeded = model.axis.Y.get(model.limits.value);
ctx.beginPath();
ctx.fillStyle = 'rgba(255, 0, 0, 0.3)';
ctx.fillRect(
model.originX, 0,
model.axis.X.len + 0.5, limitValueExceeded
);
setAccessibilityAttributes(canvas, 'limit-exceeded-layer', {
value: Formatting.formatData(Formatting.roundData(mobileUsageDifference))
});
}
return {
// Constants
WIFI_CHART_STROKE: WIFI_CHART_STROKE,
WIFI_CHART_FILL: WIFI_CHART_FILL,
MOBILE_CHART_STROKE: MOBILE_CHART_STROKE,
MOBILE_CHART_FILL: MOBILE_CHART_FILL,
// Functions
toDevicePixels: toDevicePixels,
makeCSSFontString: makeCSSFontString,
formatChartDate: formatChartDate,
getLimitInBytes: getLimitInBytes,
calculateUpperDate: calculateUpperDate,
calculateLowerDate: calculateLowerDate,
expandModel: expandModel,
clipAndDrawSegment: clipAndDrawSegment,
drawTodayMark: drawTodayMark,
drawBackgroundLayer: drawBackgroundLayer,
drawTodayLayer: drawTodayLayer,
drawAxisLayer: drawAxisLayer,
drawLimits: drawLimits,
drawDataLayer: drawDataLayer,
drawWarningLayer: drawWarningLayer
};
}());

View file

@ -0,0 +1,4 @@
;'use strict';var DEBUGGING=false;var debug=(function(){var SEQ_ID=0;var PROCESS_ID=Date.now();var DEBUG_PREFIX='CC';return function _debug(){if(!DEBUGGING){return;}
var currentWindow=window;var parents=[];while(currentWindow!==currentWindow.parent){parents.push(currentWindow.location.pathname);currentWindow=currentWindow.parent;}
parents.push(currentWindow.location.pathname);parents=parents.reverse().join('>')+':';var uId=PROCESS_ID;var message=['('+uId+'-'+(SEQ_ID++)+')',DEBUG_PREFIX,parents];for(var i=0,len=arguments.length,obj;i<len;i++){obj=arguments[i];message.push(typeof obj==='object'?JSON.stringify(obj):obj);}
if(window.dump){message.push('\n');window.dump(message.join(' '));}else if(console&&console.log){console.log(message.join(' '));}};}());

View file

@ -0,0 +1,29 @@
;'use strict';var Formatting=(function(){var MINUTE=60*1000;var HOUR=60*MINUTE;var DAY=24*HOUR;function getDaysOfDifference(a,b){return Math.floor((a.getTime()-b.getTime())/DAY);}
function getFormattedDate(timestamp,formatter){return formatter.format(timestamp);}
function formatTime(timestamp,formatter){if(!timestamp){return _('never');}
var now=new Date(),then=new Date(timestamp);if(formatter){return getFormattedDate(then,formatter);}
var date,time;var daysOfDifference=getDaysOfDifference(now,then);if(daysOfDifference===0){date=_('today');}else if(daysOfDifference===1){date=_('yesterday');}else{date=getFormattedDate(timestamp,Formatting.formatters.shortWeekday);}
time=getFormattedDate(timestamp,Formatting.formatters.shortTime);return _('day-hour-format',{day:date,time:time});}
function formatTimeSinceNow(timestamp){var now=new Date(),then=new Date(timestamp);var formattedTime=formatTime(timestamp);if(getDaysOfDifference(now,then)>0){return formattedTime;}
var age=now-then;if(age<MINUTE){formattedTime=_('minutes-ago-short',{value:0});}else if(age<HOUR){var minutes=Math.floor(age/MINUTE);formattedTime=_('minutes-ago-short',{value:minutes});}else if(age<DAY){var hours=Math.floor(age/HOUR);formattedTime=_('hours-ago-short',{value:hours});}
return formattedTime;}
function formatData(dataArray){return isNaN(dataArray[0])?'':_('magnitude',{value:dataArray[0],unit:dataArray[1]});}
function roundData(value,positions){positions=(typeof positions==='undefined')?2:positions;if(value<1000){return[value.toFixed(positions),_('B')];}
if(value<1000000){return[(value/1000).toFixed(positions),_('KB')];}
if(value<1000000000){return[(value/1000000).toFixed(positions),_('MB')];}
return[(value/1000000000).toFixed(positions),_('GB')];}
function getPositions(value,deltaPositions){deltaPositions=(typeof deltaPositions==='undefined')?0:deltaPositions;if(parseInt(value)===value){return 0;}
if(value<10){return 2+deltaPositions;}
if(value<100){return 1+deltaPositions;}
return 0;}
function smartRound(value,deltaPositions){deltaPositions=(typeof deltaPositions==='undefined')?0:deltaPositions;if(value<1000){var bPositions=getPositions(value,deltaPositions);return[value.toFixed(bPositions),_('B')];}
if(value<1000000){var kbytes=value/1000;var kbPositions=getPositions(kbytes,deltaPositions);return[kbytes.toFixed(kbPositions),_('KB')];}
if(value<1000000000){var mbytes=value/1000000;var mPositions=getPositions(mbytes,deltaPositions);return[mbytes.toFixed(mPositions),_('MB')];}
var gbytes=value/1000000000;var gPositions=getPositions(gbytes,deltaPositions);return[gbytes.toFixed(gPositions),_('GB')];}
function formatTimeHTML(timestampA,timestampB){function timeElement(content){var time=document.createElement('time');time.textContent=content;return time;}
var fragment=document.createDocumentFragment();if(typeof timestampB==='undefined'){fragment.appendChild(timeElement(Formatting.formatTime(timestampA)));return fragment;}
var dateA=new Date(timestampA);var dateB=new Date(timestampB);if(dateA.getFullYear()===dateB.getFullYear()&&dateA.getMonth()===dateB.getMonth()&&dateA.getDay()===dateB.getDay()){return formatTimeHTML(timestampB);}
fragment.appendChild(timeElement(Formatting.formatTime(timestampA,Formatting.formatters.shortDate)));fragment.appendChild(document.createTextNode(' – '));fragment.appendChild(timeElement(Formatting.formatTime(timestampB)));return fragment;}
function computeTelephonyMinutes(activity){return Math.ceil(activity.calltime/60000);}
return{getFormattedDate:getFormattedDate,formatTime:formatTime,formatTimeSinceNow:formatTimeSinceNow,formatData:formatData,roundData:roundData,getPositions:getPositions,smartRound:smartRound,formatTimeHTML:formatTimeHTML,computeTelephonyMinutes:computeTelephonyMinutes,formatters:{}};}());function setFormatters(){const locales=navigator.languages;Formatting.formatters.longDate=Intl.DateTimeFormat(locales,{month:'long',day:'numeric',year:'numeric'});Formatting.formatters.shortDate=Intl.DateTimeFormat(locales,{month:'short',day:'numeric',});Formatting.formatters.shortTime=Intl.DateTimeFormat(locales,{hour12:navigator.mozHour12,hour:'numeric',minute:'numeric'});Formatting.formatters.shortWeekday=Intl.DateTimeFormat(locales,{weekday:'short'});}
setFormatters();window.addEventListener('languagechange',setFormatters,false);window.addEventListener('timeformatchange',setFormatters,false);

View file

@ -0,0 +1,6 @@
;'use strict';var _=function cc_fallbackTranslation(keystring){var r=window.navigator.mozL10n.get.apply(this,arguments);return r||(DEBUGGING?'!!':'')+keystring;};function deepCopy(object){if(object===null||!(object instanceof Object)){return object;}
var clone,constructor=object.constructor;switch(constructor){case Date:clone=new constructor(object.getTime());break;default:clone=constructor();break;}
for(var key in object){if(object.hasOwnProperty(key)){clone[key]=deepCopy(object[key]);}}
return clone;}
var Toolkit={checkEnoughDelay:function(threshold,dateA,dateB){if(!dateA){return true;}
var end=dateB||new Date();return(end.getTime()-dateA.getTime())>threshold;},toMidnight:function _toMidnight(date){date.setHours(0);date.setMinutes(0);date.setSeconds(0);date.setMilliseconds(0);return date;}};