UNPKG

@dcloudio/uni-debugger

Version:

uni-app debugger

526 lines (456 loc) 22.3 kB
// Copyright (c) 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /** * @unrestricted */ Emulation.SensorsView = class extends UI.VBox { constructor() { super(true); this.registerRequiredCSS('emulation/sensors.css'); this.contentElement.classList.add('sensors-view'); this._geolocationSetting = Common.settings.createSetting('emulation.geolocationOverride', ''); this._geolocation = SDK.EmulationModel.Geolocation.parseSetting(this._geolocationSetting.get()); this._geolocationOverrideEnabled = false; this._createGeolocationSection(this._geolocation); this.contentElement.createChild('div').classList.add('panel-section-separator'); this._deviceOrientationSetting = Common.settings.createSetting('emulation.deviceOrientationOverride', ''); this._deviceOrientation = SDK.EmulationModel.DeviceOrientation.parseSetting(this._deviceOrientationSetting.get()); this._deviceOrientationOverrideEnabled = false; this._createDeviceOrientationSection(); this.contentElement.createChild('div').classList.add('panel-section-separator'); this._appendTouchControl(); } /** * @return {!Emulation.SensorsView} */ static instance() { if (!Emulation.SensorsView._instanceObject) Emulation.SensorsView._instanceObject = new Emulation.SensorsView(); return Emulation.SensorsView._instanceObject; } /** * @param {!SDK.EmulationModel.Geolocation} geolocation */ _createGeolocationSection(geolocation) { const geogroup = this.contentElement.createChild('section', 'sensors-group'); geogroup.createChild('div', 'sensors-group-title').textContent = Common.UIString('Geolocation'); const fields = geogroup.createChild('div', 'geo-fields'); const noOverrideOption = { title: Common.UIString('No override'), location: Emulation.SensorsView.NonPresetOptions.NoOverride }; const customLocationOption = { title: Common.UIString('Custom location...'), location: Emulation.SensorsView.NonPresetOptions.Custom }; this._locationSelectElement = this.contentElement.createChild('select', 'chrome-select'); this._locationSelectElement.appendChild(new Option(noOverrideOption.title, noOverrideOption.location)); this._locationSelectElement.appendChild(new Option(customLocationOption.title, customLocationOption.location)); const locationGroups = Emulation.SensorsView.PresetLocations; for (let i = 0; i < locationGroups.length; ++i) { const group = locationGroups[i].value; const groupElement = this._locationSelectElement.createChild('optgroup'); groupElement.label = locationGroups[i].title; for (let j = 0; j < group.length; ++j) groupElement.appendChild(new Option(group[j].title, group[j].location)); } this._locationSelectElement.selectedIndex = 0; fields.appendChild(this._locationSelectElement); this._locationSelectElement.addEventListener('change', this._geolocationSelectChanged.bind(this)); // Validated input fieldset. this._fieldsetElement = fields.createChild('fieldset'); this._fieldsetElement.disabled = !this._geolocationOverrideEnabled; this._fieldsetElement.id = 'geolocation-override-section'; const latitudeGroup = this._fieldsetElement.createChild('div', 'latlong-group'); const longitudeGroup = this._fieldsetElement.createChild('div', 'latlong-group'); this._latitudeInput = UI.createInput('', 'number'); latitudeGroup.appendChild(this._latitudeInput); this._latitudeInput.setAttribute('step', 'any'); this._latitudeInput.value = 0; this._latitudeSetter = UI.bindInput( this._latitudeInput, this._applyGeolocationUserInput.bind(this), SDK.EmulationModel.Geolocation.latitudeValidator, true, 0.1); this._latitudeSetter(String(geolocation.latitude)); this._longitudeInput = UI.createInput('', 'number'); longitudeGroup.appendChild(this._longitudeInput); this._longitudeInput.setAttribute('step', 'any'); this._longitudeInput.value = 0; this._longitudeSetter = UI.bindInput( this._longitudeInput, this._applyGeolocationUserInput.bind(this), SDK.EmulationModel.Geolocation.longitudeValidator, true, 0.1); this._longitudeSetter(String(geolocation.longitude)); const cmdOrCtrl = Host.isMac() ? '\u2318' : 'Ctrl'; const modifierKeyMessage = ls`Adjust with mousewheel or up/down keys. ${cmdOrCtrl}: ±10, Shift: ±1, Alt: ±0.01`; this._latitudeInput.title = modifierKeyMessage; this._longitudeInput.title = modifierKeyMessage; latitudeGroup.createChild('div', 'latlong-title').textContent = Common.UIString('Latitude'); longitudeGroup.createChild('div', 'latlong-title').textContent = Common.UIString('Longitude'); } _geolocationSelectChanged() { this._fieldsetElement.disabled = false; const value = this._locationSelectElement.options[this._locationSelectElement.selectedIndex].value; if (value === Emulation.SensorsView.NonPresetOptions.NoOverride) { this._geolocationOverrideEnabled = false; this._fieldsetElement.disabled = true; } else if (value === Emulation.SensorsView.NonPresetOptions.Custom) { this._geolocationOverrideEnabled = true; } else if (value === Emulation.SensorsView.NonPresetOptions.Unavailable) { this._geolocationOverrideEnabled = true; this._geolocation = new SDK.EmulationModel.Geolocation(0, 0, true); } else { this._geolocationOverrideEnabled = true; const coordinates = JSON.parse(value); this._geolocation = new SDK.EmulationModel.Geolocation(coordinates[0], coordinates[1], false); this._latitudeSetter(coordinates[0]); this._longitudeSetter(coordinates[1]); } this._applyGeolocation(); if (value === Emulation.SensorsView.NonPresetOptions.Custom) this._latitudeInput.focus(); } _applyGeolocationUserInput() { const geolocation = SDK.EmulationModel.Geolocation.parseUserInput( this._latitudeInput.value.trim(), this._longitudeInput.value.trim(), ''); if (!geolocation) return; this._setSelectElementLabel(this._locationSelectElement, Emulation.SensorsView.NonPresetOptions.Custom); this._geolocation = geolocation; this._applyGeolocation(); } _applyGeolocation() { if (this._geolocationOverrideEnabled) this._geolocationSetting.set(this._geolocation.toSetting()); for (const emulationModel of SDK.targetManager.models(SDK.EmulationModel)) emulationModel.emulateGeolocation(this._geolocationOverrideEnabled ? this._geolocation : null); } _createDeviceOrientationSection() { const orientationGroup = this.contentElement.createChild('section', 'sensors-group'); orientationGroup.createChild('div', 'sensors-group-title').textContent = Common.UIString('Orientation'); const orientationContent = orientationGroup.createChild('div', 'orientation-content'); const fields = orientationContent.createChild('div', 'orientation-fields'); const orientationOffOption = { title: Common.UIString('Off'), orientation: Emulation.SensorsView.NonPresetOptions.NoOverride }; const customOrientationOption = { title: Common.UIString('Custom orientation...'), orientation: Emulation.SensorsView.NonPresetOptions.Custom }; this._orientationSelectElement = this.contentElement.createChild('select', 'chrome-select'); this._orientationSelectElement.appendChild( new Option(orientationOffOption.title, orientationOffOption.orientation)); this._orientationSelectElement.appendChild( new Option(customOrientationOption.title, customOrientationOption.orientation)); const orientationGroups = Emulation.SensorsView.PresetOrientations; for (let i = 0; i < orientationGroups.length; ++i) { const groupElement = this._orientationSelectElement.createChild('optgroup'); groupElement.label = orientationGroups[i].title; const group = orientationGroups[i].value; for (let j = 0; j < group.length; ++j) groupElement.appendChild(new Option(group[j].title, group[j].orientation)); } this._orientationSelectElement.selectedIndex = 0; fields.appendChild(this._orientationSelectElement); this._orientationSelectElement.addEventListener('change', this._orientationSelectChanged.bind(this)); this._deviceOrientationFieldset = this._createDeviceOrientationOverrideElement(this._deviceOrientation); this._stageElement = orientationContent.createChild('div', 'orientation-stage'); this._stageElement.title = Common.UIString('Shift+drag horizontally to rotate around the y-axis'); this._orientationLayer = this._stageElement.createChild('div', 'orientation-layer'); this._boxElement = this._orientationLayer.createChild('section', 'orientation-box orientation-element'); this._boxElement.createChild('section', 'orientation-front orientation-element'); this._boxElement.createChild('section', 'orientation-top orientation-element'); this._boxElement.createChild('section', 'orientation-back orientation-element'); this._boxElement.createChild('section', 'orientation-left orientation-element'); this._boxElement.createChild('section', 'orientation-right orientation-element'); this._boxElement.createChild('section', 'orientation-bottom orientation-element'); UI.installDragHandle( this._stageElement, this._onBoxDragStart.bind(this), this._onBoxDrag.bind(this), null, '-webkit-grabbing', '-webkit-grab'); fields.appendChild(this._deviceOrientationFieldset); this._enableOrientationFields(true); this._setBoxOrientation(this._deviceOrientation, false); } /** * @param {?boolean} disable */ _enableOrientationFields(disable) { if (disable) { this._deviceOrientationFieldset.disabled = true; this._stageElement.classList.add('disabled'); } else { this._deviceOrientationFieldset.disabled = false; this._stageElement.classList.remove('disabled'); } } _orientationSelectChanged() { const value = this._orientationSelectElement.options[this._orientationSelectElement.selectedIndex].value; this._enableOrientationFields(false); if (value === Emulation.SensorsView.NonPresetOptions.NoOverride) { this._deviceOrientationOverrideEnabled = false; this._enableOrientationFields(true); } else if (value === Emulation.SensorsView.NonPresetOptions.Custom) { this._deviceOrientationOverrideEnabled = true; this._alphaElement.focus(); } else { const parsedValue = JSON.parse(value); this._deviceOrientationOverrideEnabled = true; this._deviceOrientation = new SDK.EmulationModel.DeviceOrientation(parsedValue[0], parsedValue[1], parsedValue[2]); this._setDeviceOrientation( this._deviceOrientation, Emulation.SensorsView.DeviceOrientationModificationSource.SelectPreset); } } _applyDeviceOrientation() { if (this._deviceOrientationOverrideEnabled) this._deviceOrientationSetting.set(this._deviceOrientation.toSetting()); for (const emulationModel of SDK.targetManager.models(SDK.EmulationModel)) emulationModel.emulateDeviceOrientation(this._deviceOrientationOverrideEnabled ? this._deviceOrientation : null); } /** * @param {!Element} selectElement * @param {string} labelValue */ _setSelectElementLabel(selectElement, labelValue) { const optionValues = Array.prototype.map.call(selectElement.options, x => x.value); selectElement.selectedIndex = optionValues.indexOf(labelValue); } _applyDeviceOrientationUserInput() { this._setDeviceOrientation( SDK.EmulationModel.DeviceOrientation.parseUserInput( this._alphaElement.value.trim(), this._betaElement.value.trim(), this._gammaElement.value.trim()), Emulation.SensorsView.DeviceOrientationModificationSource.UserInput); this._setSelectElementLabel(this._orientationSelectElement, Emulation.SensorsView.NonPresetOptions.Custom); } _resetDeviceOrientation() { this._setDeviceOrientation( new SDK.EmulationModel.DeviceOrientation(0, 90, 0), Emulation.SensorsView.DeviceOrientationModificationSource.ResetButton); this._setSelectElementLabel(this._orientationSelectElement, '[0, 90, 0]'); } /** * @param {?SDK.EmulationModel.DeviceOrientation} deviceOrientation * @param {!Emulation.SensorsView.DeviceOrientationModificationSource} modificationSource */ _setDeviceOrientation(deviceOrientation, modificationSource) { if (!deviceOrientation) return; /** * @param {number} angle * @return {number} */ function roundAngle(angle) { return Math.round(angle * 10000) / 10000; } if (modificationSource !== Emulation.SensorsView.DeviceOrientationModificationSource.UserInput) { this._alphaSetter(roundAngle(deviceOrientation.alpha)); this._betaSetter(roundAngle(deviceOrientation.beta)); this._gammaSetter(roundAngle(deviceOrientation.gamma)); } const animate = modificationSource !== Emulation.SensorsView.DeviceOrientationModificationSource.UserDrag; this._setBoxOrientation(deviceOrientation, animate); this._deviceOrientation = deviceOrientation; this._applyDeviceOrientation(); } /** * @param {!Element} parentElement * @param {!Element} input * @param {string} label * @return {function(string)} */ _createAxisInput(parentElement, input, label) { const div = parentElement.createChild('div', 'orientation-axis-input-container'); div.appendChild(input); div.createTextChild(label); input.type = 'number'; return UI.bindInput( input, this._applyDeviceOrientationUserInput.bind(this), SDK.EmulationModel.DeviceOrientation.validator, true); } /** * @param {!SDK.EmulationModel.DeviceOrientation} deviceOrientation * @return {!Element} */ _createDeviceOrientationOverrideElement(deviceOrientation) { const fieldsetElement = createElement('fieldset'); fieldsetElement.classList.add('device-orientation-override-section'); const cellElement = fieldsetElement.createChild('td', 'orientation-inputs-cell'); this._alphaElement = UI.createInput(); this._alphaElement.setAttribute('step', 'any'); this._alphaSetter = this._createAxisInput(cellElement, this._alphaElement, Common.UIString('\u03B1 (alpha)')); this._alphaSetter(String(deviceOrientation.alpha)); this._betaElement = UI.createInput(); this._betaElement.setAttribute('step', 'any'); this._betaSetter = this._createAxisInput(cellElement, this._betaElement, Common.UIString('\u03B2 (beta)')); this._betaSetter(String(deviceOrientation.beta)); this._gammaElement = UI.createInput(); this._gammaElement.setAttribute('step', 'any'); this._gammaSetter = this._createAxisInput(cellElement, this._gammaElement, Common.UIString('\u03B3 (gamma)')); this._gammaSetter(String(deviceOrientation.gamma)); cellElement.appendChild(UI.createTextButton( Common.UIString('Reset'), this._resetDeviceOrientation.bind(this), 'orientation-reset-button')); return fieldsetElement; } /** * @param {!SDK.EmulationModel.DeviceOrientation} deviceOrientation * @param {boolean} animate */ _setBoxOrientation(deviceOrientation, animate) { if (animate) this._stageElement.classList.add('is-animating'); else this._stageElement.classList.remove('is-animating'); // The CSS transform should not depend on matrix3d, which does not interpolate well. const matrix = new WebKitCSSMatrix(); this._boxMatrix = matrix.rotate(-deviceOrientation.beta, deviceOrientation.gamma, -deviceOrientation.alpha); const eulerAngles = new UI.Geometry.EulerAngles(deviceOrientation.alpha, deviceOrientation.beta, deviceOrientation.gamma); this._orientationLayer.style.transform = eulerAngles.toRotate3DString(); } /** * @param {!MouseEvent} event * @return {boolean} */ _onBoxDrag(event) { const mouseMoveVector = this._calculateRadiusVector(event.x, event.y); if (!mouseMoveVector) return true; event.consume(true); let axis, angle; if (event.shiftKey) { axis = new UI.Geometry.Vector(0, 0, -1); angle = (this._mouseDownVector.x - mouseMoveVector.x) * Emulation.SensorsView.ShiftDragOrientationSpeed; } else { axis = UI.Geometry.crossProduct(this._mouseDownVector, mouseMoveVector); angle = UI.Geometry.calculateAngle(this._mouseDownVector, mouseMoveVector); } // The mouse movement vectors occur in the screen space, which is offset by 90 degrees from // the actual device orientation. let currentMatrix = new WebKitCSSMatrix(); currentMatrix = currentMatrix.rotate(-90, 0, 0) .rotateAxisAngle(axis.x, axis.y, axis.z, angle) .rotate(90, 0, 0) .multiply(this._originalBoxMatrix); const eulerAngles = UI.Geometry.EulerAngles.fromRotationMatrix(currentMatrix); const newOrientation = new SDK.EmulationModel.DeviceOrientation(-eulerAngles.alpha, -eulerAngles.beta, eulerAngles.gamma); this._setDeviceOrientation(newOrientation, Emulation.SensorsView.DeviceOrientationModificationSource.UserDrag); this._setSelectElementLabel(this._orientationSelectElement, Emulation.SensorsView.NonPresetOptions.Custom); return false; } /** * @param {!MouseEvent} event * @return {boolean} */ _onBoxDragStart(event) { if (!this._deviceOrientationOverrideEnabled) return false; this._mouseDownVector = this._calculateRadiusVector(event.x, event.y); this._originalBoxMatrix = this._boxMatrix; if (!this._mouseDownVector) return false; event.consume(true); return true; } /** * @param {number} x * @param {number} y * @return {?UI.Geometry.Vector} */ _calculateRadiusVector(x, y) { const rect = this._stageElement.getBoundingClientRect(); const radius = Math.max(rect.width, rect.height) / 2; const sphereX = (x - rect.left - rect.width / 2) / radius; const sphereY = (y - rect.top - rect.height / 2) / radius; const sqrSum = sphereX * sphereX + sphereY * sphereY; if (sqrSum > 0.5) return new UI.Geometry.Vector(sphereX, sphereY, 0.5 / Math.sqrt(sqrSum)); return new UI.Geometry.Vector(sphereX, sphereY, Math.sqrt(1 - sqrSum)); } _appendTouchControl() { const groupElement = this.contentElement.createChild('div', 'sensors-group'); const title = groupElement.createChild('div', 'sensors-group-title'); const fieldsElement = groupElement.createChild('div', 'sensors-group-fields'); title.textContent = Common.UIString('Touch'); const select = fieldsElement.createChild('select', 'chrome-select'); select.appendChild(new Option(Common.UIString('Device-based'), 'auto')); select.appendChild(new Option(Common.UIString('Force enabled'), 'enabled')); select.addEventListener('change', applyTouch, false); const reloadWarning = groupElement.createChild('div', 'reload-warning hidden'); reloadWarning.textContent = Common.UIString('*Requires reload'); function applyTouch() { for (const emulationModel of SDK.targetManager.models(SDK.EmulationModel)) emulationModel.overrideEmulateTouch(select.value === 'enabled'); reloadWarning.classList.remove('hidden'); const resourceTreeModel = SDK.targetManager.models(SDK.ResourceTreeModel)[0]; if (resourceTreeModel) { resourceTreeModel.once(SDK.ResourceTreeModel.Events.MainFrameNavigated) .then(() => reloadWarning.classList.add('hidden')); } } } }; /** @enum {string} */ Emulation.SensorsView.DeviceOrientationModificationSource = { UserInput: 'userInput', UserDrag: 'userDrag', ResetButton: 'resetButton', SelectPreset: 'selectPreset' }; /** {string} */ Emulation.SensorsView.NonPresetOptions = { 'NoOverride': 'noOverride', 'Custom': 'custom', 'Unavailable': 'unavailable' }; /** @type {!Array.<{title: string, value: !Array.<{title: string, location: string}>}>} */ Emulation.SensorsView.PresetLocations = [ { title: 'Presets', value: [ {title: Common.UIString('Berlin'), location: '[52.520007, 13.404954]'}, {title: Common.UIString('London'), location: '[51.507351, -0.127758]'}, {title: Common.UIString('Moscow'), location: '[55.755826, 37.617300]'}, {title: Common.UIString('Mountain View'), location: '[37.386052, -122.083851]'}, {title: Common.UIString('Mumbai'), location: '[19.075984, 72.877656]'}, {title: Common.UIString('San Francisco'), location: '[37.774929, -122.419416]'}, {title: Common.UIString('Shanghai'), location: '[31.230416, 121.473701]'}, {title: Common.UIString('São Paulo'), location: '[-23.550520, -46.633309]'}, {title: Common.UIString('Tokyo'), location: '[35.689487, 139.691706]'}, ] }, { title: 'Error', value: [ {title: Common.UIString('Location unavailable'), location: Emulation.SensorsView.NonPresetOptions.Unavailable} ] } ]; /** @type {!Array.<{title: string, value: !Array.<{title: string, orientation: string}>}>} */ Emulation.SensorsView.PresetOrientations = [{ title: 'Presets', value: [ {title: Common.UIString('Portrait'), orientation: '[0, 90, 0]'}, {title: Common.UIString('Portrait upside down'), orientation: '[180, -90, 0]'}, {title: Common.UIString('Landscape left'), orientation: '[0, 90, -90]'}, {title: Common.UIString('Landscape right'), orientation: '[0, 90, 90]'}, {title: Common.UIString('Display up'), orientation: '[0, 0, 0]'}, {title: Common.UIString('Display down'), orientation: '[0, 180, 0]'} ] }]; /** * @implements {UI.ActionDelegate} * @unrestricted */ Emulation.SensorsView.ShowActionDelegate = class { /** * @override * @param {!UI.Context} context * @param {string} actionId * @return {boolean} */ handleAction(context, actionId) { UI.viewManager.showView('sensors'); return true; } }; Emulation.SensorsView.ShiftDragOrientationSpeed = 16;