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.
8
gaia/tv_apps/smart-system/blank.html
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
24
gaia/tv_apps/smart-system/bower.json
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "smart-system",
|
||||
"version": "0.0.0",
|
||||
"homepage": "https://github.com/mozilla-b2g/gaia/tree/master/tv_apps/smart-system",
|
||||
"description": "Main System",
|
||||
"main": "index.html",
|
||||
"license": "MPL",
|
||||
"private": true,
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"dependencies": {
|
||||
"evt": "fxos-components/evt#~0.4.0",
|
||||
"smart-button": "fxos-components/smart-button#~0.0.2",
|
||||
"smart-banner": "fxos-components/smart-banner",
|
||||
"smart-bubbles": "smart-components/smart-bubbles",
|
||||
"smart-dialog": "smart-components/smart-dialog",
|
||||
"smart-modal-dialog": "smart-components/smart-modal-dialog"
|
||||
}
|
||||
}
|
||||
28
gaia/tv_apps/smart-system/bower_components/evt/.bower.json
vendored
Executable 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"
|
||||
}
|
||||
19
gaia/tv_apps/smart-system/bower_components/evt/bower.json
vendored
Executable 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"
|
||||
]
|
||||
}
|
||||
154
gaia/tv_apps/smart-system/bower_components/evt/index.js
vendored
Executable 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;
|
||||
}
|
||||
|
||||
})();
|
||||
29
gaia/tv_apps/smart-system/bower_components/smart-banner/.bower.json
vendored
Executable file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "smart-banner",
|
||||
"homepage": "https://github.com/gaia-components/smart-banner",
|
||||
"main": "script.js",
|
||||
"license": "MPL",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"examples",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"devDependencies": {
|
||||
"evt": "gaia-components/evt#~0.4.0",
|
||||
"smart-button": "gaia-components/smart-button"
|
||||
},
|
||||
"_release": "01f885eced",
|
||||
"_resolution": {
|
||||
"type": "branch",
|
||||
"branch": "master",
|
||||
"commit": "01f885eced4ebe6e601bd252b2b5730309960b90"
|
||||
},
|
||||
"_source": "git://github.com/gaia-components/smart-banner.git",
|
||||
"_target": "*",
|
||||
"_originalSource": "gaia-components/smart-banner"
|
||||
}
|
||||
21
gaia/tv_apps/smart-system/bower_components/smart-banner/bower.json
vendored
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "smart-banner",
|
||||
"version": "0.0.0",
|
||||
"homepage": "https://github.com/gaia-components/smart-banner",
|
||||
"main": "script.js",
|
||||
"license": "MPL",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"examples",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"devDependencies": {
|
||||
"evt": "gaia-components/evt#~0.4.0",
|
||||
"smart-button": "gaia-components/smart-button"
|
||||
}
|
||||
}
|
||||
130
gaia/tv_apps/smart-system/bower_components/smart-banner/script.js
vendored
Executable file
|
|
@ -0,0 +1,130 @@
|
|||
'use strict';
|
||||
/* global evt */
|
||||
|
||||
window.SmartBanner = (function(win) {
|
||||
|
||||
// Extend from the HTMLElement prototype
|
||||
var proto = evt(Object.create(HTMLElement.prototype));
|
||||
|
||||
proto.createdCallback = function() {
|
||||
this.init();
|
||||
this.addEventListener('transitionend', this);
|
||||
this.addEventListener('animationend', this);
|
||||
};
|
||||
|
||||
proto.init = function() {
|
||||
this.style.width = '0';
|
||||
this.classList.add('closed');
|
||||
};
|
||||
|
||||
proto.calculateChildWidth = function() {
|
||||
var child = this.firstElementChild;
|
||||
var childWidth = 0;
|
||||
var style;
|
||||
while(child) {
|
||||
style = window.getComputedStyle(child);
|
||||
childWidth += child.offsetWidth + parseInt(style.marginLeft, 10) +
|
||||
parseInt(style.marginRight, 10);
|
||||
child = child.nextElementSibling;
|
||||
}
|
||||
return childWidth;
|
||||
};
|
||||
|
||||
proto.fireEvent = function(event, detail) {
|
||||
var evtObject = new CustomEvent(event, {
|
||||
bubbles: false,
|
||||
detail: detail || this
|
||||
});
|
||||
this.dispatchEvent(evtObject);
|
||||
this.fire(event, detail);
|
||||
};
|
||||
|
||||
proto.handleEvent = function(evt) {
|
||||
switch(evt.type) {
|
||||
// Like System app, the transition is our state machine.
|
||||
case 'transitionend':
|
||||
// We only process 'background-color' because all states have this
|
||||
// change.
|
||||
if ((evt.propertyName !== 'background-color' &&
|
||||
evt.propertyName !== 'opacity' &&
|
||||
evt.propertyName !== 'width') ||
|
||||
evt.target !== this) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.classList.contains('opening')) {
|
||||
this.classList.add('opened');
|
||||
this.classList.remove('opening');
|
||||
// final state: opened
|
||||
this.fireEvent('opened');
|
||||
} else if (this.classList.contains('closing')) {
|
||||
this.classList.add('closed');
|
||||
this.classList.remove('closing');
|
||||
// final state: closed
|
||||
this.fireEvent('closed');
|
||||
}
|
||||
|
||||
// go back to initial state after hiding
|
||||
if (this.classList.contains('hiding')) {
|
||||
this.classList.remove('opened');
|
||||
this.classList.remove('opening');
|
||||
this.classList.remove('closing');
|
||||
this.classList.remove('flying');
|
||||
this.classList.remove('hiding');
|
||||
|
||||
this.init();
|
||||
this.fireEvent('hidden');
|
||||
}
|
||||
|
||||
break;
|
||||
case 'animationend':
|
||||
this.classList.remove('flying');
|
||||
this.open();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
proto.open = function() {
|
||||
// no operation during flying and hiding
|
||||
if (this.classList.contains('flying') ||
|
||||
this.classList.contains('hiding')) {
|
||||
return;
|
||||
}
|
||||
this.fireEvent('will-open');
|
||||
// If we get focus when we closing the group, we need to cancel the closing
|
||||
// state.
|
||||
this.classList.remove('closed');
|
||||
this.classList.remove('closing');
|
||||
this.classList.add('opening');
|
||||
// change to opening
|
||||
this.style.width = this.calculateChildWidth() + 'px';
|
||||
};
|
||||
|
||||
proto.close = function() {
|
||||
// no operation during flying and hiding
|
||||
if (this.classList.contains('flying') ||
|
||||
this.classList.contains('hiding')) {
|
||||
return;
|
||||
}
|
||||
this.fireEvent('will-close');
|
||||
// We may call close at mid state, we need to reset all of them and go to
|
||||
// closing state
|
||||
this.classList.remove('opened');
|
||||
this.classList.remove('opening');
|
||||
this.classList.add('closing');
|
||||
this.style.width = '0';
|
||||
};
|
||||
|
||||
proto.hide = function(callback) {
|
||||
this.classList.add('hiding');
|
||||
};
|
||||
|
||||
proto.flyOpen = function() {
|
||||
if (this.classList.contains('closed')) {
|
||||
this.classList.add('flying');
|
||||
}
|
||||
};
|
||||
|
||||
// Register and return the constructor
|
||||
return document.registerElement('smart-banner', { prototype: proto });
|
||||
})(window);
|
||||
135
gaia/tv_apps/smart-system/bower_components/smart-banner/style.css
vendored
Executable file
|
|
@ -0,0 +1,135 @@
|
|||
/**
|
||||
* Button state:
|
||||
* 1. normal (class name: [none])
|
||||
* 2. opening (class name: opening)
|
||||
* 3. opened (class name: opened)
|
||||
* 4. closing (class name: closing)
|
||||
* 5. closed (class name: closed)
|
||||
* 6. hiding (class name: hiding)
|
||||
* 8. flying (class name: flying)
|
||||
**/
|
||||
|
||||
/**
|
||||
* Normal:
|
||||
* open: closed -> opening -> opened
|
||||
* close: opened -> closing -> closed
|
||||
* hide: current state -> hiding -> closed
|
||||
* flyOpen: flying -> closed -> opening -> opened
|
||||
**/
|
||||
|
||||
smart-banner {
|
||||
outline: 0;
|
||||
display: inline-block;
|
||||
height: 7rem;
|
||||
/* Same as closed state */
|
||||
padding-left: 11rem;
|
||||
padding-right: 0;
|
||||
padding-top: 2rem;
|
||||
padding-bottom: 2rem;
|
||||
opacity: 1;
|
||||
/* we need to set border to none to remove the button border */
|
||||
border: none;
|
||||
/* the radius is still make button as round even if we don't have border */
|
||||
border-radius: 5.5rem;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 5.4rem auto;
|
||||
background-position: 2.8rem 50%;
|
||||
|
||||
word-break: keep-all;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
font-size: 2.8rem;
|
||||
line-height: 7rem;
|
||||
|
||||
transition-property: none;
|
||||
transition-timing-function: cubic-bezier(0.25, 0, 0, 1.0);
|
||||
transition-duration: 0s;
|
||||
}
|
||||
|
||||
smart-banner:before {
|
||||
content: " ";
|
||||
background-repeat: no-repeat;
|
||||
background-size: 5.4rem auto;
|
||||
background-position: center;
|
||||
width: 5.4rem;
|
||||
height: 7rem;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
left: 2.8rem;
|
||||
|
||||
transition-property: all;
|
||||
transition-timing-function: cubic-bezier(0.25, 0, 0, 1.0);
|
||||
transition-duration: 0.42s;
|
||||
}
|
||||
|
||||
smart-banner.closed:before {
|
||||
width: 11rem;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
smart-banner[data-icon]:before {
|
||||
width: 5.4rem;
|
||||
left: 4.4rem;
|
||||
font-size: 5.4rem;
|
||||
line-height: 11rem;
|
||||
text-align: center;
|
||||
color: #5f6368;
|
||||
white-space: normal;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
smart-banner[data-icon].closed:before {
|
||||
left: 2.8rem;
|
||||
}
|
||||
|
||||
smart-banner.closed {
|
||||
padding-left: 11rem;
|
||||
padding-right: 0;
|
||||
transition-duration: 0.5s;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
smart-banner.closed > * {
|
||||
display: none;
|
||||
}
|
||||
|
||||
smart-banner.opened {
|
||||
padding-right: 4.4rem;
|
||||
}
|
||||
|
||||
smart-banner.opening {
|
||||
transition-property: all;
|
||||
transition-duration: 0.72s;
|
||||
/* add the same padding-right of normal to make it run transition */
|
||||
padding-right: 4.4rem;
|
||||
}
|
||||
|
||||
smart-banner.opening:before {
|
||||
transition-duration: 0.72s;
|
||||
}
|
||||
|
||||
smart-banner.closing {
|
||||
transition-property: all;
|
||||
transition-duration: 0.5s;
|
||||
padding-left: 11rem;
|
||||
padding-right: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
smart-banner.hiding {
|
||||
opacity: 0;
|
||||
transition-property: opacity;
|
||||
transition-duration: 1s;
|
||||
}
|
||||
|
||||
smart-banner.flying {
|
||||
animation: flyAnimation 1s;
|
||||
}
|
||||
|
||||
@keyframes flyAnimation {
|
||||
from {transform: translateY(20rem);}
|
||||
to {transform: translateY(0);}
|
||||
}
|
||||
30
gaia/tv_apps/smart-system/bower_components/smart-bubbles/.bower.json
vendored
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"name": "smart-bubbles",
|
||||
"homepage": "https://github.com/smart-components/smart-bubbles",
|
||||
"main": "script.js",
|
||||
"license": "MPL",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"examples",
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"gulpfile.js",
|
||||
"package.json"
|
||||
],
|
||||
"devDependencies": {
|
||||
"smart-button": "smart-components/smart-button"
|
||||
},
|
||||
"_release": "5c08ef5d84",
|
||||
"_resolution": {
|
||||
"type": "branch",
|
||||
"branch": "master",
|
||||
"commit": "5c08ef5d84b0bd1d8252600cfc744aa7869e9f92"
|
||||
},
|
||||
"_source": "git://github.com/smart-components/smart-bubbles.git",
|
||||
"_target": "*",
|
||||
"_originalSource": "smart-components/smart-bubbles"
|
||||
}
|
||||
22
gaia/tv_apps/smart-system/bower_components/smart-bubbles/bower.json
vendored
Executable file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "smart-bubbles",
|
||||
"version": "0.0.0",
|
||||
"homepage": "https://github.com/smart-components/smart-bubbles",
|
||||
"main": "script.js",
|
||||
"license": "MPL",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"examples",
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"gulpfile.js",
|
||||
"package.json"
|
||||
],
|
||||
"devDependencies": {
|
||||
"smart-button": "smart-components/smart-button"
|
||||
}
|
||||
}
|
||||
83
gaia/tv_apps/smart-system/bower_components/smart-bubbles/script.js
vendored
Executable file
|
|
@ -0,0 +1,83 @@
|
|||
window.SmartBubbles = (function(win) {
|
||||
'use strict';
|
||||
|
||||
var DEFAULT_INTERVAL = 100;
|
||||
|
||||
// Extend from the HTMLElement prototype
|
||||
var proto = Object.create(HTMLElement.prototype);
|
||||
|
||||
proto.fireEvent = function(event, detail) {
|
||||
var evtObject = new CustomEvent(event, {
|
||||
bubbles: false,
|
||||
detail: detail || this
|
||||
});
|
||||
this.dispatchEvent(evtObject);
|
||||
};
|
||||
|
||||
proto.handleEvent = function(evt) {
|
||||
switch(evt.type) {
|
||||
case 'animationend':
|
||||
this.fireEvent('item-bubbled', ++this._finishedAnimationCount);
|
||||
if (this._targetAnimationCount === this._finishedAnimationCount) {
|
||||
this.playing = false;
|
||||
[].forEach.call(this._elements, function(elem) {
|
||||
elem.classList.remove('smart-bubble-playing');
|
||||
// XXX:Usually we use <smart-button> within container of
|
||||
// smart-bubbles. If we remove style of smart-bubble-playing,
|
||||
// Gecko would apply original style of smart-button instead and it
|
||||
// has transition property too. This results in another transition
|
||||
// happens on <smart-button> while we thought we'd just stop
|
||||
// animation. That is why we need to add 'smart-bubble-stopped' here
|
||||
// to prevent unexpected animation from happening.
|
||||
elem.classList.add('smart-bubble-stopped');
|
||||
// use getComputedStyle to force flushing re-style
|
||||
window.getComputedStyle(elem).animationName;
|
||||
elem.classList.remove('smart-bubble-stopped');
|
||||
window.getComputedStyle(elem).animationName;
|
||||
elem.style.animationDelay = '';
|
||||
}.bind(this));
|
||||
this._elements = null;
|
||||
this.fireEvent('all-items-bubbled');
|
||||
}
|
||||
evt.currentTarget.removeEventListener('animationend', this);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
proto.play = function(elements) {
|
||||
this.stopImmediately();
|
||||
this._elements = elements;
|
||||
this._finishedAnimationCount = 0;
|
||||
this._targetAnimationCount = 0;
|
||||
var interval = parseInt(this.getAttribute('interval'), 10) ||
|
||||
DEFAULT_INTERVAL;
|
||||
[].forEach.call(elements, function(elem) {
|
||||
this._targetAnimationCount++;
|
||||
var delay = this._targetAnimationCount * interval / 1000;
|
||||
elem.classList.add('smart-bubble-playing');
|
||||
elem.style.animationDelay = delay + 's';
|
||||
elem.addEventListener('animationend', this);
|
||||
}.bind(this));
|
||||
this.playing = true;
|
||||
};
|
||||
|
||||
proto.stopImmediately = function() {
|
||||
if (!this.playing || !this._elements) {
|
||||
return;
|
||||
}
|
||||
[].forEach.call(this._elements, function(elem) {
|
||||
elem.classList.remove('smart-bubble-playing');
|
||||
elem.classList.add('smart-bubble-stopped');
|
||||
elem.removeEventListener('animationend', this);
|
||||
// use getComputedStyle to force flushing re-style
|
||||
window.getComputedStyle(elem).animationName;
|
||||
elem.classList.remove('smart-bubble-stopped');
|
||||
}.bind(this));
|
||||
this._elements = null;
|
||||
this.playing = false;
|
||||
this.fireEvent('all-items-stopped');
|
||||
};
|
||||
|
||||
// Register and return the constructor
|
||||
return document.registerElement('smart-bubbles', { prototype: proto });
|
||||
})(window);
|
||||
1
gaia/tv_apps/smart-system/bower_components/smart-bubbles/script.min.js
vendored
Executable file
|
|
@ -0,0 +1 @@
|
|||
window.SmartBubbles=function(t){"use strict";var e=100,i=Object.create(HTMLElement.prototype);return i.fireEvent=function(t,e){var i=new CustomEvent(t,{bubbles:!1,detail:e||this});this.dispatchEvent(i)},i.handleEvent=function(t){switch(t.type){case"animationend":this.fireEvent("item-bubbled",++this._finishedAnimationCount),this._targetAnimationCount===this._finishedAnimationCount&&(this.playing=!1,[].forEach.call(this._elements,function(t){t.classList.remove("smart-bubble-playing"),t.classList.add("smart-bubble-stopped"),window.getComputedStyle(t).animationName,t.classList.remove("smart-bubble-stopped"),window.getComputedStyle(t).animationName,t.style.animationDelay=""}.bind(this)),this._elements=null,this.fireEvent("all-items-bubbled")),t.currentTarget.removeEventListener("animationend",this)}},i.play=function(t){this.stopImmediately(),this._elements=t,this._finishedAnimationCount=0,this._targetAnimationCount=0;var i=parseInt(this.getAttribute("interval"),10)||e;[].forEach.call(t,function(t){this._targetAnimationCount++;var e=this._targetAnimationCount*i/1e3;t.classList.add("smart-bubble-playing"),t.style.animationDelay=e+"s",t.addEventListener("animationend",this)}.bind(this)),this.playing=!0},i.stopImmediately=function(){this.playing&&this._elements&&([].forEach.call(this._elements,function(t){t.classList.remove("smart-bubble-playing"),t.classList.add("smart-bubble-stopped"),t.removeEventListener("animationend",this),window.getComputedStyle(t).animationName,t.classList.remove("smart-bubble-stopped")}.bind(this)),this._elements=null,this.playing=!1,this.fireEvent("all-items-stopped"))},document.registerElement("smart-bubbles",{prototype:i})}(window);
|
||||
83
gaia/tv_apps/smart-system/bower_components/smart-bubbles/src/script.js
vendored
Executable file
|
|
@ -0,0 +1,83 @@
|
|||
window.SmartBubbles = (function(win) {
|
||||
'use strict';
|
||||
|
||||
var DEFAULT_INTERVAL = 100;
|
||||
|
||||
// Extend from the HTMLElement prototype
|
||||
var proto = Object.create(HTMLElement.prototype);
|
||||
|
||||
proto.fireEvent = function(event, detail) {
|
||||
var evtObject = new CustomEvent(event, {
|
||||
bubbles: false,
|
||||
detail: detail || this
|
||||
});
|
||||
this.dispatchEvent(evtObject);
|
||||
};
|
||||
|
||||
proto.handleEvent = function(evt) {
|
||||
switch(evt.type) {
|
||||
case 'animationend':
|
||||
this.fireEvent('item-bubbled', ++this._finishedAnimationCount);
|
||||
if (this._targetAnimationCount === this._finishedAnimationCount) {
|
||||
this.playing = false;
|
||||
[].forEach.call(this._elements, function(elem) {
|
||||
elem.classList.remove('smart-bubble-playing');
|
||||
// XXX:Usually we use <smart-button> within container of
|
||||
// smart-bubbles. If we remove style of smart-bubble-playing,
|
||||
// Gecko would apply original style of smart-button instead and it
|
||||
// has transition property too. This results in another transition
|
||||
// happens on <smart-button> while we thought we'd just stop
|
||||
// animation. That is why we need to add 'smart-bubble-stopped' here
|
||||
// to prevent unexpected animation from happening.
|
||||
elem.classList.add('smart-bubble-stopped');
|
||||
// use getComputedStyle to force flushing re-style
|
||||
window.getComputedStyle(elem).animationName;
|
||||
elem.classList.remove('smart-bubble-stopped');
|
||||
window.getComputedStyle(elem).animationName;
|
||||
elem.style.animationDelay = '';
|
||||
}.bind(this));
|
||||
this._elements = null;
|
||||
this.fireEvent('all-items-bubbled');
|
||||
}
|
||||
evt.currentTarget.removeEventListener('animationend', this);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
proto.play = function(elements) {
|
||||
this.stopImmediately();
|
||||
this._elements = elements;
|
||||
this._finishedAnimationCount = 0;
|
||||
this._targetAnimationCount = 0;
|
||||
var interval = parseInt(this.getAttribute('interval'), 10) ||
|
||||
DEFAULT_INTERVAL;
|
||||
[].forEach.call(elements, function(elem) {
|
||||
this._targetAnimationCount++;
|
||||
var delay = this._targetAnimationCount * interval / 1000;
|
||||
elem.classList.add('smart-bubble-playing');
|
||||
elem.style.animationDelay = delay + 's';
|
||||
elem.addEventListener('animationend', this);
|
||||
}.bind(this));
|
||||
this.playing = true;
|
||||
};
|
||||
|
||||
proto.stopImmediately = function() {
|
||||
if (!this.playing || !this._elements) {
|
||||
return;
|
||||
}
|
||||
[].forEach.call(this._elements, function(elem) {
|
||||
elem.classList.remove('smart-bubble-playing');
|
||||
elem.classList.add('smart-bubble-stopped');
|
||||
elem.removeEventListener('animationend', this);
|
||||
// use getComputedStyle to force flushing re-style
|
||||
window.getComputedStyle(elem).animationName;
|
||||
elem.classList.remove('smart-bubble-stopped');
|
||||
}.bind(this));
|
||||
this._elements = null;
|
||||
this.playing = false;
|
||||
this.fireEvent('all-items-stopped');
|
||||
};
|
||||
|
||||
// Register and return the constructor
|
||||
return document.registerElement('smart-bubbles', { prototype: proto });
|
||||
})(window);
|
||||
26
gaia/tv_apps/smart-system/bower_components/smart-bubbles/src/style.css
vendored
Executable file
|
|
@ -0,0 +1,26 @@
|
|||
*[smart-bubbles="true"] .smart-bubble-playing {
|
||||
transition-property: none;
|
||||
transform: none;
|
||||
animation: bubble-animation 0.5s cubic-bezier(0.25, 0, 0, 1) both;
|
||||
}
|
||||
|
||||
*[smart-bubbles="true"][smart-bubbles-direction="down"] .smart-bubble-playing {
|
||||
animation: sinking-animation 0.5s cubic-bezier(0.25, 0, 0, 1) both;
|
||||
}
|
||||
|
||||
*[smart-bubbles="true"] .smart-bubble-stopped {
|
||||
transition-property: none;
|
||||
transform: none;
|
||||
animation: none;
|
||||
}
|
||||
|
||||
@keyframes bubble-animation {
|
||||
0% {opacity: 0; transform: translateY(8rem);}
|
||||
50% {opacity: 1;}
|
||||
100% {transform: translateY(0);}
|
||||
}
|
||||
|
||||
@keyframes sinking-animation {
|
||||
0% {opacity: 1; transform: translateY(0);}
|
||||
100% {opacity: 0; transform: translateY(8rem);}
|
||||
}
|
||||
26
gaia/tv_apps/smart-system/bower_components/smart-bubbles/style.css
vendored
Executable file
|
|
@ -0,0 +1,26 @@
|
|||
*[smart-bubbles="true"] .smart-bubble-playing {
|
||||
transition-property: none;
|
||||
transform: none;
|
||||
animation: bubble-animation 0.5s cubic-bezier(0.25, 0, 0, 1) both;
|
||||
}
|
||||
|
||||
*[smart-bubbles="true"][smart-bubbles-direction="down"] .smart-bubble-playing {
|
||||
animation: sinking-animation 0.5s cubic-bezier(0.25, 0, 0, 1) both;
|
||||
}
|
||||
|
||||
*[smart-bubbles="true"] .smart-bubble-stopped {
|
||||
transition-property: none;
|
||||
transform: none;
|
||||
animation: none;
|
||||
}
|
||||
|
||||
@keyframes bubble-animation {
|
||||
0% {opacity: 0; transform: translateY(8rem);}
|
||||
50% {opacity: 1;}
|
||||
100% {transform: translateY(0);}
|
||||
}
|
||||
|
||||
@keyframes sinking-animation {
|
||||
0% {opacity: 1; transform: translateY(0);}
|
||||
100% {opacity: 0; transform: translateY(8rem);}
|
||||
}
|
||||
26
gaia/tv_apps/smart-system/bower_components/smart-button/.bower.json
vendored
Executable file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "smart-button",
|
||||
"version": "0.0.2",
|
||||
"homepage": "https://github.com/gaia-components/smart-button",
|
||||
"main": "script.js",
|
||||
"license": "MPL",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"examples",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"_release": "0.0.2",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v0.0.2",
|
||||
"commit": "04fdae935e2f2ba99d1c4f3e156d17f12eb2d142"
|
||||
},
|
||||
"_source": "git://github.com/gaia-components/smart-button.git",
|
||||
"_target": "~0.0.2",
|
||||
"_originalSource": "gaia-components/smart-button"
|
||||
}
|
||||
17
gaia/tv_apps/smart-system/bower_components/smart-button/bower.json
vendored
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "smart-button",
|
||||
"version": "0.0.2",
|
||||
"homepage": "https://github.com/gaia-components/smart-button",
|
||||
"main": "script.js",
|
||||
"license": "MPL",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"examples",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
]
|
||||
}
|
||||
62
gaia/tv_apps/smart-system/bower_components/smart-button/script.js
vendored
Executable file
|
|
@ -0,0 +1,62 @@
|
|||
'use strict';
|
||||
/* global KeyEvent */
|
||||
|
||||
window.SmartButton = (function(win) {
|
||||
// Extend from the HTMLButtonElement prototype
|
||||
var proto = Object.create(HTMLButtonElement.prototype);
|
||||
|
||||
proto.createdCallback = function() {
|
||||
this.addEventListener('mousedown', this);
|
||||
this.addEventListener('mouseup', this);
|
||||
this.addEventListener('touchstart', this);
|
||||
this.addEventListener('touchend', this);
|
||||
this.addEventListener('keydown', this);
|
||||
this.addEventListener('keyup', this);
|
||||
this.addEventListener('focus', this);
|
||||
this.addEventListener('blur', this);
|
||||
this.addEventListener('transitionend', this);
|
||||
this.tabIndex = 0;
|
||||
};
|
||||
|
||||
proto.handleEvent = function(evt) {
|
||||
switch(evt.type) {
|
||||
case 'mousedown':
|
||||
case 'touchstart':
|
||||
this.classList.add('pressed');
|
||||
break;
|
||||
case 'keydown':
|
||||
if (evt.keyCode === KeyEvent.DOM_VK_RETURN) {
|
||||
this.classList.add('pressed');
|
||||
}
|
||||
break;
|
||||
case 'mouseup':
|
||||
case 'touchend':
|
||||
this.classList.remove('pressed');
|
||||
this.classList.add('released');
|
||||
break;
|
||||
case 'keyup':
|
||||
if (evt.keyCode === KeyEvent.DOM_VK_RETURN) {
|
||||
this.classList.remove('pressed');
|
||||
this.classList.add('released');
|
||||
this.click();
|
||||
}
|
||||
break;
|
||||
case 'transitionend':
|
||||
if (this.classList.contains('released')) {
|
||||
this.classList.remove('released');
|
||||
}
|
||||
break;
|
||||
case 'focus':
|
||||
this.classList.add('focused');
|
||||
break;
|
||||
case 'blur':
|
||||
this.classList.remove('pressed');
|
||||
this.classList.remove('released');
|
||||
this.classList.remove('focused');
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
// Register and return the constructor
|
||||
return document.registerElement('smart-button', { prototype: proto });
|
||||
})(window);
|
||||
497
gaia/tv_apps/smart-system/bower_components/smart-button/style.css
vendored
Executable file
|
|
@ -0,0 +1,497 @@
|
|||
/**
|
||||
* Button state:
|
||||
* 1. normal
|
||||
* In this state, we don't add any class.
|
||||
* 2. focused (class name: focused)
|
||||
* The focused state may be triggered by keyboard or touch/mouse.
|
||||
* 3. pressed (class name: pressed)
|
||||
* This state is triggered by user pressed confirm button, mousedown,
|
||||
* and touchdown.
|
||||
* 4. released (class name: released)
|
||||
* This state is only for changing duration of transition. We enter this
|
||||
* state when user release the button. After transitionend, we goes back
|
||||
* to focused state.
|
||||
**/
|
||||
smart-button {
|
||||
font-family: "Fira Sans";
|
||||
display: inline-block;
|
||||
width: 8rem;
|
||||
height: 8rem;
|
||||
/* we need to set border to none to remove the button border */
|
||||
border: none;
|
||||
/* the radius is still make button as round even if we don't have border */
|
||||
border-radius: 50%;
|
||||
/* 50% is not a real circle because of rounding, we have to use a value larger
|
||||
than 50% */
|
||||
-moz-outline-radius: 60%;
|
||||
outline: transparent 0 solid;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
background-repeat: no-repeat;
|
||||
background-size: 5.4rem auto;
|
||||
background-position: center center;
|
||||
font-style: italic;
|
||||
|
||||
transition-property: all;
|
||||
transition-timing-function: cubic-bezier(0.25, 0, 0, 1.0);
|
||||
transition-duration: 0.42s;
|
||||
}
|
||||
|
||||
smart-button.focused {
|
||||
outline: 0;
|
||||
background-color: #ffffff;
|
||||
transform: scale(1.2);
|
||||
transition-duration: 0.42s;
|
||||
}
|
||||
|
||||
smart-button.released {
|
||||
transition-duration: 0.16s;
|
||||
}
|
||||
|
||||
smart-button.toggled {
|
||||
background-color: #00caf2;
|
||||
}
|
||||
|
||||
smart-button.pressed {
|
||||
background-color: #00caf2;
|
||||
transform: scale(0.8);
|
||||
transition-duration: 0.06s;
|
||||
}
|
||||
|
||||
smart-button.disabled {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
smart-button[data-icon]:before {
|
||||
width: 5.4rem;
|
||||
left: 1.3rem;
|
||||
font-size: 5.4rem;
|
||||
line-height: 8rem;
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
white-space: normal;
|
||||
top: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
smart-button[data-icon].focused:before {
|
||||
color: #2d2d2d;
|
||||
}
|
||||
|
||||
smart-button[data-icon].toggled:before {
|
||||
color: #00caf2;
|
||||
}
|
||||
|
||||
smart-button[data-icon].pressed:before {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
smart-button[data-icon].disabled:before {
|
||||
color: #ffffff;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple Text Button
|
||||
**/
|
||||
smart-button[type="simple-text"] {
|
||||
color: #dddddd;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
|
||||
width: auto;
|
||||
height: 8rem;
|
||||
|
||||
line-height: 8rem;
|
||||
font-size: 2.4rem;
|
||||
-moz-user-select: none;
|
||||
padding-left: 2.6rem;
|
||||
padding-right: 2.6rem;
|
||||
}
|
||||
|
||||
smart-button[type="simple-text"].focused {
|
||||
color: #ffffff;
|
||||
background: none;
|
||||
}
|
||||
|
||||
smart-button[type="simple-text"].toggled {
|
||||
color: #00caf2;
|
||||
}
|
||||
|
||||
smart-button[type="simple-text"].pressed {
|
||||
color: #00caf2;
|
||||
background: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* End of Simple Text Button
|
||||
**/
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* underlined text button
|
||||
*/
|
||||
smart-button[type="underlined-text"] {
|
||||
position: relative;
|
||||
color: white;
|
||||
font-size: 2.4rem;
|
||||
padding-left: 3.8rem;
|
||||
padding-right: 3.8rem;
|
||||
border-radius: 4rem;
|
||||
width: auto;
|
||||
line-height: 8rem;
|
||||
margin-right: 1.4rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
smart-button[type="underlined-text"]:after {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
content: ' ';
|
||||
border-bottom: 0.6rem #61acd7 solid;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
smart-button[type="underlined-text"].focused {
|
||||
transform: scale(1.2);
|
||||
color: #2d2d2d;
|
||||
}
|
||||
|
||||
smart-button[type="underlined-text"].pressed {
|
||||
transform: scale(1);
|
||||
color: white;
|
||||
background-color: #00caf2;
|
||||
}
|
||||
|
||||
/**
|
||||
* End of underlined text button
|
||||
*/
|
||||
|
||||
/**
|
||||
* App Icons
|
||||
**/
|
||||
smart-button[type="app-button"] {
|
||||
/* To use 33.4rem instead of to use 33.6rem is to work around the anti-alias
|
||||
pixel. So, the circle is filled by the background almost totally. */
|
||||
width: 33.4rem;
|
||||
height: 33.4rem;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
smart-button[type="app-button"].focused {
|
||||
transform: scale(1.4);
|
||||
/* To use outline will not change the size of button and make the whole button
|
||||
thrilling. */
|
||||
outline: 0.8rem solid #ffffff;
|
||||
}
|
||||
|
||||
smart-button[type="app-button"].pressed {
|
||||
/* While scaling, we may have a gap between outline and icon which may be
|
||||
anti-alias pixel. So, we use scale(1.22) to prevent such kind of case */
|
||||
transform: scale(1.22);
|
||||
/* To use outline will not change the size of button and make the whole button
|
||||
thrilling. */
|
||||
outline: 1.0rem solid #ffffff;
|
||||
}
|
||||
|
||||
smart-button[type="app-button"].fullsized {
|
||||
background-size: 33.6rem auto;
|
||||
}
|
||||
|
||||
smart-button[type="app-button"].fitted {
|
||||
background-size: 22rem auto;
|
||||
}
|
||||
|
||||
/** we use :after as label */
|
||||
smart-button[type="app-button"]:after {
|
||||
content: attr(label);
|
||||
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: auto;
|
||||
right: auto;
|
||||
|
||||
width: 33.6rem;
|
||||
height: 8rem;
|
||||
|
||||
padding-top: 1rem;
|
||||
|
||||
line-height: 3rem;
|
||||
text-align: center;
|
||||
|
||||
font-size: 2.8rem;
|
||||
font-style: italic;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/**
|
||||
* text backgorund color
|
||||
**/
|
||||
smart-button[type="app-button"][app-type="deck"]:after {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
smart-button[type="app-button"][app-type="tv"]:after {
|
||||
background-color: #d90c83;
|
||||
}
|
||||
|
||||
smart-button[type="app-button"][app-type="app"]:after {
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
smart-button[type="app-button"][app-type="device"]:after {
|
||||
background-color: #4a82ea;
|
||||
}
|
||||
|
||||
smart-button[type="app-button"][app-type="folder"] {
|
||||
background-color: #595959;
|
||||
}
|
||||
|
||||
smart-button[type="app-button"]:before {
|
||||
left: 0;
|
||||
line-height: 30rem;
|
||||
font-size: 16rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
smart-button[type="app-button"][app-type="folder"]:before {
|
||||
color: #CCCCCC;
|
||||
}
|
||||
|
||||
/**
|
||||
* End of App Icons
|
||||
**/
|
||||
|
||||
/**
|
||||
* text and icon button
|
||||
*/
|
||||
smart-button[type="text-and-icon"] {
|
||||
position: relative;
|
||||
color: white;
|
||||
font-size: 2.4rem;
|
||||
padding-left: 4.6rem;
|
||||
padding-right: 4.6rem;
|
||||
border-radius: 4rem;
|
||||
width: auto;
|
||||
line-height: 8rem;
|
||||
margin-right: 1.4rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
smart-button[type="text-and-icon"][data-icon] {
|
||||
padding-left: 9.6rem;
|
||||
}
|
||||
|
||||
smart-button[type="text-and-icon"][data-icon]:before {
|
||||
left: 3.8rem;
|
||||
}
|
||||
|
||||
smart-button[type="text-and-icon"].focused {
|
||||
transform: scale(1.2);
|
||||
color: #2d2d2d;
|
||||
}
|
||||
|
||||
smart-button[type="text-and-icon"].pressed {
|
||||
transform: scale(1);
|
||||
color: white;
|
||||
background-color: #00caf2;
|
||||
}
|
||||
|
||||
/**
|
||||
* End of text and icon button
|
||||
*/
|
||||
|
||||
/**
|
||||
* banner button
|
||||
**/
|
||||
smart-button[type="banner-button"] {
|
||||
position: relative;
|
||||
|
||||
color: #ffffff;
|
||||
font-size: 2rem;
|
||||
|
||||
width: 36rem;
|
||||
height: 20rem;
|
||||
overflow: hidden;
|
||||
|
||||
border-radius: 0;
|
||||
border: 0 solid #ffffff;
|
||||
|
||||
background-color: #5f5f5f;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 120%;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
transition-property: all;
|
||||
transition-timing-function: cubic-bezier(0.25, 0, 0.5, 1.0);
|
||||
transition-duration: 0.1s;
|
||||
}
|
||||
|
||||
smart-button[type="banner-button"][label]:before {
|
||||
content: attr(label);
|
||||
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
|
||||
height: 5rem;
|
||||
|
||||
line-height: 5rem;
|
||||
text-align: center;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
smart-button[type="banner-button"]:after {
|
||||
content: " ";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
transition-property: all;
|
||||
transition-timing-function: cubic-bezier(0.25, 0, 0, 1.0);
|
||||
transition-duration: 0.06s;
|
||||
background-color: rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
smart-button[type="banner-button"].focused {
|
||||
transform: none;
|
||||
border-width: 1.2rem;
|
||||
background-size: 100%;
|
||||
transition-duration: 0.24s;
|
||||
}
|
||||
|
||||
smart-button[type="banner-button"].pressed {
|
||||
transform: scale(0.945, 0.9);
|
||||
}
|
||||
|
||||
smart-button[type="banner-button"].pressed:after {
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
/**
|
||||
* banner button
|
||||
**/
|
||||
|
||||
/**
|
||||
* circle text button
|
||||
*/
|
||||
smart-button[type="circle-text"] {
|
||||
width: 21rem;
|
||||
height: 21rem;
|
||||
color: white;
|
||||
background-color: #5f6368;
|
||||
line-height: 21rem;
|
||||
padding: 2.5rem 2rem 1.5rem 2rem;
|
||||
text-align: center;
|
||||
font-size: 3.2rem;
|
||||
font-weight: bolder;
|
||||
overflow: hidden;
|
||||
transition-property: transform, color, background-color;
|
||||
}
|
||||
|
||||
smart-button[type="circle-text"].focused {
|
||||
transform: scale(1.2);
|
||||
color: #2d2d2d;
|
||||
background-color: white;
|
||||
transition-property: transform, color, background-color;
|
||||
}
|
||||
|
||||
smart-button[type="circle-text"].pressed {
|
||||
transform: scale(1);
|
||||
color: white;
|
||||
background-color: #00caf2;
|
||||
transition-property: transform, color, background-color;
|
||||
}
|
||||
|
||||
smart-button[type="circle-text"].primary {
|
||||
background-color: #00caf2;
|
||||
transition-property: transform, color, background-color;
|
||||
}
|
||||
|
||||
smart-button[type="circle-text"].primary.focused {
|
||||
color: #ffffff;
|
||||
background-color: #00caf2;
|
||||
transition-property: transform, color, background-color;
|
||||
}
|
||||
|
||||
smart-button[type="circle-text"].primary.pressed {
|
||||
background-color: #0097b5;
|
||||
transition-property: transform, color, background-color;
|
||||
}
|
||||
|
||||
smart-button[type="circle-text"].danger {
|
||||
background-color: #e63d2f;
|
||||
transition-property: transform, color, background-color;
|
||||
}
|
||||
|
||||
smart-button[type="circle-text"].danger.focused {
|
||||
color: #ffffff;
|
||||
background-color: #e63d2f;
|
||||
transition-property: transform, color, background-color;
|
||||
}
|
||||
|
||||
smart-button[type="circle-text"].danger.pressed {
|
||||
background-color: #ac2e23;
|
||||
transition-property: transform, color, background-color;
|
||||
}
|
||||
|
||||
/**
|
||||
* End of circle text button
|
||||
*/
|
||||
|
||||
/**
|
||||
* rounded-corner text button
|
||||
*/
|
||||
smart-button[type="rounded-corner-text"] {
|
||||
-moz-user-select: none;
|
||||
width: auto;
|
||||
height: 8rem;
|
||||
border-radius: 4rem;
|
||||
background-color: #5f6368;
|
||||
padding: 0 3.8rem;
|
||||
line-height: 8rem;
|
||||
color: white;
|
||||
text-align: center;
|
||||
font-size: 2.4rem;
|
||||
overflow: hidden;
|
||||
transition-property: transform, color, background-color;
|
||||
}
|
||||
|
||||
smart-button[type="rounded-corner-text"].focused,
|
||||
smart-button[type="rounded-corner-text"]:hover {
|
||||
transform: scale(1.2);
|
||||
color: #2d2d2d;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
smart-button[type="rounded-corner-text"].pressed {
|
||||
transform: scale(1);
|
||||
/*color: white;*/
|
||||
background-color: #00caf2;
|
||||
}
|
||||
|
||||
smart-button[type="rounded-corner-text"].primary {
|
||||
background-color: #00caf2;
|
||||
}
|
||||
|
||||
smart-button[type="rounded-corner-text"].primary:hover,
|
||||
smart-button[type="rounded-corner-text"].primary.focused {
|
||||
color: #ffffff;
|
||||
background-color: #00caf2;
|
||||
}
|
||||
|
||||
smart-button[type="rounded-corner-text"].primary.pressed {
|
||||
background-color: #0097b5;
|
||||
}
|
||||
|
||||
/**
|
||||
* End of rounded-corner text button
|
||||
*/
|
||||
25
gaia/tv_apps/smart-system/bower_components/smart-dialog/.bower.json
vendored
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"name": "smart-dialog",
|
||||
"homepage": "https://github.com/smart-components/smart-dialog",
|
||||
"main": "script.js",
|
||||
"license": "MPL",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"examples",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"_release": "8910d99b8f",
|
||||
"_resolution": {
|
||||
"type": "branch",
|
||||
"branch": "master",
|
||||
"commit": "8910d99b8fb7c624b32ac494fea63c0898e886df"
|
||||
},
|
||||
"_source": "git://github.com/smart-components/smart-dialog.git",
|
||||
"_target": "*",
|
||||
"_originalSource": "smart-components/smart-dialog"
|
||||
}
|
||||
17
gaia/tv_apps/smart-system/bower_components/smart-dialog/bower.json
vendored
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"name": "smart-dialog",
|
||||
"version": "0.0.0",
|
||||
"homepage": "https://github.com/smart-components/smart-dialog",
|
||||
"main": "script.js",
|
||||
"license": "MPL",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"examples",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
]
|
||||
}
|
||||
123
gaia/tv_apps/smart-system/bower_components/smart-dialog/script.js
vendored
Executable file
|
|
@ -0,0 +1,123 @@
|
|||
/**
|
||||
* Smart-Dialog
|
||||
* - Event:
|
||||
* 1. will-open
|
||||
* 2. opened
|
||||
* 3. will-close
|
||||
* 4. closed
|
||||
* - API:
|
||||
* 1. open - open a smart-dialog: closed -> opening -> opened
|
||||
* 2. close - close a smart-dialog: opened -> closing -> closed
|
||||
* - Attribute:
|
||||
* 1. title - title of the smart-dialog
|
||||
* 2. esc-close - close dialog when ESC key is clicked (default true).
|
||||
* Default scale: 1920x1080 (2*sqrt(960*960 + 1080*1080) / 20 = 144.5)
|
||||
*/
|
||||
|
||||
window.SmartDialog = (function(win) {
|
||||
'use strict';
|
||||
|
||||
// The value of openDuration and closeDuration should be the same as
|
||||
// transitionDuration in opening and closing css class.
|
||||
const openDuration = 1000;
|
||||
const closeDuration = 650;
|
||||
|
||||
// Extend from the HTMLElement prototype
|
||||
var proto = Object.create(HTMLElement.prototype);
|
||||
|
||||
proto.createdCallback = function sd_createdCallback() {
|
||||
this.addEventListener('transitionend', this);
|
||||
document.addEventListener('keyup', this);
|
||||
this.transitionTimer = null;
|
||||
this.classList.add('closed');
|
||||
if (this.hasAttribute('background-color')) {
|
||||
this.style.backgroundColor = this.getAttribute('background-color');
|
||||
}
|
||||
};
|
||||
|
||||
proto._onOpened = function sd_onOpened() {
|
||||
this.classList.remove('opening');
|
||||
this.classList.add('opened');
|
||||
this.fireEvent('opened');
|
||||
};
|
||||
|
||||
proto._onClosed = function sd_onClosed() {
|
||||
this.classList.remove('closing');
|
||||
this.classList.add('closed');
|
||||
this.fireEvent('closed');
|
||||
};
|
||||
|
||||
proto.open = function sd_open() {
|
||||
if (this.classList.contains('opened') ||
|
||||
this.classList.contains('opening')) {
|
||||
return;
|
||||
}
|
||||
this.fireEvent('will-open');
|
||||
this.classList.remove('closing');
|
||||
this.classList.remove('closed');
|
||||
this.classList.add('opening');
|
||||
// handle the case when transitionend event is not fired
|
||||
clearTimeout(this.transitionTimer);
|
||||
this.transitionTimer = setTimeout(function() {
|
||||
this._onOpened();
|
||||
this.transitionTimer = null;
|
||||
}.bind(this), openDuration);
|
||||
};
|
||||
|
||||
proto.close = function sd_close() {
|
||||
if (this.classList.contains('closed') ||
|
||||
this.classList.contains('closing')) {
|
||||
return;
|
||||
}
|
||||
this.fireEvent('will-close');
|
||||
this.classList.remove('opening');
|
||||
this.classList.remove('opened');
|
||||
this.classList.add('closing');
|
||||
// handle the case when transitionend event is not fired
|
||||
clearTimeout(this.transitionTimer);
|
||||
this.transitionTimer = setTimeout(function() {
|
||||
this._onClosed();
|
||||
this.transitionTimer = null;
|
||||
}.bind(this), closeDuration);
|
||||
};
|
||||
|
||||
proto.handleEvent = function sd_handleEvent(evt) {
|
||||
|
||||
switch(evt.type) {
|
||||
case 'transitionend':
|
||||
if (evt.target === this && this.transitionTimer) {
|
||||
if (this.classList.contains('opening')) {
|
||||
this._onOpened();
|
||||
} else if (this.classList.contains('closing')) {
|
||||
this._onClosed();
|
||||
}
|
||||
clearTimeout(this.transitionTimer);
|
||||
this.transitionTimer = null;
|
||||
}
|
||||
break;
|
||||
case 'keyup':
|
||||
// close dialog when Back is triggered
|
||||
if (this.getAttribute('esc-close') != 'false' &&
|
||||
(evt.keyCode === window.KeyEvent.DOM_VK_BACK_SPACE ||
|
||||
evt.key === 'Backspace') &&
|
||||
this.classList.contains('opened')) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
this.close();
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
proto.fireEvent = function sd_fireEvent(event, detail) {
|
||||
var evtObject = new CustomEvent(event, {
|
||||
bubbles: false,
|
||||
detail: detail || this
|
||||
});
|
||||
this.dispatchEvent(evtObject);
|
||||
};
|
||||
|
||||
// Register and return the constructor
|
||||
return document.registerElement('smart-dialog', { prototype: proto });
|
||||
})(window);
|
||||
103
gaia/tv_apps/smart-system/bower_components/smart-dialog/style.css
vendored
Executable file
|
|
@ -0,0 +1,103 @@
|
|||
/**
|
||||
* Smart-Dialog
|
||||
* - State:
|
||||
* 1. closed
|
||||
* 2. opening
|
||||
* 3. opened - content visible
|
||||
* 4. closing
|
||||
* Default scale: 1920x1080 (2*sqrt(960*960 + 1080*1080) / 20 = 144.5)
|
||||
*/
|
||||
|
||||
smart-dialog {
|
||||
position: fixed;
|
||||
z-index: 2147483647;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
smart-dialog:before {
|
||||
content: attr(title);
|
||||
display: block;
|
||||
margin-top: 5rem;
|
||||
margin-left: 7.6rem;
|
||||
font-family: "Fira Sans";
|
||||
font-size: 5.4rem;
|
||||
font-style: italic;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
smart-dialog:after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: fixed;
|
||||
height: 2rem;
|
||||
width: 2rem;
|
||||
top: calc(100% - 1rem);
|
||||
left: calc(50% - 1rem);
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
transition: none;
|
||||
transform: scale(20);
|
||||
}
|
||||
|
||||
smart-dialog.closed > *,
|
||||
smart-dialog.closing > *,
|
||||
smart-dialog.opening > *,
|
||||
smart-dialog.closed:before,
|
||||
smart-dialog.closing:before,
|
||||
smart-dialog.opening:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
smart-dialog.closed {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
smart-dialog.opened {
|
||||
background-color: rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
|
||||
smart-dialog.opened:after {
|
||||
visibility: hidden;
|
||||
border-radius: 50%;
|
||||
transform: scale(144.5);
|
||||
}
|
||||
|
||||
smart-dialog.opening:after {
|
||||
border-radius: 50%;
|
||||
transform: scale(144.5);
|
||||
/* transitionDuration should be the same as openDuration variable in script */
|
||||
transition: transform 1s cubic-bezier(0, 0, 0 ,1);
|
||||
}
|
||||
|
||||
smart-dialog.closing:after {
|
||||
border-radius: 50%;
|
||||
transform: scale(20);
|
||||
/* transitionDuration should be the same as closeDuration variable in script */
|
||||
transition: transform 0.65s cubic-bezier(0.6, 0, 1 ,1);
|
||||
}
|
||||
|
||||
/* 1280x800 for flatfish */
|
||||
@media (width: 1280px) and (height: 800px) {
|
||||
smart-dialog:after,
|
||||
smart-dialog.closing:after {
|
||||
transform: scale(7.5);
|
||||
}
|
||||
|
||||
smart-dialog.opening:after {
|
||||
transform: scale(102.5);
|
||||
}
|
||||
}
|
||||
|
||||
/* 800x1280 for flatfish vertical */
|
||||
@media (width: 800px) and (height: 1280px) {
|
||||
smart-dialog:after,
|
||||
smart-dialog.closing:after {
|
||||
transform: scale(8.3);
|
||||
}
|
||||
|
||||
smart-dialog.opening:after {
|
||||
transform: scale(134);
|
||||
}
|
||||
}
|
||||
30
gaia/tv_apps/smart-system/bower_components/smart-icons/.bower.json
vendored
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"name": "smart-icons",
|
||||
"version": "0.0.3",
|
||||
"homepage": "https://github.com/smart-components/smart-icons",
|
||||
"description": "Icon font for smart-screen",
|
||||
"license": "MPL",
|
||||
"private": true,
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"templates",
|
||||
"images",
|
||||
"test",
|
||||
"tests",
|
||||
"index.html",
|
||||
"Gruntfile.js",
|
||||
"README.md",
|
||||
"package.json"
|
||||
],
|
||||
"_release": "0.0.3",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v0.0.3",
|
||||
"commit": "0c335ac23f484069b4fafa7b7e5afd9f3c08d66e"
|
||||
},
|
||||
"_source": "git://github.com/smart-components/smart-icons.git",
|
||||
"_target": "*",
|
||||
"_originalSource": "smart-components/smart-icons"
|
||||
}
|
||||
21
gaia/tv_apps/smart-system/bower_components/smart-icons/bower.json
vendored
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "smart-icons",
|
||||
"version": "0.0.3",
|
||||
"homepage": "https://github.com/smart-components/smart-icons",
|
||||
"description": "Icon font for smart-screen",
|
||||
"license": "MPL",
|
||||
"private": true,
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"templates",
|
||||
"images",
|
||||
"test",
|
||||
"tests",
|
||||
"index.html",
|
||||
"Gruntfile.js",
|
||||
"README.md",
|
||||
"package.json"
|
||||
]
|
||||
}
|
||||
BIN
gaia/tv_apps/smart-system/bower_components/smart-icons/fonts/smart-icons.ttf
vendored
Executable file
25
gaia/tv_apps/smart-system/bower_components/smart-icons/smart-icons-embedded.css
vendored
Executable file
25
gaia/tv_apps/smart-system/bower_components/smart-icons/smart-icons.css
vendored
Executable file
|
|
@ -0,0 +1,25 @@
|
|||
/* Generated by grunt-webfont */
|
||||
/* Based on https://github.com/endtwist/fontcustom/blob/master/lib/fontcustom/templates/fontcustom.css */
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: "smart-icons";
|
||||
src: url("fonts/smart-icons.ttf") format("truetype");
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
[data-icon]:before,
|
||||
.ligature-icons {
|
||||
font-family: "smart-icons";
|
||||
content: attr(data-icon);
|
||||
display: inline-block;
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
text-decoration: inherit;
|
||||
text-transform: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
font-size: 30px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
31
gaia/tv_apps/smart-system/bower_components/smart-modal-dialog/.bower.json
vendored
Executable file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"name": "smart-modal-dialog",
|
||||
"homepage": "https://github.com/smart-components/smart-modal-dialog",
|
||||
"main": "script.js",
|
||||
"license": "MPL",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"examples",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"dependencies": {
|
||||
"smart-dialog": "smart-components/smart-dialog",
|
||||
"smart-bubbles": "smart-components/smart-bubbles",
|
||||
"smart-button": "smart-components/smart-button",
|
||||
"smart-icons": "smart-components/smart-icons"
|
||||
},
|
||||
"_release": "36275a3742",
|
||||
"_resolution": {
|
||||
"type": "branch",
|
||||
"branch": "master",
|
||||
"commit": "36275a3742389663d16c61b53a426fd3401e4850"
|
||||
},
|
||||
"_source": "git://github.com/smart-components/smart-modal-dialog.git",
|
||||
"_target": "*",
|
||||
"_originalSource": "smart-components/smart-modal-dialog"
|
||||
}
|
||||
362
gaia/tv_apps/smart-system/bower_components/smart-modal-dialog/LICENSE
vendored
Executable file
|
|
@ -0,0 +1,362 @@
|
|||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
1.1. "Contributor"
|
||||
|
||||
means each individual or legal entity that creates, contributes to the
|
||||
creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
|
||||
means the combination of the Contributions of others (if any) used by a
|
||||
Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
|
||||
means Source Code Form to which the initial Contributor has attached the
|
||||
notice in Exhibit A, the Executable Form of such Source Code Form, and
|
||||
Modifications of such Source Code Form, in each case including portions
|
||||
thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
a. that the initial Contributor has attached the notice described in
|
||||
Exhibit B to the Covered Software; or
|
||||
|
||||
b. that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the terms of
|
||||
a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
|
||||
means a work that combines Covered Software with other material, in a
|
||||
separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
|
||||
means having the right to grant, to the maximum extent possible, whether
|
||||
at the time of the initial grant or subsequently, any and all of the
|
||||
rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
|
||||
means any of the following:
|
||||
|
||||
a. any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered Software; or
|
||||
|
||||
b. any new file in Source Code Form that contains any Covered Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the License,
|
||||
by the making, using, selling, offering for sale, having made, import,
|
||||
or transfer of either its Contributions or its Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
|
||||
means either the GNU General Public License, Version 2.0, the GNU Lesser
|
||||
General Public License, Version 2.1, the GNU Affero General Public
|
||||
License, Version 3.0, or any later versions of those licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that controls, is
|
||||
controlled by, or is under common control with You. For purposes of this
|
||||
definition, "control" means (a) the power, direct or indirect, to cause
|
||||
the direction or management of such entity, whether by contract or
|
||||
otherwise, or (b) ownership of more than fifty percent (50%) of the
|
||||
outstanding shares or beneficial ownership of such entity.
|
||||
|
||||
|
||||
2. License Grants and Conditions
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
a. under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
b. under Patent Claims of such Contributor to make, use, sell, offer for
|
||||
sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
a. for any code that a Contributor has removed from Covered Software; or
|
||||
|
||||
b. for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
c. under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights to
|
||||
grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
|
||||
Section 2.1.
|
||||
|
||||
|
||||
3. Responsibilities
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
a. such Covered Software must also be made available in Source Code Form,
|
||||
as described in Section 3.1, and You must inform recipients of the
|
||||
Executable Form how they can obtain a copy of such Source Code Form by
|
||||
reasonable means in a timely manner, at a charge no more than the cost
|
||||
of distribution to the recipient; and
|
||||
|
||||
b. You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter the
|
||||
recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty, or
|
||||
limitations of liability) contained within the Source Code Form of the
|
||||
Covered Software, except that You may alter any license notices to the
|
||||
extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this License
|
||||
with respect to some or all of the Covered Software due to statute,
|
||||
judicial order, or regulation then You must: (a) comply with the terms of
|
||||
this License to the maximum extent possible; and (b) describe the
|
||||
limitations and the code they affect. Such description must be placed in a
|
||||
text file included with all distributions of the Covered Software under
|
||||
this License. Except to the extent prohibited by statute or regulation,
|
||||
such description must be sufficiently detailed for a recipient of ordinary
|
||||
skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically if You
|
||||
fail to comply with any of its terms. However, if You become compliant,
|
||||
then the rights granted under this License from a particular Contributor
|
||||
are reinstated (a) provisionally, unless and until such Contributor
|
||||
explicitly and finally terminates Your grants, and (b) on an ongoing
|
||||
basis, if such Contributor fails to notify You of the non-compliance by
|
||||
some reasonable means prior to 60 days after You have come back into
|
||||
compliance. Moreover, Your grants from a particular Contributor are
|
||||
reinstated on an ongoing basis if such Contributor notifies You of the
|
||||
non-compliance by some reasonable means, this is the first time You have
|
||||
received notice of non-compliance with this License from such
|
||||
Contributor, and You become compliant prior to 30 days after Your receipt
|
||||
of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
|
||||
license agreements (excluding distributors and resellers) which have been
|
||||
validly granted by You or Your distributors under this License prior to
|
||||
termination shall survive termination.
|
||||
|
||||
6. Disclaimer of Warranty
|
||||
|
||||
Covered Software is provided under this License on an "as is" basis,
|
||||
without warranty of any kind, either expressed, implied, or statutory,
|
||||
including, without limitation, warranties that the Covered Software is free
|
||||
of defects, merchantable, fit for a particular purpose or non-infringing.
|
||||
The entire risk as to the quality and performance of the Covered Software
|
||||
is with You. Should any Covered Software prove defective in any respect,
|
||||
You (not any Contributor) assume the cost of any necessary servicing,
|
||||
repair, or correction. This disclaimer of warranty constitutes an essential
|
||||
part of this License. No use of any Covered Software is authorized under
|
||||
this License except under this disclaimer.
|
||||
|
||||
7. Limitation of Liability
|
||||
|
||||
Under no circumstances and under no legal theory, whether tort (including
|
||||
negligence), contract, or otherwise, shall any Contributor, or anyone who
|
||||
distributes Covered Software as permitted above, be liable to You for any
|
||||
direct, indirect, special, incidental, or consequential damages of any
|
||||
character including, without limitation, damages for lost profits, loss of
|
||||
goodwill, work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses, even if such party shall have been
|
||||
informed of the possibility of such damages. This limitation of liability
|
||||
shall not apply to liability for death or personal injury resulting from
|
||||
such party's negligence to the extent applicable law prohibits such
|
||||
limitation. Some jurisdictions do not allow the exclusion or limitation of
|
||||
incidental or consequential damages, so this exclusion and limitation may
|
||||
not apply to You.
|
||||
|
||||
8. Litigation
|
||||
|
||||
Any litigation relating to this License may be brought only in the courts
|
||||
of a jurisdiction where the defendant maintains its principal place of
|
||||
business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions. Nothing
|
||||
in this Section shall prevent a party's ability to bring cross-claims or
|
||||
counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides that
|
||||
the language of a contract shall be construed against the drafter shall not
|
||||
be used to construe this License against a Contributor.
|
||||
|
||||
|
||||
10. Versions of the License
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses If You choose to distribute Source Code Form that is
|
||||
Incompatible With Secondary Licenses under the terms of this version of
|
||||
the License, the notice described in Exhibit B of this License must be
|
||||
attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular file,
|
||||
then You may include the notice in a location (such as a LICENSE file in a
|
||||
relevant directory) where a recipient would be likely to look for such a
|
||||
notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
|
||||
This Source Code Form is "Incompatible
|
||||
With Secondary Licenses", as defined by
|
||||
the Mozilla Public License, v. 2.0.
|
||||
23
gaia/tv_apps/smart-system/bower_components/smart-modal-dialog/bower.json
vendored
Executable file
|
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "smart-modal-dialog",
|
||||
"version": "0.0.0",
|
||||
"homepage": "https://github.com/smart-components/smart-modal-dialog",
|
||||
"main": "script.js",
|
||||
"license": "MPL",
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests",
|
||||
"examples",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"dependencies": {
|
||||
"smart-dialog": "smart-components/smart-dialog",
|
||||
"smart-bubbles": "smart-components/smart-bubbles",
|
||||
"smart-button": "smart-components/smart-button",
|
||||
"smart-icons": "smart-components/smart-icons"
|
||||
}
|
||||
}
|
||||
353
gaia/tv_apps/smart-system/bower_components/smart-modal-dialog/script.js
vendored
Executable file
|
|
@ -0,0 +1,353 @@
|
|||
/**
|
||||
* SmartModalDialog creates a dialog with a group of buttons as select
|
||||
* options. It also includes open/close animation and bubbling animation.
|
||||
* The template is shown below:
|
||||
* <smart-dialog class="modal-dialog">
|
||||
* <div class="outer-container">
|
||||
* <div class="container">
|
||||
* <div class="modal-dialog-message-container">
|
||||
* <div class="modal-dialog-message"></div>
|
||||
* <div class="modal-dialog-custom-group"></div>
|
||||
* </div>
|
||||
* <div class="modal-dialog-button-group">
|
||||
* <smart-button></smart-button>
|
||||
* ...
|
||||
* <smart-button></smart-button>
|
||||
* </div>
|
||||
* </div>
|
||||
* </div>
|
||||
* </smart-dialog>
|
||||
*/
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
var DEFAULT_MARGIN = 44;
|
||||
|
||||
function SmartModalDialog(container, options) {
|
||||
// Determine whether this dialog is opened or not
|
||||
this.isOpened = false;
|
||||
this._clickedIndex = null;
|
||||
this.defaultFocusIndex = -1;
|
||||
|
||||
this._margin = options && options.margin ? options.margin : DEFAULT_MARGIN;
|
||||
this._translateX = 0;
|
||||
|
||||
this.smartBubble = document.createElement('smart-bubbles');
|
||||
|
||||
// smart-dialog helps us to handle ESC key to close the dialog.
|
||||
this.element = document.createElement('smart-dialog');
|
||||
this.element.classList.add('modal-dialog');
|
||||
// make dialog focusable in order to catch focus from mouse click or touch
|
||||
// event.
|
||||
this.element.setAttribute('tabIndex', '-1');
|
||||
|
||||
// in order to make vertical align, we need additional container
|
||||
this.outerContainer = document.createElement('div');
|
||||
this.outerContainer.classList.add('outer-container');
|
||||
|
||||
this.innerContainer = document.createElement('div');
|
||||
this.innerContainer.classList.add('container');
|
||||
|
||||
this.messageElement = document.createElement('div');
|
||||
this.messageElement.classList.add('modal-dialog-message');
|
||||
|
||||
this.customElementGroup = document.createElement('div');
|
||||
this.customElementGroup.classList.add('modal-dialog-custom-group');
|
||||
|
||||
this.messageContainer = document.createElement('div');
|
||||
this.messageContainer.classList.add('modal-dialog-message-container');
|
||||
this.messageContainer.appendChild(this.messageElement);
|
||||
this.messageContainer.appendChild(this.customElementGroup);
|
||||
|
||||
this.buttonGroup = document.createElement('div');
|
||||
this.buttonGroup.classList.add('modal-dialog-button-group');
|
||||
this.buttonGroup.setAttribute('smart-bubbles', 'true');
|
||||
|
||||
this.innerContainer.appendChild(this.messageContainer);
|
||||
this.innerContainer.appendChild(this.buttonGroup);
|
||||
|
||||
this.outerContainer.appendChild(this.innerContainer);
|
||||
this.element.appendChild(this.outerContainer);
|
||||
|
||||
this.container = container || document.body;
|
||||
this.container.appendChild(this.element);
|
||||
|
||||
this.element.addEventListener('will-open', this);
|
||||
this.element.addEventListener('will-close', this);
|
||||
this.element.addEventListener('opened', this);
|
||||
this.element.addEventListener('closed', this);
|
||||
this.smartBubble.addEventListener('all-items-bubbled', this);
|
||||
|
||||
this._init();
|
||||
}
|
||||
|
||||
var proto = Object.create(SmartDialog.prototype);
|
||||
|
||||
proto._init = function(container, options) {
|
||||
// Put your custom init code here.
|
||||
};
|
||||
|
||||
proto._setL10n = function(element, l10n) {
|
||||
if ((typeof l10n) === 'string') {
|
||||
element.setAttribute('data-l10n-id', l10n);
|
||||
} else if (navigator.mozL10n) {
|
||||
navigator.mozL10n.setAttributes(element, l10n.id, l10n.args);
|
||||
}
|
||||
};
|
||||
|
||||
proto._open = function(options) {
|
||||
this.message = options.message || {};
|
||||
var renderedCallback = options.onButtonRendered;
|
||||
|
||||
this.customSettings = options.customElementSettings || null;
|
||||
if (this.customSettings) {
|
||||
while (this.customElementGroup.firstChild) {
|
||||
this.customElementGroup.removeChild(this.customElementGroup.firstChild);
|
||||
}
|
||||
this.customElementGroup.appendChild(this.customSettings.element);
|
||||
this.customElementGroup.classList.remove('hidden');
|
||||
} else {
|
||||
this.customElementGroup.classList.add('hidden');
|
||||
}
|
||||
|
||||
// onCancel is triggered when ESC key is pressed.
|
||||
this.onCancel = options.onCancel || function() {};
|
||||
this.buttonElements = [];
|
||||
this.buttonSettings = options.buttonSettings || [];
|
||||
if (!this.message.textRaw && !this.message.textL10nId) {
|
||||
this.messageElement.classList.add('hidden');
|
||||
} else {
|
||||
this.messageElement.classList.remove('hidden');
|
||||
this.messageElement.textContent = this.message.textRaw || '';
|
||||
if (this.message.textL10nId) {
|
||||
this._setL10n(this.messageElement, this.message.textL10nId);
|
||||
}
|
||||
}
|
||||
|
||||
this.buttonGroup.innerHTML = '';
|
||||
|
||||
// Set up every button
|
||||
this.buttonSettings.forEach(function buildButton(buttonSetting, index) {
|
||||
var button = document.createElement('smart-button');
|
||||
button.setAttribute('type', buttonSetting.type || 'circle-text');
|
||||
button.textContent = buttonSetting.textRaw || 'OK';
|
||||
if (buttonSetting.textL10nId) {
|
||||
this._setL10n(button, buttonSetting.textL10nId);
|
||||
}
|
||||
button.classList.add(buttonSetting.class || 'confirm');
|
||||
if (buttonSetting.icon) {
|
||||
button.style.backgroundImage = 'url(' + buttonSetting.icon + ')';
|
||||
} else if (buttonSetting.iconFont) {
|
||||
button.dataset.icon = buttonSetting.iconFont;
|
||||
}
|
||||
button.addEventListener('click', function() {
|
||||
// Click action will be handled in closed event
|
||||
this._clickedIndex = index;
|
||||
this.element.close();
|
||||
this.element.focus();
|
||||
}.bind(this));
|
||||
this.buttonElements.push(button);
|
||||
this.buttonGroup.appendChild(button);
|
||||
this._focusedIndex =
|
||||
(buttonSetting.defaultFocus) ? index : this._focusedIndex;
|
||||
if (renderedCallback) {
|
||||
// We add render callback to let user to do more complex visual
|
||||
// modifications.
|
||||
renderedCallback(button, buttonSetting);
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
this.element.classList.add('visible');
|
||||
this.element.open();
|
||||
this.element.focus();
|
||||
};
|
||||
|
||||
proto.open = function(options) {
|
||||
if (this.isOpened) {
|
||||
return;
|
||||
}
|
||||
this.isOpened = true;
|
||||
this._focusedIndex = this.defaultFocusIndex;
|
||||
// We should wait two frames for reflow.
|
||||
window.requestAnimationFrame(function() {
|
||||
window.requestAnimationFrame(this._open.bind(this, options));
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
proto.close = function() {
|
||||
this.element.close();
|
||||
this.element.focus();
|
||||
};
|
||||
|
||||
proto.remove = function() {
|
||||
this.container.removeChild(this.element);
|
||||
};
|
||||
|
||||
proto.startKeyNavigation = function() {
|
||||
this.element.addEventListener('keydown', this);
|
||||
if (this.buttonElements.length > 0) {
|
||||
if (this._focusedIndex === -1) {
|
||||
this._focusedIndex = 0;
|
||||
}
|
||||
this.focus();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Scroll the list to the input element
|
||||
*/
|
||||
proto._scrollTo = function(element) {
|
||||
var newTransition = this._getScrollOffset(element);
|
||||
this._translateX = newTransition;
|
||||
this.buttonGroup.style.transform = 'translateX(' + this._translateX +
|
||||
'px)';
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the offset of the node element
|
||||
*/
|
||||
proto._getScrollOffset = function(nodeElem) {
|
||||
if (!this._viewWidth) {
|
||||
this._viewWidth = this.innerContainer.clientWidth - 2 * this._margin;
|
||||
}
|
||||
var nodeLeft = nodeElem.offsetLeft;
|
||||
var nodeWidth = nodeElem.offsetWidth;
|
||||
var listWidth = this.buttonGroup.offsetWidth;
|
||||
var newTranslate = this._translateX;
|
||||
var lastElement = this.buttonElements[this.buttonElements.length - 1];
|
||||
var firstElement = this.buttonElements[0];
|
||||
|
||||
if (listWidth < this._viewWidth) {
|
||||
// align to horizontal center if list width is smaller than the container
|
||||
return (this._viewWidth - listWidth) / 2 + this._margin;
|
||||
} else if (nodeLeft + nodeWidth >
|
||||
-this._translateX + this._viewWidth + this._margin) {
|
||||
// scroll left if the node falls beyond the right edge of container
|
||||
newTranslate = this._viewWidth - nodeLeft - nodeWidth + this._margin;
|
||||
} else if (nodeLeft < -this._translateX + this._margin) {
|
||||
// scroll right if the node falls beyond the left edge of container
|
||||
newTranslate = -nodeLeft + this._margin;
|
||||
}
|
||||
|
||||
|
||||
// If the new scroll offset contains first/last node, we have to align the
|
||||
// list to begin/end.
|
||||
if (lastElement.offsetLeft + lastElement.offsetWidth <=
|
||||
-newTranslate + this._viewWidth + this._margin) {
|
||||
return this._viewWidth + this._margin - lastElement.offsetLeft -
|
||||
lastElement.offsetWidth;
|
||||
} else if (firstElement.offsetLeft >= -newTranslate + this._margin) {
|
||||
return -firstElement.offsetLeft + this._margin;
|
||||
}
|
||||
return newTranslate;
|
||||
};
|
||||
|
||||
proto._getFocusedElement = function() {
|
||||
return this.buttonElements[this._focusedIndex];
|
||||
};
|
||||
|
||||
proto.focus = function() {
|
||||
if (this.element.classList.contains('opening') ||
|
||||
this.element.classList.contains('closing')) {
|
||||
this.element.focus();
|
||||
} else {
|
||||
var elem = this._getFocusedElement()
|
||||
this._scrollTo(elem);
|
||||
|
||||
// move focus to smart dialog while transition running
|
||||
if (elem.focus && (typeof elem.focus) === 'function') {
|
||||
elem.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
proto.blur = function() {
|
||||
var elem = this._getFocusedElement();
|
||||
if (elem.blur && (typeof elem.blur === 'function')) {
|
||||
elem.blur();
|
||||
}
|
||||
};
|
||||
|
||||
proto.stopKeyNavigation = function() {
|
||||
this.element.removeEventListener('keydown', this);
|
||||
};
|
||||
|
||||
proto.movePrevious = function() {
|
||||
if (this._focusedIndex < 1) {
|
||||
// Do nothing when focus index is at the first button element.
|
||||
return;
|
||||
}
|
||||
|
||||
this._focusedIndex--;
|
||||
this.focus();
|
||||
};
|
||||
|
||||
proto.moveNext = function() {
|
||||
if (this._focusedIndex > this.buttonElements.length - 2) {
|
||||
// Do nothing when focus index is at the last button element
|
||||
return;
|
||||
}
|
||||
this._focusedIndex++;
|
||||
this.focus();
|
||||
};
|
||||
|
||||
proto._handleKeyEvent = function(e) {
|
||||
if (e.keyCode === KeyEvent.DOM_VK_LEFT) {
|
||||
this.movePrevious();
|
||||
} else if (e.keyCode === KeyEvent.DOM_VK_RIGHT) {
|
||||
this.moveNext();
|
||||
}
|
||||
};
|
||||
|
||||
proto.handleEvent = function(e) {
|
||||
this._handleKeyEvent(e);
|
||||
|
||||
switch(e.target) {
|
||||
case this.element:
|
||||
if (e.type === 'will-open' || e.type === 'will-close') {
|
||||
this.fireEvent('modal-dialog-' + e.type);
|
||||
} else if (e.type === 'opened') {
|
||||
this._scrollTo(this.buttonElements[0]);
|
||||
// Play bubble animation when the smart-dialog is opened
|
||||
this.smartBubble.play(this.buttonElements);
|
||||
} else if (e.type === 'closed') {
|
||||
// Do click actions when the smart-dialog is closed
|
||||
this.element.classList.remove('visible');
|
||||
if ((this._clickedIndex || this._clickedIndex === 0) &&
|
||||
this.buttonSettings[this._clickedIndex].onClick) {
|
||||
this.buttonSettings[this._clickedIndex].onClick(
|
||||
this.buttonSettings[this._clickedIndex]);
|
||||
this._clickedIndex = null;
|
||||
} else if (this.onCancel) {
|
||||
// ESC key enter
|
||||
this.onCancel();
|
||||
}
|
||||
this.isOpened = false;
|
||||
this.stopKeyNavigation();
|
||||
this.element.classList.remove('modal-dialog-opened');
|
||||
this.fireEvent('modal-dialog-closed');
|
||||
}
|
||||
break;
|
||||
case this.smartBubble:
|
||||
this.element.classList.add('modal-dialog-opened');
|
||||
this.startKeyNavigation();
|
||||
this.focus();
|
||||
this.fireEvent('modal-dialog-opened');
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
proto.fireEvent = function smd_fireEvent(event, detail) {
|
||||
var evtObject = new CustomEvent(event, {
|
||||
bubbles: true,
|
||||
detail: detail || this
|
||||
});
|
||||
this.container.dispatchEvent(evtObject);
|
||||
};
|
||||
|
||||
|
||||
SmartModalDialog.prototype = proto;
|
||||
exports.SmartModalDialog = SmartModalDialog;
|
||||
|
||||
})(window);
|
||||
271
gaia/tv_apps/smart-system/bower_components/smart-modal-dialog/smart-input-dialog.js
vendored
Executable file
|
|
@ -0,0 +1,271 @@
|
|||
/**
|
||||
* SmartInputDialog creates an input text box with a clear button to clear text,
|
||||
* and a dialog with a group of buttons as select options, Text box includes
|
||||
* focus/blur animation, buttons includes open/close and bubbling animation.
|
||||
* The template is shown below:
|
||||
* <smart-dialog class="modal-dialog">
|
||||
* <div class="outer-container">
|
||||
* <div class="container">
|
||||
* <div class="modal-dialog-message-container">
|
||||
* <div class="modal-dialog-message"></div>
|
||||
* <div class="modal-dialog-custom-group"></div>
|
||||
* <div class="modal-dialog-input-group">
|
||||
* <input></input>
|
||||
* <button class="clear"></button>
|
||||
* <div class="focus"></div>
|
||||
* </div>
|
||||
* </div>
|
||||
* <div class="modal-dialog-button-group">
|
||||
* <smart-button></smart-button>
|
||||
* ...
|
||||
* <smart-button></smart-button>
|
||||
* </div>
|
||||
* </div>
|
||||
* </div>
|
||||
* </smart-dialog>
|
||||
*/
|
||||
|
||||
(function(exports) {
|
||||
'use strict';
|
||||
|
||||
var DEFAULT_MARGIN = 44;
|
||||
var INPUT_GROUP_INDEX = 0;
|
||||
var BUTTON_GROUP_INDEX = 1;
|
||||
|
||||
function SmartInputDialog(container, options) {
|
||||
SmartModalDialog.call(this, container, options);
|
||||
}
|
||||
|
||||
var proto = Object.create(SmartModalDialog.prototype);
|
||||
|
||||
proto._init = function() {
|
||||
this.defaultFocusIndex = [-1, -1];
|
||||
|
||||
this.textInput = document.createElement('input');
|
||||
this.textInput.addEventListener('keydown', function(e) {
|
||||
if (e.keyCode === KeyEvent.DOM_VK_RIGHT) {
|
||||
if (this.selectionEnd < this.value.length) {
|
||||
// We are going to move cursor in the text input, calling
|
||||
// stopPropagation to prevent focus being transfer to clear button.
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.clearButton = document.createElement('button');
|
||||
this.clearButton.setAttribute('data-icon', 'close');
|
||||
this.clearButton.classList.add('clear');
|
||||
this.clearButton.addEventListener('click', function() {
|
||||
this.textInput.value = '';
|
||||
this.textInput.focus();
|
||||
}.bind(this));
|
||||
|
||||
this.focusBar = document.createElement('div');
|
||||
this.focusBar.classList.add('focus');
|
||||
|
||||
this.smartInputGroup = document.createElement('div');
|
||||
this.smartInputGroup.classList.add('modal-dialog-input-group');
|
||||
this.smartInputGroup.appendChild(this.textInput);
|
||||
this.smartInputGroup.appendChild(this.clearButton);
|
||||
this.smartInputGroup.appendChild(this.focusBar);
|
||||
|
||||
this.messageContainer.appendChild(this.smartInputGroup);
|
||||
};
|
||||
|
||||
proto._createMessageGroup = function(options) {
|
||||
this.message = options.message || {};
|
||||
|
||||
if (!this.message.textRaw && !this.message.textL10nId) {
|
||||
this.messageElement.classList.add('hidden');
|
||||
} else {
|
||||
this.messageElement.classList.remove('hidden');
|
||||
this.messageElement.textContent = this.message.textRaw || '';
|
||||
if (this.message.textL10nId) {
|
||||
this.messageElement.setAttribute('data-l10n-id',
|
||||
this.message.textL10nId);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
proto._createCustomGroup = function(options) {
|
||||
this.customElements = [];
|
||||
this.customElementOffset = 0;
|
||||
|
||||
this.customSettings = options.customElementSettings || null;
|
||||
|
||||
if (this.customSettings) {
|
||||
while (this.customElementGroup.firstChild) {
|
||||
this.customElementGroup.removeChild(this.customElementGroup.firstChild);
|
||||
}
|
||||
this.customElementGroup.appendChild(this.customSettings.element);
|
||||
this.customElementGroup.classList.remove('hidden');
|
||||
} else {
|
||||
this.customElementGroup.classList.add('hidden');
|
||||
}
|
||||
};
|
||||
|
||||
proto._createInputGroup = function(options) {
|
||||
this.inputElements = [this.textInput, this.clearButton];
|
||||
|
||||
if (options.initialInputValue) {
|
||||
this.textInput.value = options.initialInputValue;
|
||||
}
|
||||
|
||||
var retrunedCallback = options.onReturned;
|
||||
if (retrunedCallback) {
|
||||
this.textInput.addEventListener('keydown', function(evt) {
|
||||
if (evt.keyCode === KeyEvent.DOM_VK_RETURN &&
|
||||
this._getFocusedElement() !== this.clearButton) {
|
||||
retrunedCallback();
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
};
|
||||
|
||||
proto._createButtonGroup = function(options) {
|
||||
this.buttonElements = [];
|
||||
|
||||
// onCancel is triggered when ESC key is pressed.
|
||||
this.onCancel = options.onCancel || function() {};
|
||||
this.buttonElements = [];
|
||||
this.buttonSettings = options.buttonSettings || [];
|
||||
|
||||
this.buttonGroup.innerHTML = '';
|
||||
this._focusedIndex[BUTTON_GROUP_INDEX] = 0;
|
||||
|
||||
// Set up every button
|
||||
this.buttonSettings.forEach(function buildButton(buttonSetting, index) {
|
||||
var button = document.createElement('smart-button');
|
||||
button.setAttribute('type', buttonSetting.type || 'circle-text');
|
||||
if (buttonSetting.textL10nId) {
|
||||
button.setAttribute('data-l10n-id', buttonSetting.textL10nId);
|
||||
}
|
||||
button.textContent = buttonSetting.textRaw || 'OK';
|
||||
button.classList.add(buttonSetting.class || 'confirm');
|
||||
if (buttonSetting.icon) {
|
||||
button.style.backgroundImage = 'url(' + buttonSetting.icon + ')';
|
||||
} else if (buttonSetting.iconFont) {
|
||||
button.dataset.icon = buttonSetting.iconFont;
|
||||
}
|
||||
button.addEventListener('click', function() {
|
||||
// Click action will be handled in closed event
|
||||
this._clickedIndex = index;
|
||||
this.element.close();
|
||||
this.element.focus();
|
||||
}.bind(this));
|
||||
this.buttonElements.push(button);
|
||||
this.buttonGroup.appendChild(button);
|
||||
|
||||
var customFocus = this.customSettings && this.customSettings.defaultFocus;
|
||||
if (buttonSetting.defaultFocus && !customFocus) {
|
||||
this._focusedIndex[BUTTON_GROUP_INDEX] = index;
|
||||
}
|
||||
|
||||
var renderedCallback = options.onButtonRendered;
|
||||
if (renderedCallback) {
|
||||
// We add render callback to let user to do more complex visual
|
||||
// modifications.
|
||||
renderedCallback(button, buttonSetting);
|
||||
}
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
proto._open = function(options) {
|
||||
this._createMessageGroup(options);
|
||||
this._createCustomGroup(options);
|
||||
this._createInputGroup(options);
|
||||
this._createButtonGroup(options);
|
||||
|
||||
this._focusedGroupIndex = 0;
|
||||
this._focusedGroupIndex[INPUT_GROUP_INDEX] = 0;
|
||||
// Put focusable element groups into verticalGroup,
|
||||
// so we can simply navigate up/down among these groups.
|
||||
this.verticalGroup = [this.inputElements, this.buttonElements];
|
||||
|
||||
this.element.classList.add('visible');
|
||||
this.element.open();
|
||||
this.element.focus();
|
||||
};
|
||||
|
||||
proto.startKeyNavigation = function() {
|
||||
this.element.addEventListener('keydown', this);
|
||||
if (this._focusedIndex[0] === -1) {
|
||||
this._focusedIndex[0] = 0;
|
||||
}
|
||||
this.focus();
|
||||
};
|
||||
|
||||
proto._getFocusedElement = function() {
|
||||
return this.verticalGroup[this._focusedGroupIndex]
|
||||
[this._focusedIndex[this._focusedGroupIndex]];
|
||||
};
|
||||
|
||||
proto.moveUp = function(e) {
|
||||
if (this._focusedGroupIndex < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._focusedGroupIndex--;
|
||||
this.focus();
|
||||
};
|
||||
|
||||
proto.moveDown = function(e) {
|
||||
if (this._focusedGroupIndex > this.verticalGroup.length - 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Move input-group's focus back to input box when focus on button-group
|
||||
this._focusedIndex[INPUT_GROUP_INDEX] = 0;
|
||||
this._focusedGroupIndex++;
|
||||
this.focus();
|
||||
};
|
||||
|
||||
proto.movePrevious = function(e) {
|
||||
if (this._focusedIndex[this._focusedGroupIndex] < 1) {
|
||||
// Do nothing when focus index is at the first element.
|
||||
return;
|
||||
}
|
||||
|
||||
this._focusedIndex[this._focusedGroupIndex]--;
|
||||
if (this._focusedGroupIndex === INPUT_GROUP_INDEX) {
|
||||
// Moving from clean button to input text will move input caret position
|
||||
// back one character, preventDefault so we don't change the position.
|
||||
e.preventDefault();
|
||||
}
|
||||
this.focus();
|
||||
};
|
||||
|
||||
proto.moveNext = function(e) {
|
||||
if (this._focusedIndex[this._focusedGroupIndex] >
|
||||
this.verticalGroup[this._focusedGroupIndex].length - 2) {
|
||||
// Do nothing when focus index is at the last element
|
||||
return;
|
||||
}
|
||||
|
||||
this._focusedIndex[this._focusedGroupIndex]++;
|
||||
this.focus();
|
||||
};
|
||||
|
||||
proto._handleKeyEvent = function(e) {
|
||||
if (e.keyCode === KeyEvent.DOM_VK_UP) {
|
||||
this.moveUp(e);
|
||||
} else if (e.keyCode === KeyEvent.DOM_VK_DOWN) {
|
||||
this.moveDown(e);
|
||||
} else if (e.keyCode === KeyEvent.DOM_VK_LEFT) {
|
||||
this.movePrevious(e);
|
||||
} else if (e.keyCode === KeyEvent.DOM_VK_RIGHT) {
|
||||
this.moveNext(e);
|
||||
} else if (e.keyCode == KeyEvent.DOM_VK_TAB) {
|
||||
e.preventDefault();
|
||||
if (this._focusedGroupIndex === INPUT_GROUP_INDEX) {
|
||||
this.moveDown(e);
|
||||
} else {
|
||||
this.moveUp(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SmartInputDialog.prototype = proto;
|
||||
exports.SmartInputDialog = SmartInputDialog;
|
||||
|
||||
})(window);
|
||||
124
gaia/tv_apps/smart-system/bower_components/smart-modal-dialog/style.css
vendored
Executable file
|
|
@ -0,0 +1,124 @@
|
|||
.modal-dialog > .outer-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.modal-dialog > .outer-container:before {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
width: 0.1rem;
|
||||
margin-left: -0.1rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.modal-dialog .container {
|
||||
width: 100%;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.modal-dialog .modal-dialog-message-container {
|
||||
width: 96rem;
|
||||
position: relative;
|
||||
margin: 0 auto 13rem auto;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.modal-dialog .modal-dialog-custom-group {
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
font-family: Fira Sans;
|
||||
}
|
||||
|
||||
.modal-dialog .modal-dialog-custom-group.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.modal-dialog-input-group {
|
||||
text-align: center;
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
width: 70rem;
|
||||
}
|
||||
|
||||
.modal-dialog-input-group input {
|
||||
font-size: 3.8rem;
|
||||
font-size: 3.8rem;
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.modal-dialog-input-group .focus {
|
||||
bottom: 0px;
|
||||
width: 100%;
|
||||
height: 0.4rem;
|
||||
margin: 0.4rem auto 0 auto;
|
||||
visibility: hidden;
|
||||
background: #00caf2;
|
||||
}
|
||||
|
||||
.modal-dialog-input-group .clear:before {
|
||||
vertical-align: top;
|
||||
line-height: 3rem;
|
||||
}
|
||||
|
||||
.modal-dialog-input-group .clear {
|
||||
position: absolute;
|
||||
left: calc(100% - 4rem);
|
||||
border: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
color: #ffffff;
|
||||
cursor: pointer;
|
||||
background: #999999;
|
||||
}
|
||||
|
||||
.modal-dialog-input-group :focus ~ .focus {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.modal-dialog-input-group :focus ~ .clear,
|
||||
.modal-dialog-input-group :focus.clear {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.modal-dialog .modal-dialog-message {
|
||||
font-size: 3.8rem;
|
||||
font-style: italic;
|
||||
color: #ffffff;
|
||||
text-align: center;
|
||||
font-family: Fira Sans;
|
||||
font-weight: 100;
|
||||
margin-bottom: 3.4rem;
|
||||
}
|
||||
|
||||
.modal-dialog .modal-dialog-message.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.modal-dialog .modal-dialog-button-group {
|
||||
white-space: nowrap;
|
||||
/* use inline-block to wrap content */
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.modal-dialog.modal-dialog-opened .modal-dialog-button-group {
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.modal-dialog .modal-dialog-button-group > * {
|
||||
margin-right: 4.4rem;
|
||||
}
|
||||
|
||||
.modal-dialog .modal-dialog-button-group > *:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
57
gaia/tv_apps/smart-system/build/build.js
Executable file
|
|
@ -0,0 +1,57 @@
|
|||
'use strict';
|
||||
|
||||
/* jshint node: true */
|
||||
|
||||
var utils = require('utils');
|
||||
var preprocessor = require('preprocessor');
|
||||
|
||||
var SystemAppBuilder = function() {
|
||||
};
|
||||
|
||||
SystemAppBuilder.prototype.setOptions = function(options) {
|
||||
this.stageDir = utils.getFile(options.STAGE_APP_DIR);
|
||||
this.appDir = utils.getFile(options.APP_DIR);
|
||||
this.distDirPath = options.GAIA_DISTRIBUTION_DIR;
|
||||
};
|
||||
|
||||
SystemAppBuilder.prototype.addCustomizeFiles = function() {
|
||||
if (!utils.getFile(this.distDirPath, 'power').exists()) {
|
||||
return;
|
||||
}
|
||||
var self = this;
|
||||
var fileDir = utils.getFile(this.distDirPath, 'power');
|
||||
var files = utils.ls(fileDir);
|
||||
files.forEach(function(file) {
|
||||
utils.copyFileTo(file.path,
|
||||
utils.joinPath(self.stageDir.path, 'resources', 'power'), file.leafName);
|
||||
});
|
||||
};
|
||||
|
||||
SystemAppBuilder.prototype.enableFirefoxSync = function(options) {
|
||||
var fileList = {
|
||||
process: [
|
||||
['index.html'],
|
||||
['js/fx_accounts_client.js']
|
||||
],
|
||||
remove: [
|
||||
['js', 'sync_manager.js'],
|
||||
['js', 'sync_state_machine.js'],
|
||||
['test', 'unit', 'sync_manager_test.js'],
|
||||
['test', 'unit', 'sync_state_machine_test.js']
|
||||
]
|
||||
};
|
||||
preprocessor.execute(options, 'FIREFOX_SYNC', fileList);
|
||||
};
|
||||
|
||||
SystemAppBuilder.prototype.execute = function(options) {
|
||||
utils.copyToStage(options);
|
||||
this.setOptions(options);
|
||||
if (this.distDirPath) {
|
||||
this.addCustomizeFiles();
|
||||
}
|
||||
this.enableFirefoxSync(options);
|
||||
};
|
||||
|
||||
exports.execute = function(options) {
|
||||
(new SystemAppBuilder()).execute(options);
|
||||
};
|
||||
14
gaia/tv_apps/smart-system/elements/net_error_action_menu.html
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
<element name="net-error-action-menu" extends="section">
|
||||
<template>
|
||||
<form role="dialog" data-type="action">
|
||||
<section>
|
||||
<h1 id="error-title" class="title"></h1>
|
||||
<span id="failed-logo"></span>
|
||||
</section>
|
||||
<menu id="actions">
|
||||
<button id="settings-btn" type="button" class="icon icon-settings" data-l10n-id="check-settings"></button>
|
||||
<button id="cancel-btn" type="button" class="cancel" data-l10n-id="cancel"></button>
|
||||
</menu>
|
||||
</form>
|
||||
</template>
|
||||
</element>
|
||||
10
gaia/tv_apps/smart-system/elements/net_error_confirm_dialog.html
Executable file
|
|
@ -0,0 +1,10 @@
|
|||
<element name="net-error-confirm-dialog" extends="section">
|
||||
<template>
|
||||
<h1 id="error-title" class="net-error-dialog-title"></h1>
|
||||
<p id="error-message" class="net-error-dialog-message"></p>
|
||||
<div class="net-error-dialog-btn-group">
|
||||
<smart-button id="close-btn" class="net-error-dialog-btn" type="rounded-corner-text" data-l10n-id="close" tabindex="0"></smart-button>
|
||||
<smart-button id="retry-btn" class="net-error-dialog-btn" type="rounded-corner-text" data-l10n-id="try-again" tabindex="0"></smart-button>
|
||||
</div>
|
||||
</template>
|
||||
</element>
|
||||
19
gaia/tv_apps/smart-system/fxa/elements/fxa-coppa.html
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
<element name="fxa-coppa" extends="section">
|
||||
<template>
|
||||
<section class="vertical">
|
||||
<h3 data-l10n-id="fxa-age-verification">
|
||||
Age verification
|
||||
</h3>
|
||||
<ul class="fxa-verification-wrapper">
|
||||
<li>
|
||||
<span class="button icon icon-dialog">
|
||||
<select id="fxa-age-select">
|
||||
<option value="0" data-l10n-id="fxa-year-of-birth">Year of birth</option>
|
||||
</select>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<script src='js/screens/fxam_coppa.js'></script>
|
||||
</template>
|
||||
</element>
|
||||
19
gaia/tv_apps/smart-system/fxa/elements/fxa-email.html
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
<element name="fxa-email" extends="section">
|
||||
<template>
|
||||
<section class="vertical">
|
||||
<div id="fxa-logo" class="fxa-logo"></div>
|
||||
<h3 class="fxa-title" data-l10n-id="firefox-accounts-title"></h3>
|
||||
<p class="fxa-intro" data-l10n-id="fxa-use-email-to-sign-in">
|
||||
Use your email address to create an account or sign in.
|
||||
</p>
|
||||
<div id="fxa-email-input-wrapper">
|
||||
<input id="fxa-email-input" data-l10n-id="fxa-enter-email-input" type="email" placeholder="Enter your email" required="" x-inputmode="verbatim" tabIndex="-1">
|
||||
<button id="fxa-email-clean-btn" data-icon="close" aria-hidden="true" tabindex="-1"></button>
|
||||
</div>
|
||||
<p id="fxa-notice" class="fine-print">
|
||||
By proceeding, I agree to the <a class="disabled" tabindex="-1">Terms of Service</a> and <a class="disabled" tabindex="-1">Privacy Notice</a> of Firefox cloud services.
|
||||
</p>
|
||||
</section>
|
||||
<script src='js/screens/fxam_enter_email.js'></script>
|
||||
</template>
|
||||
</element>
|
||||
21
gaia/tv_apps/smart-system/fxa/elements/fxa-enter-password.html
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
<element name="fxa-enter-password" extends="section">
|
||||
<template>
|
||||
<section class="vertical">
|
||||
<h3 class="fxa-title" data-l10n-id="firefox-accounts-title"></h3>
|
||||
<p id="fxa-hello-known-user" class="fxa-intro">Hello, <a id="fxa-known-user-email">holaENTER@email.com</a></p>
|
||||
<div id="fxa-pw-input-wrapper">
|
||||
<input id="fxa-pw-input" type="password" placeholder="Password" required pattern=".{8,}" data-l10n-id="fxa-password" x-inputmode="verbatim" tabindex="-1">
|
||||
<button id="fxa-pw-clean-btn" data-icon="close" aria-hidden="true" tabindex="-1"></button>
|
||||
</div>
|
||||
<div id="fxa-pw-option-container">
|
||||
<p class="fine-print"><a id="fxa-forgot-password" data-l10n-id="fxa-forgot-your-password" tabindex="-1">Forgot your password?</a></p>
|
||||
<div id="show-pw-wrapper">
|
||||
<input id="fxa-show-pw" class="settings-list-checkbox" type="checkbox" tabindex="-1">
|
||||
<label class="pack-checkbox" for="fxa-show-pw"></label>
|
||||
<p data-l10n-id="fxa-show-password"></p>
|
||||
</div>
|
||||
</div>
|
||||
<script src='js/screens/fxam_enter_password.js'></script>
|
||||
</section>
|
||||
</template>
|
||||
</element>
|
||||
11
gaia/tv_apps/smart-system/fxa/elements/fxa-error-overlay.html
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
<element name="fxa-error-overlay" extends="form">
|
||||
<template>
|
||||
<section>
|
||||
<h1 id="fxa-error-title">Error message</h1>
|
||||
<p><small id="fxa-error-msg">Error message.</small></p>
|
||||
<menu>
|
||||
<button class="full" data-l10n-id="fxa-ok" id="fxa-error-ok">OK</button>
|
||||
</menu>
|
||||
</section>
|
||||
</template>
|
||||
</element>
|
||||
13
gaia/tv_apps/smart-system/fxa/elements/fxa-pp.html
Executable file
|
|
@ -0,0 +1,13 @@
|
|||
<element name="fxa-pp" extends="section">
|
||||
<template>
|
||||
<section role="region">
|
||||
<h3 data-l10n-id="fxa-pp-header">Privacy Policy</h3>
|
||||
<p data-l10n-id="fxa-pp1">
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eius mod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequ at. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
</p>
|
||||
<p data-l10n-id="fxa-pp2">
|
||||
Excepteur sint occaecat qui of fi cia deserunt mollit anim id est lab orum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. D uis aute irure dolor in.
|
||||
</p>
|
||||
</section>
|
||||
</template>
|
||||
</element>
|
||||
21
gaia/tv_apps/smart-system/fxa/elements/fxa-refresh-auth.html
Executable file
|
|
@ -0,0 +1,21 @@
|
|||
<element name="fxa-refresh-auth" extends="section">
|
||||
<template>
|
||||
<section class="vertical">
|
||||
<h3 class="fxa-title"><span data-l10n-id="firefox-accounts-title"></span><span class="fax-title-separator">|</span><span data-l10n-id="fxa-signin"></span></h3>
|
||||
<p id="fxa-password-refresh" class="fxa-intro" data-l10n-id="fxa-password-refresh">Please enter your Firefox Account password to continue.</p>
|
||||
<div id="fxa-pw-input-wrapper">
|
||||
<input id="fxa-pw-input-refresh" type="password" placeholder="Password" required="" pattern=".{8,}" data-l10n-id="fxa-password" tabindex="-1">
|
||||
<button id="fxa-pw-clean-btn" data-icon="close" aria-hidden="true" tabindex="-1"></button>
|
||||
</div>
|
||||
<div id="fxa-pw-option-container">
|
||||
<p class="fine-print"><a id="fxa-forgot-password-refresh" data-l10n-id="fxa-forgot-your-password" tabindex="-1">Forgot your password?</a></p>
|
||||
<div id="show-pw-wrapper">
|
||||
<input id="fxa-show-pw-refresh" class="settings-list-checkbox" type="checkbox" tabindex="-1">
|
||||
<label class="pack-checkbox" for="fxa-show-pw-refresh"></label>
|
||||
<p data-l10n-id="fxa-show-password">Show password</p>
|
||||
</div>
|
||||
</div>
|
||||
<script src='js/screens/fxam_refresh_auth.js'></script>
|
||||
</section>
|
||||
</template>
|
||||
</element>
|
||||
20
gaia/tv_apps/smart-system/fxa/elements/fxa-set-password.html
Executable file
|
|
@ -0,0 +1,20 @@
|
|||
<element name="fxa-set-password" extends="section">
|
||||
<template>
|
||||
<section class="vertical">
|
||||
<h3 data-l10n-id="fxa-create-password-title">Create A Password</h3>
|
||||
<p id="fxa-hello-user">Hello, <a id="fxa-user-set-email">holaSET@email.com</a></p>
|
||||
<input id="fxa-pw-set-input" type="password" placeholder="Password" required="" pattern=".{8,}" data-l10n-id="fxa-password" x-inputmode="verbatim">
|
||||
<p class="fine-print" data-l10n-id="fxa-password-length-requirement">
|
||||
Your password must be at least 8 characters long.
|
||||
</p>
|
||||
<div id="show-pw-wrapper">
|
||||
<label class="pack-checkbox" for="fxa-show-pw-set">
|
||||
<input id="fxa-show-pw-set" type="checkbox">
|
||||
<span></span>
|
||||
</label>
|
||||
<p data-l10n-id="fxa-show-password">Show password</p>
|
||||
</div>
|
||||
<script src='js/screens/fxam_set_password.js'></script>
|
||||
</section>
|
||||
</template>
|
||||
</element>
|
||||
19
gaia/tv_apps/smart-system/fxa/elements/fxa-signin-success.html
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
<element name="fxa-signin-success" extends="section">
|
||||
<template>
|
||||
<section class="vertical">
|
||||
<h3 data-l10n-id="fxa-success">
|
||||
Success!
|
||||
</h3>
|
||||
<p data-l10n-id="fxa-ready-to-go">
|
||||
Your Firefox Account is ready to go!
|
||||
</p>
|
||||
<p data-l10n-id="fxa-use-features">
|
||||
Now you can use features like Find My Device and apps like the Firefox Marketplace.
|
||||
</p>
|
||||
<p data-l10n-id="fxa-access-manage-account-settings">
|
||||
In the future, you can access and manage your account from Settings.
|
||||
</p>
|
||||
<script src='js/screens/fxam_signin_success.js'></script>
|
||||
</section>
|
||||
</template>
|
||||
</element>
|
||||
19
gaia/tv_apps/smart-system/fxa/elements/fxa-signup-success.html
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
<element name="fxa-signup-success" extends="section">
|
||||
<template>
|
||||
<section class="vertical">
|
||||
<h3 data-l10n-id="fxa-confirm-account">
|
||||
Confirm Your Account
|
||||
</h3>
|
||||
<p id="fxa-will-send-email" class="fxa-envelope">
|
||||
We will send an email to: <strong id="fxa-summary-email">email</strong>
|
||||
</p>
|
||||
<p data-l10n-id="fxa-signup-click-verification-link-in-email">
|
||||
Next time you check your email, click the verification link to confirm your Firefox Account.
|
||||
</p>
|
||||
<p data-l10n-id="fxa-access-manage-account-settings">
|
||||
In the future, you can access and manage your account from Settings.
|
||||
</p>
|
||||
<script src='js/screens/fxam_signup_success.js'></script>
|
||||
</section>
|
||||
</template>
|
||||
</element>
|
||||
13
gaia/tv_apps/smart-system/fxa/elements/fxa-tos.html
Executable file
|
|
@ -0,0 +1,13 @@
|
|||
<element name="fxa-tos" extends="section">
|
||||
<template>
|
||||
<section role="region">
|
||||
<h3 data-l10n-id="fxa-tos-header">Terms of Service</h3>
|
||||
<p data-l10n-id="fxa-tos1">
|
||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eius mod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequ at. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
</p>
|
||||
<p data-l10n-id="fxa-tos2">
|
||||
Excepteur sint occaecat qui of fi cia deserunt mollit anim id est lab orum. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. D uis aute irure dolor in.
|
||||
</p>
|
||||
</section>
|
||||
</template>
|
||||
</element>
|
||||
108
gaia/tv_apps/smart-system/fxa/fxa_module.html
Executable file
|
|
@ -0,0 +1,108 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link href="../shared/style/switches.css" rel="stylesheet" type="text/css">
|
||||
<link href="../shared/style/buttons.css" rel="stylesheet" type="text/css">
|
||||
<link href="../shared/style/confirm.css" rel="stylesheet" type="text/css">
|
||||
<link href="../shared/style/input_areas.css" rel="stylesheet" type="text/css">
|
||||
<link href="../shared/style/progress_activity.css" rel="stylesheet" type="text/css">
|
||||
<link href="style/fxa.css" rel="stylesheet" type="text/css">
|
||||
|
||||
<!-- Gaia Components -->
|
||||
<link href="../shared/elements/gaia-theme/gaia-theme.css" rel="stylesheet" type="text/css">
|
||||
<link href="../shared/elements/gaia-icons/gaia-icons.css" rel="stylesheet" type="text/css">
|
||||
|
||||
<meta name="defaultLanguage" content="en-US">
|
||||
<meta name="availableLanguages" content="en-US">
|
||||
<link rel="localization" href="locales/fxa.{locale}.properties">
|
||||
<link rel="localization" href="../shared/locales/firefox_sync/firefox_sync.{locale}.properties">
|
||||
|
||||
<link rel="import" href="elements/fxa-email.html">
|
||||
<link rel="import" href="elements/fxa-set-password.html">
|
||||
<link rel="import" href="elements/fxa-signup-success.html">
|
||||
<link rel="import" href="elements/fxa-enter-password.html">
|
||||
<link rel="import" href="elements/fxa-signin-success.html">
|
||||
<link rel="import" href="elements/fxa-tos.html">
|
||||
<link rel="import" href="elements/fxa-pp.html">
|
||||
<link rel="import" href="elements/fxa-refresh-auth.html">
|
||||
<link rel="import" href="elements/fxa-coppa.html">
|
||||
|
||||
<script defer src="../bower_components/evt/index.js"></script>
|
||||
|
||||
<script defer src="../js/service.js"></script>
|
||||
<script defer src="../js/browser_frame.js"></script>
|
||||
<script defer src="../js/entry_sheet.js"></script>
|
||||
<script defer src="../shared/js/lazy_loader.js"></script>
|
||||
<script defer src="../shared/js/l10n.js"></script>
|
||||
<script defer src="../shared/js/settings_helper.js"></script>
|
||||
<script defer src="../shared/js/utilities.js"></script>
|
||||
<script defer src="../shared/js/smart-screen/spatial_navigator.js"></script>
|
||||
<script defer src="../shared/js/smart-screen/key_navigation_adapter.js"></script>
|
||||
<script defer src="../shared/js/sync/errors.js"></script>
|
||||
|
||||
<!-- Gaia Components -->
|
||||
<script defer src="../shared/elements/config.js"></script>
|
||||
<script defer src="../shared/elements/gaia-header/dist/gaia-header.js"></script>
|
||||
|
||||
<script defer src="js/fxam_server_request.js"></script>
|
||||
<script defer src="js/fxam_overlay.js"></script>
|
||||
<script defer src="js/fxam_error_overlay.js"></script>
|
||||
<script defer src="js/fxam_navigation.js"></script>
|
||||
<script defer src="js/fxam_ui.js"></script>
|
||||
<script defer src="js/fxam_manager.js"></script>
|
||||
<script defer src="js/fxam_states.js"></script>
|
||||
<script defer src="js/fxam_module.js"></script>
|
||||
<script defer src="js/fxam_key_navigation.js"></script>
|
||||
|
||||
</head>
|
||||
<body class="theme-settings">
|
||||
<section role="region">
|
||||
<gaia-header ignore-dir id="fxa-module-header" action="close">
|
||||
<h1 data-l10n-id="firefox-accounts-title"></h1>
|
||||
</gaia-header>
|
||||
<div class="bb-steps">
|
||||
<progress id="fxa-progress" value="" max="100"></progress>
|
||||
</div>
|
||||
<article id="step-container">
|
||||
<section id="fxa-email" is="fxa-email" class="screen"></section>
|
||||
<section id="fxa-set-password" is="fxa-set-password" class="screen fxa-password"></section>
|
||||
<section id="fxa-enter-password" is="fxa-enter-password" class="screen fxa-password"></section>
|
||||
<section id="fxa-signup-success" is="fxa-signup-success" class="screen"></section>
|
||||
<section id="fxa-signin-success" is="fxa-signin-success" class="screen"></section>
|
||||
<section id="fxa-tos" is="fxa-tos" class="screen" data-navigation="back"></section>
|
||||
<section id="fxa-pp" is="fxa-pp" class="screen" data-navigation="back"></section>
|
||||
<section id="fxa-refresh-auth" is="fxa-refresh-auth" class="screen fxa-password"></section>
|
||||
<section id="fxa-coppa" is="fxa-coppa" class="screen fxa-coppa"></section>
|
||||
</article>
|
||||
<nav role="navigation" class="horizontal navigation navigation-single-button" id="fxa-module-navigation">
|
||||
<button id="fxa-module-back" class="left" data-l10n-id="fxa-back" tabindex="-1">
|
||||
Back
|
||||
</button>
|
||||
<button id="fxa-module-next" class="recommend right" data-l10n-id="fxa-next" tabindex="-1">
|
||||
Next
|
||||
</button>
|
||||
<button id="fxa-module-done" class="recommend right" data-l10n-id="fxa-done" tabindex="-1">
|
||||
Done
|
||||
</button>
|
||||
</nav>
|
||||
</section>
|
||||
<!-- Progress overlay -->
|
||||
<section id="fxa-overlay" role="dialog">
|
||||
<progress></progress>
|
||||
<p id="fxa-overlay-msg" data-l10n-id="fxa-connecting">Connecting</p>
|
||||
</section>
|
||||
<!-- Error message overlay -->
|
||||
<form role="dialog" data-type="confirm" id="fxa-error-overlay" class="fade-in">
|
||||
<section>
|
||||
<p class="fxa-error-msg-container">
|
||||
<span id="fxa-error-title">Error message</span>
|
||||
<span id="fxa-error-msg">Error message.</span>
|
||||
</p>
|
||||
</section>
|
||||
<menu>
|
||||
<button class="full" data-l10n-id="fxa-ok" id="fxa-error-ok">OK</button>
|
||||
</menu>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
80
gaia/tv_apps/smart-system/fxa/js/fxam_error_overlay.js
Executable file
|
|
@ -0,0 +1,80 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global FxaModuleKeyNavigation */
|
||||
/* global FxaModuleUI */
|
||||
/* global HtmlHelper */
|
||||
/* global KeyEvent */
|
||||
/* exported FxaModuleErrorOverlay */
|
||||
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* FxaModuleErrorOverlay shows an error prompt to the user with the info
|
||||
* retrieved after getting an error.
|
||||
* As params, we can define Title and Message.
|
||||
*/
|
||||
|
||||
var FxaModuleErrorOverlay = {
|
||||
init: function fxam_error_overlay_init() {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
HtmlHelper.importElements(this,
|
||||
'fxa-error-overlay',
|
||||
'fxa-error-title',
|
||||
'fxa-error-msg',
|
||||
'fxa-error-ok'
|
||||
);
|
||||
|
||||
this.fxaErrorOk.addEventListener('mouseup', this.hide.bind(this));
|
||||
this.fxaErrorOk.addEventListener('keydown', e => {
|
||||
if (e.keyCode && e.keyCode === KeyEvent.DOM_VK_RETURN) {
|
||||
this.fxaErrorOk.classList.add('active');
|
||||
}
|
||||
});
|
||||
this.fxaErrorOk.addEventListener('keyup', e => {
|
||||
if (e.keyCode &&
|
||||
(e.keyCode === KeyEvent.DOM_VK_RETURN ||
|
||||
e.keyCode === KeyEvent.DOM_VK_BACK_SPACE)) {
|
||||
this.fxaErrorOk.classList.remove('active');
|
||||
this.hide();
|
||||
}
|
||||
});
|
||||
this.fxaErrorOverlay.addEventListener('submit', this.prevent);
|
||||
|
||||
this.initialized = true;
|
||||
},
|
||||
|
||||
show: function fxam_error_overlay_show(titleL10n, messageL10n) {
|
||||
this.init();
|
||||
|
||||
this.fxaErrorTitle.setAttribute('data-l10n-id', titleL10n);
|
||||
if (typeof(messageL10n) === 'object') {
|
||||
this.fxaErrorMsg.innerHTML = messageL10n.html;
|
||||
} else {
|
||||
this.fxaErrorMsg.setAttribute('data-l10n-id', messageL10n);
|
||||
}
|
||||
|
||||
this.fxaErrorOverlay.classList.add('show');
|
||||
|
||||
document.activeElement.blur();
|
||||
this.fxaErrorOk.focus();
|
||||
|
||||
FxaModuleUI.disableBackButton();
|
||||
},
|
||||
|
||||
hide: function fxam_overlay_hide() {
|
||||
this.init();
|
||||
|
||||
this.fxaErrorOverlay.classList.remove('show');
|
||||
FxaModuleKeyNavigation.enable();
|
||||
FxaModuleUI.enableBackButton();
|
||||
},
|
||||
|
||||
prevent: function fxam_prevent(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
};
|
||||
141
gaia/tv_apps/smart-system/fxa/js/fxam_errors.js
Executable file
|
|
@ -0,0 +1,141 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
/* global BrowserFrame */
|
||||
/* global EntrySheet */
|
||||
/* global ERROR_INVALID_SYNC_ACCOUNT */
|
||||
|
||||
(function(exports) {
|
||||
var Errors = {
|
||||
CONNECTION_ERROR: {
|
||||
title: 'fxa-connection-error-title',
|
||||
message: 'fxa-connection-error-message'
|
||||
},
|
||||
RESET_PASSWORD_ERROR: {
|
||||
title: 'fxa-reset-password-error-title',
|
||||
message: 'fxa-reset-password-error-message'
|
||||
},
|
||||
INVALID_EMAIL: {
|
||||
title: 'fxa-invalid-email-title',
|
||||
message: 'fxa-invalid-email-message'
|
||||
},
|
||||
INVALID_PASSWORD: {
|
||||
title: 'fxa-invalid-password-title',
|
||||
message: 'fxa-invalid-password-message'
|
||||
},
|
||||
COPPA_ERROR: {
|
||||
title: 'fxa-coppa-failure-error-title',
|
||||
message: 'fxa-coppa-failure-error-message'
|
||||
},
|
||||
COPPA_FTU_ERROR: {
|
||||
title: 'fxa-coppa-failure-error-title',
|
||||
message: 'fxa-coppa-ftu-error-message'
|
||||
},
|
||||
OFFLINE: {
|
||||
title: 'fxa-offline-error-title',
|
||||
message: 'fxa-offline-error-message'
|
||||
},
|
||||
UNKNOWN: {
|
||||
title: 'fxa-unknown-error-title',
|
||||
message: 'fxa-unknown-error-message'
|
||||
}
|
||||
};
|
||||
|
||||
// Sync specific error
|
||||
Errors[ERROR_INVALID_SYNC_ACCOUNT] = {
|
||||
title: 'fxsync-error-invalid-account',
|
||||
message: 'fxsync-error-invalid-account-explanation'
|
||||
};
|
||||
|
||||
function _getError(error) {
|
||||
var l10nKeys = Errors[error] || Errors.UNKNOWN;
|
||||
return {
|
||||
title: l10nKeys.title,
|
||||
message: error == 'COPPA_ERROR' ? _getCoppaError() : l10nKeys.message
|
||||
};
|
||||
}
|
||||
|
||||
// Tracks whether or not we have registered the coppa element.
|
||||
// Registering a custom element twice will throw an error.
|
||||
var coppaElementRegistered = false;
|
||||
|
||||
/**
|
||||
* Define a custom element for the coppa link.
|
||||
* A custom element is used as an easy way to survive the current FxA
|
||||
* error html stringification. When the link is clicked a remote
|
||||
* EntrySheet is opened.
|
||||
*/
|
||||
function registerCoppaLinkElement() {
|
||||
if (coppaElementRegistered) {
|
||||
return;
|
||||
}
|
||||
|
||||
coppaElementRegistered = true;
|
||||
var _ = navigator.mozL10n.get;
|
||||
var learnMore = _('fxa-learn-more');
|
||||
var coppaUrl = 'http://www.ftc.gov/news-events/media-resources/' +
|
||||
'protecting-consumer-privacy/kids-privacy-coppa';
|
||||
|
||||
var coppaLinkProto = Object.create(HTMLElement.prototype);
|
||||
|
||||
coppaLinkProto.createdCallback = function() {
|
||||
var template = document.createElement('template');
|
||||
template.innerHTML = `<a id="coppa-link" href="#">${learnMore}</a>`;
|
||||
|
||||
var shadow = this.createShadowRoot();
|
||||
this._template = template.content.cloneNode(true);
|
||||
|
||||
shadow.appendChild(this._template);
|
||||
|
||||
var link = shadow.getElementById('coppa-link');
|
||||
link.addEventListener('click', e => {
|
||||
e.preventDefault();
|
||||
if (this.entrySheet) {
|
||||
this.entrySheet.close();
|
||||
this.entrySheet = null;
|
||||
}
|
||||
|
||||
this.entrySheet = new EntrySheet(
|
||||
window.top.document.getElementById('screen'),
|
||||
// Prefix url with LRM character
|
||||
// This ensures truncation occurs correctly in an RTL document
|
||||
// We can remove this when bug 1154438 is fixed.
|
||||
'\u200E URL:' + coppaUrl,
|
||||
new BrowserFrame({
|
||||
url: coppaUrl,
|
||||
oop: true
|
||||
})
|
||||
);
|
||||
|
||||
this.entrySheet.open();
|
||||
});
|
||||
};
|
||||
|
||||
document.registerElement('fxa-coppa-link', {
|
||||
prototype: coppaLinkProto
|
||||
});
|
||||
}
|
||||
|
||||
function _getCoppaError() {
|
||||
registerCoppaLinkElement();
|
||||
var _ = navigator.mozL10n.get;
|
||||
|
||||
var errorText = _('fxa-coppa-failure-error-message');
|
||||
var learnMorePlaceholder = /{{\s*learnmore\s*}}/;
|
||||
var learnMoreLink = '<fxa-coppa-link></fxa-coppa-link>';
|
||||
// return as a string. fxam_error_overlay will innerHTML the whole message.
|
||||
return {
|
||||
html: errorText.replace(learnMorePlaceholder, learnMoreLink)
|
||||
};
|
||||
}
|
||||
|
||||
var FxaModuleErrors = {
|
||||
responseToParams: function fxame_responseToParams(response) {
|
||||
return response && response.error ?
|
||||
_getError(response.error) : _getError('GENERIC_ERROR');
|
||||
}
|
||||
};
|
||||
|
||||
exports.FxaModuleErrors = FxaModuleErrors;
|
||||
}(window));
|
||||
111
gaia/tv_apps/smart-system/fxa/js/fxam_key_navigation.js
Executable file
|
|
@ -0,0 +1,111 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/* global KeyNavigationAdapter */
|
||||
/* global SpatialNavigator */
|
||||
/* exported FxaModuleKeyNavigation */
|
||||
|
||||
'use strict';
|
||||
|
||||
(function(exports) {
|
||||
|
||||
function getElements(elementNames) {
|
||||
var elements = [];
|
||||
|
||||
elementNames.forEach(selector => {
|
||||
var element = document.querySelector(selector);
|
||||
if (element) {
|
||||
elements.push(element);
|
||||
}
|
||||
});
|
||||
|
||||
return elements;
|
||||
}
|
||||
|
||||
var FxaModuleKeyNavigation = {
|
||||
|
||||
enabled: false,
|
||||
|
||||
spatialNavigator: null,
|
||||
|
||||
keyNavigationAdapter: null,
|
||||
|
||||
init(elementNames) {
|
||||
var elements = elementNames ? getElements(elementNames) : null;
|
||||
|
||||
this.spatialNavigator = new SpatialNavigator(elements, {
|
||||
navigableFilter: elem => {
|
||||
return !((elem.offsetWidth <= 0 && elem.offsetHeight <= 0) ||
|
||||
elem.disabled);
|
||||
}
|
||||
});
|
||||
|
||||
this.spatialNavigator.on('focus', elem => {
|
||||
document.activeElement.blur();
|
||||
elem.focus();
|
||||
});
|
||||
|
||||
this.keyNavigationAdapter = new KeyNavigationAdapter();
|
||||
|
||||
this.keyNavigationAdapter.init();
|
||||
|
||||
this.keyNavigationAdapter.on('move', key => {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
var element = this.spatialNavigator.getFocusedElement();
|
||||
// Check if the key is pressed inside an email or password input element
|
||||
if (element.tagName === 'INPUT' &&
|
||||
(element.type === 'email' || element.type === 'password') &&
|
||||
element.value.length > 0 &&
|
||||
element.selectionStart === element.selectionEnd &&
|
||||
((key === 'left' && element.selectionStart > 0) ||
|
||||
(key === 'right' &&
|
||||
element.selectionStart < element.value.length))) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.spatialNavigator.move(key);
|
||||
});
|
||||
},
|
||||
|
||||
focus(element) {
|
||||
this.spatialNavigator.focus(element);
|
||||
},
|
||||
|
||||
add(param) {
|
||||
var elements = [];
|
||||
Array.isArray(param) ?
|
||||
elements = elements.concat(getElements(param)) :
|
||||
elements.push(document.querySelector(param));
|
||||
this.spatialNavigator.multiAdd(elements);
|
||||
this.spatialNavigator.focus(elements[0]);
|
||||
this.enabled = true;
|
||||
},
|
||||
|
||||
remove(param) {
|
||||
var elements = [];
|
||||
Array.isArray(param) ?
|
||||
elements = elements.concat(getElements(param)) :
|
||||
elements.push(document.querySelector(param));
|
||||
this.spatialNavigator.multiRemove(elements);
|
||||
if (!this.spatialNavigator.getFocusedElement()) {
|
||||
this.enabled = false;
|
||||
}
|
||||
},
|
||||
|
||||
enable() {
|
||||
this.enabled = true;
|
||||
this.spatialNavigator.focus();
|
||||
},
|
||||
|
||||
disable() {
|
||||
this.enabled = false;
|
||||
}
|
||||
};
|
||||
|
||||
exports.FxaModuleKeyNavigation = FxaModuleKeyNavigation;
|
||||
|
||||
})(window);
|
||||
58
gaia/tv_apps/smart-system/fxa/js/fxam_manager.js
Executable file
|
|
@ -0,0 +1,58 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global FxaModuleUI */
|
||||
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* FxaModuleManager is in charge of communication with the core of
|
||||
* FxAccounts, sending the info retrieved during the process if the
|
||||
* process was succeeded.
|
||||
*/
|
||||
|
||||
var FxaModuleManager = {
|
||||
paramsRetrieved: null,
|
||||
init: function fxamm_init() {
|
||||
// Some FxA UI flows require us to get some data from the platform, like
|
||||
// the account information. This data is given by FxAccountsUI in the form
|
||||
// of URL query parameters.
|
||||
var hash = window.location.hash.split('?');
|
||||
var flow = hash[0].replace('#', '');
|
||||
if (hash.length > 1) {
|
||||
var queryParams = hash[1].split('&');
|
||||
for (var i = 0; i < queryParams.length; i++) {
|
||||
var param = queryParams[i].split('=');
|
||||
if (param.length != 2) {
|
||||
console.warning('Wrong query parameter');
|
||||
continue;
|
||||
}
|
||||
this.setParam(param[0], param[1]);
|
||||
}
|
||||
}
|
||||
FxaModuleUI.init(flow);
|
||||
},
|
||||
setParam: function fxamm_setParam(key, value) {
|
||||
if (!this.paramsRetrieved) {
|
||||
this.paramsRetrieved = {};
|
||||
}
|
||||
this.paramsRetrieved[key] = value;
|
||||
},
|
||||
done: function fxamm_done() {
|
||||
window.parent.FxAccountsUI.done(FxaModuleManager.paramsRetrieved);
|
||||
},
|
||||
close: function fxamm_close(error) {
|
||||
// If there is no params retrieved, or the flow is not complete ()
|
||||
if (!this.paramsRetrieved ||
|
||||
!this.paramsRetrieved.done) {
|
||||
window.parent.FxAccountsUI.error(error);
|
||||
return;
|
||||
}
|
||||
window.parent.FxAccountsUI.done(this.paramsRetrieved);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('load', function managerLoaded() {
|
||||
window.removeEventListener('load', managerLoaded);
|
||||
FxaModuleManager.init();
|
||||
});
|
||||
68
gaia/tv_apps/smart-system/fxa/js/fxam_module.js
Executable file
|
|
@ -0,0 +1,68 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global HtmlHelper, FxaModuleOverlay, LazyLoader, FxaModuleErrors,
|
||||
FxaModuleErrorOverlay */
|
||||
/* exported FxaModule */
|
||||
|
||||
'use strict';
|
||||
|
||||
var FxaModule = (function() {
|
||||
|
||||
var Module = {
|
||||
initialized: false,
|
||||
init: function fxam_init() {
|
||||
// override this to do initialization
|
||||
// l10n note: this method is called after mozL10n.once has fired. It is
|
||||
// safe to assume strings have loaded and mozL10n.readyState is 'complete'
|
||||
// inside this function and other Module functions.
|
||||
},
|
||||
|
||||
onNext: function fxam_onNext(gotoNextStepCallback) {
|
||||
// override this to take care of when the user clicks on the "next"
|
||||
// button. Validate any inputs, talk with the backend, do processing.
|
||||
// When complete, call gotoNextStepCallback with the next state from
|
||||
// fxam_states.js
|
||||
},
|
||||
|
||||
onBack: function fxam_onBack() {
|
||||
// handle "back" button presses.
|
||||
},
|
||||
|
||||
onDone: function fxam_onDone(doneCallback) {
|
||||
doneCallback();
|
||||
},
|
||||
|
||||
importElements: function fxam_importElements() {
|
||||
var args = [].slice.call(arguments);
|
||||
// context to import into is the first argument to importElements
|
||||
args.unshift(this);
|
||||
HtmlHelper.importElements.apply(null, args);
|
||||
},
|
||||
|
||||
showErrorResponse: function fxam_showErrorResponse(response) {
|
||||
// special case: if a network error occurs during FTE,
|
||||
// we show a slightly different error message
|
||||
var resp = response;
|
||||
if (window.location.search.indexOf('isftu') != -1 &&
|
||||
resp == 'OFFLINE_ERROR') {
|
||||
resp = 'CONNECTION_ERROR';
|
||||
}
|
||||
|
||||
FxaModuleOverlay.hide();
|
||||
LazyLoader.load('js/fxam_errors.js', function() {
|
||||
var config = FxaModuleErrors.responseToParams(resp);
|
||||
FxaModuleErrorOverlay.show(config.title, config.message);
|
||||
});
|
||||
},
|
||||
|
||||
hideErrorResponse: function fxam_hideErrorResponse() {
|
||||
FxaModuleErrorOverlay.hide();
|
||||
FxaModuleOverlay.hide();
|
||||
}
|
||||
};
|
||||
|
||||
return Module;
|
||||
|
||||
}());
|
||||
|
||||
116
gaia/tv_apps/smart-system/fxa/js/fxam_navigation.js
Executable file
|
|
@ -0,0 +1,116 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global LazyLoader, FxaModuleUI, View, FxaModuleManager, FxaModuleStates,
|
||||
FxaModuleKeyNavigation */
|
||||
/* exported FxaModuleNavigation */
|
||||
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* This code controls the navigation between modules. When tapping on
|
||||
* next/back, we load the next/previous module, loading resources and
|
||||
* updating the UI. Transitions are based on hash change.
|
||||
*/
|
||||
|
||||
var FxaModuleNavigation = {
|
||||
stepCount: 0,
|
||||
currentModule: null,
|
||||
init: function fxam_nav_init(flow) {
|
||||
// Listen on hash changes for panel changes
|
||||
window.addEventListener(
|
||||
'hashchange',
|
||||
this.hashchangeHandler.bind(this),
|
||||
false
|
||||
);
|
||||
|
||||
// Load view
|
||||
LazyLoader.load('view/view_' + flow + '.js', function loaded() {
|
||||
FxaModuleUI.setMaxSteps(View.length);
|
||||
window.location.hash = View.start.id;
|
||||
|
||||
FxaModuleKeyNavigation.init();
|
||||
}.bind(this));
|
||||
},
|
||||
hashchangeHandler: function fxam_nav_hashchangeHandler() {
|
||||
if (!location.hash) {
|
||||
return;
|
||||
}
|
||||
|
||||
var match = /(#.*)(?=\?)|(#.*)$/.exec(location.hash);
|
||||
var hash = match ? match[0] : null;
|
||||
var panel = document.querySelector(hash);
|
||||
if (!panel || !panel.classList.contains('screen')) {
|
||||
// After a user presses backspace key at the first step of the flow,
|
||||
// the panel will be null and we should close the dialog.
|
||||
FxaModuleManager.close('DIALOG_CLOSED_BY_USER');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.backAnim) {
|
||||
this.stepCount--;
|
||||
this.loadStep(panel, true);
|
||||
} else {
|
||||
this.stepCount++;
|
||||
this.loadStep(panel);
|
||||
}
|
||||
},
|
||||
loadStep: function fxam_nav_loadStep(panel, back) {
|
||||
if (!panel) {
|
||||
return;
|
||||
}
|
||||
|
||||
FxaModuleUI.loadScreen({
|
||||
panel: panel,
|
||||
count: this.stepCount,
|
||||
back: this.backAnim,
|
||||
onload: function() {
|
||||
this.currentModule = window[this.moduleById(panel.id)];
|
||||
this.currentModule.init &&
|
||||
this.currentModule.init(FxaModuleManager.paramsRetrieved);
|
||||
}.bind(this),
|
||||
onanimate: function() {
|
||||
this.backAnim = false;
|
||||
this.updatingStep = false;
|
||||
this.currentModule.onAnimate &&
|
||||
this.currentModule.onAnimate();
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
back: function fxam_nav_back() {
|
||||
// Avoid multiple taps on 'back' if screen transition is not over.
|
||||
if (this.updatingStep) {
|
||||
return;
|
||||
}
|
||||
this.updatingStep = true;
|
||||
// Execute module back (if is defined)
|
||||
this.currentModule.onBack && this.currentModule.onBack();
|
||||
// Go to previous step
|
||||
this.backAnim = true;
|
||||
|
||||
window.history.back();
|
||||
},
|
||||
loadNextStep: function fxam_loadNextStep(nextStep) {
|
||||
if (!nextStep || !nextStep.id) {
|
||||
return;
|
||||
}
|
||||
|
||||
location.hash = nextStep.id;
|
||||
},
|
||||
next: function fxam_nav_next() {
|
||||
this.currentModule.onNext(this.loadNextStep.bind(this));
|
||||
},
|
||||
moduleById: function fxam_nav_moduleById(id) {
|
||||
var moduleKey = Object.keys(FxaModuleStates).filter(function(module) {
|
||||
return FxaModuleStates[module] &&
|
||||
FxaModuleStates[module].id &&
|
||||
FxaModuleStates[module].id === id;
|
||||
}).pop();
|
||||
if (moduleKey) {
|
||||
return FxaModuleStates[moduleKey].module;
|
||||
}
|
||||
},
|
||||
done: function fxam_nav_done() {
|
||||
this.currentModule.onDone(FxaModuleManager.done);
|
||||
}
|
||||
};
|
||||
41
gaia/tv_apps/smart-system/fxa/js/fxam_overlay.js
Executable file
|
|
@ -0,0 +1,41 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global HtmlHelper */
|
||||
/* exported FxaModuleOverlay */
|
||||
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* FxaModuleOverlay shows an loading prompt to the user.
|
||||
* As params, we can only a message which will be shown with
|
||||
* a spinner of 'loading'.
|
||||
*/
|
||||
|
||||
var FxaModuleOverlay = {
|
||||
init: function fxam_overlay_init() {
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
HtmlHelper.importElements(this,
|
||||
'fxa-overlay',
|
||||
'fxa-overlay-msg'
|
||||
);
|
||||
|
||||
this.initialized = true;
|
||||
},
|
||||
|
||||
show: function fxam_overlay_show(l10nId) {
|
||||
this.init();
|
||||
|
||||
this.fxaOverlayMsg.setAttribute('data-l10n-id', l10nId);
|
||||
this.fxaOverlay.classList.add('show');
|
||||
},
|
||||
|
||||
hide: function fxam_overlay_hide() {
|
||||
this.init();
|
||||
|
||||
this.fxaOverlay.classList.remove('show');
|
||||
}
|
||||
};
|
||||
114
gaia/tv_apps/smart-system/fxa/js/fxam_server_request.js
Executable file
|
|
@ -0,0 +1,114 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global SettingsHelper, FxaModuleManager, MozActivity */
|
||||
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* FxModuleServerRequest wraps the functionality exposed by the server,
|
||||
* letting our code to be shielded against changes in the API of FxA.
|
||||
*/
|
||||
|
||||
(function(exports) {
|
||||
|
||||
var pref = 'identity.fxaccounts.reset-password.url';
|
||||
var fxaSettingsHelper = SettingsHelper(pref);
|
||||
var fxaURL;
|
||||
|
||||
fxaSettingsHelper.get(function on_fxa_get_settings(url) {
|
||||
fxaURL = url;
|
||||
});
|
||||
|
||||
function _setAccountDetails(response) {
|
||||
if(response && response.user && response.user.email) {
|
||||
FxaModuleManager.setParam('done', true);
|
||||
FxaModuleManager.setParam('verified', response.user.verified);
|
||||
}
|
||||
}
|
||||
|
||||
function _ensureFxaClient(callback) {
|
||||
window.parent.LazyLoader.load('../js/fx_accounts_client.js', function() {
|
||||
callback && callback();
|
||||
});
|
||||
}
|
||||
|
||||
var FxModuleServerRequest = {
|
||||
checkEmail: function fxmsr_checkEmail(email, onsuccess, onerror) {
|
||||
_ensureFxaClient(function() {
|
||||
window.parent.FxAccountsClient.queryAccount(
|
||||
email,
|
||||
onsuccess,
|
||||
onerror);
|
||||
});
|
||||
},
|
||||
signIn: function fxmsr_signIn(email, password, onsuccess, onerror) {
|
||||
|
||||
function successHandler(response) {
|
||||
_setAccountDetails(response);
|
||||
var authenticated =
|
||||
(response && response.user && response.user.verified) || false;
|
||||
onsuccess && onsuccess({
|
||||
authenticated: authenticated
|
||||
});
|
||||
}
|
||||
|
||||
function errorHandler(response) {
|
||||
onerror && onerror(response);
|
||||
}
|
||||
|
||||
_ensureFxaClient(function signIn() {
|
||||
window.parent.FxAccountsClient.signIn(
|
||||
email,
|
||||
password,
|
||||
successHandler,
|
||||
errorHandler);
|
||||
});
|
||||
|
||||
},
|
||||
signUp: function fxmsr_signUp(email, password, onsuccess, onerror) {
|
||||
function successHandler(response) {
|
||||
_setAccountDetails(response);
|
||||
onsuccess && onsuccess(response);
|
||||
}
|
||||
|
||||
_ensureFxaClient(function signUp() {
|
||||
window.parent.FxAccountsClient.signUp(
|
||||
email,
|
||||
password,
|
||||
successHandler,
|
||||
onerror);
|
||||
});
|
||||
},
|
||||
requestPasswordReset:
|
||||
function fxmsr_requestPasswordReset(email, onsuccess, onerror) {
|
||||
var url = email ? fxaURL + '?email=' + email : fxaURL;
|
||||
var activity = new MozActivity({
|
||||
name: 'view',
|
||||
data: {
|
||||
type: 'url',
|
||||
url: url
|
||||
}
|
||||
});
|
||||
activity.onsuccess = function on_reset_success() {
|
||||
// TODO When the browser loads, it is *behind* the system app. So we
|
||||
// need to dismiss this app in order to let the user reset their
|
||||
// password.
|
||||
onsuccess && onsuccess();
|
||||
FxaModuleManager.close('DIALOG_CLOSED_BY_USER');
|
||||
};
|
||||
activity.onerror = function on_reset_error(err) {
|
||||
console.error(err);
|
||||
onerror && onerror(err);
|
||||
};
|
||||
},
|
||||
logout: function fxmsr_logout(onsuccess, onerror) {
|
||||
_ensureFxaClient(() => {
|
||||
window.parent.FxAccountsClient.logout(
|
||||
onsuccess,
|
||||
onerror);
|
||||
});
|
||||
}
|
||||
};
|
||||
exports.FxModuleServerRequest = FxModuleServerRequest;
|
||||
}(window));
|
||||
64
gaia/tv_apps/smart-system/fxa/js/fxam_states.js
Executable file
|
|
@ -0,0 +1,64 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global FxaModuleNavigation */
|
||||
/* exported FxaModuleStates */
|
||||
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* Define the states of the firefox accounts signup/signin flow.
|
||||
* The object key defines the state name, the value is the
|
||||
* URL hash of the screen to show. done is a special state that has no
|
||||
* corresponding screen.
|
||||
*/
|
||||
|
||||
var FxaModuleStates = (function() {
|
||||
return {
|
||||
ENTER_EMAIL: {
|
||||
id: 'fxa-email',
|
||||
module: 'FxaModuleEnterEmail'
|
||||
},
|
||||
SET_PASSWORD: {
|
||||
id: 'fxa-set-password',
|
||||
module: 'FxaModuleSetPassword'
|
||||
},
|
||||
ENTER_PASSWORD: {
|
||||
id: 'fxa-enter-password',
|
||||
module: 'FxaModuleEnterPassword'
|
||||
},
|
||||
SIGNUP_SUCCESS: {
|
||||
id: 'fxa-signup-success',
|
||||
module: 'FxaModuleSignupSuccess'
|
||||
},
|
||||
SIGNIN_SUCCESS: {
|
||||
id: 'fxa-signin-success',
|
||||
module: 'FxaModuleSigninSuccess'
|
||||
},
|
||||
REFRESH_AUTH: {
|
||||
id: 'fxa-refresh-auth',
|
||||
module: 'FxaModuleRefreshAuth'
|
||||
},
|
||||
COPPA: {
|
||||
id: 'fxa-coppa',
|
||||
module: 'FxaModuleCoppa'
|
||||
},
|
||||
TOS: {
|
||||
id: 'fxa-tos'
|
||||
},
|
||||
PP: {
|
||||
id: 'fxa-pp'
|
||||
},
|
||||
DONE: null,
|
||||
back: function() {
|
||||
FxaModuleNavigation.back();
|
||||
},
|
||||
setState: function setState(state) {
|
||||
if ( !(state in this) || typeof state === 'function') {
|
||||
return;
|
||||
}
|
||||
document.location.hash = state.id;
|
||||
}
|
||||
};
|
||||
}());
|
||||
|
||||
218
gaia/tv_apps/smart-system/fxa/js/fxam_ui.js
Executable file
|
|
@ -0,0 +1,218 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global HtmlHelper, FxaModule, FxaModuleManager, FxaModuleNavigation,
|
||||
LazyLoader, KeyEvent */
|
||||
/* exported FxaModuleUI */
|
||||
|
||||
'use strict';
|
||||
|
||||
const OFFLINE_TIMEOUT = 30 * 1000;
|
||||
|
||||
var FxaModuleUI = {
|
||||
maxSteps: null,
|
||||
init: function(flow) {
|
||||
// Add listeners to the main elements
|
||||
HtmlHelper.importElements(this,
|
||||
'fxa-module-header',
|
||||
'fxa-module-back',
|
||||
'fxa-module-next',
|
||||
'fxa-module-navigation',
|
||||
'fxa-module-done',
|
||||
'fxa-progress'
|
||||
);
|
||||
|
||||
this.fxaModuleHeader.addEventListener('action', function() {
|
||||
FxaModuleManager.close('DIALOG_CLOSED_BY_USER');
|
||||
});
|
||||
|
||||
this.fxaModuleBack.addEventListener('mousedown', function() {
|
||||
FxaModuleNavigation.back();
|
||||
});
|
||||
|
||||
// To enable click animation on buttons, the next and done method should
|
||||
// be invoked at mouseup or keyup event.
|
||||
this.fxaModuleNext.addEventListener('mouseup',
|
||||
FxaModuleNavigation.next.bind(FxaModuleNavigation));
|
||||
|
||||
this.fxaModuleDone.addEventListener('mouseup',
|
||||
FxaModuleNavigation.done.bind(FxaModuleNavigation));
|
||||
|
||||
this.fxaModuleNext.addEventListener('keydown', e => {
|
||||
if(e.keyCode && e.keyCode === KeyEvent.DOM_VK_RETURN) {
|
||||
this.fxaModuleNext.classList.add('active');
|
||||
}
|
||||
});
|
||||
|
||||
this.fxaModuleDone.addEventListener('keydown', e => {
|
||||
if(e.keyCode && e.keyCode === KeyEvent.DOM_VK_RETURN) {
|
||||
this.fxaModuleDone.classList.add('active');
|
||||
}
|
||||
});
|
||||
|
||||
this.fxaModuleNext.addEventListener('keyup', e => {
|
||||
if(e.keyCode && e.keyCode === KeyEvent.DOM_VK_RETURN) {
|
||||
this.fxaModuleNext.classList.remove('active');
|
||||
FxaModuleNavigation.next();
|
||||
}
|
||||
});
|
||||
|
||||
this.fxaModuleDone.addEventListener('keyup', e => {
|
||||
if(e.keyCode && e.keyCode === KeyEvent.DOM_VK_RETURN) {
|
||||
this.fxaModuleDone.classList.remove('active');
|
||||
FxaModuleNavigation.done();
|
||||
}
|
||||
});
|
||||
|
||||
// Give up if the network goes offline for more than 30 seconds straight.
|
||||
var offlineTimer;
|
||||
window.addEventListener('online', function onOnline(evt) {
|
||||
clearTimeout(offlineTimer);
|
||||
FxaModule.hideErrorResponse();
|
||||
});
|
||||
|
||||
window.addEventListener('offline', function onOffline(evt) {
|
||||
if (offlineTimer) { return; }
|
||||
offlineTimer = setTimeout(function onOfflineTimeout() {
|
||||
return FxaModule.showErrorResponse({
|
||||
error: 'OFFLINE'
|
||||
});
|
||||
}, OFFLINE_TIMEOUT);
|
||||
});
|
||||
|
||||
this.enableBackButton();
|
||||
|
||||
FxaModuleNavigation.init(flow);
|
||||
},
|
||||
setMaxSteps: function(num) {
|
||||
this.maxSteps = num;
|
||||
},
|
||||
increaseMaxStepsBy: function(inc) {
|
||||
this.maxSteps = this.maxSteps + inc;
|
||||
},
|
||||
decreaseMaxStepsBy: function(dec) {
|
||||
this.maxSteps = this.maxSteps - dec;
|
||||
},
|
||||
loadScreen: function(params) {
|
||||
var currentScreen = document.querySelector('.current');
|
||||
var nextScreen = params.panel;
|
||||
// Set progress width
|
||||
this.fxaProgress.style.width = (100 / this.maxSteps) + '%';
|
||||
// Lazy load current panel
|
||||
LazyLoader.load(nextScreen, function() {
|
||||
// If the panel contains any new script elements,
|
||||
// lazy load those as well.
|
||||
var scripts = [].slice.call(nextScreen.querySelectorAll('script'))
|
||||
.map(function(script) { return script.getAttribute('src'); });
|
||||
|
||||
// Once all scripts are loaded, load the modules/UI
|
||||
LazyLoader.load(scripts, function() {
|
||||
if (params.count > 1 && params.count < this.maxSteps) {
|
||||
this.fxaModuleNavigation.classList.remove('navigation-single-button');
|
||||
this.fxaModuleNavigation.classList.remove('navigation-back-only');
|
||||
this.fxaModuleNavigation.classList.remove('navigation-done');
|
||||
|
||||
if (nextScreen.getAttribute('data-navigation') === 'back') {
|
||||
this.fxaModuleNavigation.classList.add('navigation-back-only');
|
||||
}
|
||||
} else {
|
||||
this.fxaModuleNavigation.classList.add('navigation-single-button');
|
||||
if (params.count === this.maxSteps) {
|
||||
this.fxaModuleNavigation.classList.add('navigation-done');
|
||||
} else {
|
||||
this.fxaModuleNavigation.classList.remove('navigation-done');
|
||||
}
|
||||
}
|
||||
this.setProgressBar(params.count);
|
||||
|
||||
navigator.mozL10n.once(function() {
|
||||
// fire module's init method
|
||||
params.onload && params.onload();
|
||||
|
||||
// animate it into view - TODO unclear how nextScreen could be falsy
|
||||
if (nextScreen) {
|
||||
this._animate(currentScreen,
|
||||
nextScreen,
|
||||
params.back,
|
||||
params.onanimate);
|
||||
}
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
}.bind(this));
|
||||
},
|
||||
_animate: function(from, to, back, callback) {
|
||||
if (!to) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!from) {
|
||||
to.classList.add('current');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._inTransition(from) || this._inTransition(to)) {
|
||||
return;
|
||||
}
|
||||
|
||||
from.addEventListener('animationend', function fromAnimEnd() {
|
||||
from.removeEventListener('animationend', fromAnimEnd, false);
|
||||
from.classList.remove(back ? 'currentToRight' : 'currentToLeft');
|
||||
from.classList.remove('current');
|
||||
from.classList.remove('back');
|
||||
}, false);
|
||||
|
||||
to.addEventListener('animationend', function toAnimEnd() {
|
||||
to.removeEventListener('animationend', toAnimEnd, false);
|
||||
to.classList.remove(back ? 'leftToCurrent' : 'rightToCurrent');
|
||||
to.classList.add('current');
|
||||
callback && callback();
|
||||
}, false);
|
||||
|
||||
from.classList.add(back ? 'currentToRight' : 'currentToLeft');
|
||||
to.classList.add(back ? 'leftToCurrent' : 'rightToCurrent');
|
||||
},
|
||||
_inTransition: function(elem) {
|
||||
return elem.classList.contains('currentToRight') ||
|
||||
elem.classList.contains('currentToLeft') ||
|
||||
elem.classList.contains('rightToCurrent') ||
|
||||
elem.classList.contains('leftToCurrent') || false;
|
||||
},
|
||||
setProgressBar: function(value) {
|
||||
this.fxaProgress.value = 100 * value / this.maxSteps;
|
||||
this.fxaProgress.style.transform = 'translateX(' + 100 * (value - 1) + '%)';
|
||||
},
|
||||
setNextText: function(l10n) {
|
||||
this.fxaModuleNext.textContent = l10n;
|
||||
},
|
||||
disableNextButton: function() {
|
||||
this.fxaModuleNext.setAttribute('disabled', 'disabled');
|
||||
},
|
||||
enableNextButton: function() {
|
||||
this.fxaModuleNext.removeAttribute('disabled');
|
||||
},
|
||||
disableDoneButton: function() {
|
||||
this.fxaModuleDone.setAttribute('disabled', 'disabled');
|
||||
},
|
||||
enableDoneButton: function() {
|
||||
this.fxaModuleDone.removeAttribute('disabled');
|
||||
},
|
||||
onkeypress: function(e) {
|
||||
var inputElementTypes = ['INPUT', 'TEXTAREA'];
|
||||
var active = document.activeElement;
|
||||
var isInputElementType = inputElementTypes.indexOf(active.nodeName) !== -1;
|
||||
if (e.keyCode === KeyEvent.DOM_VK_BACK_SPACE &&
|
||||
(!isInputElementType || (isInputElementType && !active.value))) {
|
||||
FxaModuleNavigation.back();
|
||||
}
|
||||
},
|
||||
disableBackButton: function() {
|
||||
window.removeEventListener('keypress', this.onkeypress);
|
||||
},
|
||||
enableBackButton: function() {
|
||||
window.addEventListener('keypress', this.onkeypress);
|
||||
},
|
||||
focusDoneButton: function() {
|
||||
document.activeElement.blur();
|
||||
this.fxaModuleDone.focus();
|
||||
}
|
||||
};
|
||||
89
gaia/tv_apps/smart-system/fxa/js/screens/fxam_coppa.js
Executable file
|
|
@ -0,0 +1,89 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* This module allow the user to select her age from a date picker and checks
|
||||
* that she is old enough to create a Firefox account. Otherwise, it shows an
|
||||
* error.
|
||||
*/
|
||||
|
||||
/* global FxaModuleUI, FxaModule, FxaModuleStates, FxaModuleNavigation */
|
||||
/* exported FxaModuleCoppa */
|
||||
|
||||
var FxaModuleCoppa = (function() {
|
||||
|
||||
var MINIMUM_AGE = 13;
|
||||
|
||||
function _populateSelect(select) {
|
||||
var currentYear = new Date().getFullYear();
|
||||
var start = currentYear - MINIMUM_AGE;
|
||||
for (var year = start; year <= currentYear; year++) {
|
||||
var option = document.createElement('option');
|
||||
option.value= year;
|
||||
if (year == start) {
|
||||
option.setAttribute('data-l10n-id', 'fxa-age-earlier');
|
||||
option.setAttribute('data-l10n-args', '{ "year": "' + year + '" }');
|
||||
} else {
|
||||
option.textContent = year;
|
||||
}
|
||||
select.appendChild(option);
|
||||
}
|
||||
}
|
||||
|
||||
function _enableNext(age) {
|
||||
if (age === '0') {
|
||||
FxaModuleUI.disableNextButton();
|
||||
return;
|
||||
}
|
||||
FxaModuleUI.enableNextButton();
|
||||
}
|
||||
|
||||
var Module = Object.create(FxaModule);
|
||||
Module.init = function init(options) {
|
||||
this.importElements('fxa-age-select');
|
||||
|
||||
this.isFTU = !!(options && options.isftu);
|
||||
|
||||
// Initially we only have one value ("Year of birth") in the age
|
||||
// selection element.
|
||||
if (this.fxaAgeSelect.length == 1) {
|
||||
_populateSelect(this.fxaAgeSelect);
|
||||
}
|
||||
|
||||
_enableNext(this.fxaAgeSelect.value);
|
||||
|
||||
// If COPPA is part of the sign up flow, then we need to add a step to the
|
||||
// progress bar. Don't increment if the user is moving backwards, though.
|
||||
if (!FxaModuleNavigation.backAnim) {
|
||||
FxaModuleUI.increaseMaxStepsBy(1);
|
||||
}
|
||||
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.fxaAgeSelect.addEventListener('change', () => {
|
||||
_enableNext(this.fxaAgeSelect.value);
|
||||
});
|
||||
|
||||
this.initialized = true;
|
||||
};
|
||||
|
||||
Module.onNext = function onNext(gotoNextStepCallback) {
|
||||
if (new Date().getFullYear() - this.fxaAgeSelect.value < MINIMUM_AGE) {
|
||||
this.showErrorResponse({
|
||||
error: this.isFTU ? 'COPPA_FTU_ERROR' : 'COPPA_ERROR'
|
||||
});
|
||||
return;
|
||||
}
|
||||
gotoNextStepCallback(FxaModuleStates.SET_PASSWORD);
|
||||
};
|
||||
|
||||
Module.onBack = function onBack() {
|
||||
FxaModuleUI.decreaseMaxStepsBy(1);
|
||||
};
|
||||
|
||||
return Module;
|
||||
})();
|
||||
288
gaia/tv_apps/smart-system/fxa/js/screens/fxam_enter_email.js
Executable file
|
|
@ -0,0 +1,288 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global BrowserFrame */
|
||||
/* global EntrySheet */
|
||||
/* global ERROR_INVALID_SYNC_ACCOUNT */
|
||||
/* global FxaModule */
|
||||
/* global FxaModuleKeyNavigation */
|
||||
/* global FxaModuleManager */
|
||||
/* global FxaModuleNavigation */
|
||||
/* global FxaModuleOverlay */
|
||||
/* global FxModuleServerRequest */
|
||||
/* global FxaModuleStates */
|
||||
/* global FxaModuleUI */
|
||||
/* global KeyEvent */
|
||||
|
||||
/* exported FxaModuleEnterEmail */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* This module checks the validity of an email address, and if valid,
|
||||
* determines which screen to go next.
|
||||
*/
|
||||
|
||||
var FxaModuleEnterEmail = (function() {
|
||||
|
||||
var _ = null;
|
||||
var termsUrl = 'https://accounts.firefox.com/legal/terms';
|
||||
var privacyUrl = 'https://accounts.firefox.com/legal/privacy';
|
||||
|
||||
function _isEmailValid(emailEl) {
|
||||
return emailEl && emailEl.value && emailEl.validity.valid;
|
||||
}
|
||||
|
||||
function _loadSignIn(done) {
|
||||
done(FxaModuleStates.ENTER_PASSWORD);
|
||||
}
|
||||
|
||||
function _loadSignUp(done) {
|
||||
done(FxaModuleStates.SET_PASSWORD);
|
||||
}
|
||||
|
||||
function _enableNext(emailEl) {
|
||||
if (_isEmailValid(emailEl)) {
|
||||
FxaModuleUI.enableNextButton();
|
||||
} else {
|
||||
FxaModuleUI.disableNextButton();
|
||||
}
|
||||
}
|
||||
|
||||
var Module = Object.create(FxaModule);
|
||||
Module.init = function init(options) {
|
||||
_ = navigator.mozL10n.get;
|
||||
|
||||
// Cache static HTML elements
|
||||
this.importElements(
|
||||
'fxa-email-input',
|
||||
'fxa-email-clean-btn',
|
||||
'fxa-logo',
|
||||
'fxa-notice'
|
||||
);
|
||||
|
||||
// Blocks the navigation until check the condition
|
||||
_enableNext(this.fxaEmailInput);
|
||||
|
||||
if (this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// dynamically construct and localize ToS/PN notice
|
||||
// XXX This relies on the current l10n fallback mechanism which will change
|
||||
// in the future; a real solution involves DOM overlays:
|
||||
// https://bugzil.la/994357
|
||||
var noticeText = _('fxa-notice');
|
||||
var tosReplaced = noticeText.replace(
|
||||
/{{\s*tos\s*}}/,
|
||||
'<a id="fxa-terms" href="' + termsUrl + '">Terms of Service</a>'
|
||||
);
|
||||
var tosPnReplaced = tosReplaced.replace(
|
||||
/{{\s*pn\s*}}/,
|
||||
'<a id="fxa-privacy" href="' + privacyUrl + '">Privacy Notice</a>'
|
||||
);
|
||||
this.fxaNotice.innerHTML = tosPnReplaced;
|
||||
|
||||
// manually import a few elements after innerHTMLing
|
||||
this.fxaPrivacy = document.getElementById('fxa-privacy');
|
||||
this.fxaPrivacy.setAttribute('data-l10n-id', 'fxa-pn');
|
||||
this.fxaTerms = document.getElementById('fxa-terms');
|
||||
this.fxaTerms.setAttribute('data-l10n-id', 'fxa-tos');
|
||||
|
||||
this.isFTU = !!(options && options.isftu);
|
||||
|
||||
// Add listeners
|
||||
this.fxaEmailInput.addEventListener(
|
||||
'input',
|
||||
function onInput(event) {
|
||||
if(this.fxaEmailInput.value) {
|
||||
this.fxaEmailCleanBtn.classList.add('show');
|
||||
} else {
|
||||
this.fxaEmailCleanBtn.classList.remove('show');
|
||||
}
|
||||
_enableNext(event.target);
|
||||
}.bind(this)
|
||||
);
|
||||
this.fxaEmailInput.addEventListener(
|
||||
'focus',
|
||||
function onFocus() {
|
||||
this.fxaLogo.setAttribute('hidden', true);
|
||||
if(this.fxaEmailInput.value) {
|
||||
this.fxaEmailCleanBtn.classList.add('show');
|
||||
// The input can only be selected at the next event queue
|
||||
setTimeout(this.fxaEmailInput.select.bind(this.fxaEmailInput));
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
this.fxaEmailInput.addEventListener(
|
||||
'blur',
|
||||
function onBlur() {
|
||||
this.fxaLogo.removeAttribute('hidden');
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this.fxaEmailCleanBtn.addEventListener(
|
||||
'click',
|
||||
function onMouseDown(e) {
|
||||
if(e.button === 0 ||
|
||||
(e.keyCode && e.keyCode === KeyEvent.DOM_VK_RETURN)) {
|
||||
this.fxaEmailInput.value = '';
|
||||
FxaModuleKeyNavigation.focus(this.fxaEmailInput);
|
||||
this.fxaEmailCleanBtn.classList.remove('show');
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this.fxaTerms.addEventListener('click', onExternalLinkClick.bind(this));
|
||||
this.fxaPrivacy.addEventListener('click', onExternalLinkClick.bind(this));
|
||||
|
||||
function onExternalLinkClick(e) {
|
||||
/*jshint validthis:true */
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (!navigator.onLine) {
|
||||
return this.showErrorResponse({error: 'OFFLINE'});
|
||||
}
|
||||
var url = e.target.href;
|
||||
if (this.entrySheet) {
|
||||
this.entrySheet.close();
|
||||
this.entrySheet = null;
|
||||
}
|
||||
this.entrySheet = new EntrySheet(
|
||||
window.top.document.getElementById('screen'),
|
||||
// Prefix url with LRM character
|
||||
// This ensures truncation occurs correctly in an RTL document
|
||||
// We can remove this when bug 1154438 is fixed.
|
||||
'\u200E' + url,
|
||||
new BrowserFrame({
|
||||
url: url,
|
||||
oop: true
|
||||
})
|
||||
);
|
||||
this.entrySheet.open();
|
||||
}
|
||||
|
||||
document.addEventListener(
|
||||
'visibilitychange',
|
||||
onVisibilityChange.bind(this)
|
||||
);
|
||||
|
||||
function onVisibilityChange() {
|
||||
/*jshint validthis:true */
|
||||
if (document.hidden) {
|
||||
document.removeEventListener('visibilitychange', onVisibilityChange);
|
||||
if (this.entrySheet) {
|
||||
this.entrySheet.close();
|
||||
this.entrySheet = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('holdhome', hideEntrySheet.bind(this));
|
||||
window.addEventListener('home', hideEntrySheet.bind(this));
|
||||
window.addEventListener('activityrequesting', hideEntrySheet.bind(this));
|
||||
|
||||
function hideEntrySheet() {
|
||||
/*jshint validthis:true */
|
||||
if (this.entrySheet) {
|
||||
this.entrySheet.close();
|
||||
this.entrySheet = null;
|
||||
}
|
||||
window.removeEventListener('holdhome', hideEntrySheet);
|
||||
window.removeEventListener('home', hideEntrySheet);
|
||||
window.removeEventListener('activityrequesting', hideEntrySheet);
|
||||
}
|
||||
|
||||
// Ensure that pressing 'ENTER' (keycode 13) we send the form
|
||||
// as expected
|
||||
this.fxaEmailInput.addEventListener(
|
||||
'keypress',
|
||||
function onKeypress(event) {
|
||||
if (_isEmailValid(this.fxaEmailInput) && event.keyCode === 13) {
|
||||
document.activeElement.blur();
|
||||
FxaModuleNavigation.next();
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
// There are 3 reasons why using setTimeout at this place:
|
||||
// 1. Focus() only works in the setTimeout callback here
|
||||
// 2. The input will be focused first and the keyboard will be brought
|
||||
// up. We need to do this after the slide up animation of the parent
|
||||
// fxa_dialog. But the fxa iframe has no way to know when the slide up
|
||||
// animation is finished.
|
||||
// 3. Put the FxaModuleKeyNavigation.add in the onanimate callback in
|
||||
// fxam_navigation.js doesn't work, since there is no animation for the
|
||||
// first page in the flow.
|
||||
setTimeout(() => {
|
||||
FxaModuleKeyNavigation.add([
|
||||
'#fxa-email-input',
|
||||
'#fxa-email-clean-btn',
|
||||
'#fxa-module-next',
|
||||
'#fxa-privacy',
|
||||
'#fxa-terms'
|
||||
]);
|
||||
}, 500);
|
||||
|
||||
// Avoid to add listener twice
|
||||
this.initialized = true;
|
||||
};
|
||||
|
||||
Module.onAnimate = function onAnimate() {
|
||||
FxaModuleKeyNavigation.add([
|
||||
'#fxa-email-input',
|
||||
'#fxa-email-clean-btn',
|
||||
'#fxa-module-next',
|
||||
'#fxa-privacy',
|
||||
'#fxa-terms'
|
||||
]);
|
||||
};
|
||||
|
||||
Module.onNext = function onNext(gotoNextStepCallback) {
|
||||
FxaModuleOverlay.show('fxa-connecting');
|
||||
|
||||
var email = this.fxaEmailInput.value;
|
||||
|
||||
FxModuleServerRequest.checkEmail(email, response => {
|
||||
FxaModuleOverlay.hide();
|
||||
FxaModuleManager.setParam('email', email);
|
||||
if (response && response.registered) {
|
||||
FxaModuleKeyNavigation.remove([
|
||||
'#fxa-email-input',
|
||||
'#fxa-email-clean-btn',
|
||||
'#fxa-module-next',
|
||||
'#fxa-privacy',
|
||||
'#fxa-terms'
|
||||
]);
|
||||
_loadSignIn(gotoNextStepCallback);
|
||||
} else if (this.isFTU) {
|
||||
// XXX Skip COPPA verification during FTU: if a child has a mobile
|
||||
// device, we assume a parent/guardian has given it to them, which
|
||||
// implies parental consent. So, we skip to the next step in the
|
||||
// signup flow, the set-password screen. See also bug 1010598.
|
||||
_loadSignUp(gotoNextStepCallback);
|
||||
} else if (response && !response.registered) {
|
||||
// XXX On the TV we don't allow the creation of new FxA users because
|
||||
// we only use FxA on Sync and we are unable to create new Sync
|
||||
// users. So we show an error to the user, asking her to go to
|
||||
// Desktop or Android to do the creation flow.
|
||||
FxaModuleKeyNavigation.disable();
|
||||
this.showErrorResponse({error: ERROR_INVALID_SYNC_ACCOUNT});
|
||||
} else {
|
||||
FxaModuleKeyNavigation.disable();
|
||||
this.showErrorResponse({error: 'OFFLINE'});
|
||||
}
|
||||
}, error => {
|
||||
FxaModuleKeyNavigation.disable();
|
||||
this.showErrorResponse(error);
|
||||
});
|
||||
};
|
||||
|
||||
Module.onBack = function onBack() {
|
||||
FxaModuleUI.enableNextButton();
|
||||
};
|
||||
|
||||
return Module;
|
||||
|
||||
}());
|
||||
|
||||
237
gaia/tv_apps/smart-system/fxa/js/screens/fxam_enter_password.js
Executable file
|
|
@ -0,0 +1,237 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global ERROR_INVALID_SYNC_ACCOUNT */
|
||||
/* global FxaModule */
|
||||
/* global FxaModuleKeyNavigation */
|
||||
/* global FxaModuleManager */
|
||||
/* global FxaModuleNavigation */
|
||||
/* global FxaModuleOverlay */
|
||||
/* global FxModuleServerRequest */
|
||||
/* global FxaModuleUI */
|
||||
/* global KeyEvent */
|
||||
|
||||
/* exported FxaModuleEnterPassword */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* This module checks the validity of password given email address, and if
|
||||
* valid, determine which screen to go next.
|
||||
*/
|
||||
|
||||
var FxaModuleEnterPassword = (function() {
|
||||
|
||||
var _ = null;
|
||||
|
||||
function _isPasswordValid(passwordEl) {
|
||||
var passwordValue = passwordEl.value;
|
||||
return passwordValue && passwordEl.validity.valid;
|
||||
}
|
||||
|
||||
function _enableNext(passwordEl) {
|
||||
if (_isPasswordValid(passwordEl)) {
|
||||
FxaModuleUI.enableNextButton();
|
||||
} else {
|
||||
FxaModuleUI.disableNextButton();
|
||||
}
|
||||
}
|
||||
|
||||
function _cleanForm(passwordEl, passwordCheck) {
|
||||
passwordEl.value = '';
|
||||
passwordCheck.checked = false;
|
||||
passwordEl.setAttribute('type', 'password');
|
||||
}
|
||||
|
||||
function _togglePasswordVisibility() {
|
||||
/*jshint validthis:true*/
|
||||
var passwordFieldType = !!this.fxaShowPw.checked ? 'text' : 'password';
|
||||
this.fxaPwInput.setAttribute('type', passwordFieldType);
|
||||
}
|
||||
|
||||
function _forgotPassword() {
|
||||
/*jshint validthis:true*/
|
||||
var self = this;
|
||||
if (this.isFTU) {
|
||||
return this.showErrorResponse({
|
||||
error: 'RESET_PASSWORD_ERROR'
|
||||
});
|
||||
}
|
||||
// Note: we don't need to pass a success callback, but we do need an errback
|
||||
FxModuleServerRequest.requestPasswordReset(
|
||||
this.email,
|
||||
null,
|
||||
function on_reset_error() {
|
||||
self.showErrorResponse({
|
||||
error: 'UNKNOWN_ERROR'
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
var Module = Object.create(FxaModule);
|
||||
Module.init = function init(options) {
|
||||
|
||||
if (!this.initialized) {
|
||||
_ = navigator.mozL10n.get;
|
||||
// Cache DOM elements
|
||||
this.importElements(
|
||||
'fxa-hello-known-user',
|
||||
'fxa-pw-input',
|
||||
'fxa-show-pw',
|
||||
'fxa-forgot-password',
|
||||
'fxa-pw-clean-btn'
|
||||
);
|
||||
// Add listeners
|
||||
this.fxaPwInput.addEventListener(
|
||||
'input',
|
||||
function onInput(event) {
|
||||
if(this.fxaPwInput.value) {
|
||||
this.fxaPwCleanBtn.classList.add('show');
|
||||
} else {
|
||||
this.fxaPwCleanBtn.classList.remove('show');
|
||||
}
|
||||
_enableNext(event.target);
|
||||
}.bind(this)
|
||||
);
|
||||
// Ensure that pressing 'ENTER' (keycode 13) we send the form
|
||||
// as expected
|
||||
this.fxaPwInput.addEventListener(
|
||||
'keypress',
|
||||
function onKeypress(event) {
|
||||
if (_isPasswordValid(this.fxaPwInput) && event.keyCode === 13) {
|
||||
document.activeElement.blur();
|
||||
FxaModuleNavigation.done();
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this.fxaPwInput.addEventListener('focus', () => {
|
||||
setTimeout(this.fxaPwInput.select.bind(this.fxaPwInput));
|
||||
});
|
||||
|
||||
this.fxaPwCleanBtn.addEventListener(
|
||||
'click',
|
||||
function onCleanButtonClick(e) {
|
||||
if(e.button === 0 ||
|
||||
(e.keyCode && e.keyCode === KeyEvent.DOM_VK_RETURN)) {
|
||||
this.fxaPwInput.value = '';
|
||||
FxaModuleKeyNavigation.focus(this.fxaPwInput);
|
||||
this.fxaPwCleanBtn.classList.remove('show');
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this.fxaShowPw.addEventListener('keypress', e => {
|
||||
if (e.keyCode && e.keyCode === KeyEvent.DOM_VK_RETURN) {
|
||||
this.fxaShowPw.checked = !this.fxaShowPw.checked;
|
||||
_togglePasswordVisibility.bind(this)();
|
||||
}
|
||||
});
|
||||
|
||||
this.fxaShowPw.addEventListener(
|
||||
'change',
|
||||
_togglePasswordVisibility.bind(this),
|
||||
false
|
||||
);
|
||||
|
||||
this.fxaForgotPassword.addEventListener(
|
||||
'click',
|
||||
_forgotPassword.bind(this),
|
||||
false
|
||||
);
|
||||
|
||||
this.fxaForgotPassword.addEventListener('keypress', e => {
|
||||
if (e.keyCode && e.keyCode === KeyEvent.DOM_VK_RETURN) {
|
||||
_forgotPassword.bind(this)();
|
||||
}
|
||||
});
|
||||
// Avoid repeated initialization
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
if (!options || !options.email) {
|
||||
console.error('Options are not sent properly. Email not available');
|
||||
return;
|
||||
}
|
||||
|
||||
this.isFTU = !!(options && options.isftu);
|
||||
this.email = options.email;
|
||||
|
||||
var helloUserText = _('fxa-hello-user');
|
||||
helloUserText = helloUserText.replace(
|
||||
/{{\s*email\s*}}/,
|
||||
'<a id="fxa-known-user-email">' + this.email + '</a>'
|
||||
);
|
||||
this.fxaHelloKnownUser.innerHTML = helloUserText;
|
||||
|
||||
_cleanForm(
|
||||
this.fxaPwInput,
|
||||
this.fxaShowPw
|
||||
);
|
||||
|
||||
_enableNext(this.fxaPwInput);
|
||||
|
||||
// There are 3 reasons why using setTimeout at this place:
|
||||
// 1. Focus() only works in the setTimeout callback here
|
||||
// 2. The input will be focused first and the keyboard will be brought
|
||||
// up. We need to do this after the slide up animation of the parent
|
||||
// fxa_dialog. But the fxa iframe has no way to know when the slide up
|
||||
// animation is finished.
|
||||
// 3. Put the FxaModuleKeyNavigation.add in the onanimate callback in
|
||||
// fxam_navigation.js doesn't work, since there is no animation for the
|
||||
// first page in the flow.
|
||||
setTimeout(() => {
|
||||
FxaModuleKeyNavigation.add([
|
||||
'#fxa-pw-input', '#fxa-forgot-password',
|
||||
'#fxa-show-pw', '#fxa-module-done', '#fxa-pw-clean-btn']);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
Module.onBack = function onBack() {
|
||||
FxaModuleKeyNavigation.remove([
|
||||
'#fxa-pw-input', '#fxa-forgot-password',
|
||||
'#fxa-show-pw', '#fxa-module-done', '#fxa-pw-clean-btn']);
|
||||
};
|
||||
|
||||
Module.onDone = function onDone(done) {
|
||||
FxaModuleOverlay.show('fxa-connecting');
|
||||
|
||||
FxaModuleManager.setParam('success', true);
|
||||
FxModuleServerRequest.signIn(
|
||||
this.email,
|
||||
this.fxaPwInput.value,
|
||||
function onServerResponse(response) {
|
||||
FxaModuleOverlay.hide();
|
||||
FxaModuleKeyNavigation.remove([
|
||||
'#fxa-pw-input', '#fxa-forgot-password',
|
||||
'#fxa-show-pw', '#fxa-module-done', '#fxa-pw-clean-btn']);
|
||||
|
||||
if (!response.authenticated) {
|
||||
// XXX For now, because we don't allow the creation of new accounts
|
||||
// on the TV, we log the user out and show a dialog asking the
|
||||
// user to go to desktop or android to create the account and
|
||||
// retry from the TV.
|
||||
FxModuleServerRequest.logout(() => {
|
||||
this.showErrorResponse({error: ERROR_INVALID_SYNC_ACCOUNT});
|
||||
FxaModuleManager.close('DIALOG_CLOSED_BY_USER');
|
||||
}, this.showErrorResponse);
|
||||
return;
|
||||
}
|
||||
done();
|
||||
}.bind(this),
|
||||
function onError(response) {
|
||||
FxaModuleKeyNavigation.disable();
|
||||
_cleanForm(
|
||||
this.fxaPwInput,
|
||||
this.fxaShowPw
|
||||
);
|
||||
this.showErrorResponse(response);
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
return Module;
|
||||
|
||||
}());
|
||||
|
||||
199
gaia/tv_apps/smart-system/fxa/js/screens/fxam_refresh_auth.js
Executable file
|
|
@ -0,0 +1,199 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global FxaModule */
|
||||
/* global FxaModuleKeyNavigation */
|
||||
/* global FxaModuleNavigation */
|
||||
/* global FxaModuleOverlay */
|
||||
/* global FxaModuleStates */
|
||||
/* global FxaModuleUI */
|
||||
/* global FxModuleServerRequest */
|
||||
/* global KeyEvent */
|
||||
/* exported FxaModuleRefreshAuth */
|
||||
/* jshint unused:false */
|
||||
|
||||
'use strict';
|
||||
|
||||
var FxaModuleRefreshAuth = (function() {
|
||||
|
||||
function _isPasswordValid(passwordEl) {
|
||||
var passwordValue = passwordEl.value;
|
||||
return passwordValue && passwordEl.validity.valid;
|
||||
}
|
||||
|
||||
function _enableDone(passwordEl) {
|
||||
if (_isPasswordValid(passwordEl)) {
|
||||
FxaModuleUI.enableDoneButton();
|
||||
} else {
|
||||
FxaModuleUI.disableDoneButton();
|
||||
}
|
||||
}
|
||||
|
||||
function _cleanForm(passwordEl, passwordCheck) {
|
||||
passwordEl.value = '';
|
||||
passwordCheck.checked = false;
|
||||
passwordEl.setAttribute('type', 'password');
|
||||
FxaModuleUI.disableDoneButton();
|
||||
}
|
||||
|
||||
function _togglePasswordVisibility() {
|
||||
/*jshint validthis:true*/
|
||||
var passwordFieldType =
|
||||
!!this.fxaShowPwRefresh.checked ? 'text' : 'password';
|
||||
this.fxaPwInputRefresh.setAttribute('type', passwordFieldType);
|
||||
}
|
||||
|
||||
function _requestPasswordReset(email, done) {
|
||||
/*jshint validthis:true */
|
||||
FxModuleServerRequest.requestPasswordReset(
|
||||
email,
|
||||
function onSuccess(response) {
|
||||
done();
|
||||
},
|
||||
function onError(response) {
|
||||
this._showCouldNotResetPassword();
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
function _showCouldNotResetPassword() {
|
||||
/*jshint validthis:true */
|
||||
this.showErrorResponse({
|
||||
error: 'RESET_PASSWORD_ERROR'
|
||||
});
|
||||
}
|
||||
|
||||
function _forgotPassword() {
|
||||
/*jshint validthis:true */
|
||||
FxaModuleOverlay.show('fxa-requesting-password-reset');
|
||||
_requestPasswordReset.call(
|
||||
this,
|
||||
this.email,
|
||||
function() {
|
||||
FxaModuleOverlay.hide();
|
||||
FxaModuleStates.setState(FxaModuleStates.PASSWORD_RESET_SUCCESS);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
var Module = Object.create(FxaModule);
|
||||
Module.init = function init(options) {
|
||||
if (!this.initialized) {
|
||||
// Cache DOM elements.
|
||||
this.importElements(
|
||||
'fxa-pw-input-refresh',
|
||||
'fxa-show-pw-refresh',
|
||||
'fxa-forgot-password-refresh',
|
||||
'fxa-pw-clean-btn'
|
||||
);
|
||||
|
||||
this.fxaPwInputRefresh.addEventListener('input', (function(event) {
|
||||
if(this.fxaPwInputRefresh.value) {
|
||||
this.fxaPwCleanBtn.classList.add('show');
|
||||
} else {
|
||||
this.fxaPwCleanBtn.classList.remove('show');
|
||||
}
|
||||
_enableDone(event.target);
|
||||
}).bind(this));
|
||||
|
||||
this.fxaPwCleanBtn.addEventListener(
|
||||
'click',
|
||||
function onCleanButtonClick(e) {
|
||||
if(e.button === 0 ||
|
||||
(e.keyCode && e.keyCode === KeyEvent.DOM_VK_RETURN)) {
|
||||
this.fxaPwInputRefresh.value = '';
|
||||
FxaModuleKeyNavigation.focus(this.fxaPwInputRefresh);
|
||||
this.fxaPwCleanBtn.classList.remove('show');
|
||||
}
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this.fxaShowPwRefresh.addEventListener('change', (function() {
|
||||
var passwordFieldType = !!this.fxaShowPwRefresh.checked ? 'text' :
|
||||
'password';
|
||||
}).bind(this), false);
|
||||
|
||||
// Ensure that pressing 'ENTER' (keycode 13) we send the form
|
||||
// as expected
|
||||
this.fxaPwInputRefresh.addEventListener('keypress', event => {
|
||||
if (_isPasswordValid(this.fxaPwInputRefresh) && event.keyCode === 13) {
|
||||
document.activeElement.blur();
|
||||
FxaModuleNavigation.done();
|
||||
}
|
||||
});
|
||||
|
||||
this.fxaPwInputRefresh.addEventListener('focus', () => {
|
||||
setTimeout(this.fxaPwInputRefresh.select.bind(this.fxaPwInputRefresh));
|
||||
});
|
||||
|
||||
this.fxaShowPwRefresh.addEventListener('keypress', e => {
|
||||
if (e.keyCode && e.keyCode === KeyEvent.DOM_VK_RETURN) {
|
||||
this.fxaShowPwRefresh.checked = !this.fxaShowPwRefresh.checked;
|
||||
_togglePasswordVisibility.bind(this)();
|
||||
}
|
||||
});
|
||||
|
||||
this.fxaShowPwRefresh.addEventListener(
|
||||
'change',
|
||||
_togglePasswordVisibility.bind(this),
|
||||
false
|
||||
);
|
||||
|
||||
this.fxaForgotPasswordRefresh.addEventListener('click',
|
||||
_forgotPassword.bind(this),
|
||||
false);
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
if (!options || !options.email) {
|
||||
console.error('Options are not sent properly. Email not available');
|
||||
return;
|
||||
}
|
||||
|
||||
this.email = options.email;
|
||||
|
||||
_cleanForm(this.fxaPwInputRefresh, this.fxaShowPwRefresh);
|
||||
_enableDone(this.fxaPwInputRefresh);
|
||||
|
||||
// There are 3 reasons why using setTimeout at this place:
|
||||
// 1. Focus() only works in the setTimeout callback here
|
||||
// 2. The input will be focused first and the keyboard will be brought
|
||||
// up. We need to do this after the slide up animation of the parent
|
||||
// fxa_dialog. But the fxa iframe has no way to know when the slide up
|
||||
// animation is finished.
|
||||
// 3. Put the FxaModuleKeyNavigation.add in the onanimate callback in
|
||||
// fxam_navigation.js doesn't work, since there is no animation for the
|
||||
// first page in the flow.
|
||||
setTimeout(() => {
|
||||
FxaModuleKeyNavigation.add([
|
||||
'#fxa-pw-input-refresh', '#fxa-show-pw-refresh',
|
||||
'#fxa-forgot-password-refresh', '#fxa-module-done',
|
||||
'#fxa-pw-clean-btn']);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
Module.onDone = function onDone(callback) {
|
||||
FxaModuleOverlay.show('fxa-connecting');
|
||||
|
||||
FxModuleServerRequest.signIn(
|
||||
this.email,
|
||||
this.fxaPwInputRefresh.value,
|
||||
function onServerResponse(response) {
|
||||
FxaModuleOverlay.hide();
|
||||
FxaModuleKeyNavigation.remove([
|
||||
'#fxa-pw-input-refresh', '#fxa-show-pw-refresh',
|
||||
'#fxa-forgot-password-refresh', '#fxa-module-done',
|
||||
'#fxa-pw-clean-btn']);
|
||||
callback();
|
||||
}, function onError(response) {
|
||||
FxaModuleKeyNavigation.disable();
|
||||
_cleanForm(this.fxaPwInputRefresh,
|
||||
this.fxaShowPwRefresh);
|
||||
this.showErrorResponse(response);
|
||||
}.bind(this)
|
||||
);
|
||||
};
|
||||
|
||||
return Module;
|
||||
})();
|
||||
130
gaia/tv_apps/smart-system/fxa/js/screens/fxam_set_password.js
Executable file
|
|
@ -0,0 +1,130 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global FxaModuleUI, FxaModuleOverlay, FxaModule, FxModuleServerRequest,
|
||||
FxaModuleManager, FxaModuleStates */
|
||||
/* exported FxaModuleSetPassword */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Takes care of a new user's set password screen. If password is valid,
|
||||
* attempt to stage the user.
|
||||
*/
|
||||
var FxaModuleSetPassword = (function() {
|
||||
|
||||
var _ = null;
|
||||
|
||||
function _isPasswordValid(passwordEl) {
|
||||
var passwordValue = passwordEl.value;
|
||||
return passwordValue && passwordEl.validity.valid;
|
||||
}
|
||||
|
||||
function _enableNext(passwordEl) {
|
||||
if (_isPasswordValid(passwordEl)) {
|
||||
FxaModuleUI.enableNextButton();
|
||||
} else {
|
||||
FxaModuleUI.disableNextButton();
|
||||
}
|
||||
}
|
||||
|
||||
function _cleanForm(passwordEl, passwordCheck) {
|
||||
passwordEl.value = '';
|
||||
passwordCheck.checked = false;
|
||||
passwordEl.setAttribute('type', 'password');
|
||||
}
|
||||
|
||||
function _showRegistering() {
|
||||
FxaModuleOverlay.show('fxa-connecting');
|
||||
}
|
||||
|
||||
function _hideRegistering() {
|
||||
FxaModuleOverlay.hide();
|
||||
}
|
||||
|
||||
function _showUserNotCreated() {
|
||||
/*jshint validthis:true */
|
||||
this.showErrorResponse({
|
||||
error: 'CANNOT_CREATE_ACCOUNT'
|
||||
});
|
||||
}
|
||||
|
||||
function _togglePasswordVisibility() {
|
||||
/*jshint validthis:true */
|
||||
var passwordFieldType = !!this.fxaShowPwSet.checked ? 'text' : 'password';
|
||||
this.fxaPwSetInput.setAttribute('type', passwordFieldType);
|
||||
}
|
||||
|
||||
var Module = Object.create(FxaModule);
|
||||
Module.init = function init(options) {
|
||||
|
||||
if (!this.initialized) {
|
||||
// l10n manager
|
||||
_ = navigator.mozL10n.get;
|
||||
// Cache DOM elements
|
||||
this.importElements(
|
||||
'fxa-pw-set-input',
|
||||
'fxa-show-pw-set',
|
||||
'fxa-hello-user'
|
||||
);
|
||||
// Add Listeners
|
||||
this.fxaPwSetInput.addEventListener(
|
||||
'input',
|
||||
function onInput(event) {
|
||||
_enableNext(event.target);
|
||||
}
|
||||
);
|
||||
|
||||
this.fxaShowPwSet.addEventListener(
|
||||
'change',
|
||||
_togglePasswordVisibility.bind(this),
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
|
||||
this.email = options.email;
|
||||
|
||||
var helloUserText = _('fxa-hello-user');
|
||||
helloUserText = helloUserText.replace(
|
||||
/{{\s*email\s*}}/,
|
||||
'<a id="fxa-user-set-email">' + this.email + '</a>'
|
||||
);
|
||||
this.fxaHelloUser.innerHTML = helloUserText;
|
||||
|
||||
_cleanForm(
|
||||
this.fxaPwSetInput,
|
||||
this.fxaShowPwSet
|
||||
);
|
||||
|
||||
_enableNext(this.fxaPwSetInput);
|
||||
};
|
||||
|
||||
Module.onNext = function onNext(gotoNextStepCallback) {
|
||||
_showRegistering();
|
||||
FxModuleServerRequest.signUp(
|
||||
this.email,
|
||||
this.fxaPwSetInput.value,
|
||||
function(response) {
|
||||
_hideRegistering();
|
||||
|
||||
FxaModuleManager.setParam('success', true);
|
||||
|
||||
var isAccountCreated = response.accountCreated;
|
||||
|
||||
if (!isAccountCreated) {
|
||||
_showUserNotCreated.call(this);
|
||||
return;
|
||||
}
|
||||
|
||||
gotoNextStepCallback(FxaModuleStates.SIGNUP_SUCCESS);
|
||||
}.bind(this),
|
||||
this.showErrorResponse
|
||||
);
|
||||
};
|
||||
|
||||
return Module;
|
||||
|
||||
}());
|
||||
|
||||
27
gaia/tv_apps/smart-system/fxa/js/screens/fxam_signin_success.js
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global FxaModule, FxaModuleStates, FxaModuleUI */
|
||||
/* exported FxaModuleSigninSuccess */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Display the signup success message to the user.
|
||||
*/
|
||||
var FxaModuleSigninSuccess = (function() {
|
||||
|
||||
var Module = Object.create(FxaModule);
|
||||
|
||||
Module.init = function init() {
|
||||
FxaModuleUI.focusDoneButton();
|
||||
};
|
||||
|
||||
Module.onNext = function onNext(done) {
|
||||
done(FxaModuleStates.DONE);
|
||||
};
|
||||
|
||||
return Module;
|
||||
|
||||
}());
|
||||
|
||||
36
gaia/tv_apps/smart-system/fxa/js/screens/fxam_signup_success.js
Executable file
|
|
@ -0,0 +1,36 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global FxaModule, FxaModuleStates */
|
||||
/* exported FxaModuleSignupSuccess */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Display the signup success message to the user.
|
||||
*/
|
||||
var FxaModuleSignupSuccess = (function() {
|
||||
|
||||
var Module = Object.create(FxaModule);
|
||||
Module.init = function init(options) {
|
||||
options = options || {};
|
||||
this.importElements(
|
||||
'fxa-will-send-email'
|
||||
);
|
||||
|
||||
var willSendText = navigator.mozL10n.get('fxa-will-send-email');
|
||||
willSendText = willSendText.replace(
|
||||
/{{\s*email\s*}}/,
|
||||
'<strong>' + options.email + '</strong>'
|
||||
);
|
||||
this.fxaWillSendEmail.innerHTML = willSendText;
|
||||
};
|
||||
|
||||
Module.onNext = function onNext(done) {
|
||||
done(FxaModuleStates.DONE);
|
||||
};
|
||||
|
||||
return Module;
|
||||
|
||||
}());
|
||||
|
||||
107
gaia/tv_apps/smart-system/fxa/locales/fxa.en-US.properties
Executable file
|
|
@ -0,0 +1,107 @@
|
|||
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Navigation
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
|
||||
firefox-accounts-title = Firefox Account
|
||||
fxa-next = Next
|
||||
fxa-back = Back
|
||||
fxa-done = Done
|
||||
fxa-ok = OK
|
||||
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Errors
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# 'spec' references below refer to the spec attached to bug 960130
|
||||
|
||||
# this is a real error screen (7.2 in spec)
|
||||
fxa-connection-error-title = Unable to Connect
|
||||
fxa-connection-error-message = You can create an account or sign in from Settings.
|
||||
|
||||
# this is a real error screen (5.4 in spec)
|
||||
fxa-reset-password-error-title = Reset Password
|
||||
fxa-reset-password-error-message = You can reset your password from Settings.
|
||||
|
||||
# this is a real error screen (19.4 in spec)
|
||||
fxa-coppa-failure-error-title = Cannot Create Account
|
||||
fxa-coppa-failure-error-message = You must meet certain age requirements to create a Firefox Account. {{learnmore}}
|
||||
# LOCALIZATION NOTE(fxa-learn-more): This string corresponds to the {{learnmore}}
|
||||
# placeholder in fxa-coppa-failure-error-message.
|
||||
fxa-learn-more = Learn more
|
||||
|
||||
# this is a real error screen (13.2 and 17.2 in spec)
|
||||
fxa-offline-error-title = Unable to Connect
|
||||
fxa-offline-error-message = Please connect to a network from Settings.
|
||||
|
||||
# this is a real error screen (3.2 in spec)
|
||||
fxa-invalid-email-title = Error
|
||||
fxa-invalid-email-message = Please enter a valid email.
|
||||
|
||||
# this is a real error screen (5.2 / 11.1 in spec)
|
||||
fxa-invalid-password-title = Invalid Password
|
||||
fxa-invalid-password-message = Please check your password and try again.
|
||||
|
||||
# this is the fallback error
|
||||
fxa-unknown-error-title = Unable to Connect
|
||||
fxa-unknown-error-message = System unavailable, try again soon.
|
||||
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Interstitial screen
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
fxa-connecting = Connecting
|
||||
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Enter Email
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
fxa-enter-email-input.placeholder = Enter your email
|
||||
fxa-use-email-to-sign-in = Use your email address to sign in.
|
||||
fxa-notice = By proceeding, I agree to the {{tos}} and {{pn}} of Firefox cloud services.
|
||||
# LOCALIZATION NOTE(fxa-tos): This string corresponds to the {{tos}} placeholder
|
||||
# in fxa-notice.
|
||||
fxa-tos = Terms of Service
|
||||
# LOCALIZATION NOTE(fxa-pn): This string corresponds to the {{pn}} placeholder
|
||||
# in fxa-notice.
|
||||
fxa-pn = Privacy Notice
|
||||
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Set & Enter Password
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
fxa-signin = Sign In
|
||||
fxa-create-password-title = Create a Password
|
||||
fxa-enter-password-title = Enter Your Password
|
||||
fxa-hello-user = Hello, {{email}}
|
||||
fxa-password-refresh = Please enter your Firefox Account password to continue.
|
||||
fxa-password.placeholder = Password
|
||||
fxa-forgot-your-password = Forgot your password?
|
||||
fxa-show-password = Show password
|
||||
fxa-password-length-requirement = Your password must be at least 8 characters long.
|
||||
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Signin success
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
fxa-success = Success!
|
||||
fxa-ready-to-go = Your Firefox Account is ready to go!
|
||||
fxa-use-features = Now you can use features like Find My Device and apps like the Firefox Marketplace.
|
||||
fxa-access-manage-account-settings = In the future, you can access and manage your account from Settings.
|
||||
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# Signup success
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
fxa-confirm-account = Confirm Your Account
|
||||
fxa-will-send-email = We will send an email to: {{email}}
|
||||
fxa-signup-click-verification-link-in-email = Next time you check your email, click the verification link to confirm your Firefox Account.
|
||||
|
||||
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
# COPPA
|
||||
#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#=#
|
||||
fxa-age-verification = Age Verification
|
||||
fxa-age-earlier = {{year}} or earlier
|
||||
fxa-year-of-birth = Year of Birth
|
||||
1
gaia/tv_apps/smart-system/fxa/locales/locales-fxa.ini
Executable file
|
|
@ -0,0 +1 @@
|
|||
@import url(fxa.en-US.properties)
|
||||
689
gaia/tv_apps/smart-system/fxa/style/fxa.css
Executable file
|
|
@ -0,0 +1,689 @@
|
|||
/*
|
||||
* Basic and shared styles of the structure
|
||||
* */
|
||||
html, body {
|
||||
font-size: 10px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
color: #000;
|
||||
background: #f4f4f4;
|
||||
}
|
||||
|
||||
html, body * {
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
input {
|
||||
-moz-user-select: text;
|
||||
}
|
||||
|
||||
.vertical {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
section[role="region"] {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding-left: 36rem;
|
||||
padding-right: 36rem;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
body {
|
||||
background: url(bg_light.png);
|
||||
}
|
||||
|
||||
h3:-moz-dir(rtl) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
label {
|
||||
width:100%;
|
||||
display:block;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Gaia header
|
||||
* * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
gaia-header {
|
||||
position: absolute;
|
||||
top: -5rem;
|
||||
opacity: 0;
|
||||
visibility: none;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Navigation bar based on a flex model
|
||||
* * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
.navigation {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
display: block;
|
||||
padding-right: 4.5rem;
|
||||
}
|
||||
|
||||
.navigation:after {
|
||||
clear: both;
|
||||
content: "";
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Overwrite this property from Building Blocks */
|
||||
.navigation button{
|
||||
font-size: 2.4rem;
|
||||
margin-left: 0.5rem;
|
||||
transition: transform 0.06s cubic-bezier(0.25, 0, 0, 1.0) ,
|
||||
background 0.06s cubic-bezier(0.25, 0, 0, 1.0) ,
|
||||
border 0.06s cubic-bezier(0.25, 0, 0, 1.0);
|
||||
}
|
||||
|
||||
.navigation button.left {
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.navigation button.left {
|
||||
width: calc((100% / 2) - 0.5rem);
|
||||
float: left;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.navigation button.right {
|
||||
width: calc((100% / 2) - 0.5rem);
|
||||
float: right;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* When there is only one option, we remove the margin */
|
||||
|
||||
.navigation-single-button button.left {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navigation.navigation-single-button button.right {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.navigation #fxa-module-back {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navigation #fxa-module-next,
|
||||
.navigation #fxa-module-done {
|
||||
width: 23.7rem;
|
||||
height: 8rem;
|
||||
background-color: #5f6368;
|
||||
border-radius: 4rem;
|
||||
}
|
||||
|
||||
.navigation #fxa-module-next:focus,
|
||||
.navigation #fxa-module-next:hover,
|
||||
.navigation #fxa-module-done:focus,
|
||||
.navigation #fxa-module-done:hover {
|
||||
transform: scale(1.2);
|
||||
background-color: #fff;
|
||||
color: #2d2d2d;
|
||||
}
|
||||
|
||||
.navigation #fxa-module-next:active,
|
||||
.navigation #fxa-module-next.active,
|
||||
.navigation #fxa-module-done:active,
|
||||
.navigation #fxa-module-done.active {
|
||||
transform: scale(1);
|
||||
background-color: #00caf2;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.navigation-back-only button {
|
||||
margin: 0;
|
||||
font-size: 2.4rem;
|
||||
border-radius: 4rem;
|
||||
}
|
||||
|
||||
.navigation-back-only button.right {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navigation-done #fxa-module-back,
|
||||
.navigation-done #fxa-module-next {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#fxa-module-done {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navigation-done #fxa-module-done {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Steps & Container
|
||||
* All steps are contained in several views inside
|
||||
* 'step-container'. We will use HTML 'import' polyfill
|
||||
* and lazy load of the views. A view is a set of steps.
|
||||
* * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
.bb-steps {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Mirror progress bar in RTL */
|
||||
.bb-steps:-moz-dir(rtl) {
|
||||
transform: scaleX(-1);
|
||||
}
|
||||
|
||||
|
||||
#step-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ff_account,
|
||||
.screen {
|
||||
font-family: Fira Sans;
|
||||
font-size: 2.8rem;
|
||||
line-height:2.8rem;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: scroll;
|
||||
display: none;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
color: #4d4d4d;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.screen .fxa-title {
|
||||
font-size: 4.6rem;
|
||||
line-height: 4.6rem;
|
||||
margin-top: 7rem;
|
||||
margin-bottom: 2.5rem;
|
||||
text-align: start;
|
||||
font-weight: 200;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.screen .fxa-title .fax-title-separator {
|
||||
margin-left: 2rem;
|
||||
margin-right: 2rem;
|
||||
}
|
||||
|
||||
.screen p {
|
||||
text-align: start;
|
||||
line-height: 2.8rem;
|
||||
font-size: 2.8rem;
|
||||
font-weight: 100;
|
||||
font-style: italic;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.screen p:-moz-dir(rtl) {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.screen p a {
|
||||
color: #4d4d4d;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.screen input {
|
||||
height: 5rem;
|
||||
padding: 0.5rem 0;
|
||||
font-family: Fira Sans;
|
||||
color: #000000;
|
||||
border-top: none;
|
||||
border-right: none;
|
||||
border-left: none;
|
||||
border-bottom: 0.4rem solid rgb(199, 199, 199);
|
||||
background-color: transparent;
|
||||
font-size: 3.8rem;
|
||||
line-height: 3.8rem;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.screen input[type="text"],
|
||||
.screen input[type="password"],
|
||||
.screen input[type="email"] {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.screen input:focus {
|
||||
border-bottom-color: rgb(0, 202, 242);
|
||||
}
|
||||
|
||||
.screen input:invalid {
|
||||
color: rgb(185, 0, 0);
|
||||
}
|
||||
|
||||
.screen input:invalid:focus {
|
||||
border-bottom-color: rgb(130, 0, 0);
|
||||
}
|
||||
|
||||
.current {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/**
|
||||
* Email screen styles
|
||||
*/
|
||||
|
||||
#fxa-email {
|
||||
height: 80rem;
|
||||
}
|
||||
|
||||
#fxa-email-input-wrapper,
|
||||
#fxa-pw-input-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#fxa-email-input {
|
||||
width: 110.8rem;
|
||||
margin-top: 19.2rem;
|
||||
margin-left: 4.5rem;
|
||||
margin-bottom: 4.5rem;
|
||||
}
|
||||
|
||||
#fxa-email-clean-btn,
|
||||
#fxa-pw-clean-btn {
|
||||
position: absolute;
|
||||
top: 1.4rem;
|
||||
right: 0;
|
||||
display: none;
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: #ffffff;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s cubic-bezier(0.25, 0, 0, 1),
|
||||
background-color 0.2s cubic-bezier(0.25, 0, 0, 1),
|
||||
color 0.2s cubic-bezier(0.25, 0, 0, 1);
|
||||
}
|
||||
|
||||
#fxa-email-clean-btn:focus,
|
||||
#fxa-email-clean-btn:hover,
|
||||
#fxa-pw-clean-btn:focus,
|
||||
#fxa-pw-clean-btn:hover {
|
||||
transform: scale(1.2);
|
||||
background-color: #fff;
|
||||
color: black;
|
||||
/*transform-origin: 50% 50%;*/
|
||||
}
|
||||
|
||||
#fxa-email-clean-btn.show,
|
||||
#fxa-pw-clean-btn.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#fxa-email-clean-btn:before,
|
||||
#fxa-pw-clean-btn:before {
|
||||
offset-inline-end: 0;
|
||||
}
|
||||
|
||||
#fxa-email-input ~ #fxa-notice {
|
||||
margin-left: 4.5rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#fxa-notice {
|
||||
padding-left: 4.6rem;
|
||||
}
|
||||
|
||||
#fxa-notice #fxa-terms,
|
||||
#fxa-notice #fxa-privacy {
|
||||
text-decoration: underline;
|
||||
cursor: unset;
|
||||
transition-property: color;
|
||||
transition-timing-function: cubic-bezier(0.25, 0, 0, 1.0);
|
||||
transition-duration: 0.42s;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#fxa-notice #fxa-terms:hover,
|
||||
#fxa-notice #fxa-terms:focus,
|
||||
#fxa-notice #fxa-privacy:hover,
|
||||
#fxa-notice #fxa-privacy:focus {
|
||||
color: #00caf2;
|
||||
}
|
||||
|
||||
/*
|
||||
* Intro screen styles
|
||||
*/
|
||||
|
||||
#fxa-hello-user {
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
#fxa-user-email,
|
||||
#fxa-user-set-email {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/*
|
||||
* Verification Screen Styles
|
||||
*/
|
||||
|
||||
ul.fxa-verification-wrapper {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul.fxa-verification-wrapper li {
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Password screen styles
|
||||
*/
|
||||
|
||||
#fxa-enter-password,
|
||||
#fxa-refresh-auth {
|
||||
height: 80rem;
|
||||
}
|
||||
|
||||
#fxa-pw-input,
|
||||
#fxa-pw-input-refresh {
|
||||
width: 110.8rem;
|
||||
margin-top: 19.2rem;
|
||||
margin-left: 4.5rem;
|
||||
margin-bottom: 4.5rem;
|
||||
}
|
||||
|
||||
#fxa-pw-option-container {
|
||||
width: 110.8rem;
|
||||
display: flex;
|
||||
margin-left: 4.5rem;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#show-pw-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#show-pw-wrapper p {
|
||||
float: left;
|
||||
display: block;
|
||||
margin: 1.5rem 0;
|
||||
}
|
||||
|
||||
#show-pw-wrapper > label {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#show-pw-wrapper p:-moz-dir(rtl) {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#show-pw-wrapper > label:-moz-dir(rtl) {
|
||||
float: right;
|
||||
}
|
||||
|
||||
/* Animate next screen */
|
||||
.currentToLeft {
|
||||
display:block;
|
||||
animation: currentToLeft var(--transition-duration) forwards;
|
||||
}
|
||||
@keyframes currentToLeft {
|
||||
0% { transform: translateX(0); }
|
||||
100% { transform: translateX(-100%); }
|
||||
}
|
||||
.rightToCurrent {
|
||||
display:block;
|
||||
animation: rightToCurrent var(--transition-duration) forwards;
|
||||
}
|
||||
@keyframes rightToCurrent {
|
||||
0% { transform: translateX(100%); }
|
||||
100% { transform: translateX(0); }
|
||||
}
|
||||
|
||||
/* Animate prev screen */
|
||||
.leftToCurrent {
|
||||
display: block;
|
||||
animation: leftToCurrent var(--transition-duration) forwards;
|
||||
}
|
||||
@keyframes leftToCurrent {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(0); }
|
||||
}
|
||||
.currentToRight {
|
||||
display: block;
|
||||
animation: currentToRight var(--transition-duration) forwards;
|
||||
}
|
||||
@keyframes currentToRight {
|
||||
0% { transform: translateX(0); }
|
||||
100% { transform: translateX(100%); }
|
||||
}
|
||||
|
||||
.current {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.screen a {
|
||||
text-decoration: underline;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.fxa-logo {
|
||||
display: none;
|
||||
background-image: url('fxa_logo.png');
|
||||
background-size: 14rem auto;
|
||||
background-repeat: no-repeat;
|
||||
margin: 0 auto;
|
||||
width: 14rem;
|
||||
height: 15rem;
|
||||
}
|
||||
|
||||
p.fxa-envelope {
|
||||
background-image: url('fxa_envelope.png');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 7rem;
|
||||
-moz-padding-start: 8rem;
|
||||
padding-top: 1rem;
|
||||
line-height: 2rem;
|
||||
min-height: 6.4rem;
|
||||
max-width:100%;
|
||||
overflow: hidden; /* short email addresses will only take up one line*/
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Overlay:
|
||||
* IMPORTANT: Avoid to use 'visibility' or 'opacity' due
|
||||
* to performance issues. For hiding, use 'display:none;''
|
||||
* * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#fxa-overlay {
|
||||
display: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
font-size: 2rem;
|
||||
background-color: rgba(200,200,200,0.9);
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#fxa-overlay.show {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
form[role=dialog] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
form[role=dialog].show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Error overlay
|
||||
* * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#fxa-error-overlay {
|
||||
padding-bottom: 0;
|
||||
opacity: 0.9;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
#fxa-error-overlay:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#fxa-error-overlay .fxa-error-msg-container {
|
||||
margin-top: 35rem;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
border-top: none;
|
||||
font-family: Fira Sans;
|
||||
font-style: italic;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
#fxa-error-overlay .fxa-error-msg-container #fxa-error-title,
|
||||
#fxa-error-overlay .fxa-error-msg-container #fxa-error-msg {
|
||||
display: block;
|
||||
font-size: 3.8rem;
|
||||
line-height: 3.8rem;
|
||||
color: #f9f9f9;
|
||||
padding-top: 3rem;
|
||||
}
|
||||
|
||||
#fxa-error-overlay menu {
|
||||
height: 26rem;
|
||||
bottom: 32.5rem;
|
||||
padding: 0px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
#fxa-error-overlay #fxa-error-ok {
|
||||
width: 21rem;
|
||||
height: 21rem;
|
||||
background-image: none;
|
||||
font-style: italic;
|
||||
color: rgb(255, 255, 255);
|
||||
background-color: #0097b6;
|
||||
float: left;
|
||||
border: medium none;
|
||||
border-radius: 50%;
|
||||
font-size: 2.4rem;
|
||||
transform: scale(1);
|
||||
transition: transform 0.16s cubic-bezier(0.25, 0, 0, 1) 0s, background-color 0.16s cubic-bezier(0.25, 0, 0, 1) 0s, color 0.16s cubic-bezier(0.25, 0, 0, 1) 0s;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
#fxa-error-overlay #fxa-error-ok:hover,
|
||||
#fxa-error-overlay #fxa-error-ok:focus {
|
||||
color: rgb(255, 255, 255);
|
||||
outline: medium none;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
#fxa-error-overlay #fxa-error-ok:active,
|
||||
#fxa-error-overlay #fxa-error-ok.active {
|
||||
background-color: #00758c;
|
||||
color: rgb(255, 255, 255);
|
||||
outline: medium none;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Classes to ensure functionality
|
||||
*/
|
||||
|
||||
a.disabled {
|
||||
opacity: 0.3;
|
||||
pointer-events: none;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.button.icon-dialog select {
|
||||
border: none;
|
||||
background: transparent;
|
||||
font-size: 1.7rem;
|
||||
}
|
||||
|
||||
/* RTL View */
|
||||
|
||||
html[dir="rtl"] .navigation button.left,
|
||||
html[dir="rtl"] .navigation button.right,
|
||||
html[dir="rtl"] #show-pw-wrapper p,
|
||||
html[dir="rtl"] #show-pw-wrapper > label {
|
||||
float: right;
|
||||
}
|
||||
|
||||
html[dir="rtl"] p.fxa-envelope {
|
||||
background-position: 100% 0;
|
||||
}
|
||||
|
||||
/* checkbox */
|
||||
|
||||
input[type="checkbox"].settings-list-checkbox {
|
||||
position: relative;
|
||||
left: 3rem;
|
||||
}
|
||||
|
||||
input[type="checkbox"].settings-list-checkbox + label {
|
||||
width: 5rem;
|
||||
height: 5rem;
|
||||
border: 0.4rem solid #5f6368;
|
||||
border-radius: 50%;
|
||||
margin-right: 2rem;
|
||||
line-height: 5rem;
|
||||
text-align: center;
|
||||
background-color: rgb(244,244,244);
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
input[type="checkbox"].settings-list-checkbox:disabled + label,
|
||||
input[type="checkbox"].settings-list-checkbox:disabled ~ span {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
input[type="checkbox"].settings-list-checkbox:focus + label {
|
||||
transform: scale(1.2);
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
|
||||
input[type="checkbox"].settings-list-checkbox:checked + label::after {
|
||||
font-family: "gaia-icons";
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
font-size: 3rem;
|
||||
text-rendering: optimizelegibility;
|
||||
content: "tick";
|
||||
color: #00caf2;
|
||||
}
|
||||
|
||||
input[type="checkbox"].settings-list-checkbox ~ span {
|
||||
line-height: normal;
|
||||
font-size: 3rem;
|
||||
}
|
||||
BIN
gaia/tv_apps/smart-system/fxa/style/fxa_envelope.png
Executable file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
gaia/tv_apps/smart-system/fxa/style/fxa_envelope@1.5x.png
Executable file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
gaia/tv_apps/smart-system/fxa/style/fxa_envelope@2.25x.png
Executable file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
gaia/tv_apps/smart-system/fxa/style/fxa_envelope@2x.png
Executable file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
gaia/tv_apps/smart-system/fxa/style/fxa_logo.png
Executable file
|
After Width: | Height: | Size: 31 KiB |
BIN
gaia/tv_apps/smart-system/fxa/style/fxa_logo@1.5x.png
Executable file
|
After Width: | Height: | Size: 56 KiB |
BIN
gaia/tv_apps/smart-system/fxa/style/fxa_logo@2.25x.png
Executable file
|
After Width: | Height: | Size: 155 KiB |
BIN
gaia/tv_apps/smart-system/fxa/style/fxa_logo@2x.png
Executable file
|
After Width: | Height: | Size: 86 KiB |
18
gaia/tv_apps/smart-system/fxa/test/marionette/.jshintrc
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"extends": "../../../../../.jshintrc",
|
||||
"node": true,
|
||||
"predef": [
|
||||
"suite",
|
||||
"test",
|
||||
"setup",
|
||||
"teardown",
|
||||
"suiteSetup",
|
||||
"suiteTeardown",
|
||||
"marionette",
|
||||
"marionetteScriptFinished",
|
||||
"require",
|
||||
"define",
|
||||
"requireApp",
|
||||
"sinon"
|
||||
]
|
||||
}
|
||||
55
gaia/tv_apps/smart-system/fxa/test/marionette/fxa_launch_test.js
Executable file
|
|
@ -0,0 +1,55 @@
|
|||
'use strict';
|
||||
|
||||
var FxA = require('./lib/fxa');
|
||||
|
||||
marionette('Firefox Accounts Launch Tests', function() {
|
||||
var app,
|
||||
selectors,
|
||||
client = marionette.client({
|
||||
profile: {
|
||||
prefs: {
|
||||
'focusmanager.testmode': true
|
||||
},
|
||||
settings: {
|
||||
'ftu.manifestURL': FxA.FTU_ORIGIN + '/manifest.webapp'
|
||||
},
|
||||
apps: {
|
||||
'test-fxa-client.gaiamobile.org': __dirname + '/test-fxa-client'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setup(function() {
|
||||
app = new FxA(client);
|
||||
selectors = FxA.Selectors;
|
||||
});
|
||||
|
||||
//If we can enter email on first screen, that should prove successful launch
|
||||
suite('Should launch FxA flow from FxA-consuming apps:', function () {
|
||||
test('Settings app', function () {
|
||||
app.launch(FxA.SETTINGS_ORIGIN);
|
||||
app.runSettingsMenu();
|
||||
app.enterEmailNew();
|
||||
});
|
||||
|
||||
test('UITest app', function () {
|
||||
app.launch(FxA.UITEST_ORIGIN);
|
||||
app.runUITestMenu();
|
||||
app.enterEmailNew();
|
||||
});
|
||||
|
||||
test('FTU menu', function () {
|
||||
app.launch(FxA.FTU_ORIGIN);
|
||||
app.runFTUMenu();
|
||||
app.enterEmailNew();
|
||||
});
|
||||
|
||||
// This is an old version which uses the IAC API.
|
||||
// TODO: INTERMITTENT failures?
|
||||
test.skip('test-fxa-client app', function () {
|
||||
app.launch(FxA.TEST_FXA_CLIENT_ORIGIN);
|
||||
app.runFxAClientTestMenu();
|
||||
app.enterEmailNew();
|
||||
});
|
||||
});
|
||||
});
|
||||
62
gaia/tv_apps/smart-system/fxa/test/marionette/fxa_screen_flow_test.js
Executable file
|
|
@ -0,0 +1,62 @@
|
|||
'use strict';
|
||||
|
||||
var FxA = require('./lib/fxa'),
|
||||
FxAUser = require('./lib/fxa_user'),
|
||||
Server = require('./lib/server'),
|
||||
config = require('./lib/config.json');
|
||||
|
||||
marionette('Firefox Accounts Screen Flow Test (UITest app)', function() {
|
||||
var app,
|
||||
selectors,
|
||||
server,
|
||||
fxaUser,
|
||||
client = marionette.client({
|
||||
profile: {
|
||||
prefs: {
|
||||
'focusmanager.testmode': true,
|
||||
'identity.fxaccounts.auth.uri': 'http://' +
|
||||
config.SERVER_HOST + ':' +
|
||||
config.SERVER_PORT + '/' +
|
||||
config.SERVER_PATH
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
setup(function(done) {
|
||||
fxaUser = new FxAUser();
|
||||
app = new FxA(client);
|
||||
Server.create(FxA.SERVER_ARGS, function (err, _server) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
throw err;
|
||||
}
|
||||
server = _server;
|
||||
done();
|
||||
});
|
||||
selectors = FxA.Selectors;
|
||||
app.launch(FxA.UITEST_ORIGIN);
|
||||
app.runUITestMenu();
|
||||
});
|
||||
|
||||
teardown(function() {
|
||||
server.stop();
|
||||
});
|
||||
|
||||
test('should walk screen flow for new user', function () {
|
||||
app.enterEmailNew();
|
||||
app.clickNext();
|
||||
app.enterCOPPANew();
|
||||
app.clickNext();
|
||||
app.enterPasswordNew();
|
||||
app.clickNext();
|
||||
app.clickDone();
|
||||
});
|
||||
|
||||
test('should walk screen flow for existing user', function () {
|
||||
app.enterEmailExisting();
|
||||
app.clickNext();
|
||||
app.enterPasswordExisting();
|
||||
app.clickNext();
|
||||
app.clickDone();
|
||||
});
|
||||
});
|
||||
17
gaia/tv_apps/smart-system/fxa/test/marionette/lib/config.json
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"MARIONETTE_TIMEOUT": 20000,
|
||||
"USER_NEW": 0,
|
||||
"USER_EXISTING": 1,
|
||||
"USER_EXISTING_EMAIL": "kilroy_exists",
|
||||
"USER_EXISTING_PW": "123456789",
|
||||
"USERNAME_LEN": "24",
|
||||
"USER_NEW_PW": "123456789",
|
||||
"PASSWORD_HASH": "059b7beebaf4824e5395fb9eba6e8424b6ee27b43675130608502790e4af2178",
|
||||
"MAIL_HOST": "restmail.net",
|
||||
"SERVER_HOST": "localhost",
|
||||
"SERVER_PORT": 8081,
|
||||
"SERVER_PATH": "v1",
|
||||
"AUTH_SERVER_URL": "https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md#response-format",
|
||||
"DEBUG": false
|
||||
}
|
||||
|
||||
235
gaia/tv_apps/smart-system/fxa/test/marionette/lib/fxa.js
Executable file
|
|
@ -0,0 +1,235 @@
|
|||
'use strict';
|
||||
|
||||
var FxAUser = require('./fxa_user'),
|
||||
config = require('./config.json');
|
||||
|
||||
/**
|
||||
* Abstraction around FxA app.
|
||||
* 'Entrance point' app URL is passed in since FxA acts more like a
|
||||
* component than a standalone app
|
||||
* (launched by Settings, Marketplace, FTU, FMD, dev_apps, etc.)
|
||||
*
|
||||
* @param client
|
||||
* @constructor
|
||||
*/
|
||||
function FxA(client) {
|
||||
this.config = config;
|
||||
this.fxaUser = new FxAUser();
|
||||
this.client = client.scope({ searchTimeout: config.MARIONETTE_TIMEOUT});
|
||||
}
|
||||
|
||||
FxA.SETTINGS_ORIGIN = 'app://settings.gaiamobile.org';
|
||||
FxA.UITEST_ORIGIN = 'app://uitest.gaiamobile.org';
|
||||
FxA.TEST_FXA_CLIENT_ORIGIN = 'app://test-fxa-client.gaiamobile.org';
|
||||
FxA.FTU_ORIGIN = 'app://ftu.gaiamobile.org';
|
||||
FxA.SERVER_PATH = 'http://' +
|
||||
config.SERVER_HOST + ':' +
|
||||
config.SERVER_PORT + '/' +
|
||||
config.SERVER_PATH;
|
||||
FxA.SERVER_ARGS = [
|
||||
config.SERVER_HOST,
|
||||
config.SERVER_PORT,
|
||||
config.SERVER_PATH
|
||||
];
|
||||
|
||||
FxA.COPPA_YEAR = new Date().getFullYear() - 13;
|
||||
|
||||
FxA.Selectors = {
|
||||
body: 'body',
|
||||
bodyReady: 'body .view-body',
|
||||
// dev_apps/uitest
|
||||
apiFxaFrame: '#test-iframe',
|
||||
fxaFrame: '#fxa-iframe',
|
||||
tabAPI: '#API',
|
||||
fxaButton: '#mozId-fxa',
|
||||
requestButton: '#request',
|
||||
// dev_apps/test-fxa-client
|
||||
tabOpenFlow: '#openFlow',
|
||||
// apps/settings
|
||||
menuItemFxa: '#menuItem-fxa',
|
||||
menuItemFmd: '#menuItem-findmydevice',
|
||||
fxaLogin: '#fxa-login',
|
||||
fmdLogin: '#findmydevice-login',
|
||||
fxaCancelAccountConfirmation: '#fxa-cancel-confirmation',
|
||||
// apps/ftu
|
||||
forward: '#forward',
|
||||
createAccountOrLogin: '#fxa-create-account',
|
||||
// apps/findmydevice
|
||||
emailInput: '#fxa-email-input',
|
||||
fxaForgotPassword: 'fxa-forgot-password',
|
||||
pwInput: '#fxa-pw-input',
|
||||
//COPPA (age-restriction menu no longer appears,
|
||||
// but password input tag is still different)
|
||||
pwInputPostCOPPA: '#fxa-pw-set-input',
|
||||
pwSetInput: '#fxa-pw-set-input',
|
||||
pwRefresh: '#fxa-pw-input-refresh',
|
||||
COPPAElementId: '#fxa-coppa',
|
||||
COPPASelectId: '#fxa-age-select',
|
||||
COPPAOptionVal: FxA.COPPA_YEAR + ' or earlier',
|
||||
COPPAOptionValToFail: FxA.COPPA_YEAR.toString(),
|
||||
moduleNext: '#fxa-module-next',
|
||||
moduleDone: '#fxa-module-done',
|
||||
errorOK: '#fxa-error-ok'
|
||||
};
|
||||
|
||||
FxA.prototype = {
|
||||
/**
|
||||
* Launches FxA app and focuses on frame.
|
||||
*/
|
||||
launch: function (origin) {
|
||||
// do unless FTU
|
||||
if (origin.search('ftu') === -1) {
|
||||
this.client.apps.launch(origin);
|
||||
}
|
||||
this.client.apps.switchToApp(origin);
|
||||
},
|
||||
|
||||
enterInput: function (inputId, inputString) {
|
||||
this.client.helper
|
||||
.waitForElement(inputId)
|
||||
.sendKeys(inputString);
|
||||
},
|
||||
|
||||
enterCOPPANew: function () {
|
||||
var sel = FxA.Selectors.COPPASelectId;
|
||||
var inputString = FxA.Selectors.COPPAOptionVal;
|
||||
this.client.helper.tapSelectOption(sel, inputString);
|
||||
},
|
||||
|
||||
enterEmailNew: function () {
|
||||
var sel = FxA.Selectors.emailInput;
|
||||
var inputString = this.fxaUser.email(config.USER_NEW);
|
||||
this.client.helper.fillInputField(sel, inputString);
|
||||
},
|
||||
enterPasswordNew: function () {
|
||||
var sel = FxA.Selectors.pwInputPostCOPPA;
|
||||
var inputString = this.fxaUser.password(config.USER_NEW);
|
||||
this.client.helper.fillInputField(sel, inputString);
|
||||
},
|
||||
enterEmailExisting: function () {
|
||||
var sel = FxA.Selectors.emailInput;
|
||||
var inputString = this.fxaUser.email(config.USER_EXISTING);
|
||||
this.client.helper.fillInputField(sel, inputString);
|
||||
},
|
||||
enterPasswordExisting: function () {
|
||||
var sel = FxA.Selectors.pwInput;
|
||||
var inputString = this.fxaUser.password(config.USER_EXISTING);
|
||||
this.client.helper.fillInputField(sel, inputString);
|
||||
},
|
||||
onClick: function(searchSelector) {
|
||||
var element = this.client.findElement(searchSelector);
|
||||
this.client.helper
|
||||
.waitForElement(element)
|
||||
.tap();
|
||||
},
|
||||
clickNext: function() {
|
||||
var element = this.client.findElement(FxA.Selectors.moduleNext);
|
||||
var client = this.client;
|
||||
|
||||
client.waitFor(function () {
|
||||
return client.findElement(FxA.Selectors.moduleNext)
|
||||
.scriptWith(function (el) {
|
||||
return el.getAttribute('disabled') !== 'disabled';
|
||||
});
|
||||
});
|
||||
|
||||
client.helper
|
||||
.waitForElement(element)
|
||||
.tap();
|
||||
},
|
||||
clickDone: function() {
|
||||
var element = this.client.findElement(FxA.Selectors.moduleDone);
|
||||
this.client.helper
|
||||
.waitForElement(element)
|
||||
.tap();
|
||||
},
|
||||
clickTabOpenFlow: function() {
|
||||
var element = this.client.findElement(FxA.Selectors.tabOpenFlow);
|
||||
this.client.helper
|
||||
.waitForElement(element)
|
||||
.tap();
|
||||
},
|
||||
clickFxaLogin: function() {
|
||||
var element = this.client.findElement(FxA.Selectors.fxaLogin);
|
||||
this.client.helper
|
||||
.waitForElement(element)
|
||||
.tap();
|
||||
},
|
||||
clickMenuItemFxa: function() {
|
||||
var element = this.client.findElement(FxA.Selectors.menuItemFxa);
|
||||
this.client.helper
|
||||
.waitForElement(element)
|
||||
.tap();
|
||||
},
|
||||
clickRequestButton: function() {
|
||||
var element = this.client.findElement(FxA.Selectors.requestButton);
|
||||
this.client.helper
|
||||
.waitForElement(element)
|
||||
.tap();
|
||||
},
|
||||
clickTabAPI: function() {
|
||||
var element = this.client.findElement(FxA.Selectors.tabAPI);
|
||||
this.client.helper
|
||||
.waitForElement(element)
|
||||
.tap();
|
||||
},
|
||||
clickFxaButton: function() {
|
||||
var element = this.client.findElement(FxA.Selectors.fxaButton);
|
||||
this.client.helper
|
||||
.waitForElement(element)
|
||||
.tap();
|
||||
},
|
||||
switchFrame: function(frameId) {
|
||||
this.client.switchToFrame();
|
||||
var frame = this.client.findElement(frameId);
|
||||
this.client.switchToFrame(frame);
|
||||
},
|
||||
switchFrameDirect: function(frameId) {
|
||||
var frame = this.client.findElement(frameId);
|
||||
this.client.switchToFrame(frame);
|
||||
},
|
||||
clickThruPanel: function(panelId, buttonId) {
|
||||
if (panelId === '#wifi') {
|
||||
// The wifi panel will bring up a screen to show it is scanning for
|
||||
// networks. Not waiting for this to clear will blow test timing
|
||||
// and cause things to fail.
|
||||
this.client.helper.waitForElementToDisappear('#loading-overlay');
|
||||
}
|
||||
// waitForElement is used to make sure animations and page changes have
|
||||
// finished, and that the panel is displayed.
|
||||
this.client.helper.waitForElement(panelId);
|
||||
|
||||
if (buttonId) {
|
||||
var button = this.client.helper.waitForElement(buttonId);
|
||||
button.click();
|
||||
}
|
||||
},
|
||||
runSettingsMenu: function() {
|
||||
this.clickMenuItemFxa();
|
||||
this.clickFxaLogin();
|
||||
this.switchFrame(FxA.Selectors.fxaFrame);
|
||||
},
|
||||
runUITestMenu: function() {
|
||||
this.clickTabAPI();
|
||||
this.clickFxaButton();
|
||||
this.switchFrameDirect(FxA.Selectors.apiFxaFrame);
|
||||
this.clickRequestButton();
|
||||
this.switchFrame(FxA.Selectors.fxaFrame);
|
||||
},
|
||||
runFTUMenu: function() {
|
||||
this.clickThruPanel('#languages', '#forward');
|
||||
this.clickThruPanel('#wifi', '#forward');
|
||||
this.clickThruPanel('#date_and_time', '#forward');
|
||||
this.clickThruPanel('#geolocation', '#forward');
|
||||
this.clickThruPanel('#import_contacts', '#forward');
|
||||
this.clickThruPanel('#firefox_accounts',
|
||||
FxA.Selectors.createAccountOrLogin);
|
||||
this.switchFrame(FxA.Selectors.fxaFrame);
|
||||
},
|
||||
runFxAClientTestMenu: function() {
|
||||
this.clickTabOpenFlow();
|
||||
this.switchFrame(FxA.Selectors.fxaFrame);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = FxA;
|
||||
54
gaia/tv_apps/smart-system/fxa/test/marionette/lib/fxa_user.js
Executable file
|
|
@ -0,0 +1,54 @@
|
|||
'use strict';
|
||||
|
||||
var config = require('./config.json');
|
||||
|
||||
/**
|
||||
* FxA user handler for email, password & string generations
|
||||
* @constructor
|
||||
*/
|
||||
function FxAUser() {
|
||||
}
|
||||
|
||||
FxAUser.prototype = {
|
||||
_randString: function(len, charSet){
|
||||
charSet = charSet || 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
var rand = '';
|
||||
for (var i = 0; i < len; i++) {
|
||||
var randomPoz = Math.floor(Math.random() * charSet.length);
|
||||
rand += charSet.substring(randomPoz, randomPoz + 1);
|
||||
}
|
||||
return rand;
|
||||
},
|
||||
|
||||
get _uniqueUserName() {
|
||||
return this._randString(config.USERNAME_LEN);
|
||||
},
|
||||
|
||||
/**
|
||||
* @TODO bug 1052267
|
||||
* add ability to generate variety of "invalid" email strings
|
||||
*/
|
||||
email: function(userType) {
|
||||
switch(userType) {
|
||||
case config.USER_NEW:
|
||||
return this._uniqueUserName + '@' + config.MAIL_HOST;
|
||||
default:
|
||||
return config.USER_EXISTING_EMAIL + '@' + config.MAIL_HOST;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @TODO bug 1052267
|
||||
* add ability to generate variety of "invalid" password strings
|
||||
*/
|
||||
password: function(userType) {
|
||||
switch(userType) {
|
||||
case config.USER_NEW:
|
||||
return config.USER_NEW_PW;
|
||||
default:
|
||||
return config.USER_EXISTING_PW;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = FxAUser;
|
||||
39
gaia/tv_apps/smart-system/fxa/test/marionette/lib/server.js
Executable file
|
|
@ -0,0 +1,39 @@
|
|||
'use strict';
|
||||
|
||||
function Server(child) {
|
||||
this.child = child;
|
||||
}
|
||||
|
||||
Server.prototype = {
|
||||
/**
|
||||
* Sends signal to stop child process and stop server.
|
||||
*
|
||||
* TODO: Bug 1056186:
|
||||
* [FxA] Create unit tests for Firefox Accounts mock server process
|
||||
*/
|
||||
stop: function() {
|
||||
this.child.kill('SIGTERM');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Spawn the child process where the FxA mock server lives.
|
||||
*
|
||||
* @param args - [ host, port, path ]
|
||||
* @param callback
|
||||
*/
|
||||
function create(args, callback) {
|
||||
var child = require('child_process')
|
||||
.fork(__dirname + '/server_mock_fxa.js', args)
|
||||
.on('message', function(data) {
|
||||
if (Array.isArray(data) && data[0] === 'start') {
|
||||
callback(null, new Server(child));
|
||||
}
|
||||
})
|
||||
.on('error', function (err) {
|
||||
callback(err);
|
||||
});
|
||||
}
|
||||
|
||||
Server.create = create;
|
||||
module.exports = Server;
|
||||
434
gaia/tv_apps/smart-system/fxa/test/marionette/lib/server_mock_fxa.js
Executable file
|
|
@ -0,0 +1,434 @@
|
|||
'use strict';
|
||||
|
||||
var restify = require('restify'),
|
||||
ecstatic = require('ecstatic'),
|
||||
config = require('./config.json');
|
||||
|
||||
var server = restify.createServer({
|
||||
name: 'Mock Firefox Accounts Server'
|
||||
});
|
||||
|
||||
server.hostname = process.argv[2];
|
||||
server.port = process.argv[3];
|
||||
server.version = process.argv[4];
|
||||
|
||||
process.on('uncaughtException', gracefulShutdown);
|
||||
server.on('uncaughtException', gracefulShutdown);
|
||||
|
||||
function shutdown(cb) {
|
||||
try {
|
||||
server.close(cb);
|
||||
} catch (e) {
|
||||
console.error(e.stack);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
function gracefulShutdown(err) {
|
||||
if (err) {
|
||||
console.error(err.stack);
|
||||
}
|
||||
shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* authPW = 123456789
|
||||
* client accepts any hex Token the server kindly grants it
|
||||
* Note: other user types could be added: i.e. verified user
|
||||
*/
|
||||
var account = {
|
||||
'new': {
|
||||
'authPW': config.PASSWORD_HASH,
|
||||
'uid': randomString(32),
|
||||
'sessionToken': randomString(64),
|
||||
'keyFetchToken': randomString(64),
|
||||
'passwordChangeToken': randomString(64),
|
||||
'verified': false,
|
||||
'authAt': Date.now()
|
||||
},
|
||||
|
||||
'exists': {
|
||||
'email': config.USER_EXISTING_EMAIL + '@' + config.MAIL_HOST,
|
||||
'authPW': config.PASSWORD_HASH,
|
||||
'uid': randomString(32),
|
||||
'sessionToken': randomString(64),
|
||||
'keyFetchToken': randomString(64),
|
||||
'passwordChangeToken': randomString(64),
|
||||
'verified': false,
|
||||
'authAt': Date.now()
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Mock Firefox Accounts Server
|
||||
*
|
||||
* Purpose:
|
||||
* Mimic behavior of FxA server v1
|
||||
* (SHA: 29dc9abbacb3fe1b6de763f3c7bd85e63215297c)
|
||||
* with mostly 'canned' responses
|
||||
* Some APIs (i.e. for authentication) provide limited logical checking
|
||||
* for testing B2G
|
||||
*
|
||||
* Reference
|
||||
* https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md
|
||||
*/
|
||||
var Server = {
|
||||
|
||||
//set to true for verbose logging
|
||||
debug: config.DEBUG || false,
|
||||
|
||||
// allows for terminating process directly when calling server standalone
|
||||
// when calling as child process, use child.kill() from parent
|
||||
stop: function() {
|
||||
gracefulShutdown();
|
||||
console.log('SERVER STOPPED');
|
||||
},
|
||||
|
||||
start: function() {
|
||||
server.use(restify.acceptParser(server.acceptable));
|
||||
server.use(restify.queryParser());
|
||||
server.use(restify.bodyParser());
|
||||
server.use(ecstatic({ root: __dirname + '/' }));
|
||||
server.get('/', ecstatic({ root: __dirname }));
|
||||
server.listen(server.port, function() {
|
||||
var debug = Server.debug || false;
|
||||
if (debug) {
|
||||
console.log(' %s running on port: %s', server.name, server.port);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
(function(err, port) {
|
||||
Server.start();
|
||||
process.send(['start']);
|
||||
})();
|
||||
|
||||
// handle process messages
|
||||
process.on('message', function(data) {
|
||||
switch (data) {
|
||||
case 'stop':
|
||||
console.log('STOPPING MOCK SERVER');
|
||||
Server.stop();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
server.post('/v1/account/create', postAccountCreate);
|
||||
server.get('/v1/account/status', getAccountStatus);
|
||||
server.get('/v1/account/devices', getAccountDevices);
|
||||
server.get('/v1/account/keys', getAccountKeys);
|
||||
server.post('/v1/account/reset', respondEmpty);
|
||||
server.post('/v1/account/destroy', respondEmpty);
|
||||
server.post('/v1/account/login', postAccountLogin);
|
||||
server.get('/v1/session/status', respondEmpty);
|
||||
server.post('/v1/session/destroy', respondEmpty);
|
||||
server.get('/v1/recovery_email/status', getRecoveryEmailStatus);
|
||||
server.post('/v1/recovery_email/resend_code', respondEmpty);
|
||||
server.post('/v1/recovery_email/verify_code', respondEmpty);
|
||||
server.post('/v1/certificate/sign', postCertificateSign);
|
||||
server.post('/v1/password/change/start', postPasswordChangeStart);
|
||||
server.post('/v1/password/change/finish', respondEmpty);
|
||||
server.post('/v1/password/forgot/send_code', postPasswordForgotSendCode);
|
||||
server.post('/v1/password/forgot/resend_code', postPasswordForgotResendCode);
|
||||
server.post('/v1/password/forgot/verify_code', postPasswordForgotVerifyCode);
|
||||
server.get('/v1/forgot/status', getPasswordForgotStatus);
|
||||
server.post('/v1/get_random_bytes', postGetRandomBytes);
|
||||
|
||||
//catch any undefined requests
|
||||
server.get(/^\/([a-zA-Z0-9_\.~-]+)\/(.*)/, anyUndefined);
|
||||
server.post(/^\/([a-zA-Z0-9_\.~-]+)\/(.*)/, anyUndefined);
|
||||
|
||||
/************************************
|
||||
* API: Account
|
||||
************************************/
|
||||
|
||||
function postAccountCreate(req,res,next) {
|
||||
var accountSelected = getAccountType(req.body.email);
|
||||
var jsonResponse = getRespAccountCreate(accountSelected);
|
||||
printLog(req.method + ' ' + req.url, jsonResponse);
|
||||
res.send(200,jsonResponse);
|
||||
}
|
||||
|
||||
function getAccountStatus(req,res,next) {
|
||||
var jsonResponse = { 'exists': false };
|
||||
printLog(req.method + ' ' + req.url, jsonResponse);
|
||||
res.send(200,jsonResponse);
|
||||
}
|
||||
|
||||
function getAccountDevices(req,res,next) {
|
||||
var jsonResponse = {
|
||||
'devices': [
|
||||
{
|
||||
'id': '4c352927-cd4f-4a4a-a03d-7d1893d950b8',
|
||||
'type': 'computer',
|
||||
'name': 'Foxys Macbook'
|
||||
}
|
||||
]
|
||||
};
|
||||
printLog(req.method + ' ' + req.url, jsonResponse);
|
||||
res.send(200,jsonResponse);
|
||||
}
|
||||
|
||||
function getAccountKeys(req,res,next) {
|
||||
var jsonResponse = {
|
||||
'bundle': randomString(256)
|
||||
};
|
||||
printLog('GET /account/keys', jsonResponse);
|
||||
res.send(200,jsonResponse);
|
||||
}
|
||||
|
||||
/************************************
|
||||
* API: Authentication
|
||||
************************************/
|
||||
|
||||
function postAccountLogin(req,res,next) {
|
||||
var jsonResponse;
|
||||
var accountSelected = getAccountType(req.body.email);
|
||||
|
||||
if (req.body.email === account.exists.email) {
|
||||
if (req.body.authPW === account.exists.authPW) {
|
||||
jsonResponse = getRespAccountLogin(accountSelected);
|
||||
res.send(200, jsonResponse);
|
||||
} else {
|
||||
//status code 400, errno 103: incorrect password
|
||||
jsonResponse = getError(103,accountSelected);
|
||||
res.send(jsonResponse);
|
||||
}
|
||||
} else {
|
||||
//status code 400, errno 102: attempt to access a non-existent account
|
||||
jsonResponse = getError(102, accountSelected);
|
||||
res.send(jsonResponse);
|
||||
}
|
||||
printLog(req.method + ' ' + req.url, jsonResponse);
|
||||
}
|
||||
|
||||
/************************************
|
||||
* API: Recovery Email
|
||||
************************************/
|
||||
|
||||
function getRecoveryEmailStatus(req,res,next) {
|
||||
var accountSelected = getAccountType(req.body.email);
|
||||
var jsonResponse = getRespRecoveryEmail(accountSelected);
|
||||
printLog(req.method + ' ' + req.url, jsonResponse);
|
||||
res.send(200,jsonResponse);
|
||||
}
|
||||
|
||||
/************************************
|
||||
* API: Certificate Signing
|
||||
************************************/
|
||||
|
||||
function postCertificateSign(req,res,next) {
|
||||
var jsonResponse = {
|
||||
'cert': randomString(520,
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZ.abcdefghijklmnopqrstuvwxyz0123456789')
|
||||
};
|
||||
printLog(req.method + ' ' + req.url, jsonResponse);
|
||||
res.send(200,jsonResponse);
|
||||
}
|
||||
|
||||
/************************************
|
||||
* API: Password
|
||||
************************************/
|
||||
|
||||
/**
|
||||
* should verify request of format:
|
||||
* {
|
||||
* "email": "me@example.com",
|
||||
* "oldAuthPW":
|
||||
* "d486e79c9f3214b0010fe31bfb50fa6c12e1d093f7770c81c6b1c19c7ee375a6"
|
||||
* }
|
||||
*/
|
||||
function postPasswordChangeStart(req,res,next) {
|
||||
|
||||
var accountSelected = getAccountType(req.body.email);
|
||||
var jsonResponse;
|
||||
if (req.body.email === account.exists.email) {
|
||||
jsonResponse = getRespPasswordChangeStart(accountSelected);
|
||||
res.send(200, jsonResponse);
|
||||
} else {
|
||||
//status code 400, errno 102: attempt to access non-existent account
|
||||
jsonResponse = getError(102, accountSelected);
|
||||
res.send(jsonResponse);
|
||||
}
|
||||
printLog(req.method + ' ' + req.url, jsonResponse);
|
||||
res.send(200,jsonResponse);
|
||||
}
|
||||
|
||||
function postPasswordForgotSendCode(req,res,next) {
|
||||
var jsonResponse = getRespPasswordForgotSendCode();
|
||||
printLog(req.method + ' ' + req.url, jsonResponse);
|
||||
res.send(200,jsonResponse);
|
||||
}
|
||||
|
||||
function postPasswordForgotResendCode(req,res,next) {
|
||||
var jsonResponse = getRespPasswordForgotSendCode();
|
||||
printLog(req.method + ' ' + req.url, jsonResponse);
|
||||
res.send(200,jsonResponse);
|
||||
}
|
||||
|
||||
function postPasswordForgotVerifyCode(req,res,next) {
|
||||
var jsonResponse = {
|
||||
'accountResetToken': randomString(128)
|
||||
};
|
||||
printLog(req.method + ' ' + req.url, jsonResponse);
|
||||
res.send(200,jsonResponse);
|
||||
}
|
||||
|
||||
function getPasswordForgotStatus(req,res,next) {
|
||||
var jsonResponse = { 'tries': 3, 'ttl': 420 };
|
||||
printLog(req.method + ' ' + req.url, jsonResponse);
|
||||
res.send(200,jsonResponse);
|
||||
}
|
||||
|
||||
/************************************
|
||||
* API: Miscellaneous
|
||||
************************************/
|
||||
|
||||
function postGetRandomBytes(req,res,next) {
|
||||
var randomData = getRandomBytes();
|
||||
var jsonResponse = { 'data': randomData };
|
||||
printLog(req.method + ' ' + req.url, jsonResponse);
|
||||
res.send(200,jsonResponse);
|
||||
}
|
||||
|
||||
/************************************
|
||||
* API: Undefined
|
||||
* any other requests land here
|
||||
************************************/
|
||||
|
||||
function anyUndefined(req,res,next) {
|
||||
printLog(req.method + ' ' + req.url + ' (UNDEFINED)',
|
||||
'value of req is: ' + req);
|
||||
}
|
||||
|
||||
function randomString(len, charSet) {
|
||||
charSet = charSet || 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||
var rand = '';
|
||||
for (var i = 0; i < len; i++) {
|
||||
var randomPoz = Math.floor(Math.random() * charSet.length);
|
||||
rand += charSet.substring(randomPoz,randomPoz+1);
|
||||
}
|
||||
return rand;
|
||||
}
|
||||
|
||||
/**
|
||||
* mock API will random 64 char length hex strings
|
||||
* @returns {string}
|
||||
*/
|
||||
function getRandomBytes() {
|
||||
return randomString(64);
|
||||
}
|
||||
|
||||
function getAccountType(returnType) {
|
||||
switch(returnType) {
|
||||
case account.exists.email:
|
||||
return account.exists;
|
||||
default:
|
||||
return account.new;
|
||||
}
|
||||
}
|
||||
|
||||
function respondEmpty(req,res,next) {
|
||||
printLog(req.method + ' ' + req.url, {});
|
||||
res.send(200, {});
|
||||
}
|
||||
|
||||
function getRespAccountLogin(accountSelected) {
|
||||
return {
|
||||
'uid': accountSelected.uid,
|
||||
'sessionToken': accountSelected.sessionToken,
|
||||
'verified': accountSelected.verified,
|
||||
'authAt': Date.now()
|
||||
};
|
||||
}
|
||||
|
||||
function getRespRecoveryEmail(accountSelected) {
|
||||
return {
|
||||
'email': accountSelected.email,
|
||||
'verified': accountSelected.verified
|
||||
};
|
||||
}
|
||||
|
||||
function getRespAccountCreate(accountSelected) {
|
||||
return {
|
||||
'uid': accountSelected.uid,
|
||||
'sessionToken': accountSelected.sessionToken,
|
||||
'keyFetchToken': accountSelected.keyFetchToken,
|
||||
'authAt': Date.now()
|
||||
};
|
||||
}
|
||||
|
||||
function getRespPasswordChangeStart(accountSelected) {
|
||||
return {
|
||||
'keyFetchToken': accountSelected.keyFetchToken,
|
||||
'passwordChangeToken': accountSelected.passwordChangeToken,
|
||||
'verified': accountSelected.verified
|
||||
};
|
||||
}
|
||||
|
||||
function getRespPasswordForgotSendCode() {
|
||||
return {
|
||||
'passwordForgotToken': randomString(128),
|
||||
'ttl': 900,
|
||||
'codeLength': 8,
|
||||
'tries': 3
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Use:
|
||||
* var errNum = req.socket._httpMessage.statusCode;
|
||||
* @param errNum
|
||||
* @param accountSelected
|
||||
* @returns {*}
|
||||
*/
|
||||
function getError(errNum,accountSelected) {
|
||||
var info = config.AUTH_SERVER_URL;
|
||||
switch(errNum) {
|
||||
case 102:
|
||||
return {
|
||||
'code': 400,
|
||||
'errno': errNum,
|
||||
'error': 'Bad Request',
|
||||
'message': 'Attempt to access an account that does not exist',
|
||||
'info': info,
|
||||
'email': accountSelected.email,
|
||||
'log': []
|
||||
};
|
||||
|
||||
case 103:
|
||||
return {
|
||||
'code': 400,
|
||||
'errno': errNum,
|
||||
'error': 'Bad Request',
|
||||
'message': 'Incorrect password',
|
||||
'info': info,
|
||||
'email': accountSelected.email,
|
||||
'log': []
|
||||
};
|
||||
|
||||
case 999:
|
||||
return {
|
||||
'code': errNum,
|
||||
'errno': 999,
|
||||
'error': 'Not Found',
|
||||
'message': 'Unspecified error',
|
||||
'info': info,
|
||||
'log': []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Log req/res to stdout
|
||||
*/
|
||||
function printLog(title,msg) {
|
||||
if (config.DEBUG) {
|
||||
console.log('\n========================');
|
||||
console.log(title);
|
||||
console.log('========================');
|
||||
console.log(msg);
|
||||
console.log('\n\n');
|
||||
}
|
||||
}
|
||||
37
gaia/tv_apps/smart-system/fxa/test/marionette/test-fxa-client/index.html
Executable file
|
|
@ -0,0 +1,37 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>FxAccounts testing tools</title>
|
||||
<!-- We don't need localization for this app. -->
|
||||
<script defer type="text/javascript" src="/js/fxa_iac_client.js"></script>
|
||||
<script defer type="text/javascript" src="/js/app.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="style/style.css">
|
||||
</head>
|
||||
<body>
|
||||
<section role="dialog">
|
||||
<menu>
|
||||
<button id="getAccount">Get Fx Account</button>
|
||||
<button id="openFlow">Launch FxAccounts flow</button>
|
||||
<button id="logout">Logout</button>
|
||||
<div class="refresh-auth">
|
||||
<button id="refreshAuthentication">Refresh Authentication UI</button>
|
||||
<input id="accountId" type="text" value="dummy@account" x-inputmode="verbatim">
|
||||
</div>
|
||||
</menu>
|
||||
|
||||
<section id="result">
|
||||
<p id="result-text"></p>
|
||||
</section>
|
||||
|
||||
<section id="events">
|
||||
<p id="events-text"></p>
|
||||
</section>
|
||||
</section>
|
||||
<!-- Progress overlay -->
|
||||
<section id="overlay" role="dialog">
|
||||
<progress></progress>
|
||||
<p id="overlay-msg">Connecting to Firefox</p>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
124
gaia/tv_apps/smart-system/fxa/test/marionette/test-fxa-client/js/app.js
Executable file
|
|
@ -0,0 +1,124 @@
|
|||
'use strict';
|
||||
/* global FxAccountsIACHelper */
|
||||
|
||||
var hideTimer;
|
||||
var Overlay = {
|
||||
show: function fxam_overlay_show() {
|
||||
if (hideTimer) {
|
||||
return;
|
||||
}
|
||||
var overlay = document.querySelector('#overlay');
|
||||
overlay.classList.add('show');
|
||||
hideTimer = setTimeout(Overlay.hide, 2000);
|
||||
},
|
||||
hide: function fxam_overlay_hide() {
|
||||
hideTimer = clearTimeout(hideTimer);
|
||||
var overlay = document.querySelector('#overlay');
|
||||
if (!overlay) {
|
||||
return;
|
||||
}
|
||||
overlay.classList.remove('show');
|
||||
}
|
||||
};
|
||||
|
||||
var TestFxAClient = function TestFxAClient() {
|
||||
|
||||
var getFxAccountsButton, launchFxAFlowButton, logoutButton,
|
||||
refreshAuthButton, resultTextEl, resultEl, timer, eventsEl,
|
||||
eventsTextEl, eventsTimer, accountIdInput;
|
||||
|
||||
var init = function init() {
|
||||
resultEl = document.getElementById('result');
|
||||
resultTextEl = document.getElementById('result-text');
|
||||
getFxAccountsButton = document.getElementById('getAccount');
|
||||
launchFxAFlowButton = document.getElementById('openFlow');
|
||||
logoutButton = document.getElementById('logout');
|
||||
refreshAuthButton = document.getElementById('refreshAuthentication');
|
||||
eventsEl = document.getElementById('events');
|
||||
eventsTextEl = document.getElementById('events-text');
|
||||
accountIdInput = document.getElementById('accountId');
|
||||
|
||||
getFxAccountsButton.addEventListener('click', handler);
|
||||
launchFxAFlowButton.addEventListener('click', handler);
|
||||
logoutButton.addEventListener('click', handler);
|
||||
refreshAuthButton.addEventListener('click', handler);
|
||||
|
||||
FxAccountsIACHelper.addEventListener('onlogin', function() {
|
||||
showEvent('onlogin');
|
||||
});
|
||||
|
||||
FxAccountsIACHelper.addEventListener('onlogout', function() {
|
||||
showEvent('onlogout');
|
||||
});
|
||||
|
||||
FxAccountsIACHelper.addEventListener('onverifiedlogin', function() {
|
||||
showEvent('onverifiedlogin');
|
||||
});
|
||||
};
|
||||
|
||||
function _setResponse(response, errorDebug) {
|
||||
resultTextEl.innerHTML = '';
|
||||
|
||||
var text = document.createTextNode((errorDebug)? 'Error ' : 'Success :');
|
||||
resultTextEl.appendChild(text);
|
||||
var responseBeautyfied =
|
||||
document.createTextNode(JSON.stringify(response || 'No params', null, 4));
|
||||
resultTextEl.appendChild(responseBeautyfied);
|
||||
|
||||
clearTimeout(timer);
|
||||
resultEl.classList.add('show');
|
||||
timer = setTimeout(function() {
|
||||
resultEl.classList.remove('show');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
function showEvent(eventName) {
|
||||
eventsTextEl.innerHTML = '';
|
||||
|
||||
var text = document.createTextNode('Event received: ' + eventName);
|
||||
eventsTextEl.appendChild(text);
|
||||
|
||||
clearTimeout(timer);
|
||||
eventsEl.classList.add('show');
|
||||
eventsTimer = setTimeout(function() {
|
||||
eventsEl.classList.remove('show');
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
var showResponse = function showResponse(response) {
|
||||
Overlay.hide();
|
||||
_setResponse(response);
|
||||
};
|
||||
|
||||
var showError = function showResponse(response) {
|
||||
Overlay.hide();
|
||||
_setResponse(response, true);
|
||||
};
|
||||
|
||||
var handler = function handler(evt) {
|
||||
var method = evt.target.id;
|
||||
switch (method) {
|
||||
case 'openFlow':
|
||||
FxAccountsIACHelper[method](showResponse, showError);
|
||||
break;
|
||||
case 'getAccount':
|
||||
case 'logout':
|
||||
Overlay.show();
|
||||
FxAccountsIACHelper[method](showResponse, showError);
|
||||
break;
|
||||
case 'refreshAuthentication':
|
||||
FxAccountsIACHelper[method](accountIdInput.value,
|
||||
showResponse, showError);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
return {
|
||||
'init': init
|
||||
};
|
||||
|
||||
}();
|
||||
|
||||
TestFxAClient.init();
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"name": "Test FxA Client",
|
||||
"launch_path": "/index.html",
|
||||
"type": "certified",
|
||||
"developer": {
|
||||
"name": "The Gaia Team",
|
||||
"url": "https://github.com/mozilla-b2g/gaia"
|
||||
},
|
||||
"icons": {
|
||||
"120": "/style/icons/icon.png"
|
||||
},
|
||||
"orientation": "default"
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.1 KiB |
|
|
@ -0,0 +1,97 @@
|
|||
html, body {
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #000;
|
||||
/* XXX: The below overflow property is being ignored by B2G Phone
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=777672
|
||||
*/
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 6rem;
|
||||
line-height: 6rem;
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
menu {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#result {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#result.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#events {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#events.show {
|
||||
display: block;
|
||||
}
|
||||
|
||||
h1 {
|
||||
padding: 1rem 1.5rem;
|
||||
-moz-box-sizing: border-box;
|
||||
font-size: 2rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
p {
|
||||
padding: 1rem 1.5rem;
|
||||
-moz-box-sizing: border-box;
|
||||
font-size: 1.6rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Overlay:
|
||||
* IMPORTANT: Avoid to use 'visibility' or 'opacity' due
|
||||
* to performance issues. For hiding, use 'display:none;''
|
||||
* * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
#overlay {
|
||||
display: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
font-size: 2rem;
|
||||
background-color: rgba(200,200,200,0.9);
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#overlay.show {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
height: 4rem;
|
||||
line-height: 6rem;
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.refresh-auth {
|
||||
border-top: 0.1rem solid;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
19
gaia/tv_apps/smart-system/fxa/view/view_login.js
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
/* global FxaModuleStates */
|
||||
/* exported View */
|
||||
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
*
|
||||
* "View" object defines the number of steps to follow and the
|
||||
* first step of the flow. Then entire flow is self-contained, so
|
||||
* once the first step is loaded the next ones are loaded taking into
|
||||
* consideration the decision taken by the user.
|
||||
* Number of steps is needed for showing to the user the progress bar.
|
||||
*
|
||||
*/
|
||||
|
||||
var View = {
|
||||
length: 2,
|
||||
start: FxaModuleStates.ENTER_EMAIL
|
||||
};
|
||||
18
gaia/tv_apps/smart-system/fxa/view/view_refresh_auth.js
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
/* global FxaModuleStates */
|
||||
/* exported View */
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
*
|
||||
* "View" object defines the number of steps to follow and the
|
||||
* first step of the flow. Then entire flow is self-contained, so
|
||||
* once the first step is loaded the next ones are loaded taking into
|
||||
* consideration the decision taken by the user.
|
||||
* Number of steps is needed for showing to the user the progress bar.
|
||||
*
|
||||
*/
|
||||
|
||||
var View = {
|
||||
length: 1,
|
||||
start: FxaModuleStates.REFRESH_AUTH
|
||||
};
|
||||
793
gaia/tv_apps/smart-system/index.html
Executable file
|
|
@ -0,0 +1,793 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
|
||||
<meta charset="utf-8">
|
||||
<!-- Icons -->
|
||||
<link rel="stylesheet" type="text/css" href="shared/elements/gaia-icons/gaia-icons.css">
|
||||
|
||||
<!-- Init Logo -->
|
||||
<link rel="stylesheet" type="text/css" href="style/system/initlogo.css">
|
||||
|
||||
<!-- Shared code -->
|
||||
<script src="shared/js/l10n.js"></script>
|
||||
<script src="shared/js/l10n_date.js"></script>
|
||||
<script src="shared/js/idletimer.js"></script>
|
||||
<script src="shared/js/dump.js"></script>
|
||||
<script src="shared/js/sanitizer.js"></script>
|
||||
<script defer src="shared/js/gesture_detector.js"></script>
|
||||
<script defer src="shared/js/settings_listener.js"></script>
|
||||
<script defer src="shared/js/custom_dialog.js"></script>
|
||||
<script defer src="shared/js/notification_helper.js"></script>
|
||||
<!-- We need this before we can show the boot animation -->
|
||||
<script src="shared/js/async_storage.js"></script>
|
||||
<script src="shared/js/smart-screen/settings_cache.js"></script>
|
||||
<script src="shared/js/smart-screen/animations.js"></script>
|
||||
<!-- focus manager -->
|
||||
<script src="js/focus_manager.js"></script>
|
||||
|
||||
<script defer src="shared/js/smart-screen/shared_utils.js"></script>
|
||||
<script defer src="bower_components/evt/index.js"></script>
|
||||
<script defer src="shared/js/manifest_helper.js"></script>
|
||||
<script defer src="shared/js/mime_mapper.js"></script>
|
||||
<script defer src="shared/js/settings_url.js"></script>
|
||||
<script defer src="shared/js/advanced_timer.js"></script>
|
||||
<script defer src="shared/js/lazy_loader.js"></script>
|
||||
<script defer src="shared/js/screen_layout.js"></script>
|
||||
<script defer src="shared/js/iac_handler.js"></script>
|
||||
<script defer src="shared/js/utilities.js"></script>
|
||||
<script defer src="shared/js/font_size_utils.js"></script>
|
||||
<!-- XXX can be removed while gecko support navigator.mozHour12 API -->
|
||||
<script defer src="shared/js/date_time_helper.js"></script>
|
||||
<script defer src="shared/js/smart-screen/simple_key_navigation.js"></script>
|
||||
<script defer src="shared/js/smart-screen/key_navigation_adapter.js"></script>
|
||||
<script defer src="shared/js/smart-screen/f_t_e_wizard.js"></script>
|
||||
|
||||
<script defer src="js/service.js"></script>
|
||||
<script defer src="js/base_ui.js"></script>
|
||||
<script defer src="js/audio_channel_controller.js"></script>
|
||||
|
||||
<script defer src="js/orientation_manager.js"></script>
|
||||
<!--
|
||||
<script defer src="shared/js/input_mgmt/input_app_list.js"></script>
|
||||
<script defer src="shared/js/input_mgmt/input_app_list.js"></script>
|
||||
<script defer src="shared/js/keyboard_helper.js"></script>
|
||||
<script defer src="shared/js/template.js"></script>
|
||||
-->
|
||||
|
||||
<!-- Wrapper -->
|
||||
<link rel="stylesheet" type="text/css" href="style/wrapper/wrapper.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/chrome/chrome.css">
|
||||
<link rel="stylesheet" href="shared/elements/gaia_overflow_menu/style/external.css">
|
||||
<!--
|
||||
<script defer src="shared/js/bookmarks_database.js"></script>
|
||||
<script defer src="shared/elements/gaia_overflow_menu/script.js"></script>
|
||||
-->
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="style/definitions.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/attention_indicator.css">
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/smart-screen/navigation.css">
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/smart-screen/animations.css">
|
||||
|
||||
<!-- System -->
|
||||
<link rel="stylesheet" type="text/css" href="style/system/system.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/system/keyboard.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/sound_manager/sound_manager.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/ime_menu.css">
|
||||
|
||||
<!-- Include shared building blocks -->
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/confirm.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/input_areas.css">
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/switches.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/value_selector.css">
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/date_selector.css">
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/time_selector.css">
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/object_menu.css">
|
||||
|
||||
<!-- Include shared web components -->
|
||||
<link rel="stylesheet" type="text/css" href="shared/elements/gaia_progress/style/external.css">
|
||||
<link rel="stylesheet" type="text/css" href="shared/elements/gaia-theme/gaia-theme.css">
|
||||
<link rel="stylesheet" type="text/css" href="shared/elements/gaia-icons/gaia-icons.css">
|
||||
<!-- Include tv shared web components -->
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/smart-icons/smart-icons.css">
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/smart-button/style.css">
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/smart-banner/style.css">
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/smart-dialog/style.css">
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/smart-bubbles/style.css">
|
||||
<link rel="stylesheet" type="text/css" href="bower_components/smart-modal-dialog/style.css">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="style/fake-notification.css"/>
|
||||
|
||||
<script defer src="shared/elements/config.js"></script>
|
||||
<script defer src="shared/js/component_utils.js"></script>
|
||||
<!-- <script defer src="/shared/js/homescreens/confirm_dialog_helper.js"></script> -->
|
||||
<!-- <script defer src="/shared/elements/gaia_confirm/script.js"></script> -->
|
||||
<!-- <script defer src="/shared/elements/gaia_menu/script.js"></script> -->
|
||||
<script defer src="shared/elements/gaia_buttons/script.js"></script>
|
||||
<script defer src="shared/elements/gaia_progress/script.js"></script>
|
||||
<script defer src="shared/elements/gaia_subheader/script.js"></script>
|
||||
<script defer src="shared/elements/gaia-header/dist/gaia-header.js"></script>
|
||||
<!-- Include tv shared web components -->
|
||||
<script defer src="bower_components/smart-button/script.js"></script>
|
||||
<script defer src="bower_components/smart-banner/script.js"></script>
|
||||
<script defer src="bower_components/smart-dialog/script.js"></script>
|
||||
<script defer src="bower_components/smart-bubbles/script.js"></script>
|
||||
<script defer src="bower_components/smart-modal-dialog/script.js"></script>
|
||||
<script defer src="bower_components/smart-modal-dialog/smart-input-dialog.js"></script>
|
||||
|
||||
<!-- Include shared resources
|
||||
<link rel="resource" type="application/json" href="shared/resources/apn.json"/>
|
||||
<link rel="resource" type="image/png" href="shared/resources/branding/initlogo.png"/>
|
||||
<link rel="resource" type="image/png" href="shared/resources/branding/initlogo_large.png"/>
|
||||
<link rel="resource" type="audio/ogg" href="shared/resources/media/notifications/notifier_firefox.opus"/>
|
||||
-->
|
||||
|
||||
<link rel="resource" type="image/png" href="shared/resources/branding/browser_84.png"/>
|
||||
<link rel="resource" type="image/png" href="shared/resources/branding/browser_126.png"/>
|
||||
<!-- applications.js must not be deferred
|
||||
in order to catch webapps-registry-ready mozChromeEvent -->
|
||||
<script src="js/applications.js"></script>
|
||||
<!-- Not deferring the following to show startup logo/animation as soon as possible. -->
|
||||
<script src="js/logo_loader.js"></script>
|
||||
<script src="js/init_logo_handler.js"></script>
|
||||
|
||||
<script defer src="js/base_module.js"></script>
|
||||
<script defer src="js/core.js"></script>
|
||||
|
||||
<script defer src="js/screen_manager.js"></script>
|
||||
<script defer src="js/activities.js"></script>
|
||||
<script defer src="js/usb_storage.js"></script>
|
||||
<script defer src="js/external_storage_monitor.js"></script>
|
||||
<script defer src="js/bootstrap.js"></script>
|
||||
<script defer src="js/screenshot.js"></script>
|
||||
<script defer src="shared/js/async_semaphore.js"></script>
|
||||
<script defer src="js/sound_manager.js"></script>
|
||||
<script defer src="js/browser_key_event_manager.js"></script>
|
||||
<script defer src="js/hardware_buttons.js"></script>
|
||||
<script defer src="js/system_banner.js"></script>
|
||||
<script defer src="js/home_gesture.js"></script>
|
||||
<script defer src="js/wallpaper_manager.js"></script>
|
||||
<!-- WallpaperManager lazily loads this script if needed
|
||||
<script defer src="shared/js/image_utils.js"></script>
|
||||
-->
|
||||
|
||||
<!-- TTL View -->
|
||||
<link rel="stylesheet" type="text/css" href="style/ttlview/ttlview.css">
|
||||
<script defer src="js/ttlview.js"></script>
|
||||
|
||||
<!-- Accessibility -->
|
||||
<link rel="stylesheet" type="text/css" href="style/accessibility/accessibility.css">
|
||||
<script defer src="js/accessibility.js"></script>
|
||||
|
||||
<!-- Sleep Menu -->
|
||||
<link rel="stylesheet" type="text/css" href="style/sleep_menu/sleep_menu.css">
|
||||
<script defer src="js/sleep_menu.js"></script>
|
||||
|
||||
<!-- Battery Manager -->
|
||||
<link rel="stylesheet" type="text/css" href="style/battery_manager/battery_manager.css">
|
||||
<script defer src="js/battery_manager.js"></script>
|
||||
|
||||
<!-- Keyboard -->
|
||||
<script defer src="js/input_transition.js"></script>
|
||||
<script defer src="js/input_frame_manager.js"></script>
|
||||
<script defer src="js/input_layouts.js"></script>
|
||||
<script defer src="js/keyboard_manager.js"></script>
|
||||
|
||||
<!-- Action Menu -->
|
||||
<link rel="stylesheet" type="text/css" href="shared/style/action_menu.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/action_menu/action_menu_extended.css">
|
||||
<script defer src="js/action_menu.js"></script>
|
||||
|
||||
<!-- Date and Time Selector Extension -->
|
||||
<link rel="stylesheet" type="text/css" href="style/date_selector/date_selector_extended.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/time_selector/time_selector_extended.css">
|
||||
|
||||
<!-- Ime Menu -->
|
||||
<script defer src="js/ime_menu.js"></script>
|
||||
|
||||
<!-- FTU launcher -->
|
||||
<script defer src="js/ftu_launcher.js"></script>
|
||||
|
||||
<!-- FTU ping -->
|
||||
<script defer src="/shared/js/uuid.js"></script>
|
||||
<script defer src="js/ftu_ping.js"></script>
|
||||
|
||||
<!-- Airplane Mode -->
|
||||
<script defer src="js/airplane_mode.js"></script>
|
||||
|
||||
<!-- Modal Dialog -->
|
||||
<link rel="stylesheet" type="text/css" href="style/modal_dialog/prompt.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/modal_dialog/modal_dialog.css">
|
||||
<script defer src="js/modal_dialog.js"></script>
|
||||
|
||||
<!-- Authentication Dialog -->
|
||||
<link rel="stylesheet" type="text/css" href="style/authentication_dialog.css">
|
||||
|
||||
<!-- Value selector -->
|
||||
<link rel="stylesheet" type="text/css" href="style/value_selector/value_selector.css">
|
||||
<script defer src="js/value_selector/value_picker.js"></script>
|
||||
<script defer src="js/value_selector/spin_date_picker.js"></script>
|
||||
<script defer src="js/value_selector/value_selector.js"></script>
|
||||
<script defer src="shared/js/input_parser.js"></script>
|
||||
|
||||
<!-- Trusted UI -->
|
||||
<link rel="stylesheet" type="text/css" href="style/trusted_ui/trusted_ui.css">
|
||||
<script defer src="js/trusted_ui.js"></script>
|
||||
|
||||
<!-- Permission Manager -->
|
||||
<link rel="stylesheet" type="text/css" href="style/permission_manager/permission_manager.css">
|
||||
<script defer src="js/permission_manager.js"></script>
|
||||
|
||||
<!-- App Install -->
|
||||
<link rel="stylesheet" type="text/css" href="style/app_install_manager/app_install_manager.css">
|
||||
<script defer src="js/app_install_dialogs.js"></script>
|
||||
<script defer src="js/app_install_manager.js"></script>
|
||||
|
||||
<!-- Notifications -->
|
||||
<link rel="stylesheet" type="text/css" href="style/notifications/notifications.css">
|
||||
<script defer src="js/notifications.js"></script>
|
||||
<script defer src="js/interactive_notifications.js"></script>
|
||||
<script defer src="js/notification_connector.js"></script>
|
||||
|
||||
<!-- Bluetooth -->
|
||||
<script defer src="js/bluetooth.js"></script>
|
||||
<script defer src="js/bluetooth_transfer.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="style/bluetooth_transfer/bluetooth_transfer.css">
|
||||
|
||||
<!-- Wifi -->
|
||||
<script defer src="js/wifi.js"></script>
|
||||
|
||||
<!-- Update Manager -->
|
||||
<script defer src="js/updatable.js"></script>
|
||||
<script defer src="js/update_manager.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="style/update_manager/update_manager.css">
|
||||
|
||||
<!-- Media Recording Logic without UI-->
|
||||
<script defer src="js/media_recording.js"></script>
|
||||
|
||||
<!-- Theme and localization -->
|
||||
<link rel="stylesheet" type="text/css" href="style/themes/default/system.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/themes/default/core.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/themes/default/menus.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/themes/default/buttons.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/themes/default/banner.css">
|
||||
<meta name="defaultLanguage" content="en-US">
|
||||
<meta name="availableLanguages" content="en-US">
|
||||
<link rel="localization" href="/shared/locales/branding/branding.{locale}.properties">
|
||||
<link rel="localization" href="locales/system.{locale}.properties">
|
||||
<link rel="localization" href="locales/accessibility.{locale}.properties">
|
||||
<link rel="localization" href="/shared/locales/date/date.{locale}.properties">
|
||||
<link rel="localization" href="/shared/locales/permissions/permissions.{locale}.properties">
|
||||
<link rel="localization" href="/shared/locales/download/download.{locale}.properties">
|
||||
<link rel="localization" href="/shared/elements/gaia_menu/locales/gaia_menu.{locale}.properties">
|
||||
|
||||
<!-- Identity -->
|
||||
<script defer src="js/identity.js"></script>
|
||||
|
||||
<!-- Crash Reporter -->
|
||||
<link rel="stylesheet" type="text/css" href="style/crash_reporter/crash_reporter.css">
|
||||
<script defer src="js/crash_reporter.js"></script>
|
||||
|
||||
<!-- Browser class -->
|
||||
<link rel="stylesheet" type="text/css" href="style/browser_frame/browser_frame.css">
|
||||
<script defer src="js/browser_config_helper.js"></script>
|
||||
<script defer src="js/browser_frame.js"></script>
|
||||
|
||||
<!-- Captive Portal Login -->
|
||||
<script defer src="js/captive_portal.js"></script>
|
||||
|
||||
<!-- Windows -->
|
||||
<!-- Any module that wants to intercept the Home button and prevent the -->
|
||||
<!-- homescreen from being displayed must come before this script. -->
|
||||
<link rel="stylesheet" type="text/css" href="style/window_layout.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/window.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/app_titlebar.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/browser_context_menu/browser_context_menu.css">
|
||||
<link rel="stylesheet" type="text/css" href="style/fte/fte.css">
|
||||
<script defer src="js/base_ui.js"></script>
|
||||
<script defer src="shared/js/smart-screen/scrollable.js"></script>
|
||||
<script defer src="js/browser_context_menu.js"></script>
|
||||
<script defer src="js/child_window_factory.js"></script>
|
||||
<script defer src="js/app_modal_dialog.js"></script>
|
||||
<script defer src="js/app_chrome.js"></script>
|
||||
<script defer src="js/attention_toaster.js"></script>
|
||||
<script defer src="js/app_transition_controller.js"></script>
|
||||
<script defer src="js/app_authentication_dialog.js"></script>
|
||||
<script defer src="js/app_window.js"></script>
|
||||
<script defer src="js/popup_window.js"></script>
|
||||
<script defer src="js/preview_window.js"></script>
|
||||
<script defer src="js/browser_mixin.js"></script>
|
||||
<script defer src="js/wrapper_factory.js"></script>
|
||||
<script defer src="js/homescreen_window.js"></script>
|
||||
<script defer src="js/homescreen_launcher.js"></script>
|
||||
<script defer src="js/landing_app_window.js"></script>
|
||||
<script defer src="js/landing_app_launcher.js"></script>
|
||||
<script defer src="js/homescreen_window_manager.js"></script>
|
||||
<script defer src="js/activity_window.js"></script>
|
||||
<script defer src="js/activity_window_manager.js"></script>
|
||||
<script defer src="js/app_window_factory.js"></script>
|
||||
<script defer src="js/attention_window.js"></script>
|
||||
<script defer src="js/attention_indicator.js"></script>
|
||||
<script defer src="js/attention_window_manager.js"></script>
|
||||
<script defer src="js/app_window_manager.js"></script>
|
||||
<script defer src="js/system_dialog.js"></script>
|
||||
<script defer src="js/system_dialog_manager.js"></script>
|
||||
<script defer src="js/fxa_dialog.js"></script>
|
||||
<script defer src="js/visibility_manager.js"></script>
|
||||
<script defer src="js/layout_manager.js"></script>
|
||||
<script defer src="js/suspending_app_priority_manager.js"></script>
|
||||
|
||||
<!-- Stack Manager -->
|
||||
<script defer src="js/stack_manager.js"></script>
|
||||
|
||||
<!-- Entry Sheet -->
|
||||
<link rel="stylesheet" type="text/css" href="style/entry_sheet/entry_sheet.css">
|
||||
<script defer src="js/entry_sheet.js"></script>
|
||||
|
||||
<!-- Developer Tools -->
|
||||
<script defer src="js/remote_debugger.js"></script>
|
||||
<script defer src="js/devtools/logshake.js"></script>
|
||||
<script defer src="js/devtools/developer_hud.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="style/devtools/developer_hud.css">
|
||||
|
||||
<!-- z-indexes of all the overlays in the system -->
|
||||
<link rel="stylesheet" type="text/css" href="style/zindex.css">
|
||||
|
||||
<!-- Rocketbar -->
|
||||
<script defer src="js/browser.js"></script>
|
||||
<script defer src="shared/js/url_helper.js"></script>
|
||||
<!-- <script defer src="shared/js/icons_helper.js"></script> -->
|
||||
|
||||
<!-- Navigation -->
|
||||
<script defer src="shared/js/smart-screen/spatial_navigator.js"></script>
|
||||
<script defer src="shared/js/smart-screen/init_gesture.js"></script>
|
||||
|
||||
<!-- API Services -->
|
||||
<script defer src="js/screenshot_service.js"></script>
|
||||
|
||||
<!-- Firefox Accounts -->
|
||||
<link rel="stylesheet" type="text/css" href="style/firefox_account/firefox_account_container.css">
|
||||
<script defer src="js/fx_accounts_u_i.js"></script>
|
||||
<script defer src="js/fx_accounts_manager.js"></script>
|
||||
|
||||
<!-- App Usage Metrics -->
|
||||
<script defer src="js/app_usage_metrics.js"></script>
|
||||
<!-- AppUsageMetrics lazily loads this script if needed
|
||||
<script defer src="shared/js/telemetry.js"></script>
|
||||
-->
|
||||
|
||||
<!-- Sync -->
|
||||
<!--IFDEF_FIREFOX_SYNC
|
||||
<script defer src="js/sync_state_machine.js"></script>
|
||||
<script defer src="js/sync_manager.js"></script>
|
||||
<link rel="localization" href="/shared/locales/firefox_sync/firefox_sync.{locale}.properties">
|
||||
ENDIF_FIREFOX_SYNC-->
|
||||
<!-- <script defer src="shared/js/sync/errors.js"></script> -->
|
||||
|
||||
<!-- Remote Control -->
|
||||
<script defer src="js/remote_control.js"></script>
|
||||
|
||||
<!-- Bookmark Manager -->
|
||||
<script defer src="shared/js/smart-screen/piped_promise.js"></script>
|
||||
<script defer src="shared/js/smart-screen/card_store.js"></script>
|
||||
<script defer src="shared/js/smart-screen/bookmark_manager.js"></script>
|
||||
</head>
|
||||
|
||||
<body class="theme-settings" role="application">
|
||||
<div id="screen" aria-hidden="true">
|
||||
<div id="os-logo" data-z-index-level="initlogo">
|
||||
<div id="carrier-logo"></div>
|
||||
</div>
|
||||
|
||||
<div id="windows" data-z-index-level="app">
|
||||
<!-- application windows are added here -->
|
||||
</div>
|
||||
|
||||
<div id="dialog-overlay" data-z-index-level="dialog-overlay">
|
||||
|
||||
<!-- Crash reporter -->
|
||||
<div id="crash-dialog">
|
||||
<form role="dialog" class="generic-dialog" data-type="confirm">
|
||||
<section id="crash-dialog-contents">
|
||||
<h1 id="crash-dialog-title"></h1>
|
||||
<p data-l10n-id="crash-dialog-message">Would you like to send Mozilla a report about the crash to help us fix the problem? (Reports are sent over Wi-Fi only)</p>
|
||||
<p><a id="crash-info-link" data-l10n-id="crash-info-link">What's in a crash report?</a></p>
|
||||
<p>
|
||||
<input id="always-send" type="checkbox" checked="true"/>
|
||||
<label for="always-send" data-l10n-id="crash-always-report">Always send Mozilla a report when a crash occurs.</label>
|
||||
<p>
|
||||
</section>
|
||||
<menu data-items="2">
|
||||
<button id="dont-send-report" disabled="true" data-l10n-id="crash-dont-send">Don't Send</button>
|
||||
<button id="send-report" class="recommend" data-l10n-id="crash-end">Send Report</button>
|
||||
</menu>
|
||||
</form>
|
||||
|
||||
<!-- "Crash Reports" information page -->
|
||||
<section role="region">
|
||||
<gaia-header ignore-dir id="crash-reports-header" action="close">
|
||||
<h1 data-l10n-id="crashReports"></h1>
|
||||
</gaia-header>
|
||||
<div>
|
||||
<ul>
|
||||
<li class="description" data-l10n-id="crash-reports-description-1">
|
||||
A crash report contains some details about the crash and your device, as well as a snapshot of the state of your device when it crashed.</li>
|
||||
<li class="description" data-l10n-id="crash-reports-description-2">This may include things like open pages and apps, text typed into forms and the content of open messages, recent browsing history, or geolocation used by an open app.</li>
|
||||
<li class="description">
|
||||
<span data-l10n-id="crash-reports-description-3-start">We use crash reports to try to fix problems and improve our products. We handle your information as we describe in our </span><span data-l10n-id="crash-reports-description-3-privacy">privacy policy</span><span data-l10n-id="crash-reports-description-3-end">.</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div id="trustedui-container" class="up theme-settings">
|
||||
<div id="trustedui-inner">
|
||||
<section role="region" class="title-container skin-organic">
|
||||
<gaia-header ignore-dir id="trustedui-header" action="close">
|
||||
<h1 id="trustedui-title"></h1>
|
||||
</gaia-header>
|
||||
<div id="trustedui-throbber"></div>
|
||||
</section>
|
||||
<div id="trustedui-frame-container">
|
||||
<div id="trustedui-error-dialog" role="dialog" class="generic-dialog">
|
||||
<div class="inner">
|
||||
<h3 data-l10n-id="error-title" id="trustedui-error-title">Error</h3>
|
||||
<p>
|
||||
<span data-l10n-id="error-message" id="trustedui-error-message">The app has encountered an error.</span>
|
||||
</p>
|
||||
</div>
|
||||
<menu data-items="1">
|
||||
<button id="trustedui-error-close" data-l10n-id="close">Close</button>
|
||||
</menu>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Firefox Account Dialog -->
|
||||
<!-- <div id="fxa-dialog"></div> Add the dialog via SystemDialog dynamically. -->
|
||||
|
||||
<div id="modal-dialog">
|
||||
<div id="modal-dialog-alert" role="dialog" class="generic-dialog" tabindex="-1">
|
||||
<div class="modal-dialog-message-container inner">
|
||||
<h3 id="modal-dialog-alert-title"></h3>
|
||||
<p>
|
||||
<span id="modal-dialog-alert-message"></span>
|
||||
</p>
|
||||
</div>
|
||||
<menu>
|
||||
<button id="modal-dialog-alert-ok" data-l10n-id="ok" class="affirmative">OK</button>
|
||||
</menu>
|
||||
</div>
|
||||
|
||||
<div id="modal-dialog-confirm" role="dialog" class="generic-dialog" tabindex="-1">
|
||||
<div class="modal-dialog-message-container inner">
|
||||
<h3 id="modal-dialog-confirm-title"></h3>
|
||||
<p>
|
||||
<span id="modal-dialog-confirm-message"></span>
|
||||
</p>
|
||||
</div>
|
||||
<menu data-items="2">
|
||||
<button id="modal-dialog-confirm-cancel" data-l10n-id="cancel">Cancel</button>
|
||||
<button id="modal-dialog-confirm-ok" data-l10n-id="ok" class="affirmative">OK</button>
|
||||
</menu>
|
||||
</div>
|
||||
|
||||
<div id="modal-dialog-prompt" role="dialog" class="generic-dialog" tabindex="-1">
|
||||
<div class="modal-dialog-message-container inner">
|
||||
<h3 id="modal-dialog-prompt-title"></h3>
|
||||
<p>
|
||||
<span id="modal-dialog-prompt-message"></span>
|
||||
<input id="modal-dialog-prompt-input" />
|
||||
</p>
|
||||
</div>
|
||||
<menu data-items="2">
|
||||
<button id="modal-dialog-prompt-cancel" data-l10n-id="cancel">Cancel</button>
|
||||
<button id="modal-dialog-prompt-ok" data-l10n-id="ok" class="affirmative">OK</button>
|
||||
</menu>
|
||||
</div>
|
||||
|
||||
<div id="modal-dialog-select-one" role="dialog" class="generic-dialog" tabindex="-1">
|
||||
<div class="modal-dialog-message-container inner">
|
||||
<h3 id="modal-dialog-select-one-title"></h3>
|
||||
<ul id="modal-dialog-select-one-menu"></ul>
|
||||
</div>
|
||||
<menu>
|
||||
<button id="modal-dialog-select-one-cancel" data-l10n-id="cancel">Cancel</button>
|
||||
</menu>
|
||||
</div>
|
||||
</div> <!-- end of #modal-dialog -->
|
||||
</div> <!-- end of #overlay-dialog -->
|
||||
|
||||
<div id="value-selector-template" hidden>
|
||||
<!--
|
||||
<div data-z-index-level="value-selector" class="value-selector" id="${id}">
|
||||
<form class="value-selector-select-option-popup" role="dialog" data-type="value-selector" hidden>
|
||||
<section class="value-selector-container">
|
||||
<h1 class="value-selector-options-title" data-l10n-id="choose-option">Choose your option</h1>
|
||||
<ol class="value-selector-options-container" role="listbox"></ol>
|
||||
</section>
|
||||
<menu class="value-selector-select-options-buttons value-selector-buttons">
|
||||
<button class="value-option-confirm affirmative full" data-type="ok" data-l10n-id="ok">OK</button>
|
||||
</menu>
|
||||
</form>
|
||||
<div class="value-selector-time-picker-popup" role="dialog" data-type="time-selector" hidden>
|
||||
<h1 data-l10n-id="select-time">Select time</h1>
|
||||
<div class="value-selector-time-picker">
|
||||
<div class="value-selector-time-picker-container picker-container">
|
||||
<div class="picker-bar-background"></div>
|
||||
<div class="value-picker-hours-wrapper">
|
||||
<div class="value-picker-hours animation-on"></div>
|
||||
</div>
|
||||
<div class="value-picker-minutes-wrapper">
|
||||
<div class="value-picker-minutes animation-on"></div>
|
||||
</div>
|
||||
<div class="value-picker-hour24-wrapper">
|
||||
<div class="value-picker-hour24-state animation-on"></div>
|
||||
</div>
|
||||
<div class="value-indicator">
|
||||
<div aria-hidden="true" class="value-indicator-colon hours-minutes-separator">:</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<menu class="value-selector-time-picker-buttons value-selector-buttons" data-items="2">
|
||||
<button class="value-selector-cancel" data-type="cancel" data-l10n-id="cancel">Cancel</button>
|
||||
<button class="value-selector-confirm affirmative" data-type="ok" data-l10n-id="ok">OK</button>
|
||||
</menu>
|
||||
</div>
|
||||
<div role="dialog" data-type="date-selector" class="value-selector-spin-date-picker-popup" hidden>
|
||||
<h1 data-l10n-id="select-day">Select day</h1>
|
||||
<div class="value-selector-spin-date-picker">
|
||||
<div class="picker-container">
|
||||
<div class="picker-bar-background"></div>
|
||||
<div class="value-picker-date-wrapper">
|
||||
<div class="value-picker-date animation-on"></div>
|
||||
<div class="value-picker-date animation-on"></div>
|
||||
<div class="value-picker-date animation-on"></div>
|
||||
<div class="value-picker-date animation-on"></div>
|
||||
</div>
|
||||
<div class="value-picker-month-wrapper">
|
||||
<div class="value-picker-month animation-on"></div>
|
||||
</div>
|
||||
<div class="value-picker-year-wrapper">
|
||||
<div class="value-picker-year animation-on"></div>
|
||||
</div>
|
||||
<div class="value-indicator"></div>
|
||||
</div>
|
||||
</div>
|
||||
<menu class="value-selector-spin-date-picker-buttons value-selector-buttons" data-items="2">
|
||||
<button class="value-selector-cancel" data-type="cancel" data-l10n-id="cancel">Cancel</button>
|
||||
<button class="value-option-confirm affirmative" data-type="ok" data-l10n-id="ok">OK</button>
|
||||
</menu>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="value-selector-groupoption-template" hidden>
|
||||
<!--
|
||||
<li role="subheader">
|
||||
<label role="presentation">
|
||||
<span>${text}</span>
|
||||
</label>
|
||||
</li>
|
||||
-->
|
||||
</div>
|
||||
<div id="value-selector-option-template" hidden>
|
||||
<!--
|
||||
<li role="option" data-option-index="${index}" aria-selected="${checked}">
|
||||
<label role="presentation" for="${for}">
|
||||
<span>${text}</span>
|
||||
</label>
|
||||
</li>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="permission-screen" data-z-index-level="permission-screen">
|
||||
<div id="permission-dialog" role="dialog" class="generic-dialog">
|
||||
<div class="inner">
|
||||
<h1 id="permission-title"></h1>
|
||||
<div id="permission-icon"></div>
|
||||
<h2 id="permission-message"></h2>
|
||||
<div id="permission-more-info" class="hidden">
|
||||
<smart-button id="permission-more-info-link" type="simple-text" data-l10n-id="more-info" href="#">More Info…</smart-button>
|
||||
<div id="permission-more-info-box"> </div>
|
||||
</div>
|
||||
<div id="permission-remember-section" class="hidden" tabIndex="0">
|
||||
<label class="pack-checkbox">
|
||||
<input type="checkbox" id="permission-remember-checkbox" />
|
||||
<span></span>
|
||||
</label>
|
||||
<a data-l10n-id="remember-my-choice" id="permission-remember-label">Remember my choice</a>
|
||||
</div>
|
||||
<div id="permission-device-selector"></div>
|
||||
<ul id="permission-devices">
|
||||
</ul>
|
||||
<menu id="permission-buttons" data-items="2">
|
||||
<smart-button id="permission-no" type="circle-text"></smart-button>
|
||||
<smart-button id="permission-yes" type="circle-text" class="affirmative"></smart-button>
|
||||
</menu>
|
||||
<smart-button id="permission-hide-info-link" type="circle-text" data-l10n-id="hide-info">Hide Info</smart-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="device-list-item-tmpl" class="hide">
|
||||
<!--
|
||||
<label class="device-list deviceEnable">
|
||||
<input class="input-enable" id="${id}" type="checkbox" ${checked}>
|
||||
<span></span>
|
||||
</label>
|
||||
<span class="device-item" data-l10n-id="${label}"></span>
|
||||
-->
|
||||
</div>
|
||||
|
||||
<div id="app-install-dialogs" data-z-index-level="app-install-dialog">
|
||||
</div>
|
||||
|
||||
<form id="updates-download-dialog" data-type="confirm" role="dialog"
|
||||
data-z-index-level="updates-download-dialog" data-nowifi="false"
|
||||
class="generic-dialog">
|
||||
<section>
|
||||
<h1>
|
||||
Updates
|
||||
</h1>
|
||||
<ul>
|
||||
</ul>
|
||||
</section>
|
||||
<p id="updates-offline-warning" data-l10n-id="downloadOfflineWarning">
|
||||
Connection unavailable. Connect to a network to download updates.
|
||||
</p>
|
||||
<menu data-items="2">
|
||||
<button id="updates-later-button" type="reset"
|
||||
data-l10n-id="later">Later</button>
|
||||
<button id="updates-download-button" type="submit"
|
||||
data-l10n-id="download">Download</button>
|
||||
</menu>
|
||||
</form>
|
||||
|
||||
<form id="ime-layout-dialog" class="ime-layout-dialog"
|
||||
data-type="confirm" role="dialog"
|
||||
data-z-index-level="ime-layout-dialog">
|
||||
<section>
|
||||
<h1 data-l10n-id="ime-addkeyboards">Add keyboards</h1>
|
||||
<!-- template for selecting IME layout after 3rd-party keyboard installed -->
|
||||
<div id="ime-list-template" hidden>
|
||||
<!--
|
||||
<li>
|
||||
<a>${displayName}</a>
|
||||
<label class="pack-checkbox ime">
|
||||
<input type="checkbox" name="keyboards" value="${imeName}">
|
||||
<span></span>
|
||||
</label>
|
||||
</li>
|
||||
-->
|
||||
</div>
|
||||
<ul id="ime-list">
|
||||
</ul>
|
||||
<menu data-items="2">
|
||||
<button id="ime-cancel-button" type="button" data-l10n-id="cancel">Cancel</button>
|
||||
<button id="ime-confirm-button" class="recommend" type="button" data-l10n-id="confirm">Confirm</button>
|
||||
</menu>
|
||||
</section>
|
||||
</form>
|
||||
|
||||
<div id="update-manager-toaster" data-z-index-level="notification-toaster" aria-live="assertive" aria-atomic="true">
|
||||
<div class="icon">
|
||||
</div>
|
||||
<div class="message">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- keyboard -->
|
||||
<div id="keyboards" class="hide" data-z-index-level="keyboards"></div>
|
||||
|
||||
<!-- template for IME Menu. -->
|
||||
<div id="ime-menu-template" hidden>
|
||||
<!--
|
||||
<form role="dialog" data-type="value-selector" class="ime-menu value-selector-container" data-z-index-level="action-menu">
|
||||
<section>
|
||||
<h1 data-l10n-id="${title}"></h1>
|
||||
<ol class="value-selector-options-container ime-menu-list" aria-multiselectable="false" role="listbox">
|
||||
</ol>
|
||||
</section>
|
||||
<menu class="ime-menu-button-container">
|
||||
<button class="ime-menu-button" data-type="cancel" data-action="cancel" data-l10n-id="${cancelLabel}"></button>
|
||||
</menu>
|
||||
</div>
|
||||
-->
|
||||
</div> <!-- end of template for IME Menu. -->
|
||||
|
||||
<!-- template for each IME item. -->
|
||||
<div id="ime-menu-item-template" hidden>
|
||||
<!--
|
||||
<li role="option" aria-selected="${selected}" data-id="${layoutId}">
|
||||
<label role="presentation">
|
||||
<span class="item-label">${layoutName}</span>
|
||||
<span class="item-note">${appName}</span>
|
||||
</label>
|
||||
</li>
|
||||
-->
|
||||
</div> <!-- end of template for each IME item. -->
|
||||
|
||||
<div id="sleep-menu" data-z-index-level="sleep-menu">
|
||||
<div id="sleep-menu-container" role="dialog" data-type="object">
|
||||
<header>
|
||||
<h1 data-l10n-id="deviceMenu">Device menu</h1>
|
||||
</header>
|
||||
<section>
|
||||
<ul role="menu">
|
||||
</ul>
|
||||
</section>
|
||||
<gaia-buttons skin="dark">
|
||||
<button data-l10n-id="cancel">Cancel</button>
|
||||
</gaia-buttons>
|
||||
</div>
|
||||
</div>
|
||||
<!-- notification ui -->
|
||||
<smart-banner id="notification-container" class="hidden closed" data-z-index-level="system-notification-banner">
|
||||
<div class="notification-message">
|
||||
<span id="notification-title" class="notification-title"></span>
|
||||
<span id="notification-body" class="notification-body"></span>
|
||||
</div>
|
||||
<menu id="notification-button-group" class="notification-button-group">
|
||||
<smart-button id="notification-button-0" type="simple-text"></smart-button>
|
||||
<smart-button id="notification-button-1" type="simple-text"></smart-button>
|
||||
</menu>
|
||||
</smart-banner>
|
||||
|
||||
<div id="system-overlay" data-z-index-level="system-overlay">
|
||||
<div id="battery">
|
||||
<span class="icon-battery"></span>
|
||||
<div class="battery-notification">
|
||||
<span data-l10n-id="battery-almost-empty3">Battery almost empty.</span>
|
||||
<span data-l10n-id="plug-in-your-charger">Plug in your charger.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="volume" class="vibration">
|
||||
<span class="channel-type"></span>
|
||||
<section>
|
||||
<div class="active"></div>
|
||||
<div class="active"></div>
|
||||
<div class="active"></div>
|
||||
<div class="active"></div>
|
||||
<div class="active"></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="top-panel" class="gesture-panel" data-z-index-level="gesture-panel"></div>
|
||||
<div id="left-panel" class="gesture-panel disabled" data-z-index-level="gesture-panel"></div>
|
||||
<div id="right-panel" class="gesture-panel disabled" data-z-index-level="gesture-panel"></div>
|
||||
<div id="bottom-panel" class="gesture-panel" data-z-index-level="gesture-panel"></div>
|
||||
<div id="fte_template" hidden>
|
||||
<!--
|
||||
<section class="fte-page">
|
||||
<section class="fte-content">
|
||||
<smart-button class="focused demo" type="contextmenu" data-icon="pin" aria-hidden="true">
|
||||
<span data-l10n-id="pin-to-home"></span>
|
||||
</smart-button>
|
||||
<div class="fte-description" data-l10n-id="fte-pin-to-home-description"></div>
|
||||
</section>
|
||||
<section class="fte-panel">
|
||||
<smart-button data-behavior="finish" type="rounded-corner-text" class="fte-button primary" data-l10n-id="ok"></smart-button>
|
||||
</section>
|
||||
</section>
|
||||
-->
|
||||
</div>
|
||||
</div> <!-- end of #screen -->
|
||||
</body>
|
||||
</html>
|
||||
590
gaia/tv_apps/smart-system/js/accessibility.js
Executable file
|
|
@ -0,0 +1,590 @@
|
|||
'use strict';
|
||||
/* global SettingsCache, SettingsListener */
|
||||
|
||||
(function(exports) {
|
||||
|
||||
/**
|
||||
* Accessibility enables and disables the screenreader after the user
|
||||
* gestures using the hardware buttons of the phone. To toggle the setting.
|
||||
* the user must press volume up, then volume down three times in a row.
|
||||
* @class Accessibility
|
||||
* @requires SettingsListener
|
||||
*/
|
||||
function Accessibility() {}
|
||||
|
||||
Accessibility.prototype = {
|
||||
|
||||
/**
|
||||
* How fast the autorepeat is.
|
||||
* @type {Number}
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
REPEAT_INTERVAL: 600000,
|
||||
|
||||
/**
|
||||
* Maximum interval between initial and final TOGGLE_SCREEN_READER_COUNT
|
||||
* volume button presses.
|
||||
* @type {Number}
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
REPEAT_BUTTON_PRESS: 15000000,
|
||||
|
||||
/**
|
||||
* Number of times the buttons need to be pressed before the screen reader
|
||||
* setting is toggled.
|
||||
* @type {Number}
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
TOGGLE_SCREEN_READER_COUNT: 6,
|
||||
|
||||
/**
|
||||
* Cap the full range of contrast. Actual -1 is completely gray, and 1
|
||||
* makes things hard to see. This value is the max/min contrast.
|
||||
*/
|
||||
CONTRAST_CAP: 0.6,
|
||||
|
||||
/**
|
||||
* Current counter for button presses in short succession.
|
||||
* @type {Number}
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
counter: 0,
|
||||
|
||||
/**
|
||||
* Next expected event.
|
||||
* @type {Object}
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
expectedEvent: {
|
||||
type: 'volumeup',
|
||||
timeStamp: 0
|
||||
},
|
||||
|
||||
/**
|
||||
* Expected complete time stamp.
|
||||
* @type {Number}
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
expectedCompleteTimeStamp: 0,
|
||||
|
||||
/**
|
||||
* Accessibility settings to be observed.
|
||||
* @type {Object} name: value pairs.
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
settings: {
|
||||
'accessibility.screenreader': false,
|
||||
'accessibility.screenreader-volume': 1,
|
||||
'accessibility.screenreader-rate': 0,
|
||||
'accessibility.screenreader-captions': false,
|
||||
'accessibility.colors.enable': false,
|
||||
'accessibility.colors.invert': false,
|
||||
'accessibility.colors.grayscale': false,
|
||||
'accessibility.colors.contrast': '0.0'
|
||||
},
|
||||
|
||||
/**
|
||||
* Audio used by the screen reader.
|
||||
* Note: Lazy-loaded when first needed
|
||||
* @type {Object}
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
sounds: {
|
||||
clickedAudio: null,
|
||||
vcKeyAudio: null,
|
||||
vcMoveAudio: null
|
||||
},
|
||||
|
||||
/**
|
||||
* URLs for screen reader audio files.
|
||||
* @type {Object}
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
soundURLs: {
|
||||
clickedAudio: './resources/sounds/screen_reader_clicked.ogg',
|
||||
vcKeyAudio: './resources/sounds/screen_reader_virtual_cursor_key.ogg',
|
||||
vcMoveAudio: './resources/sounds/screen_reader_virtual_cursor_move.ogg'
|
||||
},
|
||||
|
||||
/**
|
||||
* Start listening for events.
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
start: function ar_init() {
|
||||
|
||||
this.screen = document.getElementById('screen');
|
||||
|
||||
this.speechSynthesizer = speechSynthesizer;
|
||||
|
||||
window.addEventListener('mozChromeEvent', this);
|
||||
window.addEventListener('volumeup', this);
|
||||
window.addEventListener('volumedown', this);
|
||||
window.addEventListener('logohidden', this);
|
||||
|
||||
// Attach all observers.
|
||||
Object.keys(this.settings).forEach(function attach(settingKey) {
|
||||
SettingsCache.observe(settingKey, this.settings[settingKey],
|
||||
function observe(aValue) {
|
||||
this.settings[settingKey] = aValue;
|
||||
switch (settingKey) {
|
||||
case 'accessibility.screenreader':
|
||||
// Show Accessibility panel if it is not already visible
|
||||
if (aValue) {
|
||||
SettingsListener.getSettingsLock().set({
|
||||
'accessibility.screenreader-show-settings': true
|
||||
});
|
||||
}
|
||||
this.screen.classList.toggle('screenreader', aValue);
|
||||
break;
|
||||
|
||||
case 'accessibility.colors.enable':
|
||||
SettingsListener.getSettingsLock().set({
|
||||
'layers.effect.invert': aValue ?
|
||||
this.settings['accessibility.colors.invert'] : false,
|
||||
'layers.effect.grayscale': aValue ?
|
||||
this.settings['accessibility.colors.grayscale'] : false,
|
||||
'layers.effect.contrast': aValue ?
|
||||
this.settings['accessibility.colors.contrast'] : '0.0'
|
||||
});
|
||||
break;
|
||||
|
||||
case 'accessibility.screenreader-captions':
|
||||
this.speechSynthesizer.captions = aValue;
|
||||
// If captions are displayed hide them.
|
||||
if (!aValue) {
|
||||
this.speechSynthesizer.hideSpeech(true);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'accessibility.colors.invert':
|
||||
case 'accessibility.colors.grayscale':
|
||||
case 'accessibility.colors.contrast':
|
||||
if (this.settings['accessibility.colors.enable']) {
|
||||
var effect = settingKey.split('.').pop();
|
||||
var gfxSetting = {};
|
||||
if (effect === 'contrast') {
|
||||
gfxSetting['layers.effect.contrast'] =
|
||||
aValue * this.CONTRAST_CAP;
|
||||
} else {
|
||||
gfxSetting['layers.effect.' + effect] = aValue;
|
||||
}
|
||||
SettingsListener.getSettingsLock().set(gfxSetting);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}.bind(this));
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset the expected event to defaults.
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
reset: function ar_resetEvent() {
|
||||
this.expectedEvent = {
|
||||
type: 'volumeup',
|
||||
timeStamp: 0
|
||||
};
|
||||
this.counter = 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Unset speaking flag and set the expected complete time stamp.
|
||||
* @param {?Number} aExpectedCompleteTimeStamp Expected complete time
|
||||
* stamp.
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
resetSpeaking: function ar_resetSpeaking(aExpectedCompleteTimeStamp) {
|
||||
this.isSpeaking = false;
|
||||
this.expectedCompleteTimeStamp = aExpectedCompleteTimeStamp || 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle volumeup and volumedown events generated from HardwareButtons.
|
||||
* @param {Object} aEvent a high-level key event object generated from
|
||||
* HardwareButtons.
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
handleVolumeButtonPress: function ar_handleVolumeButtonPress(aEvent) {
|
||||
var type = aEvent.type;
|
||||
var timeStamp = aEvent.timeStamp;
|
||||
var expectedEvent = this.expectedEvent;
|
||||
if (type !== expectedEvent.type || timeStamp > expectedEvent.timeStamp) {
|
||||
this.reset();
|
||||
if (type !== 'volumeup') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.expectedEvent = {
|
||||
type: type === 'volumeup' ? 'volumedown' :
|
||||
'volumeup',
|
||||
timeStamp: timeStamp + this.REPEAT_INTERVAL
|
||||
};
|
||||
|
||||
if (++this.counter < this.TOGGLE_SCREEN_READER_COUNT) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.reset();
|
||||
|
||||
if (!this.isSpeaking && timeStamp > this.expectedCompleteTimeStamp) {
|
||||
this.speechSynthesizer.cancel();
|
||||
this.announceScreenReader(function onEnd() {
|
||||
this.resetSpeaking(timeStamp + this.REPEAT_BUTTON_PRESS);
|
||||
}.bind(this));
|
||||
return;
|
||||
}
|
||||
|
||||
this.speechSynthesizer.cancel();
|
||||
this.resetSpeaking();
|
||||
SettingsListener.getSettingsLock().set({
|
||||
'accessibility.screenreader':
|
||||
!this.settings['accessibility.screenreader']
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Play audio for a screen reader notification.
|
||||
* @param {String} aSoundKey a key for the screen reader audio.
|
||||
* XXX: When Bug 848954 lands we should be able to use Web Audio API.
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
_playSound: function ar__playSound(aSoundKey) {
|
||||
if (!this.sounds[aSoundKey]) {
|
||||
this.sounds[aSoundKey] = new Audio(this.soundURLs[aSoundKey]);
|
||||
this.sounds[aSoundKey].load();
|
||||
}
|
||||
var audio = this.sounds[aSoundKey].cloneNode(false);
|
||||
audio.volume = this.volume;
|
||||
audio.play();
|
||||
},
|
||||
|
||||
/**
|
||||
* Get current screen reader volume defined by the setting.
|
||||
* @return {Number} Screen reader volume wihtin the [0, 1] interval.
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
get volume() {
|
||||
return this.settings['accessibility.screenreader-volume'];
|
||||
},
|
||||
|
||||
/**
|
||||
* Get current screen reader speech rate defined by the setting.
|
||||
* @return {Number} Screen reader rate within the [0.2, 10] interval.
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
get rate() {
|
||||
var rate = this.settings['accessibility.screenreader-rate'];
|
||||
return rate >= 0 ? rate + 1 : 1 / (Math.abs(rate) + 1);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle accessfu mozChromeEvent.
|
||||
* @param {Object} accessfu details object.
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
handleAccessFuOutput: function ar_handleAccessFuOutput(aDetails) {
|
||||
var options = aDetails.options || {};
|
||||
window.dispatchEvent(new CustomEvent('accessibility-action'));
|
||||
switch (aDetails.eventType) {
|
||||
case 'vc-change':
|
||||
// Vibrate when the virtual cursor changes.
|
||||
navigator.vibrate(options.pattern);
|
||||
this._playSound(options.isKey ? 'vcKeyAudio' : 'vcMoveAudio');
|
||||
break;
|
||||
case 'action':
|
||||
if (aDetails.data[0].string === 'clickAction') {
|
||||
// If element is clicked, play 'click' sound instead of speech.
|
||||
this._playSound('clickedAudio');
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
this.speak(aDetails.data, null, {
|
||||
enqueue: options.enqueue
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove aria-hidden from the screen element to make content accessible to
|
||||
* the screen reader.
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
activateScreen: function ar_activateScreen() {
|
||||
// Screen reader will not say anything until the splash animation is
|
||||
// hidden and the aria-hidden attribute is removed from #screen.
|
||||
this.screen.removeAttribute('aria-hidden');
|
||||
window.removeEventListener('logohidden', this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle event.
|
||||
* @param {Object} aEvent mozChromeEvent/logohidden/volumeup/volumedown.
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
handleEvent: function ar_handleEvent(aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case 'logohidden':
|
||||
this.activateScreen();
|
||||
break;
|
||||
case 'mozChromeEvent':
|
||||
switch (aEvent.detail.type) {
|
||||
case 'accessibility-output':
|
||||
this.handleAccessFuOutput(JSON.parse(aEvent.detail.details));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'volumeup':
|
||||
case 'volumedown':
|
||||
this.handleVolumeButtonPress(aEvent);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Based on whether the screen reader is currently enabled, announce the
|
||||
* instructions of how to enable/disable it.
|
||||
* @param {Function} aCallback A callback after the speech synthesis is
|
||||
* completed.
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
announceScreenReader: function ar_announceScreenReader(aCallback) {
|
||||
var enabled = this.settings['accessibility.screenreader'];
|
||||
this.isSpeaking = true;
|
||||
this.speak({
|
||||
string: enabled ? 'disableScreenReaderSteps' : 'enableScreenReaderSteps'
|
||||
}, aCallback, {enqueue: false});
|
||||
},
|
||||
|
||||
/**
|
||||
* Use speechSynthesis to speak screen reader utterances.
|
||||
* @param {?Array} aData Speech data before it is localized.
|
||||
* @param {?Function} aCallback aCallback A callback after the speech
|
||||
* synthesis is completed.
|
||||
* @param {?Object} aOptions = {} Speech options such as enqueue etc.
|
||||
* @memberof Accessibility.prototype
|
||||
*/
|
||||
speak: function ar_speak(aData, aCallback, aOptions = {}) {
|
||||
this.speechSynthesizer.speak(aData, aOptions, this.rate, this.volume,
|
||||
aCallback);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A speech synthesizer component that handles speech localization and
|
||||
* pronunciation.
|
||||
* @type {Object}
|
||||
*/
|
||||
var speechSynthesizer = {
|
||||
|
||||
/**
|
||||
* A delay before hiding screen reader caption.
|
||||
* @type {Number}
|
||||
* @memberof speechSynthesizer
|
||||
*/
|
||||
CAPTIONS_DELAY: 1500,
|
||||
|
||||
/**
|
||||
* A flag used for speech captions rendering.
|
||||
* @type {Boolean}
|
||||
* @memberof speechSynthesizer
|
||||
*/
|
||||
captions: false,
|
||||
|
||||
/**
|
||||
* Screen container getter.
|
||||
* @type {Object}
|
||||
* @memberof speechSynthesizer
|
||||
*/
|
||||
get screen() {
|
||||
delete this.screen;
|
||||
this.screen = document.getElementById('screen');
|
||||
return this.screen;
|
||||
},
|
||||
|
||||
/**
|
||||
* Speech Synthesis
|
||||
* @type {Object}
|
||||
* @memberof speechSynthesizer
|
||||
*/
|
||||
get speech() {
|
||||
delete this.speech;
|
||||
// If there are no voices bundled, consider speech synthesis unavailable.
|
||||
if (!window.speechSynthesis ||
|
||||
window.speechSynthesis.getVoices().length === 0) {
|
||||
this.speech = null;
|
||||
}
|
||||
this.speech = window.speechSynthesis;
|
||||
return this.speech;
|
||||
},
|
||||
|
||||
/**
|
||||
* Speech utterance
|
||||
* @type {Object}
|
||||
* @memberof speechSynthesizer
|
||||
*/
|
||||
get utterance() {
|
||||
delete this.utterance;
|
||||
this.utterance = window.SpeechSynthesisUtterance;
|
||||
return this.utterance;
|
||||
},
|
||||
|
||||
/**
|
||||
* Cancle speech if the screen reader is speaking.
|
||||
* @memberof speechSynthesizer
|
||||
*/
|
||||
cancel: function ss_cancel() {
|
||||
if (this.speech) {
|
||||
this.speech.cancel();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Localize speech data.
|
||||
* @param {Object} aDetails Speech data object.
|
||||
* @return {String} Localized speech data.
|
||||
* @memberof speechSynthesizer
|
||||
*/
|
||||
localize: function ss_localize(aDetails) {
|
||||
if (!aDetails || typeof aDetails === 'string') {
|
||||
return aDetails;
|
||||
}
|
||||
var string = aDetails.string;
|
||||
var data = {
|
||||
count: aDetails.count
|
||||
};
|
||||
if (!string) {
|
||||
return '';
|
||||
} else {
|
||||
string = 'accessibility-' + string;
|
||||
}
|
||||
|
||||
if (aDetails.args) {
|
||||
data = aDetails.args.reduce(function(aData, val, index) {
|
||||
aData[index] = val;
|
||||
return aData;
|
||||
}, data);
|
||||
}
|
||||
return navigator.mozL10n.get(string, data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Build a complete utterance string by localizing an array of speech data.
|
||||
* @param {?Array} aData Speech data.
|
||||
* @return {String} A complete localized string from speech array data.
|
||||
* @memberof speechSynthesizer
|
||||
*/
|
||||
buildUtterance: function ss_buildUtterance(aData) {
|
||||
if (!Array.isArray(aData)) {
|
||||
aData = [aData];
|
||||
}
|
||||
var words = [], localize = this.localize;
|
||||
aData.reduce(function(words, details) {
|
||||
var localized = localize(details);
|
||||
if (localized) {
|
||||
var word = localized.trim();
|
||||
if (word) {
|
||||
words.push(word);
|
||||
}
|
||||
}
|
||||
return words;
|
||||
}, words);
|
||||
|
||||
return words.join(' ');
|
||||
},
|
||||
|
||||
/**
|
||||
* Show caption for the screen reader.
|
||||
* @param {String} aUtterance Current screen reader caption.
|
||||
* @memberof speechSynthesizer
|
||||
*/
|
||||
showSpeech: function ar_showSpeech(aUtterance) {
|
||||
if (!this.captionsBox) {
|
||||
this.captionsBox = document.createElement('div');
|
||||
this.captionsBox.id = 'accessibility-captions-box';
|
||||
this.captionsBox.setAttribute('data-z-index-level',
|
||||
'accessibility-captions');
|
||||
this.captionsBox.setAttribute('aria-hidden', true);
|
||||
this.screen.appendChild(this.captionsBox);
|
||||
}
|
||||
window.clearTimeout(this.captionsHideTimeout);
|
||||
this.captionsHideTimeout = null;
|
||||
this.captionsBox.textContent = aUtterance;
|
||||
this.captionsBox.classList.add('visible');
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide current screen reader caption.
|
||||
* @param {Boolean} aImmediately A flag to hide captionsBox immediately.
|
||||
* @memberof speechSynthesizer
|
||||
*/
|
||||
hideSpeech: function ar_hideSpeech(aImmediately) {
|
||||
if (!this.captionsBox) {
|
||||
return;
|
||||
}
|
||||
// Hide the caption after CAPTIONS_DELAY.
|
||||
if (aImmediately) {
|
||||
this.captionsBox.classList.remove('visible');
|
||||
} else {
|
||||
this.captionsHideTimeout = window.setTimeout(function() {
|
||||
this.captionsBox.classList.remove('visible');
|
||||
}.bind(this), this.CAPTIONS_DELAY);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Utter a message with a speechSynthesizer.
|
||||
* @param {?Array} aData A messages array to be localized.
|
||||
* @param {JSON} aOptions Options to be used when speaking. For example: {
|
||||
* enqueue: false
|
||||
* }
|
||||
* @param {Number} aRate Speech rate.
|
||||
* @param {Number} aVolume Speech volume.
|
||||
* @param {Function} aCallback A callback after the speech synthesis is
|
||||
* completed.
|
||||
* @memberof speechSynthesizer
|
||||
*/
|
||||
speak: function ss_speak(aData, aOptions, aRate, aVolume, aCallback) {
|
||||
if (!this.speech || !this.utterance) {
|
||||
if (aCallback) {
|
||||
aCallback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!aOptions.enqueue) {
|
||||
this.cancel();
|
||||
}
|
||||
|
||||
var sentence = this.buildUtterance(aData);
|
||||
if (!sentence) {
|
||||
if (aCallback) {
|
||||
aCallback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var utterance = new this.utterance(sentence);
|
||||
utterance.volume = aVolume;
|
||||
utterance.rate = aRate;
|
||||
utterance.addEventListener('end', function() {
|
||||
if (this.captions) {
|
||||
this.hideSpeech();
|
||||
}
|
||||
if (aCallback) {
|
||||
aCallback();
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
if (this.captions) {
|
||||
this.showSpeech(sentence);
|
||||
}
|
||||
this.speech.speak(utterance);
|
||||
}
|
||||
};
|
||||
|
||||
exports.Accessibility = Accessibility;
|
||||
|
||||
}(window));
|
||||
247
gaia/tv_apps/smart-system/js/action_menu.js
Executable file
|
|
@ -0,0 +1,247 @@
|
|||
/* global focusManager */
|
||||
'use strict';
|
||||
|
||||
(function(exports) {
|
||||
|
||||
/**
|
||||
* ActionMenu displays a list of user-selectable actions in an overlay.
|
||||
* An example of this would be when the user selects a single activity form
|
||||
* a list of several activities. Each ActionMenu instance maintains its own
|
||||
* dom and event listeners.
|
||||
* @class ActionMenu
|
||||
* @param {Array} listItems An array of objects to display.
|
||||
* @param {String} titleL10nId The l10n id of the header.
|
||||
* @param {Function} successCb Called when the user selects an option.
|
||||
* @param {Function} cancelCb Called when the menu is cancelled.
|
||||
* @param {Boolean} preventFocusChange Set to true to prevent focus changing.
|
||||
*/
|
||||
function ActionMenu(listItems, titleL10nId, successCb, cancelCb,
|
||||
preventFocusChange) {
|
||||
this.onselected = successCb || function() {};
|
||||
this.oncancel = cancelCb || function() {};
|
||||
this.listItems = listItems;
|
||||
this.titleL10nId = titleL10nId;
|
||||
}
|
||||
|
||||
ActionMenu.prototype = {
|
||||
|
||||
/**
|
||||
* Whether or not the ActionMenu is visible.
|
||||
* @memberof ActionMenu.prototype
|
||||
* @return {Boolean} The ActionMenu is visible.
|
||||
*/
|
||||
get visible() {
|
||||
return this.container.classList.contains('visible');
|
||||
},
|
||||
|
||||
/**
|
||||
* Builds dom and adds event listeners
|
||||
* @memberof ActionMenu.prototype
|
||||
*/
|
||||
start: function() {
|
||||
// Create the structure
|
||||
this.container = document.createElement('form');
|
||||
this.container.dataset.type = 'action';
|
||||
this.container.setAttribute('role', 'dialog');
|
||||
this.container.setAttribute('data-z-index-level', 'action-menu');
|
||||
|
||||
// An action menu has a mandatory header
|
||||
this.header = document.createElement('header');
|
||||
if (this.titleL10nId !== undefined) {
|
||||
this.header.setAttribute('data-l10n-id', this.titleL10nId);
|
||||
}
|
||||
|
||||
this.container.appendChild(this.header);
|
||||
|
||||
// Following our paradigm we need a cancel
|
||||
this.cancel = document.createElement('button');
|
||||
this.cancel.dataset.action = 'cancel';
|
||||
this.cancel.dataset.l10nId = 'cancel';
|
||||
|
||||
// We have a menu with all the options
|
||||
this.menu = document.createElement('menu');
|
||||
|
||||
this.container.appendChild(this.menu);
|
||||
this.container.classList.add('visible');
|
||||
|
||||
// We append to System app (actually to '#screen')
|
||||
var screen = document.getElementById('screen');
|
||||
screen.appendChild(this.container);
|
||||
screen.classList.add('action-menu');
|
||||
|
||||
this.buildMenu(this.listItems);
|
||||
|
||||
this.container.addEventListener('submit', this);
|
||||
this.menu.addEventListener('click', this);
|
||||
|
||||
window.addEventListener('attentionopened', this, true);
|
||||
window.addEventListener('screenchange', this, true);
|
||||
window.addEventListener('home', this);
|
||||
window.addEventListener('holdhome', this);
|
||||
window.addEventListener('sheets-gesture-begin', this);
|
||||
|
||||
focusManager.addUI(this);
|
||||
focusManager.focus();
|
||||
if (this.preventFocusChange) {
|
||||
this.menu.addEventListener('mousedown', this.preventFocusChange);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the dom and stops event listeners
|
||||
* @memberof ActionMenu.prototype
|
||||
*/
|
||||
stop: function() {
|
||||
var screen = document.getElementById('screen');
|
||||
focusManager.removeUI(this);
|
||||
screen.removeChild(this.container);
|
||||
screen.classList.remove('action-menu');
|
||||
|
||||
window.removeEventListener('attentionopened', this, true);
|
||||
window.removeEventListener('screenchange', this, true);
|
||||
window.removeEventListener('home', this);
|
||||
window.removeEventListener('holdhome', this);
|
||||
window.removeEventListener('sheets-gesture-begin', this);
|
||||
|
||||
if (this.preventFocusChange) {
|
||||
this.menu.removeEventListener('mousedown', this.preventFocusChange);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Builds the dom for the menu.
|
||||
* @memberof ActionMenu.prototype
|
||||
*/
|
||||
buildMenu: function(items) {
|
||||
this.menu.innerHTML = '';
|
||||
items.forEach(function traveseItems(item) {
|
||||
var action = document.createElement('button');
|
||||
action.dataset.value = item.value;
|
||||
action.textContent = item.label;
|
||||
|
||||
if (item.icon) {
|
||||
action.classList.add(item.iconClass || 'icon');
|
||||
action.style.backgroundImage = 'url(' + item.icon + ')';
|
||||
}
|
||||
this.menu.appendChild(action);
|
||||
}, this);
|
||||
this.cancel.setAttribute('data-l10n-id', 'cancel');
|
||||
this.menu.appendChild(this.cancel);
|
||||
},
|
||||
|
||||
/**
|
||||
* Hides the ActionMenu.
|
||||
* @memberof ActionMenu.prototype
|
||||
* @param {Function} callback The callback to call after hiding.
|
||||
*/
|
||||
hide: function(callback) {
|
||||
this.container.classList.remove('visible');
|
||||
this.stop();
|
||||
// focus back to the top most window/overlay.
|
||||
focusManager.focus();
|
||||
if (callback && typeof callback === 'function') {
|
||||
setTimeout(callback);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* When IME switcher shows, prevent the keyboard focus getting changed.
|
||||
* @memberof ActionMenu.prototype
|
||||
* @param {DOMEvent} evt The event.
|
||||
*/
|
||||
preventFocusChange: function(evt) {
|
||||
evt.preventDefault();
|
||||
},
|
||||
|
||||
/**
|
||||
* General event handler interface.
|
||||
* Handles submission and cancellation events.
|
||||
* @memberof ActionMenu.prototype
|
||||
* @param {DOMEvent} evt The event.
|
||||
*/
|
||||
handleEvent: function(evt) {
|
||||
var target = evt.target;
|
||||
var type = evt.type;
|
||||
switch (type) {
|
||||
case 'submit':
|
||||
evt.preventDefault();
|
||||
break;
|
||||
case 'screenchange':
|
||||
if (!this.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!evt.detail.screenEnabled) {
|
||||
this.hide();
|
||||
this.oncancel();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'click':
|
||||
evt.preventDefault();
|
||||
var action = target.dataset.action;
|
||||
if (action && action === 'cancel') {
|
||||
this.hide();
|
||||
this.oncancel();
|
||||
return;
|
||||
}
|
||||
|
||||
var value = target.dataset.value;
|
||||
if (!value) {
|
||||
return;
|
||||
}
|
||||
value = parseInt(value);
|
||||
this.hide(this.onselected.bind(this, value));
|
||||
break;
|
||||
|
||||
case 'home':
|
||||
case 'holdhome':
|
||||
case 'sheets-gesture-begin':
|
||||
if (!this.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hide();
|
||||
this.oncancel();
|
||||
break;
|
||||
|
||||
case 'attentionopened':
|
||||
this.hide();
|
||||
this.oncancel();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Whether or not the ActionMenu is visible.
|
||||
* @memberof ActionMenu.prototype
|
||||
* @return {Boolean} if the container is focusible, return true
|
||||
*/
|
||||
isFocusable: function() {
|
||||
return this.container && this.visible;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the z-index value of container
|
||||
* @memberof ActionMenu.prototype
|
||||
* @return {HTMLElement} the container
|
||||
*/
|
||||
getElement: function() {
|
||||
return this.container;
|
||||
},
|
||||
|
||||
/**
|
||||
* Focus cancel button in ActionMenu
|
||||
* @memberof ActionMenu.prototype
|
||||
*/
|
||||
focus: function() {
|
||||
if (this.cancel) {
|
||||
document.activeElement.blur();
|
||||
this.cancel.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.ActionMenu = ActionMenu;
|
||||
|
||||
}(window));
|
||||
199
gaia/tv_apps/smart-system/js/activities.js
Executable file
|
|
@ -0,0 +1,199 @@
|
|||
'use strict';
|
||||
/* global ActionMenu, applications, ManifestHelper */
|
||||
|
||||
(function(exports) {
|
||||
|
||||
/**
|
||||
* Handles relaying of information for web activities.
|
||||
* Contains code to display the list of valid activities,
|
||||
* and fires an event off when the user selects one.
|
||||
* @class Activities
|
||||
*/
|
||||
function Activities() {
|
||||
window.addEventListener('mozChromeEvent', this);
|
||||
window.addEventListener('appopened', this);
|
||||
this.actionMenu = null;
|
||||
}
|
||||
|
||||
Activities.prototype = {
|
||||
|
||||
HIGH_PRIORITY_ACTIVITY: Object.freeze({
|
||||
//
|
||||
// Our OMA Forward Lock DRM implementation relies on a "view"
|
||||
// activity to invoke the "fl" app when the user clicks on a
|
||||
// link to content with a mime type of
|
||||
// "application/vnd.oma.dd+xml" or "application/vnd.oma.drm.message".
|
||||
//
|
||||
// In order for this to be secure, we need to ensure that the
|
||||
// FL app is the only one that can respond to view activity
|
||||
// requests for those particular mime types. Here in the System app
|
||||
// we don't know what the type associated with an activity request is
|
||||
// but we do know the name of the activity. So if this is an activity
|
||||
// choice for a "view" activity, and the FL app is one of the choices
|
||||
// then we must select the FL app without allowing the user to choose
|
||||
// any of the others.
|
||||
//
|
||||
// If we wanted to be more general here we could perhaps
|
||||
// modify this code to allow any certified app to handle the
|
||||
// activity, but it is much simpler to restrict to the FL app
|
||||
// only.
|
||||
'view': /^(http|https|app)\:\/\/fl\.gaiamobile\.org\//,
|
||||
//
|
||||
// The smart-settings app listens "configure" to show the settings menu
|
||||
// which is a smei-transparent menu. We need to let it have the highest
|
||||
// priority to handle the activity and prevent 3rd-party apps to
|
||||
// intercept this activity.
|
||||
'configure': /^(http|https|app)\:\/\/smart-settings\.gaiamobile\.org\//
|
||||
}),
|
||||
|
||||
/** @lends Activities */
|
||||
|
||||
/**
|
||||
* General event handler interface.
|
||||
* Updates the overlay with as we receive load events.
|
||||
* @memberof Activities.prototype
|
||||
* @param {DOMEvent} evt The event.
|
||||
*/
|
||||
handleEvent: function(evt) {
|
||||
switch (evt.type) {
|
||||
case 'mozChromeEvent':
|
||||
var detail = evt.detail;
|
||||
switch (detail.type) {
|
||||
case 'activity-choice':
|
||||
this.chooseActivity(detail);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'appopened':
|
||||
if (this.actionMenu) {
|
||||
this.actionMenu.hide();
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Displays the activity menu if needed.
|
||||
* If there is only one option, the activity is automatically launched.
|
||||
* @memberof Activities.prototype
|
||||
* @param {Object} detail The activity choose event detail.
|
||||
*/
|
||||
chooseActivity: function(detail) {
|
||||
this._id = detail.id;
|
||||
var choices = detail.choices;
|
||||
this.publish('activityrequesting');
|
||||
if (choices.length === 1) {
|
||||
this.choose('0');
|
||||
} else {
|
||||
// apply activity filter.
|
||||
if (this.HIGH_PRIORITY_ACTIVITY[detail.name]) {
|
||||
var appIndex = choices.findIndex((function(choice) {
|
||||
var matchingRegex = this.HIGH_PRIORITY_ACTIVITY[detail.name];
|
||||
return matchingRegex.test(choice.manifest);
|
||||
}).bind(this));
|
||||
if (appIndex !== -1) {
|
||||
this.choose(appIndex.toString(10)); // choose() requires a string
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Since the mozChromeEvent could be triggered by a 'click', and gecko
|
||||
// event are synchronous make sure to exit the event loop before
|
||||
// showing the list.
|
||||
setTimeout((function nextTick() {
|
||||
// Bug 852785: force the keyboard to close before the activity menu
|
||||
// shows
|
||||
window.dispatchEvent(new CustomEvent('activitymenuwillopen'));
|
||||
|
||||
var activityNameL10nId = 'activity-' + detail.name;
|
||||
if (!this.actionMenu) {
|
||||
this.actionMenu = new ActionMenu(this._listItems(choices),
|
||||
activityNameL10nId, this.choose.bind(this),
|
||||
this.cancel.bind(this));
|
||||
this.actionMenu.start();
|
||||
}
|
||||
}).bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The user chooses an activity from the activity menu.
|
||||
* @memberof Activities.prototype
|
||||
* @param {Number} choice The activity choice.
|
||||
*/
|
||||
choose: function(choice) {
|
||||
this.actionMenu = null;
|
||||
|
||||
var returnedChoice = {
|
||||
id: this._id,
|
||||
type: 'activity-choice',
|
||||
value: choice
|
||||
};
|
||||
|
||||
this._sendEvent(returnedChoice);
|
||||
delete this._id;
|
||||
},
|
||||
|
||||
/**
|
||||
* Cancels from the activity menu.
|
||||
* @memberof Activities.prototype
|
||||
*/
|
||||
cancel: function() {
|
||||
this.actionMenu = null;
|
||||
|
||||
var returnedChoice = {
|
||||
id: this._id,
|
||||
type: 'activity-choice',
|
||||
value: -1
|
||||
};
|
||||
|
||||
this._sendEvent(returnedChoice);
|
||||
delete this._id;
|
||||
},
|
||||
|
||||
publish: function(eventName) {
|
||||
var event = new CustomEvent(eventName);
|
||||
window.dispatchEvent(event);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends an event to the platform when a user makes a choice
|
||||
* or cancels the activity menu.
|
||||
* @memberof Activities.prototype
|
||||
* @param {Number} value The index of the selected activity.
|
||||
*/
|
||||
_sendEvent: function(value) {
|
||||
var event = document.createEvent('CustomEvent');
|
||||
event.initCustomEvent('mozContentEvent', true, true, value);
|
||||
window.dispatchEvent(event);
|
||||
},
|
||||
|
||||
/**
|
||||
* Formats and returns a list of activity choices.
|
||||
* @memberof Activities.prototype
|
||||
* @param {Array} choices The list of activity choices.
|
||||
* @return {Array}
|
||||
*/
|
||||
_listItems: function(choices) {
|
||||
var items = [];
|
||||
|
||||
choices.forEach(function(choice, index) {
|
||||
var app = applications.getByManifestURL(choice.manifest);
|
||||
if (!app) {
|
||||
return;
|
||||
}
|
||||
|
||||
items.push({
|
||||
label: new ManifestHelper(app.manifest).name,
|
||||
icon: choice.icon,
|
||||
value: index
|
||||
});
|
||||
});
|
||||
|
||||
return items;
|
||||
}
|
||||
};
|
||||
|
||||
exports.Activities = Activities;
|
||||
|
||||
}(window));
|
||||
282
gaia/tv_apps/smart-system/js/activity_window.js
Executable file
|
|
@ -0,0 +1,282 @@
|
|||
/* global AppWindow, BrowserFrame, OrientationManager, focusManager */
|
||||
'use strict';
|
||||
|
||||
(function(exports) {
|
||||
/**
|
||||
* For smart screen, we use transparent background for smart settings app to
|
||||
* have a semi-transparent overlay on top of other app. And smart settings may
|
||||
* change the color settings which need to see through the smart settings app.
|
||||
* The color is applied to gecko while user change the color settings and user
|
||||
* gets the result immediately.
|
||||
*/
|
||||
const TRANSPARENT_ACTIVITY_WHITE_LIST = Object.freeze([
|
||||
/^(http|https|app)\:\/\/smart-settings\.gaiamobile\.org\//
|
||||
]);
|
||||
|
||||
var _id = 0;
|
||||
|
||||
/**
|
||||
* ActivityWindow is the wrapper for the inline activity instances.
|
||||
* For window disposition activity, they are done in AppWindow.
|
||||
*
|
||||
* ##### Flow chart
|
||||
* 
|
||||
*
|
||||
* @example
|
||||
* var app = new AppWindow({
|
||||
* url: 'http://uitest.gaiamobile.org:8080/index.html',
|
||||
* manifestURL: 'http://uitest.gaiamobile.org:8080/manifest.webapp'
|
||||
* });
|
||||
* var activity = new ActivityWindow({
|
||||
* url: 'http://gallery.gaiamobile.org:8080/pick.html',
|
||||
* manifestURL: 'http://gallery.gaiamobile.org:8080/manifest.webapp'
|
||||
* }, app);
|
||||
*
|
||||
* @class ActivityWindow
|
||||
* @param {Object} config The configuration object of this activity.
|
||||
* @param {AppWindow|ActivityWindow} caller The caller of this activity.
|
||||
*/
|
||||
/**
|
||||
* Fired when the activity window is created.
|
||||
* @event ActivityWindow#activitycreated
|
||||
*/
|
||||
/**
|
||||
* Fired when the activity window is removed.
|
||||
* @event ActivityWindow#activityterminated
|
||||
*/
|
||||
/**
|
||||
* Fired when the activity window is opening.
|
||||
* @event ActivityWindow#activityopening
|
||||
*/
|
||||
/**
|
||||
* Fired when the activity window is opened.
|
||||
* @event ActivityWindow#activityopen
|
||||
*/
|
||||
/**
|
||||
* Fired when the activity window is cloing.
|
||||
* @event ActivityWindow#activityclosing
|
||||
*/
|
||||
/**
|
||||
* Fired when the activity window is closed.
|
||||
* @event ActivityWindow#activityclose
|
||||
*/
|
||||
/**
|
||||
* Fired before the activity window is rendered.
|
||||
* @event ActivityWindow#activitywillrender
|
||||
*/
|
||||
/**
|
||||
* Fired when the activity window is rendered to the DOM tree.
|
||||
* @event ActivityWindow#activityrendered
|
||||
*/
|
||||
/**
|
||||
* Fired when the page visibility of the activity window is
|
||||
* changed to foreground.
|
||||
* @event ActivityWindow#activityforeground
|
||||
*/
|
||||
/**
|
||||
* Fired when the page visibility of the activity window is
|
||||
* changed to background.
|
||||
* @event ActivityWindow#activitybackground
|
||||
*/
|
||||
var ActivityWindow = function ActivityWindow(config, caller) {
|
||||
this.config = config;
|
||||
for (var key in config) {
|
||||
this[key] = config[key];
|
||||
}
|
||||
|
||||
if (caller) {
|
||||
caller.setFrontWindow(this);
|
||||
this.rearWindow = caller;
|
||||
if (caller.element) {
|
||||
this.containerElement = caller.element;
|
||||
}
|
||||
}
|
||||
|
||||
this.transparent = TRANSPARENT_ACTIVITY_WHITE_LIST.some(function(regex) {
|
||||
return regex.test(config.manifestURL);
|
||||
});
|
||||
|
||||
this.publish('creating');
|
||||
this.render();
|
||||
this.publish('created');
|
||||
focusManager.addUI(this);
|
||||
};
|
||||
|
||||
ActivityWindow.prototype = Object.create(AppWindow.prototype);
|
||||
|
||||
ActivityWindow.prototype.eventPrefix = 'activity';
|
||||
|
||||
ActivityWindow.prototype.CLASS_NAME = 'ActivityWindow';
|
||||
|
||||
/**
|
||||
* Turn on this flag to dump debugging messages for all activity windows.
|
||||
* @type {Boolean}
|
||||
*/
|
||||
ActivityWindow.prototype._DEBUG = false;
|
||||
|
||||
ActivityWindow.prototype.openAnimation = 'fade-in';
|
||||
ActivityWindow.prototype.closeAnimation = 'fade-out';
|
||||
|
||||
/**
|
||||
* ActivityWindow's fullscreen state is copying from the caller
|
||||
* instead of reading manifest so we just overwrite the
|
||||
* isFullScreen method of AppWindow.
|
||||
*
|
||||
* @return {Boolean} The activity window is fullscreen or not.
|
||||
*/
|
||||
ActivityWindow.prototype.isFullScreen = function acw_isFullScreen() {
|
||||
if (typeof(this._fullscreen) !== 'undefined') {
|
||||
return this._fullscreen;
|
||||
}
|
||||
|
||||
this._fullscreen = this.rearWindow ?
|
||||
this.rearWindow.isFullScreen() :
|
||||
this.manifest ?
|
||||
!!this.manifest.fullscreen :
|
||||
false;
|
||||
return this._fullscreen;
|
||||
};
|
||||
|
||||
/**
|
||||
* Lock or unlock orientation for this activity.
|
||||
*/
|
||||
ActivityWindow.prototype.lockOrientation =
|
||||
function acw_lockOrientation(noCapture) {
|
||||
if (this.isActive()) {
|
||||
var orientation1 = (this.manifest) ?
|
||||
this.manifest.orientation : null;
|
||||
var orientation2 = (this.config.manifest) ?
|
||||
this.config.manifest.orientation : null;
|
||||
var orientation3 = (this.rearWindow.manifest) ?
|
||||
this.rearWindow.manifest.orientation : null;
|
||||
var orientation4 = OrientationManager.globalOrientation;
|
||||
|
||||
var orientation = orientation1 ||
|
||||
orientation2 ||
|
||||
orientation3 ||
|
||||
orientation4;
|
||||
|
||||
if (orientation) {
|
||||
var rv = false;
|
||||
if ('lockOrientation' in screen) {
|
||||
rv = screen.lockOrientation(orientation);
|
||||
} else if ('mozLockOrientation' in screen) {
|
||||
rv = screen.mozLockOrientation(orientation);
|
||||
}
|
||||
if (rv === false) {
|
||||
console.warn('screen.mozLockOrientation() returned false for',
|
||||
this.origin, 'orientation', orientation);
|
||||
}
|
||||
} else { // If no orientation was requested, then let it rotate
|
||||
if ('unlockOrientation' in screen) {
|
||||
screen.unlockOrientation();
|
||||
} else if ('mozUnlockOrientation' in screen) {
|
||||
screen.mozUnlockOrientation();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ActivityWindow.prototype.view = function acw_view() {
|
||||
this.instanceID = this.CLASS_NAME + '_' + _id++;
|
||||
return '<div class="appWindow activityWindow inline-activity' +
|
||||
'" id="' + this.instanceID + '">' +
|
||||
'<div class="titlebar">' +
|
||||
' <div class="notifications-shadow"></div>' +
|
||||
'</div>' +
|
||||
'<div class="fade-overlay"></div>' +
|
||||
'<div class="browser-container">' +
|
||||
' <div class="screenshot-overlay"></div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
};
|
||||
|
||||
ActivityWindow.SUB_COMPONENTS = {
|
||||
'transitionController': window.AppTransitionController,
|
||||
'modalDialog': window.AppModalDialog,
|
||||
'valueSelector': window.ValueSelector,
|
||||
'authDialog': window.AppAuthenticationDialog,
|
||||
'contextmenu': window.BrowserContextMenu,
|
||||
'childWindowFactory': window.ChildWindowFactory
|
||||
};
|
||||
|
||||
ActivityWindow.REGISTERED_EVENTS =
|
||||
['mozbrowserclose', 'mozbrowsererror', 'mozbrowservisibilitychange',
|
||||
'mozbrowserloadend', 'mozbrowseractivitydone', 'mozbrowserloadstart',
|
||||
'_localized'];
|
||||
|
||||
ActivityWindow.prototype._handle_mozbrowseractivitydone =
|
||||
function aw__handle_mozbrowseractivitydone() {
|
||||
this.kill();
|
||||
};
|
||||
|
||||
ActivityWindow.prototype.render = function acw_render() {
|
||||
this.publish('willrender');
|
||||
this.containerElement.insertAdjacentHTML('beforeend', this.view());
|
||||
// TODO: Use BrowserConfigHelper.
|
||||
this.browser_config = {
|
||||
parentApp: this.parentApp,
|
||||
origin: this.origin,
|
||||
url: this.url,
|
||||
name: this.name,
|
||||
manifest: this.manifest,
|
||||
manifestURL: this.manifestURL,
|
||||
window_name: 'inline' + this.instanceID,
|
||||
oop: true
|
||||
};
|
||||
this.browser = new BrowserFrame(this.browser_config);
|
||||
this.element =
|
||||
document.getElementById(this.instanceID);
|
||||
|
||||
if (this.transparent) {
|
||||
this.element.classList.add('transparent-inline-activity');
|
||||
}
|
||||
|
||||
this.browserContainer = this.element.querySelector('.browser-container');
|
||||
this.browserContainer.appendChild(this.browser.element);
|
||||
this.frame = this.element;
|
||||
this.iframe = this.browser.element;
|
||||
this.screenshotOverlay = this.element.querySelector('.screenshot-overlay');
|
||||
this.fadeOverlay = this.element.querySelector('.fade-overlay');
|
||||
|
||||
// Copy fullscreen state from caller.
|
||||
if (this.isFullScreen()) {
|
||||
this.element.classList.add('fullscreen-app');
|
||||
}
|
||||
|
||||
this._registerEvents();
|
||||
this.installSubComponents();
|
||||
this.publish('rendered');
|
||||
};
|
||||
|
||||
/**
|
||||
* ActivityWindow's default container is '#windows'.
|
||||
* However, we could dynamically change this in layout manager
|
||||
* after it recieves the activitywillrender event.
|
||||
*/
|
||||
ActivityWindow.prototype.containerElement =
|
||||
document.getElementById('windows');
|
||||
|
||||
/**
|
||||
* Bringing ourselves up. To do so we simply do
|
||||
* requestOpen() the original caller app window instance.
|
||||
* Note: this overrides AppWindow.prototype.requestOpen().
|
||||
*/
|
||||
ActivityWindow.prototype.requestOpen = function acw_requestOpen() {
|
||||
if (this.rearWindow) {
|
||||
this.rearWindow.requestOpen();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Request to close. We don't need to reset the visibility of
|
||||
* the bottom window here so we simply close.
|
||||
* Note: this overrides AppWindow.prototype.requestClose().
|
||||
*/
|
||||
ActivityWindow.prototype.requestClose = function acw_requestOpen() {
|
||||
this.close();
|
||||
};
|
||||
|
||||
exports.ActivityWindow = ActivityWindow;
|
||||
|
||||
}(window));
|
||||
144
gaia/tv_apps/smart-system/js/activity_window_manager.js
Executable file
|
|
@ -0,0 +1,144 @@
|
|||
/* global AppWindowManager */
|
||||
'use strict';
|
||||
(function(exports) {
|
||||
/**
|
||||
* ActivityWindowManager manages the activity window instances.
|
||||
*
|
||||
* Currently it's only responsible to kill an existing
|
||||
* activity window if the next creating instance has the same
|
||||
* manifestURL + pageURL.
|
||||
*
|
||||
* @class ActivityWindowManager
|
||||
*/
|
||||
function ActivityWindowManager() {
|
||||
}
|
||||
ActivityWindowManager.prototype = {
|
||||
/**
|
||||
* The list of all current running activity window instances.
|
||||
* @access private
|
||||
* @type {Array}
|
||||
* @memberof ActivityWindowManager.prototype
|
||||
*/
|
||||
_activities: [],
|
||||
|
||||
/**
|
||||
* Register all event handlers.
|
||||
* @memberof ActivityWindowManager.prototype
|
||||
*/
|
||||
start: function acwf_start() {
|
||||
if (this._started) {
|
||||
return;
|
||||
}
|
||||
this._started = true;
|
||||
|
||||
this.activityPool = new Map();
|
||||
window.addEventListener('activityopened', this);
|
||||
window.addEventListener('popupopened', this);
|
||||
window.addEventListener('appopened', this);
|
||||
window.addEventListener('activityrequesting', this);
|
||||
window.addEventListener('activitycreated', this);
|
||||
window.addEventListener('activityterminated', this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Unregister all event handlers.
|
||||
* @memberof ActivityWindowManager.prototype
|
||||
*/
|
||||
stop: function acwf_stop() {
|
||||
if (!this._started) {
|
||||
return;
|
||||
}
|
||||
this._started = false;
|
||||
|
||||
this.activityPool = null;
|
||||
window.removeEventListener('activityopened', this);
|
||||
window.removeEventListener('popupopened', this);
|
||||
window.removeEventListener('appopened', this);
|
||||
window.removeEventListener('activityrequesting', this);
|
||||
window.removeEventListener('activitycreated', this);
|
||||
window.removeEventListener('activityterminated', this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Put all window ID which is involved in an activity here.
|
||||
* XXX: This is a workaround of bug 931339.
|
||||
* @type {Map}
|
||||
*/
|
||||
activityPool: null,
|
||||
|
||||
/**
|
||||
* Activity Config
|
||||
* @typedef {Object} ActivityConfig
|
||||
* @property {String} manifestURL The manifestURL of the activity
|
||||
* @property {String} url The URL of the activity handling page
|
||||
* @property {Boolean} isActivity
|
||||
* @property {Boolean} inline The disposition of the activty is inline
|
||||
* or not
|
||||
*/
|
||||
|
||||
handleEvent: function acwf_handleEvent(evt) {
|
||||
switch (evt.type) {
|
||||
// XXX: Workaround of bug 931339.
|
||||
// We are maintaining only one chain of activities here
|
||||
// in this.activityPool.
|
||||
// If the next coming activity request does not belong
|
||||
// to the previous pool,
|
||||
// kill all the background inline activities.
|
||||
case 'popupopened':
|
||||
case 'activityopened':
|
||||
case 'appopened':
|
||||
var app = evt.detail;
|
||||
var parent = app.callerWindow ||
|
||||
app.bottomWindow ||
|
||||
app.previousWindow;
|
||||
if (parent && this.activityPool.has(parent.instanceID)) {
|
||||
// We don't really care about the instance context,
|
||||
// so we set the value to a boolean.
|
||||
this.activityPool.set(app.instanceID, true);
|
||||
}
|
||||
break;
|
||||
case 'popupterminated':
|
||||
case 'appterminated':
|
||||
this.activityPool.delete(app.instanceID);
|
||||
break;
|
||||
case 'activityrequesting':
|
||||
// The request may come from the top most window
|
||||
// or the system app, but we don't care here.
|
||||
var caller = AppWindowManager.getActiveApp().getTopMostWindow();
|
||||
if (!this.activityPool.size) {
|
||||
this.activityPool.set(caller.instanceID, true);
|
||||
} else {
|
||||
if (!this.activityPool.has(caller.instanceID)) {
|
||||
// A new request from a new chain,
|
||||
// kill all the background.
|
||||
this._activities.forEach(function iterator(activity, index) {
|
||||
if (!activity.getBottomMostWindow().isActive() ||
|
||||
!activity.isActive()) {
|
||||
activity.kill();
|
||||
}
|
||||
}, this);
|
||||
this.activityPool.clear();
|
||||
this.activityPool.set(caller.instanceID, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'activityterminated':
|
||||
this._activities.some(function iterator(activity, index) {
|
||||
if (activity.instanceID === evt.detail.instanceID) {
|
||||
this._activities.splice(index, 1);
|
||||
return true;
|
||||
}
|
||||
}, this);
|
||||
this.activityPool.delete(evt.detail.instanceID);
|
||||
break;
|
||||
|
||||
case 'activitycreated':
|
||||
this._activities.push(evt.detail);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.ActivityWindowManager = ActivityWindowManager;
|
||||
}(window));
|
||||
339
gaia/tv_apps/smart-system/js/airplane_mode.js
Executable file
|
|
@ -0,0 +1,339 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/* global SettingsCache, SettingsListener, SettingsCache, AirplaneMode */
|
||||
/* jshint loopfunc:true */
|
||||
'use strict';
|
||||
|
||||
(function(exports) {
|
||||
|
||||
var AirplaneModeServiceHelper = {
|
||||
_settings: {},
|
||||
_initSetting: function(settingID) {
|
||||
var self = this;
|
||||
var settingEnabledID = settingID + '.enabled';
|
||||
var settingSuspendedID = settingID + '.suspended';
|
||||
// forget the mozSetting states when user toggle 'xyz' on manually,
|
||||
// e.g. set 'xyz'.suspend = false when 'xyz'.enabled === true
|
||||
SettingsCache.observe(settingEnabledID,
|
||||
'',
|
||||
function(value) {
|
||||
if (value) {
|
||||
self._unsuspend(settingSuspendedID);
|
||||
}
|
||||
});
|
||||
// init and observe the corresponding mozSettings
|
||||
// for Bluetooth, Wifi, and GPS.
|
||||
SettingsCache.observe(settingEnabledID, false,
|
||||
function(value) {
|
||||
self._settings[settingEnabledID] = value;
|
||||
});
|
||||
// remember the mozSetting states before the airplane mode disables them
|
||||
SettingsCache.observe(settingSuspendedID, false,
|
||||
function(value) {
|
||||
self._settings[settingSuspendedID] = value;
|
||||
});
|
||||
},
|
||||
// turn off the mozSetting corresponding to `key'
|
||||
// and remember its initial state by storing it in another setting
|
||||
_suspend: function(key) {
|
||||
var enabled = this._settings[key + '.enabled'];
|
||||
var suspended = this._settings[key + '.suspended'];
|
||||
|
||||
if (suspended) {
|
||||
return;
|
||||
}
|
||||
|
||||
// remember the state before switching it to false
|
||||
var sset = {};
|
||||
sset[key + '.suspended'] = enabled;
|
||||
SettingsListener.getSettingsLock().set(sset);
|
||||
|
||||
// switch the state to false if necessary
|
||||
if (enabled) {
|
||||
var eset = {};
|
||||
eset[key + '.enabled'] = false;
|
||||
SettingsListener.getSettingsLock().set(eset);
|
||||
}
|
||||
},
|
||||
// turn on the mozSetting corresponding to `key'
|
||||
// if it has been suspended by the airplane mode
|
||||
_restore: function(key) {
|
||||
var suspended = this._settings[key + '.suspended'];
|
||||
|
||||
// clear the 'suspended' state
|
||||
var sset = {};
|
||||
sset[key + '.suspended'] = false;
|
||||
SettingsListener.getSettingsLock().set(sset);
|
||||
|
||||
// switch the state to true if it was suspended
|
||||
if (suspended) {
|
||||
var rset = {};
|
||||
rset[key + '.enabled'] = true;
|
||||
SettingsListener.getSettingsLock().set(rset);
|
||||
}
|
||||
},
|
||||
_unsuspend: function(settingSuspendedID) {
|
||||
// clear the 'suspended' state
|
||||
var sset = {};
|
||||
sset[settingSuspendedID] = false;
|
||||
SettingsListener.getSettingsLock().set(sset);
|
||||
},
|
||||
isEnabled: function(key) {
|
||||
return this._settings[key + '.enabled'];
|
||||
},
|
||||
isSuspended: function(key) {
|
||||
return this._settings[key + '.suspended'];
|
||||
},
|
||||
init: function() {
|
||||
['bluetooth', 'wifi', 'geolocation'].forEach(
|
||||
this._initSetting.bind(this)
|
||||
);
|
||||
},
|
||||
updateStatus: function(value) {
|
||||
// FM Radio will be turned off in Gecko, more detailed about why we do
|
||||
// this in Gecko instead, please check bug 997064.
|
||||
var bluetooth = window.navigator.mozBluetooth;
|
||||
var wifiManager = window.navigator.mozWifiManager;
|
||||
|
||||
if (value) {
|
||||
|
||||
// Turn off Bluetooth.
|
||||
if (bluetooth) {
|
||||
this._suspend('bluetooth');
|
||||
}
|
||||
|
||||
// Turn off Wifi and Wifi tethering.
|
||||
if (wifiManager) {
|
||||
this._suspend('wifi');
|
||||
SettingsListener.getSettingsLock().set({
|
||||
'tethering.wifi.enabled': false
|
||||
});
|
||||
}
|
||||
|
||||
// Turn off Geolocation.
|
||||
this._suspend('geolocation');
|
||||
|
||||
} else {
|
||||
// Note that we don't restore Wifi tethering when leaving airplane mode
|
||||
// because Wifi tethering can't be switched on before data connection is
|
||||
// established.
|
||||
|
||||
// Don't attempt to turn on Bluetooth if it's already on
|
||||
if (bluetooth && !bluetooth.enabled) {
|
||||
this._restore('bluetooth');
|
||||
}
|
||||
|
||||
// Don't attempt to turn on Wifi if it's already on
|
||||
if (wifiManager && !wifiManager.enabled) {
|
||||
this._restore('wifi');
|
||||
}
|
||||
|
||||
// Don't attempt to turn on Geolocation if it's already on
|
||||
if (!this._settings['geolocation.enabled']) {
|
||||
this._restore('geolocation');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// main
|
||||
var AirplaneMode = {
|
||||
/*
|
||||
* We will cache the helper as our internal value
|
||||
*/
|
||||
_serviceHelper: AirplaneModeServiceHelper,
|
||||
|
||||
/*
|
||||
* This is an internal key to store current state of AirplaneMode
|
||||
*/
|
||||
_enabled: null,
|
||||
|
||||
/*
|
||||
* This is an event mapping table that will help us wait for
|
||||
* specific event from its manager to make sure we are now
|
||||
* in airplane mode or not.
|
||||
*/
|
||||
_checkedActionsMap: {
|
||||
wifi: {
|
||||
enabled: 'wifi-enabled',
|
||||
disabled: 'wifi-disabled'
|
||||
},
|
||||
bluetooth: {
|
||||
enabled: 'bluetooth-adapter-added',
|
||||
disabled: 'bluetooth-disabled'
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* When turning on / off airplane mode, we will start watching
|
||||
* needed events to make sure we are in airplane mode or not.
|
||||
*
|
||||
* @param {boolean} value
|
||||
* @param {Object} checkedActions
|
||||
*/
|
||||
watchEvents: function(value, checkedActions) {
|
||||
var self = this;
|
||||
// We don't want to wait until the first event reacts in order to
|
||||
// update the status, because we can set the status to 'enabling' or
|
||||
// 'disabling' already through `_updateAirplaneModeStatus`.
|
||||
self._updateAirplaneModeStatus(checkedActions);
|
||||
for (var serviceName in this._checkedActionsMap) {
|
||||
|
||||
// if we are waiting for specific service
|
||||
if (serviceName in checkedActions) {
|
||||
var action = value ? 'disabled' : 'enabled';
|
||||
var eventName = this._checkedActionsMap[serviceName][action];
|
||||
// then we will start watch events coming from its manager
|
||||
window.addEventListener(eventName,
|
||||
(function(eventName, serviceName) {
|
||||
return function toUpdateAirplaneMode() {
|
||||
window.removeEventListener(eventName, toUpdateAirplaneMode);
|
||||
checkedActions[serviceName] = true;
|
||||
self._updateAirplaneModeStatus(checkedActions);
|
||||
};
|
||||
}(eventName, serviceName)));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* This is a ES5 feature that can help the others easily get/set
|
||||
* AirplaneMode.
|
||||
*
|
||||
* @param {boolean} value
|
||||
*/
|
||||
set enabled(value) {
|
||||
if (value !== this._enabled) {
|
||||
this._enabled = value;
|
||||
|
||||
// start watching events
|
||||
this.watchEvents(value, this._getCheckedActions(value));
|
||||
|
||||
// tell services to do their own operations
|
||||
this._serviceHelper.updateStatus(value);
|
||||
this.publish('airplanemodechanged', {enabled: value});
|
||||
}
|
||||
},
|
||||
|
||||
publish: function publish(event, detail) {
|
||||
var evt = new CustomEvent(event,
|
||||
{
|
||||
bubbles: true,
|
||||
detail: detail || this
|
||||
});
|
||||
window.dispatchEvent(evt);
|
||||
},
|
||||
|
||||
/*
|
||||
* This is a ES5 feature that can help the others easily get AirplaneMode
|
||||
* states.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
get enabled() {
|
||||
return this._enabled;
|
||||
},
|
||||
|
||||
/*
|
||||
* In order to make sure all needed managers work successfully. We have to
|
||||
* use this method to update airplaneMode related keys to tell
|
||||
* AirplaneModeHelper our current states and is finised or not.
|
||||
*/
|
||||
_updateAirplaneModeStatus: function(checkActions) {
|
||||
var self = this;
|
||||
var areAllActionsDone;
|
||||
|
||||
areAllActionsDone = this._areCheckedActionsAllDone(checkActions);
|
||||
|
||||
if (areAllActionsDone) {
|
||||
SettingsListener.getSettingsLock().set({
|
||||
'airplaneMode.enabled': self._enabled,
|
||||
'airplaneMode.status': self._enabled ? 'enabled' : 'disabled'
|
||||
});
|
||||
} else {
|
||||
// keep updating the status to reflect current status
|
||||
SettingsListener.getSettingsLock().set({
|
||||
'airplaneMode.status': self._enabled ? 'enabling' : 'disabling'
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
/*
|
||||
* By default, these three API takes longer time and with success / error
|
||||
* callback. we just have to wait for these three items.
|
||||
*
|
||||
* @param {boolean} value
|
||||
* @return {Object} checkedActions
|
||||
*/
|
||||
_getCheckedActions: function(value) {
|
||||
// we have to re-init all need-to-check managers
|
||||
var checkedActions = {};
|
||||
|
||||
if (value === true) {
|
||||
// check bluetooth
|
||||
if (this._serviceHelper.isEnabled('bluetooth')) {
|
||||
checkedActions.bluetooth = false;
|
||||
}
|
||||
|
||||
// check wifi
|
||||
if (this._serviceHelper.isEnabled('wifi')) {
|
||||
checkedActions.wifi = false;
|
||||
}
|
||||
} else {
|
||||
// check bluetooth
|
||||
if (this._serviceHelper.isSuspended('bluetooth')) {
|
||||
checkedActions.bluetooth = false;
|
||||
}
|
||||
|
||||
// check wifi
|
||||
if (this._serviceHelper.isSuspended('wifi')) {
|
||||
checkedActions.wifi = false;
|
||||
}
|
||||
}
|
||||
|
||||
return checkedActions;
|
||||
},
|
||||
|
||||
/*
|
||||
* We have to use this method to check whether all actions
|
||||
* are done or not.
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
_areCheckedActionsAllDone: function(checkedActions) {
|
||||
for (var key in checkedActions) {
|
||||
if (checkedActions[key] === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/*
|
||||
* Entry point
|
||||
*/
|
||||
init: function apm_init() {
|
||||
var self = this;
|
||||
|
||||
this._serviceHelper.init();
|
||||
|
||||
// monitor airplaneMode communication key change
|
||||
SettingsCache.observe('airplaneMode.enabled', false, function(value) {
|
||||
self.enabled = value;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.AirplaneMode = AirplaneMode;
|
||||
})(window);
|
||||
|
||||
if (document && (document.readyState === 'complete' ||
|
||||
document.readyState === 'interactive')) {
|
||||
setTimeout(AirplaneMode.init.bind(AirplaneMode));
|
||||
} else {
|
||||
window.addEventListener('load', function onload() {
|
||||
window.removeEventListener('load', onload);
|
||||
AirplaneMode.init();
|
||||
});
|
||||
}
|
||||
245
gaia/tv_apps/smart-system/js/app_authentication_dialog.js
Executable file
|
|
@ -0,0 +1,245 @@
|
|||
/* global focusManager */
|
||||
|
||||
'use strict';
|
||||
|
||||
(function(exports) {
|
||||
var _id = 0;
|
||||
|
||||
/**
|
||||
* The https authentication dialog of the AppWindow.
|
||||
*
|
||||
* @class AppAuthenticationDialog
|
||||
* @param {AppWindow} app The app window instance
|
||||
* where this dialog should popup.
|
||||
* @extends BaseUI
|
||||
*/
|
||||
function AppAuthenticationDialog(app) {
|
||||
this.app = app;
|
||||
this._event = null;
|
||||
this.containerElement = app.element;
|
||||
this.instanceID = _id++;
|
||||
this._injected = false;
|
||||
this.app.element.addEventListener('mozbrowserusernameandpasswordrequired',
|
||||
this);
|
||||
}
|
||||
|
||||
AppAuthenticationDialog.prototype = Object.create(window.BaseUI.prototype);
|
||||
|
||||
AppAuthenticationDialog.prototype.CLASS_NAME = 'AuthenticationDialog';
|
||||
|
||||
AppAuthenticationDialog.prototype.EVENT_PREFIX = 'authdialog';
|
||||
|
||||
/**
|
||||
* Used for element id access.
|
||||
* e.g., 'authentication-dialog-alert-ok'
|
||||
* @type {String}
|
||||
* @memberof AppAuthenticationDialog.prototype
|
||||
*/
|
||||
AppAuthenticationDialog.prototype.ELEMENT_PREFIX = 'authentication-dialog-';
|
||||
|
||||
/**
|
||||
* Maps to DOM elements.
|
||||
* @type {Object}
|
||||
* @memberof AppAuthenticationDialog.prototype
|
||||
*/
|
||||
AppAuthenticationDialog.prototype.elements = null;
|
||||
|
||||
/**
|
||||
* The current authentication event.
|
||||
* Note: Only one event one time.
|
||||
* @type {Event}
|
||||
* @memberof AppAuthenticationDialog.prototype
|
||||
*/
|
||||
AppAuthenticationDialog.prototype._event = null;
|
||||
|
||||
/**
|
||||
* Get all elements when inited.
|
||||
* @memberof AppAuthenticationDialog.prototype
|
||||
*/
|
||||
AppAuthenticationDialog.prototype._fetchElements =
|
||||
function aad__fetchElements() {
|
||||
this.element = document.getElementById(this.CLASS_NAME + this.instanceID);
|
||||
this.elements = {};
|
||||
|
||||
var toCamelCase = function toCamelCase(str) {
|
||||
return str.replace(/\-(.)/g, function replacer(str, p1) {
|
||||
return p1.toUpperCase();
|
||||
});
|
||||
};
|
||||
|
||||
this.elementClasses = [
|
||||
'http-username-input', 'http-password-input',
|
||||
'http-authentication-message', 'http-authentication-ok',
|
||||
'http-authentication-cancel', 'http-authentication-header'
|
||||
];
|
||||
|
||||
this.elementClasses.forEach(function createElementRef(name) {
|
||||
this.elements[toCamelCase(name)] =
|
||||
this.element.querySelector('.' + this.ELEMENT_PREFIX + name);
|
||||
}, this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates markup for the dialog.
|
||||
* @memberof AppAuthenticationDialog.prototype
|
||||
*/
|
||||
AppAuthenticationDialog.prototype.view = function aad_view() {
|
||||
return '<section class="authentication-dialog skin-organic" ' +
|
||||
'id="' + this.CLASS_NAME + this.instanceID + '"' +
|
||||
'role="region">' +
|
||||
'<gaia-header ignore-dir action="close" ' +
|
||||
'class="authentication-dialog-http-authentication-header">' +
|
||||
'<button class="'+
|
||||
'authentication-dialog-http-authentication-cancel">' +
|
||||
'</button>' +
|
||||
'<h1 data-l10n-id="sign-in-to-website">Sign in to website</h1>' +
|
||||
'<button class="' +
|
||||
'authentication-dialog-http-authentication-ok" ' +
|
||||
'data-l10n-id="login">Login</button>' +
|
||||
'</gaia-header>' +
|
||||
'<span class="authentication-dialog-http-authentication-message">' +
|
||||
'</span>' +
|
||||
'<label data-l10n-id="username">Username' +
|
||||
'<input type="text" ' +
|
||||
'class="authentication-dialog-http-username-input" />' +
|
||||
'</label>' +
|
||||
'<label data-l10n-id="password">Password' +
|
||||
'<input type="password" ' +
|
||||
'class="authentication-dialog-http-password-input" />' +
|
||||
'</label>' +
|
||||
'</section>';
|
||||
};
|
||||
|
||||
/**
|
||||
* General event handler interface.
|
||||
* @memberof AppAuthenticationDialog.prototype
|
||||
*/
|
||||
AppAuthenticationDialog.prototype.handleEvent = function(evt) {
|
||||
this.debug(' AAD>> got event: ' + evt.type);
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
this._event = evt;
|
||||
if (!this._injected) {
|
||||
this.render();
|
||||
focusManager.addUI(this);
|
||||
}
|
||||
this.show();
|
||||
this._injected = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Registers click events.
|
||||
* @memberof AppAuthenticationDialog.prototype
|
||||
*/
|
||||
AppAuthenticationDialog.prototype._registerEvents =
|
||||
function aad__registerEvents() {
|
||||
this.elements.httpAuthenticationOk.
|
||||
addEventListener('click', this.confirmHandler.bind(this));
|
||||
this.elements.httpAuthenticationHeader.
|
||||
addEventListener('action', this.cancelHandler.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Unregisters authentication events from the app element.
|
||||
* @memberof AppAuthenticationDialog.prototype
|
||||
*/
|
||||
AppAuthenticationDialog.prototype._unregisterEvents =
|
||||
function aad__unregisterEvents() {
|
||||
this.app.element.removeEventListener(
|
||||
'mozbrowserusernameandpasswordrequired',
|
||||
this);
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows the authentication dialog.
|
||||
* @memberof AppAuthenticationDialog.prototype
|
||||
*/
|
||||
AppAuthenticationDialog.prototype.show = function aad_show() {
|
||||
var evt = this._event;
|
||||
var elements = this.elements;
|
||||
this.element.classList.add('visible');
|
||||
this.debug(' AAD>> showing');
|
||||
navigator.mozL10n.setAttributes(
|
||||
elements.httpAuthenticationMessage,
|
||||
'http-authentication-message2',
|
||||
{host: evt.detail.host}
|
||||
);
|
||||
elements.httpUsernameInput.value = '';
|
||||
elements.httpPasswordInput.value = '';
|
||||
focusManager.focus();
|
||||
};
|
||||
|
||||
/**
|
||||
* Hides the authentication dialog.
|
||||
* @memberof AppAuthenticationDialog.prototype
|
||||
*/
|
||||
AppAuthenticationDialog.prototype.hide = function aad_hide() {
|
||||
this.elements.httpUsernameInput.blur();
|
||||
this.elements.httpPasswordInput.blur();
|
||||
this.element.classList.remove('visible');
|
||||
this.debug(' AAD>> hided');
|
||||
focusManager.focus();
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the user confirms the authentication dialog.
|
||||
* @memberof AppAuthenticationDialog.prototype
|
||||
*/
|
||||
AppAuthenticationDialog.prototype.confirmHandler =
|
||||
function aad_confirmHandler() {
|
||||
var elements = this.elements;
|
||||
var evt = this._event;
|
||||
this.hide();
|
||||
evt.detail.authenticate(elements.httpUsernameInput.value,
|
||||
elements.httpPasswordInput.value);
|
||||
this._event = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Called when the user cancels the authentication dialog.
|
||||
* @memberof AppAuthenticationDialog.prototype
|
||||
*/
|
||||
AppAuthenticationDialog.prototype.cancelHandler =
|
||||
function aad_cancelHandler() {
|
||||
var evt = this._event;
|
||||
this.hide();
|
||||
evt.detail.cancel();
|
||||
this._event = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine whether the dialog is focusable.
|
||||
* @memberof AppAuthenticationDialog.prototype
|
||||
*/
|
||||
AppAuthenticationDialog.prototype.isFocusable =
|
||||
function aad_isFocusable() {
|
||||
return this.element && this.element.classList.contains('visible');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get z-index of the dialog.
|
||||
* @memberof AppAuthenticationDialog.prototype
|
||||
* @return {Number} z-index of the dialog
|
||||
*/
|
||||
AppAuthenticationDialog.prototype.getElement =
|
||||
function aad_getElement() {
|
||||
if (this.isFocusable()) {
|
||||
return this.element;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Focus the cancel button of authentication dialog
|
||||
* @memberof AppAuthenticationDialog.prototype
|
||||
*/
|
||||
AppAuthenticationDialog.prototype.focus =
|
||||
function aad_focus() {
|
||||
if (this.isFocusable() && this.elements.httpUsernameInput) {
|
||||
document.activeElement.blur();
|
||||
this.elements.httpUsernameInput.focus();
|
||||
}
|
||||
};
|
||||
|
||||
exports.AppAuthenticationDialog = AppAuthenticationDialog;
|
||||
|
||||
}(window));
|
||||