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,19 @@
{
"name": "notification-sender",
"version": "0.0.0",
"homepage": "https://github.com/mozilla-b2g/gaia/tree/master/tv_apps/notification-sender",
"description": "Notification Sender",
"main": "index.html",
"license": "MPL",
"private": true,
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
],
"dependencies": {
"evt": "fxos-components/evt#~0.4.0"
}
}

View file

@ -0,0 +1,28 @@
{
"name": "evt",
"main": "index.js",
"version": "0.4.0",
"homepage": "https://github.com/wilsonpage/evt",
"authors": [
"Wilson Page <wilsonpage@me.com>"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"package.json",
"test",
"tests",
"README.md"
],
"_release": "0.4.0",
"_resolution": {
"type": "version",
"tag": "v0.4.0",
"commit": "3593dd6b4dd19f683d00fd0eaf2581c11a128da6"
},
"_source": "git://github.com/gaia-components/evt.git",
"_target": "~0.4.0",
"_originalSource": "gaia-components/evt"
}

View file

@ -0,0 +1,19 @@
{
"name": "evt",
"main": "index.js",
"version": "0.4.0",
"homepage": "https://github.com/wilsonpage/evt",
"authors": [
"Wilson Page <wilsonpage@me.com>"
],
"license": "MIT",
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"package.json",
"test",
"tests",
"README.md"
]
}

View file

@ -0,0 +1,154 @@
/**
* Evt
*
* A super lightweight
* event emitter library.
*
* @version 0.3.3
* @author Wilson Page <wilson.page@me.com>
*/
;(function() {
/**
* Locals
*/
var proto = Events.prototype;
var slice = [].slice;
/**
* Creates a new event emitter
* instance, or if passed an
* object, mixes the event logic
* into it.
*
* @param {Object} obj
* @return {Object}
*/
function Events(obj) {
if (!(this instanceof Events)) return new Events(obj);
if (obj) return mixin(obj, proto);
}
/**
* Registers a callback
* with an event name.
*
* @param {String} name
* @param {Function} cb
* @return {Event}
*/
proto.on = function(name, cb) {
this._cbs = this._cbs || {};
(this._cbs[name] || (this._cbs[name] = [])).push(cb);
return this;
};
/**
* Attach a callback that once
* called, detaches itself.
*
* TODO: Implement `.off()` to work
* with `once()` callbacks.
*
* @param {String} name
* @param {Function} cb
* @public
*/
proto.once = function(name, cb) {
this.on(name, one);
function one() {
cb.apply(this, arguments);
this.off(name, one);
}
};
/**
* Removes a single callback,
* or all callbacks associated
* with the passed event name.
*
* @param {String} name
* @param {Function} cb
* @return {Event}
*/
proto.off = function(name, cb) {
this._cbs = this._cbs || {};
if (!name) { this._cbs = {}; return; }
if (!cb) { return delete this._cbs[name]; }
var cbs = this._cbs[name] || [];
var i;
while (cbs && ~(i = cbs.indexOf(cb))) { cbs.splice(i, 1); }
return this;
};
/**
* Fires an event, triggering
* all callbacks registered on this
* event name.
*
* @param {String} name
* @return {Event}
*/
proto.fire = proto.emit = function(options) {
var cbs = this._cbs = this._cbs || {};
var name = options.name || options;
var batch = (cbs[name] || []).concat(cbs['*'] || []);
var ctx = options.ctx || this;
if (batch.length) {
this._fireArgs = arguments;
var args = slice.call(arguments, 1);
while (batch.length) {
batch.shift().apply(ctx, args);
}
}
return this;
};
proto.firer = function(name) {
var self = this;
return function() {
var args = slice.call(arguments);
args.unshift(name);
self.fire.apply(self, args);
};
};
/**
* Util
*/
/**
* Mixes in the properties
* of the second object into
* the first.
*
* @param {Object} a
* @param {Object} b
* @return {Object}
*/
function mixin(a, b) {
for (var key in b) a[key] = b[key];
return a;
}
/**
* Expose 'Event' (UMD)
*/
if (typeof exports === 'object') {
module.exports = Events;
} else if (typeof define === 'function' && define.amd) {
define(function(){ return Events; });
} else {
window.evt = Events;
}
})();

