Commit 532786e7 authored by Kuo Jen Wei's avatar Kuo Jen Wei Committed by Commit Bot

[CCA] Migrate settings views to ES6 class

Bug: b/141518780
Test: Pass closure compiler check, tast run <DUT> 'camera.CCAUI*' and
validate all function of CCA on HALv1/v3 device works correctly.

Change-Id: Id933fc47dbc7a9b3efcf939b0ac3cf3b93d0b259
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1952568
Commit-Queue: Kuo Jen Wei <inker@chromium.org>
Auto-Submit: Kuo Jen Wei <inker@chromium.org>
Reviewed-by: default avatarShik Chen <shik@chromium.org>
Cr-Commit-Position: refs/heads/master@{#722415}
parent 36c3184b
...@@ -42,607 +42,593 @@ cca.views.DeviceSetting; ...@@ -42,607 +42,593 @@ cca.views.DeviceSetting;
/** /**
* Creates the base controller of settings view. * Creates the base controller of settings view.
* @param {string} selector Selector text of the view's root element.
* @param {!Object<string, !function(Event=)>=} itemHandlers Click-handlers
* mapped by element ids.
* @extends {cca.views.View}
* @constructor
*/ */
cca.views.BaseSettings = function(selector, itemHandlers = {}) { cca.views.BaseSettings = class extends cca.views.View {
cca.views.View.call(this, selector, true, true);
this.root.querySelector('.menu-header button')
.addEventListener('click', () => this.leave());
this.root.querySelectorAll('.menu-item').forEach((element) => {
/** @type {!function(Event=)|undefined} */
const handler = itemHandlers[element.id];
if (handler) {
element.addEventListener('click', handler);
}
});
};
cca.views.BaseSettings.prototype = {
__proto__: cca.views.View.prototype,
};
/**
* @override
*/
cca.views.BaseSettings.prototype.focus = function() {
this.rootElement_.querySelector('[tabindex]').focus();
};
/**
* Opens sub-settings.
* @param {string} id Settings identifier.
* @private
*/
cca.views.BaseSettings.prototype.openSubSettings = function(id) {
// Dismiss master-settings if sub-settings was dimissed by background click.
cca.nav.open(id).then((cond) => cond && cond.bkgnd && this.leave(cond));
};
/**
* Creates the controller of master settings view.
* @extends {cca.views.BaseSettings}
* @constructor
*/
cca.views.MasterSettings = function() {
cca.views.BaseSettings.call(this, '#settings', {
'settings-gridtype': () => this.openSubSettings('gridsettings'),
'settings-timerdur': () => this.openSubSettings('timersettings'),
'settings-resolution': () => this.openSubSettings('resolutionsettings'),
'settings-expert': () => this.openSubSettings('expertsettings'),
'settings-feedback': () => this.openFeedback(),
'settings-help': () => cca.util.openHelp(),
});
// End of properties, seal the object.
Object.seal(this);
};
cca.views.MasterSettings.prototype = {
__proto__: cca.views.BaseSettings.prototype,
};
/**
* Opens feedback.
* @private
*/
cca.views.MasterSettings.prototype.openFeedback = function() {
var data = {
'categoryTag': 'chromeos-camera-app',
'requestFeedback': true,
'feedbackInfo': {
'description': '',
'systemInformation': [
{key: 'APP ID', value: chrome.runtime.id},
{key: 'APP VERSION', value: chrome.runtime.getManifest().version},
],
},
};
const id = 'gfdkimpbcpahaombhbimeihdjnejgicl'; // Feedback extension id.
chrome.runtime.sendMessage(id, data);
};
/**
* Creates the controller of resolution settings view.
* @param {!cca.device.DeviceInfoUpdater} infoUpdater
* @param {!cca.device.PhotoConstraintsPreferrer} photoPreferrer
* @param {!cca.device.VideoConstraintsPreferrer} videoPreferrer
* @extends {cca.views.BaseSettings}
* @constructor
*/
cca.views.ResolutionSettings = function(
infoUpdater, photoPreferrer, videoPreferrer) {
/** /**
* @param {function(): ?cca.views.DeviceSetting} getSetting * @param {string} selector Selector text of the view's root element.
* @param {function(): !HTMLElement} getElement * @param {!Object<string, !function(Event=)>=} itemHandlers Click-handlers
* @param {boolean} isPhoto * mapped by element ids.
* @return {!function()}
*/ */
const createOpenMenuHandler = (getSetting, getElement, isPhoto) => () => { constructor(selector, itemHandlers = {}) {
const setting = getSetting(); super(selector, true, true);
if (setting === null) {
console.error('Open settings of non-exist device.'); this.root.querySelector('.menu-header button')
return; .addEventListener('click', () => this.leave());
} this.root.querySelectorAll('.menu-item').forEach((element) => {
const element = getElement(); /** @type {!function(Event=)|undefined} */
if (element.classList.contains('multi-option')) { const handler = itemHandlers[element.id];
if (isPhoto) { if (handler) {
this.openPhotoResSettings_(setting, element); element.addEventListener('click', handler);
} else {
this.openVideoResSettings_(setting, element);
} }
} });
}; }
cca.views.BaseSettings.call(this, '#resolutionsettings', {
'settings-front-photores': createOpenMenuHandler(
() => this.frontSetting_, () => this.frontPhotoItem_, true),
'settings-front-videores': createOpenMenuHandler(
() => this.frontSetting_, () => this.frontVideoItem_, false),
'settings-back-photores': createOpenMenuHandler(
() => this.backSetting_, () => this.backPhotoItem_, true),
'settings-back-videores': createOpenMenuHandler(
() => this.backSetting_, () => this.backVideoItem_, false),
});
/**
* @type {!cca.device.PhotoConstraintsPreferrer}
* @private
*/
this.photoPreferrer_ = photoPreferrer;
/** /**
* @type {!cca.device.VideoConstraintsPreferrer} * @override
* @private
*/ */
this.videoPreferrer_ = videoPreferrer; focus() {
this.rootElement_.querySelector('[tabindex]').focus();
}
/** /**
* @type {!HTMLElement} * Opens sub-settings.
* @param {string} id Settings identifier.
* @private * @private
*/ */
this.resMenu_ = /** @type {!HTMLElement} */ ( openSubSettings(id) {
document.querySelector('#resolutionsettings>div.menu')); // Dismiss master-settings if sub-settings was dismissed by background
// click.
cca.nav.open(id).then((cond) => cond && cond.bkgnd && this.leave(cond));
}
};
/**
* @type {!HTMLElement}
* @private
*/
this.videoResMenu_ = /** @type {!HTMLElement} */ (
document.querySelector('#videoresolutionsettings>div.menu'));
/**
* Creates the controller of master settings view.
*/
cca.views.MasterSettings = class extends cca.views.BaseSettings {
/** /**
* @type {!HTMLElement} * @public
* @private
*/ */
this.photoResMenu_ = /** @type {!HTMLElement} */ ( constructor() {
document.querySelector('#photoresolutionsettings>div.menu')); super('#settings', {
'settings-gridtype': () => this.openSubSettings('gridsettings'),
'settings-timerdur': () => this.openSubSettings('timersettings'),
'settings-resolution': () => this.openSubSettings('resolutionsettings'),
'settings-expert': () => this.openSubSettings('expertsettings'),
'settings-feedback': () => this.openFeedback(),
'settings-help': () => cca.util.openHelp(),
});
}
/** /**
* @type {!HTMLElement} * Opens feedback.
* @private * @private
*/ */
this.frontPhotoItem_ = /** @type {!HTMLElement} */ ( openFeedback() {
document.querySelector('#settings-front-photores')); var data = {
'categoryTag': 'chromeos-camera-app',
'requestFeedback': true,
'feedbackInfo': {
'description': '',
'systemInformation': [
{key: 'APP ID', value: chrome.runtime.id},
{key: 'APP VERSION', value: chrome.runtime.getManifest().version},
],
},
};
const id = 'gfdkimpbcpahaombhbimeihdjnejgicl'; // Feedback extension id.
chrome.runtime.sendMessage(id, data);
}
};
/**
* Creates the controller of resolution settings view.
*/
cca.views.ResolutionSettings = class extends cca.views.BaseSettings {
/** /**
* @type {!HTMLElement} * @param {!cca.device.DeviceInfoUpdater} infoUpdater
* @private * @param {!cca.device.PhotoConstraintsPreferrer} photoPreferrer
* @param {!cca.device.VideoConstraintsPreferrer} videoPreferrer
*/ */
this.frontVideoItem_ = /** @type {!HTMLElement} */ ( constructor(infoUpdater, photoPreferrer, videoPreferrer) {
document.querySelector('#settings-front-videores')); /**
* @param {function(): ?cca.views.DeviceSetting} getSetting
* @param {function(): !HTMLElement} getElement
* @param {boolean} isPhoto
* @return {!function()}
*/
const createOpenMenuHandler = (getSetting, getElement, isPhoto) => () => {
const setting = getSetting();
if (setting === null) {
console.error('Open settings of non-exist device.');
return;
}
const element = getElement();
if (element.classList.contains('multi-option')) {
if (isPhoto) {
this.openPhotoResSettings_(setting, element);
} else {
this.openVideoResSettings_(setting, element);
}
}
};
super('#resolutionsettings', {
'settings-front-photores': createOpenMenuHandler(
() => this.frontSetting_, () => this.frontPhotoItem_, true),
'settings-front-videores': createOpenMenuHandler(
() => this.frontSetting_, () => this.frontVideoItem_, false),
'settings-back-photores': createOpenMenuHandler(
() => this.backSetting_, () => this.backPhotoItem_, true),
'settings-back-videores': createOpenMenuHandler(
() => this.backSetting_, () => this.backVideoItem_, false),
});
/** /**
* @type {!HTMLElement} * @type {!cca.device.PhotoConstraintsPreferrer}
* @private * @private
*/ */
this.backPhotoItem_ = /** @type {!HTMLElement} */ ( this.photoPreferrer_ = photoPreferrer;
document.querySelector('#settings-back-photores'));
/**
* @type {!cca.device.VideoConstraintsPreferrer}
* @private
*/
this.videoPreferrer_ = videoPreferrer;
/**
* @type {!HTMLElement}
* @private
*/
this.resMenu_ = /** @type {!HTMLElement} */ (
document.querySelector('#resolutionsettings>div.menu'));
/**
* @type {!HTMLElement}
* @private
*/
this.videoResMenu_ = /** @type {!HTMLElement} */ (
document.querySelector('#videoresolutionsettings>div.menu'));
/**
* @type {!HTMLElement}
* @private
*/
this.photoResMenu_ = /** @type {!HTMLElement} */ (
document.querySelector('#photoresolutionsettings>div.menu'));
/**
* @type {!HTMLElement}
* @private
*/
this.frontPhotoItem_ = /** @type {!HTMLElement} */ (
document.querySelector('#settings-front-photores'));
/**
* @type {!HTMLElement}
* @private
*/
this.frontVideoItem_ = /** @type {!HTMLElement} */ (
document.querySelector('#settings-front-videores'));
/**
* @type {!HTMLElement}
* @private
*/
this.backPhotoItem_ = /** @type {!HTMLElement} */ (
document.querySelector('#settings-back-photores'));
/**
* @type {!HTMLElement}
* @private
*/
this.backVideoItem_ = /** @type {!HTMLElement} */ (
document.querySelector('#settings-back-videores'));
/**
* @type {!HTMLTemplateElement}
* @private
*/
this.resItemTempl_ = /** @type {!HTMLTemplateElement} */ (
document.querySelector('#resolution-item-template'));
/**
* @type {!HTMLTemplateElement}
* @private
*/
this.extcamItemTempl_ = /** @type {!HTMLTemplateElement} */ (
document.querySelector('#extcam-resolution-item-template'));
/**
* Device setting of front camera. Null if no front camera.
* @type {?cca.views.DeviceSetting}
* @private
*/
this.frontSetting_ = null;
/**
* Device setting of back camera. Null if no front camera.
* @type {?cca.views.DeviceSetting}
* @private
*/
this.backSetting_ = null;
/**
* Device setting of external cameras.
* @type {!Array<!cca.views.DeviceSetting>}
* @private
*/
this.externalSettings_ = [];
/** /**
* @type {!HTMLElement} * Device id of currently opened resolution setting view.
* @private * @type {?string}
*/ * @private
this.backVideoItem_ = /** @type {!HTMLElement} */ ( */
document.querySelector('#settings-back-videores')); this.openedSettingDeviceId_ = null;
infoUpdater.addDeviceChangeListener(async (updater) => {
/** @type {?Array<!cca.device.Camera3DeviceInfo>} */
const devices = await updater.getCamera3DevicesInfo();
if (devices === null) {
cca.state.set('no-resolution-settings', true);
return;
}
/** this.frontSetting_ = this.backSetting_ = null;
* @type {!HTMLTemplateElement} this.externalSettings_ = [];
* @private
*/ devices.forEach(({deviceId, facing, photoResols, videoResols}) => {
this.resItemTempl_ = /** @type {!HTMLTemplateElement} */ ( const /** !cca.views.DeviceSetting */ deviceSetting = {
document.querySelector('#resolution-item-template')); deviceId,
photo: {
prefResol: /** @type {!Resolution} */ (
photoPreferrer.getPrefResolution(deviceId)),
resols:
/* Filter out resolutions of megapixels < 0.1 i.e. megapixels
* 0.0*/
photoResols.filter((r) => r.area >= 100000),
},
video: {
prefResol: /** @type {!Resolution} */ (
videoPreferrer.getPrefResolution(deviceId)),
resols: videoResols,
},
};
switch (facing) {
case cros.mojom.CameraFacing.CAMERA_FACING_FRONT:
this.frontSetting_ = deviceSetting;
break;
case cros.mojom.CameraFacing.CAMERA_FACING_BACK:
this.backSetting_ = deviceSetting;
break;
case cros.mojom.CameraFacing.CAMERA_FACING_EXTERNAL:
this.externalSettings_.push(deviceSetting);
break;
default:
console.error(`Ignore device of unknown facing: ${facing}`);
}
});
this.updateResolutions_();
});
/** this.photoPreferrer_.setPreferredResolutionChangeListener(
* @type {!HTMLTemplateElement} this.updateSelectedPhotoResolution_.bind(this));
* @private this.videoPreferrer_.setPreferredResolutionChangeListener(
*/ this.updateSelectedVideoResolution_.bind(this));
this.extcamItemTempl_ = /** @type {!HTMLTemplateElement} */ ( }
document.querySelector('#extcam-resolution-item-template'));
/** /**
* Device setting of front camera. Null if no front camera. * Template for generating option text from photo resolution width and height.
* @type {?cca.views.DeviceSetting} * @param {!Resolution} r Resolution of text to be generated.
* @param {!ResolutionList} resolutions All available resolutions.
* @return {string} Text shown on resolution option item.
* @private * @private
*/ */
this.frontSetting_ = null; photoOptTextTempl_(r, resolutions) {
/**
* @param {number} a
* @param {number} b
* @return {number}
*/
const gcd = (a, b) => (a === 0 ? b : gcd(b % a, a));
/**
* @param {!Resolution} r
* @return {number}
*/
const toMegapixel = (r) => Math.round(r.area / 100000) / 10;
const /** number */ d = gcd(r.width, r.height);
if (resolutions.some(
(findR) => !findR.equals(r) && r.aspectRatioEquals(findR) &&
toMegapixel(r) === toMegapixel(findR))) {
return chrome.i18n.getMessage(
'label_detail_photo_resolution',
[r.width / d, r.height / d, r.width, r.height, toMegapixel(r)]);
} else {
return chrome.i18n.getMessage(
'label_photo_resolution',
[r.width / d, r.height / d, toMegapixel(r)]);
}
}
/** /**
* Device setting of back camera. Null if no front camera. * Template for generating option text from video resolution width and height.
* @type {?cca.views.DeviceSetting} * @param {!Resolution} r Resolution of text to be generated.
* @return {string} Text shown on resolution option item.
* @private * @private
*/ */
this.backSetting_ = null; videoOptTextTempl_(r) {
return chrome.i18n.getMessage(
'label_video_resolution', [r.height, r.width].map(String));
}
/** /**
* Device setting of external cameras. * Finds photo and video resolution setting of target device id.
* @type {!Array<!cca.views.DeviceSetting>} * @param {string} deviceId
* @return {?cca.views.DeviceSetting}
* @private * @private
*/ */
this.externalSettings_ = []; getDeviceSetting_(deviceId) {
if (this.frontSetting_ && this.frontSetting_.deviceId === deviceId) {
return this.frontSetting_;
}
if (this.backSetting_ && this.backSetting_.deviceId === deviceId) {
return this.backSetting_;
}
return this.externalSettings_.find((e) => e.deviceId === deviceId) || null;
}
/** /**
* Device id of currently opened resolution setting view. * Updates resolution information of front, back camera and external cameras.
* @type {?string}
* @private * @private
*/ */
this.openedSettingDeviceId_ = null; updateResolutions_() {
/**
// End of properties, seal the object. * @param {!HTMLElement} item
Object.seal(this); * @param {string} id
* @param {!cca.views.ResolutionConfig} config
infoUpdater.addDeviceChangeListener(async (updater) => { * @param {!function(!Resolution, !ResolutionList): string} optTextTempl
/** @type {?Array<!cca.device.Camera3DeviceInfo>} */ */
const devices = await updater.getCamera3DevicesInfo(); const prepItem = (item, id, {prefResol, resols}, optTextTempl) => {
if (devices === null) { item.dataset.deviceId = id;
cca.state.set('no-resolution-settings', true); item.classList.toggle('multi-option', resols.length > 1);
return; item.querySelector('.description>span').textContent =
optTextTempl(prefResol, resols);
};
// Update front camera setting
cca.state.set('has-front-camera', this.frontSetting_ !== null);
if (this.frontSetting_) {
const {deviceId, photo, video} = this.frontSetting_;
prepItem(this.frontPhotoItem_, deviceId, photo, this.photoOptTextTempl_);
prepItem(this.frontVideoItem_, deviceId, video, this.videoOptTextTempl_);
} }
this.frontSetting_ = this.backSetting_ = null; // Update back camera setting
this.externalSettings_ = []; cca.state.set('has-back-camera', this.backSetting_ !== null);
if (this.backSetting_) {
const {deviceId, photo, video} = this.backSetting_;
prepItem(this.backPhotoItem_, deviceId, photo, this.photoOptTextTempl_);
prepItem(this.backVideoItem_, deviceId, video, this.videoOptTextTempl_);
}
devices.forEach(({deviceId, facing, photoResols, videoResols}) => { // Update external camera settings
const /** !cca.views.DeviceSetting */ deviceSetting = { // To prevent losing focus on item already exist before update, locate
deviceId, // focused item in both previous and current list, pop out all items in
photo: { // previous list except those having same deviceId as focused one and
prefResol: /** @type {!Resolution} */ ( // recreate all other items from current list.
photoPreferrer.getPrefResolution(deviceId)), const prevFocus = /** @type {?HTMLElement} */ (
resols: this.resMenu_.querySelector('.menu-item.external-camera:focus'));
/* Filter out resolutions of megapixels < 0.1 i.e. megapixels /** @type {?string} */
* 0.0*/ const prevFId = prevFocus && prevFocus.dataset.deviceId;
photoResols.filter((r) => r.area >= 100000), const /** number */ focusIdx =
}, this.externalSettings_.findIndex(({deviceId}) => deviceId === prevFId);
video: { const fTitle = /** @type {?HTMLElement} */ (this.resMenu_.querySelector(
prefResol: /** @type {!Resolution} */ ( `.external-camera.title-item[data-device-id="${prevFId}"]`));
videoPreferrer.getPrefResolution(deviceId)), /** @type {?string} */
resols: videoResols, const focusedId = focusIdx === -1 ? null : prevFId;
},
}; this.resMenu_.querySelectorAll('.menu-item.external-camera')
switch (facing) { .forEach(
case cros.mojom.CameraFacing.CAMERA_FACING_FRONT: (element) => element.dataset.deviceId !== focusedId &&
this.frontSetting_ = deviceSetting; element.parentNode.removeChild(element));
break;
case cros.mojom.CameraFacing.CAMERA_FACING_BACK: this.externalSettings_.forEach((config, index) => {
this.backSetting_ = deviceSetting; const {deviceId} = config;
break; let /** !HTMLElement */ titleItem;
case cros.mojom.CameraFacing.CAMERA_FACING_EXTERNAL: let /** !HTMLElement */ photoItem;
this.externalSettings_.push(deviceSetting); let /** !HTMLElement */ videoItem;
break; if (deviceId !== focusedId) {
default: const extItem = /** @type {!HTMLElement} */ (
console.error(`Ignore device of unknown facing: ${facing}`); document.importNode(this.extcamItemTempl_.content, true));
cca.util.setupI18nElements(extItem);
[titleItem, photoItem, videoItem] =
/** @type {!NodeList<!HTMLElement>}*/ (
extItem.querySelectorAll('.menu-item'));
photoItem.addEventListener('click', () => {
if (photoItem.classList.contains('multi-option')) {
this.openPhotoResSettings_(config, photoItem);
}
});
photoItem.setAttribute('aria-describedby', `${deviceId}-photores-desc`);
photoItem.querySelector('.description').id =
`${deviceId}-photores-desc`;
videoItem.addEventListener('click', () => {
if (videoItem.classList.contains('multi-option')) {
this.openVideoResSettings_(config, videoItem);
}
});
videoItem.setAttribute('aria-describedby', `${deviceId}-videores-desc`);
videoItem.querySelector('.description').id =
`${deviceId}-videores-desc`;
if (index < focusIdx) {
this.resMenu_.insertBefore(extItem, fTitle);
} else {
this.resMenu_.appendChild(extItem);
}
} else {
titleItem = /** @type {!HTMLElement}*/ (fTitle);
photoItem = /** @type {!HTMLElement}*/ (fTitle.nextElementSibling);
videoItem = /** @type {!HTMLElement}*/ (photoItem.nextElementSibling);
} }
titleItem.dataset.deviceId = deviceId;
prepItem(photoItem, deviceId, config.photo, this.photoOptTextTempl_);
prepItem(videoItem, deviceId, config.video, this.videoOptTextTempl_);
}); });
this.updateResolutions_(); // Force closing opened setting of unplugged device.
}); if ((cca.state.get('photoresolutionsettings') ||
cca.state.get('videoresolutionsettings')) &&
this.photoPreferrer_.setPreferredResolutionChangeListener( this.openedSettingDeviceId_ !== null &&
this.updateSelectedPhotoResolution_.bind(this)); this.getDeviceSetting_(this.openedSettingDeviceId_) === null) {
this.videoPreferrer_.setPreferredResolutionChangeListener( cca.nav.close(
this.updateSelectedVideoResolution_.bind(this)); cca.state.get('photoresolutionsettings') ? 'photoresolutionsettings' :
}; 'videoresolutionsettings');
}
cca.views.ResolutionSettings.prototype = { }
__proto__: cca.views.BaseSettings.prototype,
};
/**
* Template for generating option text from photo resolution width and height.
* @param {!Resolution} r Resolution of text to be generated.
* @param {!ResolutionList} resolutions All available resolutions.
* @return {string} Text shown on resolution option item.
* @private
*/
cca.views.ResolutionSettings.prototype.photoOptTextTempl_ = function(
r, resolutions) {
/**
* @param {number} a
* @param {number} b
* @return {number}
*/
const gcd = (a, b) => (a === 0 ? b : gcd(b % a, a));
/** /**
* @param {!Resolution} r * Updates current selected photo resolution.
* @return {number} * @param {string} deviceId Device id of the selected resolution.
* @param {!Resolution} resolution Selected resolution.
* @private
*/ */
const toMegapixel = (r) => Math.round(r.area / 100000) / 10; updateSelectedPhotoResolution_(deviceId, resolution) {
const /** number */ d = gcd(r.width, r.height); const {photo} = this.getDeviceSetting_(deviceId);
if (resolutions.some( photo.prefResol = resolution;
(findR) => !findR.equals(r) && r.aspectRatioEquals(findR) && let /** !HTMLElement */ photoItem;
toMegapixel(r) === toMegapixel(findR))) { if (this.frontSetting_ && this.frontSetting_.deviceId === deviceId) {
return chrome.i18n.getMessage( photoItem = this.frontPhotoItem_;
'label_detail_photo_resolution', } else if (this.backSetting_ && this.backSetting_.deviceId === deviceId) {
[r.width / d, r.height / d, r.width, r.height, toMegapixel(r)]); photoItem = this.backPhotoItem_;
} else { } else {
return chrome.i18n.getMessage( photoItem = /** @type {!HTMLElement} */ (this.resMenu_.querySelector(
'label_photo_resolution', [r.width / d, r.height / d, toMegapixel(r)]); `.menu-item.photo-item[data-device-id="${deviceId}"]`));
} }
}; photoItem.querySelector('.description>span').textContent =
this.photoOptTextTempl_(photo.prefResol, photo.resols);
/**
* Template for generating option text from video resolution width and height. // Update setting option if it's opened.
* @param {!Resolution} r Resolution of text to be generated. if (cca.state.get('photoresolutionsettings') &&
* @return {string} Text shown on resolution option item. this.openedSettingDeviceId_ === deviceId) {
* @private this.photoResMenu_
*/ .querySelector(
cca.views.ResolutionSettings.prototype.videoOptTextTempl_ = function(r) { 'input' +
return chrome.i18n.getMessage( `[data-width="${resolution.width}"]` +
'label_video_resolution', [r.height, r.width].map(String)); `[data-height="${resolution.height}"]`)
}; .checked = true;
}
/**
* Finds photo and video resolution setting of target device id.
* @param {string} deviceId
* @return {?cca.views.DeviceSetting}
* @private
*/
cca.views.ResolutionSettings.prototype.getDeviceSetting_ = function(deviceId) {
if (this.frontSetting_ && this.frontSetting_.deviceId === deviceId) {
return this.frontSetting_;
}
if (this.backSetting_ && this.backSetting_.deviceId === deviceId) {
return this.backSetting_;
} }
return this.externalSettings_.find((e) => e.deviceId === deviceId) || null;
};
/**
* Updates resolution information of front, back camera and external cameras.
* @private
*/
cca.views.ResolutionSettings.prototype.updateResolutions_ = function() {
/** /**
* @param {!HTMLElement} item * Updates current selected video resolution.
* @param {string} id * @param {string} deviceId Device id of the selected resolution.
* @param {!cca.views.ResolutionConfig} config * @param {!Resolution} resolution Selected resolution.
* @param {!function(!Resolution, !ResolutionList): string} optTextTempl * @private
*/ */
const prepItem = (item, id, {prefResol, resols}, optTextTempl) => { updateSelectedVideoResolution_(deviceId, resolution) {
item.dataset.deviceId = id; const {video} = this.getDeviceSetting_(deviceId);
item.classList.toggle('multi-option', resols.length > 1); video.prefResol = resolution;
item.querySelector('.description>span').textContent =
optTextTempl(prefResol, resols);
};
// Update front camera setting
cca.state.set('has-front-camera', this.frontSetting_ !== null);
if (this.frontSetting_) {
const {deviceId, photo, video} = this.frontSetting_;
prepItem(this.frontPhotoItem_, deviceId, photo, this.photoOptTextTempl_);
prepItem(this.frontVideoItem_, deviceId, video, this.videoOptTextTempl_);
}
// Update back camera setting
cca.state.set('has-back-camera', this.backSetting_ !== null);
if (this.backSetting_) {
const {deviceId, photo, video} = this.backSetting_;
prepItem(this.backPhotoItem_, deviceId, photo, this.photoOptTextTempl_);
prepItem(this.backVideoItem_, deviceId, video, this.videoOptTextTempl_);
}
// Update external camera settings
// To prevent losing focus on item already exist before update, locate
// focused item in both previous and current list, pop out all items in
// previous list except those having same deviceId as focused one and
// recreate all other items from current list.
const prevFocus = /** @type {?HTMLElement} */ (
this.resMenu_.querySelector('.menu-item.external-camera:focus'));
/** @type {?string} */
const prevFId = prevFocus && prevFocus.dataset.deviceId;
const /** number */ focusIdx =
this.externalSettings_.findIndex(({deviceId}) => deviceId === prevFId);
const fTitle = /** @type {?HTMLElement} */ (this.resMenu_.querySelector(
`.external-camera.title-item[data-device-id="${prevFId}"]`));
/** @type {?string} */
const focusedId = focusIdx === -1 ? null : prevFId;
this.resMenu_.querySelectorAll('.menu-item.external-camera')
.forEach(
(element) => element.dataset.deviceId !== focusedId &&
element.parentNode.removeChild(element));
this.externalSettings_.forEach((config, index) => {
const {deviceId} = config;
let /** !HTMLElement */ titleItem;
let /** !HTMLElement */ photoItem;
let /** !HTMLElement */ videoItem; let /** !HTMLElement */ videoItem;
if (deviceId !== focusedId) { if (this.frontSetting_ && this.frontSetting_.deviceId === deviceId) {
const extItem = /** @type {!HTMLElement} */ ( videoItem = this.frontVideoItem_;
document.importNode(this.extcamItemTempl_.content, true)); } else if (this.backSetting_ && this.backSetting_.deviceId === deviceId) {
cca.util.setupI18nElements(extItem); videoItem = this.backVideoItem_;
[titleItem, photoItem, videoItem] =
/** @type {!NodeList<!HTMLElement>}*/ (
extItem.querySelectorAll('.menu-item'));
photoItem.addEventListener('click', () => {
if (photoItem.classList.contains('multi-option')) {
this.openPhotoResSettings_(config, photoItem);
}
});
photoItem.setAttribute('aria-describedby', `${deviceId}-photores-desc`);
photoItem.querySelector('.description').id = `${deviceId}-photores-desc`;
videoItem.addEventListener('click', () => {
if (videoItem.classList.contains('multi-option')) {
this.openVideoResSettings_(config, videoItem);
}
});
videoItem.setAttribute('aria-describedby', `${deviceId}-videores-desc`);
videoItem.querySelector('.description').id = `${deviceId}-videores-desc`;
if (index < focusIdx) {
this.resMenu_.insertBefore(extItem, fTitle);
} else {
this.resMenu_.appendChild(extItem);
}
} else { } else {
titleItem = /** @type {!HTMLElement}*/ (fTitle); videoItem = /** @type {!HTMLElement} */ (this.resMenu_.querySelector(
photoItem = /** @type {!HTMLElement}*/ (fTitle.nextElementSibling); `.menu-item.video-item[data-device-id="${deviceId}"]`));
videoItem = /** @type {!HTMLElement}*/ (photoItem.nextElementSibling); }
videoItem.querySelector('.description>span').textContent =
this.videoOptTextTempl_(video.prefResol);
// Update setting option if it's opened.
if (cca.state.get('videoresolutionsettings') &&
this.openedSettingDeviceId_ === deviceId) {
this.videoResMenu_
.querySelector(
'input' +
`[data-width="${resolution.width}"]` +
`[data-height="${resolution.height}"]`)
.checked = true;
} }
titleItem.dataset.deviceId = deviceId;
prepItem(photoItem, deviceId, config.photo, this.photoOptTextTempl_);
prepItem(videoItem, deviceId, config.video, this.videoOptTextTempl_);
});
// Force closing opened setting of unplugged device.
if ((cca.state.get('photoresolutionsettings') ||
cca.state.get('videoresolutionsettings')) &&
this.openedSettingDeviceId_ !== null &&
this.getDeviceSetting_(this.openedSettingDeviceId_) === null) {
cca.nav.close(
cca.state.get('photoresolutionsettings') ? 'photoresolutionsettings' :
'videoresolutionsettings');
} }
};
/** /**
* Updates current selected photo resolution. * Opens photo resolution setting view.
* @param {string} deviceId Device id of the selected resolution. * @param {!cca.views.DeviceSetting} Setting of video device to be opened.
* @param {!Resolution} resolution Selected resolution. * @param {!HTMLElement} resolItem Dom element from upper layer menu item
* @private * showing title of the selected resolution.
*/ * @private
cca.views.ResolutionSettings.prototype.updateSelectedPhotoResolution_ = */
function(deviceId, resolution) { openPhotoResSettings_({deviceId, photo}, resolItem) {
const {photo} = this.getDeviceSetting_(deviceId); this.openedSettingDeviceId_ = deviceId;
photo.prefResol = resolution; this.updateMenu_(
let /** !HTMLElement */ photoItem; resolItem, this.photoResMenu_, this.photoOptTextTempl_,
if (this.frontSetting_ && this.frontSetting_.deviceId === deviceId) { (r) => this.photoPreferrer_.changePreferredResolution(deviceId, r),
photoItem = this.frontPhotoItem_; photo.resols, photo.prefResol);
} else if (this.backSetting_ && this.backSetting_.deviceId === deviceId) { this.openSubSettings('photoresolutionsettings');
photoItem = this.backPhotoItem_;
} else {
photoItem = /** @type {!HTMLElement} */ (this.resMenu_.querySelector(
`.menu-item.photo-item[data-device-id="${deviceId}"]`));
}
photoItem.querySelector('.description>span').textContent =
this.photoOptTextTempl_(photo.prefResol, photo.resols);
// Update setting option if it's opened.
if (cca.state.get('photoresolutionsettings') &&
this.openedSettingDeviceId_ === deviceId) {
this.photoResMenu_
.querySelector(
'input' +
`[data-width="${resolution.width}"]` +
`[data-height="${resolution.height}"]`)
.checked = true;
} }
};
/** /**
* Updates current selected video resolution. * Opens video resolution setting view.
* @param {string} deviceId Device id of the selected resolution. * @param {!cca.views.DeviceSetting} Setting of video device to be opened.
* @param {!Resolution} resolution Selected resolution. * @param {!HTMLElement} resolItem Dom element from upper layer menu item
* @private * showing title of the selected resolution.
*/ * @private
cca.views.ResolutionSettings.prototype.updateSelectedVideoResolution_ = */
function(deviceId, resolution) { openVideoResSettings_({deviceId, video}, resolItem) {
const {video} = this.getDeviceSetting_(deviceId); this.openedSettingDeviceId_ = deviceId;
video.prefResol = resolution; this.updateMenu_(
let /** !HTMLElement */ videoItem; resolItem, this.videoResMenu_, this.videoOptTextTempl_,
if (this.frontSetting_ && this.frontSetting_.deviceId === deviceId) { (r) => this.videoPreferrer_.changePreferredResolution(deviceId, r),
videoItem = this.frontVideoItem_; video.resols, video.prefResol);
} else if (this.backSetting_ && this.backSetting_.deviceId === deviceId) { this.openSubSettings('videoresolutionsettings');
videoItem = this.backVideoItem_;
} else {
videoItem = /** @type {!HTMLElement} */ (this.resMenu_.querySelector(
`.menu-item.video-item[data-device-id="${deviceId}"]`));
}
videoItem.querySelector('.description>span').textContent =
this.videoOptTextTempl_(video.prefResol);
// Update setting option if it's opened.
if (cca.state.get('videoresolutionsettings') &&
this.openedSettingDeviceId_ === deviceId) {
this.videoResMenu_
.querySelector(
'input' +
`[data-width="${resolution.width}"]` +
`[data-height="${resolution.height}"]`)
.checked = true;
} }
};
/** /**
* Opens photo resolution setting view. * Updates resolution menu with specified resolutions.
* @param {!cca.views.DeviceSetting} Setting of video device to be opened. * @param {!HTMLElement} resolItem DOM element holding selected resolution.
* @param {!HTMLElement} resolItem Dom element from upper layer menu item * @param {!HTMLElement} menu Menu holding all resolution option elements.
* showing title of the selected resolution. * @param {!function(!Resolution, !ResolutionList): string} optTextTempl
* @private * Template generating text content for each resolution option from its
*/ * width and height.
cca.views.ResolutionSettings.prototype.openPhotoResSettings_ = function( * @param {!function(!Resolution)} onChange Called when selected option
{deviceId, photo}, resolItem) { * changed with resolution of newly selected option.
this.openedSettingDeviceId_ = deviceId; * @param {!ResolutionList} resolutions Resolutions of its width and height to
this.updateMenu_( * be updated with.
resolItem, this.photoResMenu_, this.photoOptTextTempl_, * @param {!Resolution} selectedR Selected resolution.
(r) => this.photoPreferrer_.changePreferredResolution(deviceId, r), * @private
photo.resols, photo.prefResol); */
this.openSubSettings('photoresolutionsettings'); updateMenu_(resolItem, menu, optTextTempl, onChange, resolutions, selectedR) {
}; const captionText = resolItem.querySelector('.description>span');
captionText.textContent = '';
/** menu.querySelectorAll('.menu-item')
* Opens video resolution setting view. .forEach((element) => element.parentNode.removeChild(element));
* @param {!cca.views.DeviceSetting} Setting of video device to be opened.
* @param {!HTMLElement} resolItem Dom element from upper layer menu item resolutions.forEach((r) => {
* showing title of the selected resolution. const item = /** @type {!HTMLElement} */ (
* @private document.importNode(this.resItemTempl_.content, true));
*/ const inputElement =
cca.views.ResolutionSettings.prototype.openVideoResSettings_ = function( /** @type {!HTMLElement} */ (item.querySelector('input'));
{deviceId, video}, resolItem) { item.querySelector('span').textContent = optTextTempl(r, resolutions);
this.openedSettingDeviceId_ = deviceId; inputElement.name = menu.dataset.name;
this.updateMenu_( inputElement.dataset.width = r.width;
resolItem, this.videoResMenu_, this.videoOptTextTempl_, inputElement.dataset.height = r.height;
(r) => this.videoPreferrer_.changePreferredResolution(deviceId, r), if (r.equals(selectedR)) {
video.resols, video.prefResol);
this.openSubSettings('videoresolutionsettings');
};
/**
* Updates resolution menu with specified resolutions.
* @param {!HTMLElement} resolItem DOM element holding selected resolution.
* @param {!HTMLElement} menu Menu holding all resolution option elements.
* @param {!function(!Resolution, !ResolutionList): string} optTextTempl
* Template generating text content for each resolution option from its
* width and height.
* @param {!function(!Resolution)} onChange Called when selected option changed
* with resolution of newly selected option.
* @param {!ResolutionList} resolutions Resolutions of its width and height to
* be updated with.
* @param {!Resolution} selectedR Selected resolution.
* @private
*/
cca.views.ResolutionSettings.prototype.updateMenu_ = function(
resolItem, menu, optTextTempl, onChange, resolutions, selectedR) {
const captionText = resolItem.querySelector('.description>span');
captionText.textContent = '';
menu.querySelectorAll('.menu-item')
.forEach((element) => element.parentNode.removeChild(element));
resolutions.forEach((r) => {
const item = /** @type {!HTMLElement} */ (
document.importNode(this.resItemTempl_.content, true));
const inputElement =
/** @type {!HTMLElement} */ (item.querySelector('input'));
item.querySelector('span').textContent = optTextTempl(r, resolutions);
inputElement.name = menu.dataset.name;
inputElement.dataset.width = r.width;
inputElement.dataset.height = r.height;
if (r.equals(selectedR)) {
captionText.textContent = optTextTempl(r, resolutions);
inputElement.checked = true;
}
inputElement.addEventListener('click', (event) => {
if (!cca.state.get('streaming') || cca.state.get('taking')) {
event.preventDefault();
}
});
inputElement.addEventListener('change', (event) => {
if (inputElement.checked) {
captionText.textContent = optTextTempl(r, resolutions); captionText.textContent = optTextTempl(r, resolutions);
onChange(r); inputElement.checked = true;
} }
inputElement.addEventListener('click', (event) => {
if (!cca.state.get('streaming') || cca.state.get('taking')) {
event.preventDefault();
}
});
inputElement.addEventListener('change', (event) => {
if (inputElement.checked) {
captionText.textContent = optTextTempl(r, resolutions);
onChange(r);
}
});
menu.appendChild(item);
}); });
menu.appendChild(item); }
});
}; };
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment