Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

1128
gaia/apps/homescreen/js/apps.js Executable file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,110 @@
'use strict';
(function(exports) {
const DB_NAME = 'home-metadata';
const DB_ORDER_STORE = 'order';
const DB_ICON_STORE = 'icon';
const DB_VERSION = 1;
function AppsMetadata() {}
AppsMetadata.prototype = {
db: null,
init: function() {
return new Promise((resolve, reject) => {
var req = window.indexedDB.open(DB_NAME, DB_VERSION);
req.onupgradeneeded = this.upgradeSchema;
req.onsuccess = (e) => {
this.db = e.target.result;
resolve();
};
req.onerror = (e) => {
console.error('Error opening homescreen metadata db', e);
reject(e);
};
});
},
upgradeSchema: function(e) {
var db = e.target.result;
var fromVersion = e.oldVersion;
if (fromVersion < 1) {
var store = db.createObjectStore(DB_ORDER_STORE, { keyPath: 'id' });
store.createIndex('order', 'order', { unique: false });
store = db.createObjectStore(DB_ICON_STORE, { keyPath: 'id' });
store.createIndex('icon', 'icon', { unique: false });
}
},
set: function(data) {
return new Promise((resolve, reject) => {
var txn = this.db.transaction([DB_ORDER_STORE, DB_ICON_STORE],
'readwrite');
for (var entry of data) {
if (!entry.id) {
continue;
}
if (typeof entry.order !== 'undefined') {
txn.objectStore(DB_ORDER_STORE).
put({ id: entry.id, order: entry.order });
}
if (typeof entry.icon !== 'undefined') {
txn.objectStore(DB_ICON_STORE).
put({ id: entry.id, icon: entry.icon });
}
}
txn.oncomplete = resolve;
txn.onerror = reject;
});
},
remove: function(id) {
return new Promise((resolve, reject) => {
var txn = this.db.transaction([DB_ORDER_STORE, DB_ICON_STORE],
'readwrite');
txn.objectStore(DB_ORDER_STORE).delete(id);
txn.objectStore(DB_ICON_STORE).delete(id);
txn.oncomplete = resolve;
txn.onerror = reject;
});
},
getAll: function(onResult) {
return new Promise((resolve, reject) => {
var txn = this.db.transaction([DB_ORDER_STORE, DB_ICON_STORE],
'readonly');
var orderStore = txn.objectStore(DB_ORDER_STORE);
var iconStore = txn.objectStore(DB_ICON_STORE);
var cursor = orderStore.index('order').openCursor();
var results = [];
cursor.onsuccess = e => {
var cursor = e.target.result;
if (cursor) {
var result = cursor.value;
var iconRequest = iconStore.get(result.id);
iconRequest.onsuccess = function(result, e) {
if (e.target.result) {
result.icon = e.target.result.icon;
}
results.push(result);
if (onResult) {
onResult(result);
}
}.bind(this, result);
cursor.continue();
}
};
txn.oncomplete = () => {
resolve(results);
};
});
}
};
exports.AppsMetadata = AppsMetadata;
}(window));

5
gaia/apps/homescreen/js/bootstrap.js vendored Executable file
View file

@ -0,0 +1,5 @@
/* global AppWindow */
/* exported appWindow */
'use strict';
var appWindow = new AppWindow();

View file