View file

@ -0,0 +1,66 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-cale=1">
<meta charset="utf-8">
<title>Notification Sender</title>
<!-- Building blocks -->
<link rel="stylesheet" type="text/css" href="shared/style/headers.css"></link>
<link rel="stylesheet" type="text/css" href="shared/style/buttons.css"></link>
<link rel="stylesheet" type="text/css" href="shared/style/input_areas.css"></link>
<link rel="stylesheet" type="text/css" href="shared/style/status.css"></link>
<!-- App main style -->
<link rel="stylesheet" type="text/css" href="style/notification-sender.css"></link>
<!-- Shared libraries -->
<script src="shared/js/l10n.js"></script>
<script defer src="bower_components/evt/index.js"></script>
<!-- Specific code -->
<script defer src="js/notification_sender.js"></script>
<script defer src="js/bootstrap.js"></script>
<!-- L10n properties -->
<meta name="defaultLanguage" content="en-US">
<meta name="availableLanguages" content="en-US">
<link rel="localization" href="locales/notification-sender.{locale}.properties"/>
<link rel="localization" href="shared/locales/date/date.{locale}.properties"/>
</head>
<body>
<section role="region">
<header>
<h1>Notification Sender</h1>
</header>
<input id="url-input" type="text" value="app://notification-receiver.gaiamobile.org/index.html"></input><br>
<button type="underlined-text" id="connect-btn" label="connect">
Connect
</button><br>
<button type="underlined-text" id="disconnect-btn" label="disconnect">
Disconnect
</button><br>
<hr>
<section id="send-panel">
<button type="underlined-text" data-message-title="Smart Phone" data-message-body="You’ve got a new message." data-message-type="Message">
Message
</button><br>
<button type="underlined-text" data-message-title="Washing Machine" data-message-body="Your laundry is ready." data-message-type="Laundry">
Laundry
</button><br>
<button type="underlined-text" data-message-title="Smart Watch" data-message-body="Welcome Home." data-message-type="Home">
Home
</button>
<button type="underlined-text" data-message-title="Smart Phone" data-message-body="You’ve got a new mail." data-message-type="Mail">
Mail
</button>
</section>
<section role="status">
<p id='state-label'></p>
</section>
</section>
</body>
</html>

View file

@ -0,0 +1,3 @@
/* globals NotificationSender */
window.notificationSender = new NotificationSender();
window.notificationSender.init();

View file

@ -0,0 +1,94 @@
/* global Promise */
(function(exports) {
'use strict';
function NotificationSender() {}
NotificationSender.prototype = {
sendPanel: document.getElementById('send-panel'),
urlInput: document.getElementById('url-input'),
connectBtn: document.getElementById('connect-btn'),
disconnectBtn: document.getElementById('disconnect-btn'),
stateLabel: document.getElementById('state-label'),
senderButtons: [],
init: function ns_init() {
this.senderButtons =
Array.from(this.sendPanel.getElementsByTagName('button'));
this.sendPanel.addEventListener('click', this);
this.connectBtn.addEventListener('click', this.connect.bind(this));
this.disconnectBtn.addEventListener('click', this.disconnect.bind(this));
this.disableButtons(true);
this._updateSessionState();
},
handleEvent: function ns_handleEvent(evt) {
var target = evt.target;
switch (evt.type) {
case 'click':
if (target.tagName == 'BUTTON') {
this.sendMessage({
type: target.dataset.messageType,
title: target.dataset.messageTitle,
body: target.dataset.messageBody
});
}
break;
}
},
uninit: function ns_uninit() {
},
disableButtons: function ns_switchButtons(disabled) {
this.senderButtons.forEach(function(button) {
button.disabled = disabled;
}, this);
},
sendMessage: function ns_sendMessage(message) {
if (!this.session || !this.session.state) {
console.log('unable to send message: ' + message);
return;
}
this.session.send(JSON.stringify(message));
},
connect: function ns_connect() {
if(!navigator.mozPresentation) {
return null;
}
return navigator.mozPresentation.startSession(this.urlInput.value).then(
function onFullfilled(session) {
this.session = session;
this.disableButtons(false);
this.session.onstatechange = this._updateSessionState.bind(this);
return Promise.resolve();
}.bind(this),
function onRejected() {
console.log('Unable to connect remote.');
return Promise.reject();
}
);
},
disconnect: function ns_disconnect() {
this.session && this.session.disconnect();
this.disableButtons(true);
delete this.session;
},
_updateSessionState: function ns_updateSessionState() {
this.stateLabel.textContent =
(this.session && this.session.state) ? 'connected' : 'disconnected';
}
};
exports.NotificationSender = NotificationSender;
}(window));

