blob: d01ac160a2d66b379ae36c334d748faed9c45d50 [file] [edit]
// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
/**
* Namespace for the Camera app.
*/
var cca = cca || {};
/**
* Creates the Camera App main object.
* @param {number} aspectRatio Aspect ratio of app window when launched.
* @constructor
*/
cca.App = function(aspectRatio) {
/**
* @type {cca.models.Gallery}
* @private
*/
this.model_ = new cca.models.Gallery();
/**
* @type {cca.views.Camera}
* @private
*/
this.cameraView_ = new cca.views.Camera(
this.model_, this.onWindowResize_.bind(this));
/**
* @type {cca.views.MasterSettings}
* @private
*/
this.settingsView_ = new cca.views.MasterSettings();
/**
* @type {cca.views.GridSettings}
* @private
*/
this.gridsettingsView_ = new cca.views.GridSettings();
/**
* @type {cca.views.TimerSettings}
* @private
*/
this.timersettingsView_ = new cca.views.TimerSettings();
/**
* @type {cca.views.Browser}
* @private
*/
this.browserView_ = new cca.views.Browser(this.model_);
/**
* @type {cca.views.Warning}
* @private
*/
this.warningView_ = new cca.views.Warning();
/**
* @type {cca.views.Dialog}
* @private
*/
this.dialogView_ = new cca.views.Dialog();
/**
* @type {?number}
* @private
*/
this.resizeWindowTimeout_ = null;
/**
* @type {number}
* @private
*/
this.aspectRatio_ = aspectRatio;
// End of properties. Seal the object.
Object.seal(this);
document.body.addEventListener('keydown', this.onKeyPressed_.bind(this));
window.addEventListener('resize', this.onWindowResize_.bind(this, null));
document.title = chrome.i18n.getMessage('name');
this.setupI18nElements_();
this.setupToggles_();
};
/*
* Checks if it is applicable to use CrOS gallery app.
* @return {boolean} Whether applicable or not.
*/
cca.App.useGalleryApp = function() {
return chrome.fileManagerPrivate &&
document.body.classList.contains('ext-fs');
};
/**
* Sets up i18n messages on elements by i18n attributes.
* @private
*/
cca.App.prototype.setupI18nElements_ = function() {
var getElements = (attr) => document.querySelectorAll('[' + attr + ']');
var getMessage = (element, attr) => chrome.i18n.getMessage(
element.getAttribute(attr));
var setAriaLabel = (element, attr) => element.setAttribute(
'aria-label', getMessage(element, attr));
getElements('i18n-content').forEach(
(element) => element.textContent = getMessage(element, 'i18n-content'));
getElements('i18n-aria').forEach(
(element) => setAriaLabel(element, 'i18n-aria'));
cca.tooltip.setup(getElements('i18n-label')).forEach(
(element) => setAriaLabel(element, 'i18n-label'));
};
/**
* Sets up toggles (checkbox and radio) by data attributes.
* @private
*/
cca.App.prototype.setupToggles_ = function() {
document.querySelectorAll('input').forEach((element) => {
element.addEventListener('keypress', (event) =>
cca.util.getShortcutIdentifier(event) == 'Enter' && element.click());
var css = element.getAttribute('data-css');
var key = element.getAttribute('data-key');
var payload = () => {
var keys = {};
keys[key] = element.checked;
return keys;
};
element.addEventListener('change', (event) => {
if (css) {
document.body.classList.toggle(css, element.checked);
}
if (event.isTrusted) {
element.save();
if (element.type == 'radio' && element.checked) {
// Handle unchecked grouped sibling radios.
var grouped = `input[type=radio][name=${element.name}]:not(:checked)`;
document.querySelectorAll(grouped).forEach((radio) =>
radio.dispatchEvent(new Event('change')) && radio.save());
}
}
});
element.toggleChecked = (checked) => {
element.checked = checked;
element.dispatchEvent(new Event('change')); // Trigger toggling css.
};
element.save = () => {
return key && chrome.storage.local.set(payload());
};
if (key) {
// Restore the previously saved state on startup.
chrome.storage.local.get(payload(),
(values) => element.toggleChecked(values[key]));
}
});
};
/**
* Starts the app by preparing views/model and opening the camera-view.
*/
cca.App.prototype.start = function() {
cca.nav.setup([
this.cameraView_,
this.settingsView_,
this.gridsettingsView_,
this.timersettingsView_,
this.browserView_,
this.warningView_,
this.dialogView_,
]);
cca.models.FileSystem.initialize(() => {
// Prompt to migrate pictures if needed.
var message = chrome.i18n.getMessage('migratePicturesMsg');
return cca.nav.open('dialog', message, false).then((acked) => {
if (!acked) {
throw new Error('no-migrate');
}
});
}).then((external) => {
document.body.classList.toggle('ext-fs', external);
// Prepare the views/model and open camera-view.
this.cameraView_.prepare();
this.model_.addObserver(this.cameraView_.galleryButton);
if (!cca.App.useGalleryApp()) {
this.model_.addObserver(this.browserView_);
}
this.model_.load();
cca.nav.open('camera');
}).catch((error) => {
console.error(error);
if (error && error.message == 'no-migrate') {
chrome.app.window.current().close();
return;
}
cca.nav.open('warning', 'filesystem-failure');
});
};
/**
* Resizes the window to match the last known aspect ratio if applicable.
* @private
*/
cca.App.prototype.resizeByAspectRatio_ = function() {
// Don't update window size if it's maximized or fullscreen.
if (cca.util.isWindowFullSize()) {
return;
}
// Keep the width fixed and calculate the height by the aspect ratio.
// TODO(yuli): Update min-width for resizing at portrait orientation.
var inner = chrome.app.window.current().innerBounds;
var innerW = inner.minWidth;
var innerH = Math.round(innerW / this.aspectRatio_);
// Limit window resizing capability by setting min-height. Don't limit
// max-height here as it may disable maximize/fullscreen capabilities.
inner.minHeight = innerH;
inner.width = innerW;
inner.height = innerH;
};
/**
* Handles resizing window/views for size or aspect ratio changes.
* @param {number=} aspectRatio Aspect ratio changed.
* @private
*/
cca.App.prototype.onWindowResize_ = function(aspectRatio) {
if (this.resizeWindowTimeout_) {
clearTimeout(this.resizeWindowTimeout_);
this.resizeWindowTimeout_ = null;
}
if (aspectRatio) {
// Update window size immediately for changed aspect ratio.
this.aspectRatio_ = aspectRatio;
this.resizeByAspectRatio_();
} else {
// Don't further resize window during resizing for smooth UX.
this.resizeWindowTimeout_ = setTimeout(() => {
this.resizeWindowTimeout_ = null;
this.resizeByAspectRatio_();
}, 500);
}
cca.nav.onWindowResized();
};
/**
* Handles pressed keys.
* @param {Event} event Key press event.
* @private
*/
cca.App.prototype.onKeyPressed_ = function(event) {
cca.tooltip.hide(); // Hide shown tooltip on any keypress.
cca.nav.onKeyPressed(event);
};
/**
* @type {cca.App} Singleton of the App object.
* @private
*/
cca.App.instance_ = null;
/**
* Creates the Camera object and starts screen capturing.
*/
document.addEventListener('DOMContentLoaded', () => {
var appWindow = chrome.app.window.current();
if (!cca.App.instance_) {
var inner = appWindow.innerBounds;
cca.App.instance_ = new cca.App(inner.width / inner.height);
}
cca.App.instance_.start();
appWindow.show();
});