diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b12e53f4a..0a35bf39d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,3 +13,8 @@ jobs: npm install npm run compile npm run test + - name: tests + working-directory: ./tests + run: | + npm install + npm run test diff --git a/.gitignore b/.gitignore index 85835b6ea..94f81056a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ -/node_modules +node_modules /.idea /*.iml /dist /public -/package-lock.json -/yarn.lock -/yarn-error.log +package-lock.json +yarn.lock +yarn-error.log /three-examples diff --git a/docs/guide/adapters/README.md b/docs/guide/adapters/README.md index 2787f6a1c..7a9ffa2e1 100644 --- a/docs/guide/adapters/README.md +++ b/docs/guide/adapters/README.md @@ -38,7 +38,7 @@ new PhotoSphereViewer.Viewer({ ::: tab ES import ```js -import CubemapAdapter from 'photo-sphere-viewer/dist/adapters/cubemap'; +import { CubemapAdapter } from 'photo-sphere-viewer/dist/adapters/cubemap'; new Viewer({ adapter: [CubemapAdapter, { diff --git a/docs/plugins/README.md b/docs/plugins/README.md index 433c2ba52..b1a54dcd2 100644 --- a/docs/plugins/README.md +++ b/docs/plugins/README.md @@ -24,7 +24,7 @@ Official plugins (listed on the left menu) are available in the the main `photo- Import `photo-sphere-viewer/dist/plugins/markers.css` with the prefered way depending on your tooling. ```js -import MarkersPlugins from 'photo-sphere-viewer/dist/plugins/markers'; +import { MarkersPlugins } from 'photo-sphere-viewer/dist/plugins/markers'; ``` ::: diff --git a/docs/plugins/writing-a-plugin.md b/docs/plugins/writing-a-plugin.md index eb26a7b69..0288a81d4 100644 --- a/docs/plugins/writing-a-plugin.md +++ b/docs/plugins/writing-a-plugin.md @@ -19,7 +19,7 @@ Your plugin is also an [`EventEmitter`](https://github.com/mistic100/uEvent) wit ```js import { AbstractPlugin } from 'photo-sphere-viewer'; -export default class PhotoSphereViewerCustomPlugin extends AbstractPlugin { +export class PhotoSphereViewerCustomPlugin extends AbstractPlugin { static id = 'custom-plugin'; @@ -68,7 +68,8 @@ export default { ], plugins : [ require('rollup-plugin-babel')({ - exclude: 'node_modules/**', + exclude : 'node_modules/**', + babelHelpers: 'bundled', }), ], }; @@ -192,7 +193,7 @@ A plugin can expose one or more settings to the viewer by using the [Settings pl This is done by requiring the settings plugin and calling the `addSetting` method. Consult the [Settings plugin](./plugin-settings.md) page for more information. ```js -import SettingsPlugin from 'photo-sphere-viewer/plugins/settings'; +import { SettingsPlugin } from 'photo-sphere-viewer/dist/plugins/settings'; export default class PhotoSphereViewerCustomPlugin extends AbstractPlugin { @@ -227,6 +228,23 @@ export default class PhotoSphereViewerCustomPlugin extends AbstractPlugin { } ``` +For this two work you will need two modifications in your rollup configuration: + +```js +// rollup.config.js + +export default { + output : { + globals : { + 'photo-sphere-viewer/dist/plugins/settings': 'PhotoSphereViewer.SettingsPlugin', + }, + }, + external: [ + 'photo-sphere-viewer/dist/plugins/settings', + ], +}; +``` + ## Naming and publishing diff --git a/package.json b/package.json index dc5c53b06..b86783eff 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,11 @@ "description": "A JavaScript library to display Photo Sphere panoramas", "homepage": "https://photo-sphere-viewer.js.org", "main": "dist/photo-sphere-viewer.js", + "types": "dist/photo-sphere-viewer.d.ts", "files": [ "src/", - "dist/" + "dist/", + "types/" ], "authors": [ { @@ -32,7 +34,7 @@ }, "dependencies": { "three": "^0.131.2", - "uevent": "~2.0.0" + "uevent": "~2.0.1" }, "devDependencies": { "@babel/core": "^7.7.4", @@ -42,7 +44,6 @@ "@babel/register": "^7.7.4", "@csstools/postcss-sass": "https://github.com/sinankeskin/postcss-sass#master", "@pixi/jsdoc-template": "^2.5.1", - "@rollup/plugin-alias": "^3.0.1", "@rollup/plugin-babel": "^5.2.2", "@rollup/plugin-replace": "^3.0.0", "@vuepress/plugin-active-header-links": "^1.2.0", @@ -68,6 +69,7 @@ "promise-polyfill": "^8.1.3", "raw-loader": "^4.0.0", "rollup": "^2.36.2", + "rollup-plugin-dts": "^3.0.2", "rollup-plugin-local-resolve": "^1.0.7", "rollup-plugin-postcss": "^4.0.0", "rollup-plugin-string": "^3.0.0", diff --git a/rollup.config.js b/rollup.config.js index 10968d21d..70a03248f 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,19 +1,19 @@ -import alias from '@rollup/plugin-alias'; import babel from '@rollup/plugin-babel'; import replace from '@rollup/plugin-replace'; import fs from 'fs'; import path from 'path'; import localResolve from 'rollup-plugin-local-resolve'; import postcss from 'rollup-plugin-postcss' +import dts from 'rollup-plugin-dts'; import { string } from 'rollup-plugin-string'; import pkg from './package.json'; const plugins = fs.readdirSync(path.join(__dirname, 'src/plugins')) - .filter(p => p !== 'AbstractPlugin.js'); + .filter(p => fs.lstatSync(`src/plugins/${p}`).isDirectory()); const adapters = fs.readdirSync(path.join(__dirname, 'src/adapters')) - .filter(p => p !== 'AbstractAdapter.js') + .filter(p => fs.lstatSync(`src/adapters/${p}`).isDirectory()) .filter(p => p !== 'equirectangular'); const banner = `/*! @@ -49,8 +49,6 @@ const baseConfig = { 'three', 'uevent', ], - // wrapped in a function to ensure unique plugin instances for each entry-point - // https://github.com/egoist/rollup-plugin-postcss/issues/158 plugins : () => [ localResolve(), babel({ @@ -95,21 +93,46 @@ const secondaryConfig = { 'photo-sphere-viewer', ...plugins.map(p => `photo-sphere-viewer/dist/plugins/${p}`), ], - plugins: () => [ - ...baseConfig.plugins(), - alias({ - 'photo-sphere-viewer': './src', - ...plugins.reduce((alias, p) => { - alias[`photo-sphere-viewer/dist/plugins/${p}`] = `./src/plugins/${p}`; - return alias; - }, {}), - }), replace({ - // configuration to embed the examples files in PSV source delimiters : ['', ''], + preventAssignment : true, [`from 'three/examples/jsm`]: `from '../../../three-examples`, + [`from '../..'`] : `from 'photo-sphere-viewer'`, + ...plugins.reduce((replace, p) => { + replace[`from '../${p}'`] = `from 'photo-sphere-viewer/dist/plugins/${p}'`; + return replace; + }, {}), }), + ...baseConfig.plugins(), + ], +}; + +const baseConfigDTS = { + output : { + format: 'es', + }, + plugins: () => [ + dts(), + ], +}; + +const secondaryConfigDTS = { + ...baseConfigDTS, + external: [ + ...secondaryConfig.external, + ], + plugins : () => [ + replace({ + delimiters : ['', ''], + preventAssignment: true, + [`from '../..'`] : `from 'photo-sphere-viewer'`, + ...plugins.reduce((replace, p) => { + replace[`from '../${p}'`] = `from 'photo-sphere-viewer/dist/plugins/${p}'`; + return replace; + }, {}), + }), + ...baseConfigDTS.plugins(), ], }; @@ -134,8 +157,16 @@ export default [ }, plugins: secondaryConfig.plugins(), }, -].concat( - plugins.map(p => ({ + { + ...baseConfigDTS, + input : 'types/index.d.ts', + output : { + ...baseConfigDTS.output, + file: 'dist/photo-sphere-viewer.d.ts', + }, + plugins: baseConfigDTS.plugins(), + }, + ...plugins.map(p => ({ ...secondaryConfig, input : `src/plugins/${p}/index.js`, output : { @@ -144,9 +175,8 @@ export default [ name: `PhotoSphereViewer.${camelize(p)}Plugin`, }, plugins: secondaryConfig.plugins(), - })) -).concat( - adapters.map(p => ({ + })), + ...adapters.map(p => ({ ...secondaryConfig, input : `src/adapters/${p}/index.js`, output : { @@ -155,5 +185,23 @@ export default [ name: `PhotoSphereViewer.${camelize(p)}Adapter`, }, plugins: secondaryConfig.plugins(), + })), + ...plugins.map(p => ({ + ...secondaryConfigDTS, + input : `types/plugins/${p}/index.d.ts`, + output : { + ...secondaryConfigDTS.output, + file: `dist/plugins/${p}.d.ts`, + }, + plugins: secondaryConfigDTS.plugins(), + })), + ...adapters.map(p => ({ + ...secondaryConfigDTS, + input : `types/adapters/${p}/index.d.ts`, + output : { + ...secondaryConfigDTS.output, + file: `dist/adapters/${p}.d.ts`, + }, + plugins: secondaryConfigDTS.plugins(), })) -); +]; diff --git a/src/Viewer.js b/src/Viewer.js index 7303372f0..1389a7471 100644 --- a/src/Viewer.js +++ b/src/Viewer.js @@ -10,6 +10,7 @@ import { CONFIG_PARSERS, DEFAULTS, DEPRECATED_OPTIONS, getConfig, READONLY_OPTIO import { CHANGE_EVENTS, EVENTS, IDS, SPHERE_RADIUS, VIEWER_DATA } from './data/constants'; import { SYSTEM } from './data/system'; import errorIcon from './icons/error.svg'; +import { AbstractPlugin } from './plugins/AbstractPlugin'; import { PSVError } from './PSVError'; import { DataHelper } from './services/DataHelper'; import { EventsHandler } from './services/EventsHandler'; @@ -24,6 +25,7 @@ import { isExtendedPosition, isFullscreenEnabled, logWarn, + pluginInterop, requestFullscreen, throttle, toggleClass @@ -359,7 +361,13 @@ export class Viewer extends EventEmitter { * @returns {PSV.plugins.AbstractPlugin} */ getPlugin(pluginId) { - return pluginId ? this.plugins[typeof pluginId === 'function' ? pluginId.id : pluginId] : null; + if (typeof pluginId === 'string') { + return this.plugins[pluginId]; + } + else { + const pluginCtor = pluginInterop(pluginId, AbstractPlugin); + return pluginCtor ? this.plugins[pluginCtor.id] : undefined; + } } /** diff --git a/src/adapters/AbstractAdapter.js b/src/adapters/AbstractAdapter.js index 42f8821bd..0f7dc5985 100644 --- a/src/adapters/AbstractAdapter.js +++ b/src/adapters/AbstractAdapter.js @@ -40,7 +40,7 @@ export class AbstractAdapter { } /** - * @summary Destroys the service + * @summary Destroys the adapter */ destroy() { delete this.psv; diff --git a/src/adapters/cubemap/index.js b/src/adapters/cubemap/index.js index ec86aca10..e09cde046 100644 --- a/src/adapters/cubemap/index.js +++ b/src/adapters/cubemap/index.js @@ -1,5 +1,5 @@ -import { AbstractAdapter, CONSTANTS, PSVError, SYSTEM, utils } from 'photo-sphere-viewer'; import * as THREE from 'three'; +import { AbstractAdapter, CONSTANTS, PSVError, SYSTEM, utils } from '../..'; /** * @typedef {Object} PSV.adapters.CubemapAdapter.Cubemap @@ -20,7 +20,7 @@ const CUBE_HASHMAP = ['left', 'right', 'top', 'bottom', 'back', 'front']; * @summary Adapter for cubemaps * @memberof PSV.adapters */ -export default class CubemapAdapter extends AbstractAdapter { +export class CubemapAdapter extends AbstractAdapter { static id = 'cubemap'; static supportsTransition = true; diff --git a/src/adapters/equirectangular-tiles/index.js b/src/adapters/equirectangular-tiles/index.js index 75346920b..eacab3447 100644 --- a/src/adapters/equirectangular-tiles/index.js +++ b/src/adapters/equirectangular-tiles/index.js @@ -1,5 +1,5 @@ -import { AbstractAdapter, CONSTANTS, PSVError, SYSTEM, utils } from 'photo-sphere-viewer'; import * as THREE from 'three'; +import { AbstractAdapter, CONSTANTS, PSVError, SYSTEM, utils } from '../..'; import { Queue } from './Queue'; import { Task } from './Task'; @@ -54,7 +54,7 @@ function powerOfTwo(x) { * @summary Adapter for tiled panoramas * @memberof PSV.adapters */ -export default class EquirectangularTilesAdapter extends AbstractAdapter { +export class EquirectangularTilesAdapter extends AbstractAdapter { static id = 'equirectangular-tiles'; static supportsTransition = false; diff --git a/src/adapters/equirectangular/index.js b/src/adapters/equirectangular/index.js index e0f260467..7c336bf68 100644 --- a/src/adapters/equirectangular/index.js +++ b/src/adapters/equirectangular/index.js @@ -11,7 +11,7 @@ const SPHERE_SEGMENTS = 64; * @summary Adapter for equirectangular panoramas * @memberof PSV.adapters */ -export default class EquirectangularAdapter extends AbstractAdapter { +export class EquirectangularAdapter extends AbstractAdapter { static id = 'equirectangular'; static supportsTransition = true; diff --git a/src/data/config.js b/src/data/config.js index 1e61cb852..b4303a727 100644 --- a/src/data/config.js +++ b/src/data/config.js @@ -1,6 +1,8 @@ +import { AbstractAdapter } from '../adapters/AbstractAdapter'; +import { EquirectangularAdapter } from '../adapters/equirectangular'; +import { AbstractPlugin } from '../plugins/AbstractPlugin'; import { PSVError } from '../PSVError'; -import EquirectangularAdapter from '../adapters/equirectangular'; -import { bound, clone, deepmerge, each, logWarn, parseAngle, parseSpeed } from '../utils'; +import { bound, clone, deepmerge, each, logWarn, parseAngle, parseSpeed, pluginInterop } from '../utils'; import { ACTIONS } from './constants'; /** @@ -112,10 +114,10 @@ export const CONFIG_PARSERS = { return [EquirectangularAdapter]; } else if (Array.isArray(adapter)) { - return adapter; + return [pluginInterop(adapter[0], AbstractAdapter), adapter[1]]; } else { - return [adapter]; + return [pluginInterop(adapter, AbstractAdapter)]; } }, defaultLong : (defaultLong) => { @@ -187,10 +189,10 @@ export const CONFIG_PARSERS = { return plugins .map((plugin) => { if (Array.isArray(plugin)) { - return plugin; + return [pluginInterop(plugin[0], AbstractPlugin), plugin[1]]; } else { - return [plugin]; + return [pluginInterop(plugin, AbstractPlugin)]; } }) .filter(plugin => !!plugin[0]); diff --git a/src/index.js b/src/index.js index beb3de183..e53344825 100644 --- a/src/index.js +++ b/src/index.js @@ -106,10 +106,11 @@ export { */ /** - * @typedef {PSV.AnimateOptions} PSV.PanoramaOptions + * @typedef {PSV.ExtendedPosition} PSV.PanoramaOptions * @summary Object defining panorama and animation options * @property {boolean|number} [transition=1500] - duration of the transition between all and new panorama * @property {boolean} [showLoader=true] - show the loader + * @property {number} [zoom] - new zoom level between 0 and 100 * @property {PSV.SphereCorrection} [sphereCorrection] - new sphere correction to apply to the panorama * @property {PSV.PanoData | PSV.PanoDataProvider} [panoData] - new panorama data used for this panorama */ diff --git a/src/plugins/autorotate-keypoints/index.js b/src/plugins/autorotate-keypoints/index.js index 91edc4619..de7baf758 100644 --- a/src/plugins/autorotate-keypoints/index.js +++ b/src/plugins/autorotate-keypoints/index.js @@ -1,4 +1,4 @@ -import { AbstractPlugin, CONSTANTS, PSVError, utils } from 'photo-sphere-viewer'; +import { AbstractPlugin, CONSTANTS, PSVError, utils } from '../..'; /** * @typedef {PSV.ExtendedPosition|string|Object} PSV.plugins.AutorotateKeypointsPlugin.Keypoints @@ -31,7 +31,7 @@ const serializePt = position => [position.longitude, position.latitude]; * @extends PSV.plugins.AbstractPlugin * @memberof PSV.plugins */ -export default class AutorotateKeypointsPlugin extends AbstractPlugin { +export class AutorotateKeypointsPlugin extends AbstractPlugin { static id = 'autorotate-keypoints'; diff --git a/src/plugins/gyroscope/GyroscopeButton.js b/src/plugins/gyroscope/GyroscopeButton.js index 9b1dab6c4..03e7e1003 100644 --- a/src/plugins/gyroscope/GyroscopeButton.js +++ b/src/plugins/gyroscope/GyroscopeButton.js @@ -1,6 +1,6 @@ -import { AbstractButton } from 'photo-sphere-viewer'; +import { AbstractButton } from '../..'; import compass from './compass.svg'; -import GyroscopePlugin from './index'; +import { GyroscopePlugin } from './index'; /** * @summary Navigation bar gyroscope button class diff --git a/src/plugins/gyroscope/index.js b/src/plugins/gyroscope/index.js index c7981be61..d79dd5b0e 100644 --- a/src/plugins/gyroscope/index.js +++ b/src/plugins/gyroscope/index.js @@ -1,6 +1,6 @@ -import { AbstractPlugin, CONSTANTS, DEFAULTS, registerButton, utils } from 'photo-sphere-viewer'; import * as THREE from 'three'; import { DeviceOrientationControls } from 'three/examples/jsm/controls/DeviceOrientationControls'; +import { AbstractPlugin, CONSTANTS, DEFAULTS, registerButton, utils } from '../..'; import { GyroscopeButton } from './GyroscopeButton'; @@ -23,7 +23,7 @@ const direction = new THREE.Vector3(); * @extends PSV.plugins.AbstractPlugin * @memberof PSV.plugins */ -export default class GyroscopePlugin extends AbstractPlugin { +export class GyroscopePlugin extends AbstractPlugin { static id = 'gyroscope'; diff --git a/src/plugins/markers/Marker.js b/src/plugins/markers/Marker.js index e0b12bb4d..a679331bb 100644 --- a/src/plugins/markers/Marker.js +++ b/src/plugins/markers/Marker.js @@ -1,5 +1,5 @@ -import { CONSTANTS, PSVError, utils } from 'photo-sphere-viewer'; -import MarkersPlugin from './index'; +import { CONSTANTS, PSVError, utils } from '../..'; +import { MarkersPlugin } from './index'; import { getPolygonCenter, getPolylineCenter } from './utils'; /** diff --git a/src/plugins/markers/MarkersButton.js b/src/plugins/markers/MarkersButton.js index 692ed7297..4fa98c657 100644 --- a/src/plugins/markers/MarkersButton.js +++ b/src/plugins/markers/MarkersButton.js @@ -1,5 +1,5 @@ -import { AbstractButton } from 'photo-sphere-viewer'; -import MarkersPlugin from './index'; +import { AbstractButton } from '../..'; +import { MarkersPlugin } from './index'; import pin from './pin.svg'; /** diff --git a/src/plugins/markers/MarkersListButton.js b/src/plugins/markers/MarkersListButton.js index 4ab9edfb9..2f95d2479 100644 --- a/src/plugins/markers/MarkersListButton.js +++ b/src/plugins/markers/MarkersListButton.js @@ -1,5 +1,5 @@ -import { AbstractButton, CONSTANTS } from 'photo-sphere-viewer'; -import MarkersPlugin from './index'; +import { AbstractButton, CONSTANTS } from '../..'; +import { MarkersPlugin } from './index'; import pinList from './pin-list.svg'; /** diff --git a/src/plugins/markers/index.js b/src/plugins/markers/index.js index 07732a086..51921f69e 100644 --- a/src/plugins/markers/index.js +++ b/src/plugins/markers/index.js @@ -1,5 +1,5 @@ -import { AbstractPlugin, CONSTANTS, DEFAULTS, PSVError, registerButton, utils } from 'photo-sphere-viewer'; import * as THREE from 'three'; +import { AbstractPlugin, CONSTANTS, DEFAULTS, PSVError, registerButton, utils } from '../..'; import { Marker } from './Marker'; import { MarkersButton } from './MarkersButton'; import { MarkersListButton } from './MarkersListButton'; @@ -33,7 +33,7 @@ registerButton(MarkersListButton); * @extends PSV.plugins.AbstractPlugin * @memberof PSV.plugins */ -export default class MarkersPlugin extends AbstractPlugin { +export class MarkersPlugin extends AbstractPlugin { static id = 'markers'; diff --git a/src/plugins/markers/utils.js b/src/plugins/markers/utils.js index 731b7fc61..bbeedfaf2 100644 --- a/src/plugins/markers/utils.js +++ b/src/plugins/markers/utils.js @@ -1,4 +1,4 @@ -import { CONSTANTS, utils } from 'photo-sphere-viewer'; +import { CONSTANTS, utils } from '../..'; /** * Returns intermediary point between two points on the sphere diff --git a/src/plugins/resolution/index.js b/src/plugins/resolution/index.js index dbaebfbe9..0998c31eb 100644 --- a/src/plugins/resolution/index.js +++ b/src/plugins/resolution/index.js @@ -1,5 +1,5 @@ -import { AbstractPlugin, CONSTANTS, DEFAULTS, PSVError } from 'photo-sphere-viewer'; -import SettingsPlugin from 'photo-sphere-viewer/dist/plugins/settings'; +import { AbstractPlugin, CONSTANTS, DEFAULTS, PSVError } from '../..'; +import { SettingsPlugin } from '../settings'; import { deepEqual } from './utils'; @@ -23,7 +23,7 @@ DEFAULTS.lang.resolution = 'Quality'; * @extends PSV.plugins.AbstractPlugin * @memberof PSV.plugins */ -export default class ResolutionPlugin extends AbstractPlugin { +export class ResolutionPlugin extends AbstractPlugin { static id = 'resolution'; diff --git a/src/plugins/settings/SettingsButton.js b/src/plugins/settings/SettingsButton.js index 4ca9bc11d..26f2681ec 100644 --- a/src/plugins/settings/SettingsButton.js +++ b/src/plugins/settings/SettingsButton.js @@ -1,5 +1,5 @@ -import { AbstractButton, CONSTANTS } from 'photo-sphere-viewer'; -import SettingsPlugin from './index'; +import { AbstractButton, CONSTANTS } from '../..'; +import { SettingsPlugin } from './index'; import icon from './settings.svg'; /** diff --git a/src/plugins/settings/index.js b/src/plugins/settings/index.js index 09b8fca96..4f3f29466 100644 --- a/src/plugins/settings/index.js +++ b/src/plugins/settings/index.js @@ -1,4 +1,4 @@ -import { AbstractPlugin, DEFAULTS, PSVError, registerButton, utils } from 'photo-sphere-viewer'; +import { AbstractPlugin, DEFAULTS, PSVError, registerButton, utils } from '../..'; import check from './check.svg'; import chevron from './chevron.svg'; import icon from './settings.svg'; @@ -50,7 +50,7 @@ registerButton(SettingsButton); * @extends PSV.plugins.AbstractPlugin * @memberof PSV.plugins */ -export default class SettingsPlugin extends AbstractPlugin { +export class SettingsPlugin extends AbstractPlugin { static id = 'settings'; diff --git a/src/plugins/stereo/StereoButton.js b/src/plugins/stereo/StereoButton.js index f6a7f0c21..64af7cd3e 100644 --- a/src/plugins/stereo/StereoButton.js +++ b/src/plugins/stereo/StereoButton.js @@ -1,5 +1,5 @@ -import { AbstractButton } from 'photo-sphere-viewer'; -import StereoPlugin from './index'; +import { AbstractButton } from '../..'; +import { StereoPlugin } from './index'; import stereo from './stereo.svg'; /** diff --git a/src/plugins/stereo/index.js b/src/plugins/stereo/index.js index bf39b1a61..91b198a4e 100644 --- a/src/plugins/stereo/index.js +++ b/src/plugins/stereo/index.js @@ -1,6 +1,6 @@ -import { AbstractPlugin, CONSTANTS, DEFAULTS, PSVError, registerButton, utils } from 'photo-sphere-viewer'; -import GyroscopePlugin from 'photo-sphere-viewer/dist/plugins/gyroscope'; import { StereoEffect } from 'three/examples/jsm/effects/StereoEffect'; +import { AbstractPlugin, CONSTANTS, DEFAULTS, PSVError, registerButton, utils } from '../..'; +import { GyroscopePlugin } from '../gyroscope'; import mobileRotateIcon from './mobile-rotate.svg'; import { StereoButton } from './StereoButton'; @@ -26,7 +26,7 @@ DEFAULTS.lang.pleaseRotate = ['Please rotate your device', '(or tap to continue) * @extends PSV.plugins.AbstractPlugin * @memberof PSV.plugins */ -export default class StereoPlugin extends AbstractPlugin { +export class StereoPlugin extends AbstractPlugin { static id = 'stereo'; diff --git a/src/plugins/visible-range/index.js b/src/plugins/visible-range/index.js index 51a12e6e8..e1065431b 100644 --- a/src/plugins/visible-range/index.js +++ b/src/plugins/visible-range/index.js @@ -1,5 +1,5 @@ -import { AbstractPlugin, utils, CONSTANTS, Animation } from 'photo-sphere-viewer'; import * as THREE from 'three'; +import { AbstractPlugin, Animation, CONSTANTS, utils } from '../..'; /** * @typedef {Object} PSV.plugins.VisibleRangePlugin.Options @@ -13,7 +13,7 @@ import * as THREE from 'three'; * @extends PSV.plugins.AbstractPlugin * @memberof PSV.plugins */ -export default class VisibleRangePlugin extends AbstractPlugin { +export class VisibleRangePlugin extends AbstractPlugin { static id = 'visible-range'; diff --git a/src/utils/math.js b/src/utils/math.js index b4323be2b..d99615885 100644 --- a/src/utils/math.js +++ b/src/utils/math.js @@ -83,7 +83,7 @@ export function getAngle(position1, position2) { } /** - * Returns the distance between two points on a sphere of radius one + * @summary Returns the distance between two points on a sphere of radius one * @memberOf PSV.utils * @param {number[]} p1 * @param {number[]} p2 diff --git a/src/utils/misc.js b/src/utils/misc.js index 9dde650a7..55ed11ed1 100644 --- a/src/utils/misc.js +++ b/src/utils/misc.js @@ -1,5 +1,5 @@ /** - * @summary Transforms a string to dash-case{@link https://github.com/shahata/dasherize} + * @summary Transforms a string to dash-case {@link https://github.com/shahata/dasherize} * @memberOf PSV.utils * @param {string} str * @returns {string} diff --git a/src/utils/psv.js b/src/utils/psv.js index 9cc9ea74f..bee87be0f 100644 --- a/src/utils/psv.js +++ b/src/utils/psv.js @@ -2,6 +2,23 @@ import * as THREE from 'three'; import { PSVError } from '../PSVError'; import { bound } from './math'; +/** + * @summary Returns the plugin constructor from the imported object + * For retrocompatibility with previous default exports + * @memberOf PSV.utils + * @package + */ +export function pluginInterop(plugin, target) { + if (plugin) { + for (const [, p] of [['_', plugin], ...Object.entries(plugin)]) { + if (p.prototype instanceof target) { + return p; + } + } + } + return null; +} + /** * @summary Displays a warning in the console * @memberOf PSV.utils @@ -66,7 +83,7 @@ const CSS_POSITIONS = { * @memberOf PSV.utils * @description The implementation is as close as possible to the "background-position" specification * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/background-position} - * @param {string|object} value + * @param {string|PSV.Point} value * @returns {PSV.Point} */ export function parsePosition(value) { diff --git a/tests/package.json b/tests/package.json new file mode 100644 index 000000000..171771ecc --- /dev/null +++ b/tests/package.json @@ -0,0 +1,13 @@ +{ + "name": "photo-sphere-viewer-tests", + "version": "1.0.0", + "license": "MIT", + "private": true, + "dependencies": { + "typescript": "^4.4.2" + }, + "scripts": { + "postinstall": "cpx \"../dist/**\" \"node_modules/photo-sphere-viewer/dist\" && cpx \"../package.json\" \"node_modules/photo-sphere-viewer\"", + "test": "tsc typescript/index.ts" + } +} diff --git a/tests/typescript/.gitignore b/tests/typescript/.gitignore new file mode 100644 index 000000000..a6c7c2852 --- /dev/null +++ b/tests/typescript/.gitignore @@ -0,0 +1 @@ +*.js diff --git a/tests/typescript/CustomPlugin.ts b/tests/typescript/CustomPlugin.ts new file mode 100644 index 000000000..7314512d6 --- /dev/null +++ b/tests/typescript/CustomPlugin.ts @@ -0,0 +1,15 @@ +import { AbstractPlugin, Viewer } from 'photo-sphere-viewer'; + +export class CustomPlugin extends AbstractPlugin { + + static id = 'custom'; + + constructor(psv: Viewer) { + super(psv); + } + + doSomething() { + + } + +} diff --git a/tests/typescript/index.ts b/tests/typescript/index.ts new file mode 100644 index 000000000..d86b82073 --- /dev/null +++ b/tests/typescript/index.ts @@ -0,0 +1,74 @@ +import { CONSTANTS, Viewer } from 'photo-sphere-viewer'; +import { EquirectangularTilesAdapter, EquirectangularTilesPanorama } from 'photo-sphere-viewer/dist/adapters/equirectangular-tiles'; +import { MarkersPlugin, MarkersPluginOptions } from 'photo-sphere-viewer/dist/plugins/markers'; +import { CustomPlugin } from './CustomPlugin'; + +const viewer = new Viewer({ + container: 'container', + adapter: EquirectangularTilesAdapter, + plugins: [ + [MarkersPlugin, { + clickEventOnMarker: true, + } as MarkersPluginOptions], + CustomPlugin, + ], +}); + +viewer.setPanorama({ + baseUrl: 'small.jpg', + width: 16000, + cols: 8, + rows: 4, +} as EquirectangularTilesPanorama, { + transition: false, +}) + .then(() => { + + }); + +viewer.animate({ + longitude: 0, + latitude: 0, + speed: '2rpm', +}) + .then(() => { + + }); + +viewer.zoom(50); + +viewer.setOption('useXmpData', true); + +viewer.setOptions({ + useXmpData: false, +}); + +viewer.navbar.setCaption('Test'); + +viewer.panel.show({ + content: 'Content', + clickHandler: (e: MouseEvent) => null, +}); + +viewer.once('ready', e => { + +}); + +viewer.on('position-updated', (e, position) => { + const longitude: number = position.longitude; +}); + +viewer.on(CONSTANTS.CHANGE_EVENTS.GET_ANIMATE_POSITION, (e, position) => { + return {longitude: position.longitude + 0.1, latitude: position.latitude + 0.1}; +}); + +const markers = viewer.getPlugin(MarkersPlugin); +markers.on('select-marker', (e, marker) => { + const markerId: string = marker.id; +}); + +const customPlugin = viewer.getPlugin(CustomPlugin); +customPlugin.doSomething(); + +const customPluginAgain = viewer.getPlugin('custom'); +customPluginAgain.doSomething(); diff --git a/types/Animation.d.ts b/types/Animation.d.ts new file mode 100644 index 000000000..af6f5cff0 --- /dev/null +++ b/types/Animation.d.ts @@ -0,0 +1,35 @@ +export type AnimationOptions = { + properties: { [K: string]: { start: number, end: number } }; + duration: number; + delay?: number; + easing?: string | ((progress: number) => number); + onTick: (properties: { [K: string]: number }, progress: number) => void; +}; + +/** + * @summary Interpolation helper for animations + * @description + * Implements the Promise API with an additional "cancel" and "finally" methods. + * The promise is resolved when the animation is complete and rejected if the animation is cancelled. + */ +export class Animation implements Promise { + + constructor(options: AnimationOptions); + + // @ts-ignore + then(onFulfilled?: (() => void | Animation | PromiseLike) | undefined | null, onRejected?: (() => void | Animation | PromiseLike) | undefined | null): Animation; + + // @ts-ignore + catch(onRejected?: (() => void | Animation | PromiseLike) | undefined | null): Animation; + + // @ts-ignore + finally(onFinally?: (() => void | Animation | PromiseLike) | undefined | null): Animation; + + cancel(); + + /** + * @summary Returns a resolved animation promise + */ + static resolve(): Animation; + +} diff --git a/types/PSVError.d.ts b/types/PSVError.d.ts new file mode 100644 index 000000000..7300a10b1 --- /dev/null +++ b/types/PSVError.d.ts @@ -0,0 +1,6 @@ +/** + * @summary Custom error used in the lib + */ +export class PSVError extends Error { + name: 'PSVError'; +} diff --git a/types/Viewer.d.ts b/types/Viewer.d.ts new file mode 100644 index 000000000..2542aeb05 --- /dev/null +++ b/types/Viewer.d.ts @@ -0,0 +1,392 @@ +import { Vector3 } from 'three'; +import { Event, EventEmitter } from 'uevent'; +import { AdapterConstructor } from './adapters/AbstractAdapter'; +import { Animation } from './Animation'; +import { Loader } from './components/Loader'; +import { Navbar } from './components/Navbar'; +import { Notification } from './components/Notification'; +import { Overlay } from './components/Overlay'; +import { Panel } from './components/Panel'; +import { Tooltip } from './components/Tooltip'; +import { + AnimateOptions, + ClickData, + CssSize, + ExtendedPosition, + NavbarCustomButton, + PanoData, + PanoDataProvider, + PanoramaOptions, + Position, + Size +} from './models'; +import { AbstractPlugin, PluginConstructor } from './plugins/AbstractPlugin'; +import { DataHelper } from './services/DataHelper'; +import { TextureLoader } from './services/TextureLoader'; +import { TooltipRenderer } from './services/TooltipRenderer'; + +/** + * @summary Viewer options, see {@link http://photo-sphere-viewer.js.org/guide/config.html} + */ +export type ViewerOptions = { + container: HTMLElement | string; + panorama?: string; + adapter?: AdapterConstructor | [AdapterConstructor, any]; + caption?: string; + loadingImg?: string; + loadingTxt?: string; + size?: Size; + fisheye?: boolean; + minFov?: number; + maxFov?: number; + defaultZoomLvl?: number; + defaultLong?: number; + defaultLat?: number; + sphereCorrection?: { pan?: number, tilt?: number, roll?: number }, + sphereCorrectionReorder?: boolean; + moveSpeed?: number; + zoomSpeed?: number; + autorotateDelay?: null, + autorotateSpeed?: string | number; + autorotateLat?: number; + moveInertia?: boolean; + mousewheel?: boolean; + mousemove?: boolean; + captureCursor?: boolean; + mousewheelCtrlKey?: boolean; + touchmoveTwoFingers?: boolean; + useXmpData?: boolean; + panoData?: PanoData | PanoDataProvider; + requestHeaders?: Record | ((url: string) => Record); + canvasBackground?: string; + withCredentials?: boolean; + navbar?: string | Array, + lang?: Record, + keyboard?: Record, + plugins?: Array | [PluginConstructor, any]>, +}; + +/** + * Internal properties of the viewer + */ +export type ViewerProps = { + ready: boolean; + uiRefresh: boolean; + needsUpdate: boolean; + fullscreen: boolean; + direction: Vector3; + vFov: number; + hFov: number; + aspect: number; + autorotateEnabled: boolean; + animationPromise: Animation; + loadingPromise: Promise; + startTimeout: any; + size: Size; + panoData: PanoData; +}; + +/** + * Main class + */ +export class Viewer extends EventEmitter { + + /** + * Configuration holder + */ + readonly config: ViewerOptions; + + /** + * Internal properties + */ + prop: ViewerProps; + + /** + * Top most parent + */ + readonly parent: HTMLElement; + + /** + * Main container + */ + readonly container: HTMLElement; + + /** + * Textures loader + */ + readonly textureLoader: TextureLoader; + + /** + * Utilities to help converting data + */ + readonly dataHelper: DataHelper; + + readonly loader: Loader; + + readonly navbar: Navbar; + + readonly panel: Panel; + + readonly tooltip: TooltipRenderer; + + readonly notification: Notification; + + readonly overlay: Overlay; + + /** + * @throws {PSVError} when the configuration is incorrect + */ + constructor(options: ViewerOptions); + + /** + * @summary Destroys the viewer + * @description The memory used by the ThreeJS context is not totally cleared. This will be fixed as soon as possible. + */ + destroy(); + + /** + * @summary Returns the instance of a plugin if it exists + */ + getPlugin(pluginId: string | PluginConstructor): T; + + /** + * @summary Returns the current position of the camera + */ + getPosition(): Position; + + /** + * @summary Returns the current zoom level + */ + getZoomLevel(): number; + + /** + * @summary Returns the current viewer size + */ + getSize(): Size; + + /** + * @summary Checks if the automatic rotation is enabled + */ + isAutorotateEnabled(): boolean; + + /** + * @summary Checks if the viewer is in fullscreen + */ + isFullscreenEnabled(): boolean; + + /** + * @summary Flags the view has changed for the next render + */ + needsUpdate(); + + /** + * @summary Resizes the canvas when the window is resized + */ + autoSize(); + + /** + * @summary Loads a new panorama file + * @description Loads a new panorama file, optionally changing the camera position/zoom and activating the transition animation.
+ * If the "options" parameter is not defined, the camera will not move and the ongoing animation will continue.
+ * If another loading is already in progress it will be aborted. + */ + setPanorama(panorama: any, options?: PanoramaOptions): Promise; + + /** + * @summary Update options + */ + setOptions(options: Partial); + + /** + * @summary Update options + */ + setOption(option: K, value: ViewerOptions[K]); + + /** + * @summary Starts the automatic rotation + */ + startAutorotate(); + + /** + * @summary Stops the automatic rotation + */ + stopAutorotate(); + + /** + * @summary Starts or stops the automatic rotation + */ + toggleAutorotate(); + + /** + * @summary Displays an error message + */ + showError(message: string); + + /** + * @summary Hides the error message + */ + hideError(); + + /** + * @summary Rotates the view to specific longitude and latitude + */ + rotate(position: ExtendedPosition); + + /** + * @summary Rotates and zooms the view with a smooth animation + */ + animate(options: AnimateOptions): Animation; + + /** + * @summary Stops the ongoing animation + * @description The return value is a Promise because the is no guaranty the animation can be stopped synchronously. + */ + stopAnimation(): Promise; + + /** + * @summary Zooms to a specific level between `max_fov` and `min_fov` + */ + zoom(level: number); + + /** + * @summary Increases the zoom level + * @param {number} [step=1] + */ + zoomIn(step?: number); + + /** + * @summary Decreases the zoom level + * @param {number} [step=1] + */ + zoomOut(step?: number); + + /** + * @summary Resizes the viewer + */ + resize(size: CssSize); + + /** + * @summary Enters the fullscreen mode + */ + enterFullscreen(); + + /** + * @summary Exits the fullscreen mode + */ + exitFullscreen(); + + /** + * @summary Enters or exits the fullscreen mode + */ + toggleFullscreen(); + + /** + * @summary Enables the keyboard controls (done automatically when entering fullscreen) + */ + startKeyboardControl(); + + /** + * @summary Disables the keyboard controls (done automatically when exiting fullscreen) + */ + stopKeyboardControl(); + + /** + * @summary Triggered when the automatic rotation is enabled/disabled + */ + on(e: 'autorotate', cb: (e: Event, enabled: true) => void): this; + /** + * @summary Triggered before a render, used to modify the view + */ + on(e: 'before-render', cb: (e: Event, timestamp: number, elapsed: number) => void): this; + /** + * @summary Triggered before a rotate operation, can be cancelled + */ + on(e: 'before-rotate', cb: (e: Event, position: ExtendedPosition) => void): this; + /** + * @summary Triggered when the user clicks on the viewer (everywhere excluding the navbar and the side panel) + */ + on(e: 'click', cb: (e: Event, data: ClickData) => void): this; + /** + * @summary Trigered when the panel is closed + */ + on(e: 'close-panel', cb: (e: Event, id: string | undefined) => void): this; + /** + * @summary Triggered after a call to setOption/setOptions + */ + on(e: 'config-changed', cb: (e: Event, options: string[]) => void): this; + /** + * @summary Triggered when the user double clicks on the viewer. The simple `click` event is always fired before `dblclick` + */ + on(e: 'dblclick', cb: (e: Event, data: ClickData) => void): this; + /** + * @summary Triggered when the fullscreen mode is enabled/disabled + */ + on(e: 'fullscreen-updated', cb: (e: Event, enabled: true) => void): this; + /** + * @summary Called to alter the target position of an animation + */ + on(e: 'get-animate-position', cb: (e: Event, position: Position) => Position): this; + /** + * @summary Called to alter the target position of a rotation + */ + on(e: 'get-rotate-position', cb: (e: Event, position: Position) => Position): this; + /** + * @summary Triggered when the notification is hidden + */ + on(e: 'hide-notification', cb: (e: Event) => void): this; + /** + * @summary Triggered when the overlay is hidden + */ + on(e: 'hide-overlay', cb: (e: Event, id: string | undefined) => void): this; + /** + * @summary Trigered when the tooltip is hidden + */ + on(e: 'hide-tooltip', cb: (e: Event, data: any) => void): this; + /** + * @summary Triggered when the panel is opened + */ + on(e: 'open-panel', cb: (e: Event, id: string | undefined) => void): this; + /** + * @summary Triggered when a panorama image has been loaded + */ + on(e: 'panorama-loaded', cb: (e: Event) => void): this; + /** + * @summary Triggered when the view longitude and/or latitude changes + */ + on(e: 'position-updated', cb: (e: Event, position: Position) => void): this; + + /** + * @summary Triggered when the panorama image has been loaded and the viewer is ready to perform the first render + */ + once(e: 'ready', cb: (e: Event) => void): this; + + /** + * @summary Triggered on each viewer render, **this event is triggered very often** + */ + on(e: 'render', cb: (e: Event) => void): this; + /** + * @summary Trigered when the notification is shown + */ + on(e: 'show-notification', cb: (e: Event) => void): this; + /** + * @summary Trigered when the overlay is shown + */ + on(e: 'show-overlay', cb: (e: Event, id: string | undefined) => void): this; + /** + * @summary Trigered when the tooltip is shown + */ + on(e: 'show-tooltip', cb: (e: Event, data: any, tooltip: Tooltip) => void): this; + /** + * @summary Triggered when the viewer size changes + */ + on(e: 'size-updated', cb: (e: Event, size: Size) => void): this; + /** + * @summary Triggered when all current animations are stopped + */ + on(e: 'stop-all', cb: (e: Event) => void): this; + /** + * @summary Triggered when the zoom level changes + */ + on(e: 'zoom-updated', cb: (e: Event, zoom: number) => void): this; + +} diff --git a/types/adapters/AbstractAdapter.d.ts b/types/adapters/AbstractAdapter.d.ts new file mode 100644 index 000000000..ccea98e2f --- /dev/null +++ b/types/adapters/AbstractAdapter.d.ts @@ -0,0 +1,50 @@ +import { Mesh } from 'three'; +import { PanoData, PanoDataProvider, TextureData } from '../models'; +import { Viewer } from '../Viewer'; + +/** + * @summary Base adapters class + */ +export abstract class AbstractAdapter { + + /** + * @summary Unique identifier of the adapter + */ + static id: string; + + /** + * @summary Indicates if the adapter supports transitions between panoramas + */ + static supportsTransition: boolean; + + constructor(parent: Viewer); + + /** + * @summary Destroys the adapter + */ + destroy(); + + /** + * @summary Loads the panorama texture(s) + */ + loadTexture(panorama: any, newPanoData?: PanoData | PanoDataProvider): Promise; + + /** + * @summary Creates the cube mesh + * @param {number} [scale=1] + */ + createMesh(scale?: number): Mesh; + + /** + * @summary Applies the texture to the mesh + */ + setTexture(mesh: Mesh, textureData: TextureData); + + /** + * @summary Changes the opacity of the mesh + */ + setTextureOpacity(mesh: Mesh, opacity: number); + +} + +export type AdapterConstructor = new (psv: Viewer, options?: any) => T; diff --git a/types/adapters/cubemap/index.d.ts b/types/adapters/cubemap/index.d.ts new file mode 100644 index 000000000..e6df849d5 --- /dev/null +++ b/types/adapters/cubemap/index.d.ts @@ -0,0 +1,22 @@ +import { AbstractAdapter, TextureData } from '../..'; + +/** + * @summary Object defining a cubemap + */ +export type Cubemap = string[6] | { + top: string; + right: string; + bottom: string; + left: string; + front: string; + back: string; +}; + +/** + * @summary Adapter for cubemaps + */ +export class CubemapAdapter extends AbstractAdapter { + + loadTexture(panorama: Cubemap): Promise; + +} diff --git a/types/adapters/equirectangular-tiles/index.d.ts b/types/adapters/equirectangular-tiles/index.d.ts new file mode 100644 index 000000000..b68f619fa --- /dev/null +++ b/types/adapters/equirectangular-tiles/index.d.ts @@ -0,0 +1,28 @@ +import { AbstractAdapter, TextureData, Viewer } from '../..'; + +/** + * @summary Configuration of a tiled panorama + */ +export type EquirectangularTilesPanorama = { + baseUrl?: string; + width: number; + cols: number; + rows: number; + tileUrl: (col: number, row: number) => string; +}; + +export type EquirectangularTilesAdapterOptions = { + showErrorTile?: boolean; + baseBlur?: boolean; +} + +/** + * @summary Adapter for tiled panoramas + */ +export class EquirectangularTilesAdapter extends AbstractAdapter { + + constructor(psv: Viewer, options: EquirectangularTilesAdapterOptions); + + loadTexture(panorama: EquirectangularTilesPanorama): Promise; + +} diff --git a/types/adapters/equirectangular/index.d.ts b/types/adapters/equirectangular/index.d.ts new file mode 100644 index 000000000..6fbc26138 --- /dev/null +++ b/types/adapters/equirectangular/index.d.ts @@ -0,0 +1,10 @@ +import { AbstractAdapter, PanoData, PanoDataProvider, TextureData } from '../..'; + +/** + * @summary Adapter for equirectangular panoramas + */ +export class EquirectangularAdapter extends AbstractAdapter { + + loadTexture(panorama: string, newPanoData?: PanoData | PanoDataProvider): Promise; + +} diff --git a/types/buttons/AbstractButton.d.ts b/types/buttons/AbstractButton.d.ts new file mode 100644 index 000000000..aa643ae00 --- /dev/null +++ b/types/buttons/AbstractButton.d.ts @@ -0,0 +1,56 @@ +import { AbstractComponent } from '../components/AbstractComponent'; +import { Navbar } from '../components/Navbar'; + +/** + * @summary Base navbar button class + */ +export abstract class AbstractButton extends AbstractComponent { + + /** + * @summary Unique identifier of the button + */ + static id: string; + + /** + * @summary SVG icon name injected in the button + */ + static icon?: string; + + /** + * @summary SVG icon name injected in the button when it is active + */ + static iconActive?: string; + + constructor(navbar: Navbar, className?: string, collapsable?: boolean); + + /** + * @summary Checks if the button can be displayed + */ + isSupported(): boolean | { initial: boolean, promise: Promise }; + + /** + * @summary Changes the active state of the button + */ + toggleActive(active?: boolean); + + /** + * @summary Disables the button + */ + disable(); + + /** + * @summary Enables the button + */ + enable(); + + /** + * @summary Collapses the button in the navbar menu + */ + collapse(); + + /** + * @summary Uncollapses the button from the navbar menu + */ + uncollapse(); + +} diff --git a/types/components/AbstractComponent.d.ts b/types/components/AbstractComponent.d.ts new file mode 100644 index 000000000..6c57b28b4 --- /dev/null +++ b/types/components/AbstractComponent.d.ts @@ -0,0 +1,30 @@ +import { Viewer } from '../Viewer'; + +/** + * @summary Base component class + */ +export abstract class AbstractComponent { + + constructor(parent: Viewer | AbstractComponent, className?: string); + + /** + * @summary Displays the component + */ + show(options?: any); + + /** + * @summary Hides the component + */ + hide(options?: any); + + /** + * @summary Displays or hides the component + */ + toggle(); + + /** + * @summary Check if the component is visible + */ + isVisible(options?: any): boolean; + +} diff --git a/types/components/Loader.d.ts b/types/components/Loader.d.ts new file mode 100644 index 000000000..6b2186a5b --- /dev/null +++ b/types/components/Loader.d.ts @@ -0,0 +1,14 @@ +import { AbstractComponent } from './AbstractComponent'; + +/** + * @summary Loader class + */ +export class Loader extends AbstractComponent { + + /** + * @summary Sets the loader progression + * @param value + */ + setProgress(value: number); + +} diff --git a/types/components/Navbar.d.ts b/types/components/Navbar.d.ts new file mode 100644 index 000000000..2a45e21ff --- /dev/null +++ b/types/components/Navbar.d.ts @@ -0,0 +1,30 @@ +import { AbstractButton } from '../buttons/AbstractButton'; +import { NavbarCustomButton } from '../models'; +import { AbstractComponent } from './AbstractComponent'; + +/** + * @summary Register a new button available for all viewers + */ +export function registerButton(button: typeof AbstractButton); + +/** + * @summary Navigation bar class + */ +export class Navbar extends AbstractComponent { + + /** + * @summary Change the buttons visible on the navbar + */ + setButtons(buttons: string | Array); + + /** + * @summary Sets the bar caption + */ + setCaption(html: string); + + /** + * @summary Returns a button by its identifier + */ + getButton(id: string): AbstractButton; + +} diff --git a/types/components/Notification.d.ts b/types/components/Notification.d.ts new file mode 100644 index 000000000..6b040321a --- /dev/null +++ b/types/components/Notification.d.ts @@ -0,0 +1,15 @@ +import { AbstractComponent } from './AbstractComponent'; + +export type NotificationOptions = { + content: string; + timeout?: number; +}; + +/** + * @summary Notification class + */ +export class Notification extends AbstractComponent { + + show(config: string | NotificationOptions); + +} diff --git a/types/components/Overlay.d.ts b/types/components/Overlay.d.ts new file mode 100644 index 000000000..3701e0d08 --- /dev/null +++ b/types/components/Overlay.d.ts @@ -0,0 +1,22 @@ +import { AbstractComponent } from './AbstractComponent'; + +export type OverlayOptions = { + id?: string; + image: string; + text: string; + subtext?: string; + dissmisable?: boolean; +}; + +/** + * @summary Overlay class + */ +export class Overlay extends AbstractComponent { + + show(config: string | OverlayOptions); + + hide(id?: string); + + isVisible(id?: string): boolean; + +} diff --git a/types/components/Panel.d.ts b/types/components/Panel.d.ts new file mode 100644 index 000000000..42180a39a --- /dev/null +++ b/types/components/Panel.d.ts @@ -0,0 +1,22 @@ +import { AbstractComponent } from './AbstractComponent'; + +export type PanelOptions = { + id?: string; + content: string; + noMargin?: boolean; + width?: string; + clickHandler?: (e: MouseEvent) => {}; +}; + +/** + * @summary Panel class + */ +export class Panel extends AbstractComponent { + + show(config: string | PanelOptions); + + hide(id?: string); + + isVisible(id?: string): boolean; + +} diff --git a/types/components/Tooltip.d.ts b/types/components/Tooltip.d.ts new file mode 100644 index 000000000..d2386737c --- /dev/null +++ b/types/components/Tooltip.d.ts @@ -0,0 +1,35 @@ +import { AbstractComponent } from './AbstractComponent'; + +/** + * Object defining the tooltip position + */ +export type TooltipPosition = { + top: number; + left: number; + position?: string | string[]; + box?: { width: number, height: number }; +}; + +/** + * Object defining the tooltip configuration + */ +export type TooltipOptions = TooltipPosition & { + content: string; + className?: string; + data?: any; +}; + +export class Tooltip extends AbstractComponent { + + /** + * Do not call this method directly, use {@link TooltipRenderer} instead. + */ + show(options: TooltipOptions); + + /** + * @summary Moves the tooltip to a new position + * @throws {PSVError} when the configuration is incorrect + */ + move(position: TooltipPosition); + +} diff --git a/types/data/config.d.ts b/types/data/config.d.ts new file mode 100644 index 000000000..4a6d644a2 --- /dev/null +++ b/types/data/config.d.ts @@ -0,0 +1,6 @@ +import { ViewerOptions } from '../Viewer'; + +/** + * @summary Default options + */ +export const DEFAULTS: ViewerOptions; diff --git a/types/data/constants.d.ts b/types/data/constants.d.ts new file mode 100644 index 000000000..84b4fc005 --- /dev/null +++ b/types/data/constants.d.ts @@ -0,0 +1,89 @@ +/** + * @summary Property name added to viewer element + */ +export const VIEWER_DATA: 'photoSphereViewer'; + +/** + * @summary Available actions + */ +export const ACTIONS: { + ROTATE_LAT_UP: 'rotateLatitudeUp', + ROTATE_LAT_DOWN: 'rotateLatitudeDown', + ROTATE_LONG_RIGHT: 'rotateLongitudeRight', + ROTATE_LONG_LEFT: 'rotateLongitudeLeft', + ZOOM_IN: 'zoomIn', + ZOOM_OUT: 'zoomOut', + TOGGLE_AUTOROTATE: 'toggleAutorotate', +}; + +/** + * @summary Available events names + */ +export const EVENTS: { + AUTOROTATE: 'autorotate', + BEFORE_RENDER: 'before-render', + BEFORE_ROTATE: 'before-rotate', + CLICK: 'click', + CLOSE_PANEL: 'close-panel', + CONFIG_CHANGED: 'config-changed', + DOUBLE_CLICK: 'dblclick', + FULLSCREEN_UPDATED: 'fullscreen-updated', + HIDE_NOTIFICATION: 'hide-notification', + HIDE_OVERLAY: 'hide-overlay', + HIDE_TOOLTIP: 'hide-tooltip', + OPEN_PANEL: 'open-panel', + PANORAMA_LOADED: 'panorama-loaded', + POSITION_UPDATED: 'position-updated', + READY: 'ready', + RENDER: 'render', + SHOW_NOTIFICATION: 'show-notification', + SHOW_OVERLAY: 'show-overlay', + SHOW_TOOLTIP: 'show-tooltip', + SIZE_UPDATED: 'size-updated', + STOP_ALL: 'stop-all', + ZOOM_UPDATED: 'zoom-updated', +}; + +/** + * @summary Available change events names + */ +export const CHANGE_EVENTS: { + GET_ANIMATE_POSITION: 'get-animate-position', + GET_ROTATE_POSITION: 'get-rotate-position', +}; + +/** + * @summary Collection of easing functions + * @see {@link https://gist.github.com/frederickk/6165768} + */ +export const EASINGS: { + linear: (t: number) => number, + + inQuad: (t: number) => number, + outQuad: (t: number) => number, + inOutQuad: (t: number) => number, + + inCubic: (t: number) => number, + outCubic: (t: number) => number, + inOutCubic: (t: number) => number, + + inQuart: (t: number) => number, + outQuart: (t: number) => number, + inOutQuart: (t: number) => number, + + inQuint: (t: number) => number, + outQuint: (t: number) => number, + inOutQuint: (t: number) => number, + + inSine: (t: number) => number, + outSine: (t: number) => number, + inOutSine: (t: number) => number, + + inExpo: (t: number) => number, + outExpo: (t: number) => number, + inOutExpo: (t: number) => number, + + inCirc: (t: number) => number, + outCirc: (t: number) => number, + inOutCirc: (t: number) => number, +}; diff --git a/types/data/system.d.ts b/types/data/system.d.ts new file mode 100644 index 000000000..093f87f0a --- /dev/null +++ b/types/data/system.d.ts @@ -0,0 +1,13 @@ +/** + * @summary General information about the system + */ +export const SYSTEM: { + loaded: boolean; + pixelRatio: number; + isWebGLSupported: boolean; + maxTextureWidth: number; + mouseWheelEvent: string; + fullscreenEvent: string; + getMaxCanvasWidth: () => number; + isTouchEnabled: Promise; +}; diff --git a/types/index.d.ts b/types/index.d.ts new file mode 100644 index 000000000..9bab38fd8 --- /dev/null +++ b/types/index.d.ts @@ -0,0 +1,23 @@ +import * as CONSTANTS from './/data/constants'; +import * as utils from './utils'; + +export * from './models'; +export * from './data/constants'; +export * from './data/config'; +export * from './data/system'; +export * from './adapters/AbstractAdapter'; +export * from './buttons/AbstractButton'; +export * from './plugins/AbstractPlugin'; +export * from './Animation'; +export * from './PSVError'; +export * from './components/Navbar'; +export * from './components/Loader'; +export * from './components/Notification'; +export * from './components/Overlay'; +export * from './components/Panel'; +export * from './components/Tooltip'; +export * from './services/DataHelper'; +export * from './services/TextureLoader'; +export * from './services/TooltipRenderer'; +export * from './Viewer'; +export { CONSTANTS, utils }; diff --git a/types/models.d.ts b/types/models.d.ts new file mode 100644 index 000000000..0ab07a259 --- /dev/null +++ b/types/models.d.ts @@ -0,0 +1,121 @@ +import { Texture } from 'three'; + +/** + * Object defining a point + */ +export type Point = { + x: number; + y: number; +} + +/** + * Object defining a size + */ +export type Size = { + width: number; + height: number; +} + +/** + * Object defining a size in CSS (px, % or auto) + */ +export type CssSize = { + width: string; + height: string; +} + +export type SphereCorrection = { + pan?: number; + tilt?: number; + roll?: number; +} + +/** + * Object defining a spherical position + */ +export type Position = { + longitude: number; + latitude: number; +} + +/** + * Object defining a spherical or texture position + */ +export type ExtendedPosition = Position | Point; + +/** + * Object defining animation options + */ +export type AnimateOptions = ExtendedPosition & { + speed: string | number; + zoom?: number; +}; + +/** + * Crop information of the panorama + */ +export type PanoData = { + fullWidth: number; + fullHeight: number; + croppedWidth: number; + croppedHeight: number; + croppedX: number; + croppedY: number; + poseHeading?: number; + posePitch?: number; + poseRoll?: number; +} + +/** + * Function to compute panorama data once the image is loaded + */ +export type PanoDataProvider = (image: HTMLImageElement) => PanoData; + +/** + * Object defining panorama and animation options + */ +export type PanoramaOptions = (ExtendedPosition | {}) & { + zoom?: number; + transition?: boolean | number; + showLoader?: boolean; + sphereCorrection?: SphereCorrection; + panoData?: PanoData | PanoDataProvider; +}; + +/** + * Result of the AbstractAdapter#loadTexture method + */ +export type TextureData = { + texture: Texture | Texture[] | Record; + panoData?: PanoData; +}; + +/** + * Data of the `click` event + */ +export type ClickData = { + rightclick: boolean; + clientX: number; + clientY: number; + viewerX: number; + viewerY: number; + longitude: number; + latitude: number; + textureX?: number; + textureY?: number; + marker?: any; +} + +/** + * Definition of a custom navbar button + */ +export type NavbarCustomButton = { + id?: string; + title?: string; + content?: string; + className?: string; + onClick: () => void; + disabled?: boolean; + visible?: boolean; + collapsable?: boolean; +}; diff --git a/types/plugins/AbstractPlugin.d.ts b/types/plugins/AbstractPlugin.d.ts new file mode 100644 index 000000000..a14b4aa2e --- /dev/null +++ b/types/plugins/AbstractPlugin.d.ts @@ -0,0 +1,23 @@ +import { EventEmitter } from 'uevent'; +import { Viewer } from '../Viewer'; + +/** + * @summary Base plugins class + */ +export abstract class AbstractPlugin extends EventEmitter { + + /** + * @summary Unique identifier of the plugin + */ + static id: string; + + constructor(psv: Viewer); + + /** + * @summary Destroys the plugin + */ + destroy(); + +} + +export type PluginConstructor = new (psv: Viewer, options?: any) => T; diff --git a/types/plugins/autorotate-keypoints/index.d.ts b/types/plugins/autorotate-keypoints/index.d.ts new file mode 100644 index 000000000..d43afd4e5 --- /dev/null +++ b/types/plugins/autorotate-keypoints/index.d.ts @@ -0,0 +1,30 @@ +import { AbstractPlugin, ExtendedPosition, Viewer } from '../..'; + +/** + * @summary Definition of keypoints for automatic rotation, can be a position object, a marker id or an object with the following properties + */ +export type AutorotateKeypoint = string | ExtendedPosition | { + markerId?: string; + position?: ExtendedPosition; + tooltip?: string | { content: string, position: string }; + pause?: number; +}; + +export type AutorotateKeypointsPluginOptions = { + startFromClosest?: boolean; + keypoints?: AutorotateKeypoint[]; +} + +/** + * @summary Replaces the standard autorotate animation by a smooth transition between multiple points + */ +export class AutorotateKeypointsPlugin extends AbstractPlugin { + + constructor(psv: Viewer, options: AutorotateKeypointsPluginOptions); + + /** + * @summary Changes the keypoints + */ + setKeypoints(keypoints: AutorotateKeypoint[]); + +} diff --git a/types/plugins/gyroscope/index.d.ts b/types/plugins/gyroscope/index.d.ts new file mode 100644 index 000000000..3de2eb78b --- /dev/null +++ b/types/plugins/gyroscope/index.d.ts @@ -0,0 +1,44 @@ +import { AbstractPlugin, Viewer, PSVError } from '../..'; +import { Event } from 'uevent'; + +export type GyroscopePluginOptions = { + touchmove?: boolean; + absolutePosition?: boolean; +}; + +/** + * @summary Adds gyroscope controls on mobile devices + */ +export class GyroscopePlugin extends AbstractPlugin { + + static EVENTS: Record; + + constructor(psv: Viewer, options: GyroscopePluginOptions); + + /** + * @summary Checks if the gyroscope is enabled + */ + isEnabled(): boolean; + + /** + * @summary Enables the gyroscope navigation if available + * @throws {PSVError} if the gyroscope API is not available/granted + */ + start(): Promise; + + /** + * @summary Disables the gyroscope navigation + */ + stop(); + + /** + * @summary Enables or disables the gyroscope navigation + */ + toggle(); + + /** + * @summary Triggered when the gyroscope mode is enabled/disabled + */ + on(e: 'gyroscope-updated', cb: (e: Event, enabled: boolean) => void): this; + +} diff --git a/types/plugins/markers/index.d.ts b/types/plugins/markers/index.d.ts new file mode 100644 index 000000000..01fcd3f31 --- /dev/null +++ b/types/plugins/markers/index.d.ts @@ -0,0 +1,271 @@ +import { AbstractPlugin, Animation, ExtendedPosition, PSVError, Viewer } from '../..'; +import { Event } from 'uevent'; + +export type MarkerType = + 'image' + | 'html' + | 'square' + | 'rect' + | 'circle' + | 'ellipse' + | 'path' + | 'polygonPx' + | 'polygonRad' + | 'polylinePx' + | 'polylineRad'; + +/** + * @summary Marker properties + */ +export type MarkerProperties = Partial & { + image?: string; + html?: string; + square?: number; + rect?: [number, number] | { width: number, height: number }; + circle?: number; + ellipse?: [number, number] | { cx: number, cy: number }; + path?: string; + polygonPx?: [number, number][]; + polygonRad?: [number, number][]; + polylinePx?: [number, number][]; + polylineRad?: [number, number][]; + + id: string; + width?: number; + height?: number; + scale?: number | [number, number]; + className?: string; + style?: Record; + svgStyle?: Record; + anchor?: string; + visible?: boolean; + tooltip?: string | { content: string, position?: string }; + content?: string; + hideList?: boolean; + data?: any; +}; + +/** + * @summary Data of the `select-marker` event + */ +export type SelectMarkerData = { + dblclick: boolean; + rightclick: boolean; +}; + +export type MarkersPluginOptions = { + clickEventOnMarker?: boolean; + markers?: MarkerProperties[]; +}; + +/** + * @summary Object representing a marker + */ +export class Marker { + + private constructor(); + + readonly id: string; + readonly type: MarkerType; + readonly visible: boolean; + readonly config: MarkerProperties; + readonly data?: any; + + /** + * @summary Checks if it is a normal marker (image or html) + */ + isNormal(): boolean; + + /** + * @summary Checks if it is a polygon/polyline marker + */ + isPoly(): boolean; + + /** + * @summary Checks if it is a polygon/polyline using pixel coordinates + */ + isPolyPx(): boolean; + + /** + * @summary Checks if it is a polygon/polyline using radian coordinates + */ + isPolyRad(): boolean; + + /** + * @summary Checks if it is a polygon marker + */ + isPolygon(): boolean; + + /** + * @summary Checks if it is a polyline marker + */ + isPolyline(): boolean; + + /** + * @summary Checks if it is an SVG marker + */ + isSvg(): boolean; + +} + +/** + * @summary Displays various markers on the viewer + */ +export class MarkersPlugin extends AbstractPlugin { + + static EVENTS: Record; + + constructor(psv: Viewer, options: MarkersPluginOptions); + + /** + * @summary Toggles the visibility of all tooltips + */ + toggleAllTooltips(); + + /** + * @summary Displays all tooltips + */ + showAllTooltips(); + + /** + * @summary Hides all tooltips + */ + hideAllTooltips(); + + /** + * @summary Return the total number of markers + * @returns {number} + */ + getNbMarkers(): number; + + /** + * @summary Adds a new marker to viewer + * @returns {PSV.plugins.MarkersPlugin.Marker} + * @throws {PSVError} when the marker's id is missing or already exists + */ + addMarker(properties: MarkerProperties, render?: boolean): Marker; + + /** + * @summary Returns the internal marker object for a marker id + * @throws {PSVError} when the marker cannot be found + */ + getMarker(markerId: string): Marker; + + /** + * @summary Returns the last marker selected by the user + */ + getCurrentMarker(): Marker; + + /** + * @summary Updates the existing marker with the same id + * @description Every property can be changed but you can't change its type (Eg: `image` to `html`). + */ + updateMarker(properties: MarkerProperties, render?: boolean): Marker; + + /** + * @summary Removes a marker from the viewer + */ + removeMarker(markerId: string, render?: boolean); + + /** + * @summary Replaces all markers + */ + setMarkers(markers: MarkerProperties[], render?: boolean); + + /** + * @summary Removes all markers + */ + clearMarkers(render?: boolean); + + /** + * @summary Rotate the view to face the marker + */ + gotoMarker(markerId: string, speed: string | number): Animation; + + /** + * @summary Hides a marker + */ + hideMarker(markerId: string); + + /** + * @summary Shows a marker + */ + showMarker(markerId: string); + + /** + * @summary Toggles a marker + */ + toggleMarker(markerId: string); + + /** + * @summary Opens the panel with the content of the marker + */ + showMarkerPanel(markerId: string); + + /** + * @summary Toggles the visibility of markers list + */ + toggleMarkersList(); + + /** + * @summary Opens side panel with list of markers + */ + showMarkersList(); + + /** + * @summary Closes side panel if it contains the list of markers + */ + hideMarkersList(); + + /** + * @summary Updates the visibility and the position of all markers + */ + renderMarkers(); + + /** + * @summary Triggered when the animation to a marker is done + */ + on(e: 'goto-marker-done', cb: (e: Event, marker: Marker) => void): this; + + /** + * @summary Triggered when the user puts the cursor away from a marker + */ + on(e: 'leave-marker', cb: (e: Event, marker: Marker) => void): this; + + /** + * @summary Triggered when the user puts the cursor hover a marker + */ + on(e: 'over-marker', cb: (e: Event, marker: Marker) => void): this; + + /** + * @summary Used to alter the list of markers displayed on the side-panel + */ + on(e: 'render-markers-list', cb: (e: Event, markers: Marker[]) => Marker[]): this; + + /** + * @summary Triggered when the user clicks on a marker. The marker can be retrieved from outside the event handler + * with {@link MarkersPlugin.getCurrentMarker} + */ + on(e: 'select-marker', cb: (e: Event, marker: Marker, data: SelectMarkerData) => void): this; + + /** + * @summary Triggered when a marker is selected from the side panel + */ + on(e: 'select-marker-list', cb: (e: Event, marker: Marker) => void): this; + + /** + * @summary Triggered when a marker was selected and the user clicks elsewhere + */ + on(e: 'unselect-marker', cb: (e: Event, marker: Marker) => void): this; + + /** + * @summary Triggered when the markers are hidden + */ + on(e: 'hide-markers', cb: (e: Event) => void): this; + + /** + * @summary Triggered when the markers are shown + */ + on(e: 'show-markers', cb: (e: Event) => void): this; + +} diff --git a/types/plugins/resolution/index.d.ts b/types/plugins/resolution/index.d.ts new file mode 100644 index 000000000..42db35549 --- /dev/null +++ b/types/plugins/resolution/index.d.ts @@ -0,0 +1,43 @@ +import { AbstractPlugin, Viewer } from '../..'; +import { Event } from 'uevent'; + +export type Resolution = { + id: string; + label: string; + panorama: any; +}; + +export type ResolutionPluginOptions = { + resolutions: Resolution[]; +}; + +/** + * @summary Adds a setting to choose between multiple resolutions of the panorama. + */ +export class ResolutionPlugin extends AbstractPlugin { + + static EVENTS: Record; + + constructor(psv: Viewer, options: ResolutionPluginOptions); + + /** + * @summary Changes the available resolutions + */ + setResolutions(resolutions: Resolution[]); + + /** + * @summary Changes the current resolution + */ + setResolution(id: string); + + /** + * @summary Returns the current resolution + */ + getResolution(): string; + + /** + * @summary Triggered when the resolution is changed + */ + on(e: 'resolution-changed', cb: (e: Event, resolutionId: string) => void): this; + +} diff --git a/types/plugins/settings/index.d.ts b/types/plugins/settings/index.d.ts new file mode 100644 index 000000000..9fbea11a4 --- /dev/null +++ b/types/plugins/settings/index.d.ts @@ -0,0 +1,73 @@ +import { AbstractPlugin, Viewer } from '../..'; + +/** + * @summary Description of a setting + */ +export type BaseSetting = { + id: string; + label: string; +}; + +/** + * @summary Description of a 'options' setting + */ +export type OptionsSetting = BaseSetting & { + type: 'options'; + current: () => string; + options: () => SettingOption[] + apply: (string) => void; +}; + +/** + * @summary Description of a 'toggle' setting + */ +export type ToggleSetting = BaseSetting & { + type: 'toggle'; + active: () => boolean; + toggle: () => void; +}; + +/** + * @summary Option of an 'option' setting + */ +export type SettingOption = { + id: string; + label: string; +}; + +export type Setting = OptionsSetting | ToggleSetting; + +/** + * @summary Adds a button to access various settings. + */ +export class SettingsPlugin extends AbstractPlugin { + + constructor(psv: Viewer); + + /** + * @summary Registers a new setting + */ + addSetting(setting: Setting); + + /** + * @summary Removes a setting + * @param {string} id + */ + removeSetting(id: string); + + /** + * @summary Toggles the settings panel + */ + toggleSettings(); + + /** + * @summary Hides the settings panel + */ + hideSettings(); + + /** + * @summary Shows the settings panel + */ + showSettings(); + +} diff --git a/types/plugins/stereo/index.d.ts b/types/plugins/stereo/index.d.ts new file mode 100644 index 000000000..4c2860f34 --- /dev/null +++ b/types/plugins/stereo/index.d.ts @@ -0,0 +1,39 @@ +import { AbstractPlugin, Viewer, PSVError } from '../..'; +import { Event } from 'uevent'; + +/** + * @summary Adds stereo view on mobile devices + */ +export class StereoPlugin extends AbstractPlugin { + + static EVENTS: Record; + + constructor(psv: Viewer); + + /** + * @summary Checks if the stereo view is enabled + */ + isEnabled(): boolean; + + /** + * @summary Enables the stereo view + * @throws {PSVError} if the gyroscope API is not available/granted + */ + start(): Promise; + + /** + * @summary Disables the stereo view + */ + stop(); + + /** + * @summary Enables or disables the stereo view + */ + toggle(); + + /** + * @summary Triggered when the stereo view is enabled/disabled + */ + on(e: 'stereo-updated', cb: (e: Event, enabled: boolean) => void): this; + +} diff --git a/types/plugins/visible-range/index.d.ts b/types/plugins/visible-range/index.d.ts new file mode 100644 index 000000000..3b3001690 --- /dev/null +++ b/types/plugins/visible-range/index.d.ts @@ -0,0 +1,31 @@ +import { AbstractPlugin, Viewer } from '../..'; + +export type VisibleRangePluginOptions = { + latitudeRange?: number[] | string[]; + longitudeRange?: number[] | string[]; + usePanoData: boolean; +}; + +/** + * @summary Locks visible longitude and/or latitude + */ +export class VisibleRangePlugin extends AbstractPlugin { + + constructor(psv: Viewer, options: VisibleRangePluginOptions); + + /** + * @summary Changes the latitude range + */ + setLatitudeRange(range: number[] | string[]); + + /** + * @summary Changes the longitude range + */ + setLongitudeRange(range: number[] | string[]); + + /** + * @summary Changes the latitude and longitude ranges according the current panorama cropping data + */ + setRangesFromPanoData() + +} diff --git a/types/services/DataHelper.d.ts b/types/services/DataHelper.d.ts new file mode 100644 index 000000000..e78c3834b --- /dev/null +++ b/types/services/DataHelper.d.ts @@ -0,0 +1,74 @@ +import { Vector3 } from 'three'; +import { ExtendedPosition, Point, Position, SphereCorrection } from '../models'; + +/** + * @summary Collections of data converters for the current viewer + */ +export class DataHelper { + + /** + * @summary Converts vertical FOV to zoom level + */ + fovToZoomLevel(fov: number): number; + + /** + * @summary Converts zoom level to vertical FOV + */ + zoomLevelToFov(level: number): number; + + /** + * @summary Convert vertical FOV to horizontal FOV + */ + vFovToHFov(vFov: number): number; + + /** + * @summary Converts a speed into a duration from current position to a new position + */ + speedToDuration(value: string | number, angle: number): number; + + /** + * @summary Converts pixel texture coordinates to spherical radians coordinates + */ + textureCoordsToSphericalCoords(point: Point): Position; + + /** + * @summary Converts spherical radians coordinates to pixel texture coordinates + */ + sphericalCoordsToTextureCoords(position: Position): Point; + + /** + * @summary Converts spherical radians coordinates to a THREE.Vector3 + */ + sphericalCoordsToVector3(position: Position): Vector3; + + /** + * @summary Converts a THREE.Vector3 to spherical radians coordinates + */ + vector3ToSphericalCoords(vector: Vector3): Position; + + /** + * @summary Converts position on the viewer to a THREE.Vector3 + */ + viewerCoordsToVector3(point: Point): Vector3; + + /** + * @summary Converts a THREE.Vector3 to position on the viewer + */ + vector3ToViewerCoords(vector: Vector3): Point; + + /** + * @summary Converts spherical radians coordinates to position on the viewer + */ + sphericalCoordsToViewerCoords(position: Position): Point; + + /** + * @summary Converts x/y to latitude/longitude if present and ensure boundaries + */ + cleanPosition(position: ExtendedPosition): Position; + + /** + * @summary Ensure a SphereCorrection object is valid + */ + cleanSphereCorrection(sphere: SphereCorrection): SphereCorrection; + +} diff --git a/types/services/TextureLoader.d.ts b/types/services/TextureLoader.d.ts new file mode 100644 index 000000000..760e3ac67 --- /dev/null +++ b/types/services/TextureLoader.d.ts @@ -0,0 +1,11 @@ +/** + * @summary Texture loader + */ +export class TextureLoader { + + /** + * @summary Preload a panorama file without displaying it + */ + preloadPanorama(panorama: any): Promise; + +} diff --git a/types/services/TooltipRenderer.d.ts b/types/services/TooltipRenderer.d.ts new file mode 100644 index 000000000..00587c7a9 --- /dev/null +++ b/types/services/TooltipRenderer.d.ts @@ -0,0 +1,15 @@ +import { Tooltip, TooltipOptions } from '../components/Tooltip'; +import { AbstractComponent } from '../components/AbstractComponent'; + +/** + * @summary Tooltip renderer + */ +export class TooltipRenderer extends AbstractComponent { + + /** + * @summary Displays a tooltip on the viewer + * @throws {PSVError} when the configuration is incorrect + */ + create(config: TooltipOptions): Tooltip; + +} diff --git a/types/utils/browser.d.ts b/types/utils/browser.d.ts new file mode 100644 index 000000000..459e9c0ff --- /dev/null +++ b/types/utils/browser.d.ts @@ -0,0 +1,57 @@ +/** + * @summary Toggles a CSS class + */ +export function toggleClass(element: HTMLElement | SVGElement, className: string, active?: boolean); + +/** + * @summary Adds one or several CSS classes to an element + */ +export function addClasses(element: HTMLElement, className: string); + +/** + * @summary Removes one or several CSS classes to an element + */ +export function removeClasses(element: HTMLElement, className: string); + +/** + * @summary Searches if an element has a particular parent at any level including itself + */ +export function hasParent(el: HTMLElement, parent: HTMLElement): boolean; + +/** + * @summary Gets the closest parent (can by itself) + */ +export function getClosest(el: HTMLElement | SVGElement, selector: string): HTMLElement; + +/** + * @summary Returns the key name of a KeyboardEvent + */ +export function getEventKey(evt: KeyboardEvent): string; + +/** + * @summary Detects if fullscreen is enabled + */ +export function isFullscreenEnabled(elt: HTMLElement): boolean; + +/** + * @summary Enters fullscreen mode + */ +export function requestFullscreen(elt: HTMLElement); + +/** + * @summary Exits fullscreen mode + */ +export function exitFullscreen(); + +/** + * @summary Gets an element style + */ +export function getStyle(elt: HTMLElement, prop: string): any; + +/** + * @summary Normalize mousewheel values accross browsers + * @description From Facebook's Fixed Data Table + * {@link https://github.com/facebookarchive/fixed-data-table/blob/master/src/vendor_upstream/dom/normalizeWheel.js} + * @copyright Facebook + */ +export function normalizeWheel(event: WheelEvent): { spinX: number, spinY: number, pixelX: number, pixelY: number }; diff --git a/types/utils/index.d.ts b/types/utils/index.d.ts new file mode 100644 index 000000000..cbba0232b --- /dev/null +++ b/types/utils/index.d.ts @@ -0,0 +1,4 @@ +export * from './browser.d.js'; +export * from './math.d.js'; +export * from './misc.d.js'; +export * from './psv.d.js'; diff --git a/types/utils/math.d.ts b/types/utils/math.d.ts new file mode 100644 index 000000000..de9db6ff8 --- /dev/null +++ b/types/utils/math.d.ts @@ -0,0 +1,36 @@ +import { Point, Position } from '../models'; + +/** + * @summary Ensures that a number is in a given interval + */ +export function bound(x: number, min: number, max: number): number; + +/** + * @summary Checks if a value is an integer + */ +export function isInteger(value: any): boolean; + +/** + * @summary Computes the sum of an array + */ +export function sum(array: number[]): number; + +/** + * @summary Computes the distance between two points + */ +export function distance(p1: Point, p2: Point): number; + +/** + * @summary Compute the shortest offset between two longitudes + */ +export function getShortestArc(from: number, to: number): number; + +/** + * @summary Computes the angle between the current position and a target position + */ +export function getAngle(position1: Position, position2: Position): number; + +/** + * @summary Returns the distance between two points on a sphere of radius one + */ +export function greatArcDistance(p1: number[], p2: number[]): number; diff --git a/types/utils/misc.d.ts b/types/utils/misc.d.ts new file mode 100644 index 000000000..feb96cfc8 --- /dev/null +++ b/types/utils/misc.d.ts @@ -0,0 +1,58 @@ +/** + * @summary Transforms a string to dash-case {@link https://github.com/shahata/dasherize} + */ + +export function dasherize(str: string): string; + +/** + * @summary Returns a function, that, when invoked, will only be triggered at most once during a given window of time. + * @copyright underscore.js - modified by Clément Prévost {@link http://stackoverflow.com/a/27078401} + */ +export function throttle(func: Function, wait: number): Function; + +/** + * @summary Test if an object is a plain object + * @description Test if an object is a plain object, i.e. is constructed + * by the built-in Object constructor and inherits directly from Object.prototype + * or null. Some built-in objects pass the test, e.g. Math which is a plain object + * and some host or exotic objects may pass also. + * {@link http://stackoverflow.com/a/5878101/1207670} + */ +export function isPlainObject(obj: any): boolean; + +/** + * @summary Merges the enumerable attributes of two objects + * @description Replaces arrays and alters the target object. + * @copyright Nicholas Fisher + */ +export function deepmerge(target: object, src: object): object; + +/** + * @summary Deeply clones an object + */ +export function clone(src: object): object; + +/** + * @summery Test of an object is empty + */ +export function isEmpty(obj: object): boolean; + +/** + * @summary Loops over enumerable properties of an object + */ +export function each(object: object, callback: (value: any, key: string) => void); + +/** + * @summary Returns the intersection between two arrays + */ +export function intersect(array1: T[], array2: T[]): T[]; + +/** + * @summary Returns if a valu is null or undefined + */ +export function isNil(val: any): val is null | undefined; + +/** + * @summary Returns the first non null non undefined parameter + */ +export function firstNonNull(...values: any[]): any; diff --git a/types/utils/psv.d.ts b/types/utils/psv.d.ts new file mode 100644 index 000000000..1ef152f9a --- /dev/null +++ b/types/utils/psv.d.ts @@ -0,0 +1,46 @@ +import { Texture } from 'three'; +import { ExtendedPosition, Point } from '../models'; + +/** + * @summary Displays a warning in the console + */ +export function logWarn(message: string); + +/** + * @summary Checks if an object is a {PSV.ExtendedPosition}, ie has x/y or longitude/latitude + */ +export function isExtendedPosition(object: any): object is ExtendedPosition; + +/** + * @summary Returns the value of a given attribute in the panorama metadata + */ +export function getXMPValue(data: string, attr: string): number | null; + +/** + * @summary Translate CSS values like "top center" or "10% 50%" as top and left positions + * @description The implementation is as close as possible to the "background-position" specification + * {@link https://developer.mozilla.org/en-US/docs/Web/CSS/background-position} + */ +export function parsePosition(value: string | Point): Point; + +/** + * @summary Parses an speed + * @param speed - The speed, in radians/degrees/revolutions per second/minute + * @returns radians per second + * @throws {PSVError} when the speed cannot be parsed + */ +export function parseSpeed(speed: string | number): number; + +/** + * @summary Parses an angle value in radians or degrees and returns a normalized value in radians + * @param {string|number} angle - eg: 3.14, 3.14rad, 180deg + * @param {boolean} [zeroCenter=false] - normalize between -Pi - Pi instead of 0 - 2*Pi + * @param {boolean} [halfCircle=zeroCenter] - normalize between -Pi/2 - Pi/2 instead of -Pi - Pi + * @throws {PSVError} when the angle cannot be parsed + */ +export function parseAngle(angle: string | number, zeroCenter?: boolean, halfCircle?: boolean): number; + +/** + * @summary Creates a THREE texture from an image + */ +export function createTexture(img: HTMLImageElement | HTMLCanvasElement): Texture;