@ -0,0 +1,234 @@
'use strict';
(function(exports) {
function Datastore(name) {
this.name = name;
}
Datastore.prototype = {
/**
* The name of the datastore
*/
name: '',
/**
* The version of the indexed database
*/
DB_VERSION: 1,
/**
* The datastore
*/
datastore: null,
/**
* The last datastore revision that was synced
*/
lastRevision: null,
/**
* Our local indexed db where we store our copy of bookmarks
*/
db: null,
get revisionName() {
return this.name + '_revision';
},
get idbName() {
return this.name + '_mirror';
},
get storeName() {
return this.name + '_store';
},
init: function() {
var revisionString = localStorage.getItem(this.revisionName);
if (revisionString) {
this.lastRevision = JSON.parse(revisionString);
}
return new Promise((resolve, reject) => {
var req = window.indexedDB.open(this.idbName, this.DB_VERSION);
req.onupgradeneeded = this.upgradeSchema.bind(this);
req.onsuccess = (e) => {
this.db = e.target.result;
resolve();
// Open up the shared datastore
if (!navigator.getDataStores) {
console.error('DataStore API is unavailable');
return;
}
navigator.getDataStores(this.name).then((stores) => {
if (stores.length < 1) {
console.error(this.name + ' inaccessible');
return;
}
this.datastore = stores[0];
this.datastore.addEventListener('change',
this.onChange.bind(this));
this.synchronise();
}, (e) => {
console.error('Error getting datastore', e);
});
};
req.onerror = (e) => {
console.error('Error opening datastore mirror database', e);
reject(e);
};
});
},
upgradeSchema: function(e) {
var db = e.target.result;
var fromVersion = e.oldVersion;
if (fromVersion < 1) {
var store = db.createObjectStore(this.storeName, { keyPath: 'id' });
store.createIndex('data', 'data', { unique: false });
}
},
synchronise: function() {
// Serialise mirror refreshes: overlapping clear/add snapshots erase
// rows while listeners are reading them.
this._syncQueue = (this._syncQueue || Promise.resolve()).then(
() => this._synchronise());
return this._syncQueue;
},
_synchronise: function() {
return new Promise((resolve, reject) => {
var cursor = this.datastore.sync(this.lastRevision);
var self = this;
function cursorResolve(task) {
var promises = [];
switch (task.operation) {
case 'update':
case 'add':
promises.push(self.set(task.data));
break;
case 'remove':
promises.push(self.remove(task.id));
break;
case 'clear':
promises.push(self.clear());
break;
case 'done':
self.updateRevision();
resolve();
return;
}
promises.push(cursor.next());
Promise.all(promises).then(
(results) => {
cursorResolve(results.pop());
}, reject);
}
cursor.next().then(cursorResolve, reject);
});
},
set: function(data) {
return new Promise((resolve, reject) => {
var txn = this.db.transaction([this.storeName], 'readwrite');
txn.oncomplete = function onComplete(data, resolve) {
resolve();
document.dispatchEvent(new CustomEvent(this.name + '-set',
{ detail: { id: data.id }}));
}.bind(this, data, resolve);
txn.onerror = reject;
try {
txn.objectStore(this.storeName).put({ id: data.id, data: data });
} catch(e) {
console.error('Error putting data in ' + this.idbName + ':', e);
resolve();
}
});
},
remove: function(id) {
return new Promise((resolve, reject) => {
var txn = this.db.transaction([this.storeName], 'readwrite');
txn.oncomplete = function onComplete(id, resolve) {
resolve();
document.dispatchEvent(new CustomEvent(this.name + '-removed',
{ detail: { id: id }}));
}.bind(this, id, resolve);
txn.onerror = reject;
try {
txn.objectStore(this.storeName).delete(id);
} catch(e) {
console.error('Error deleting data from ' + this.idbName + ':', e);
resolve();
}
});
},
clear: function() {
return new Promise((resolve, reject) => {
var txn = this.db.transaction([this.storeName], 'readwrite');
txn.oncomplete = function onComplete(resolve) {
resolve();
document.dispatchEvent(new CustomEvent(this.name + '-cleared'));
}.bind(this, resolve);
txn.onerror = reject;
txn.objectStore(this.storeName).clear();
});
},
updateRevision: function() {
this.lastRevision = this.datastore.revisionId;
localStorage.setItem(this.revisionName,
JSON.stringify(this.lastRevision));
},
onChange: function(e) {
this.synchronise().then(() => {},
(error) => {
console.error('Failed to handle ' + this.name + ' change', error);
});
},
get: function(id) {
return new Promise((resolve, reject) => {
var txn = this.db.transaction([this.storeName], 'readonly');
txn.onerror = reject;
txn.objectStore(this.storeName).get(id).onsuccess =
(event) => {
resolve(event.target.result);
};
});
},
getAll: function(filter = () => true) {
return new Promise((resolve, reject) => {
var results = [];
var txn = this.db.transaction([this.storeName], 'readonly');
txn.onerror = reject;
txn.oncomplete = () => { resolve(results); };
txn.objectStore(this.storeName).openCursor().onsuccess =
(event) => {
var cursor = event.target.result;
if (cursor) {
if (filter(cursor.value)) {
results.push(cursor.value);
}
cursor.continue();
}
};
});
}
};
exports.Datastore = Datastore;
}(window));

View file

@ -0,0 +1,180 @@
/* global LazyLoader */
'use strict';
(function(exports) {
const COLS_PREF = 'grid.cols';
const VERTICALHOME_APPS = 'verticalhome_items_migration';
const VERTICALHOME_SETTINGS = 'vertical_preferences_store';
const HOMESCREEN_SETTINGS = 'homescreen_settings';
function FirstRun() {
return importVerticalHome().then(Promise.resolve(),
(e) => {
console.warn('Loading default settings, import failed:', e);
return loadDefault();
});
}
function importVerticalHome() {
return new Promise((resolve, reject) => {
if (!navigator.getDataStores) {
reject('DataStore API is unavailable');
return;
}
navigator.getDataStores(VERTICALHOME_APPS).then(
stores => {
if (stores.length < 1) {
reject('verticalhome datastores inaccessible');
return;
}
stores[0].getLength().then(length => {
if (length === 0) {
reject('No verticalhome app order to import');
return;
}
var cursor = stores[0].sync();
var items = {};
var iterateItems = () => {
cursor.next().then(task => {
switch (task.operation) {
case 'done':
// Convert from verticalhome storage format to something
// homescreen can use, skipping over features that aren't
// supported.
var id;
var order = [];
for (var prop in items) {
var item = items[prop];
switch (item.type) {
case 'app':
id = item.manifestURL + '/' +
(item.entryPoint ? item.entryPoint : '');
break;
case 'bookmark':
id = item.id;
break;
default:
continue;
}
order.push({ id: id, order: item.index });
}
// Sort by order
order.sort((a, b) => { return a.order - b.order; });
// Rewrite order so there are no gaps
order.forEach((entry, index) => {
entry.order = index;
});
// Load columns setting and resolve
navigator.getDataStores(VERTICALHOME_SETTINGS).then(
stores => {
stores[0].get(COLS_PREF).then(cols => {
resolve({ order: order, small: cols >= 4 });
// If 4-column is set, we should synchronise the
// homescreen settings datastore so that Settings
// reflects the correct value. (3 is the default)
if (cols >= 4) {
navigator.getDataStores(HOMESCREEN_SETTINGS).then(
stores => {
stores[0].put(cols, COLS_PREF);
}).catch(
e => {
console.error(
'Error storing homescreen cols setting', e);
});
}
}, () => {
resolve({ order: order, small: false });
});
},
(e) => {
console.error('Error retrieving columns preference', e);
resolve({ order: order, small: false });
});
return;
case 'clear':
items = {};
break;
case 'put':
case 'add':
items[task.id] = task.data;
break;
case 'remove':
delete items[task.id];
break;
default:
reject('Unhandled datastore operation: ' + task.operation);
return;
}
iterateItems();
}, reject);
};
iterateItems();
}, reject);
}, reject);
});
}
function loadDefault() {
return new Promise((resolve, reject) => {
LazyLoader.getJSON('js/init.json').then(
json => {
if (!json) {
resolve({ order: [], small: false});
return;
}
// Convert the initialisation JSON format to something more useful
// for homescreen.
var small = !!(json.preferences && json.preferences[COLS_PREF] &&
(json.preferences[COLS_PREF] > 3));
var order = [];
if (json.grid) {
var index = 0;
json.grid.forEach((section) => {
section.forEach((entry) => {
if (!entry || !entry.manifestURL) {
return;
}
var id = entry.manifestURL + '/' +
(entry.entry_point ? entry.entry_point : '');
order.push({ id: id, order: index });
index ++;
});
});
}
resolve({ order: order, small: small});
},
e => {
reject(e);
}
);
});
}
exports.FirstRun = FirstRun;
}(window));

272
gaia/apps/homescreen/js/pages.js Executable file
View file

