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

294
gaia/apps/clock/js/timer.js Executable file
View file

@ -0,0 +1,294 @@
define(function(require) {
'use strict';
var asyncStorage = require('shared/js/async_storage');
var Utils = require('utils');
var timerPrivate = new WeakMap();
/**
* Timer
*
* Create new or revive existing timer objects.
*
* @param {Object} opts Optional timer object to create or revive
* a new or existing timer object.
* - startTime, number time in ms.
* - duration, time to count from `start`.
* - configuredDuration, time requested by user.
* - sound, string sound name.
* - vibrate, boolean, vibrate or not.
* - id, integer, mozAlarm API id number.
*/
function Timer(opts) {
opts = opts || {};
var now = Date.now();
if (opts.id !== undefined) {
delete opts.id;
}
// private properties
timerPrivate.set(this, Utils.extend({
state: Timer.INITIAL
}, extractProtected(opts)));
// public properties
Utils.extend(this, {
startTime: now,
duration: null,
configuredDuration: null,
sound: 'ac_awake.opus',
vibrate: true
}, opts);
}
Timer.prototype.constructor = Timer;
/**
* request - get the persisted Timer object.
*
* @param {function} [callback] - called with (err, timer_raw).
*/
Timer.getFromStorage = function(callback) {
asyncStorage.getItem('active_timer', function(timer) {
if (timer) {
// Normalize the timer data. Pre-April-2014 code may have stored
// 'vibrate' and 'sound' as the string "0".
timer.sound = (timer.sound !== '0' ? timer.sound : null);
timer.vibrate = (timer.vibrate && timer.vibrate !== '0');
}
callback && callback(timer || null);
});
};
/**
* singleton - get the unique persisted Timer object.
*
* @param {function} [callback] - called with (err, timer).
*/
var timerSingleton = Utils.singleton(Timer);
Timer.singleton = function tm_singleton(callback) {
Timer.getFromStorage(function(err, obj) {
var ts = timerSingleton(obj);
callback && callback(null, ts);
});
};
function extractProtected(config) {
var ret = {};
var protectedProperties = new Set(['state']);
for (var i in config) {
if (protectedProperties.has(i)) {
ret[i] = config[i];
delete config[i];
}
}
return ret;
}
/**
* toSerializable - convert `this` to a serialized format.
*
* @return {object} - object representation of this Timer.
*/
Timer.prototype.toSerializable = function timerToSerializable() {
var timer = Utils.extend({}, this, timerPrivate.get(this));
// Normalize the data. TODO: Perform this normalization immediately
// at the getter/setter level when this class is refactored.
return {
startTime: timer.startTime,
duration: timer.duration,
configuredDuration: timer.configuredDuration,
sound: (timer.sound !== '0' ? timer.sound : null),
vibrate: (timer.vibrate !== '0' ? timer.vibrate : null),
state: timer.state
};
};
/**
* save - Save the timer to the database.
*
* @param {function} [callback] - callback to call after the timer
* has been saved.
*/
Timer.prototype.save = function timerSave(callback) {
asyncStorage.setItem('active_timer', this.toSerializable(), function() {
callback && callback(null, this);
}.bind(this));
};
/**
* register - Register the timer with mozAlarm API.
*
* @param {function} [callback] - callback to call after the timer
* has been registered.
*/
Timer.prototype.register = function timerRegister(callback) {
var data = {
type: 'timer'
};
var request;
// Remove previously-created mozAlarm for this alarm, if necessary.
this.unregister();
request = navigator.mozAlarms.add(
new Date(Date.now() + this.remaining), 'ignoreTimezone', data
);
request.onsuccess = (function(ev) {
this.id = ev.target.result;
callback && callback(null, this);
}.bind(this));
request.onerror = function(ev) {
callback && callback(ev.target.error);
};
};
/**
* commit - save and register the timer as necessary.
*
* @param {function} [callback] - callback to call after the timer
* has been registered.
*/
Timer.prototype.commit = function timerCommit(callback) {
var saveSelf = this.save.bind(this, callback);
if (this.state === Timer.STARTED) {
this.register(saveSelf);
} else {
this.unregister();
saveSelf();
}
};
Timer.prototype.unregister = function timerUnregister() {
if (typeof this.id === 'number') {
navigator.mozAlarms.remove(this.id);
}
};
Object.defineProperty(Timer.prototype, 'remaining', {
get: function() {
if (this.state === Timer.INITIAL) {
return this.configuredDuration;
} else if (this.state === Timer.PAUSED) {
return this.duration;
} else if (this.state === Timer.STARTED) {
if (typeof this.startTime === 'undefined' ||
typeof this.duration === 'undefined') {
return 0;
}
var r = (this.startTime + this.duration) - Date.now();
return r >= 0 ? r : 0;
}
}
});
Object.defineProperty(Timer.prototype, 'state', {
get: function() {
var priv = timerPrivate.get(this);
return priv.state;
}
});
Timer.prototype.start = function timerStart() {
if (this.state !== Timer.STARTED) {
var priv = timerPrivate.get(this);
priv.state = Timer.STARTED;
this.startTime = Date.now();
this.duration = (typeof this.duration === 'number') ? this.duration :
this.configuredDuration;
window.dispatchEvent(new CustomEvent('timer-start', {
detail: { remaining: this.remaining }
}));
this._tick();
}
};
Timer.prototype.pause = function timerPause() {
if (this.state === Timer.STARTED) {
this.duration = this.remaining; // remaining getter observes private state
var priv = timerPrivate.get(this);
priv.state = Timer.PAUSED;
this.startTime = null;
this._cancelTick();
window.dispatchEvent(new CustomEvent('timer-pause', {
detail: { remaining: this.remaining }
}));
}
};
Timer.prototype.cancel = function timerReset() {
if (this.state !== Timer.INITIAL) {
var priv = timerPrivate.get(this);
priv.state = Timer.INITIAL;
this.startTime = null;
this.duration = this.configuredDuration;
this._cancelTick();
this._dispatchEnd();
}
};
/**
* plus Increase the duration and extend the endAt time
*
* @param {Number} seconds The time in seconds to add.
*
* @return {Timer} Timer instance.
*/
Timer.prototype.plus = function timerPlus(seconds) {
// Convert to ms
var ms = seconds * 1000;
this.duration += ms;
return this;
};
Timer.prototype._dispatchEnd = function timerDispatchEnd() {
// cancel and end are basically the same thing
window.dispatchEvent(new CustomEvent('timer-end', {
detail: { remaining: 0 }
}));
};
Timer.prototype._tick = function timerTick() {
if (this.state !== Timer.STARTED) {
return;
}
// we cache the previous value posted to avoid triggering multiple "tick"
// messages for the same second
var remaining = this.remaining;
var current = Math.round(remaining / 1000);
if (current !== this._previousTick) {
this._previousTick = current;
window.dispatchEvent(new CustomEvent('timer-tick', {
detail: { remaining: remaining }
}));
}
if (remaining > 0) {
// use setTimeout instead of requestAnimationFrame because window will
// probably be hidden when handling connections
this._tickTimeout = window.setTimeout(() => this._tick(), 100);
} else {
this._dispatchEnd();
}
};
Timer.prototype._cancelTick = function timerCancelTick() {
window.clearTimeout(this._tickTimeout);
};
/**
* Static "const" Timer states.
*/
Object.defineProperties(Timer, {
INITIAL: { value: 0 },
STARTED: { value: 1 },
PAUSED: { value: 2 }
});
return Timer;
});