Publish Vulpes Preview 15 sources for desktop, Android and Tundra
Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
parent
93e3a48004
commit
b38dea0597
25901 changed files with 9982925 additions and 369 deletions
412
gaia/tv_apps/browser/js/authentication_dialog.js
Executable file
412
gaia/tv_apps/browser/js/authentication_dialog.js
Executable file
|
|
@ -0,0 +1,412 @@
|
|||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
|
||||
/* exported AuthenticationDialog */
|
||||
|
||||
/**
|
||||
* Handles mozbrowserusernameandpasswordrequired event and show
|
||||
* authentication dialog.
|
||||
* @namespace AuthenticationDialog
|
||||
*/
|
||||
var AuthenticationDialog = {
|
||||
|
||||
_confirmed: {},
|
||||
focusElement: [],
|
||||
focusIndex: {x:0, y:0},
|
||||
|
||||
/** Get all DOM elements when inited. */
|
||||
getAllElements: function ad_getAllElements() {
|
||||
|
||||
var toCamelCase = function toCamelCase(str) {
|
||||
return str.replace(/\-(.)/g, function replacer(str, p1) {
|
||||
return p1.toUpperCase();
|
||||
});
|
||||
};
|
||||
|
||||
var elementIDs = [
|
||||
'http-authentication-dialog',
|
||||
'http-authentication-username', 'http-authentication-username-clear',
|
||||
'http-authentication-password', 'http-authentication-password-clear',
|
||||
'http-authentication-username-area', 'http-authentication-password-area',
|
||||
'http-authentication-message',
|
||||
'http-authentication-ok', 'http-authentication-cancel'];
|
||||
|
||||
// Loop and add element with camel style name to Modal Dialog attribute.
|
||||
elementIDs.forEach(function createElementRef(name) {
|
||||
this[toCamelCase(name)] = document.getElementById(name);
|
||||
}, this);
|
||||
},
|
||||
|
||||
currentEvents: {},
|
||||
|
||||
/** Initialization. Get elements and add listeners. */
|
||||
init: function ad_init(bindToWindow) {
|
||||
// Get all elements initially.
|
||||
this.getAllElements();
|
||||
|
||||
this.boundToWindow = bindToWindow || false;
|
||||
|
||||
this.httpAuthenticationUsername.addEventListener('submit',
|
||||
this.dialogInputSubmit.bind(this));
|
||||
this.httpAuthenticationPassword.addEventListener('submit',
|
||||
this.dialogInputSubmit.bind(this));
|
||||
|
||||
this.httpAuthenticationUsernameArea.addEventListener('blur',
|
||||
this.dialogInputAreaBlur.bind(this));
|
||||
this.httpAuthenticationPasswordArea.addEventListener('blur',
|
||||
this.dialogInputAreaBlur.bind(this));
|
||||
|
||||
this.httpAuthenticationUsernameArea.addEventListener('focus',
|
||||
this.dialogUsernameAreaFocus.bind(this));
|
||||
this.httpAuthenticationPasswordArea.addEventListener('focus',
|
||||
this.dialogPasswordAreaFocus.bind(this));
|
||||
|
||||
this.httpAuthenticationUsernameClear.addEventListener('mouseup',
|
||||
this.clearUsername.bind(this));
|
||||
this.httpAuthenticationUsernameClear.addEventListener('keyup',
|
||||
this.dialogButtonKeyup.bind(this));
|
||||
this.httpAuthenticationUsernameClear.tabIndex = 0;
|
||||
|
||||
this.httpAuthenticationPasswordClear.addEventListener('mouseup',
|
||||
this.clearPassword.bind(this));
|
||||
this.httpAuthenticationPasswordClear.addEventListener('keyup',
|
||||
this.dialogButtonKeyup.bind(this));
|
||||
this.httpAuthenticationPasswordClear.tabIndex = 0;
|
||||
|
||||
this.httpAuthenticationOk.addEventListener('mouseup', this);
|
||||
this.httpAuthenticationOk.addEventListener('keyup',
|
||||
this.dialogButtonKeyup.bind(this));
|
||||
this.httpAuthenticationOk.tabIndex = 0;
|
||||
|
||||
this.httpAuthenticationCancel.addEventListener('mouseup', this);
|
||||
this.httpAuthenticationCancel.addEventListener('keyup',
|
||||
this.dialogButtonKeyup.bind(this));
|
||||
this.httpAuthenticationCancel.tabIndex = 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* Default event handler for mozbrowser event and click event.
|
||||
* @param {Event} evt
|
||||
* @param {String} origin Tab ID
|
||||
*/
|
||||
handleEvent: function ad_handleEvent(evt, origin) {
|
||||
switch (evt.type) {
|
||||
case 'mozbrowserusernameandpasswordrequired':
|
||||
evt.preventDefault();
|
||||
this.currentEvents[origin] = evt;
|
||||
|
||||
// Show authentication dialog only if
|
||||
// the frame is currently displayed.
|
||||
this.show(origin);
|
||||
break;
|
||||
|
||||
case 'mouseup':
|
||||
if (evt.currentTarget === this.httpAuthenticationCancel) {
|
||||
this.focusIndex.x = 2;
|
||||
this.focusIndex.y = 1;
|
||||
this.cancelHandler();
|
||||
} else {
|
||||
this.focusIndex.x = 2;
|
||||
this.focusIndex.y = 0;
|
||||
this.confirmHandler();
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show dialog and set message.
|
||||
* @param {String} origin Tab ID
|
||||
*/
|
||||
show: function ad_show(origin) {
|
||||
this.currentOrigin = origin;
|
||||
var evt = this.currentEvents[origin];
|
||||
|
||||
this.httpAuthenticationDialog.classList.remove('hidden');
|
||||
|
||||
// XXX: We don't have a better way to detect login failed.
|
||||
if (this._confirmed[origin]) {
|
||||
//this.httpAuthenticationMessage.setAttribute('data-l10n-id',
|
||||
//'the-username-or-password-is-incorrect');
|
||||
//this.httpAuthenticationMessage.classList.add('error');
|
||||
} else {
|
||||
navigator.mozL10n.setAttributes(
|
||||
this.httpAuthenticationMessage,
|
||||
'WB_LT_AUTH_MESSAGE',
|
||||
{
|
||||
host: evt.detail.host,
|
||||
msg: evt.detail.realm
|
||||
}
|
||||
);
|
||||
/*
|
||||
navigator.mozL10n.setAttributes(this.httpAuthenticationMessage,
|
||||
'http-authentication-message',
|
||||
{ host: evt.detail.host });
|
||||
*/
|
||||
this.httpAuthenticationMessage.classList.remove('error');
|
||||
}
|
||||
|
||||
Browser.switchCursorMode(false);
|
||||
this.httpAuthenticationUsername.classList.remove('exfocus');
|
||||
this.httpAuthenticationPassword.classList.remove('exfocus');
|
||||
this.httpAuthenticationUsername.classList.remove('input');
|
||||
this.httpAuthenticationPassword.classList.remove('input');
|
||||
|
||||
this.httpAuthenticationUsernameArea.value = '';
|
||||
this.httpAuthenticationPasswordArea.value = '';
|
||||
this.httpAuthenticationUsernameArea.focus();
|
||||
|
||||
this.focusElement[0] = [];
|
||||
this.focusElement[0][0] = this.httpAuthenticationUsernameArea;
|
||||
this.focusElement[0][1] = this.httpAuthenticationUsernameClear;
|
||||
this.focusElement[1] = [];
|
||||
this.focusElement[1][0] = this.httpAuthenticationPasswordArea;
|
||||
this.focusElement[1][1] = this.httpAuthenticationPasswordClear;
|
||||
this.focusElement[2] = [];
|
||||
this.focusElement[2][0] = this.httpAuthenticationOk;
|
||||
this.focusElement[2][1] = this.httpAuthenticationCancel;
|
||||
this.focusIndex.x = 0;
|
||||
this.focusIndex.y = 0;
|
||||
},
|
||||
|
||||
/** Hide authentication dialog. */
|
||||
hide: function ad_hide() {
|
||||
var evt = this.currentEvents[this.currentOrigin];
|
||||
if (!evt) {
|
||||
return;
|
||||
}
|
||||
this.httpAuthenticationDialog.classList.add('hidden');
|
||||
this.currentOrigin = null;
|
||||
},
|
||||
|
||||
clearUsername: function ad_clearUsername() {
|
||||
this.focusIndex.x = 0;
|
||||
this.focusIndex.y = 1;
|
||||
this.httpAuthenticationUsernameArea.value = '';
|
||||
},
|
||||
|
||||
clearPassword: function ad_clearPassword() {
|
||||
this.focusIndex.x = 1;
|
||||
this.focusIndex.y = 1;
|
||||
this.httpAuthenticationPasswordArea.value = '';
|
||||
},
|
||||
|
||||
dialogButtonKeyup: function ad_dialogButtonKeyup(evt) {
|
||||
if( evt ) evt.preventDefault();
|
||||
switch( evt.keyCode ) {
|
||||
case KeyEvent.DOM_VK_RETURN :
|
||||
var elm = document.activeElement;
|
||||
elm.classList.remove('active');
|
||||
var dEvt = document.createEvent("MouseEvents");
|
||||
dEvt.initMouseEvent("mouseup", true, true, window, 0, 0, 0, 0, 0,
|
||||
false, false, false, false, 0, elm);
|
||||
elm.dispatchEvent( dEvt );
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
dialogInputSubmit: function ad_dialogInputSubmit(evt) {
|
||||
if( evt ) evt.preventDefault();
|
||||
this.dialogInputAreaBlur(evt);
|
||||
},
|
||||
|
||||
dialogInputAreaBlur: function ad_dialogInputBlur(evt) {
|
||||
if(Awesomescreen.pointerImg.style.display == 'none') {
|
||||
Browser.switchCursorMode(true);
|
||||
Browser.switchCursorMode(false);
|
||||
}
|
||||
if (evt.currentTarget === this.httpAuthenticationUsernameArea) {
|
||||
this.focusIndex.x = 0;
|
||||
this.focusIndex.y = 0;
|
||||
this.httpAuthenticationUsername.classList.remove('input');
|
||||
this.httpAuthenticationUsername.classList.add('exfocus');
|
||||
} else if(evt.currentTarget === this.httpAuthenticationPasswordArea) {
|
||||
this.focusIndex.x = 1;
|
||||
this.focusIndex.y = 0;
|
||||
this.httpAuthenticationPassword.classList.remove('input');
|
||||
this.httpAuthenticationPassword.classList.add('exfocus');
|
||||
}
|
||||
Awesomescreen.focusImgFunc(this.focusElement[this.focusIndex.x][this.focusIndex.y]);
|
||||
},
|
||||
|
||||
dialogUsernameAreaFocus: function ad_dialogUsernameAreaFocus(evt) {
|
||||
if( evt ) evt.preventDefault();
|
||||
this.httpAuthenticationUsername.classList.add('input');
|
||||
this.httpAuthenticationUsername.classList.remove('exfocus');
|
||||
},
|
||||
|
||||
dialogPasswordAreaFocus: function ad_dialogPasswordAreaFocus(evt) {
|
||||
if( evt ) evt.preventDefault();
|
||||
this.httpAuthenticationPassword.classList.add('input');
|
||||
this.httpAuthenticationPassword.classList.remove('exfocus');
|
||||
},
|
||||
|
||||
/**
|
||||
* When user clicks OK button on authentication dialog,
|
||||
* authenticate user.
|
||||
*/
|
||||
confirmHandler: function ad_confirmHandler() {
|
||||
var evt = this.currentEvents[this.currentOrigin];
|
||||
evt.detail.authenticate(this.httpAuthenticationUsernameArea.value,
|
||||
this.httpAuthenticationPasswordArea.value);
|
||||
this.httpAuthenticationUsername.classList.remove('exfocus');
|
||||
this.httpAuthenticationPassword.classList.remove('exfocus');
|
||||
this.httpAuthenticationDialog.classList.add('hidden');
|
||||
|
||||
// To remember we had ever logged in.
|
||||
this._confirmed[this.currentOrigin] = true;
|
||||
|
||||
delete this.currentEvents[this.currentOrigin];
|
||||
Browser.switchCursorMode(true);
|
||||
Awesomescreen.pointerImg.style.display = 'none';
|
||||
},
|
||||
|
||||
/**
|
||||
* When user clicks cancel button on authentication dialog or
|
||||
* when the user try to escape the dialog with the escape key,
|
||||
* cancel authentication.
|
||||
*/
|
||||
cancelHandler: function ad_cancelHandler() {
|
||||
var evt = this.currentEvents[this.currentOrigin];
|
||||
|
||||
evt.detail.cancel();
|
||||
|
||||
if (this._confirmed[this.currentOrigin]) {
|
||||
delete this._confirmed[this.currentOrigin];
|
||||
}
|
||||
|
||||
delete this.currentEvents[this.currentOrigin];
|
||||
this.httpAuthenticationUsername.classList.remove('exfocus');
|
||||
this.httpAuthenticationPassword.classList.remove('exfocus');
|
||||
this.httpAuthenticationDialog.classList.add('hidden');
|
||||
Browser.switchCursorMode(true);
|
||||
Awesomescreen.pointerImg.style.display = 'none';
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the specified tab currently has any event.
|
||||
* @param {String} origin Tab ID
|
||||
*/
|
||||
originHasEvent: function(origin) {
|
||||
return origin in this.currentEvents;
|
||||
},
|
||||
|
||||
/**
|
||||
* Clear events of the specified tab ID.
|
||||
* @param {String} origin Tab ID
|
||||
*/
|
||||
clear: function ad_clear(origin) {
|
||||
if (this.currentEvents[origin]) {
|
||||
delete this.currentEvents[origin];
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Authentication dialog displays a confirmation.
|
||||
*/
|
||||
isDisplayed: function is_displayed() {
|
||||
return !this.httpAuthenticationDialog.classList.contains('hidden');
|
||||
},
|
||||
|
||||
moveFocus: function ad_moveFocus(xd, yd) {
|
||||
if(xd < 0 || yd < 0) {
|
||||
return;
|
||||
}
|
||||
if( (!this.focusElement[xd]) || (!this.focusElement[xd][yd]) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var opt = null;
|
||||
var flg = false;
|
||||
this.focusIndex.x = xd;
|
||||
this.focusIndex.y = yd;
|
||||
if(Awesomescreen.pointerImg.style.display == 'none') {
|
||||
Browser.switchCursorMode(true);
|
||||
Browser.switchCursorMode(false);
|
||||
flg = true;
|
||||
}
|
||||
document.activeElement.blur();
|
||||
if(this.focusElement[this.focusIndex.x][this.focusIndex.y].id == 'http-authentication-username-area') {
|
||||
this.httpAuthenticationPassword.classList.remove('exfocus');
|
||||
if( !this.httpAuthenticationUsername.classList.contains('exfocus') ) {
|
||||
this.httpAuthenticationUsername.classList.add('exfocus');
|
||||
}
|
||||
} else if(this.focusElement[this.focusIndex.x][this.focusIndex.y].id == 'http-authentication-username-clear') {
|
||||
this.focusElement[this.focusIndex.x][this.focusIndex.y].focus();
|
||||
this.httpAuthenticationPassword.classList.remove('exfocus');
|
||||
if( !this.httpAuthenticationUsername.classList.contains('exfocus') ) {
|
||||
this.httpAuthenticationUsername.classList.add('exfocus');
|
||||
}
|
||||
if( flg ) opt = Awesomescreen.DEFAULT_EXCLEAR;
|
||||
} else if(this.focusElement[this.focusIndex.x][this.focusIndex.y].id == 'http-authentication-password-area') {
|
||||
this.httpAuthenticationUsername.classList.remove('exfocus');
|
||||
if( !this.httpAuthenticationPassword.classList.contains('exfocus') ) {
|
||||
this.httpAuthenticationPassword.classList.add('exfocus');
|
||||
}
|
||||
} else if(this.focusElement[this.focusIndex.x][this.focusIndex.y].id == 'http-authentication-password-clear') {
|
||||
this.httpAuthenticationUsername.classList.remove('exfocus');
|
||||
this.focusElement[this.focusIndex.x][this.focusIndex.y].focus();
|
||||
if( !this.httpAuthenticationPassword.classList.contains('exfocus') ) {
|
||||
this.httpAuthenticationPassword.classList.add('exfocus');
|
||||
}
|
||||
if( flg ) opt = Awesomescreen.DEFAULT_EXCLEAR;
|
||||
} else {
|
||||
this.focusElement[this.focusIndex.x][this.focusIndex.y].focus();
|
||||
this.httpAuthenticationUsername.classList.remove('exfocus');
|
||||
this.httpAuthenticationPassword.classList.remove('exfocus');
|
||||
}
|
||||
Awesomescreen.focusImgFunc(this.focusElement[this.focusIndex.x][this.focusIndex.y], opt);
|
||||
},
|
||||
|
||||
handleKeyEvent: function ad_handleKeyEvent(evt) {
|
||||
if( !this.isDisplayed() ) return false;
|
||||
if( document.activeElement.nodeName == 'INPUT' ) {
|
||||
return true;
|
||||
}
|
||||
switch( evt.keyCode ) {
|
||||
case KeyEvent.DOM_VK_LEFT :
|
||||
evt.preventDefault();
|
||||
this.moveFocus(this.focusIndex.x, this.focusIndex.y-1);
|
||||
break;
|
||||
case KeyEvent.DOM_VK_RIGHT :
|
||||
evt.preventDefault();
|
||||
this.moveFocus(this.focusIndex.x, this.focusIndex.y+1);
|
||||
break;
|
||||
case KeyEvent.DOM_VK_UP :
|
||||
evt.preventDefault();
|
||||
if( this.focusIndex.x == 2 ) {
|
||||
this.moveFocus(this.focusIndex.x-1, 0);
|
||||
} else {
|
||||
this.moveFocus(this.focusIndex.x-1, this.focusIndex.y);
|
||||
}
|
||||
break;
|
||||
case KeyEvent.DOM_VK_DOWN :
|
||||
evt.preventDefault();
|
||||
if( this.focusIndex.x == 1 ) {
|
||||
this.moveFocus(this.focusIndex.x+1, 0);
|
||||
} else {
|
||||
this.moveFocus(this.focusIndex.x+1, this.focusIndex.y);
|
||||
}
|
||||
break;
|
||||
case KeyEvent.DOM_VK_RETURN :
|
||||
evt.preventDefault();
|
||||
if((this.focusIndex.x == 0 && this.focusIndex.y == 0) ||
|
||||
(this.focusIndex.x == 1 && this.focusIndex.y == 0)) {
|
||||
this.focusElement[this.focusIndex.x][this.focusIndex.y].focus();
|
||||
Awesomescreen.pointerImg.style.display = 'none';
|
||||
} else {
|
||||
document.activeElement.classList.remove('active');
|
||||
document.activeElement.classList.add('active');
|
||||
}
|
||||
break;
|
||||
case KeyEvent.DOM_VK_BACK_SPACE :
|
||||
this.cancelHandler();
|
||||
return true;
|
||||
default :
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
3216
gaia/tv_apps/browser/js/awesomescreen.js
Executable file
3216
gaia/tv_apps/browser/js/awesomescreen.js
Executable file
File diff suppressed because it is too large
Load diff
239
gaia/tv_apps/browser/js/bookmarkStore.js
Executable file
239
gaia/tv_apps/browser/js/bookmarkStore.js
Executable file
|
|
@ -0,0 +1,239 @@
|
|||
/* globals BrowserDB, SyncBrowserDB, SyncManagerBridge */
|
||||
'use strict';
|
||||
|
||||
(function(exports){
|
||||
|
||||
//IFDEF_FIREFOX_SYNC
|
||||
/**
|
||||
* The root folder id of synced bookmark data.
|
||||
* @type {String}
|
||||
*/
|
||||
const SYNC_BOOKMARK_ROOT_FOLDER_ID = 'places';
|
||||
/**
|
||||
* The mobile folder id of synced bookmark data.
|
||||
* @type {String}
|
||||
*/
|
||||
const SYNC_BOOKMARK_MOBILE_FOLDER_ID = 'mobile';
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
|
||||
// TODO: for the live time bookmark data loading task.
|
||||
var BookmarkStore = {
|
||||
isSynced: false,
|
||||
|
||||
cache: [],
|
||||
|
||||
currentFolder : null,
|
||||
|
||||
//IFDEF_FIREFOX_SYNC
|
||||
syncBookmarkRootFolderCache: {
|
||||
id: SYNC_BOOKMARK_ROOT_FOLDER_ID,
|
||||
title: '',
|
||||
type: 'folder',
|
||||
readOnly: true
|
||||
},
|
||||
|
||||
syncBookmarkMobileFolderCache: {
|
||||
id: SYNC_BOOKMARK_MOBILE_FOLDER_ID,
|
||||
title: '',
|
||||
type: 'folder',
|
||||
readOnly: true
|
||||
},
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
|
||||
init: function () {
|
||||
|
||||
//IFDEF_FIREFOX_SYNC
|
||||
var l10nPromises = [];
|
||||
l10nPromises
|
||||
.push(navigator.mozL10n.formatValue('fxsync-desktop-bookmarks'));
|
||||
l10nPromises
|
||||
.push(navigator.mozL10n.formatValue('fxsync-mobile-bookmarks'));
|
||||
Promise.all(l10nPromises).then(
|
||||
([l10nFxsyncDesktopBookmarks, l10nFxsyncMobileBookmarks]) => {
|
||||
this.syncBookmarkRootFolderCache.title = l10nFxsyncDesktopBookmarks;
|
||||
this.syncBookmarkMobileFolderCache.title = l10nFxsyncMobileBookmarks;
|
||||
}
|
||||
);
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
|
||||
},
|
||||
|
||||
reset: function(cb) {
|
||||
this.cache = [];
|
||||
this.currentFolder = null;
|
||||
|
||||
return new Promise(resolve => {
|
||||
|
||||
//IFNDEF_FIREFOX_SYNC
|
||||
resolve();
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
|
||||
//IFDEF_FIREFOX_SYNC
|
||||
SyncManagerBridge.getInfo().then(message => {
|
||||
if (message.state === 'enabled' || message.state === 'syncing') {
|
||||
this.isSynced = true;
|
||||
} else {
|
||||
this.isSynced = false;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
getByRange: function(start, num, folderId, cb) {
|
||||
var self = this;
|
||||
function getCaches(){
|
||||
var length = (start + num) > self.cache.length ?
|
||||
(self.cache.length - start) : (start + num);
|
||||
var result = [];
|
||||
|
||||
for(var i = start; i < length; i++) {
|
||||
result.push(self.cache[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if(folderId !== this.currentFolder) {
|
||||
this.currentFolder = folderId;
|
||||
this.updateCache().then(() => {
|
||||
cb(getCaches());
|
||||
});
|
||||
} else {
|
||||
cb(getCaches());
|
||||
}
|
||||
},
|
||||
|
||||
getByIndex: function(index, folderId, cb) {
|
||||
var self = this;
|
||||
function getCache(){
|
||||
var result = null;
|
||||
if(index >= 0 && index < self.cache.length) {
|
||||
result = self.cache[index];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if(folderId !== this.currentFolder) {
|
||||
this.currentFolder = folderId;
|
||||
this.updateCache().then(() => {
|
||||
cb(getCache());
|
||||
});
|
||||
} else {
|
||||
cb(getCache());
|
||||
}
|
||||
},
|
||||
|
||||
//IFDEF_FIREFOX_SYNC
|
||||
_createRootFolderCache: function () {
|
||||
var self = this;
|
||||
// We query remote bookmarks by parentid with
|
||||
// SYNC_BOOKMARK_ROOT_FOLDER_ID for checking if the indexedDB has
|
||||
// remote bookmark data. If there is any remote data, we create a
|
||||
// folder item to show the synced bookmark.
|
||||
function getPlacesBookmarks() {
|
||||
return new Promise(resolve => {
|
||||
SyncBrowserDB.getBookmark({
|
||||
parentid: SYNC_BOOKMARK_ROOT_FOLDER_ID
|
||||
}, syncPlacesBookmarks => {
|
||||
if(syncPlacesBookmarks.length > 0) {
|
||||
self.cache.push(self.syncBookmarkRootFolderCache);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// If there is mobile folder and also has children in it,
|
||||
// create a folder item to show the synced mobile bookmark
|
||||
function getMobileBookmarks() {
|
||||
return new Promise(resolve => {
|
||||
SyncBrowserDB.getBookmark({
|
||||
parentid: SYNC_BOOKMARK_MOBILE_FOLDER_ID
|
||||
}, syncMobileBookmarks => {
|
||||
if(syncMobileBookmarks.length > 0) {
|
||||
self.cache.push(self.syncBookmarkMobileFolderCache);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// get bookmark data from origin indexdDB
|
||||
function getLocalBookmarks() {
|
||||
return new Promise(resolve => {
|
||||
BrowserDB.getBookmarks(localBookmarks => {
|
||||
if(localBookmarks.length > 0) {
|
||||
self.cache = self.cache.concat(localBookmarks);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return getPlacesBookmarks()
|
||||
.then(getMobileBookmarks)
|
||||
.then(getLocalBookmarks);
|
||||
},
|
||||
|
||||
_updateFolderCache: function () {
|
||||
return new Promise(resolve => {
|
||||
SyncBrowserDB.getBookmark({
|
||||
parentid: this.currentFolder
|
||||
}, syncBookmark => {
|
||||
var length = syncBookmark.length,
|
||||
bookmark = null;
|
||||
for(var i = 0; i < length; i++) {
|
||||
bookmark = syncBookmark[i];
|
||||
//hide mobile folder in sync bookmark folder
|
||||
if(this.currentFolder === SYNC_BOOKMARK_ROOT_FOLDER_ID &&
|
||||
bookmark.id === SYNC_BOOKMARK_MOBILE_FOLDER_ID) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// XXX: Now we only support two types: folder and bookmark.
|
||||
// And the data from firefox sync can't be modified.
|
||||
if(bookmark.type === 'folder' && bookmark.title) {
|
||||
bookmark.readOnly = true;
|
||||
this.cache.push(bookmark);
|
||||
} else if (bookmark.type === 'bookmark') {
|
||||
bookmark.uri = bookmark.bmkUri;
|
||||
bookmark.readOnly = true;
|
||||
this.cache.push(bookmark);
|
||||
}
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
},
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
|
||||
updateCache: function(){
|
||||
this.cache = [];
|
||||
return new Promise(resolve => {
|
||||
if(!this.isSynced) {
|
||||
// Get bookmark data from origin indexedDB
|
||||
BrowserDB.getBookmarks(bookmarks => {
|
||||
this.cache = bookmarks;
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
//IFNDEF_FIREFOX_SYNC
|
||||
resolve();
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
|
||||
//IFDEF_FIREFOX_SYNC
|
||||
if(!this.currentFolder) {
|
||||
this._createRootFolderCache().then(resolve);
|
||||
} else {
|
||||
this._updateFolderCache().then(resolve);
|
||||
}
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
exports.BookmarkStore = BookmarkStore;
|
||||
})(window);
|
||||
2030
gaia/tv_apps/browser/js/browser_db.js
Executable file
2030
gaia/tv_apps/browser/js/browser_db.js
Executable file
File diff suppressed because it is too large
Load diff
607
gaia/tv_apps/browser/js/browser_dialog.js
Executable file
607
gaia/tv_apps/browser/js/browser_dialog.js
Executable file
|
|
@ -0,0 +1,607 @@
|
|||
/* global Awesomescreen */
|
||||
/* global Browser */
|
||||
/* global BrowserDB */
|
||||
/* global KeyEvent */
|
||||
/* global Settings */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* The dialog listen to mozbrowsershowmodalprompt event.
|
||||
* (alert/confirm/prompt)
|
||||
*
|
||||
* @namespace Dialog
|
||||
*/
|
||||
var BrowserDialog = {
|
||||
// Dialog Event
|
||||
dialogEvt: null,
|
||||
browserDialogInputArea: null,
|
||||
PORT_MIN: 0,
|
||||
PORT_MAX: 65535,
|
||||
focusElement: [],
|
||||
focusIndex: {x:0, y:0},
|
||||
defaultFocusIndex: {x:0, y:0},
|
||||
deferredActions: new Map(),
|
||||
|
||||
/** Get all elements when inited. */
|
||||
getAllElements: function dialog_getAllElements() {
|
||||
var toCamelCase = function toCamelCase(str) {
|
||||
return str.replace(/\-(.)/g, function replacer(str, p1) {
|
||||
return p1.toUpperCase();
|
||||
});
|
||||
};
|
||||
|
||||
var elementIDs = [
|
||||
'browser-dialog-base', 'browser-dialog',
|
||||
'browser-dialog-title', 'browser-dialog-msg',
|
||||
'browser-dialog-input',
|
||||
'browser-dialog-input-area',
|
||||
'browser-dialog-input-clear',
|
||||
'browser-dialog-button',
|
||||
'browser-dialog-button1', 'browser-dialog-button2',
|
||||
];
|
||||
|
||||
// Loop and add element with camel style name to Dialog attribute.
|
||||
elementIDs.forEach(function createElementRef(name) {
|
||||
this[toCamelCase(name)] = document.getElementById(name);
|
||||
}, this);
|
||||
},
|
||||
|
||||
/** Initialization. Get DOM elements and add listeners. */
|
||||
init: function dialog_init() {
|
||||
// Get all elements initially.
|
||||
this.getAllElements();
|
||||
|
||||
this.browserDialogBase.addEventListener('mouseup',
|
||||
this.dialogBaseClick.bind(this));
|
||||
|
||||
// Add event listener(dialog)
|
||||
this.browserDialogButton1.addEventListener('mouseup',
|
||||
this.dialogButton1.bind(this));
|
||||
this.browserDialogButton1.addEventListener('keyup',
|
||||
this.dialogButtonKeyup.bind(this));
|
||||
|
||||
this.browserDialogButton2.addEventListener('mouseup',
|
||||
this.dialogButton2.bind(this));
|
||||
this.browserDialogButton2.addEventListener('keyup',
|
||||
this.dialogButtonKeyup.bind(this));
|
||||
|
||||
this.browserDialogInput.addEventListener('submit',
|
||||
this.dialogInput.bind(this));
|
||||
this.browserDialogInputClear.addEventListener('mouseup',
|
||||
this.dialogInputClear.bind(this));
|
||||
this.browserDialogInputClear.addEventListener('keyup',
|
||||
this.dialogButtonKeyup.bind(this));
|
||||
|
||||
this.browserDialogInputArea.addEventListener('blur',
|
||||
this.dialogInputAreaBlur.bind(this));
|
||||
this.browserDialogInputArea.addEventListener('focus',
|
||||
this.dialogInputAreaFocus.bind(this));
|
||||
this.browserDialogInputArea.addEventListener('mousedown',
|
||||
this.dialogInputAreaClick.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* create Dialog
|
||||
*/
|
||||
createDialog: function dialog_createDialog(type, evt) {
|
||||
var opt;
|
||||
|
||||
this.cancelDialog();
|
||||
if( evt != null ) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
this.dialogEvt = evt;
|
||||
this.browserDialogBase.classList.remove('hide');
|
||||
this.browserDialogBase.dataset.type = type;
|
||||
|
||||
var promise;
|
||||
switch(type) {
|
||||
case 'del_cookie':
|
||||
opt = {
|
||||
title: null,
|
||||
msg: 'WB_LT_CONFIRM_DELETE_COOKIES',
|
||||
bt1: 'LT_CANCEL',
|
||||
bt2: 'WB_LT_CLEAR'
|
||||
};
|
||||
break;
|
||||
|
||||
case 'clear_history':
|
||||
opt = {
|
||||
title: null,
|
||||
msg: 'WB_LT_CLEAR_ALL_HISTORY',
|
||||
bt1: 'LT_CANCEL',
|
||||
bt2: 'WB_LT_CLEAR'
|
||||
};
|
||||
break;
|
||||
|
||||
case 'max_bookmark':
|
||||
opt = {
|
||||
title: null,
|
||||
msg: 'WB_LT_BOOKMARK_ERROR_1',
|
||||
bt1: 'LT_CANCEL',
|
||||
bt2: null
|
||||
};
|
||||
break;
|
||||
|
||||
case 'alert':
|
||||
opt = {
|
||||
title: null,
|
||||
msg: {raw: evt.detail.message},
|
||||
bt1: 'LT_WB_OK',
|
||||
bt2: null
|
||||
};
|
||||
break;
|
||||
|
||||
case 'prompt':
|
||||
opt = {
|
||||
title: null,
|
||||
msg: {raw: evt.detail.message},
|
||||
bt1: 'LT_CANCEL',
|
||||
bt2: 'LT_WB_OK'
|
||||
};
|
||||
break;
|
||||
|
||||
case 'confirm':
|
||||
opt = {
|
||||
title: null,
|
||||
msg: {raw: evt.detail.message},
|
||||
bt1: 'LT_CANCEL',
|
||||
bt2: 'LT_WB_OK'
|
||||
};
|
||||
break;
|
||||
|
||||
//IFDEF_FIREFOX_SYNC
|
||||
case 'signout_confirm':
|
||||
opt = {
|
||||
title: 'fxsync-confirm-sign-out-title',
|
||||
msg: 'fxsync-confirm-sign-out-message',
|
||||
bt1: 'LT_CANCEL',
|
||||
bt2: 'fxsync-sign-out'
|
||||
};
|
||||
var deferred = {};
|
||||
deferred.promise = new Promise(function(resolve, reject) {
|
||||
deferred.resolve = resolve;
|
||||
deferred.reject = reject;
|
||||
});
|
||||
promise = deferred.promise;
|
||||
this.deferredActions.set('signout_confirm', deferred);
|
||||
break;
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
Browser.switchCursorMode(false);
|
||||
|
||||
// title
|
||||
if (opt.title) {
|
||||
this.browserDialogTitle.setAttribute('data-l10n-id', opt.title);
|
||||
} else {
|
||||
this.browserDialogTitle.removeAttribute('data-l10n-id');
|
||||
this.browserDialogTitle.textContent = '';
|
||||
}
|
||||
|
||||
// msg
|
||||
if(opt.msg) {
|
||||
if (typeof opt.msg === 'string') {
|
||||
this.browserDialogMsg.setAttribute('data-l10n-id', opt.msg);
|
||||
} else if (opt.msg.hasOwnProperty('raw')) {
|
||||
this.browserDialogMsg.innerHTML = opt.msg.raw.replace(/\\n/g, '<br>');
|
||||
}
|
||||
} else {
|
||||
this.browserDialogMsg.removeAttribute('data-l10n-id');
|
||||
this.browserDialogMsg.textContent = '';
|
||||
}
|
||||
|
||||
var countIndex = 0;
|
||||
this.focusElement = [];
|
||||
this.focusElement[0] = [];
|
||||
this.focusElement[1] = [];
|
||||
this.defaultFocusIndex = {x:0, y:0};
|
||||
// button1
|
||||
if(opt.bt1) {
|
||||
this.browserDialogButton1.blur();
|
||||
this.browserDialogButton1.setAttribute('data-l10n-id', opt.bt1);
|
||||
this.browserDialogButton1.dataset.type = type;
|
||||
this.browserDialogButton1.classList.add('visible');
|
||||
}
|
||||
|
||||
// button2
|
||||
if(opt.bt2) {
|
||||
this.browserDialogButton2.blur();
|
||||
this.browserDialogButton2.setAttribute('data-l10n-id', opt.bt2);
|
||||
this.browserDialogButton2.dataset.type = type;
|
||||
this.browserDialogButton2.classList.add('visible');
|
||||
}
|
||||
|
||||
var width = Browser.SCREEN_WIDTH;
|
||||
if( Browser.sideBlock.dataset.sidebar == 'true' ) {
|
||||
width -= Browser.SIDE_WINDOW_WIDTH;
|
||||
}
|
||||
|
||||
if(type == 'prompt') {
|
||||
this.browserDialogInput.style.display = 'block';
|
||||
this.browserDialogInput.classList.remove('exfocus');
|
||||
this.browserDialogInput.classList.remove('input');
|
||||
this.browserDialogInputClear.tabIndex = 0;
|
||||
countIndex = 0;
|
||||
this.focusElement[1][countIndex++] = this.browserDialogInputArea;
|
||||
this.focusElement[1][countIndex++] = this.browserDialogInputClear;
|
||||
//this.browserDialogInput.insertBefore(this.browserDialogInputArea,
|
||||
// this.browserDialogInputClear);
|
||||
this.browserDialogInputArea.type = 'text';
|
||||
this.browserDialogInputArea.value = '';
|
||||
} else {
|
||||
this.browserDialogInput.style.display = 'none';
|
||||
}
|
||||
|
||||
// initialize position
|
||||
countIndex = 0;
|
||||
if(opt.bt1 && opt.bt2) {
|
||||
this.defaultFocusIndex.x = 0;
|
||||
this.defaultFocusIndex.y = 0;
|
||||
this.focusIndex.x = 0;
|
||||
|
||||
//IFDEF_FIREFOX_SYNC
|
||||
if(type === 'signout_confirm') {
|
||||
this.focusIndex.y = 0;
|
||||
this.focusElement[0][countIndex++] = this.browserDialogButton1;
|
||||
this.focusElement[0][countIndex++] = this.browserDialogButton2;
|
||||
} else {
|
||||
this.focusIndex.y = 1;
|
||||
this.focusElement[0][countIndex++] = this.browserDialogButton2;
|
||||
this.focusElement[0][countIndex++] = this.browserDialogButton1;
|
||||
}
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
|
||||
//IFNDEF_FIREFOX_SYNC
|
||||
this.focusIndex.y = 1;
|
||||
this.focusElement[0][countIndex++] = this.browserDialogButton2;
|
||||
this.focusElement[0][countIndex++] = this.browserDialogButton1;
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
} else if(opt.bt1) {
|
||||
this.defaultFocusIndex.x = 0;
|
||||
this.defaultFocusIndex.y = 0;
|
||||
this.focusIndex.x = 0;
|
||||
this.focusIndex.y = 0;
|
||||
this.focusElement[0][countIndex++] = this.browserDialogButton1;
|
||||
}
|
||||
Awesomescreen.focusImgFunc(
|
||||
this.focusElement[this.focusIndex.x][this.focusIndex.y]);
|
||||
this.focusElement[this.focusIndex.x][this.focusIndex.y].focus();
|
||||
return promise;
|
||||
},
|
||||
|
||||
isDisplayed: function dialog_isDisplayed() {
|
||||
return !this.browserDialogBase.classList.contains('hide');
|
||||
},
|
||||
|
||||
dialogBaseClick: function dialog_dialogBaseClick(evt) {
|
||||
if( evt ) {
|
||||
evt.stopPropagation();
|
||||
}
|
||||
},
|
||||
|
||||
dialogButtonKeyup: function dialog_dialogButton1Keyup(evt) {
|
||||
if( evt ) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
switch( evt.keyCode ) {
|
||||
case KeyEvent.DOM_VK_RETURN :
|
||||
var elm = document.activeElement;
|
||||
elm.classList.remove('active');
|
||||
var dEvt = document.createEvent('MouseEvents');
|
||||
dEvt.initMouseEvent('mouseup', true, true, window, 0, 0, 0, 0, 0,
|
||||
false, false, false, false, 0, elm);
|
||||
elm.dispatchEvent( dEvt );
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
completeDeferredAction: function(type, resolution) {
|
||||
if (!this.deferredActions.has(type)) {
|
||||
return;
|
||||
}
|
||||
this.deferredActions.get(type)[resolution]();
|
||||
this.deferredActions.delete(type);
|
||||
},
|
||||
|
||||
dialogButton1: function dialog_dialogButton1(evt) {
|
||||
if (evt) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
|
||||
var type = this.browserDialogButton1.dataset.type;
|
||||
|
||||
switch(type) {
|
||||
case 'del_cookie':
|
||||
case 'clear_history':
|
||||
case 'max_bookmark':
|
||||
case 'alert':
|
||||
case 'prompt':
|
||||
case 'confirm':
|
||||
case 'signout_confirm':
|
||||
this.dialogButton2End(evt.currentTarget);
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
this.completeDeferredAction(type, 'reject');
|
||||
},
|
||||
|
||||
dialogButton2End: function dialog_dialogButton2End(target) {
|
||||
// Animation end event
|
||||
var end_event = (function() {
|
||||
target.removeEventListener('transitionend', end_event, false);
|
||||
BrowserDialog.cancelDialog();
|
||||
});
|
||||
target.addEventListener('transitionend', end_event, false);
|
||||
},
|
||||
|
||||
dialogButton2: function dialog_dialogButton2(evt) {
|
||||
if (evt) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
|
||||
this.argEvt = evt.currentTarget;
|
||||
var type = this.browserDialogButton2.dataset.type;
|
||||
switch (type) {
|
||||
case 'del_cookie':
|
||||
var request = navigator.mozApps.getSelf();
|
||||
request.onsuccess = function() {
|
||||
var rtn = request.result.clearBrowserData();
|
||||
rtn.onsuccess = function() {
|
||||
//BrowserDialog.cancelDialog();
|
||||
BrowserDialog.dialogButton2End(BrowserDialog.argEvt);
|
||||
};
|
||||
rtn.onerror = function() {
|
||||
//BrowserDialog.cancelDialog();
|
||||
BrowserDialog.dialogButton2End(BrowserDialog.argEvt);
|
||||
Settings.clearCookieFailed();
|
||||
};
|
||||
};
|
||||
request.onerror = function() {
|
||||
//BrowserDialog.cancelDialog();
|
||||
BrowserDialog.dialogButton2End(BrowserDialog.argEvt);
|
||||
Settings.clearCookieFailed();
|
||||
};
|
||||
break;
|
||||
|
||||
case 'clear_history':
|
||||
//BrowserDB.clearHistory(this.cancelDialog.bind(this));
|
||||
BrowserDB.clearHistory(
|
||||
function(){
|
||||
BrowserDialog.dialogButton2End(BrowserDialog.argEvt);
|
||||
}.bind(this));
|
||||
Awesomescreen.selectTopSites();
|
||||
break;
|
||||
|
||||
case 'prompt':
|
||||
if (this.dialogEvt != null) {
|
||||
this.dialogEvt.detail.returnValue = this.browserDialogInputArea.value;
|
||||
if (this.dialogEvt.detail.unblock) {
|
||||
this.dialogEvt.detail.unblock();
|
||||
}
|
||||
this.dialogEvt = null;
|
||||
}
|
||||
//this.cancelDialog();
|
||||
this.dialogButton2End(this.argEvt);
|
||||
break;
|
||||
|
||||
case 'confirm':
|
||||
if (this.dialogEvt != null) {
|
||||
this.dialogEvt.detail.returnValue = true;
|
||||
if (this.dialogEvt.detail.unblock) {
|
||||
this.dialogEvt.detail.unblock();
|
||||
}
|
||||
this.dialogEvt = null;
|
||||
}
|
||||
//this.cancelDialog();
|
||||
this.dialogButton2End(this.argEvt);
|
||||
break;
|
||||
|
||||
case 'signout_confirm':
|
||||
BrowserDialog.dialogButton2End(BrowserDialog.argEvt);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
this.completeDeferredAction(type, 'resolve');
|
||||
},
|
||||
|
||||
dialogInput: function dialog_dialogInput(evt) {
|
||||
if( evt ) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
this.browserDialogInputArea.blur();
|
||||
},
|
||||
|
||||
dialogInputAreaFocus: function dialog_dialogInputAreaFocus(evt) {
|
||||
if( evt ) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
this.browserDialogInput.classList.add('input');
|
||||
this.browserDialogInput.classList.remove('exfocus');
|
||||
},
|
||||
|
||||
dialogInputClear: function dialog_dialogInputClear() {
|
||||
this.browserDialogInputArea.value = '';
|
||||
},
|
||||
|
||||
dialogInputAreaKeydown: function dialog_dialogInputKeydown(evt) {
|
||||
if( evt ) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
switch( evt.keyCode ) {
|
||||
case KeyEvent.DOM_VK_RETURN :
|
||||
this.moveFocus(this.focusIndex.x+1, 0);
|
||||
break;
|
||||
}
|
||||
},
|
||||
dialogInputAreaClick: function dialog_dialogInputAreaClick(evt) {
|
||||
Browser.switchCursorMode(true);
|
||||
Browser.switchCursorMode(false);
|
||||
|
||||
this.dialogInputAreaFocus();
|
||||
|
||||
this.focusIndex.x = 1;
|
||||
this.focusIndex.y = 0;
|
||||
this.focusElement[this.focusIndex.x][this.focusIndex.y].focus();
|
||||
Awesomescreen.pointerImg.style.display = 'none';
|
||||
},
|
||||
dialogInputAreaBlur: function dialog_dialogInputBlur() {
|
||||
if(Awesomescreen.pointerImg.style.display == 'none') {
|
||||
Browser.switchCursorMode(true);
|
||||
Browser.switchCursorMode(false);
|
||||
}
|
||||
BrowserDialog.browserDialogInput.classList.remove('input');
|
||||
BrowserDialog.browserDialogInput.classList.add('exfocus');
|
||||
var self = this;
|
||||
setTimeout( function() {
|
||||
self.focusIndex.x = 1;
|
||||
self.focusIndex.y = 0;
|
||||
Awesomescreen.focusImgFunc(self.browserDialogInputArea);
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
/**
|
||||
* cancel confirm dialog
|
||||
*/
|
||||
cancelDialog: function dialog_cancelDialog() {
|
||||
if( !BrowserDialog.isDisplayed() ) {
|
||||
return;
|
||||
}
|
||||
switch( BrowserDialog.browserDialogButton1.dataset.type ) {
|
||||
case 'alert':
|
||||
case 'prompt':
|
||||
if( BrowserDialog.dialogEvt != null ) {
|
||||
BrowserDialog.dialogEvt.detail.returnValue = null;
|
||||
if( BrowserDialog.dialogEvt.detail.unblock ) {
|
||||
BrowserDialog.dialogEvt.detail.unblock();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'confirm':
|
||||
if( BrowserDialog.dialogEvt != null ) {
|
||||
BrowserDialog.dialogEvt.detail.returnValue = false;
|
||||
if( BrowserDialog.dialogEvt.detail.unblock ) {
|
||||
BrowserDialog.dialogEvt.detail.unblock();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
if( BrowserDialog.dialogEvt != null ) {
|
||||
BrowserDialog.dialogEvt = null;
|
||||
}
|
||||
// hide dialog
|
||||
if( BrowserDialog.browserDialogInputArea ) {
|
||||
BrowserDialog.browserDialogInput.classList.remove('exfocus');
|
||||
BrowserDialog.browserDialogInput.classList.remove('input');
|
||||
}
|
||||
BrowserDialog.browserDialogBase.classList.add('hide');
|
||||
BrowserDialog.browserDialogButton1.classList.remove('visible');
|
||||
BrowserDialog.browserDialogButton2.classList.remove('visible');
|
||||
Browser.switchCursorMode(true);
|
||||
Awesomescreen.pointerImg.style.display = 'none';
|
||||
},
|
||||
|
||||
moveFocus: function dialog_moveFocus(xd, yd) {
|
||||
if(!this.focusElement[xd]) {
|
||||
return;
|
||||
}
|
||||
if(( xd < 0 || yd < 0 ) || ( !this.focusElement[xd][yd] )) {
|
||||
if(( !this.browserDialogButton2.classList.contains('visible') ) &&
|
||||
( Awesomescreen.pointerImg.style.display !== 'block' )) {
|
||||
Browser.switchCursorMode(true);
|
||||
Browser.switchCursorMode(false);
|
||||
var x = this.defaultFocusIndex.x;
|
||||
var y = this.defaultFocusIndex.y;
|
||||
this.focusIndex.x = x;
|
||||
this.focusIndex.y = y;
|
||||
Awesomescreen.focusImgFunc(this.focusElement[x][y]);
|
||||
this.focusElement[x][y].focus();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.focusIndex.x = xd;
|
||||
this.focusIndex.y = yd;
|
||||
var opt = null;
|
||||
var flg = false;
|
||||
if(Awesomescreen.pointerImg.style.display == 'none') {
|
||||
Browser.switchCursorMode(true);
|
||||
Browser.switchCursorMode(false);
|
||||
flg = true;
|
||||
}
|
||||
document.activeElement.blur();
|
||||
Awesomescreen.focusImgFunc(
|
||||
this.focusElement[this.focusIndex.x][this.focusIndex.y], opt);
|
||||
if(this.focusElement[this.focusIndex.x][this.focusIndex.y].nodeName ==
|
||||
'INPUT') {
|
||||
if( !this.browserDialogInput.classList.contains('exfocus') ) {
|
||||
this.browserDialogInput.classList.add('exfocus');
|
||||
}
|
||||
} else if(this.focusElement[this.focusIndex.x][this.focusIndex.y].id ==
|
||||
'browser-dialog-input-clear') {
|
||||
this.focusElement[this.focusIndex.x][this.focusIndex.y].focus();
|
||||
if( !this.browserDialogInput.classList.contains('exfocus') ) {
|
||||
this.browserDialogInput.classList.add('exfocus');
|
||||
}
|
||||
if( flg ) {
|
||||
opt = Awesomescreen.DEFAULT_EXCLEAR;
|
||||
}
|
||||
} else {
|
||||
this.focusElement[this.focusIndex.x][this.focusIndex.y].focus();
|
||||
this.browserDialogInput.classList.remove('exfocus');
|
||||
}
|
||||
},
|
||||
|
||||
handleKeyEvent: function dialog_handleKeyEvent(evt) {
|
||||
if( !BrowserDialog.isDisplayed() ) {
|
||||
return false;
|
||||
}
|
||||
if( document.activeElement.nodeName == 'INPUT' ) {
|
||||
return true;
|
||||
}
|
||||
switch( evt.keyCode ) {
|
||||
case KeyEvent.DOM_VK_LEFT :
|
||||
evt.preventDefault();
|
||||
this.moveFocus(this.focusIndex.x, this.focusIndex.y-1);
|
||||
break;
|
||||
case KeyEvent.DOM_VK_RIGHT :
|
||||
evt.preventDefault();
|
||||
this.moveFocus(this.focusIndex.x, this.focusIndex.y+1);
|
||||
break;
|
||||
case KeyEvent.DOM_VK_UP :
|
||||
evt.preventDefault();
|
||||
this.moveFocus(this.focusIndex.x+1, 0);
|
||||
break;
|
||||
case KeyEvent.DOM_VK_DOWN :
|
||||
evt.preventDefault();
|
||||
this.moveFocus(this.focusIndex.x-1, this.defaultFocusIndex.y);
|
||||
break;
|
||||
case KeyEvent.DOM_VK_RETURN :
|
||||
evt.preventDefault();
|
||||
if(this.focusIndex.x === 1 && this.focusIndex.y === 0) {
|
||||
this.focusElement[this.focusIndex.x][this.focusIndex.y].focus();
|
||||
Awesomescreen.pointerImg.style.display = 'none';
|
||||
} else {
|
||||
document.activeElement.classList.remove('active');
|
||||
document.activeElement.classList.add('active');
|
||||
}
|
||||
break;
|
||||
case KeyEvent.DOM_VK_BACK_SPACE :
|
||||
BrowserDialog.cancelDialog();
|
||||
return true;
|
||||
default :
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
77
gaia/tv_apps/browser/js/connection_handler.js
Executable file
77
gaia/tv_apps/browser/js/connection_handler.js
Executable file
|
|
@ -0,0 +1,77 @@
|
|||
/* global ConnectionManager, ConnectionHandler, Browser,
|
||||
UrlHelper, Awesomescreen */
|
||||
'use strict';
|
||||
(function(exports) {
|
||||
var ConnectionHandler = function() {};
|
||||
|
||||
ConnectionHandler.prototype = {
|
||||
_active: false,
|
||||
_messageQueue: [],
|
||||
|
||||
init: function() {
|
||||
this._connectionManager = new ConnectionManager();
|
||||
this._connectionManager.init(['iac-webpage-open']);
|
||||
this._connectionManager.on('view', this.preHandleMessage.bind(this));
|
||||
},
|
||||
|
||||
preHandleMessage: function(detail) {
|
||||
if (this._active) {
|
||||
this._digestMessage();
|
||||
this.openPage(detail.url);
|
||||
} else {
|
||||
this._messageQueue.push(detail);
|
||||
}
|
||||
},
|
||||
|
||||
_digestMessage: function() {
|
||||
if (this._messageQueue.length !== 0) {
|
||||
this._messageQueue.forEach(function(detail){
|
||||
this.openPage(detail.url);
|
||||
}.bind(this));
|
||||
this._messageQueue = [];
|
||||
}
|
||||
},
|
||||
|
||||
openPage: function(url) {
|
||||
url = Browser.getUrlFromInput(url);
|
||||
if (Browser.currentInfo && Browser.currentInfo.url &&
|
||||
UrlHelper.isURL(Browser.currentInfo.url)) {
|
||||
var ev = {
|
||||
detail: {
|
||||
url: url,
|
||||
frameElement: null
|
||||
}
|
||||
};
|
||||
Awesomescreen.openNewTab(ev);
|
||||
} else {
|
||||
Browser.navigate(url);
|
||||
}
|
||||
this.bringToFront();
|
||||
},
|
||||
|
||||
bringToFront: function() {
|
||||
if (document.visibilityState === 'hidden') {
|
||||
navigator.mozApps.getSelf().onsuccess = function(evt) {
|
||||
var app = evt.target.result;
|
||||
if (app) {
|
||||
app.launch();
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this._active = true;
|
||||
this._digestMessage();
|
||||
},
|
||||
|
||||
pause: function() {
|
||||
this._active = false;
|
||||
},
|
||||
};
|
||||
|
||||
exports.ConnectionHandler = ConnectionHandler;
|
||||
}(window));
|
||||
|
||||
var connectionHandler = new ConnectionHandler();
|
||||
connectionHandler.init();
|
||||
76
gaia/tv_apps/browser/js/fte.js
Executable file
76
gaia/tv_apps/browser/js/fte.js
Executable file
|
|
@ -0,0 +1,76 @@
|
|||
/* 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 Browser */
|
||||
/* global FTEWizard */
|
||||
/* global ResourceLoader */
|
||||
/* global SyncManagerBridge */
|
||||
|
||||
// XXX: If Firefox sign in is available outside of the browser app, we'll have
|
||||
// to remove or redesign FTE.
|
||||
|
||||
'use strict';
|
||||
|
||||
(function (exports) {
|
||||
|
||||
if (localStorage.getItem('browserFTE.fteskip')) {
|
||||
return;
|
||||
}
|
||||
|
||||
var FTE = {
|
||||
|
||||
fteContainer: null,
|
||||
|
||||
fteWizard: null,
|
||||
|
||||
onuninit: function () {},
|
||||
|
||||
init(options) {
|
||||
this.onuninit = options.onuninit;
|
||||
|
||||
ResourceLoader.loadByName('fte')
|
||||
.then(node => {
|
||||
document.body.appendChild(node);
|
||||
|
||||
this.fteContainer = document.getElementById('fte-container');
|
||||
|
||||
this.fteWizard = new FTEWizard('browserFTE');
|
||||
this.fteWizard.init({
|
||||
container: this.fteContainer,
|
||||
propagateKeyEvent: true,
|
||||
onfinish: () => {
|
||||
SyncManagerBridge.enable();
|
||||
this.uninit();
|
||||
},
|
||||
onskip: () => {
|
||||
this.uninit();
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('visibilitychange', this);
|
||||
});
|
||||
},
|
||||
|
||||
handleEvent() {
|
||||
if (!document.hidden) {
|
||||
this.fteWizard.focus();
|
||||
}
|
||||
},
|
||||
|
||||
uninit() {
|
||||
document.body.removeChild(this.fteContainer);
|
||||
document.removeEventListener('visibilitychange', this);
|
||||
ResourceLoader.unloadByName('fte');
|
||||
this.onuninit();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
exports.FTE = FTE;
|
||||
exports.FTE.init({onuninit: () => {
|
||||
Browser.switchCursorMode(true);
|
||||
delete exports.FTE;
|
||||
}});
|
||||
|
||||
})(window);
|
||||
338
gaia/tv_apps/browser/js/historyStore.js
Executable file
338
gaia/tv_apps/browser/js/historyStore.js
Executable file
|
|
@ -0,0 +1,338 @@
|
|||
/* globals BrowserDB, SyncBrowserDB, SyncManagerBridge */
|
||||
'use strict';
|
||||
|
||||
(function(exports){
|
||||
|
||||
const INITIAL_HISTORY_COUNT = 21;
|
||||
|
||||
const DAY_IN_MILLI_SECOND = 86400000;
|
||||
|
||||
// TODO: for the live time history data loading task.
|
||||
var HistoryStore = {
|
||||
isSynced: false,
|
||||
|
||||
cache: new Map(),
|
||||
|
||||
cacheIndexStartAt: 0,
|
||||
|
||||
cacheIndexEndAt: 0,
|
||||
|
||||
lastDataIndex: null,
|
||||
|
||||
currentFolder: null,
|
||||
|
||||
isHandlingCache: false,
|
||||
|
||||
cacheEventQueue: [],
|
||||
|
||||
reset: function (cb) {
|
||||
return new Promise(resolve => {
|
||||
this.cache.clear();
|
||||
this.cacheIndexStartAt = 0;
|
||||
this.cacheIndexEndAt = 0;
|
||||
this.lastDataIndex = null;
|
||||
this.currentFolder = null;
|
||||
this.isHandlingCache = false;
|
||||
this.cacheEventQueue = [];
|
||||
//IFNDEF_FIREFOX_SYNC
|
||||
resolve();
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
|
||||
//IFDEF_FIREFOX_SYNC
|
||||
SyncManagerBridge.getInfo().then(message => {
|
||||
if (message.state === 'enabled' || message.state === 'syncing') {
|
||||
this.isSynced = true;
|
||||
} else {
|
||||
this.isSynced = false;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
getByRange: function (start, num, folderId, cb) {
|
||||
var self = this;
|
||||
function getCaches(){
|
||||
|
||||
var length = (start + num) > self.cache.size ?
|
||||
(self.cache.size - start) : (start + num);
|
||||
var result = [];
|
||||
|
||||
for(var i = start; i < length; i++) {
|
||||
result.push(self.cache.get(i));
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
if(folderId !== this.currentFolder) {
|
||||
this.currentFolder = folderId;
|
||||
this.fetchCache().then(() => {
|
||||
cb(getCaches());
|
||||
});
|
||||
} else {
|
||||
cb(getCaches());
|
||||
}
|
||||
},
|
||||
|
||||
//IFDEF_FIREFOX_SYNC
|
||||
traverseNextCache: function(cacheIndex) {
|
||||
return new Promise(resolve => {
|
||||
var traverse = () =>{
|
||||
var start = this.cache.get(this.cacheIndexStartAt).timestamp;
|
||||
SyncBrowserDB.getHistoryTimestamp(start, 'next', false,
|
||||
timestamp => {
|
||||
if(timestamp) {
|
||||
this.updateCacheByNextHistory(timestamp).then(() => {
|
||||
var history = this.cache.get(cacheIndex);
|
||||
if(!history) {
|
||||
traverse();
|
||||
} else {
|
||||
resolve(history);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
resolve(null);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
traverse();
|
||||
});
|
||||
},
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
|
||||
//IFDEF_FIREFOX_SYNC
|
||||
traversePreviousCache: function(cacheIndex) {
|
||||
return new Promise(resolve => {
|
||||
var traverse = () => {
|
||||
var start = this.cache.get(this.cacheIndexEndAt).timestamp;
|
||||
SyncBrowserDB.getHistoryTimestamp(start, 'prev', false,
|
||||
timestamp => {
|
||||
if(timestamp) {
|
||||
this.updateCacheByPreviousHistory(timestamp).then(() => {
|
||||
var history = this.cache.get(cacheIndex);
|
||||
if(!history) {
|
||||
traverse();
|
||||
} else {
|
||||
resolve(history);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.lastDataIndex = this.cacheIndexEndAt;
|
||||
resolve(null);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
traverse();
|
||||
});
|
||||
},
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
|
||||
getCache: function(cacheIndex) {
|
||||
return new Promise(resolve => {
|
||||
var history = null;
|
||||
|
||||
//IFNDEF_FIREFOX_SYNC
|
||||
if(this.cacheIndexStartAt <= cacheIndex <= this.cacheIndexEndAt) {
|
||||
resolve(null);
|
||||
} else {
|
||||
history = this.cache.get(cacheIndex);
|
||||
resolve(history);
|
||||
}
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
|
||||
//IFDEF_FIREFOX_SYNC
|
||||
if(cacheIndex < this.cacheIndexStartAt) {
|
||||
this.traverseNextCache(cacheIndex).then(history => {
|
||||
resolve(history);
|
||||
});
|
||||
} else if (cacheIndex > this.cacheIndexEndAt) {
|
||||
if(this.lastDataIndex && cacheIndex > this.lastDataIndex) {
|
||||
resolve(history);
|
||||
} else {
|
||||
this.traversePreviousCache(cacheIndex).then(history => {
|
||||
resolve(history);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
history = this.cache.get(cacheIndex);
|
||||
resolve(history);
|
||||
}
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
handleCacheEvent: function() {
|
||||
if(!this.isHandlingCache) {
|
||||
this.isHandlingCache = true;
|
||||
var handler = this.cacheEventQueue.shift();
|
||||
handler().then(() => {
|
||||
this.isHandlingCache = false;
|
||||
if(this.cacheEventQueue.length > 0) {
|
||||
this.handleCacheEvent();
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
getByIndex: function (index, folderId, cb) {
|
||||
var handler = () => new Promise(resolve => {
|
||||
if(folderId !== this.currentFolder) {
|
||||
this.currentFolder = folderId;
|
||||
this.fetchCache().then(() => {
|
||||
this.getCache(index).then(history => {
|
||||
cb(history);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
this.getCache(index).then(history => {
|
||||
cb(history);
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
this.cacheEventQueue.push(handler);
|
||||
this.handleCacheEvent();
|
||||
},
|
||||
|
||||
fetchCache: function () {
|
||||
this.cache.clear();
|
||||
this.cacheIndexStartAt = 0;
|
||||
this.cacheIndexEndAt = -1;
|
||||
this.lastDataIndex = null;
|
||||
|
||||
return new Promise( resolve => {
|
||||
if(!this.isSynced) {
|
||||
// Get bookmark data from origin indexdDB
|
||||
BrowserDB.getHistory(localHistory => {
|
||||
localHistory.forEach(history => {
|
||||
this.cacheIndexEndAt++;
|
||||
this.cache.set(this.cacheIndexEndAt, history);
|
||||
});
|
||||
this.lastDataIndex = this.cacheIndexEndAt;
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
//IFNDEF_FIREFOX_SYNC
|
||||
resolve();
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
|
||||
//IFDEF_FIREFOX_SYNC
|
||||
if(!this.currentFolder) {
|
||||
// XXX: Ideally we should use Promise.all for
|
||||
// SyncBrowserDB.getBookmark and BrowserDB.getBookmarks.
|
||||
// But now the implementation of these function are not
|
||||
// return Promise.
|
||||
SyncBrowserDB.getHistoryTimestamp(Date.now(), 'prev', true,
|
||||
timestamp => {
|
||||
// We query remote history by parentid for checking if
|
||||
// the indexedDB has remote history data. If there is
|
||||
// any remote data, we create a folder item to show the
|
||||
// synced history.
|
||||
if(timestamp) {
|
||||
this.cacheIndexEndAt++;
|
||||
this.cache.set(this.cacheIndexEndAt, {
|
||||
id: 'sync_history',
|
||||
title: 'Synced History',
|
||||
type: 'folder',
|
||||
readOnly: true
|
||||
});
|
||||
}
|
||||
|
||||
// get bookmark data from origin indexdDB
|
||||
BrowserDB.getHistory(localHistory => {
|
||||
localHistory.forEach(history => {
|
||||
this.cacheIndexEndAt++;
|
||||
this.cache.set(this.cacheIndexEndAt, history);
|
||||
});
|
||||
this.lastDataIndex = this.cacheIndexEndAt;
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
);
|
||||
} else {
|
||||
SyncBrowserDB.getHistory(INITIAL_HISTORY_COUNT, syncHistory => {
|
||||
syncHistory.forEach(history => {
|
||||
// XXX: the data from firefox sync can't be modified.
|
||||
history.readOnly = true;
|
||||
this.cacheIndexEndAt++;
|
||||
this.cache.set(this.cacheIndexEndAt, history);
|
||||
});
|
||||
|
||||
if(syncHistory.length < INITIAL_HISTORY_COUNT) {
|
||||
this.lastDataIndex = this.cacheIndexEndAt;
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
//IFDEF_FIREFOX_SYNC
|
||||
updateCacheByNextHistory: function (timestamp) {
|
||||
var promise = new Promise(resolve => {
|
||||
var start = timestamp,
|
||||
end = timestamp + DAY_IN_MILLI_SECOND;
|
||||
|
||||
SyncBrowserDB.getHistoryByTime(start, end, true, true, syncHistory => {
|
||||
this.cache.clear();
|
||||
this.cacheIndexEndAt = this.cacheIndexStartAt - 1;
|
||||
var length = syncHistory.length;
|
||||
while(length--) {
|
||||
this.cacheIndexStartAt--;
|
||||
this.cache.set(this.cacheIndexStartAt, syncHistory[length]);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
|
||||
//IFDEF_FIREFOX_SYNC
|
||||
updateCacheByPreviousHistory: function (timestamp) {
|
||||
var promise = new Promise(resolve => {
|
||||
var start = timestamp - DAY_IN_MILLI_SECOND,
|
||||
end = timestamp;
|
||||
|
||||
SyncBrowserDB.getHistoryByTime(start, end, true, true, syncHistory => {
|
||||
this.cache.clear();
|
||||
this.cacheIndexStartAt = this.cacheIndexEndAt + 1;
|
||||
syncHistory.forEach(history => {
|
||||
// XXX: the data from firefox sync can't be modified.
|
||||
history.readOnly = true;
|
||||
this.cacheIndexEndAt++;
|
||||
this.cache.set(this.cacheIndexEndAt, history);
|
||||
});
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
return promise;
|
||||
},
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
|
||||
removeCache: function (index) {
|
||||
if(this.cacheIndexStartAt <= index <= this.cacheIndexEndAt) {
|
||||
var value = null;
|
||||
for(var i = index; i<this.cacheIndexEndAt; i++) {
|
||||
value = this.cache.get(i+1);
|
||||
this.cache.set(i, value);
|
||||
}
|
||||
this.cache.delete(this.cacheIndexEndAt);
|
||||
this.cacheIndexEndAt--;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
exports.HistoryStore = HistoryStore;
|
||||
})(window);
|
||||
1221
gaia/tv_apps/browser/js/index.js
Executable file
1221
gaia/tv_apps/browser/js/index.js
Executable file
File diff suppressed because it is too large
Load diff
136
gaia/tv_apps/browser/js/resource_loader.js
Executable file
136
gaia/tv_apps/browser/js/resource_loader.js
Executable file
|
|
@ -0,0 +1,136 @@
|
|||
/* 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/. */
|
||||
|
||||
/* HTML files to be loaded should be enclosed by a <template> element.
|
||||
* <template>
|
||||
* put HTML content here
|
||||
* </template>
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
(function (exports) {
|
||||
|
||||
/**
|
||||
* ResourceLoader is a stand-alone module which provides methods for lazy
|
||||
* loading HTML, CSS and javascript resources.
|
||||
*
|
||||
* TODO: Methods for lazy loading javascript files.
|
||||
*
|
||||
* @param {object} options [description]
|
||||
*/
|
||||
function ResourceLoader(options) {
|
||||
this.path = '';
|
||||
|
||||
this.init(options);
|
||||
}
|
||||
|
||||
ResourceLoader.prototype = {
|
||||
|
||||
constructor: ResourceLoader,
|
||||
|
||||
init(options) {
|
||||
options = options || {};
|
||||
|
||||
this.path = options.path || 'elements';
|
||||
},
|
||||
|
||||
/**
|
||||
* Load HTML and CSS by name.
|
||||
* @param {string} name [description]
|
||||
* @return {Promise} The returned Promise is resolved with a Node.
|
||||
*/
|
||||
loadByName(name) {
|
||||
var path = [this.path, name].join('/');
|
||||
this.loadCss(path);
|
||||
return this.loadHtml(path);
|
||||
},
|
||||
|
||||
/**
|
||||
* Load an HTML file from the specified path, the resulted DOM Node can be
|
||||
* directly appended or modified with DOM API.
|
||||
* @param {string} path The path of the HTML file, with or without
|
||||
* html extension.
|
||||
* @return {Promise} The returned Promise is resolved with a DOM Node.
|
||||
*/
|
||||
loadHtml(path) {
|
||||
if (!path.endsWith('.html')) {
|
||||
path = [path, 'html'].join('.');
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.onload = function() {
|
||||
var template = xhr.responseXML.querySelector('template');
|
||||
var node = document.importNode(template.content, true);
|
||||
resolve(node);
|
||||
};
|
||||
xhr.open('GET', path);
|
||||
xhr.responseType = 'document';
|
||||
xhr.overrideMimeType('text/html');
|
||||
xhr.send();
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Load an CSS file from the specified path.
|
||||
* @param {string} path The path of the CSS file, with or without
|
||||
* css extension.
|
||||
* @return {Promise}
|
||||
*/
|
||||
loadCss(path) {
|
||||
if (!path.endsWith('.css')) {
|
||||
path = [path, 'css'].join('.');
|
||||
}
|
||||
|
||||
if (document.querySelector('link[rel=stylesheet][href="' + path + '"]')) {
|
||||
return;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
var link = document.createElement('link');
|
||||
link.type = 'text/css';
|
||||
link.rel = 'stylesheet';
|
||||
link.href = path;
|
||||
link.addEventListener('load', () => {
|
||||
resolve();
|
||||
});
|
||||
document.head.appendChild(link);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Unload a loaded template. We don't cache HTML templates, so we only need
|
||||
* to remove the loaded link element from document.head.
|
||||
* @param {string} name The name of the template.
|
||||
*/
|
||||
unloadByName(name) {
|
||||
var path = [this.path, name].join('/');
|
||||
this.unloadCss(path);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove a CSS link element from document.head.
|
||||
* @param {string} path The path of the CSS file, with or without
|
||||
* css extension.
|
||||
*/
|
||||
unloadCss(path) {
|
||||
if (!path.endsWith('.css')) {
|
||||
path = [path, 'css'].join('.');
|
||||
}
|
||||
|
||||
var link =
|
||||
document.querySelector('link[rel=stylesheet][href="' + path + '"]');
|
||||
|
||||
if (link) {
|
||||
document.head.removeChild(link);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
exports.ResourceLoader = new ResourceLoader();
|
||||
|
||||
})(window);
|
||||
242
gaia/tv_apps/browser/js/search_result.js
Executable file
242
gaia/tv_apps/browser/js/search_result.js
Executable file
|
|
@ -0,0 +1,242 @@
|
|||
/* global _, Browser, BrowserDB, MozActivity */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Browser app search_result
|
||||
* @namespace SearchResult
|
||||
*/
|
||||
var SearchResult = {
|
||||
|
||||
listTemplate: null,
|
||||
searchResultTemplate: null,
|
||||
allResult: null,
|
||||
updateInProgress: false,
|
||||
pendingUpdateFilter: null,
|
||||
|
||||
/**
|
||||
* Intialise SearchResult.
|
||||
*/
|
||||
init: function result_init() {
|
||||
/* get Element */
|
||||
this.getAllElements();
|
||||
|
||||
/* Event Listener */
|
||||
window.addEventListener('click', this);
|
||||
|
||||
// Create template elements
|
||||
this.listTemplate = this.createList();
|
||||
this.searchResultTemplate = this.createSearchTemplate();
|
||||
},
|
||||
|
||||
/**
|
||||
* CamelCase (xxx-yyy -> xxxYyy)
|
||||
*/
|
||||
toCamelCase: function toCamelCase(str) {
|
||||
return str.replace(/-(.)/g, function replacer(str, p1) {
|
||||
return p1.toUpperCase();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get All Elements (from id)
|
||||
*/
|
||||
getAllElements: function result_getAllElements() {
|
||||
var elementIDs = [
|
||||
'search-result', 'search-result-list',
|
||||
];
|
||||
|
||||
// Loop and add element with camel style name to Modal Dialog attribute.
|
||||
elementIDs.forEach(function createElementRef(name) {
|
||||
this[this.toCamelCase(name)] = document.getElementById(name);
|
||||
}, this);
|
||||
},
|
||||
|
||||
show: function result_show() {
|
||||
this.searchResult.classList.add('visible');
|
||||
this.handleSearchResultFormInput(null);
|
||||
},
|
||||
|
||||
hide: function result_hide() {
|
||||
var list = this.searchResultList.firstElementChild;
|
||||
if( list ) this.searchResultList.removeChild(list);
|
||||
this.searchResult.classList.remove('visible');
|
||||
Toolbar.setSearchBarStyle('false');
|
||||
},
|
||||
|
||||
isDisplayed: function result_isDisplayed() {
|
||||
return this.searchResult.classList.contains('visible');
|
||||
},
|
||||
|
||||
/**
|
||||
* Submit search form
|
||||
*/
|
||||
handleSearchResultFormInput: function result_handleSearchResultFormInput(ev) {
|
||||
if( ev ) ev.preventDefault();
|
||||
this.resultUpdate(Toolbar.searchInput.value);
|
||||
},
|
||||
|
||||
handleSearchResultFormSubmit: function result_handleSearchResultFormSubmit(ev) {
|
||||
if( ev ) ev.preventDefault();
|
||||
},
|
||||
|
||||
createList: function result_createList() {
|
||||
var list = document.createElement('ul');
|
||||
list.classList.add('result-box');
|
||||
return list;
|
||||
},
|
||||
|
||||
createSearchTemplate: function result_createSearchTemplate() {
|
||||
var template = document.createElement('li');
|
||||
template.classList.add('result-item');
|
||||
var link = document.createElement('div');
|
||||
link.classList.add('result-base');
|
||||
var title = document.createElement('div');
|
||||
title.classList.add('result-title');
|
||||
var url = document.createElement('div');
|
||||
url.classList.add('result-url');
|
||||
|
||||
link.appendChild(title);
|
||||
link.appendChild(url);
|
||||
template.appendChild(link);
|
||||
|
||||
return template;
|
||||
},
|
||||
|
||||
resultUpdate: function result_resultUpdate(filter) {
|
||||
// If an update is already in progress enqueue the following ones
|
||||
if (this.updateInProgress) {
|
||||
this.pendingUpdateFilter = filter;
|
||||
return;
|
||||
} else {
|
||||
this.updateInProgress = true;
|
||||
}
|
||||
|
||||
this.allResult = [];
|
||||
BrowserDB.getBookmarks((function(bookmarks) {
|
||||
this.resultFiltering(filter, bookmarks);
|
||||
BrowserDB.getHistory((function(history) {
|
||||
this.resultFiltering(filter, history);
|
||||
this.populateResults(filter);
|
||||
|
||||
this.updateInProgress = false;
|
||||
var pendingUpdateFilter = this.pendingUpdateFilter;
|
||||
if (pendingUpdateFilter !== null) {
|
||||
this.pendingUpdateFilter = null;
|
||||
this.resultUpdate(pendingUpdateFilter);
|
||||
}
|
||||
}).bind(this));
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
resultFiltering: function result_resultFiltering(filter, data) {
|
||||
if(( filter == '' ) || ( data == null )) return;
|
||||
|
||||
for( var i = 0 ; i < data.length ; i ++ ) {
|
||||
var current = data[i];
|
||||
var matched = false;
|
||||
if( filter ) {
|
||||
matched = this.matchesFilter(current.uri, filter) ||
|
||||
this.matchesFilter(current.title, filter);
|
||||
}
|
||||
if( matched || !filter ) {
|
||||
var j = 0;
|
||||
for( ; j < this.allResult.length ; j ++ ) {
|
||||
if( this.allResult[j].uri === current.uri ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( j == this.allResult.length ) {
|
||||
this.allResult.push(current);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if the URI matches the regular expression filter.
|
||||
* @param {String} uri
|
||||
* @param {String} filter
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
matchesFilter: function result_matchesFilter(uri, filter) {
|
||||
if( !uri ) return false;
|
||||
return uri.match(new RegExp(filter, 'i')) !== null;
|
||||
},
|
||||
|
||||
populateResults: function result_populateResults(filter) {
|
||||
var list = this.createList();
|
||||
var engine = SearchUtil.getCurrentEngineName();
|
||||
if( engine ) {
|
||||
var result = this.searchResultTemplate.cloneNode(true);
|
||||
var title = result.childNodes[0].childNodes[0];
|
||||
// title.innerHTML = 'Search by ' + engine;
|
||||
navigator.mozL10n.setAttributes(title, 'WB_LT_TIPS_S_SEARCH', {
|
||||
value0:engine
|
||||
});
|
||||
var uri = result.childNodes[0].childNodes[1];
|
||||
uri.innerHTML = Browser.getSearchFromInput(filter);
|
||||
list.appendChild(result);
|
||||
result.addEventListener('mouseup',
|
||||
this.handleResultSelect.bind(this));
|
||||
}
|
||||
|
||||
for( var i = 0 ; i < this.allResult.length ; i ++ ) {
|
||||
var result = this.searchResultTemplate.cloneNode(true);
|
||||
var title = result.childNodes[0].childNodes[0];
|
||||
title.innerHTML = this.allResult[i].title;
|
||||
var uri = result.childNodes[0].childNodes[1];
|
||||
uri.innerHTML = this.allResult[i].uri;
|
||||
list.appendChild(result);
|
||||
result.addEventListener('mouseup',
|
||||
this.handleResultSelect.bind(this));
|
||||
}
|
||||
var oldList = this.searchResultList.firstElementChild;
|
||||
|
||||
if (oldList) {
|
||||
this.searchResultList.replaceChild(list, oldList);
|
||||
} else {
|
||||
this.searchResultList.appendChild(list);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* handle event
|
||||
*/
|
||||
handleEvent: function result_handleEvent(ev) {
|
||||
switch(ev.type) {
|
||||
case 'click':
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
handleResultSelect: function result_handleResultSelect(ev) {
|
||||
if( !ev ) return;
|
||||
ev.preventDefault();
|
||||
if(( ev.target.lastChild.lastChild.textContent != '' ) ||
|
||||
( ev.target.lastChild.lastChild.textContent != undefined )) {
|
||||
var url = ev.target.lastChild.lastChild.textContent;
|
||||
Browser.navigate(url);
|
||||
this.hide();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle Key Event.
|
||||
*/
|
||||
handleKeyEvent: function result_handleKeyEvent(ev) {
|
||||
// in the input area focus (= display keyboard)
|
||||
if(document.activeElement.nodeName == 'INPUT') {
|
||||
return;
|
||||
}
|
||||
switch( ev.keyCode ) {
|
||||
case KeyEvent.DOM_VK_BACK_SPACE :
|
||||
this.hide();
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
283
gaia/tv_apps/browser/js/search_util.js
Executable file
283
gaia/tv_apps/browser/js/search_util.js
Executable file
|
|
@ -0,0 +1,283 @@
|
|||
'use strict';
|
||||
|
||||
var SearchUtil = {
|
||||
// country code
|
||||
COUNTRY: 'US',
|
||||
|
||||
/* search engine list of the search bar */
|
||||
ENGINE_LIST: {
|
||||
'CN': [
|
||||
{id: 2, name: 'Baidu'},
|
||||
{id: 1, name: 'Bing'}
|
||||
],
|
||||
// default
|
||||
'US': [
|
||||
{id: 1, name: 'Bing'},
|
||||
{id: 0, name: 'Google'}
|
||||
]
|
||||
},
|
||||
// search engine
|
||||
engineList: null,
|
||||
searchId: null,
|
||||
searchIdx: null,
|
||||
|
||||
DOMAIN_LIST: {
|
||||
'US':'com' , 'CA':'ca' , 'DE':'de' , 'IE':'ie' ,
|
||||
'NL':'nl' , 'HU':'hu' , 'PL':'pl' , 'CZ':'cz' ,
|
||||
'SK':'sk' , 'IT':'it' , 'GB':'co.uk' , 'SI':'si' ,
|
||||
'GR':'gr' , 'PT':'pt' , 'DK':'dk' , 'FI':'fi' ,
|
||||
'HR':'hr' , 'NO':'no' , 'RO':'ro' , 'RU':'ru' ,
|
||||
'BG':'bg' , 'LT':'lt' , 'YU':'rs' , 'SE':'se' ,
|
||||
'LV':'lv' , 'EE':'ee' , 'TR':'com.tr' , 'BR':'com.br' ,
|
||||
'ID':'co.id' , 'CN':'com.hk' , 'TW':'com.tw' , 'HK':'com.hk' ,
|
||||
'VN':'com.vn' , 'TH':'co.th' , 'SA':'com.sa' , 'FR':'fr' ,
|
||||
'ES':'es' , 'SG':'com.sg' , 'AU':'com.au' , 'NZ':'co.nz' ,
|
||||
'IN':'co.in' , 'MY':'com.my' , 'AE':'ae' , 'KW':'com.kw' ,
|
||||
'MX':'com.mx' , 'AT':'at' , 'BE':'be' , 'CH':'ch' ,
|
||||
'MT':'com.mt' , 'AD':':' , 'LU':'lu' , 'IS':'is' ,
|
||||
'UA':'com.ua' , 'KZ':'kz' , 'CIS':'by' , 'UK':'co.uk' ,
|
||||
'CL':'cl' , 'PE':'com.pe' , 'CO':'com.co' , 'AR':'com.ar' ,
|
||||
'PA':'com.pa' , 'JP':'co.jp' , 'MK':'mk' , 'MM':'com.mm'
|
||||
},
|
||||
// search domain
|
||||
domainList: null,
|
||||
|
||||
/* URL of each search engine */
|
||||
SEARCH_URL: {
|
||||
//Google :
|
||||
0 : [
|
||||
"http://www.google.DOMAIN/search?ie=UTF-8&oe=UTF-8&q=",
|
||||
"http://www.google.DOMAIN/search?ie=UTF-8&oe=UTF-8&tbm=isch&q=",
|
||||
"http://www.google.DOMAIN/search?ie=UTF-8&oe=UTF-8&tbm=vid&q=",
|
||||
"http://www.google.DOMAIN/search?ie=UTF-8&oe=UTF-8&tbm=nws&q=",
|
||||
"http://maps.google.com/maps?q=",
|
||||
"http://www.youtube.DOMAIN/results?search_query="
|
||||
],
|
||||
//bing :
|
||||
1 : [
|
||||
"http://www.bing.com/search?FORM=PANSSB&PC=PANS&q=",
|
||||
"http://www.bing.com/images/search?FORM=PANSSB&PC=PANS&q=",
|
||||
//"http://www.bing.com/videos/search?FORM=PANSSB&PC=PANS&q=",
|
||||
"http://www.bing.com/?scope=video&FORM=PANSSB&PC=PANS&q=",
|
||||
"http://www.bing.com/news/search?FORM=PANSSB&PC=PANS&q=",
|
||||
"http://www.bing.com/maps/?FORM=PANSSB&PC=PANS&q=",
|
||||
"http://www.bing.com/videos/search?FORM=PANSSB&PC=PANS&q="
|
||||
],
|
||||
//Baidu :
|
||||
2 : [
|
||||
"http://www.baidu.com/s?word=",
|
||||
"http://image.baidu.com/i?word=",
|
||||
"http://video.baidu.com/v?word=",
|
||||
"http://news.baidu.com/ns?word=",
|
||||
"http://map.baidu.com/m?word=",
|
||||
"http://video.baidu.com/v?word="
|
||||
]
|
||||
},
|
||||
searchUrl: null,
|
||||
|
||||
// keyword for auto category selection at voice search
|
||||
SEARCH_CAT: {
|
||||
//Google :
|
||||
0 :
|
||||
[
|
||||
[ [ "www.google.", "/search", "" ] ],
|
||||
[ [ "www.google.", "/search", "tbm=isch" ],
|
||||
[ "www.google.", "/imghp", "" ] ],
|
||||
[ [ "www.google.", "/search", "tbm=vid" ],
|
||||
[ "www.google.", "/videohp","" ] ],
|
||||
[ [ "www.google.", "/search", "tbm=nws" ],
|
||||
[ "news.google.", "/nwshp", "" ] ],
|
||||
[ [ "maps.google.", "", "" ],
|
||||
[ "www.google.", "/maps", "" ] ],
|
||||
[ [ "www.youtube.", "", "" ] ]
|
||||
],
|
||||
//Bing :
|
||||
1 :
|
||||
[
|
||||
[ [ "www.bing.com", "/search", "" ] ],
|
||||
[ [ "www.bing.com", "/images", "" ],
|
||||
[ "www.bing.com", "", "scope=images" ] ],
|
||||
[ [ "www.bing.com", "/videos", "" ],
|
||||
[ "www.bing.com", "", "scope=video" ] ],
|
||||
[ [ "www.bing.com", "/news", "" ],
|
||||
[ "www.bing.com", "", "scope=news" ] ],
|
||||
[ [ "www.bing.com", "/maps", "" ] ],
|
||||
[ [ "www.bing.com", "/videos", "" ],
|
||||
[ "www.bing.com", "", "scope=video" ] ]
|
||||
],
|
||||
//Baidu :
|
||||
2 :
|
||||
[
|
||||
[ [ "www.baidu.com", "", "" ] ],
|
||||
[ [ "image.baidu.com", "", "" ] ],
|
||||
[ [ "video.baidu.com", "", "" ] ],
|
||||
[ [ "news.baidu.com", "", "" ] ],
|
||||
[ [ "map.baidu.com", "", "" ] ],
|
||||
[ [ "video.baidu.com", "", "" ] ]
|
||||
]
|
||||
},
|
||||
|
||||
displaySearchEngineName: false,
|
||||
|
||||
/**
|
||||
* init SearchUtil
|
||||
*/
|
||||
init: function searchUtil_init(country, cb) {
|
||||
// set country code
|
||||
this.COUNTRY = country;
|
||||
|
||||
// search engine (default US)
|
||||
this.engineList = (country in this.ENGINE_LIST)?
|
||||
this.ENGINE_LIST[country]: this.ENGINE_LIST['US'];
|
||||
|
||||
// search domain (default US)
|
||||
this.domainList = (country in this.DOMAIN_LIST)?
|
||||
this.DOMAIN_LIST[country]: this.DOMAIN_LIST['US'];
|
||||
|
||||
// search URL (default US)
|
||||
this.searchUrl = this.SEARCH_URL;
|
||||
var index;
|
||||
for(index in this.searchUrl[0]){
|
||||
this.searchUrl[0][index] =
|
||||
this.searchUrl[0][index].replace(/DOMAIN/, this.domainList);
|
||||
}
|
||||
|
||||
// get all database
|
||||
this.getAllDatabase(cb);
|
||||
},
|
||||
|
||||
/**
|
||||
* get all database
|
||||
*/
|
||||
getAllDatabase: function searchUtil_getAllDatabase(cb){
|
||||
// DB get all
|
||||
this.getSearchEngineId((function(){
|
||||
this.initDatabase((function(){
|
||||
if(cb){
|
||||
cb();
|
||||
}
|
||||
}).bind(this))
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
// init database
|
||||
initDatabase: function search_initDatabase(cb){
|
||||
// default search engine url
|
||||
if(this.searchId != null){
|
||||
this.convSearchIdToIdx(this.searchId);
|
||||
}else{
|
||||
this.searchIdx = 0;
|
||||
this.displaySearchEngineName = true;
|
||||
}
|
||||
if(cb){
|
||||
cb();
|
||||
}
|
||||
},
|
||||
|
||||
// get search engine id
|
||||
getSearchEngineId: function searchUtil_getSearchEngineId(cb){
|
||||
BrowserDB.getSetting('SEARCH_ENGINE', (function(result) {
|
||||
Browser.debug("SEARCH_ENGINE = " + result);
|
||||
if(result != undefined){
|
||||
this.searchId = result;
|
||||
this.convSearchIdToIdx(result);
|
||||
}
|
||||
if(cb){
|
||||
cb(result);
|
||||
}
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
// set search engine id
|
||||
setSearchEngineId: function searchUtil_setSearchEngineId(id, cb){
|
||||
this.searchId = id;
|
||||
this.convSearchIdToIdx(id);
|
||||
BrowserDB.updateSetting(id, 'SEARCH_ENGINE', cb);
|
||||
},
|
||||
|
||||
// get search engine index
|
||||
getSearchEngineIdx: function searchUtil_getSearchEngineIdx(cb){
|
||||
if(this.searchId){
|
||||
this.convSearchIdToIdx(this.searchId);
|
||||
if(cb){
|
||||
cb();
|
||||
}
|
||||
}else{
|
||||
BrowserDB.getSetting('SEARCH_ENGINE', (function(result) {
|
||||
if(result != undefined){
|
||||
this.convSearchIdToIdx(result);
|
||||
}
|
||||
if(cb){
|
||||
cb();
|
||||
}
|
||||
}).bind(this));
|
||||
}
|
||||
},
|
||||
|
||||
// Convert the search_id (DB) to search index.
|
||||
convSearchIdToIdx: function searchUtil_convSearchIdToIdx(search_id) {
|
||||
var index = null;
|
||||
for(var i in this.engineList){
|
||||
if(search_id == this.engineList[i].id){
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(index == null){
|
||||
// terminated search service of Web. so select default.
|
||||
this.searchIdx = 0;
|
||||
this.searchId = this.engineList[0].id;
|
||||
BrowserDB.updateSetting(undefined, 'SEARCH_ENGINE');
|
||||
this.displaySearchEngineName = false;
|
||||
}else{
|
||||
this.searchIdx = index;
|
||||
this.displaySearchEngineName = true;
|
||||
}
|
||||
},
|
||||
|
||||
// Get Search Engine List.
|
||||
getEngineList: function searchUtil_getEngineList(){
|
||||
return this.engineList;
|
||||
},
|
||||
// Get Current Search Engine List.
|
||||
getCurrentEngineList: function searchUtil_getCurrentEngineList(){
|
||||
if(this.searchIdx != null && this.searchIdx < this.engineList.length) {
|
||||
return this.engineList[this.searchIdx];
|
||||
}else{
|
||||
return this.engineList[0];
|
||||
}
|
||||
},
|
||||
// Get Current Search Engine Name.
|
||||
getCurrentEngineName: function searchUtil_getCurrentEngineName(){
|
||||
if(this.searchIdx != null && this.searchIdx < this.engineList.length) {
|
||||
return this.engineList[this.searchIdx].name;
|
||||
}else{
|
||||
return this.engineList[0].name;
|
||||
}
|
||||
},
|
||||
// Get Search Id.
|
||||
getSearchId: function searchUtil_getSearchId(){
|
||||
return this.searchId;
|
||||
},
|
||||
// Get Search Index.
|
||||
getSearchIdx: function searchUtil_getSearchIdx(){
|
||||
return this.searchIdx;
|
||||
},
|
||||
// Get Search Url.
|
||||
getSearchUrl: function searchUtil_getSearchUrl(){
|
||||
return this.searchUrl;
|
||||
},
|
||||
// Get Current Search Url.
|
||||
getCurrentSearchUrl: function searchUtil_getCurrentSearchUrl(){
|
||||
if(this.searchId != null && (this.searchId in this.searchUrl)){
|
||||
return this.searchUrl[this.searchId];
|
||||
}else{
|
||||
return this.searchUrl[this.engineList[0].id];
|
||||
}
|
||||
},
|
||||
// Get Display Search Engine Name flag.
|
||||
getDispFlag: function searchUtil_getDispFlag(){
|
||||
return this.displaySearchEngineName;
|
||||
},
|
||||
};
|
||||
|
||||
719
gaia/tv_apps/browser/js/settings.js
Executable file
719
gaia/tv_apps/browser/js/settings.js
Executable file
|
|
@ -0,0 +1,719 @@
|
|||
/* exported Settings */
|
||||
/* global Awesomescreen */
|
||||
/* global Browser */
|
||||
/* global BrowserDB */
|
||||
/* global BrowserDialog */
|
||||
/* global KeyEvent */
|
||||
/* global LazyLoader */
|
||||
/* global SearchUtil */
|
||||
/* global Toolbar */
|
||||
/* global UrlHelper */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Browser app settings panel.
|
||||
* @namespace Settings
|
||||
*/
|
||||
var Settings = {
|
||||
DEFAULT_HOMEPAGE: 'http://www.mozilla.org',
|
||||
currentSearchEngine: null,
|
||||
selectSearchEngine: null,
|
||||
searchEngineList: null,
|
||||
focusList: null,
|
||||
focusPos: 0,
|
||||
|
||||
/** Get all DOM elements when inited. */
|
||||
getAllElements: function settings_getAllElements() {
|
||||
|
||||
var toCamelCase = function toCamelCase(str) {
|
||||
return str.replace(/\-(.)/g, function replacer(str, p1) {
|
||||
return p1.toUpperCase();
|
||||
});
|
||||
};
|
||||
|
||||
var elementIDs = [
|
||||
'settings-list', 'settings-list-area',
|
||||
'settings-homepage', 'settings-homepage-name',
|
||||
'settings-search-engine', 'settings-search-engine-name',
|
||||
'settings-clear-history',
|
||||
'settings-clear-cookie',
|
||||
|
||||
'settings-dialog',
|
||||
|
||||
'settings-dialog-homepage',
|
||||
'settings-dialog-homepage-input',
|
||||
'settings-dialog-homepage-input-area',
|
||||
'settings-dialog-homepage-clear',
|
||||
'settings-dialog-homepage-default',
|
||||
'settings-dialog-homepage-cancel',
|
||||
'settings-dialog-homepage-ok',
|
||||
|
||||
'settings-dialog-search',
|
||||
'settings-dialog-search-select',
|
||||
'settings-dialog-search-select-list',
|
||||
'settings-dialog-search-cancel',
|
||||
'settings-dialog-search-ok',
|
||||
|
||||
'settings-banner-message'
|
||||
];
|
||||
|
||||
// Loop and add element with camel style name to Modal Dialog attribute.
|
||||
elementIDs.forEach(function createElementRef(name) {
|
||||
this[toCamelCase(name)] = document.getElementById(name);
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Intialise settings panel.
|
||||
*/
|
||||
init: function settings_init() {
|
||||
this.getAllElements();
|
||||
|
||||
this.getDefaultHomepage((function(result) {
|
||||
/* default homepage nothing... */
|
||||
Browser.debug('default homepage = ' + result);
|
||||
if(!result) {
|
||||
console.log('set default homepage...');
|
||||
this.setDefaultHomepage();
|
||||
this.setHomepage(this.DEFAULT_HOMEPAGE);
|
||||
}
|
||||
}).bind(this));
|
||||
|
||||
this.settingsList.addEventListener('mouseup',
|
||||
this.handleListClick.bind(this));
|
||||
this.settingsListArea.addEventListener('mouseup',
|
||||
this.handleListAreaClick.bind(this));
|
||||
this.settingsDialog.addEventListener('mouseup',
|
||||
this.handleListAreaClick.bind(this));
|
||||
|
||||
this.settingsHomepage.addEventListener('mouseup',
|
||||
this.handleHomepageClick.bind(this));
|
||||
this.settingsSearchEngine.addEventListener('mouseup',
|
||||
this.handleSearchEngineClick.bind(this));
|
||||
this.settingsClearHistory.addEventListener('mouseup',
|
||||
this.handleClearHistoryClick.bind(this));
|
||||
this.settingsClearCookie.addEventListener('mouseup',
|
||||
this.handleClearCookieClick.bind(this));
|
||||
|
||||
this.settingsDialogHomepageInput.addEventListener('submit',
|
||||
this.handleDialogHomepageInputSubmit.bind(this));
|
||||
this.settingsDialogHomepageClear.addEventListener('mouseup',
|
||||
this.handleDialogHomepageClear.bind(this));
|
||||
this.settingsDialogHomepageDefault.addEventListener('mouseup',
|
||||
this.handleDialogHomepageDefault.bind(this));
|
||||
this.settingsDialogHomepageCancel.addEventListener('mouseup',
|
||||
this.handleDialogHomepageCancel.bind(this));
|
||||
this.settingsDialogHomepageOk.addEventListener('mouseup',
|
||||
this.handleDialogHomepageOk.bind(this));
|
||||
|
||||
this.settingsDialogSearchCancel.addEventListener('mouseup',
|
||||
this.handleDialogSearchCancel.bind(this));
|
||||
this.settingsDialogSearchOk.addEventListener('mouseup',
|
||||
this.handleDialogSearchOk.bind(this));
|
||||
|
||||
this.settingsDialogHomepageInputArea.addEventListener('focus',
|
||||
this.handleDialogHomepageInputAreaFocus.bind(this));
|
||||
this.settingsDialogHomepageInputArea.addEventListener('mousedown',
|
||||
this.handleDialogHomepageInputAreaClick.bind(this));
|
||||
|
||||
this.settingsDialogHomepageInputArea.addEventListener('blur',
|
||||
this.handleDialogHomepageInputBlur.bind(this));
|
||||
this.settingsDialogHomepageClear.addEventListener('keyup',
|
||||
this.handleDialogHomepageClear.bind(this));
|
||||
this.settingsDialogHomepageDefault.addEventListener('keyup',
|
||||
this.handleDialogHomepageDefault.bind(this));
|
||||
this.settingsDialogHomepageCancel.addEventListener('keyup',
|
||||
this.handleDialogHomepageCancel.bind(this));
|
||||
this.settingsDialogHomepageOk.addEventListener('keyup',
|
||||
this.handleDialogHomepageOk.bind(this));
|
||||
|
||||
this.settingsDialogSearchCancel.addEventListener('keyup',
|
||||
this.handleDialogSearchCancel.bind(this));
|
||||
this.settingsDialogSearchOk.addEventListener('keyup',
|
||||
this.handleDialogSearchOk.bind(this));
|
||||
|
||||
//IFDEF_FIREFOX_SYNC
|
||||
LazyLoader.load('js/sync/settings.js');
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
},
|
||||
|
||||
getDefaultHomepage: function settings_getDefaultHomepage(cb) {
|
||||
BrowserDB.db.open((function() {
|
||||
BrowserDB.getSetting( 'default_homepage', cb );
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
setDefaultHomepage: function settings_setDefaultHomepage() {
|
||||
BrowserDB.db.open((function() {
|
||||
BrowserDB.updateSetting( this.DEFAULT_HOMEPAGE, 'default_homepage' );
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
getHomepage: function settings_getHomepage(cb) {
|
||||
BrowserDB.db.open((function() {
|
||||
BrowserDB.getSetting( 'settings_homepage', cb );
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
setHomepage: function settings_setHomepage(url) {
|
||||
BrowserDB.db.open((function() {
|
||||
BrowserDB.updateSetting( url, 'settings_homepage' );
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Show settings panel.
|
||||
*/
|
||||
show: function settings_show() {
|
||||
document.body.classList.add('settings-screen');
|
||||
this.getHomepage((function(result) {
|
||||
this.settingsHomepageName.textContent = result;
|
||||
}).bind(this));
|
||||
this.settingsSearchEngineName.textContent =
|
||||
SearchUtil.getCurrentEngineName();
|
||||
this.currentSearchEngine = this.settingsSearchEngineName.textContent;
|
||||
this.selectSearchEngine = this.currentSearchEngine;
|
||||
|
||||
// XXX: Get Firefox Account sign in status and display it
|
||||
},
|
||||
|
||||
/**
|
||||
* settings list displays a confirmation.
|
||||
*/
|
||||
isDisplayed: function settings_isDisplayed() {
|
||||
return document.body.classList.contains('settings-screen');
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide settings panel.
|
||||
*/
|
||||
hide: function settings_hide() {
|
||||
// Browser.switchCursorMode( true );
|
||||
BrowserDialog.cancelDialog();
|
||||
this.hideDialogHomepage();
|
||||
this.hideDialogSearch();
|
||||
document.body.classList.remove('settings-screen');
|
||||
},
|
||||
|
||||
handleListClick: function settings_handleListClick(evt) {
|
||||
if( evt ) {
|
||||
evt.stopPropagation();
|
||||
}
|
||||
this.hide();
|
||||
},
|
||||
|
||||
handleListAreaClick: function settings_handleListAreaClick(evt) {
|
||||
if( evt ) {
|
||||
evt.stopPropagation();
|
||||
}
|
||||
},
|
||||
|
||||
focusChange: function settings_focusChange(pos) {
|
||||
var opt = null;
|
||||
var flg = false;
|
||||
if( Awesomescreen.pointerImg.style.display == 'none' ) {
|
||||
Browser.switchCursorMode(true);
|
||||
Browser.switchCursorMode(false);
|
||||
flg = true;
|
||||
}
|
||||
for( var i = 0 ; i < Settings.focusList.length ; i ++ ) {
|
||||
if( i == pos ) {
|
||||
if(Settings.focusList[i].nodeName == 'INPUT') {
|
||||
if( !Settings.settingsDialogHomepageInput
|
||||
.classList.contains('exfocus') ) {
|
||||
Settings.settingsDialogHomepageInput.classList.add('exfocus');
|
||||
}
|
||||
} else if(Settings.focusList[i].id == 'settings-dialog-homepage-clear'){
|
||||
Settings.focusList[i].focus();
|
||||
if( !Settings.settingsDialogHomepageInput
|
||||
.classList.contains('exfocus') ) {
|
||||
Settings.settingsDialogHomepageInput.classList.add('exfocus');
|
||||
}
|
||||
if( flg ) {
|
||||
opt = Awesomescreen.DEFAULT_EXCLEAR;
|
||||
}
|
||||
} else {
|
||||
Settings.focusList[i].focus();
|
||||
Settings.settingsDialogHomepageInput.classList.remove('exfocus');
|
||||
}
|
||||
Awesomescreen.focusImgFunc(Settings.focusList[i], opt);
|
||||
} else {
|
||||
Settings.focusList[i].blur();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle homepage click.
|
||||
*/
|
||||
handleHomepageClick: function settings_handleHomepageClick() {
|
||||
if( !this.isDialogHomepageDisplayed() ) {
|
||||
this.focusList = [];
|
||||
Browser.switchCursorMode(false);
|
||||
this.settingsDialog.classList.remove('hidden');
|
||||
this.settingsDialogHomepage.classList.remove('hidden');
|
||||
this.settingsDialogHomepageInput.classList.remove('input');
|
||||
this.settingsDialogHomepageInput.classList.remove('exfocus');
|
||||
this.settingsDialogHomepageInputArea.value =
|
||||
this.settingsHomepageName.textContent;
|
||||
this.settingsDialogHomepageInputArea.tabIndex = '0';
|
||||
this.settingsDialogHomepageClear.tabIndex = '0';
|
||||
|
||||
this.focusList.push(this.settingsDialogHomepageInputArea);
|
||||
this.focusList.push(this.settingsDialogHomepageClear);
|
||||
this.focusList.push(this.settingsDialogHomepageDefault);
|
||||
this.focusList.push(this.settingsDialogHomepageOk);
|
||||
this.focusList.push(this.settingsDialogHomepageCancel);
|
||||
this.focusPos = this.focusList.length - 1;
|
||||
this.focusChange(this.focusPos);
|
||||
}
|
||||
},
|
||||
|
||||
hideDialogHomepage: function settings_hideDialogHomepage() {
|
||||
if( this.isDialogHomepageDisplayed() ) {
|
||||
Awesomescreen.pointerImg.style.display = 'none';
|
||||
this.settingsDialogHomepageInput.classList.remove('input');
|
||||
this.settingsDialogHomepageInput.classList.remove('exfocus');
|
||||
this.settingsDialogHomepage.classList.add('hidden');
|
||||
this.settingsDialog.classList.add('hidden');
|
||||
Browser.switchCursorMode(true);
|
||||
}
|
||||
},
|
||||
|
||||
isDialogHomepageDisplayed: function settings_isDialogHomepageDisplayed() {
|
||||
return !this.settingsDialogHomepage.classList.contains('hidden');
|
||||
},
|
||||
|
||||
handleDialogHomepageInputSubmit:
|
||||
function settings_handleDialogHomepageInputSubmit(ev) {
|
||||
if (ev) {
|
||||
// Can be canceled
|
||||
ev.preventDefault();
|
||||
}
|
||||
|
||||
var hasScheme =
|
||||
UrlHelper.hasScheme(this.settingsDialogHomepageInputArea.value);
|
||||
// No scheme, prepend basic protocol and return
|
||||
if (!hasScheme) {
|
||||
this.settingsDialogHomepageInputArea.value =
|
||||
'http://' + this.settingsDialogHomepageInputArea.value;
|
||||
}
|
||||
|
||||
this.settingsDialogHomepageInputArea.blur();
|
||||
},
|
||||
|
||||
handleDialogHomepageInputAreaFocus:
|
||||
function settings_handleDialogHomepageInputAreaFocus(evt) {
|
||||
if( evt ) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
Settings.settingsDialogHomepageInput.classList.add('input');
|
||||
Settings.settingsDialogHomepageInput.classList.remove('exfocus');
|
||||
},
|
||||
|
||||
handleDialogHomepageInputAreaClick:
|
||||
function settings_handleDialogHomepageInputAreaClick(evt) {
|
||||
Browser.switchCursorMode(true);
|
||||
Browser.switchCursorMode(false);
|
||||
|
||||
Settings.settingsDialogHomepageInput.classList.add('input');
|
||||
Settings.settingsDialogHomepageInput.classList.remove('exfocus');
|
||||
|
||||
Settings.focusPos = 0;
|
||||
Settings.focusList[Settings.focusPos].focus();
|
||||
Awesomescreen.pointerImg.style.display = 'none';
|
||||
},
|
||||
|
||||
handleDialogHomepageInputBlur:
|
||||
function settings_handleDialogHomepageInputBlur(evt) {
|
||||
if( evt ) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
if( Awesomescreen.pointerImg.style.display == 'none' ) {
|
||||
Browser.switchCursorMode(true);
|
||||
Browser.switchCursorMode(false);
|
||||
}
|
||||
// Pointer image to the input area(fucusPos=0)
|
||||
Settings.focusPos = 0;
|
||||
Settings.settingsDialogHomepageInput.classList.remove('input');
|
||||
Settings.focusChange(Settings.focusPos);
|
||||
Awesomescreen.pointerImg.style.display = 'block';
|
||||
},
|
||||
|
||||
handleDialogHomepageClear:
|
||||
function settings_handleDialogHomepageClear(evt) {
|
||||
if( evt ) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
if(( evt.type == 'keyup' ) && ( evt.keyCode != KeyEvent.DOM_VK_RETURN )) {
|
||||
return;
|
||||
}
|
||||
if( evt.keyCode != KeyEvent.DOM_VK_RETURN ) {
|
||||
Awesomescreen.pointerImg.style.display = 'none';
|
||||
document.activeElement.blur();
|
||||
document.activeElement.classList.remove('exfocus');
|
||||
}
|
||||
document.activeElement.classList.remove('active');
|
||||
this.settingsDialogHomepageInputArea.value = '';
|
||||
},
|
||||
|
||||
handleDialogHomepageDefault:
|
||||
function settings_handleDialogHomepageDefault(evt) {
|
||||
if( evt ) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
if(( evt.type == 'keyup' ) && ( evt.keyCode != KeyEvent.DOM_VK_RETURN )) {
|
||||
return;
|
||||
}
|
||||
if( evt.keyCode != KeyEvent.DOM_VK_RETURN ) {
|
||||
Awesomescreen.pointerImg.style.display = 'none';
|
||||
document.activeElement.blur();
|
||||
document.activeElement.classList.remove('exfocus');
|
||||
}
|
||||
document.activeElement.classList.remove('active');
|
||||
this.getDefaultHomepage((function(result) {
|
||||
this.settingsDialogHomepageInputArea.value = result;
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
handleDialogHomepageCancel:
|
||||
function settings_handleDialogHomepageCancel(evt) {
|
||||
if( evt ) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
if(( evt.type == 'keyup' ) && ( evt.keyCode != KeyEvent.DOM_VK_RETURN )) {
|
||||
return;
|
||||
}
|
||||
// animation end event handler
|
||||
var end_event = (function() {
|
||||
Settings.settingsDialogHomepageCancel.removeEventListener('transitionend',
|
||||
end_event, false);
|
||||
Settings.hideDialogHomepage();
|
||||
});
|
||||
// end animantion event
|
||||
Settings.settingsDialogHomepageCancel.addEventListener('transitionend',
|
||||
end_event, false);
|
||||
document.activeElement.classList.remove('active');
|
||||
},
|
||||
|
||||
handleDialogHomepageOk: function settings_handleDialogHomepageOk(evt) {
|
||||
if( evt ) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
if(( evt.type == 'keyup' ) && ( evt.keyCode != KeyEvent.DOM_VK_RETURN )) {
|
||||
return;
|
||||
}
|
||||
// animation end event handler
|
||||
var end_event = (function() {
|
||||
Settings.settingsDialogHomepageOk.removeEventListener('transitionend',
|
||||
end_event, false);
|
||||
Settings.hideDialogHomepage();
|
||||
});
|
||||
// end animantion event
|
||||
Settings.settingsDialogHomepageOk.addEventListener('transitionend',
|
||||
end_event, false);
|
||||
document.activeElement.classList.remove('active');
|
||||
this.setHomepage(this.settingsDialogHomepageInputArea.value);
|
||||
this.settingsHomepageName.textContent =
|
||||
this.settingsDialogHomepageInputArea.value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle search engine click.
|
||||
*/
|
||||
handleSearchEngineClick: function settings_handleSearchEngineClick() {
|
||||
if( !this.isDialogSearchDisplayed() ) {
|
||||
this.focusList = [];
|
||||
Browser.switchCursorMode(false);
|
||||
this.settingsDialog.classList.remove('hidden');
|
||||
this.settingsDialogSearch.classList.remove('hidden');
|
||||
|
||||
this.selectSearchEngine = this.currentSearchEngine;
|
||||
this.searchEngineList = SearchUtil.getEngineList();
|
||||
for(var i in this.searchEngineList) {
|
||||
var title = document.createElement('div');
|
||||
title.classList.add('title');
|
||||
var name = document.createElement('p');
|
||||
name.classList.add('name');
|
||||
name.textContent = this.searchEngineList[i].name;
|
||||
title.appendChild(name);
|
||||
|
||||
var check = document.createElement('div');
|
||||
check.classList.add('check');
|
||||
var onoff = document.createElement('p');
|
||||
if( name.textContent == this.currentSearchEngine ) {
|
||||
name.dataset.display = 'selected';
|
||||
onoff.classList.add('on');
|
||||
} else {
|
||||
name.dataset.display = '';
|
||||
onoff.classList.add('off');
|
||||
}
|
||||
check.appendChild(onoff);
|
||||
|
||||
var item = document.createElement('div');
|
||||
item.classList.add('item');
|
||||
item.appendChild(title);
|
||||
item.appendChild(check);
|
||||
item.tabIndex = '0';
|
||||
this.settingsDialogSearchSelectList.appendChild(item);
|
||||
item.addEventListener('mouseup',
|
||||
this.handleDialogSearchSelected.bind(this));
|
||||
item.addEventListener('keyup',
|
||||
this.handleDialogSearchSelected.bind(this));
|
||||
this.focusList.push(item);
|
||||
}
|
||||
this.focusList.push(this.settingsDialogSearchOk);
|
||||
this.focusList.push(this.settingsDialogSearchCancel);
|
||||
this.focusPos = this.focusList.length - 1;
|
||||
this.focusChange(this.focusPos);
|
||||
}
|
||||
},
|
||||
|
||||
handleClearHistoryClick: function settings_handleClearHistoryClick() {
|
||||
BrowserDialog.createDialog('clear_history', null);
|
||||
},
|
||||
|
||||
handleClearCookieClick: function settings_handleClearCookieClick() {
|
||||
BrowserDialog.createDialog('del_cookie', null);
|
||||
},
|
||||
|
||||
clearCookieFailed: function settings_clearCookieFailed() {
|
||||
// this.settingsBannerMessage.innerHTML =
|
||||
// 'Failed to clear cookies and stored data';
|
||||
this.settingsBannerMessage.setAttribute('data-l10n-id', 'LT_FAILED_');
|
||||
this.showBannerMessage();
|
||||
},
|
||||
|
||||
showBannerMessage: function settings_showBannerMessage() {
|
||||
if( this.settingsBannerMessage.dataset.display != 'visible' ) {
|
||||
this.settingsBannerMessage.dataset.display = 'visible';
|
||||
}
|
||||
clearTimeout( this.showBannerMessageTimeoutID );
|
||||
this.showBannerMessageTimeoutID = setTimeout( function() {
|
||||
Settings.settingsBannerMessage.dataset.display = '';
|
||||
}, 3000);
|
||||
},
|
||||
|
||||
hideDialogSearch: function settings_hideDialogSearch() {
|
||||
if( this.isDialogSearchDisplayed() ) {
|
||||
Awesomescreen.pointerImg.style.display = 'none';
|
||||
var childs = this.settingsDialogSearchSelectList.childNodes;
|
||||
var len = childs.length;
|
||||
for(var i = 0 ; i < len ; i ++) {
|
||||
this.settingsDialogSearchSelectList.removeChild(childs[0]);
|
||||
}
|
||||
this.settingsDialogSearch.classList.add('hidden');
|
||||
this.settingsDialog.classList.add('hidden');
|
||||
Browser.switchCursorMode(true);
|
||||
}
|
||||
},
|
||||
|
||||
isDialogSearchDisplayed: function settings_isDialogSearchDisplayed() {
|
||||
return !this.settingsDialogSearch.classList.contains('hidden');
|
||||
},
|
||||
|
||||
handleDialogSearchSelected:
|
||||
function settings_handleDialogSearchSelected(ev) {
|
||||
if( ev ) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
if(( ev.type == 'keyup' ) && ( ev.keyCode != KeyEvent.DOM_VK_RETURN )) {
|
||||
return;
|
||||
}
|
||||
this.selectSearchEngine = ev.currentTarget.textContent;
|
||||
var childs = this.settingsDialogSearchSelectList.childNodes;
|
||||
var len = childs.length;
|
||||
for(var i = 0 ; i < len ; i ++) {
|
||||
if(childs[i].textContent == this.selectSearchEngine) {
|
||||
childs[i].firstChild.firstChild.dataset.display = 'selected';
|
||||
childs[i].lastChild.lastChild.classList.remove('off');
|
||||
childs[i].lastChild.lastChild.classList.add('on');
|
||||
} else {
|
||||
childs[i].firstChild.firstChild.dataset.display = '';
|
||||
childs[i].lastChild.lastChild.classList.remove('on');
|
||||
childs[i].lastChild.lastChild.classList.add('off');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
handleDialogSearchCancel: function settings_handleDialogSearchCancel(ev) {
|
||||
if( ev ) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
if(( ev.type == 'keyup' ) && ( ev.keyCode != KeyEvent.DOM_VK_RETURN )) {
|
||||
return;
|
||||
}
|
||||
// animation end event handler
|
||||
var end_event = (function() {
|
||||
Settings.settingsDialogSearchCancel.removeEventListener('transitionend',
|
||||
end_event, false);
|
||||
Settings.hideDialogSearch();
|
||||
});
|
||||
// end animantion event
|
||||
Settings.settingsDialogSearchCancel.addEventListener('transitionend',
|
||||
end_event, false);
|
||||
document.activeElement.classList.remove('active');
|
||||
},
|
||||
|
||||
handleDialogSearchOk: function settings_handleDialogSearchOk(ev) {
|
||||
if( ev ) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
if(( ev.type == 'keyup' ) && ( ev.keyCode != KeyEvent.DOM_VK_RETURN )) {
|
||||
return;
|
||||
}
|
||||
// animation end event handler
|
||||
var end_event = (function() {
|
||||
Settings.settingsDialogSearchOk.removeEventListener('transitionend',
|
||||
end_event, false);
|
||||
Settings.hideDialogSearch();
|
||||
});
|
||||
|
||||
var setSearchEngine = function () {
|
||||
this.currentSearchEngine = this.selectSearchEngine;
|
||||
this.settingsSearchEngineName.textContent =
|
||||
this.currentSearchEngine;
|
||||
Toolbar.setSearchEngine();
|
||||
};
|
||||
|
||||
// end animantion event
|
||||
Settings.settingsDialogSearchOk.addEventListener('transitionend',
|
||||
end_event, false);
|
||||
if( this.currentSearchEngine != this.selectSearchEngine ) {
|
||||
for(var i in this.searchEngineList) {
|
||||
if( this.selectSearchEngine == this.searchEngineList[i].name ) {
|
||||
SearchUtil.setSearchEngineId(this.searchEngineList[i].id,
|
||||
setSearchEngine.bind(this));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
document.activeElement.classList.remove('active');
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle Key Event.
|
||||
*/
|
||||
handleKeyEvent: function settings_handleKeyEvent(ev) {
|
||||
if( !Settings.isDisplayed() ) {
|
||||
return false;
|
||||
}
|
||||
// in the input area focus (= display keyboard)
|
||||
if(document.activeElement.nodeName == 'INPUT') {
|
||||
return true;
|
||||
}
|
||||
switch( ev.keyCode ) {
|
||||
case KeyEvent.DOM_VK_LEFT :
|
||||
ev.preventDefault();
|
||||
if( Settings.isDialogHomepageDisplayed() ) {
|
||||
if(( Settings.focusPos == 1 ) || ( Settings.focusPos == 4 )) {
|
||||
Settings.focusPos --;
|
||||
Settings.focusChange(Settings.focusPos);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if( Settings.isDialogSearchDisplayed() ) {
|
||||
if( Settings.focusPos == Settings.focusList.length - 1 ) {
|
||||
Settings.focusPos --;
|
||||
Settings.focusChange(Settings.focusPos);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case KeyEvent.DOM_VK_RIGHT :
|
||||
ev.preventDefault();
|
||||
if( Settings.isDialogHomepageDisplayed() ) {
|
||||
if(( Settings.focusPos === 0 ) || ( Settings.focusPos === 3 )) {
|
||||
Settings.focusPos ++;
|
||||
Settings.focusChange(Settings.focusPos);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if( Settings.isDialogSearchDisplayed() ) {
|
||||
if( Settings.focusPos == Settings.focusList.length - 2 ) {
|
||||
Settings.focusPos ++;
|
||||
Settings.focusChange(Settings.focusPos);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case KeyEvent.DOM_VK_UP :
|
||||
ev.preventDefault();
|
||||
if( Settings.isDialogHomepageDisplayed() ) {
|
||||
if(( Settings.focusPos === 2 ) || ( Settings.focusPos === 4 )) {
|
||||
Settings.focusPos -= 2;
|
||||
Settings.focusChange(Settings.focusPos);
|
||||
} else if( Settings.focusPos === 3 ) {
|
||||
Settings.focusPos --;
|
||||
Settings.focusChange(Settings.focusPos);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if( Settings.isDialogSearchDisplayed() ) {
|
||||
if( Settings.focusPos > 0 ) {
|
||||
if( Settings.focusPos >= Settings.focusList.length - 1 ) {
|
||||
Settings.focusPos = Settings.focusList.length - 3;
|
||||
} else {
|
||||
Settings.focusPos --;
|
||||
}
|
||||
Settings.focusChange(Settings.focusPos);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case KeyEvent.DOM_VK_DOWN :
|
||||
ev.preventDefault();
|
||||
if( Settings.isDialogHomepageDisplayed() ) {
|
||||
if(( Settings.focusPos == 1 ) || ( Settings.focusPos === 2 )) {
|
||||
Settings.focusPos ++;
|
||||
Settings.focusChange(Settings.focusPos);
|
||||
} else if( Settings.focusPos === 0 ) {
|
||||
Settings.focusPos += 2;
|
||||
Settings.focusChange(Settings.focusPos);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if( Settings.isDialogSearchDisplayed() ) {
|
||||
if( Settings.focusPos < Settings.focusList.length - 2 ) {
|
||||
Settings.focusPos ++;
|
||||
Settings.focusChange(Settings.focusPos);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case KeyEvent.DOM_VK_RETURN :
|
||||
ev.preventDefault();
|
||||
if( Settings.focusPos === 0 ) {
|
||||
Settings.focusList[Settings.focusPos].focus();
|
||||
if( Settings.isDialogHomepageDisplayed() ) {
|
||||
Awesomescreen.pointerImg.style.display = 'none';
|
||||
}
|
||||
}else{
|
||||
document.activeElement.classList.remove('active');
|
||||
document.activeElement.classList.add('active');
|
||||
}
|
||||
break;
|
||||
|
||||
case KeyEvent.DOM_VK_BACK_SPACE :
|
||||
if( Settings.isDialogHomepageDisplayed() ) {
|
||||
Settings.hideDialogHomepage();
|
||||
return true;
|
||||
}
|
||||
if( Settings.isDialogSearchDisplayed() ) {
|
||||
Settings.hideDialogSearch();
|
||||
return true;
|
||||
}
|
||||
Settings.hide();
|
||||
return true;
|
||||
|
||||
default :
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
941
gaia/tv_apps/browser/js/smartList.js
Executable file
941
gaia/tv_apps/browser/js/smartList.js
Executable file
|
|
@ -0,0 +1,941 @@
|
|||
/* globals KeyEvent */
|
||||
|
||||
'use strict';
|
||||
|
||||
(function(exports){
|
||||
|
||||
/**
|
||||
* Max number of list item visible when scroll.
|
||||
* @type {Number}
|
||||
*/
|
||||
const MAX_VISIBLE_ITEM = 7;
|
||||
|
||||
/**
|
||||
* Initial list element query number when first render list view
|
||||
* @type {Number}
|
||||
*/
|
||||
const INIT_LIST_ELEMENT_QUERY_NUMBER = 14;
|
||||
|
||||
/**
|
||||
* Max number of list html element can be render.
|
||||
* @type {Number}
|
||||
*/
|
||||
const MAX_LIST_ELEMENT = 21;
|
||||
|
||||
/**
|
||||
* Height of list item element, unit is rem.
|
||||
* @type {Number}
|
||||
*/
|
||||
const LIST_ITEM_HEIGHT = 12;
|
||||
|
||||
/**
|
||||
* 1rem equals to 10px
|
||||
* @type {Number}
|
||||
*/
|
||||
const PER_REM = 10;
|
||||
|
||||
/**
|
||||
* SmartList constructor
|
||||
* @param {Node} el - Target HTML element of SmartList.
|
||||
* @param {String} l10nTitle - Smart list title string.
|
||||
*/
|
||||
function SmartList(el, l10nTitle){
|
||||
this.el = el;
|
||||
|
||||
this.listEl = null;
|
||||
|
||||
this.listPointerEl = null;
|
||||
|
||||
this.l10nTitle = l10nTitle;
|
||||
|
||||
/**
|
||||
* Number of list item rendered.
|
||||
* @type {Number}
|
||||
*/
|
||||
this.listItemRenderNum = 0;
|
||||
|
||||
/**
|
||||
* Index of focus element in list.
|
||||
* @type {Number}
|
||||
*/
|
||||
this.focusIndex = 0;
|
||||
|
||||
/**
|
||||
* Index of list item start at.
|
||||
* @type {Number}
|
||||
*/
|
||||
this.listIndexStartAt = 0;
|
||||
|
||||
/**
|
||||
* Index of list item end at.
|
||||
* @type {Number}
|
||||
*/
|
||||
this.listIndexEndAt = 0;
|
||||
|
||||
/**
|
||||
* Index of visible list item start at.
|
||||
* @type {Number}
|
||||
*/
|
||||
this.listVisibleStartAt = 0;
|
||||
|
||||
/**
|
||||
* Current navigation folder.
|
||||
* @type {String}
|
||||
*/
|
||||
this.navState = null;
|
||||
|
||||
/**
|
||||
* Save user browser history.
|
||||
* @type {Array}
|
||||
*/
|
||||
this.navHistory = [];
|
||||
|
||||
/**
|
||||
* list items which in dom tree and been initialized but render with data
|
||||
* @type {Array}
|
||||
*/
|
||||
this.initializedItems = [];
|
||||
|
||||
/**
|
||||
* List item element map
|
||||
* @type {Object}
|
||||
*/
|
||||
this.listItemMap = {};
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
SmartList.prototype = {
|
||||
constructor: SmartList,
|
||||
|
||||
/**
|
||||
* Create smart list dom tree architecture
|
||||
*/
|
||||
|
||||
/**
|
||||
* <div class="smart-list-container">
|
||||
* <h2 class="smart-list-title" data-l10n-id={this.l10nTitle}></h2>
|
||||
* <div class="smart-list-wrap">
|
||||
* <ul class="smart-list-view"></ul>
|
||||
* <div class="smart-list-mask"></div>
|
||||
* <div class="smart-list-pointer"></div>
|
||||
* </div>
|
||||
* </div>
|
||||
*/
|
||||
init: function() {
|
||||
var container = document.createElement('div'),
|
||||
title = document.createElement('h2'),
|
||||
listWrap = document.createElement('div'),
|
||||
listView = document.createElement('ul'),
|
||||
listMask = document.createElement('div'),
|
||||
listPointer = document.createElement('div');
|
||||
|
||||
container.classList.add('smart-list-container');
|
||||
title.classList.add('smart-list-title');
|
||||
title.setAttribute('data-l10n-id', this.l10nTitle);
|
||||
listWrap.classList.add('smart-list-wrap');
|
||||
listView.classList.add('smart-list-view');
|
||||
listMask.classList.add('smart-list-mask');
|
||||
listPointer.classList.add('smart-list-pointer');
|
||||
|
||||
container.appendChild(title);
|
||||
container.appendChild(listWrap);
|
||||
listWrap.appendChild(listView);
|
||||
listWrap.appendChild(listMask);
|
||||
listWrap.appendChild(listPointer);
|
||||
|
||||
this.el.classList.add('smart-list');
|
||||
this.el.addEventListener('keyup', this.handleListKeyUp.bind(this));
|
||||
this.el.appendChild(container);
|
||||
this.listEl = listView;
|
||||
this.listPointerEl = listPointer;
|
||||
},
|
||||
|
||||
/**
|
||||
* Open smart list.
|
||||
*/
|
||||
open: function() {
|
||||
this.reset();
|
||||
this.dispatchLoadDataByRange(0, INIT_LIST_ELEMENT_QUERY_NUMBER, null,
|
||||
(listData) => {
|
||||
this.el.classList.add('show');
|
||||
|
||||
if (listData.length > 0) {
|
||||
this.render(listData);
|
||||
var focusEl = this.listItemMap[0];
|
||||
this.pointerItem(focusEl);
|
||||
this.focusItem(focusEl);
|
||||
} else {
|
||||
this.focusItem(this.el);
|
||||
}
|
||||
|
||||
var event = new Event('open');
|
||||
this.el.dispatchEvent(event);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Close smart list.
|
||||
*/
|
||||
close: function() {
|
||||
var event = new Event('close');
|
||||
this.navState = null;
|
||||
this.navHistory = [];
|
||||
this.el.classList.remove('show');
|
||||
this.el.dispatchEvent(event);
|
||||
if(this.isPointerDisplay()) {
|
||||
this.hideListPointer();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset smart list
|
||||
*/
|
||||
reset: function(){
|
||||
var key = 0;
|
||||
|
||||
this.listEl.style.transform = 'translateY(0rem)';
|
||||
|
||||
this.focusIndex = 0;
|
||||
this.listVisibleStartAt = 0;
|
||||
this.listIndexStartAt = 0;
|
||||
this.listIndexEndAt = 0;
|
||||
|
||||
for (key in this.listItemMap) {
|
||||
if (this.listItemMap.hasOwnProperty(key)) {
|
||||
this.resetItem(this.listItemMap[key]);
|
||||
this.initializedItems.push(this.listItemMap[key]);
|
||||
delete this.listItemMap[key];
|
||||
}
|
||||
}
|
||||
|
||||
this.hideListPointer();
|
||||
},
|
||||
|
||||
/**
|
||||
* Go back to previous folder. If there is no previous folder, close list.
|
||||
*/
|
||||
goBack: function() {
|
||||
if (!this.navState) {
|
||||
this.close();
|
||||
} else {
|
||||
this.navHistory.pop();
|
||||
this.navState = this.getCurNavHistory();
|
||||
var folderId = this.navState ? this.navState.folderId : null;
|
||||
this.reset();
|
||||
this.switchListView(folderId);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Render smart list
|
||||
* @param {Array} listData - Array of listData.
|
||||
*/
|
||||
render: function(listData){
|
||||
var itemEl = null,
|
||||
i = 0,
|
||||
length = listData.length;
|
||||
|
||||
for (; i < length; i++) {
|
||||
/*
|
||||
* if there are reseted list item, use these to render data,
|
||||
* else create new html element to render data
|
||||
*/
|
||||
if (this.initializedItems.length > 0) {
|
||||
itemEl = this.initializedItems.pop();
|
||||
this.updateItem(itemEl, listData[i]);
|
||||
} else {
|
||||
itemEl = this.createItem(listData[i]);
|
||||
this.listEl.appendChild(itemEl);
|
||||
this.listItemRenderNum++;
|
||||
}
|
||||
|
||||
itemEl.setAttribute('data-index', i);
|
||||
itemEl.style.transform =
|
||||
'translateY(' + (LIST_ITEM_HEIGHT * i) + 'rem)';
|
||||
this.listItemMap[i] = itemEl;
|
||||
}
|
||||
this.listIndexEndAt = length - 1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Is smart list opened.
|
||||
* @return {Boolean} - true when smart list opened.
|
||||
*/
|
||||
isDisplay: function() {
|
||||
return this.el.classList.contains('show');
|
||||
},
|
||||
|
||||
/**
|
||||
* Add new list item.
|
||||
* @param {Number} index - Index of new list item.
|
||||
* @param {Object} data - The list item data.
|
||||
*/
|
||||
addItem: function(index, data) {
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
var itemEl = null,
|
||||
transform = 0;
|
||||
if (index < this.listIndexStartAt) {
|
||||
// add item to the beginning of the list
|
||||
if (this.initializedItems.length > 0) {
|
||||
itemEl = this.initializedItems.pop();
|
||||
this.updateItem(itemEl, data);
|
||||
} else if (this.listItemRenderNum === MAX_LIST_ELEMENT){
|
||||
itemEl = this.listItemMap[this.listIndexEndAt];
|
||||
this.updateItem(itemEl, data);
|
||||
delete this.listItemMap[this.listIndexEndAt];
|
||||
this.listIndexEndAt--;
|
||||
} else {
|
||||
itemEl = this.createItem(data);
|
||||
this.listEl.appendChild(itemEl);
|
||||
this.listItemRenderNum++;
|
||||
}
|
||||
this.listItemMap[index] = itemEl;
|
||||
itemEl.setAttribute('data-index', index);
|
||||
transform = (index) * LIST_ITEM_HEIGHT;
|
||||
itemEl.style.transform =
|
||||
'translateY(' + transform + 'rem)';
|
||||
this.listIndexStartAt = index;
|
||||
} else if (index > this.listIndexEndAt) {
|
||||
// add item to the end of the list
|
||||
if (this.initializedItems.length > 0) {
|
||||
itemEl = this.initializedItems.pop();
|
||||
this.updateItem(itemEl, data);
|
||||
} else if (this.listItemRenderNum === MAX_LIST_ELEMENT){
|
||||
itemEl = this.listItemMap[this.listIndexStartAt];
|
||||
this.updateItem(itemEl, data);
|
||||
delete this.listItemMap[this.listIndexStartAt];
|
||||
this.listIndexStartAt++;
|
||||
} else {
|
||||
itemEl = this.createItem(data);
|
||||
this.listEl.appendChild(itemEl);
|
||||
this.listItemRenderNum++;
|
||||
}
|
||||
this.listItemMap[index] = itemEl;
|
||||
itemEl.setAttribute('data-index', index);
|
||||
transform = (index) * LIST_ITEM_HEIGHT;
|
||||
itemEl.style.transform =
|
||||
'translateY(' + transform + 'rem)';
|
||||
this.listIndexEndAt = index;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Create List Item.
|
||||
* @param {Object} data - list item data object
|
||||
*/
|
||||
|
||||
/**
|
||||
* <li class="list-item">
|
||||
* <div class="icon-box icon-box-left">
|
||||
* <div class="icon"></div>
|
||||
* </div>
|
||||
* <div class="text-box">
|
||||
* <span class="title"></span>
|
||||
* <span class="uri"></span>
|
||||
* </div>
|
||||
* <div class="icon-box icon-box-right">
|
||||
* <div class="icon" data-icon="arrow-right"></div>
|
||||
* </div>
|
||||
* </li>
|
||||
*/
|
||||
createItem: function(data) {
|
||||
var itemEl = document.createElement('li'),
|
||||
iconLeftEl = document.createElement('div'),
|
||||
iconImgLeftEl = document.createElement('div'),
|
||||
textEl = document.createElement('div'),
|
||||
titleEl = document.createElement('span'),
|
||||
uriEl = document.createElement('span'),
|
||||
iconRightEl = document.createElement('div'),
|
||||
iconImgRightEl = document.createElement('div');
|
||||
|
||||
itemEl.classList.add('list-item');
|
||||
itemEl.setAttribute('tabindex', '0');
|
||||
itemEl.setAttribute('data-type', data.type);
|
||||
iconLeftEl.classList.add('icon-box', 'icon-box-left');
|
||||
iconImgLeftEl.classList.add('icon');
|
||||
iconLeftEl.appendChild(iconImgLeftEl);
|
||||
|
||||
textEl.classList.add('text-box');
|
||||
titleEl.classList.add('title');
|
||||
uriEl.classList.add('uri');
|
||||
textEl.appendChild(titleEl);
|
||||
textEl.appendChild(uriEl);
|
||||
|
||||
iconRightEl.classList.add('icon-box', 'icon-box-right');
|
||||
iconImgRightEl.classList.add('icon');
|
||||
iconRightEl.appendChild(iconImgRightEl);
|
||||
|
||||
itemEl.appendChild(iconLeftEl);
|
||||
itemEl.appendChild(textEl);
|
||||
itemEl.appendChild(iconRightEl);
|
||||
|
||||
this.updateItemContent({
|
||||
item: itemEl,
|
||||
iconImgLeft: iconImgLeftEl,
|
||||
iconImgRight: iconImgRightEl,
|
||||
title: titleEl,
|
||||
uri: uriEl
|
||||
}, data);
|
||||
|
||||
// XXX: delegate these events later
|
||||
itemEl.
|
||||
addEventListener('keydown', this.handleItemKeyDown.bind(this));
|
||||
itemEl.
|
||||
addEventListener('keyup', this.handleItemKeyUp.bind(this));
|
||||
itemEl.
|
||||
addEventListener('mouseup', this.handleItemMouseUp.bind(this));
|
||||
itemEl.
|
||||
addEventListener('mouseover', this.handleItemMouseOver.bind(this));
|
||||
itemEl.
|
||||
addEventListener('mouseout', this.handleItemMouseOut.bind(this));
|
||||
|
||||
return itemEl;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update list item element content.
|
||||
* @param {Node} el - HTML list item to update.
|
||||
* @param {Object} data - list item data object.
|
||||
*/
|
||||
updateItem: function(el, data) {
|
||||
var iconImgLeftEl = el.querySelector('.icon-box-left .icon'),
|
||||
titleEl = el.querySelector('.title'),
|
||||
uriEl = el.querySelector('.uri'),
|
||||
iconImgRightEl = el.querySelector('.icon-box-right .icon');
|
||||
|
||||
this.updateItemContent({
|
||||
item: el,
|
||||
iconImgLeft: iconImgLeftEl,
|
||||
iconImgRight: iconImgRightEl,
|
||||
title: titleEl,
|
||||
uri: uriEl
|
||||
}, data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the content of the elements in an given item.
|
||||
* @param {Object} elems - all elements have to be updated.
|
||||
* @param {Object} data - list item data object.
|
||||
*/
|
||||
updateItemContent: function(elems, data) {
|
||||
var itemEl = elems.item,
|
||||
iconImgLeftEl = elems.iconImgLeft,
|
||||
iconImgRightEl = elems.iconImgRight,
|
||||
titleEl = elems.title,
|
||||
uriEl = elems.uri;
|
||||
|
||||
if (data.readOnly) {
|
||||
itemEl.setAttribute('readOnly', data.readOnly);
|
||||
} else {
|
||||
itemEl.setAttribute('readOnly', false);
|
||||
}
|
||||
|
||||
if (data.type) {
|
||||
itemEl.setAttribute('data-type', data.type);
|
||||
switch (data.type) {
|
||||
case 'folder':
|
||||
itemEl.setAttribute('data-folder', data.id);
|
||||
iconImgLeftEl.style.backgroundImage = '';
|
||||
iconImgLeftEl.setAttribute('data-icon', 'folder');
|
||||
titleEl.textContent = data.title;
|
||||
uriEl.textContent = '';
|
||||
iconImgRightEl.setAttribute('data-icon', 'arrow-right');
|
||||
break;
|
||||
case 'bookmark':
|
||||
itemEl.setAttribute('data-folder', '');
|
||||
if (data.iconUri) {
|
||||
iconImgLeftEl.style.backgroundImage = 'url(' + data.iconUri +')';
|
||||
iconImgLeftEl.setAttribute('data-icon', '');
|
||||
} else {
|
||||
iconImgLeftEl.style.backgroundImage = '';
|
||||
iconImgLeftEl.setAttribute('data-icon', 'default-fav');
|
||||
}
|
||||
titleEl.textContent = data.title ? data.title : data.uri;
|
||||
uriEl.textContent = data.uri;
|
||||
iconImgRightEl.setAttribute('data-icon', '');
|
||||
break;
|
||||
case 'button':
|
||||
itemEl.setAttribute('data-folder', '');
|
||||
iconImgLeftEl.style.backgroundImage = '';
|
||||
iconImgLeftEl.setAttribute('data-icon', 'arrow-left');
|
||||
titleEl.textContent = data.title;
|
||||
uriEl.textContent = '';
|
||||
iconImgRightEl.setAttribute('data-icon', '');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// The records without "type" will be recognized as history records.
|
||||
itemEl.setAttribute('data-type', 'bookmark');
|
||||
itemEl.setAttribute('data-folder', '');
|
||||
if (data.iconUri) {
|
||||
iconImgLeftEl.setAttribute('data-icon', '');
|
||||
iconImgLeftEl.style.backgroundImage = 'url(' + data.iconUri +')';
|
||||
} else {
|
||||
iconImgLeftEl.setAttribute('data-icon', 'default-fav');
|
||||
iconImgLeftEl.style.backgroundImage = '';
|
||||
}
|
||||
titleEl.textContent = data.title ? data.title : data.uri;
|
||||
uriEl.textContent = data.uri;
|
||||
iconImgRightEl.setAttribute('data-icon', '');
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove the focus list item.
|
||||
*/
|
||||
removeFocusItem: function() {
|
||||
var targetEl = null,
|
||||
i = 0;
|
||||
|
||||
targetEl = this.listItemMap[this.focusIndex];
|
||||
if (targetEl) {
|
||||
this.resetItem(targetEl);
|
||||
this.initializedItems.push(targetEl);
|
||||
|
||||
//reindex and update transform of the element behind the focus index
|
||||
for (i = this.focusIndex; i < this.listIndexEndAt; i++) {
|
||||
this.listItemMap[i] = this.listItemMap[i+1];
|
||||
targetEl = this.listItemMap[i];
|
||||
targetEl.setAttribute('data-index', i);
|
||||
this.shiftElement(targetEl, (-1 * LIST_ITEM_HEIGHT));
|
||||
}
|
||||
|
||||
delete this.listItemMap[this.listIndexEndAt];
|
||||
this.listIndexEndAt--;
|
||||
|
||||
// if the removed item is the last item of index,
|
||||
// focus the new last element
|
||||
if (this.listItemMap[this.focusIndex]) {
|
||||
this.focusItem(this.listItemMap[this.focusIndex]);
|
||||
} else if (this.listIndexEndAt !== -1) {
|
||||
this.focusIndex = this.listIndexEndAt;
|
||||
this.focusItem(this.listItemMap[this.focusIndex]);
|
||||
}
|
||||
|
||||
var event = new Event('itemRemoved');
|
||||
this.el.dispatchEvent(event);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Focus list item.
|
||||
* @param {Node} el - HTML list item to focus.
|
||||
*/
|
||||
focusItem: function(el) {
|
||||
el.focus();
|
||||
},
|
||||
|
||||
pointerItem: function(el) {
|
||||
var pos = el.getBoundingClientRect(),
|
||||
transposX = (Math.ceil(pos.right) - 120 ) / PER_REM,
|
||||
transposY = (Math.ceil(pos.bottom) - 40 ) / PER_REM;
|
||||
|
||||
this.listPointerEl.style.transform =
|
||||
'translate(' + transposX + 'rem,' + transposY + 'rem)';
|
||||
this.showListPointer();
|
||||
},
|
||||
|
||||
/**
|
||||
* Focus the First visible list item.
|
||||
*/
|
||||
focusFirstVisibleItem: function() {
|
||||
var targetEl = null;
|
||||
|
||||
// if there is list item rendered and the current active element not in
|
||||
// smart list, focus the first visible list item.
|
||||
if (this.listIndexEndAt !== -1 &&
|
||||
Array.prototype.indexOf.call(
|
||||
this.listEl.children,
|
||||
document.activeElement) === -1) {
|
||||
this.focusIndex = this.listVisibleStartAt;
|
||||
targetEl = this.listItemMap[this.focusIndex];
|
||||
if (targetEl) {
|
||||
this.pointerItem(targetEl);
|
||||
this.focusItem(targetEl);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset list item.
|
||||
* @param {Node} el - HTML list item to focus.
|
||||
*/
|
||||
resetItem: function(el) {
|
||||
var iconImgEl = el.querySelector('.icon-box-left .icon'),
|
||||
titleEl = el.querySelector('.title'),
|
||||
uriEl = el.querySelector('.uri');
|
||||
|
||||
el.setAttribute('data-index', -1);
|
||||
el.setAttribute('data-type', '');
|
||||
el.setAttribute('data-folder', '');
|
||||
el.setAttribute('readOnly', false);
|
||||
el.style.transform = '';
|
||||
iconImgEl.style.backgroundImage = '';
|
||||
iconImgEl.setAttribute('data-icon', '');
|
||||
titleEl.textContent = '';
|
||||
uriEl.textContent = '';
|
||||
},
|
||||
|
||||
/**
|
||||
* Preload the item out of visible view.
|
||||
* @param {Number} listIndex - preload list index.
|
||||
*/
|
||||
preloadItem: function(listIndex) {
|
||||
var dataIndex = 0;
|
||||
|
||||
if (this.navState) {
|
||||
if (listIndex === 0) {
|
||||
var data = this.generateBackButtonData(this.navState.folderTitle);
|
||||
this.addItem(0, data);
|
||||
} else {
|
||||
dataIndex = listIndex - 1;
|
||||
this.dispatchLoadDataByIndex(
|
||||
listIndex,
|
||||
dataIndex,
|
||||
this.navState.folderId
|
||||
);
|
||||
}
|
||||
} else {
|
||||
dataIndex = listIndex;
|
||||
this.dispatchLoadDataByIndex(
|
||||
listIndex,
|
||||
dataIndex,
|
||||
null
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Shift list item transform
|
||||
* @param {Node} el - list item element to transform
|
||||
* @param {Nunber} shift - shift transform, unit is rem
|
||||
*/
|
||||
shiftElement: function(el, shift) {
|
||||
var style = el.style.transform.split('('),
|
||||
transform = parseInt(style[1], 10);
|
||||
transform += shift;
|
||||
el.style.transform =
|
||||
'translateY(' + transform + 'rem) translateZ(0.01px)';
|
||||
},
|
||||
|
||||
/**
|
||||
* Get Focus list element's index
|
||||
*/
|
||||
getFocusItemIndex: function() {
|
||||
return this.focusIndex;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get Focus list element's uri content
|
||||
*/
|
||||
getFocusItemUri: function() {
|
||||
var targetEl = this.listItemMap[this.focusIndex];
|
||||
return targetEl ? targetEl.querySelector('.uri').textContent : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get Focus list element's title content
|
||||
*/
|
||||
getFocusItemTitle: function() {
|
||||
var targetEl = this.listItemMap[this.focusIndex];
|
||||
return targetEl ? targetEl.querySelector('.title').textContent : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Update focus list element's title content
|
||||
* @param {String} title - title to update
|
||||
*/
|
||||
updateFocusItemTitle: function(title) {
|
||||
var targetEl = this.listItemMap[this.focusIndex];
|
||||
|
||||
if (targetEl) {
|
||||
targetEl.querySelector('.title').textContent = title;
|
||||
this.focusItem(targetEl);
|
||||
var event = new Event('itemUpdated');
|
||||
this.el.dispatchEvent(event);
|
||||
}
|
||||
},
|
||||
|
||||
isPointerDisplay: function() {
|
||||
return this.listPointerEl.classList.contains('show');
|
||||
},
|
||||
|
||||
showListPointer: function() {
|
||||
this.listPointerEl.classList.add('show');
|
||||
},
|
||||
|
||||
hideListPointer: function() {
|
||||
this.listPointerEl.classList.remove('show');
|
||||
},
|
||||
|
||||
/**
|
||||
* Add folder id and folder title
|
||||
* @param {[type]} folderId [description]
|
||||
* @param {[type]} folderTitle [description]
|
||||
*/
|
||||
addNavHistory: function(folderId, folderTitle) {
|
||||
this.navHistory.push({
|
||||
folderId: folderId,
|
||||
folderTitle: folderTitle
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get current user browse state.
|
||||
* @return {String} current user browse folder.
|
||||
*/
|
||||
getCurNavHistory: function() {
|
||||
var len = this.navHistory.length;
|
||||
if (len > 0) {
|
||||
return this.navHistory[len - 1];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Generate back button data.
|
||||
* @return {Object} - back button data
|
||||
*/
|
||||
generateBackButtonData: function(title) {
|
||||
return {
|
||||
type: 'button',
|
||||
title: 'Back to ' + title
|
||||
};
|
||||
},
|
||||
|
||||
switchListView: function(folderId) {
|
||||
this.dispatchLoadDataByRange(0, INIT_LIST_ELEMENT_QUERY_NUMBER, folderId,
|
||||
(listData) => {
|
||||
// if user is browser list data in a folder,
|
||||
// add back button data at the first place of listData
|
||||
if (this.navState) {
|
||||
listData.unshift(
|
||||
this.generateBackButtonData(this.navState.folderTitle)
|
||||
);
|
||||
}
|
||||
|
||||
if (listData.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.render(listData);
|
||||
|
||||
// focus the first item in list. if the first item is button and list
|
||||
// item length is bigger than 1, focus the second item
|
||||
this.focusIndex = 0;
|
||||
if (this.navState && this.listIndexEndAt > 0) {
|
||||
this.focusIndex = 1;
|
||||
}
|
||||
var focusEl = this.listItemMap[this.focusIndex];
|
||||
this.pointerItem(focusEl);
|
||||
this.focusItem(focusEl);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Move focus to the previous element
|
||||
*/
|
||||
moveFocusIndexUp: function() {
|
||||
this.focusIndex -= 1;
|
||||
var preloadItemIndex = this.focusIndex - MAX_VISIBLE_ITEM;
|
||||
if (preloadItemIndex >= 0 && preloadItemIndex < this.listIndexStartAt) {
|
||||
this.preloadItem(preloadItemIndex);
|
||||
}
|
||||
|
||||
// if the new focus element is before this.listVisibleStartAt,
|
||||
// update list item's transform to shift down
|
||||
var targetEl = this.listItemMap[this.focusIndex];
|
||||
if (this.focusIndex < this.listVisibleStartAt) {
|
||||
this.listVisibleStartAt--;
|
||||
this.shiftElement(this.listEl, LIST_ITEM_HEIGHT);
|
||||
this.focusItem(targetEl);
|
||||
} else {
|
||||
this.pointerItem(targetEl);
|
||||
this.focusItem(targetEl);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Move focus to the next element
|
||||
*/
|
||||
moveFocusIndexDown: function() {
|
||||
this.focusIndex += 1;
|
||||
|
||||
// pre-load list item later then listIndexEndAt
|
||||
var preloadItemIndex = this.focusIndex + MAX_VISIBLE_ITEM;
|
||||
if (preloadItemIndex > this.listIndexEndAt) {
|
||||
this.preloadItem(preloadItemIndex);
|
||||
}
|
||||
|
||||
// if the new focus element is after this.listIndexEndAt,
|
||||
// update list item's transform to shift up
|
||||
var targetEl = this.listItemMap[this.focusIndex];
|
||||
if ((this.focusIndex - this.listVisibleStartAt) ===
|
||||
MAX_VISIBLE_ITEM ) {
|
||||
this.listVisibleStartAt++;
|
||||
this.shiftElement(this.listEl, (-1 * LIST_ITEM_HEIGHT));
|
||||
this.focusItem(targetEl);
|
||||
} else {
|
||||
this.pointerItem(targetEl);
|
||||
this.focusItem(targetEl);
|
||||
}
|
||||
},
|
||||
|
||||
handleListKeyUp: function(e) {
|
||||
switch(e.keyCode){
|
||||
case KeyEvent.DOM_VK_ESCAPE:
|
||||
case KeyEvent.DOM_VK_BACK_SPACE:
|
||||
this.goBack();
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
handleItemKeyDown: function(e) {
|
||||
switch(e.keyCode){
|
||||
case KeyEvent.DOM_VK_UP:
|
||||
if(this.focusIndex > this.listIndexStartAt) {
|
||||
this.moveFocusIndexUp();
|
||||
}
|
||||
break;
|
||||
case KeyEvent.DOM_VK_DOWN:
|
||||
if(this.focusIndex < this.listIndexEndAt) {
|
||||
this.moveFocusIndexDown();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
handleItemKeyUp: function(e) {
|
||||
switch(e.keyCode){
|
||||
case KeyEvent.DOM_VK_SUBMENU:
|
||||
this.handleKeySubmenu(e);
|
||||
break;
|
||||
case KeyEvent.DOM_VK_RETURN:
|
||||
this.handleKeyReturn(e);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
handleItemMouseUp: function(e) {
|
||||
switch(e.button){
|
||||
case 0 :
|
||||
// left click
|
||||
this.handleKeyReturn(e);
|
||||
break;
|
||||
case 2:
|
||||
//right click
|
||||
this.handleKeySubmenu(e);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
handleItemMouseOver: function(e) {
|
||||
var targetEl = e.currentTarget;
|
||||
if (targetEl) {
|
||||
var dataIndex = targetEl.getAttribute('data-index');
|
||||
var event = new CustomEvent('mouseOverItem', {detail: targetEl});
|
||||
targetEl.focus();
|
||||
this.focusIndex = parseInt(dataIndex, 10);
|
||||
this.el.dispatchEvent(event);
|
||||
}
|
||||
},
|
||||
|
||||
handleItemMouseOut: function(e) {
|
||||
var targetEl = e.currentTarget;
|
||||
if (targetEl) {
|
||||
this.hideListPointer();
|
||||
targetEl.blur();
|
||||
}
|
||||
},
|
||||
|
||||
handleKeyReturn: function(e) {
|
||||
var targetEl = e.currentTarget,
|
||||
type = targetEl.getAttribute('data-type'),
|
||||
folderId = null;
|
||||
|
||||
switch(type) {
|
||||
case 'folder':
|
||||
var folderTitle = targetEl.querySelector('.title').textContent;
|
||||
|
||||
folderId = targetEl.getAttribute('data-folder');
|
||||
this.addNavHistory(folderId, folderTitle);
|
||||
this.navState = this.getCurNavHistory();
|
||||
this.reset();
|
||||
this.switchListView(folderId);
|
||||
break;
|
||||
case 'bookmark':
|
||||
var uriEL = targetEl.querySelector('.uri');
|
||||
if (uriEL) {
|
||||
this.dispatchDisplayWebsite(uriEL.textContent);
|
||||
this.close();
|
||||
}
|
||||
break;
|
||||
case 'button':
|
||||
this.navHistory.pop();
|
||||
this.navState = this.getCurNavHistory();
|
||||
folderId = this.navState ? this.navState.folderId : null;
|
||||
this.reset();
|
||||
this.switchListView(folderId);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
handleKeySubmenu: function(e) {
|
||||
var targetEl = e.currentTarget;
|
||||
if (targetEl.getAttribute('data-type') === 'bookmark') {
|
||||
var event = new CustomEvent('showSubMenu', {
|
||||
detail: {
|
||||
readOnly: targetEl.getAttribute('readOnly')
|
||||
}
|
||||
});
|
||||
this.el.dispatchEvent(event);
|
||||
}
|
||||
},
|
||||
|
||||
dispatchDisplayWebsite: function(uri) {
|
||||
var event = new CustomEvent('displayWebsite', {detail: uri});
|
||||
this.el.dispatchEvent(event);
|
||||
},
|
||||
|
||||
dispatchLoadDataByRange: function(start, number, folderId, cb) {
|
||||
var eventDetail = {
|
||||
startAt: start,
|
||||
number: number,
|
||||
folderId: folderId,
|
||||
callback: cb
|
||||
};
|
||||
var event = new CustomEvent('loadDataByRange', {detail: eventDetail});
|
||||
this.el.dispatchEvent(event);
|
||||
},
|
||||
|
||||
dispatchLoadDataByIndex: function(listIndex, dataIndex, navState) {
|
||||
var event = new CustomEvent('loadDataByIndex', {
|
||||
detail: {
|
||||
listIndex: listIndex,
|
||||
dataIndex: dataIndex,
|
||||
folderId: navState
|
||||
}});
|
||||
this.el.dispatchEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
exports.SmartList = SmartList;
|
||||
})(window);
|
||||
107
gaia/tv_apps/browser/js/sync/bookmarks.js
Executable file
107
gaia/tv_apps/browser/js/sync/bookmarks.js
Executable file
|
|
@ -0,0 +1,107 @@
|
|||
/* 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/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
/* exported SyncBookmark */
|
||||
/* global SyncBrowserDB */
|
||||
/* global SyncDsHelper */
|
||||
|
||||
var SyncBookmark = (function () {
|
||||
var syncDataStore;
|
||||
|
||||
function updateWrapper(data) {
|
||||
return new Promise(resolve => {
|
||||
if (data.deleted) {
|
||||
SyncBrowserDB.removeBookmark(data.id, () => {
|
||||
resolve();
|
||||
});
|
||||
} else {
|
||||
SyncBrowserDB.updateRawBookmark(data, () => {
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function handleTask(task) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var data = [];
|
||||
if (task.data && task.data.fxsyncRecords) {
|
||||
for (var key in task.data.fxsyncRecords) {
|
||||
data.push(task.data.fxsyncRecords[key]);
|
||||
}
|
||||
}
|
||||
switch(task.operation) {
|
||||
case 'update':
|
||||
case 'add':
|
||||
Promise.all(data.map(updateWrapper)).then(resolve);
|
||||
break;
|
||||
case 'clear':
|
||||
// Ignore clear operation.
|
||||
resolve();
|
||||
break;
|
||||
case 'remove':
|
||||
getBookmarkByDsId(task.id, bookmark => {
|
||||
if (bookmark) {
|
||||
SyncBrowserDB.removeBookmark(bookmark.id, () => {
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'done':
|
||||
resolve();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getBookmarkByDsId(dsid, callback) {
|
||||
SyncBrowserDB.getBookmark({bmkUri: dsid}, bookmark => {
|
||||
if (bookmark) {
|
||||
callback(bookmark);
|
||||
} else {
|
||||
var specialType =
|
||||
['query', 'microsummary', 'folder', 'livemark', 'separator'];
|
||||
var isSpecialType = specialType.some(value => {
|
||||
return dsid.indexOf(value + '|') === 0;
|
||||
});
|
||||
var tmp = dsid.split('|');
|
||||
if (isSpecialType && tmp.length === 2) {
|
||||
var fxsyncId = tmp[1];
|
||||
SyncBrowserDB.getBookmark(fxsyncId, callback);
|
||||
} else {
|
||||
console.warn('trying to delete a unknown type?', dsid);
|
||||
callback(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function start() {
|
||||
if (syncDataStore) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
syncDataStore = new SyncDsHelper('bookmarks_store');
|
||||
return syncDataStore.init().then(() => {
|
||||
return syncDataStore.start(handleTask);
|
||||
});
|
||||
}
|
||||
|
||||
function stop() {
|
||||
if (!syncDataStore) {
|
||||
return Promise.reject('Uninitialized DataStore');
|
||||
}
|
||||
return syncDataStore.stop().then(() => {
|
||||
syncDataStore = null;
|
||||
return SyncBrowserDB.clearBookmarks();
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
start: start,
|
||||
stop: stop
|
||||
};
|
||||
})();
|
||||
1958
gaia/tv_apps/browser/js/sync/db.js
Executable file
1958
gaia/tv_apps/browser/js/sync/db.js
Executable file
File diff suppressed because it is too large
Load diff
113
gaia/tv_apps/browser/js/sync/ds_helper.js
Executable file
113
gaia/tv_apps/browser/js/sync/ds_helper.js
Executable file
|
|
@ -0,0 +1,113 @@
|
|||
/* 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/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
/* exported SyncDsHelper */
|
||||
/* global localStorage */
|
||||
|
||||
var SyncDsHelper = function (dataStoreName) {
|
||||
if (typeof dataStoreName !== 'string') {
|
||||
throw new Event('Incorrect DataStore name.');
|
||||
}
|
||||
this.dataStoreName = dataStoreName;
|
||||
this.dataStoreRevId = dataStoreName + '::revId';
|
||||
};
|
||||
|
||||
SyncDsHelper.prototype = {
|
||||
store: null,
|
||||
dataChangeHandler: null,
|
||||
|
||||
init() {
|
||||
return this._ensureStore();
|
||||
},
|
||||
|
||||
_ensureStore() {
|
||||
if (this.store) {
|
||||
return Promise.resolve(this.store);
|
||||
}
|
||||
return navigator.getDataStores(this.dataStoreName).then(stores => {
|
||||
this.store = stores[0];
|
||||
return this.store;
|
||||
});
|
||||
},
|
||||
|
||||
setSyncedRevId(revId) {
|
||||
localStorage.setItem(this.dataStoreRevId, revId);
|
||||
},
|
||||
|
||||
getSyncedRevId() {
|
||||
return localStorage.getItem(this.dataStoreRevId);
|
||||
},
|
||||
|
||||
registerStoreChangeEvent(handler) {
|
||||
this._ensureStore().then(() => {
|
||||
if (!this.dataChangeHandler) {
|
||||
this.store.removeEventListener('change', this.dataChangeHandler);
|
||||
}
|
||||
this.dataChangeHandler = handler;
|
||||
this.store.addEventListener('change', this.dataChangeHandler);
|
||||
});
|
||||
},
|
||||
|
||||
unregisterStoreChangeEvent() {
|
||||
return this._ensureStore().then(() => {
|
||||
this.store.removeEventListener('change', this.dataChangeHandler);
|
||||
this.dataChangeHandler = null;
|
||||
});
|
||||
},
|
||||
|
||||
clear() {
|
||||
localStorage.removeItem(this.dataStoreRevId);
|
||||
return this._ensureStore().then(() => {
|
||||
return this.store.clear();
|
||||
});
|
||||
},
|
||||
|
||||
start(handleTask) {
|
||||
return this.init().then(() => {
|
||||
this.registerStoreChangeEvent(() => {
|
||||
this.dataStoreSync(handleTask);
|
||||
});
|
||||
return this.dataStoreSync(handleTask);
|
||||
});
|
||||
},
|
||||
|
||||
stop() {
|
||||
return this.unregisterStoreChangeEvent().then(this.clear.bind(this));
|
||||
},
|
||||
|
||||
dataStoreSync(handle) {
|
||||
return this._ensureStore().then(() => {
|
||||
var _revId = this.getSyncedRevId();
|
||||
return new Promise((resolve, reject) => {
|
||||
var runNextTask = (cursor) => {
|
||||
cursor.next().then(task => {
|
||||
manageTask(cursor, task);
|
||||
});
|
||||
};
|
||||
|
||||
var post = (cursor, task) => {
|
||||
if (task.operation === 'done') {
|
||||
this.setSyncedRevId(this.store.revisionId);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
runNextTask(cursor);
|
||||
};
|
||||
|
||||
var manageTask = (cursor, task) => {
|
||||
if (handle) {
|
||||
handle(task).then(() => post(cursor, task));
|
||||
} else {
|
||||
post(cursor, task);
|
||||
}
|
||||
};
|
||||
|
||||
runNextTask(this.store.sync(_revId));
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
86
gaia/tv_apps/browser/js/sync/history.js
Executable file
86
gaia/tv_apps/browser/js/sync/history.js
Executable file
|
|
@ -0,0 +1,86 @@
|
|||
/* 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/. */
|
||||
|
||||
'use strict';
|
||||
|
||||
/* exported SyncHistory */
|
||||
/* global SyncBrowserDB */
|
||||
/* global SyncDsHelper */
|
||||
|
||||
/*
|
||||
exampleHistroyInIndexedDB = {
|
||||
"uri": "http://example.com/",
|
||||
"title": "Hello World",
|
||||
"frecency": 1,
|
||||
"iconUri": "http://example.com/favicon.ico",
|
||||
"screenshot": {}
|
||||
}
|
||||
*/
|
||||
|
||||
var SyncHistory = (function () {
|
||||
var syncDataStore;
|
||||
|
||||
function handleTask(task) {
|
||||
return new Promise(resolve => {
|
||||
switch(task.operation) {
|
||||
case 'update':
|
||||
case 'add':
|
||||
var data = {};
|
||||
var syncedData = task.data;
|
||||
if (syncedData && syncedData.url &&
|
||||
Array.isArray(syncedData.visits) && syncedData.visits.length > 0) {
|
||||
data.uri = syncedData.url;
|
||||
data.title = syncedData.title;
|
||||
data.fxsyncId = syncedData.fxsyncId;
|
||||
data.timestamp = syncedData.visits[0];
|
||||
} else {
|
||||
console.warn('Invalid places record:', task);
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
SyncBrowserDB.updateRawHistory(data, () => {
|
||||
resolve();
|
||||
});
|
||||
break;
|
||||
case 'clear':
|
||||
// Ignore clear operation.
|
||||
resolve();
|
||||
break;
|
||||
case 'remove':
|
||||
SyncBrowserDB.removeHistory(task.id, () => {
|
||||
resolve();
|
||||
});
|
||||
break;
|
||||
case 'done':
|
||||
resolve();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function start() {
|
||||
if (syncDataStore) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
syncDataStore = new SyncDsHelper('places');
|
||||
return syncDataStore.init().then(() => {
|
||||
return syncDataStore.start(handleTask);
|
||||
});
|
||||
}
|
||||
|
||||
function stop() {
|
||||
if (!syncDataStore) {
|
||||
return Promise.reject('Uninitialized DataStore');
|
||||
}
|
||||
return syncDataStore.stop().then(() => {
|
||||
syncDataStore = null;
|
||||
return SyncBrowserDB.clearHistoryDeep();
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
start: start,
|
||||
stop: stop
|
||||
};
|
||||
})();
|
||||
163
gaia/tv_apps/browser/js/sync/manager_bridge.js
Executable file
163
gaia/tv_apps/browser/js/sync/manager_bridge.js
Executable file
|
|
@ -0,0 +1,163 @@
|
|||
/* 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/. */
|
||||
|
||||
/**
|
||||
* This module acts as a bridge between the browser UI and the Sync manager
|
||||
* that lives in the System application.
|
||||
*
|
||||
* It allows consumers to subscribe to updates done in the Sync state machine
|
||||
* so they can update the UI accordingly.
|
||||
*
|
||||
* The simple high level picture of Sync in the browser is:
|
||||
*
|
||||
* settings.js toolbar.js
|
||||
* | |
|
||||
* | |
|
||||
* \______ manager_bridge.js _______/
|
||||
* |
|
||||
* | IAC
|
||||
* |
|
||||
* smart_system/sync_manager.js
|
||||
*
|
||||
*/
|
||||
|
||||
/* global LazyLoader */
|
||||
/* global SyncBookmark */
|
||||
/* global SyncHistory */
|
||||
|
||||
'use strict';
|
||||
|
||||
(function(exports) {
|
||||
|
||||
const DEBUG = false;
|
||||
function debug() {
|
||||
if (DEBUG) {
|
||||
console.log('[SyncManagerBridge] ' + Array.slice(arguments).concat());
|
||||
}
|
||||
}
|
||||
|
||||
var SyncManagerBridge = {
|
||||
_port: null,
|
||||
_requests: new Map(),
|
||||
_listeners: new Map(),
|
||||
|
||||
addListener(listener) {
|
||||
debug('addListener', listener);
|
||||
if (!this._listeners.has(listener)) {
|
||||
this._listeners.set(listener);
|
||||
}
|
||||
},
|
||||
|
||||
removeListener(listener) {
|
||||
debug('removeListener', listener);
|
||||
if (!this._listeners.has(listener)) {
|
||||
return;
|
||||
}
|
||||
this._listeners.delete(listener);
|
||||
},
|
||||
|
||||
connect() {
|
||||
if (this._port) {
|
||||
return Promise.resolve(this._port);
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
navigator.mozApps.getSelf().onsuccess = event => {
|
||||
var app = event.target.result;
|
||||
app.connect('gaia-sync-management').then(ports => {
|
||||
if (!ports || !ports.length) {
|
||||
return reject();
|
||||
}
|
||||
this._port = ports[0];
|
||||
this._port.onmessage = this.onmessage.bind(this);
|
||||
resolve(this._port);
|
||||
}).catch(reject);
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
iacRequest(request) {
|
||||
debug('iacRequest', JSON.stringify(request));
|
||||
return new Promise(resolve => {
|
||||
this.connect().then(port => {
|
||||
if (request.id) {
|
||||
this._requests.set(request.id, resolve);
|
||||
}
|
||||
port.postMessage(request);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
onmessage(event) {
|
||||
var self = this;
|
||||
function notify() {
|
||||
if (self._listeners.size) {
|
||||
Array.from(self._listeners.keys()).forEach(listener => {
|
||||
listener(message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var message = event.data;
|
||||
if (!message) {
|
||||
return;
|
||||
}
|
||||
|
||||
debug('message', JSON.stringify(message));
|
||||
|
||||
if (message.id && this._requests.has(message.id)) {
|
||||
this._requests.get(message.id)(message);
|
||||
this._requests.delete(message.id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.name !== 'onsyncchange') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.state == 'disabled' ||
|
||||
message.state == 'enabled') {
|
||||
LazyLoader.load([
|
||||
'js/sync/ds_helper.js',
|
||||
'js/sync/bookmarks.js',
|
||||
'js/sync/history.js'
|
||||
]).then(() => {
|
||||
var method = message.state == 'enabled' ? 'start'
|
||||
: 'stop';
|
||||
Promise.all([
|
||||
SyncBookmark[method](),
|
||||
SyncHistory[method]()
|
||||
]).catch(error => {
|
||||
console.warn(error);
|
||||
});
|
||||
|
||||
notify();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
notify();
|
||||
},
|
||||
|
||||
getInfo() {
|
||||
return this.iacRequest({
|
||||
name: 'getInfo',
|
||||
id: Date.now()
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
['enable',
|
||||
'disable',
|
||||
'sync'].forEach(requestName => {
|
||||
SyncManagerBridge[requestName] = () => {
|
||||
return SyncManagerBridge.iacRequest({
|
||||
name: requestName
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
exports.SyncManagerBridge = SyncManagerBridge;
|
||||
|
||||
}(window));
|
||||
507
gaia/tv_apps/browser/js/sync/settings.js
Executable file
507
gaia/tv_apps/browser/js/sync/settings.js
Executable file
|
|
@ -0,0 +1,507 @@
|
|||
/* 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 Awesomescreen */
|
||||
/* global Browser */
|
||||
/* global BrowserDialog */
|
||||
/* global ERROR_DIALOG_CLOSED_BY_USER */
|
||||
/* global ERROR_INVALID_SYNC_ACCOUNT */
|
||||
/* global ERROR_OFFLINE */
|
||||
/* global ERROR_SYNC_APP_KILLED */
|
||||
/* global ERROR_UNKNOWN */
|
||||
/* global LazyLoader */
|
||||
/* global Settings */
|
||||
/* global SettingsListener */
|
||||
/* global SyncManagerBridge */
|
||||
|
||||
'use strict';
|
||||
|
||||
(function(exports) {
|
||||
const DEBUG = false;
|
||||
function debug() {
|
||||
if (DEBUG) {
|
||||
console.log('[FirefoxSyncSettings] ' + Array.slice(arguments).concat());
|
||||
}
|
||||
}
|
||||
|
||||
// Screens
|
||||
const ENABLED = 'enabled';
|
||||
const DISABLED = 'disabled';
|
||||
|
||||
const BOOKMARKS_SETTING = 'sync.collections.bookmarks.enabled';
|
||||
const HISTORY_SETTING = 'sync.collections.history.enabled';
|
||||
|
||||
const ELEMENTS = [{
|
||||
screen: DISABLED,
|
||||
selector: '#fxsync-sign-in-button',
|
||||
event: 'mouseup',
|
||||
listener: 'enable'
|
||||
}, {
|
||||
screen: ENABLED,
|
||||
selector: '#fxsync-signed-in-as'
|
||||
}, {
|
||||
screen: ENABLED,
|
||||
selector: '#fxsync-sync-now-button',
|
||||
event: 'mouseup',
|
||||
listener: 'sync'
|
||||
}, {
|
||||
screen: ENABLED,
|
||||
selector: '#fxsync-sign-out-button',
|
||||
event: 'mouseup',
|
||||
listener: 'disable'
|
||||
}, {
|
||||
screen: ENABLED,
|
||||
selector: '#fxsync-collection-bookmarks',
|
||||
event: 'click',
|
||||
listener: 'onbookmarkschecked',
|
||||
init: 'onbookmarkschange'
|
||||
}, {
|
||||
screen: ENABLED,
|
||||
selector: '#fxsync-collection-history',
|
||||
event: 'click',
|
||||
listener: 'onhistorychecked',
|
||||
init: 'onhistorychange'
|
||||
}, {
|
||||
screen: ENABLED,
|
||||
selector: '#fxsync-tos',
|
||||
event: 'click',
|
||||
listener: 'openTos'
|
||||
}, {
|
||||
screen: ENABLED,
|
||||
selector: '#fxsync-privacy',
|
||||
event: 'click',
|
||||
listener: 'openPrivacy'
|
||||
}];
|
||||
|
||||
function getElementName(str) {
|
||||
str = str.toLowerCase().replace('#fxsync-', '');
|
||||
return str.replace(/-([a-z])/g, g => {
|
||||
return g[1].toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
var FirefoxSyncSettings = {
|
||||
init() {
|
||||
this.area = document.querySelector('#fxsync-area');
|
||||
this.elements = {
|
||||
enabled: this.area.querySelector('#fxsync-enabled'),
|
||||
disabled: this.area.querySelector('#fxsync-disabled')
|
||||
};
|
||||
this.listeners = new Map();
|
||||
this.collections = new Map();
|
||||
this.screen = null;
|
||||
this.state = 'disabled';
|
||||
|
||||
this.showScreen(DISABLED);
|
||||
|
||||
LazyLoader.load('shared/js/settings_listener.js').then(() => {
|
||||
[{
|
||||
name: BOOKMARKS_SETTING,
|
||||
onchange: 'onbookmarkschange'
|
||||
}, {
|
||||
name: HISTORY_SETTING,
|
||||
onchange: 'onhistorychange'
|
||||
}].forEach(setting => {
|
||||
SettingsListener.observe(setting.name, true, enabled => {
|
||||
debug(setting.name + ' changed to ' + enabled);
|
||||
enabled ? this.collections.set(setting.name, enabled)
|
||||
: this.collections.delete(setting.name);
|
||||
this[setting.onchange]();
|
||||
this.maybeEnableSyncNow();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
SyncManagerBridge.addListener(this.onsyncchange.bind(this));
|
||||
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
debug('Visibility change', document.hidden);
|
||||
if (!document.hidden) {
|
||||
this.refresh();
|
||||
}
|
||||
});
|
||||
|
||||
this.refresh();
|
||||
},
|
||||
|
||||
refresh() {
|
||||
debug('Refreshing');
|
||||
SyncManagerBridge.getInfo().then(this.onsyncchange.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* This is where most part of the Sync settings logic lives.
|
||||
*
|
||||
* Here we handle Sync lifecycle events coming from the SyncManager
|
||||
* through the SyncMangerBridge. We react to these events by
|
||||
* changing the screen depending on the state.
|
||||
*
|
||||
* We show two possible different state:
|
||||
* 1. Logged out screen. We simply show the user what Sync is and
|
||||
* allow her to start using it or to keep walking.
|
||||
* 2. Logged in screen. We show to the user several different options:
|
||||
* + The user owning the Sync account. If the account belongs to a
|
||||
* verified user that previosly used Sync, we are fine, if not, we
|
||||
* show a warning asking the user to go to other Firefox client to
|
||||
* create a FxA and login into Sync.
|
||||
* + A button to disable sync. Clicking it will log the user out from
|
||||
* FxA and will clear the synchronized data.
|
||||
* + A button to start a sync on demand. Clicking it will disable the
|
||||
* button and the collections switches while the synchronization is
|
||||
* being performed. Once the sync is done successfully, we enable
|
||||
* all the options back and refresh the last sync time.
|
||||
* We disable this button if no collection is selected.
|
||||
* + Collections switches. Allow the user to select which collections
|
||||
* she wants to keep synchronized. If no collection is selected, the
|
||||
* synchronization will be disabled.
|
||||
* + ToS and Privacy links.
|
||||
*/
|
||||
|
||||
onsyncchange(message) {
|
||||
debug(JSON.stringify(message));
|
||||
if (!message || !message.state) {
|
||||
throw new Error('Missing sync state');
|
||||
}
|
||||
switch (message.state) {
|
||||
case 'disabled':
|
||||
// We want to show a dialog once Sync is disabled
|
||||
// but we only want to do that if it's disabled via user action
|
||||
// and the user has already logged in.
|
||||
// (and not because it is already disabled from a previous run).
|
||||
if (this.state === 'disabling' &&
|
||||
(this.previousState === 'enabled' ||
|
||||
this.previousState === 'syncing')) {
|
||||
navigator.mozL10n.formatValue('fxsync-disabled').then(result => {
|
||||
window.alert(result);
|
||||
});
|
||||
}
|
||||
this.showScreen(DISABLED);
|
||||
this.hideEnabling();
|
||||
break;
|
||||
case 'enabling':
|
||||
this.showEnabling();
|
||||
break;
|
||||
case 'enabled':
|
||||
// We want to show a dialog once Sync is enabled
|
||||
// but we only want to do that if its enabled via user action
|
||||
// (and not because it is already enabled from a previous run).
|
||||
if (this.state === 'enabling') {
|
||||
navigator.mozL10n.formatValue('fxsync-enabled').then(result => {
|
||||
window.alert(result);
|
||||
});
|
||||
}
|
||||
this.hideEnabling();
|
||||
this.showScreen(ENABLED);
|
||||
this.showUser(message.user);
|
||||
this.showSyncNow();
|
||||
break;
|
||||
case 'syncing':
|
||||
this.showScreen(ENABLED);
|
||||
// In case that the user opens the browser app while we are on a
|
||||
// syncing state the previous 'enabled' state is missed and so
|
||||
// we need to make sure that we show the user here as well.
|
||||
this.showUser(message.user);
|
||||
this.showSyncing();
|
||||
break;
|
||||
case 'errored':
|
||||
LazyLoader.load('shared/js/sync/errors.js', () => {
|
||||
const IGNORED_ERRORS = [
|
||||
ERROR_DIALOG_CLOSED_BY_USER,
|
||||
ERROR_SYNC_APP_KILLED
|
||||
];
|
||||
|
||||
if (IGNORED_ERRORS.indexOf(message.error) > -1) {
|
||||
Settings.hide.bind(Settings)();
|
||||
return;
|
||||
}
|
||||
|
||||
var errorIds = [ERROR_UNKNOWN, ERROR_UNKNOWN + '-explanation'];
|
||||
|
||||
const KNOWN_ERRORS = [
|
||||
ERROR_INVALID_SYNC_ACCOUNT,
|
||||
ERROR_OFFLINE
|
||||
];
|
||||
|
||||
if (KNOWN_ERRORS.indexOf(message.error) > -1) {
|
||||
errorIds[0] = message.error;
|
||||
errorIds[1] = message.error + '-explanation';
|
||||
}
|
||||
|
||||
var l10n = navigator.mozL10n;
|
||||
Promise.all(
|
||||
errorIds.map(l10n.formatValue.bind(l10n))
|
||||
).then(result => {
|
||||
window.alert(result[0] + '\n' + result[1], () => {
|
||||
this.showScreen(DISABLED);
|
||||
});
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
this.previousState = this.state;
|
||||
this.state = message.state;
|
||||
},
|
||||
|
||||
onbookmarkschange() {
|
||||
debug('onbookmarkschange');
|
||||
var needsCleanup;
|
||||
if (!this.elements.collectionBookmarks) {
|
||||
// We may observe a setting change once we already disabled Sync
|
||||
// because the Sync Manager is reverting user preference changes to
|
||||
// the default ones. In that case, we need to create the object
|
||||
// briefly, so we can update the DOM accordingly.
|
||||
this.elements.collectionBookmarks = this.area.querySelector(
|
||||
'#fxsync-collection-bookmarks'
|
||||
);
|
||||
needsCleanup = true;
|
||||
}
|
||||
|
||||
this.elements.collectionBookmarks.checked =
|
||||
this.collections.has(BOOKMARKS_SETTING);
|
||||
|
||||
if (needsCleanup) {
|
||||
this.elements.collectionBookmarks = null;
|
||||
}
|
||||
},
|
||||
|
||||
onbookmarkschecked() {
|
||||
debug('onbookmarkschecked');
|
||||
this.oncollectionchecked(this.elements.collectionBookmarks,
|
||||
BOOKMARKS_SETTING);
|
||||
},
|
||||
|
||||
onhistorychange() {
|
||||
debug('onhistorychange');
|
||||
var needsCleanup;
|
||||
if (!this.elements.collectionHistory) {
|
||||
// We may observe a setting change once we already disabled Sync
|
||||
// because the Sync Manager is reverting user preference changes to
|
||||
// the default ones. In that case, we need to create the object
|
||||
// briefly, so we can update the DOM accordingly.
|
||||
this.elements.collectionHistory = this.area.querySelector(
|
||||
'#fxsync-collection-history'
|
||||
);
|
||||
needsCleanup = true;
|
||||
}
|
||||
|
||||
this.elements.collectionHistory.checked =
|
||||
this.collections.has(HISTORY_SETTING);
|
||||
|
||||
if (needsCleanup) {
|
||||
this.elements.collectionHistory = null;
|
||||
}
|
||||
},
|
||||
|
||||
onhistorychecked() {
|
||||
debug('onhistorychecked');
|
||||
this.oncollectionchecked(this.elements.collectionHistory,
|
||||
HISTORY_SETTING);
|
||||
},
|
||||
|
||||
oncollectionchecked(element, setting) {
|
||||
if (!element || !setting) {
|
||||
return;
|
||||
}
|
||||
|
||||
var checked = element.checked;
|
||||
var settingObj = {};
|
||||
settingObj[setting] = checked;
|
||||
if (!checked) {
|
||||
this.collections.delete(setting);
|
||||
navigator.mozSettings.createLock().set(settingObj);
|
||||
return;
|
||||
}
|
||||
|
||||
// Yeah, mozId sucks...
|
||||
// We cannot call watch twice to change the id lifetime
|
||||
// callbacks. But we still need to do different stuff
|
||||
// within the onlogin callback depending on who calls
|
||||
// oncollectionchecked. So we need to save state.
|
||||
// Thank you mozId.
|
||||
this.setting = setting;
|
||||
this.settingObj = settingObj;
|
||||
|
||||
try {
|
||||
navigator.mozId.watch({
|
||||
wantIssuer: 'firefox-accounts',
|
||||
onlogin: () => {
|
||||
// This callback is fired every time a new account logs in,
|
||||
// but we only want to enable the history setting when this
|
||||
// callback is called because the refresh auth flow succeeded.
|
||||
// This can happen only if Sync is already enabled.
|
||||
if (this.state !== 'enabled') {
|
||||
return;
|
||||
}
|
||||
this.collections.set(this.setting, true);
|
||||
navigator.mozSettings.createLock().set(this.settingObj);
|
||||
},
|
||||
onlogout: () => {},
|
||||
onready: () => {},
|
||||
onerror: error => {
|
||||
element.checked = false;
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
} catch(e) {}
|
||||
|
||||
navigator.mozId.request({
|
||||
oncancel: () => {
|
||||
element.checked = false;
|
||||
},
|
||||
// We keep authenticated sessions of 5 minutes, if the user clicks on
|
||||
// the history collection switch after the 5 minutes are expired, we
|
||||
// will be asking her to re-enter her fxa password.
|
||||
refreshAuthentication: 5 * 60
|
||||
});
|
||||
},
|
||||
|
||||
addListener(element, event, listenerName) {
|
||||
if (!element || !event || !listenerName) {
|
||||
return;
|
||||
}
|
||||
// Because we bind this to the listener, we need to save
|
||||
// the reference so we can remove it afterwards.
|
||||
var listener = this[listenerName].bind(this);
|
||||
element.addEventListener(event, listener);
|
||||
return listener;
|
||||
},
|
||||
|
||||
removeListener(element, event, listener) {
|
||||
if (!element || !event || !listener) {
|
||||
return;
|
||||
}
|
||||
element.removeEventListener(event, listener);
|
||||
},
|
||||
|
||||
loadElements(screen) {
|
||||
ELEMENTS.forEach(element => {
|
||||
var name = getElementName(element.selector);
|
||||
if (!Array.isArray(element.screen)) {
|
||||
element.screen = [element.screen];
|
||||
}
|
||||
if (!this.elements[name]) {
|
||||
this.elements[name] = this.area.querySelector(element.selector);
|
||||
this.listeners.set(
|
||||
name,
|
||||
this.addListener(this.elements[name],
|
||||
element.event, element.listener)
|
||||
);
|
||||
if (element.init) {
|
||||
this[element.init]();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.init) {
|
||||
this[element.init]();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.listeners.has(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.elements[name].checked) {
|
||||
this.elements[name].checked = false;
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
|
||||
showScreen(screen) {
|
||||
if (this.screen == screen) {
|
||||
return;
|
||||
}
|
||||
this.screen = screen;
|
||||
|
||||
this.loadElements(screen);
|
||||
|
||||
this.elements.enabled.hidden = (screen != ENABLED);
|
||||
this.elements.disabled.hidden = (screen != DISABLED);
|
||||
},
|
||||
|
||||
showUser(user) {
|
||||
navigator.mozL10n.setAttributes(this.elements.signedInAs,
|
||||
'fxsync-signed-in-as', {
|
||||
email: user
|
||||
});
|
||||
},
|
||||
|
||||
maybeEnableSyncNow() {
|
||||
if (!this.elements.syncNowButton) {
|
||||
return;
|
||||
}
|
||||
(this.collections.size <= 0) ?
|
||||
this.elements.syncNowButton.classList.add('disabled') :
|
||||
this.elements.syncNowButton.classList.remove('disabled');
|
||||
},
|
||||
|
||||
disableSyncNowAndCollections(disabled) {
|
||||
['collectionBookmarks',
|
||||
'collectionHistory'].forEach(name => {
|
||||
if (!this.elements[name]) {
|
||||
return;
|
||||
}
|
||||
this.elements[name].disabled = disabled;
|
||||
});
|
||||
disabled ? this.elements.syncNowButton.classList.add('disabled')
|
||||
: this.elements.syncNowButton.classList.remove('disabled');
|
||||
},
|
||||
|
||||
showEnabling() {
|
||||
this.elements.signInButton.classList.add('disabled');
|
||||
this.elements.signInButton.dataset.l10nId = 'fxsync-signing';
|
||||
},
|
||||
|
||||
hideEnabling() {
|
||||
this.elements.signInButton.classList.remove('disabled');
|
||||
this.elements.signInButton.dataset.l10nId = 'fxsync-sign-in';
|
||||
},
|
||||
|
||||
showSyncNow() {
|
||||
this.elements.syncNowButton.dataset.l10nId = 'fxsync-sync-now';
|
||||
this.disableSyncNowAndCollections(false);
|
||||
this.maybeEnableSyncNow();
|
||||
},
|
||||
|
||||
showSyncing() {
|
||||
this.elements.syncNowButton.dataset.l10nId = 'fxsync-syncing';
|
||||
this.disableSyncNowAndCollections(true);
|
||||
},
|
||||
|
||||
enable() {
|
||||
SyncManagerBridge.enable();
|
||||
},
|
||||
|
||||
disable() {
|
||||
BrowserDialog.createDialog('signout_confirm').then(() => {
|
||||
SyncManagerBridge.disable();
|
||||
});
|
||||
},
|
||||
|
||||
sync() {
|
||||
SyncManagerBridge.sync();
|
||||
},
|
||||
|
||||
openTos() {
|
||||
this.showWebpage('https://accounts.firefox.com/legal/terms');
|
||||
},
|
||||
|
||||
openPrivacy() {
|
||||
this.showWebpage('https://accounts.firefox.com/legal/privacy');
|
||||
},
|
||||
|
||||
showWebpage(url) {
|
||||
Awesomescreen.createAddNewTab();
|
||||
Browser.navigate(url);
|
||||
Settings.hide();
|
||||
}
|
||||
};
|
||||
|
||||
exports.FirefoxSyncSettings = FirefoxSyncSettings;
|
||||
|
||||
LazyLoader.load('js/sync/manager_bridge.js', () => {
|
||||
FirefoxSyncSettings.init();
|
||||
});
|
||||
}(window));
|
||||
113
gaia/tv_apps/browser/js/sync/toolbar.js
Executable file
113
gaia/tv_apps/browser/js/sync/toolbar.js
Executable file
|
|
@ -0,0 +1,113 @@
|
|||
/* 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/. */
|
||||
|
||||
/* exports FirefoxSyncToolbar */
|
||||
|
||||
/* global LazyLoader */
|
||||
/* global Settings */
|
||||
/* global SyncManagerBridge */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* This small module handles the logic of the Sync entry in the browser's
|
||||
* top right toolbar menu.
|
||||
*
|
||||
* We show two different variations of the UI depending on the state of Sync:
|
||||
*
|
||||
* - When Sync is disabled, we show a 'Sign in to Sync' entry. Clicking it
|
||||
* triggers the Firefox Accounts flow and signs the user into Sync if the
|
||||
* FxA flow is completed successfuly.
|
||||
* - When Sync is enabled, we show the 'Sign is as {{email}}' entry. Where
|
||||
* {{email}} is the email used by the user to login into FxA.
|
||||
*
|
||||
* We also handle a third Sync state. When an error is received through the
|
||||
* Sync Manager Bridge, we inform the user about them. The expected errors are:
|
||||
*
|
||||
* - ERROR_OFFLINE: The user is trying to log in Sync when the device is
|
||||
* offline.
|
||||
* - ERROR_INVALID_SYNC_ACCOUNT: The user is trying to log into Sync with a
|
||||
* Firefox Account that has never been used before with Sync. On the first
|
||||
* version we don't support the creation of new Sync accounts, so we ask the
|
||||
* user to first login in Firefox Desktop or Android and try login again
|
||||
* in the TV.
|
||||
* - ERROR_TRY_LATER: The server is probably down. We inform the user about it
|
||||
* and ask her to retry in a few minutes.
|
||||
* - ERROR_UNKNOWN: We don't know what the h* was wrong, but we can't log the
|
||||
* user in.
|
||||
*
|
||||
* Here we don't need to worry about keeping any kind of state or managing
|
||||
* any state transistion apart from the UI changes. The Sync State Machine
|
||||
* and Sync State Manager takes care of handling all that for us.
|
||||
*/
|
||||
|
||||
(function(exports) {
|
||||
const DEBUG = true;
|
||||
function debug() {
|
||||
if (DEBUG) {
|
||||
console.log('[FirefoxSyncToolbar] ' + Array.slice(arguments).concat());
|
||||
}
|
||||
}
|
||||
|
||||
var FirefoxSyncToolbar = {
|
||||
init() {
|
||||
SyncManagerBridge.addListener(this.onsyncchange.bind(this));
|
||||
|
||||
this.syncBlock = document.getElementById('fxsync-block');
|
||||
this.syncTab = this.syncBlock.querySelector('#fxsync-tab');
|
||||
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (!document.hidden) {
|
||||
this.refresh();
|
||||
}
|
||||
});
|
||||
|
||||
this.refresh();
|
||||
},
|
||||
|
||||
refresh() {
|
||||
SyncManagerBridge.getInfo().then(this.onsyncchange.bind(this));
|
||||
},
|
||||
|
||||
onsyncchange(message) {
|
||||
debug(JSON.stringify(message));
|
||||
if (!message || !message.state) {
|
||||
throw new Error('Missing sync state');
|
||||
}
|
||||
switch (message.state) {
|
||||
case 'disabled':
|
||||
if (this.showListener) {
|
||||
this.syncBlock.removeEventListener('mouseup', this.showListener);
|
||||
this.showListener = null;
|
||||
}
|
||||
this.syncBlock.addEventListener('mouseup', SyncManagerBridge.enable);
|
||||
this.syncTab.setAttribute('data-l10n-id', 'fxsync-sign-in-to-sync');
|
||||
this.syncTab.removeAttribute('data-l10n-args');
|
||||
break;
|
||||
case 'enabling':
|
||||
Settings.show();
|
||||
this.syncTab.setAttribute('data-l10n-id', 'fxsync-signing');
|
||||
break;
|
||||
case 'syncing':
|
||||
case 'enabled':
|
||||
this.showListener = Settings.show.bind(Settings);
|
||||
this.syncBlock.addEventListener('mouseup', this.showListener);
|
||||
this.syncBlock.removeEventListener('mouseup',
|
||||
SyncManagerBridge.enable);
|
||||
navigator.mozL10n.setAttributes(this.syncTab, 'fxsync-signed-in-as', {
|
||||
email: message.user
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Exported only for testing purposes
|
||||
exports.FirefoxSyncToolbar = FirefoxSyncToolbar;
|
||||
|
||||
LazyLoader.load('js/sync/manager_bridge.js', () => {
|
||||
FirefoxSyncToolbar.init();
|
||||
});
|
||||
|
||||
}(window));
|
||||
758
gaia/tv_apps/browser/js/toolbar.js
Executable file
758
gaia/tv_apps/browser/js/toolbar.js
Executable file
|
|
@ -0,0 +1,758 @@
|
|||
/* global Awesomescreen */
|
||||
/* global Browser */
|
||||
/* global BrowserDB */
|
||||
/* global LazyLoader */
|
||||
/* global SearchResult */
|
||||
/* global SearchUtil */
|
||||
/* global Settings */
|
||||
/* global Tooltip*/
|
||||
/* global UrlHelper */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Browser app toolbar
|
||||
* @namespace Toolbar
|
||||
*/
|
||||
var Toolbar = {
|
||||
|
||||
/* screen size */
|
||||
SCREEN_HEIGHT: 1080,
|
||||
SCREEN_WIDTH: 1920,
|
||||
|
||||
/* zoom */
|
||||
availableZoomScale: [
|
||||
{'scale': 1.25, 'disp': '100%'},
|
||||
{'scale': 1.625, 'disp': '130%'},
|
||||
{'scale': 2.0, 'disp': '160%'},
|
||||
],
|
||||
defaultZoomScale: 0,
|
||||
zoomScale: 0,
|
||||
availableZoomScaleYoutube: [
|
||||
{'scale': 1.55, 'disp': '100%'},
|
||||
{'scale': 1.775, 'disp': '130%'},
|
||||
{'scale': 2.0, 'disp': '160%'},
|
||||
],
|
||||
|
||||
urlButtonMode: null,
|
||||
GO: 0,
|
||||
REFRESH: 1,
|
||||
STOP: 2,
|
||||
SEARCH: 3,
|
||||
|
||||
initStart: true,
|
||||
|
||||
/**
|
||||
* Intialise toolbar.
|
||||
*/
|
||||
init: function toolbar_init() {
|
||||
/* get Element */
|
||||
this.getAllElements();
|
||||
|
||||
/* Event Listener */
|
||||
window.addEventListener('click', this);
|
||||
|
||||
this.sidebarButtonBlock.addEventListener('mouseup',
|
||||
this.showHideSidebar.bind(this));
|
||||
|
||||
this.backButtonBlock.addEventListener('mouseup',
|
||||
this.goBack.bind(this));
|
||||
|
||||
this.forwardButtonBlock.addEventListener('mouseup',
|
||||
this.goForward.bind(this));
|
||||
|
||||
this.addressForm.addEventListener('submit',
|
||||
this.handleUrlFormSubmit.bind(this));
|
||||
|
||||
this.urlInput.addEventListener('input',
|
||||
this.handleUrlInputKeypress.bind(this));
|
||||
|
||||
this.addressButton.addEventListener('mouseup',
|
||||
this.clickAddressButton.bind(this));
|
||||
|
||||
this.searchForm.addEventListener('submit',
|
||||
this.handleSearchFormSubmit.bind(this));
|
||||
|
||||
this.searchInput.addEventListener('focus',
|
||||
this.handleSearchInputFocus.bind(this));
|
||||
|
||||
// this.searchInput.addEventListener('keyup',
|
||||
// this.handleSearchInputKeypress.bind(this));
|
||||
|
||||
this.searchInput.addEventListener('input',
|
||||
this.handleSearchInputInput.bind(this));
|
||||
|
||||
this.searchButton.addEventListener('mouseup',
|
||||
this.clickSearchButton.bind(this));
|
||||
|
||||
this.searchClearButton.addEventListener('mouseup',
|
||||
this.clickSearchClearButton.bind(this));
|
||||
|
||||
this.searchCloseButton.addEventListener('mouseup',
|
||||
this.clickSearchCloseButton.bind(this));
|
||||
|
||||
this.bookmarkButtonBlock.addEventListener('mouseup',
|
||||
Browser.addBookmark.bind(Browser));
|
||||
this.bookmarkButtonAnime.addEventListener('animationend',
|
||||
Browser.bookmarkButtonAnimeEnd.bind(Browser));
|
||||
|
||||
this.showBookmarksButtonBlock.addEventListener('mouseup',
|
||||
Awesomescreen.selectBookmarksTab.bind(Awesomescreen));
|
||||
|
||||
this.homeButtonBlock.addEventListener('mouseup',
|
||||
this.clickHomeButtonBlock.bind(this));
|
||||
|
||||
this.zoomButtonBlock.addEventListener('mouseup',
|
||||
this.clickZoomButtonBlock.bind(this));
|
||||
|
||||
this.tabsButtonBlock.addEventListener('click',
|
||||
Awesomescreen.handleNewTab.bind(Awesomescreen));
|
||||
|
||||
this.newTabButtonBlock.addEventListener('click',
|
||||
Awesomescreen.createAddNewTab.bind(Awesomescreen));
|
||||
|
||||
this.menuButtonBlock.addEventListener('mouseup',
|
||||
this.clickMenuButtonBlock.bind(this));
|
||||
this.historyBlock.addEventListener('mouseup',
|
||||
Awesomescreen.selectHistoryTab.bind(Awesomescreen));
|
||||
this.pocketBlock.addEventListener('mouseup',
|
||||
this.clickPocketListBlock.bind(this));
|
||||
this.privateWindowBlock.addEventListener('mouseup',
|
||||
Browser.handlePrivateBrowsing.bind(Browser));
|
||||
this.settingsBlock.addEventListener('mouseup',
|
||||
Settings.show.bind(Settings));
|
||||
|
||||
this.modeButtonBlock.addEventListener('mouseover',
|
||||
this.selectedModeButtonBlock.bind(this));
|
||||
this.modeButtonBlock.addEventListener('mouseout',
|
||||
this.unselectedModeButtonBlock.bind(this));
|
||||
|
||||
this.toolbarPanel.dataset.search = 'false';
|
||||
|
||||
var colorBar = Browser.getColorBarData();
|
||||
this.panCursorButtonBlock.dataset.colorbar = colorBar[0].COLOR;
|
||||
this.zoomButtonBlock.dataset.colorbar = colorBar[1].COLOR;
|
||||
this.tabsButtonBlock.dataset.colorbar = colorBar[2].COLOR;
|
||||
this.newTabButtonBlock.dataset.colorbar = colorBar[3].COLOR;
|
||||
|
||||
// li = 73px * 3(list) + 20px(hover) + 10px(padding)
|
||||
this.menuBlock.style.height = 73 * 3 + 20 + 10 + 'px';
|
||||
Tooltip.init();
|
||||
|
||||
//IFDEF_FIREFOX_SYNC
|
||||
LazyLoader.load('js/sync/toolbar.js');
|
||||
//ENDIF_FIREFOX_SYNC
|
||||
},
|
||||
|
||||
/**
|
||||
* toolbar bookmark registered check.
|
||||
*/
|
||||
isBookmarks: function toolbar_isBookmarks() {
|
||||
if(Toolbar.bookmarkButton.dataset.isbookmark == 'true'){
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Set search engine
|
||||
*/
|
||||
setSearchEngine: function toolbar_setSearchEngine() {
|
||||
var engineName = SearchUtil.getCurrentEngineName().toLowerCase();
|
||||
// favicon
|
||||
this.searchFavicon.dataset.engine = engineName;
|
||||
// placeholder
|
||||
engineName = engineName.charAt(0).toUpperCase() + engineName.slice(1);
|
||||
this.searchInput.placeholder = engineName;
|
||||
// tooltip
|
||||
navigator.mozL10n.formatValue('WB_LT_TIPS_SEARCH_BAR2', {
|
||||
engine: engineName
|
||||
}).then(val => {
|
||||
this.searchInput.dataset.tips = val;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* CamelCase (xxx-yyy -> xxxYyy)
|
||||
*/
|
||||
toCamelCase: function toCamelCase(str) {
|
||||
return str.replace(/-(.)/g, function replacer(str, p1) {
|
||||
return p1.toUpperCase();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get All Elements (from id)
|
||||
*/
|
||||
getAllElements: function toolbar_getAllElements() {
|
||||
var elementIDs = [
|
||||
'toolbar-panel',
|
||||
'sidebar-button-block', 'sidebar-button',
|
||||
'back-button-block', 'back-button',
|
||||
'forward-button-block', 'forward-button-border', 'forward-button',
|
||||
'space-block',
|
||||
'addressbar-block', 'address-form', 'ssl-indicator', 'url-input',
|
||||
'address-button',
|
||||
'searchbar-block', 'search-form', 'search-favicon', 'search-input',
|
||||
'search-button', 'search-clear-button', 'search-close-button',
|
||||
'iconbutton-block',
|
||||
'bookmark-button-block', 'bookmark-button',
|
||||
'show-bookmarks-button-block', 'show-bookmarks-button',
|
||||
'home-button-block', 'home-button',
|
||||
'zoom-button-block', 'zoom-button',
|
||||
'tabs-button-block', 'tabs-button',
|
||||
'new-tab-button-block', 'new-tab-button',
|
||||
'menu-button-block', 'menu-button',
|
||||
'menu-block',
|
||||
'history-block', 'private-window-block',
|
||||
'settings-block',
|
||||
'history-tab', 'private-window-tab', 'settings-tab',
|
||||
'mode-button-block',
|
||||
'mode-button-title', 'pan-cursor-button', 'pan-cursor-button-block',
|
||||
'pan-cursor-banner-message',
|
||||
'zoom-banner-message-block', 'zoom-banner-message',
|
||||
'loading-icon',
|
||||
//'tooltip-block',
|
||||
'bookmark-button-anime', 'show-bookmarks-button-anime',
|
||||
'pocket-block', 'pocket-tab'
|
||||
];
|
||||
|
||||
// Loop and add element with camel style name to Modal Dialog attribute.
|
||||
elementIDs.forEach(function createElementRef(name) {
|
||||
this[this.toCamelCase(name)] = document.getElementById(name);
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* handle event
|
||||
*/
|
||||
handleEvent: function toolbar_handleEvent(ev) {
|
||||
switch(ev.type) {
|
||||
case 'click':
|
||||
if(ev.target != this.menuButtonBlock) {
|
||||
this.toolbarPanel.dataset.menu = 'hide';
|
||||
}
|
||||
|
||||
// Settings in the display
|
||||
if(Settings.isDisplayed()) {
|
||||
switch(ev.target) {
|
||||
case this.backButtonBlock:
|
||||
case this.forwardButtonBlock:
|
||||
case this.addressForm:
|
||||
case this.urlInput:
|
||||
case this.addressButton:
|
||||
case this.searchForm:
|
||||
case this.searchInput:
|
||||
case this.searchButton:
|
||||
case this.bookmarkButtonBlock:
|
||||
case this.showBookmarksButtonBlock:
|
||||
case this.homeButtonBlock:
|
||||
case this.zoomButtonBlock:
|
||||
case this.tabsButtonBlock:
|
||||
case this.newTabButtonBlock:
|
||||
case this.menuButtonBlock:
|
||||
Settings.hide();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Specialized processing youtube (zoom)
|
||||
*/
|
||||
checkYoutube: function toolbar_checkYoutube() {
|
||||
if(!Browser.currentInfo || !Browser.currentInfo.url) {
|
||||
return false;
|
||||
}
|
||||
var end_pos1 =
|
||||
Browser.currentInfo.url.indexOf('https://www.youtube.com', 0);
|
||||
var end_pos2 = Browser.currentInfo.url.indexOf('http://www.youtube.com', 0);
|
||||
if(end_pos1 < 0 && end_pos2 < 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Full screen or Screen with submenu
|
||||
*/
|
||||
showHideSidebar: function toolbar_showHideSidebar() {
|
||||
this.sidebarButtonBlock.blur();
|
||||
// Hide Tooltip
|
||||
Tooltip.hide();
|
||||
|
||||
// Fade animation end event handler
|
||||
var fade_event = (function() {
|
||||
Browser.fadeBase.classList.toggle('fade');
|
||||
if(Browser.sideBlock.dataset.sidebar == 'true'){
|
||||
// Full screen
|
||||
Browser.sideBlock.dataset.sidebar = 'false';
|
||||
Browser.mainBlock.dataset.sidebar = 'false';
|
||||
// save screen mode
|
||||
BrowserDB.updateSetting('full', 'screen_mode');
|
||||
}else{
|
||||
// Disp side screen
|
||||
Browser.sideBlock.dataset.sidebar = 'true';
|
||||
Browser.mainBlock.dataset.sidebar = 'true';
|
||||
// save screen mode
|
||||
BrowserDB.updateSetting('side', 'screen_mode');
|
||||
}
|
||||
Browser.fadeBase.removeEventListener('transitionend', fade_event, false);
|
||||
// Specialized processing youtube (zoom)
|
||||
Browser.currentInfo.dom.zoom(Toolbar.getZoomScale());
|
||||
});
|
||||
// Add fade animantion event
|
||||
Browser.fadeBase.addEventListener('transitionend', fade_event, false);
|
||||
Browser.fadeBase.classList.toggle('fade');
|
||||
this.sidebarButtonBlock.dataset.fade='true';
|
||||
},
|
||||
|
||||
/**
|
||||
* Go back to the previous page
|
||||
*/
|
||||
goBack: function toolbar_goBack() {
|
||||
|
||||
if (Awesomescreen.isDisplayedTop()){
|
||||
Awesomescreen.topsiteReturnFunc();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Browser.currentInfo.dom.getCanGoBack){
|
||||
return;
|
||||
}
|
||||
|
||||
Browser.currentInfo.dom.getCanGoBack().onsuccess = (function(ev) {
|
||||
if(!ev.target.result){
|
||||
// dialog
|
||||
// XXX: refactor this and the same code in awesomescreen.js
|
||||
navigator.mozL10n.formatValue('LT_BROWSER_CONFIRM_EXIT2')
|
||||
.then(result => {
|
||||
if (window.confirm(result)) {
|
||||
window.close();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
}).bind(this);
|
||||
|
||||
Browser.currentInfo.dom.goBack();
|
||||
},
|
||||
|
||||
/**
|
||||
* Go forward to the next page
|
||||
*/
|
||||
goForward: function toolbar_goForward() {
|
||||
Browser.currentInfo.dom.goForward();
|
||||
},
|
||||
|
||||
// Set url form input
|
||||
setUrlBar: function toolbar_setUrlBar(value) {
|
||||
this.urlInput.value = value;
|
||||
},
|
||||
// Get url form input
|
||||
getUrlFromInput: function toolbar_getUrlFromInput(input) {
|
||||
var hasScheme = UrlHelper.hasScheme(input);
|
||||
|
||||
// No scheme, prepend basic protocol and return
|
||||
if (!hasScheme) {
|
||||
return 'http://' + input;
|
||||
}
|
||||
|
||||
return input;
|
||||
},
|
||||
/**
|
||||
* Url form submit
|
||||
*/
|
||||
handleUrlFormSubmit: function toolbar_handleUrlFormSubmit(ev) {
|
||||
if (ev) {
|
||||
// Can be canceled
|
||||
ev.preventDefault();
|
||||
}
|
||||
|
||||
if (this.urlButtonMode === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var url = this.getUrlFromInput(this.urlInput.value);
|
||||
|
||||
if (url !== Browser.currentInfo.url) {
|
||||
this.setUrlBar(url);
|
||||
Browser.currentInfo.url = url;
|
||||
}
|
||||
|
||||
this.urlInput.blur();
|
||||
|
||||
Browser.navigate(url);
|
||||
},
|
||||
|
||||
/**
|
||||
* Input Url
|
||||
*/
|
||||
handleUrlInputKeypress: function toolbar_handleUrlInputKeypress(evt) {
|
||||
var input = this.urlInput.value;
|
||||
if (input === '') {
|
||||
this.setUrlButtonMode(null);
|
||||
this.addressButton.classList.add('disable');
|
||||
return;
|
||||
}
|
||||
|
||||
this.addressButton.classList.remove('disable');
|
||||
this.setUrlButtonMode(
|
||||
UrlHelper.isNotURL(input) ? this.SEARCH : this.GO
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Address Bookmark Current url
|
||||
*/
|
||||
clickAddressButton: function toolbar_clickAddressButton(ev) {
|
||||
//if(this.addressButton.classList.contains('disable')){
|
||||
//return;
|
||||
//}
|
||||
if(this.addressButton.dataset.mode == 'load') {
|
||||
if(Browser.currentInfo.url){
|
||||
Browser.currentInfo.dom.reload(false);
|
||||
}
|
||||
this.stopAddressIcon();
|
||||
} else {
|
||||
Browser.currentInfo.dom.stop();
|
||||
this.hiddenLoadingIcon();
|
||||
this.loadAddressIcon();
|
||||
}
|
||||
},
|
||||
|
||||
loadAddressIcon: function toolbar_loadAddressIcon() {
|
||||
this.addressButton.dataset.mode = 'load';
|
||||
this.addressButton.dataset.tips = 'WB_LT_TIPS_RELOAD';
|
||||
},
|
||||
|
||||
stopAddressIcon: function toolbar_stopAddressIcon() {
|
||||
this.addressButton.dataset.mode = 'stop';
|
||||
this.addressButton.dataset.tips = 'WB_LT_TIPS_STOP_LOADING';
|
||||
},
|
||||
|
||||
showLoadingIcon: function toolbar_showLoadingIcon() {
|
||||
if(Settings.isDisplayed()) {
|
||||
return;
|
||||
}
|
||||
if(!this.loadingIcon.classList.contains('visible')){
|
||||
this.loadingIcon.classList.add('visible');
|
||||
this.stopAddressIcon();
|
||||
}
|
||||
},
|
||||
|
||||
hiddenLoadingIcon: function toolbar_hiddenLoadingIcon() {
|
||||
if(Tooltip.isShow(this.addressButton)){
|
||||
Tooltip.hide();
|
||||
}
|
||||
this.loadingIcon.classList.remove('visible');
|
||||
this.loadAddressIcon();
|
||||
},
|
||||
|
||||
/**
|
||||
* Submit search form
|
||||
*/
|
||||
handleSearchFormSubmit: function toolbar_handleSearchFormSubmit(ev) {
|
||||
if (ev) {
|
||||
// Can be canceled
|
||||
ev.preventDefault();
|
||||
}
|
||||
|
||||
if( SearchResult.isDisplayed() ) {
|
||||
this.searchInput.blur();
|
||||
SearchResult.hide();
|
||||
}
|
||||
var url = Browser.getSearchFromInput(this.searchInput.value);
|
||||
Browser.navigate(url);
|
||||
},
|
||||
|
||||
/**
|
||||
* Click search button
|
||||
*/
|
||||
clickSearchButton: function toolbar_clickSearchButton(ev) {
|
||||
this.handleSearchFormSubmit(ev);
|
||||
},
|
||||
|
||||
clickSearchClearButton: function toolbar_clickSearchClearButton(ev) {
|
||||
if( ev ) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
this.searchInput.value = '';
|
||||
SearchResult.resultUpdate(this.searchInput.value);
|
||||
},
|
||||
|
||||
clickSearchCloseButton: function toolbar_clickSearchCloseButton(ev) {
|
||||
if( ev ) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
SearchResult.hide();
|
||||
},
|
||||
|
||||
/**
|
||||
* Input Search
|
||||
*/
|
||||
getSearchBarStyle: function toolbar_getSearchBarStyle() {
|
||||
return this.toolbarPanel.dataset.search;
|
||||
},
|
||||
|
||||
setSearchBarStyle: function toolbar_setSearchBarStyle(value) {
|
||||
this.toolbarPanel.dataset.search = value;
|
||||
},
|
||||
|
||||
handleSearchInputFocus: function toolbar_handleSearchInputFocus(evt) {
|
||||
this.handleSearchInputKeypress(evt);
|
||||
},
|
||||
|
||||
handleSearchInputInput: function toolbar_handleSearchInputInput(evt) {
|
||||
this.handleSearchInputKeypress(evt);
|
||||
},
|
||||
|
||||
handleSearchInputKeypress: function toolbar_handleSearchInputKeypress(evt) {
|
||||
if( this.getSearchBarStyle() == 'false' ) {
|
||||
if( evt ) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
if(Toolbar.searchInput.value) {
|
||||
Tooltip.hide();
|
||||
this.setSearchBarStyle('true');
|
||||
SearchResult.show();
|
||||
}
|
||||
} else {
|
||||
SearchResult.handleSearchResultFormInput(evt);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Click home button
|
||||
*/
|
||||
clickHomeButtonBlock: function toolbar_clickHomeButtonBlock(ev) {
|
||||
Settings.getHomepage((function(result) {
|
||||
if(result) {
|
||||
Browser.navigate(result);
|
||||
}
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Click zoom button
|
||||
*/
|
||||
clickZoomButtonBlock: function toolbar_clickZoomButtonBlock(ev) {
|
||||
if(ev){
|
||||
// stop event
|
||||
ev.stopPropagation();
|
||||
}
|
||||
clearTimeout(this.showZoomBannerTimeoutID);
|
||||
|
||||
this.zoomScale = Browser.currentInfo.zoom;
|
||||
this.zoomScale++;
|
||||
if(this.zoomScale >= this.availableZoomScale.length) {
|
||||
this.zoomScale = 0;
|
||||
}
|
||||
Browser.currentInfo.zoom = this.zoomScale;
|
||||
Browser.currentInfo.dom.zoom(this.getZoomScale());
|
||||
|
||||
this.zoomBannerMessage.innerHTML =
|
||||
this.availableZoomScale[this.zoomScale].disp;
|
||||
if(!this.zoomBannerMessageBlock.classList.contains('visible')){
|
||||
this.zoomBannerMessageBlock.classList.add('visible');
|
||||
}
|
||||
this.showZoomBannerTimeoutID = setTimeout(function(){
|
||||
Toolbar.zoomBannerMessageBlock.classList.remove('visible');
|
||||
}, 1500);
|
||||
},
|
||||
getZoomScale: function toolbar_getZoomScale() {
|
||||
if(this.checkYoutube() && Browser.sideBlock.dataset.sidebar == 'false'){
|
||||
return this.availableZoomScaleYoutube[this.zoomScale].scale;
|
||||
} else {
|
||||
return this.availableZoomScale[this.zoomScale].scale;
|
||||
}
|
||||
},
|
||||
setZoomScale: function toolbar_setZoomScale(scale) {
|
||||
this.zoomScale = scale;
|
||||
},
|
||||
getDefaultZoomScale: function toolbar_getDefaultZoomScale() {
|
||||
if(this.checkYoutube() && Browser.sideBlock.dataset.sidebar == 'false'){
|
||||
return this.availableZoomScaleYoutube[this.defaultZoomScale].scale;
|
||||
} else {
|
||||
return this.availableZoomScale[this.defaultZoomScale].scale;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Zoom out(for ars)
|
||||
*/
|
||||
zoomOut: function toolbar_zoomOut() {
|
||||
clearTimeout(this.showZoomBannerTimeoutID);
|
||||
|
||||
this.zoomScale = Browser.currentInfo.zoom;
|
||||
if(this.zoomScale === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.zoomScale--;
|
||||
Browser.currentInfo.zoom = this.zoomScale;
|
||||
Browser.currentInfo.dom.zoom(this.getZoomScale());
|
||||
|
||||
this.zoomBannerMessage.innerHTML =
|
||||
this.availableZoomScale[this.zoomScale].disp;
|
||||
if(!this.zoomBannerMessageBlock.classList.contains('visible')){
|
||||
this.zoomBannerMessageBlock.classList.add('visible');
|
||||
}
|
||||
this.showZoomBannerTimeoutID = setTimeout(function(){
|
||||
Toolbar.zoomBannerMessageBlock.classList.remove('visible');
|
||||
}, 1500);
|
||||
},
|
||||
|
||||
/**
|
||||
* Click menu button
|
||||
*/
|
||||
clickMenuButtonBlock: function toolbar_clickMenuButtonBlock(ev) {
|
||||
if(ev){
|
||||
// stop event
|
||||
ev.stopPropagation();
|
||||
}
|
||||
if(this.toolbarPanel.dataset.menu == 'show') {
|
||||
this.toolbarPanel.dataset.menu = 'hide';
|
||||
} else {
|
||||
this.toolbarPanel.dataset.menu = 'show';
|
||||
if(!this.menuBlock.style.minWidth) {
|
||||
var ar = [];
|
||||
// rate 1.25
|
||||
ar.push(432); // default 432(540 / 1.25)
|
||||
ar.push(this.historyTab.offsetWidth);
|
||||
ar.push(this.privateWindowTab.offsetWidth);
|
||||
|
||||
ar.push(this.settingsTab.offsetWidth);
|
||||
this.menuBlock.style.minWidth =
|
||||
(Math.max.apply(null, ar) * 1.25) + 'px';
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Show mode button
|
||||
*/
|
||||
showModeButtonTimer: function toolbar_showModeButtonTimer() {
|
||||
this.showModeButtonTimeoutID = setTimeout(function(){
|
||||
Toolbar.modeButtonBlock.classList.remove('visible');
|
||||
}, 5000);
|
||||
},
|
||||
|
||||
/**
|
||||
* Show mode button
|
||||
*/
|
||||
showPanCursorButton: function toolbar_showPanCursorButton() {
|
||||
if(!this.modeButtonBlock.classList.contains('visible')){
|
||||
this.modeButtonBlock.classList.add('visible');
|
||||
this.showModeButtonTimer();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Selected mode button
|
||||
*/
|
||||
selectedModeButtonBlock: function toolbar_selectedModeButtonBlock() {
|
||||
clearTimeout(this.showModeButtonTimeoutID);
|
||||
},
|
||||
|
||||
/**
|
||||
* Unselected mode button
|
||||
*/
|
||||
unselectedModeButtonBlock: function toolbar_unselectedModeButtonBlock() {
|
||||
this.showModeButtonTimer();
|
||||
},
|
||||
|
||||
/**
|
||||
* Show pan cursor banner
|
||||
*/
|
||||
showPanCursorBanner: function toolbar_showPanCursorBanner() {
|
||||
if(!this.panCursorBannerMessage.classList.contains('visible')){
|
||||
this.panCursorBannerMessage.classList.add('visible');
|
||||
}
|
||||
clearTimeout(this.showPanCursorBannerTimeoutID);
|
||||
this.showPanCursorBannerTimeoutID = setTimeout(function(){
|
||||
Toolbar.panCursorBannerMessage.classList.remove('visible');
|
||||
}, 5000);
|
||||
},
|
||||
|
||||
/**
|
||||
* Click mode button
|
||||
*/
|
||||
clickModeButtonBlock: function toolbar_clickModeButtonBlock() {
|
||||
if(Browser.getCursorPanMode() == 'cursor') {
|
||||
Browser.setCursorPanMode('pan');
|
||||
this.modeButtonTitle.setAttribute('data-l10n-id', 'WB_LT_PAN_MODE');
|
||||
this.panCursorBannerMessage.setAttribute(
|
||||
'data-l10n-id', 'WB_LT_SWITCH_PAN_MODE');
|
||||
} else {
|
||||
this.clearDragMode();
|
||||
}
|
||||
|
||||
// Show mode button
|
||||
this.showPanCursorButton();
|
||||
// Show pan cursor banner
|
||||
this.showPanCursorBanner();
|
||||
},
|
||||
// clear drag mode
|
||||
clearDragMode: function toolbar_clearDragMode(){
|
||||
if(Browser.getCursorPanMode() == 'pan') {
|
||||
Browser.setCursorPanMode('cursor');
|
||||
this.modeButtonTitle.setAttribute('data-l10n-id', 'WB_LT_CURSOR_MODE');
|
||||
this.panCursorBannerMessage.setAttribute(
|
||||
'data-l10n-id', 'WB_LT_SWITCH_CURSOR_MODE');
|
||||
}
|
||||
},
|
||||
|
||||
setUrlButtonMode: function toolbar_setUrlButtonMode(mode) {
|
||||
this.urlButtonMode = mode;
|
||||
},
|
||||
|
||||
setPrivateBrowsing: function toolbar_setPrivateBrowsing(bool) {
|
||||
this.toolbarPanel.dataset.pvt = bool;
|
||||
},
|
||||
|
||||
isPrivateBrowsing: function toolbar_getPrivateBrowsing() {
|
||||
return (this.toolbarPanel.dataset.pvt == 'true');
|
||||
},
|
||||
|
||||
initStartBrowsing: function toolbar_initStartBrowsing() {
|
||||
if(this.initStart) {
|
||||
this.initStart = false;
|
||||
/* show pan cursor mode button */
|
||||
this.showPanCursorButton();
|
||||
}
|
||||
},
|
||||
|
||||
setToolbarMode: function toolbar_setToolbarMode(mode) {
|
||||
this.toolbarPanel.dataset.mode = mode;
|
||||
},
|
||||
getToolbarMode: function toolbar_getToolbarMode() {
|
||||
return this.toolbarPanel.dataset.mode;
|
||||
},
|
||||
|
||||
/**
|
||||
* Open Pocket List
|
||||
*/
|
||||
clickPocketListBlock: function toolbar_clickPocketListBlock() {
|
||||
var pocketHomePageUri = 'https://getpocket.com/fxostv';
|
||||
if (Browser.currentInfo.url &&
|
||||
UrlHelper.isURL(Browser.currentInfo.url)) {
|
||||
var ev = {
|
||||
detail: {
|
||||
url: pocketHomePageUri,
|
||||
frameElement: null
|
||||
}
|
||||
};
|
||||
Awesomescreen.openNewTab(ev);
|
||||
} else {
|
||||
Browser.navigate(pocketHomePageUri);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
190
gaia/tv_apps/browser/js/tooltip.js
Executable file
190
gaia/tv_apps/browser/js/tooltip.js
Executable file
|
|
@ -0,0 +1,190 @@
|
|||
/* global _ */
|
||||
/* global Toolbar */
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Browser App Tooltip.
|
||||
* @namespace Tooltip
|
||||
*/
|
||||
var Tooltip = {
|
||||
tooltipBlock: null,
|
||||
showTimeoutID: null,
|
||||
hideTimeoutID: null,
|
||||
hideTimeoutData: 100,
|
||||
overObj: null,
|
||||
moveObj: null,
|
||||
showTarget: null,
|
||||
|
||||
/**
|
||||
* initialize
|
||||
*/
|
||||
init: function tooltip_init() {
|
||||
this.tooltipBlock = document.getElementById('tooltip-block');
|
||||
|
||||
var elements = [
|
||||
{target: Toolbar.sidebarButtonBlock},
|
||||
{target: Toolbar.backButtonBlock},
|
||||
{target: Toolbar.forwardButtonBlock},
|
||||
{target: Toolbar.addressButton},
|
||||
{target: Toolbar.searchInput},
|
||||
{target: Toolbar.bookmarkButtonBlock},
|
||||
{target: Toolbar.showBookmarksButtonBlock},
|
||||
{target: Toolbar.homeButtonBlock},
|
||||
{target: Toolbar.zoomButtonBlock},
|
||||
{target: Toolbar.tabsButtonBlock},
|
||||
{target: Toolbar.newTabButtonBlock},
|
||||
{target: Toolbar.menuButtonBlock},
|
||||
{target: Toolbar.modeButtonBlock},
|
||||
];
|
||||
|
||||
this.overObj = this.mouseoverEvent.bind(this);
|
||||
this.moveObj = this.mousemoveEvent.bind(this);
|
||||
// toolbar icon
|
||||
elements.forEach(function addEvent(elm) {
|
||||
var target = elm.target;
|
||||
// add event
|
||||
target.addEventListener('mouseover', this.overObj);
|
||||
target.addEventListener('mouseout', this.mouseoutEvent.bind(this));
|
||||
target.addEventListener('mousedown', this.clickEvent.bind(this));
|
||||
target.addEventListener('mouseup', this.clickEvent.bind(this));
|
||||
}, this);
|
||||
},
|
||||
|
||||
/**
|
||||
* mousemove event
|
||||
*/
|
||||
mousemoveEvent: function tooltip_mousemoveEvent(ev) {
|
||||
if(this.isShow()) {
|
||||
// hide the tooltip in mouse movement at tooltip display.
|
||||
this.stopMousemoveEvent(ev.target);
|
||||
this.hideTimeoutID = setTimeout(function(){
|
||||
Tooltip.hide();
|
||||
}, this.hideTimeoutData);
|
||||
return;
|
||||
}
|
||||
if(this.showTimeoutID) {
|
||||
clearTimeout(this.showTimeoutID);
|
||||
}
|
||||
|
||||
var bounds = ev.currentTarget.getBoundingClientRect();
|
||||
var posY = Math.ceil(bounds.bottom);
|
||||
var top = Math.ceil(bounds.top);
|
||||
var tips = null;
|
||||
if(ev.target.dataset.tips != null && ev.target.dataset.tips != undefined) {
|
||||
if(ev.target == Toolbar.searchInput) {
|
||||
tips = {raw: ev.target.dataset.tips};
|
||||
} else {
|
||||
tips = ev.target.dataset.tips;
|
||||
}
|
||||
}
|
||||
this.showTimeoutID = setTimeout(function(){
|
||||
Tooltip.showTarget = ev.target;
|
||||
var disp = {
|
||||
top: top,
|
||||
text: tips,
|
||||
x: ev.pageX,
|
||||
//y: ev.pageY
|
||||
y: posY,
|
||||
};
|
||||
Tooltip.displaySetting(disp);
|
||||
}, 150);
|
||||
},
|
||||
startMousemoveEvent: function tooltip_startMousemoveEvent(target) {
|
||||
target.addEventListener('mousemove', this.moveObj);
|
||||
},
|
||||
stopMousemoveEvent: function tooltip_stopMousemoveEvent(target) {
|
||||
target.removeEventListener('mousemove', this.moveObj);
|
||||
},
|
||||
|
||||
/**
|
||||
* mouseover event
|
||||
*/
|
||||
mouseoverEvent: function tooltip_mouseoverEvent(ev) {
|
||||
this.startMousemoveEvent(ev.target);
|
||||
},
|
||||
|
||||
/**
|
||||
* mouseout event
|
||||
*/
|
||||
mouseoutEvent: function tooltip_mouseoutEvent(ev) {
|
||||
this.hide();
|
||||
},
|
||||
|
||||
/**
|
||||
* click event
|
||||
*/
|
||||
clickEvent: function tooltip_clickEvent(ev) {
|
||||
this.hide();
|
||||
},
|
||||
|
||||
/**
|
||||
* show tooltip
|
||||
*/
|
||||
isShow: function tooltip_isShow(target) {
|
||||
if(!target){
|
||||
return this.tooltipBlock.classList.contains('visible');
|
||||
} else {
|
||||
if(target == this.showTarget){
|
||||
return this.tooltipBlock.classList.contains('visible');
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* show tooltip
|
||||
*/
|
||||
show: function tooltip_show() {
|
||||
this.tooltipBlock.classList.add('visible');
|
||||
},
|
||||
|
||||
/**
|
||||
* hide tooltip
|
||||
*/
|
||||
hide: function tooltip_hide() {
|
||||
clearTimeout(this.showTimeoutID);
|
||||
clearTimeout(this.hideTimeoutID);
|
||||
this.tooltipBlock.classList.remove('visible');
|
||||
},
|
||||
|
||||
/**
|
||||
* display setting
|
||||
*/
|
||||
displaySetting: function tooltip_displaySetting(disp) {
|
||||
if(disp.text == null || disp.text == undefined) {
|
||||
return;
|
||||
}
|
||||
this.show();
|
||||
this.tooltipBlock.style.left = '0px';
|
||||
if (typeof disp.text === 'string') {
|
||||
this.tooltipBlock.setAttribute('data-l10n-id', disp.text);
|
||||
} else if (disp.text.hasOwnProperty('raw')) {
|
||||
this.tooltipBlock.innerHTML = disp.text.replace(/\\n/g, '<br>');
|
||||
}
|
||||
|
||||
// pos x adjustment
|
||||
// 25 = scrollbar margin
|
||||
var maxW = this.tooltipBlock.offsetWidth + disp.x + 25;
|
||||
var posX = disp.x;
|
||||
if(maxW >= Browser.SCREEN_WIDTH) {
|
||||
// 58 = margin
|
||||
posX = Browser.SCREEN_WIDTH - this.tooltipBlock.offsetWidth - 58;
|
||||
}
|
||||
|
||||
// pos y adjustment
|
||||
var bounds = this.tooltipBlock.getBoundingClientRect();
|
||||
var hg = Math.ceil(bounds.height);
|
||||
var posY = disp.y + 20;
|
||||
var maxH = posY + hg;
|
||||
if(maxH >= Browser.SCREEN_HEIGHT) {
|
||||
// hover item
|
||||
posY = disp.top - 20 - hg;
|
||||
}
|
||||
|
||||
this.tooltipBlock.style.top = (posY + 0) + 'px';
|
||||
this.tooltipBlock.style.left = (posX + 0) + 'px';
|
||||
},
|
||||
};
|
||||
|
||||
Loading…
Reference in a new issue