View file

@ -0,0 +1,21 @@
{
"name": "notification-sender",
"description": "Notification Sender",
"launch_path": "/index.html",
"type": "certified",
"developer": {
"name": "The Gaia Team",
"url": "https://github.com/mozilla-b2g/gaia"
},
"locales": {
"en-US": {
"name": "Notification Sender",
"description": "Notification Sender"
}
},
"default_locale": "en-US",
"icons": {
"90": "/style/icons/appic_iot_90.png",
"135": "/style/icons/appic_iot_135.png"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View file

@ -0,0 +1,15 @@
html {
font-size: 10px;
/* We have Fira Sans as default font in real device. But it isn't default in
* browser. That's the main reason this line is here.*/
font-family: "Fira Sans";
}
html, body {
width: 100%;
height: 100%;
overflow: hidden;
margin: 0;
padding: 0;
background-color: white;
}

View file

@ -0,0 +1,17 @@
{
"extends": "../../../../.jshintrc",
"predef": [
"assert",
"suite",
"test",
"setup",
"teardown",
"suiteSetup",
"suiteTeardown",
"require",
"requireApp",
"sinon",
"mocha",
"loadBodyHTML"
]
}

View file

@ -0,0 +1,78 @@
/* globals NotificationSender, MockPresentation, MockPresentationSession */
'use strict';
require('/bower_components/smart-button/script.js');
require('/shared/test/unit/mocks/mock_presentation.js');
require('/shared/test/unit/mocks/mock_presentation_session.js');
suite('NotificationSender >', function() {
var subject;
var realPresentation;
var urlInput;
suiteSetup(function(done) {
urlInput = document.createElement('input');
urlInput.id = 'url-input';
urlInput.value = 'http://www.test.url.goes.here';
document.body.appendChild(urlInput);
require('/tv_apps/notification-sender/js/notification_sender.js', done);
});
suiteTeardown(function() {
document.body.removeChild(urlInput);
});
setup(function() {
subject = new NotificationSender();
realPresentation = navigator.mozPresentation;
navigator.mozPresentation = MockPresentation;
});
teardown(function() {
navigator.mozPresentation = realPresentation;
MockPresentation._mReset();
MockPresentationSession._mReset();
});
test('connect() should enable buttons', function(done) {
var spy = this.sinon.spy(subject, 'disableButtons');
spy.withArgs(false);
subject.connect().then(function() {
assert.isTrue(spy.withArgs(false).calledOnce);
done();
});
});
test('connect() should listen to session.onstatechange', function(done) {
assert.isUndefined(MockPresentationSession._onstatechange);
subject.connect().then(function() {
assert.isFunction(MockPresentationSession._onstatechange);
done();
});
});
suite('Connected behaviors > ', function() {
setup(function(done) {
subject.connect().then(function() {
done();
});
});
test('sendMessage() should call session.sendMessage', function() {
var data = {data: 'hello, world'};
var dataJSON = JSON.stringify(data);
var spy = this.sinon.spy(MockPresentationSession, 'send');
spy.withArgs(dataJSON);
subject.sendMessage(data);
assert.isTrue(spy.withArgs(dataJSON).calledOnce);
});
});
});

View file

@ -0,0 +1,4 @@
'use strict';
require('/shared/test/unit/mocks/mocks_helper.js');