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,318 @@
define(function(require) {
'use strict';
/* global IntlHelper */
var Panel = require('panel');
var Picker = require('picker/picker');
var View = require('view');
var Utils = require('utils');
var Timer = require('timer');
var Sounds = require('sounds');
var FormButton = require('form_button');
var html = require('text!panels/timer/panel.html');
var AudioManager = require('audio_manager');
var asyncStorage = require('shared/js/async_storage');
var priv = new WeakMap();
function timeFromPicker(value) {
var hm, ms;
hm = value.split(':');
ms = Utils.dateMath.toMS({
hours: hm[0],
minutes: hm[1]
});
return ms;
}
IntlHelper.define('timer-hms', 'mozduration', {
minUnit: 'second',
maxUnit: 'hour'
});
/**
* Timer.Panel
*
* Construct a UI panel for the Timer panel.
*
* @return {Timer.Panel} Timer.Panel object.
*
*/
Timer.Panel = function(element) {
Panel.apply(this, arguments);
element.innerHTML = html;
this.timer = null;
this.nodes = {};
this.picker = new Picker({
element: this.element.querySelector('#time-picker'),
pickers: {
hours: {
range: [0, 23],
l10nId: 'nSpinnerHours'
},
minutes: {
range: [0, 59],
isPadded: true,
l10nId: 'nSpinnerMinutes'
}
}
});
this.ringtonePlayer = AudioManager.createAudioPlayer();
// Gather elements
[
'create', 'cancel', 'dialog', 'pause', 'start', 'sound', 'time', 'vibrate',
'plus'
].forEach(function(id) {
this.nodes[id] = this.element.querySelector('#timer-' + id);
}, this);
// Bind click events
[
'create', 'cancel', 'pause', 'start', 'plus'
].forEach(function(action) {
var element = this.nodes[action];
if (priv.has(element)) {
priv.delete(element);
}
priv.set(element, {
action: action,
panel: this
});
element.addEventListener('click', this.onclick.bind(this), false);
}, this);
var sound = this.nodes.sound;
sound.addEventListener('blur', this.pauseAlarm.bind(this), false);
sound.addEventListener('change', this.previewAlarm.bind(this), false);
var soundMenuConfig = {
id: 'timer-sound-menu',
formatLabel: Sounds.formatLabel
};
this.soundButton = new FormButton(sound, soundMenuConfig);
this.soundButton.refresh();
element.addEventListener('panel-visibilitychange',
this.onvisibilitychange.bind(this));
// The start button is disable by default (picker at 00:00 by default)
this.nodes.create.setAttribute('disabled', 'true');
var create = this.nodes.create;
var picker = this.picker;
var enableButton = () => {
if(timeFromPicker(picker.value) === 0) {
create.setAttribute('disabled', 'true');
} else {
create.removeAttribute('disabled');
}
this.saveCurrentSettingsAsDefaults();
};
// The start button is enable if the value of the timer is not 00:00
picker.nodes.minutes.addEventListener('transitionend', enableButton);
picker.nodes.hours.addEventListener('transitionend', enableButton);
this.restoreDefaults().then(() => {
enableButton(); // Update the enabled state to match the new defaults.
});
this.nodes.vibrate.addEventListener(
'change', (e) => this.saveCurrentSettingsAsDefaults());
this.nodes.sound.addEventListener(
'change', (e) => this.saveCurrentSettingsAsDefaults());
Timer.singleton(function(err, timer) {
this.timer = timer;
var onTimerEvent = this.onTimerEvent.bind(this);
window.addEventListener('timer-start', onTimerEvent);
window.addEventListener('timer-pause', onTimerEvent);
window.addEventListener('timer-tick', onTimerEvent);
window.addEventListener('timer-end', onTimerEvent);
IntlHelper.observe('timer-hms', this.update.bind(this));
if (this.visible) {
// If the timer panel already became visible before we fetched
// the timer, we must update the display to show the proper
// timer status.
this.onvisibilitychange({ detail: { isVisible: true } });
}
}.bind(this));
};
Timer.Panel.prototype = Object.create(Panel.prototype);
Timer.Panel.prototype.restoreDefaults = function() {
return new Promise((resolve) => {
asyncStorage.getItem('timer-defaults', (values) => {
values = values || {};
if (values.sound != null) {
this.soundButton.value = values.sound;
}
if (values.time != null) {
this.picker.value = values.time;
}
if (values.vibrate != null) {
this.nodes.vibrate.checked = values.vibrate;
}
resolve();
});
});
};
Timer.Panel.prototype.saveCurrentSettingsAsDefaults = function() {
asyncStorage.setItem('timer-defaults', {
sound: this.soundButton.value,
time: this.picker.value,
vibrate: this.nodes.vibrate.checked
});
};
Timer.Panel.prototype.onvisibilitychange = function(evt) {
var isVisible = evt.detail.isVisible;
var timer = this.timer;
if (isVisible) {
// No active timer, or timer has expired...
// - Show the new timer dialog
if (timer === null || timer.state === Timer.INITIAL) {
this.showDialog();
} else {
// Active timer exists, might be started by an external app, so we need
// to make sure dialog is hidden and buttons/time are on correct state
this.toggleButtons();
this.hideDialog();
this.update();
}
} else {
this.pauseAlarm();
}
};
Timer.Panel.prototype._toggleDialog = function(isVisible = true) {
View.instance(this.nodes.dialog).visible = isVisible;
return this;
};
Timer.Panel.prototype.showDialog = function() {
return this._toggleDialog(true);
};
Timer.Panel.prototype.hideDialog = function() {
return this._toggleDialog(false);
};
Timer.Panel.prototype.onTimerEvent = function(event) {
switch (event.type) {
case 'timer-start':
// new timer dialog should always be hidden during timer-start (even
// if started from external app)
this.hideDialog();
/* falls through */
case 'timer-pause':
this.toggleButtons();
/* falls through */
case 'timer-tick':
this.update();
break;
case 'timer-end':
this.showDialog();
break;
}
};
/**
* Given milliseconds, render the time as a rounded-to-seconds
* countdown.
*/
Timer.Panel.prototype.update = function() {
return IntlHelper.get('timer-hms').then(formatter => {
var remaining = this.timer.remaining;
var newText = formatter.format(remaining);
// Use localized caching here to prevent unnecessary DOM repaints.
if (this._cachedTimerText !== newText) {
this.nodes.time.textContent = this._cachedTimerText = newText;
}
});
};
Timer.Panel.prototype.toggleButtons = function() {
var nodes = this.nodes;
var started = this.timer.state === Timer.STARTED;
nodes.pause.classList.toggle('hidden', !started);
nodes.start.classList.toggle('hidden', started);
};
/**
* previewAlarm Plays the currently selected alarm value on a loop.
*/
Timer.Panel.prototype.previewAlarm = function() {
var ringtoneName = Utils.getSelectedValueByIndex(this.nodes.sound);
this.ringtonePlayer.playRingtone(ringtoneName);
};
/**
* pauseAlarm stops the alarm if it is playing
*/
Timer.Panel.prototype.pauseAlarm = function() {
this.ringtonePlayer.pause();
};
/**
* handleEvent Handler for all panel bound UI events.
* (`this` context object is not Timer.Panel)
*
* @param {Event} event The Event object.
*/
Timer.Panel.prototype.onclick = function(event) {
var meta = priv.get(event.target);
var panel = meta.panel;
var nodes = panel.nodes;
var timer = panel.timer;
var time;
if (meta.action === 'create') {
time = timeFromPicker(panel.picker.value);
if (!time) {
return;
}
timer.duration = time;
timer.sound = nodes.sound.value;
timer.vibrate = nodes.vibrate.checked;
timer.start();
// Hide the new timer dialog
panel.hideDialog();
} else if (meta.action === 'plus') {
timer.plus(+event.target.dataset.value);
// need to update immediately cause timer might be paused during click
panel.update();
} else {
// meta.action => timer[meta.action]()
//
// ie.
//
// if start => timer.start()
// if pause => timer.pause()
// if cancel => timer.cancel()
//
timer[meta.action]();
}
timer.commit();
};
return Timer.Panel;
});