@ -0,0 +1,272 @@
/* global PagesStore, IconsHelper */
'use strict';
(function(exports) {
/**
* The size in pixels of the icons in the pinned pages.
*/
const PAGES_ICON_SIZE = 30;
function Pages() {
// Element references
this.panel = document.getElementById('pages-panel');
this.panels = document.getElementById('panels');
this.pages = document.getElementById('pages');
this.scrollable = document.querySelector('#pages-panel > .scrollable');
this.bottombar = document.getElementById('bottombar');
this.remove = document.getElementById('remove');
this.done = document.getElementById('done');
// Tracking if the list is empty
this.empty = true;
// Edit mode
this.editMode = false;
// Dialogs
this.dialogs = [];
// Signal handlers
this.pages.addEventListener('click', this);
this.pages.addEventListener('contextmenu', this);
this.pages.addEventListener('keydown', this);
window.addEventListener('resize', this);
this.done.addEventListener('click', e => {
e.preventDefault();
this.exitEditMode();
});
this.remove.addEventListener('click', e => {
e.preventDefault();
this.unpinSelectedCard();
});
// Initialise and populate pinned pages
this.pagesStore = new PagesStore('places');
this.pagesStore.init().then(() => {
// Triggered when pages are added and updated.
document.addEventListener('places-set', (e) => {
var id = e.detail.id;
this.pagesStore.get(id).then((page) => {
for (var child of this.pages.children) {
if (child.dataset.id === id) {
if (!page.data.pinned) {
this.removeCard(child);
} else {
this.updatePinnedPage(child, page.data);
}
return;
}
}
if (!page.data.pinned) {
return;
}
// A new page was pinned.
this.addPinnedPage(page.data);
});
});
document.addEventListener('places-removed', (e) => {
var id = e.detail.id;
for (var child of this.pages.children) {
if (child.dataset.id === id) {
this.removeCard(child);
return;
}
}
});
document.addEventListener('places-cleared', () => {
this.exitEditMode();
for (var child of this.pages.children) {
this.pages.removeChild(child);
}
if (!this.empty) {
this.empty = true;
this.panel.classList.add('empty');
}
});
},
(e) => {
console.error('Error initialising pinned pages', e);
}).then(() => {
return this.pagesStore.getAll().then((pages) => {
for (var page of pages) {
this.addPinnedPage(page.data);
}
// If there are no pinned pages, display helpful information.
if (this.empty) {
this.panel.classList.add('empty');
}
}, (e) => {
console.error('Error getting pinned pages', e);
});
});
}
Pages.prototype = {
updatePinnedPage: function(card, page) {
card.title = page.title;
card.dataset.id = page.url;
card.style.order = -Math.round(page.pinTime / 1000);
page.meta = page.meta || {};
if (page.screenshot) {
page.meta.screenshot = page.screenshot;
}
setTimeout(function(card, page) {
card.meta = page.meta;
IconsHelper.getIconBlob(page.url, PAGES_ICON_SIZE, page)
.then((iconObj) => {
if (iconObj.blob) {
var iconURL = URL.createObjectURL(iconObj.blob);
card.icon = `url(${iconURL})`;
}
})
.catch((e) => {
console.error('Failed to fetch icon', e);
});
}.bind(this, card, page));
},
addPinnedPage: function(page) {
var pinCard = document.createElement('gaia-pin-card');
// Make the card accessible/allow activation
pinCard.tabIndex = 0;
pinCard.setAttribute('role', 'link');
this.updatePinnedPage(pinCard, page);
this.pages.appendChild(pinCard);
if (this.empty) {
this.empty = false;
this.panel.classList.remove('empty');
}
},
launchCard: function(card) {
var features = {
name: card.title,
icon: card.icon,
remote: true
};
window.open(card.dataset.id, '_blank', Object.keys(features).map(
key => encodeURIComponent(key) + '=' + encodeURIComponent(features[key])
).join(','));
},
enterEditMode: function(card) {
if (this.selectedCard) {
this.selectedCard.classList.remove('selected');
}
if (card) {
this.selectedCard = card;
card.classList.add('selected');
}
this.editMode = true;
document.body.classList.add('edit-mode');
this.remove.classList.add('active');
},
exitEditMode: function() {
if (!this.editMode) {
return;
}
this.editMode = false;
document.body.classList.remove('edit-mode');
this.remove.classList.remove('active');
if (this.selectedCard) {
this.selectedCard.classList.remove('selected');
this.selectedCard = null;
}
},
removeCard: function(card) {
if (this.selectedCard === card) {
this.selectedCard = null;
}
this.pages.removeChild(card, () => {
if (!this.empty && this.pages.children.length === 0) {
this.exitEditMode();
this.empty = true;
this.panel.classList.add('empty');
}
});
},
unpinSelectedCard: function() {
if (!this.selectedCard) {
return;
}
var id = this.selectedCard.dataset.id;
// Remove child immediately, datastore operations can be quite slow
this.removeCard(this.selectedCard);
this.pagesStore.get(id).then(entry => {
entry.data.pinned = false;
this.pagesStore.datastore.put(entry.data, id).then(() => {},
e => {
console.error('Error unpinning page:', e);
});
}, e => {
console.error('Error retrieving page to unpin:', e);
});
},
handleEvent: function(e) {
switch (e.type) {
case 'click':
if (e.target.nodeName !== 'GAIA-PIN-CARD') {
break;
}
if (this.editMode) {
this.enterEditMode(e.target);
} else {
this.launchCard(e.target);
}
break;
case 'contextmenu':
if (e.target.nodeName !== 'GAIA-PIN-CARD') {
break;
}
this.enterEditMode(e.target);
break;
case 'keydown':
if (e.target.nodeName !== 'GAIA-PIN-CARD') {
break;
}
switch (e.keyCode) {
case 32: // Space
case 13: // Enter
this.launchCard(e.target);
}
break;
case 'resize':
this.pages.synchronise();
break;
}
}
};
exports.Pages = Pages;
}(window));

View file

@ -0,0 +1,25 @@
/* global Datastore */
'use strict';
(function(exports) {
function PagesStore(name) {
this.name = name;
}
PagesStore.prototype = Object.create(Datastore.prototype);
PagesStore.prototype.DB_VERSION = 1;
PagesStore.prototype.set = function(data) {
data.id = data.url;
return Datastore.prototype.set.call(this, data);
};
PagesStore.prototype.getAll = function() {
var filter = (value) => value.data && value.data.pinned;
return Datastore.prototype.getAll.call(this, filter);
};
exports.PagesStore = PagesStore;
}(window));

View file

@ -0,0 +1,118 @@
'use strict';
(function(exports) {
/**
* Name of the global home screen settings datastore.
*/
const SETTINGS_STORE = 'homescreen_settings';
/**
* Stored settings version, for use when changing/refactoring settings
* storage.
*/
const SETTINGS_VERSION = 0;
/**
* Name of the global home screen columns setting.
*/
const COLUMNS_SETTING = 'grid.cols';
/**
* Name of the global home screen paging setting.
*/
const PAGING_SETTING = 'grid.paging';
function Settings() {
// Initialise default settings
this.small = false;
this.scrollSnapping = false;
this.firstRun = false;
// Listen to external settings changes
if (navigator.getDataStores) {
navigator.getDataStores(SETTINGS_STORE).then(stores => {
if (stores.length < 1) {
return Promise.reject('Settings datastore inaccessible');
}
var signalChange = () => {
this.save();
window.dispatchEvent(new CustomEvent('settings-changed'));
};
var syncSmallSetting = (signal) => {
stores[0].get(COLUMNS_SETTING).then(cols => {
var oldSmall = this.small;
this.small = cols > 3;
if (this.small !== oldSmall) {
signalChange();
}
});
};
var syncPagingSetting = () => {
stores[0].get(PAGING_SETTING).then(paging => {
var oldSnapping = this.scrollSnapping;
this.scrollSnapping = paging || false;
if (this.scrollSnapping !== oldSnapping) {
signalChange();
}
});
};
stores[0].addEventListener('change', e => {
switch (e.id) {
case COLUMNS_SETTING:
syncSmallSetting();
break;
case PAGING_SETTING:
syncPagingSetting();
break;
}
});
syncSmallSetting();
syncPagingSetting();
}, e => {
console.error('Error retrieving home screen settings datastore:', e);
});
} else {
console.error('Datastore API unavailable');
}
// Restore existing settings
var settingsString = localStorage.getItem('settings');
if (!settingsString) {
this.firstRun = true;
return;
}
var settings = JSON.parse(settingsString);
if (settings.version !== SETTINGS_VERSION) {
return;
}
this.small = settings.small || false;
this.scrollSnapping = settings.scrollSnapping || false;
// Monitor global homescreen settings
if (!navigator.getDataStores) {
console.error('Datastore API unavailable');
return;
}
}
Settings.prototype = {
save: function() {
localStorage.setItem('settings', JSON.stringify({
version: SETTINGS_VERSION,
small: this.small,
scrollSnapping: this.scrollSnapping
}));
}
};
exports.Settings = Settings;
}(window));

View file

@ -0,0 +1 @@
window.GaiaAppIconBaseurl = 'shared/elements/gaia-site-icon/';

175
gaia/apps/homescreen/js/window.js Executable file
View file

@ -0,0 +1,175 @@
/* global Apps, Pages */
'use strict';
(function(exports) {
/**
* Time after receiving a handled hashchange that we ignore hashchange
* events for.
*/
const HASH_CHANGE_DEBOUNCE = 100;
function AppWindow() {
this.header = document.getElementById('page-indicator-header');
this.indicator = document.getElementById('page-indicator');
this.panels = document.getElementById('panels');
this.shadow = document.getElementById('shadow');
this.apps = new Apps();
this.pages = new Pages();
// Panel visibility state
this.appsVisible = undefined;
// Home-button handling
this.ignoreHashChangeTimeout = null;
// Signal handlers
this.indicator.addEventListener('keypress', this);
this.panels.addEventListener('scroll', this);
window.addEventListener('hashchange', this, true);
window.addEventListener('localized', this);
document.addEventListener('visibilitychange', this);
// Update the panel indicator
this.updatePanelIndicator();
// Application has finished initialisation
window.performance.mark('navigationInteractive');
}
AppWindow.prototype = {
updatePanelIndicator: function() {
var appsVisible =
Math.abs(this.panels.scrollLeft) <= this.panels.scrollWidth / 4;
if (this.appsVisible !== appsVisible) {
this.appsVisible = appsVisible;
this.header.setAttribute('data-l10n-id', appsVisible ?
'apps-panel' : 'pages-panel');
this.indicator.children[0].classList.toggle('active', appsVisible);
this.indicator.children[1].classList.toggle('active', !appsVisible);
this.indicator.setAttribute('aria-valuenow', appsVisible ? 1 : 2);
this.indicator.setAttribute('data-l10n-args', JSON.stringify({
currentPage: appsVisible ? 1 : 2,
totalPages: 2
}));
this.apps.panel.setAttribute('aria-hidden', !appsVisible);
this.pages.panel.setAttribute('aria-hidden', appsVisible);
if (appsVisible) {
this.pages.scrollable.removeEventListener('scroll', this);
this.apps.scrollable.addEventListener('scroll', this);
} else {
this.apps.scrollable.removeEventListener('scroll', this);
this.pages.scrollable.addEventListener('scroll', this);
}
this.updateShadowState();
}
},
updateShadowState: function() {
var position = this.appsVisible ?
this.apps.scrollable.scrollTop : this.pages.scrollable.scrollTop;
var scrolled = position > 1;
if (this.shadow.classList.contains('visible') !== scrolled) {
this.shadow.classList.toggle('visible', scrolled);
}
},
handleEvent: function(e) {
switch (e.type) {
// Switch between panels
case 'keypress':
if (!e.ctrlKey) {
return;
}
switch (e.keyCode) {
case e.DOM_VK_RIGHT:
this.panels.scrollTo(
{ left: this.panels.scrollLeftMax, top: 0, behavior: 'smooth' });
return;
case e.DOM_VK_LEFT:
this.panels.scrollTo(
{ left: 0, top: 0, behavior: 'smooth' });
return;
}
return;
// Display the top shadow when scrolling down
case 'scroll':
if (e.target === this.panels) {
this.updatePanelIndicator();
} else {
this.updateShadowState();
}
return;
// Cancel edit modes/scroll to the top/scroll to apps on home-button
case 'hashchange':
if (document.hidden || this.ignoreHashChangeTimeout !== null) {
return;
}
e.preventDefault();
e.stopImmediatePropagation();
this.ignoreHashChangeTimeout = setTimeout(() => {
this.ignoreHashChangeTimeout = null;
}, HASH_CHANGE_DEBOUNCE);
// If a dialog is showing, cancel the dialog
for (var dialog of this.apps.dialogs.concat(this.pages.dialogs)) {
if (!dialog.opened) {
continue;
}
dialog.close();
return;
}
// If we're in edit mode, cancel edit mode
if (this.apps.editMode) {
this.apps.exitEditMode();
return;
}
if (this.pages.editMode) {
this.pages.exitEditMode();
return;
}
// If we're on the pages panel and scrolled to the top, show apps
if (!this.appsVisible && this.pages.scrollable.scrollTop === 0) {
this.panels.scrollTo({ left: 0, top: 0, behavior: 'smooth' });
return;
}
// Otherwise, scroll the visible panel to the top
var visiblePanel = this.appsVisible ?
this.apps.scrollable : this.pages.scrollable;
visiblePanel.scrollTo({ left: 0, top: 0, behavior: 'smooth' });
return;
case 'localized':
this.updatePanelIndicator();
return;
// Exit edit mode if the user switches away from the home screen (e.g.
// by locking the phone).
case 'visibilitychange':
if (document.hidden) {
this.apps.exitEditMode();
this.pages.exitEditMode();
}
return;
}
}
};
exports.AppWindow = AppWindow;
}(window));