View file

@ -0,0 +1,78 @@
<div id="timer-dialog" class="panel skin-dark">
<div id="value-selector">
<!-- Time Picker -->
<div id="time-picker" data-type="picker">
<div class="picker-container">
<div class="picker-labels">
<div class="picker-label" id="hours" data-l10n-id="hours">
</div>
<div class="picker-label" id="minutes" data-l10n-id="minutes">
</div>
</div>
<div class="picker-bar-background">
</div>
<div class="picker-hours-wrapper" role="spinbutton" aria-labelledby="hours">
<div class="picker-hours">
</div>
</div>
<div class="picker-minutes-wrapper" role="spinbutton" aria-labelledby="minutes">
<div class="picker-minutes">
</div>
</div>
<div class="value-indicator">
<div class="value-indicator-colon" aria-hidden="true" data-l10n-id="hourMinutesSeparator"></div>
</div>
</div>
</div>
<ul id="timer-edit" class="compact">
<li>
<button id="timer-create" class="recommend" data-l10n-id="start">
</button>
</li>
<li>
<label data-l10n-id="sound"></label>
<select id="timer-sound">
<option value="0" data-l10n-id="noSound"></option>
<option value="ac_awake.opus" data-l10n-id="ac_awake_opus" selected></option>
<option value="ac_crystalize.opus" data-l10n-id="ac_crystalize_opus"></option>
<option value="ac_cycle.opus" data-l10n-id="ac_cycle_opus"></option>
<option value="ac_digicloud.opus" data-l10n-id="ac_digicloud_opus"></option>
<option value="ac_humming_waves.opus" data-l10n-id="ac_humming_waves_opus"></option>
<option value="ac_into_the_void.opus" data-l10n-id="ac_into_the_void_opus"></option>
<option value="ac_lightly.opus" data-l10n-id="ac_lightly_opus"></option>
<option value="ac_mobile.opus" data-l10n-id="ac_mobile_opus"></option>
<option value="ac_pinger.opus" data-l10n-id="ac_pinger_opus"></option>
<option value="ac_skip.opus" data-l10n-id="ac_skip_opus"></option>
<option value="ac_tri.opus" data-l10n-id="ac_tri_opus"></option>
<option value="ac_universal.opus" data-l10n-id="ac_universal_opus"></option>
</select>
</li>
<li>
<gaia-switch id="timer-vibrate" checked>
<label data-l10n-id="vibrate"></label>
</gaia-switch>
</li>
</ul>
</div>
</div>
<div id="timer-active">
<div id="timer-time-display">
<div id="timer-time"></div>
<menu id="timer-controls">
<button id="timer-start" class="edit-button" data-l10n-id="resume">
</button>
<button id="timer-pause" class="edit-button" data-l10n-id="pause">
</button>
<button id="timer-cancel" class="danger" data-l10n-id="cancel">
</button>
</menu>
</div>
<div id="timer-plus-time">
<button id="timer-plus" data-value="60" data-l10n-id="plusMinute">
</button>
</div>
</div>