diff --git a/dist/ramp.cjs.js b/dist/ramp.cjs.js index 796b9262..baafb403 100644 --- a/dist/ramp.cjs.js +++ b/dist/ramp.cjs.js @@ -7,8 +7,8 @@ var manifesto_js = require('manifesto.js'); var mimeDb = require('mime-db'); var sanitizeHtml = require('sanitize-html'); var reactErrorBoundary = require('react-error-boundary'); -var videojs = require('video.js'); var cx = require('classnames'); +var videojs = require('video.js'); var mammoth = require('mammoth'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } @@ -34,8 +34,8 @@ function _interopNamespace(e) { var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var mimeDb__default = /*#__PURE__*/_interopDefaultLegacy(mimeDb); var sanitizeHtml__default = /*#__PURE__*/_interopDefaultLegacy(sanitizeHtml); -var videojs__default = /*#__PURE__*/_interopDefaultLegacy(videojs); var cx__default = /*#__PURE__*/_interopDefaultLegacy(cx); +var videojs__default = /*#__PURE__*/_interopDefaultLegacy(videojs); var mammoth__default = /*#__PURE__*/_interopDefaultLegacy(mammoth); var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; @@ -1303,8 +1303,8 @@ function isEmpty(value) { var isEmpty_1 = isEmpty; -function ownKeys$8(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } -function _objectSpread$8(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$8(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$8(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } +function ownKeys$9(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } +function _objectSpread$9(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$9(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$9(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } var S_ANNOTATION_TYPE = { transcript: 1, caption: 2, @@ -1709,7 +1709,7 @@ function parseResourceAnnotations(annotation, duration, motivation) { * there is a start defined at the manifest level */ if (!isPlaylist) { - target = _objectSpread$8(_objectSpread$8({}, target), {}, { + target = _objectSpread$9(_objectSpread$9({}, target), {}, { customStart: target.start, start: 0, altStart: 0 @@ -2050,8 +2050,8 @@ var groupBy = function groupBy(arry, key) { function _createForOfIteratorHelper$3(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$3(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray$3(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$3(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$3(o, minLen); } function _arrayLikeToArray$3(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } -function ownKeys$7(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } -function _objectSpread$7(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$7(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$7(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } +function ownKeys$8(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } +function _objectSpread$8(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$8(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$8(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } // HTML tags and attributes allowed in IIIF var HTML_SANITIZE_CONFIG = { @@ -2178,7 +2178,7 @@ function getMediaInfo(_ref) { // return empty object when canvasIndex is undefined if (canvasIndex === undefined || canvasIndex < 0) { - return _objectSpread$7(_objectSpread$7({}, info), {}, { + return _objectSpread$8(_objectSpread$8({}, info), {}, { error: 'Error fetching content' }); } @@ -2186,7 +2186,7 @@ function getMediaInfo(_ref) { // return an error when the given Manifest doesn't have any Canvas(es) var canvases = manifest.items; if ((canvases === null || canvases === void 0 ? void 0 : canvases.length) == 0) { - return _objectSpread$7(_objectSpread$7({}, info), {}, { + return _objectSpread$8(_objectSpread$8({}, info), {}, { poster: GENERIC_EMPTY_MANIFEST_MESSAGE }); } @@ -2224,14 +2224,14 @@ function getMediaInfo(_ref) { poster: poster }; if (mediaInfo.error) { - return _objectSpread$7({}, mediaInfo); + return _objectSpread$8({}, mediaInfo); } else { // Get media type var allTypes = mediaInfo.sources.map(function (q) { return q.kind; }); var mediaType = setMediaType(allTypes); - return _objectSpread$7(_objectSpread$7({}, mediaInfo), {}, { + return _objectSpread$8(_objectSpread$8({}, mediaInfo), {}, { error: null, mediaType: mediaType }); @@ -2560,7 +2560,7 @@ function parseMetadata(metadata, resourceType) { var _getLabelValue; // get value and replace \n characters with
to display new lines in UI var value = (_getLabelValue = getLabelValue(md.value, true)) === null || _getLabelValue === void 0 ? void 0 : _getLabelValue.replace(/\n/g, "
"); - var sanitizedValue = sanitizeHtml__default["default"](value, _objectSpread$7({}, HTML_SANITIZE_CONFIG)); + var sanitizedValue = sanitizeHtml__default["default"](value, _objectSpread$8({}, HTML_SANITIZE_CONFIG)); parsedMetadata.push({ label: getLabelValue(md.label), value: sanitizedValue @@ -2913,10 +2913,10 @@ function parseMarkerAnnotation(a) { } } -function ownKeys$6(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } -function _objectSpread$6(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$6(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$6(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } -var ManifestStateContext = /*#__PURE__*/React__default["default"].createContext(); -var ManifestDispatchContext = /*#__PURE__*/React__default["default"].createContext(); +function ownKeys$7(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } +function _objectSpread$7(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$7(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$7(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } +var ManifestStateContext = /*#__PURE__*/React.createContext(); +var ManifestDispatchContext = /*#__PURE__*/React.createContext(); /** * Definition of all state variables in this Context @@ -2974,11 +2974,11 @@ function manifestReducer() { var isPlaylist = getIsPlaylist(manifest.label); var annotationService = getAnnotationService(manifest.service); var playlistMarkers = parsePlaylistAnnotations(manifest); - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { manifest: manifest, allCanvases: canvases, autoAdvance: manifestBehavior, - playlist: _objectSpread$6(_objectSpread$6({}, state.playlist), {}, { + playlist: _objectSpread$7(_objectSpread$7({}, state.playlist), {}, { isPlaylist: isPlaylist, annotationServiceId: annotationService, hasAnnotationService: annotationService ? true : false, @@ -2988,56 +2988,56 @@ function manifestReducer() { } case 'switchCanvas': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { canvasIndex: action.canvasIndex, hasStructure: getHasStructure(state.canvasSegments, action.canvasIndex) }); } case 'switchItem': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { currentNavItem: action.item }); } case 'canvasDuration': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { canvasDuration: action.canvasDuration }); } case 'canvasLink': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { canvasLink: action.canvasLink }); } case 'canvasTargets': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { targets: action.canvasTargets }); } case 'hasMultipleItems': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { hasMultiItems: action.isMultiSource }); } case 'setSrcIndex': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { srcIndex: action.srcIndex }); } case 'setItemStartTime': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { startTime: action.startTime }); } case 'setAutoAdvance': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { autoAdvance: action.autoAdvance }); } @@ -3045,16 +3045,16 @@ function manifestReducer() { { // Set a new set of markers for the canvases in the Manifest if (action.markers) { - return _objectSpread$6(_objectSpread$6({}, state), {}, { - playlist: _objectSpread$6(_objectSpread$6({}, state.playlist), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { + playlist: _objectSpread$7(_objectSpread$7({}, state.playlist), {}, { markers: action.markers }) }); } // Update the existing markers for the current canvas on CRUD ops if (action.updatedMarkers) { - return _objectSpread$6(_objectSpread$6({}, state), {}, { - playlist: _objectSpread$6(_objectSpread$6({}, state.playlist), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { + playlist: _objectSpread$7(_objectSpread$7({}, state.playlist), {}, { markers: state.playlist.markers.map(function (m) { if (m.canvasIndex === state.canvasIndex) { m.canvasMarkers = action.updatedMarkers; @@ -3067,21 +3067,21 @@ function manifestReducer() { } case 'setIsEditing': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { - playlist: _objectSpread$6(_objectSpread$6({}, state.playlist), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { + playlist: _objectSpread$7(_objectSpread$7({}, state.playlist), {}, { isEditing: action.isEditing }) }); } case 'setCanvasIsEmpty': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { canvasIsEmpty: action.isEmpty }); } case 'setStructures': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { structures: action.structures }); } @@ -3091,7 +3091,7 @@ function manifestReducer() { var canvasStructures = action.timespans.filter(function (c) { return c.canvasIndex == state.canvasIndex + 1 && !c.isCanvas; }); - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { canvasSegments: action.timespans, hasStructure: canvasStructures.length > 0 }); @@ -3101,7 +3101,7 @@ function manifestReducer() { var _action$customStart = action.customStart, canvas = _action$customStart.canvas, time = _action$customStart.time; - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { customStart: { startIndex: canvas, startTime: time @@ -3112,8 +3112,8 @@ function manifestReducer() { } case 'setRenderingFiles': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { - renderings: _objectSpread$6({}, action.renderings) + return _objectSpread$7(_objectSpread$7({}, state), {}, { + renderings: _objectSpread$7({}, action.renderings) }); } default: @@ -3126,10 +3126,10 @@ function ManifestProvider(_ref) { var _ref$initialState = _ref.initialState, initialState = _ref$initialState === void 0 ? defaultState$1 : _ref$initialState, children = _ref.children; - var _React$useReducer = React__default["default"].useReducer(manifestReducer, initialState), - _React$useReducer2 = _slicedToArray(_React$useReducer, 2), - state = _React$useReducer2[0], - dispatch = _React$useReducer2[1]; + var _useReducer = React.useReducer(manifestReducer, initialState), + _useReducer2 = _slicedToArray(_useReducer, 2), + state = _useReducer2[0], + dispatch = _useReducer2[1]; return /*#__PURE__*/React__default["default"].createElement(ManifestStateContext.Provider, { value: state }, /*#__PURE__*/React__default["default"].createElement(ManifestDispatchContext.Provider, { @@ -3137,24 +3137,24 @@ function ManifestProvider(_ref) { }, children)); } function useManifestState() { - var context = React__default["default"].useContext(ManifestStateContext); + var context = React.useContext(ManifestStateContext); if (context === undefined) { throw new Error('useManifestState must be used within a ManifestProvider'); } return context; } function useManifestDispatch() { - var context = React__default["default"].useContext(ManifestDispatchContext); + var context = React.useContext(ManifestDispatchContext); if (context === undefined) { throw new Error('useManifestDispatch must be used within a ManifestProvider'); } return context; } -function ownKeys$5(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } -function _objectSpread$5(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$5(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$5(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } -var PlayerStateContext = /*#__PURE__*/React__default["default"].createContext(); -var PlayerDispatchContext = /*#__PURE__*/React__default["default"].createContext(); +function ownKeys$6(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } +function _objectSpread$6(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$6(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$6(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } +var PlayerStateContext = /*#__PURE__*/React.createContext(); +var PlayerDispatchContext = /*#__PURE__*/React.createContext(); /** * Definition of all state variables in this Context @@ -3177,63 +3177,63 @@ function PlayerReducer() { switch (action.type) { case 'updatePlayer': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { player: action.player }); } case 'navClick': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { clickedUrl: action.clickedUrl, isClicked: true }); } case 'resetClick': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { isClicked: false }); } case 'setTimeFragment': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { startTime: action.startTime, endTime: action.endTime }); } case 'setSearchMarkers': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { searchMarkers: action.payload }); } case 'setPlayingStatus': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { isPlaying: action.isPlaying }); } case 'setCaptionStatus': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { captionOn: action.captionOn }); } case 'setIsEnded': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { isEnded: action.isEnded }); } case 'setCurrentTime': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { currentTime: action.currentTime }); } case 'setPlayerFocusElement': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { playerFocusElement: action.element ? action.element : '' }); } @@ -3247,10 +3247,10 @@ function PlayerProvider(_ref) { var _ref$initialState = _ref.initialState, initialState = _ref$initialState === void 0 ? defaultState : _ref$initialState, children = _ref.children; - var _React$useReducer = React__default["default"].useReducer(PlayerReducer, initialState), - _React$useReducer2 = _slicedToArray(_React$useReducer, 2), - state = _React$useReducer2[0], - dispatch = _React$useReducer2[1]; + var _useReducer = React.useReducer(PlayerReducer, initialState), + _useReducer2 = _slicedToArray(_useReducer, 2), + state = _useReducer2[0], + dispatch = _useReducer2[1]; return /*#__PURE__*/React__default["default"].createElement(PlayerStateContext.Provider, { value: state }, /*#__PURE__*/React__default["default"].createElement(PlayerDispatchContext.Provider, { @@ -3258,14 +3258,14 @@ function PlayerProvider(_ref) { }, children)); } function usePlayerState() { - var context = React__default["default"].useContext(PlayerStateContext); + var context = React.useContext(PlayerStateContext); if (context === undefined) { throw new Error("usePlayerState must be used within the PlayerProvider"); } return context; } function usePlayerDispatch() { - var context = React__default["default"].useContext(PlayerDispatchContext); + var context = React.useContext(PlayerDispatchContext); if (context === undefined) { throw new Error("usePlayerDispatch must be used within the PlayerProvider"); } @@ -3729,10 +3729,10 @@ function IIIFPlayerWrapper(_ref) { startCanvasTime = _ref.startCanvasTime, children = _ref.children, manifestValue = _ref.manifest; - var _React$useState = React__default["default"].useState(manifestValue), - _React$useState2 = _slicedToArray(_React$useState, 2), - manifest = _React$useState2[0], - setManifest = _React$useState2[1]; + var _useState = React.useState(manifestValue), + _useState2 = _slicedToArray(_useState, 2), + manifest = _useState2[0], + setManifest = _useState2[1]; var manifestDispatch = useManifestDispatch(); var playerDispatch = usePlayerDispatch(); var _useErrorBoundary = reactErrorBoundary.useErrorBoundary(), @@ -3748,7 +3748,7 @@ function IIIFPlayerWrapper(_ref) { case 0: controller = new AbortController(); requestOptions = { - // NOTE: try thin in Avalon + // NOTE: try this in Avalon //credentials: 'include', // headers: { 'Avalon-Api-Key': '' }, }; @@ -3793,7 +3793,7 @@ function IIIFPlayerWrapper(_ref) { return _ref2.apply(this, arguments); }; }(); - React__default["default"].useEffect(function () { + React.useEffect(function () { setAppErrorMessage(customErrorMessage); setAppEmptyManifestMessage(emptyManifestMessage); if (!manifest && manifestUrl) { @@ -3805,7 +3805,7 @@ function IIIFPlayerWrapper(_ref) { if (controller) controller.abort(); }; }, []); - React__default["default"].useEffect(function () { + React.useEffect(function () { if (manifest) { // Set customStart and rendering files in state before setting Manifest var renderingFiles = getRenderingFiles(manifest); @@ -3877,6 +3877,17 @@ ErrorMessage.propTypes = { children: PropTypes.object }; +/** + * Component with wrapped in React Contexts to provide access + * to global state across its children + * @param {Object} props + * @param {String} props.manifestUrl + * @param {Object} props.manifest + * @param {String} props.customErrorMessage + * @param {String} props.emptyManifestMessage + * @param {String} props.startCanvasId + * @param {String} props.startCanvasTime + */ function IIIFPlayer(_ref) { var manifestUrl = _ref.manifestUrl, manifest = _ref.manifest, @@ -5270,1956 +5281,1862 @@ var FileDownloadIcon = function FileDownloadIcon() { }))); }; -var classCallCheck = createCommonjsModule(function (module) { -function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } -} -module.exports = _classCallCheck, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); - -var _classCallCheck = /*@__PURE__*/getDefaultExportFromCjs(classCallCheck); - -var createClass = createCommonjsModule(function (module) { -function _defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, toPropertyKey(descriptor.key), descriptor); - } -} -function _createClass(Constructor, protoProps, staticProps) { - if (protoProps) _defineProperties(Constructor.prototype, protoProps); - if (staticProps) _defineProperties(Constructor, staticProps); - Object.defineProperty(Constructor, "prototype", { - writable: false - }); - return Constructor; -} -module.exports = _createClass, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); - -var _createClass = /*@__PURE__*/getDefaultExportFromCjs(createClass); - -var assertThisInitialized = createCommonjsModule(function (module) { -function _assertThisInitialized(self) { - if (self === void 0) { - throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); - } - return self; -} -module.exports = _assertThisInitialized, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); - -var _assertThisInitialized = /*@__PURE__*/getDefaultExportFromCjs(assertThisInitialized); - -var getPrototypeOf = createCommonjsModule(function (module) { -function _getPrototypeOf(o) { - module.exports = _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { - return o.__proto__ || Object.getPrototypeOf(o); - }, module.exports.__esModule = true, module.exports["default"] = module.exports; - return _getPrototypeOf(o); -} -module.exports = _getPrototypeOf, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); - -var _getPrototypeOf = /*@__PURE__*/getDefaultExportFromCjs(getPrototypeOf); - -var superPropBase = createCommonjsModule(function (module) { -function _superPropBase(object, property) { - while (!Object.prototype.hasOwnProperty.call(object, property)) { - object = getPrototypeOf(object); - if (object === null) break; - } - return object; -} -module.exports = _superPropBase, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); - -var get = createCommonjsModule(function (module) { -function _get() { - if (typeof Reflect !== "undefined" && Reflect.get) { - module.exports = _get = Reflect.get.bind(), module.exports.__esModule = true, module.exports["default"] = module.exports; - } else { - module.exports = _get = function _get(target, property, receiver) { - var base = superPropBase(target, property); - if (!base) return; - var desc = Object.getOwnPropertyDescriptor(base, property); - if (desc.get) { - return desc.get.call(arguments.length < 3 ? target : receiver); - } - return desc.value; - }, module.exports.__esModule = true, module.exports["default"] = module.exports; +var taggedTemplateLiteral = createCommonjsModule(function (module) { +function _taggedTemplateLiteral(strings, raw) { + if (!raw) { + raw = strings.slice(0); } - return _get.apply(this, arguments); + return Object.freeze(Object.defineProperties(strings, { + raw: { + value: Object.freeze(raw) + } + })); } -module.exports = _get, module.exports.__esModule = true, module.exports["default"] = module.exports; +module.exports = _taggedTemplateLiteral, module.exports.__esModule = true, module.exports["default"] = module.exports; }); -var _get = /*@__PURE__*/getDefaultExportFromCjs(get); +var _taggedTemplateLiteral = /*@__PURE__*/getDefaultExportFromCjs(taggedTemplateLiteral); -var setPrototypeOf = createCommonjsModule(function (module) { -function _setPrototypeOf(o, p) { - module.exports = _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { - o.__proto__ = p; - return o; - }, module.exports.__esModule = true, module.exports["default"] = module.exports; - return _setPrototypeOf(o, p); -} -module.exports = _setPrototypeOf, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); +var _templateObject$1, _templateObject2, _templateObject3, _templateObject4; +function _createForOfIteratorHelper$2(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$2(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } +function _unsupportedIterableToArray$2(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$2(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$2(o, minLen); } +function _arrayLikeToArray$2(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } -var inherits = createCommonjsModule(function (module) { -function _inherits(subClass, superClass) { - if (typeof superClass !== "function" && superClass !== null) { - throw new TypeError("Super expression must either be null or a function"); - } - subClass.prototype = Object.create(superClass && superClass.prototype, { - constructor: { - value: subClass, - writable: true, - configurable: true - } - }); - Object.defineProperty(subClass, "prototype", { - writable: false - }); - if (superClass) setPrototypeOf(subClass, superClass); -} -module.exports = _inherits, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); +// ENum for supported transcript MIME types +var TRANSCRIPT_MIME_TYPES = { + webvtt: ['text/vtt'], + srt: ['application/x-subrip', 'text/srt'], + text: ['text/plain'], + json: ['application/json'], + docx: ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'] +}; +var VTT_TIMESTAMP_REGEX = /^(?:\d{2}:)?\d{2}:\d{2}(?:\.\d+)/g; +// SRT allows using comma for milliseconds while WebVTT does not +var SRT_TIMESTAMP_REGEX = /^(?:\d{2}:)?\d{2}:\d{2}(?:[.,]\d+)/g; +var TRANSCRIPT_MIME_EXTENSIONS = [{ + type: TRANSCRIPT_MIME_TYPES.json, + ext: 'json' +}, { + type: TRANSCRIPT_MIME_TYPES.webvtt, + ext: 'vtt' +}, { + type: TRANSCRIPT_MIME_TYPES.text, + ext: 'txt' +}, { + type: TRANSCRIPT_MIME_TYPES.docx, + ext: 'docx' +}, { + type: TRANSCRIPT_MIME_TYPES.srt, + ext: 'srt' +}]; -var _inherits = /*@__PURE__*/getDefaultExportFromCjs(inherits); +// ENum for describing transcript types include invalid and no transcript info +var TRANSCRIPT_TYPES = { + invalidTimestamp: -4, + invalidVTT: -3, + noSupport: -2, + invalid: -1, + noTranscript: 0, + timedText: 1, + plainText: 2, + docx: 3 +}; -var possibleConstructorReturn = createCommonjsModule(function (module) { -var _typeof = _typeof_1["default"]; +// ENum for types transcript text lines in a time-synced transcript +var TRANSCRIPT_CUE_TYPES = { + note: 'NOTE', + timedCue: 'TIMED_CUE', + nonTimedLine: 'NON_TIMED_LINE' +}; -function _possibleConstructorReturn(self, call) { - if (call && (_typeof(call) === "object" || typeof call === "function")) { - return call; - } else if (call !== void 0) { - throw new TypeError("Derived constructors may only return object or undefined"); - } - return assertThisInitialized(self); +/** + * Parse the transcript information in the Manifest presented as supplementing annotations + * @param {String} manifestURL IIIF Presentation 3.0 manifest URL + * @param {String} title optional title given in the transcripts list in props + * @returns {Array} array of supplementing annotations for transcripts for all + * canvases in the Manifest + */ +function readSupplementingAnnotations(_x) { + return _readSupplementingAnnotations.apply(this, arguments); } -module.exports = _possibleConstructorReturn, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); - -var _possibleConstructorReturn = /*@__PURE__*/getDefaultExportFromCjs(possibleConstructorReturn); -function _createSuper$6(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$6(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } -function _isNativeReflectConstruct$6() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } -var SeekBar = videojs__default["default"].getComponent('SeekBar'); -var VideoJSProgress = /*#__PURE__*/function (_SeekBar) { - _inherits(VideoJSProgress, _SeekBar); - var _super = _createSuper$6(VideoJSProgress); - function VideoJSProgress(player, options) { - var _this; - _classCallCheck(this, VideoJSProgress); - _this = _super.call(this, player, options); - /** - * Set start values for progress bar - * @param {Number} start canvas start time - */ - _defineProperty(_assertThisInitialized(_this), "initializeProgress", function (start) { - _this.setProgress(start); - _this.setInitTime(start); - _this.player.currentTime(start); - }); - _this.addClass('vjs-custom-progress-bar'); - _this.setAttribute('data-testid', 'videojs-custom-progressbar'); - _this.setAttribute('tabindex', 0); - _this.player = player; - _this.options = options; - _this.selectSource = _this.options.nextItemClicked; - _this.playerEventListener; - _this.initTimeRef = /*#__PURE__*/React__default["default"].createRef(); - _this.progressRef = /*#__PURE__*/React__default["default"].createRef(); - _this.canvasTargetsRef = /*#__PURE__*/React__default["default"].createRef(); - _this.srcIndexRef = /*#__PURE__*/React__default["default"].createRef(); - _this.isMultiSourceRef = /*#__PURE__*/React__default["default"].createRef(); - _this.currentTimeRef = /*#__PURE__*/React__default["default"].createRef(); - _this.pointerDragged = false; - _this.totalDuration; - _this.playProgress = _this.getChild('PlayProgressBar'); - _this.loadProgress = _this.getChild('LoadProgressBar'); - _this.player.on('ready', function () { - _this.initializeEl(); - _this.updateComponent(); - }); - _this.player.on('loadstart', function () { - _this.updateComponent(); - _this.buildProgressBar(); - }); - - // Update our progress bar after the user leaves full screen - _this.player.on('fullscreenchange', function (e) { - if (!_this.player.isFullscreen()) { - _this.setProgress(_this.player.currentTime()); - } - }); - _this.player.on('dispose', function () { - clearInterval(_this.playerEventListener); - }); - return _this; - } - _createClass(VideoJSProgress, [{ - key: "setInitTime", - value: function setInitTime(t) { - this.initTimeRef.current = t; - } - }, { - key: "setSrcIndex", - value: function setSrcIndex(i) { - this.srcIndexRef.current = i; - } - }, { - key: "setProgress", - value: function setProgress(p) { - this.progressRef.current = p; - } - }, { - key: "setCanvasTargets", - value: function setCanvasTargets(t) { - this.canvasTargetsRef.current = t; - this.totalDuration = t.reduce(function (acc, c) { - return acc + c.duration; - }, 0); - } - }, { - key: "setIsMultiSource", - value: function setIsMultiSource(m) { - this.isMultiSourceRef.current = m; - } - }, { - key: "setCurrentTime", - value: function setCurrentTime(t) { - this.currentTimeRef.current = t; - } - }, { - key: "updateComponent", - value: function updateComponent() { - var _this2 = this; - var _this$player = this.player, - srcIndex = _this$player.srcIndex, - targets = _this$player.targets; - this.setSrcIndex(srcIndex); - this.setCanvasTargets(targets); - var cTimes = targets[srcIndex]; - if (cTimes.customStart > cTimes.start) { - this.initializeProgress(cTimes.customStart); - } else { - this.initializeProgress(cTimes.start); +/** + * Refine and sanitize the user provided transcripts list in the props. If there are manifests + * in the given array process them to find supplementing annotations in the manifest and + * them to the transcripts array to be displayed in the component. + * @param {Array} transcripts list of transcripts from Transcript component's props + * @returns {Array} a refined transcripts array for each canvas with the following json + * structure; + * { canvasId: , items: [{ title, filename, url, isMachineGen, id }]} + */ +function _readSupplementingAnnotations() { + _readSupplementingAnnotations = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee(manifestURL) { + var title, + data, + _args = arguments; + return regenerator.wrap(function _callee$(_context) { + while (1) switch (_context.prev = _context.next) { + case 0: + title = _args.length > 1 && _args[1] !== undefined ? _args[1] : ''; + _context.next = 3; + return fetch(manifestURL).then(function (response) { + var fileType = response.headers.get('Content-Type'); + if (fileType.includes('application/json')) { + var jsonData = response.json(); + return jsonData; + } else { + // Avoid throwing an error when fetched file is not a JSON + return {}; + } + }).then(function (manifest) { + var canvases = manifest.items; + var newTranscriptsList = []; + if ((canvases === null || canvases === void 0 ? void 0 : canvases.length) > 0) { + canvases.map(function (canvas, index) { + var annotations = getAnnotations(canvas.annotations, 'supplementing'); + var canvasTranscripts = []; + if (annotations.length > 0) { + var annotBody = annotations[0].body; + if (annotBody.type === 'TextualBody') { + var label = title.length > 0 ? title : annotBody.label ? getLabelValue(annotBody.label) : "Canvas-".concat(index); + var _identifyMachineGen = identifyMachineGen(label), + isMachineGen = _identifyMachineGen.isMachineGen, + labelText = _identifyMachineGen.labelText; + canvasTranscripts.push({ + url: annotBody.id === undefined ? manifestURL : annotBody.id, + title: labelText, + isMachineGen: isMachineGen, + id: "".concat(labelText, "-").concat(index), + format: '' + }); + } else { + annotations.forEach(function (annotation, i) { + var annotBody = annotation.body; + var label = ''; + var filename = ''; + if (annotBody.label && Object.keys(annotBody.label).length > 0) { + var languages = Object.keys(annotBody.label); + if ((languages === null || languages === void 0 ? void 0 : languages.length) > 1) { + // If there are multiple labels for an annotation assume the first + // is the one intended for default display. + label = getLabelValue(annotBody.label); + // Assume that an unassigned language is meant to be the downloadable filename + filename = annotBody.label.hasOwnProperty('none') ? getLabelValue(annotBody.label.none[0]) : label; + } else { + // If there is a single label, use for both label and downloadable filename + label = getLabelValue(annotBody.label); + } + } else { + label = "".concat(i); + } + var id = annotBody.id; + var sType = identifySupplementingAnnotation(id); + var _identifyMachineGen2 = identifyMachineGen(label), + isMachineGen = _identifyMachineGen2.isMachineGen, + labelText = _identifyMachineGen2.labelText; + if (filename === '') { + filename = labelText; + } + if (sType === 1 || sType === 3) { + canvasTranscripts.push({ + title: labelText, + filename: filename, + url: id, + isMachineGen: isMachineGen, + id: "".concat(labelText, "-").concat(index, "-").concat(i), + format: annotBody.format || '' + }); + } + }); + } + } + newTranscriptsList.push({ + canvasId: index, + items: canvasTranscripts + }); + }); + } + return newTranscriptsList; + })["catch"](function (error) { + console.error('transcript-parser -> readSupplementingAnnotations() -> error fetching transcript resource at, ', manifestURL); + return []; + }); + case 3: + data = _context.sent; + return _context.abrupt("return", data); + case 5: + case "end": + return _context.stop(); } - this.setIsMultiSource((targets === null || targets === void 0 ? void 0 : targets.length) > 1 ? true : false); - if (!this.playerEventListener) { - /** - * Using a time interval instead of 'timeupdate event in VideoJS, because Safari - * and other browsers in MacOS stops firing the 'timeupdate' event consistently - * after a while - */ - this.playerEventListener = setInterval(function () { - /** - * Abortable inerval for Safari desktop browsers, for a smoother scrubbing - * experience. - * Mobile devices are excluded since they use native iOS player. - */ - if (IS_SAFARI && !IS_IPHONE) { - _this2.abortableTimeupdateHandler(); - } else { - _this2.timeUpdateHandler(); + }, _callee); + })); + return _readSupplementingAnnotations.apply(this, arguments); +} +function sanitizeTranscripts(_x2) { + return _sanitizeTranscripts.apply(this, arguments); +} + +/** + * Group a nested JSON object array by a given property name + * @param {Array} objectArray nested array to reduced + * @param {String} indexKey property name to be used to group elements in the array + * @param {String} selectKey property to be selected from the objects to accumulated + * @returns {Array} + */ +function _sanitizeTranscripts() { + _sanitizeTranscripts = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee4(transcripts) { + var allTranscripts, sanitizedTrs, newTranscripts; + return regenerator.wrap(function _callee4$(_context4) { + while (1) switch (_context4.prev = _context4.next) { + case 0: + if (!(!transcripts || transcripts == undefined || transcripts.length == 0)) { + _context4.next = 5; + break; } - }, 100); - } - } - }, { - key: "update", - value: function update() { - // Need this to make the other updates work - _get(_getPrototypeOf(VideoJSProgress.prototype), "update", this).call(this); - // Explicitly played range variable on update for touch devices - if (IS_TOUCH_ONLY && this.player.currentTime() === 0) { - this.removeClass('played-range'); - document.documentElement.style.setProperty('--range-progress', "calc(".concat(0, "%)")); - } - if (IS_MOBILE && IS_SAFARI && this.player.paused()) { - var _this$player$structSt; - var structStart = (_this$player$structSt = this.player.structStart) !== null && _this$player$structSt !== void 0 ? _this$player$structSt : 0; - if (structStart != 0 && this.player.currentTime() === 0) { - this.player.currentTime(structStart); - var played = Math.min(100, Math.max(0, 100 * (structStart / this.totalDuration))); - this.addClass('played-range'); - document.documentElement.style.setProperty('--range-progress', "calc(".concat(played, "%)")); - this.player.structStart = 0; - } - } else { - return; - } - } - }, { - key: "initializeEl", - value: function initializeEl() { - var _this3 = this; - var leftBlock = videojs__default["default"].dom.createEl('div', { - className: 'block-stripes', - role: 'presentation', - id: 'left-block' - }); - var rightBlock = videojs__default["default"].dom.createEl('div', { - className: 'block-stripes', - role: 'presentation', - id: 'right-block' - }); - this.el().appendChild(leftBlock); - this.el().appendChild(rightBlock); + console.error('No transcripts given as input'); + return _context4.abrupt("return", []); + case 5: + allTranscripts = []; // Build an empty list for each canvasId from the given transcripts prop + transcripts.map(function (trs) { + return allTranscripts.push({ + canvasId: trs.canvasId, + items: [] + }); + }); - /** - * Add eventlisteners to handle time tool-tip display and progress updates. - * Using pointerup, pointermove, pointerdown events instead of mouseup, - * mousemove, mousedown events to make it work with both mouse pointer - * and touch events. - */ - this.el().addEventListener('mouseenter', function (e) { - _this3.handleMouseMove(e); - }); - this.el().addEventListener('pointerup', function (e) { - if (_this3.pointerDragged) { - _this3.handleMouseUp(e); - } - }); - this.el().addEventListener('pointermove', function (e) { - _this3.handleMouseMove(e); - _this3.pointerDragged = true; - }); - this.el().addEventListener('pointerdown', function (e) { - _this3.handleMouseDown(e); - _this3.pointerDragged = false; - }); - } - }, { - key: "handleMouseMove", - value: function handleMouseMove(e) { - var _this$convertToTime = this.convertToTime(e), - currentTime = _this$convertToTime.currentTime, - offsetx = _this$convertToTime.offsetx; - if (currentTime != undefined) this.setCurrentTime(currentTime); - var mouseTimeDisplay = this.getChild('MouseTimeDisplay'); - if (mouseTimeDisplay) { - var timeTooltip = mouseTimeDisplay.getChild('TimeTooltip'); - var toolTipEl = timeTooltip.el_; - if (currentTime) { - toolTipEl.innerHTML = timeToHHmmss(currentTime); - } - var pullTooltip = toolTipEl.clientWidth / 2; - toolTipEl.style.left = "".concat(offsetx - pullTooltip, "px"); - } - } - }, { - key: "handleMouseDown", - value: function handleMouseDown(e) { - // Do nothing when right-click is pressed - if (!IS_TOUCH_ONLY && e.buttons === 2) return; - var _this$convertToTime2 = this.convertToTime(e), - currentTime = _this$convertToTime2.currentTime; - _this$convertToTime2._; - var clickedSrc; - if (this.isMultiSourceRef.current) { - clickedSrc = this.canvasTargetsRef.current.find(function (t) { - var virtualEnd = t.altStart + t.duration; - if (currentTime >= t.altStart && currentTime <= virtualEnd) { - return t; - } - }); - } - if (clickedSrc) { - var _clickedSrc$sIndex, _clickedSrc; - var clickedIndex = (_clickedSrc$sIndex = (_clickedSrc = clickedSrc) === null || _clickedSrc === void 0 ? void 0 : _clickedSrc.sIndex) !== null && _clickedSrc$sIndex !== void 0 ? _clickedSrc$sIndex : 0; - if (clickedIndex != this.srcIndexRef.current) { - this.selectSource(clickedSrc.sIndex, currentTime - clickedSrc.altStart); - this.setSrcIndex(clickedIndex); - } else { - this.player.currentTime(currentTime - clickedSrc.altStart); - } - } else { - this.player.currentTime(currentTime); - } + // Process the async function to resolve manifest URLs in the given transcripts array + // parallely to extract supplementing annotations in the manifests + _context4.next = 9; + return Promise.all(transcripts.map( /*#__PURE__*/function () { + var _ref5 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee3(transcript) { + var canvasId, items, sanitizedItems; + return regenerator.wrap(function _callee3$(_context3) { + while (1) switch (_context3.prev = _context3.next) { + case 0: + canvasId = transcript.canvasId, items = transcript.items; + _context3.next = 3; + return Promise.all(items.map( /*#__PURE__*/function () { + var _ref6 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee2(item, index) { + var title, url, manifestTranscripts, _identifyMachineGen3, isMachineGen, labelText, manifestItems, groupedTrs; + return regenerator.wrap(function _callee2$(_context2) { + while (1) switch (_context2.prev = _context2.next) { + case 0: + title = item.title, url = item.url; // For each item in the list check if it is a manifest and parse + // the it to identify any supplementing annotations in the + // manifest for each canvas + _context2.next = 3; + return readSupplementingAnnotations(url, title); + case 3: + manifestTranscripts = _context2.sent; + _identifyMachineGen3 = identifyMachineGen(title), isMachineGen = _identifyMachineGen3.isMachineGen, labelText = _identifyMachineGen3.labelText; + manifestItems = []; + if ((manifestTranscripts === null || manifestTranscripts === void 0 ? void 0 : manifestTranscripts.length) > 0) { + manifestItems = manifestTranscripts.map(function (mt) { + return mt.items; + }).flat(); - /** - * For touch devices, player.currentTime() update doesn't show the - * played range, even though the player's currentTime is properly set. - * Therefore, update the CSS here explicitly. - */ - if (IS_TOUCH_ONLY) { - var played = Math.min(100, Math.max(0, 100 * (currentTime / this.totalDuration))); - this.player.currentTime(currentTime); - this.addClass('played-range'); - document.documentElement.style.setProperty('--range-progress', "calc(".concat(played, "%)")); - } - } - }, { - key: "handleMouseUp", - value: function handleMouseUp(e) { - this.handleMouseDown(e); - } - }, { - key: "buildProgressBar", - value: function buildProgressBar() { - var _canvasTargetsRef$cur; - // Reset progress-bar for played range - this.removeClass('played-range'); - var canvasTargetsRef = this.canvasTargetsRef, - isMultiSourceRef = this.isMultiSourceRef, - player = this.player, - srcIndexRef = this.srcIndexRef, - totalDuration = this.totalDuration; - if (((_canvasTargetsRef$cur = canvasTargetsRef.current) === null || _canvasTargetsRef$cur === void 0 ? void 0 : _canvasTargetsRef$cur.length) > 0) { - var _canvasTargetsRef$cur2 = canvasTargetsRef.current[srcIndexRef.current], - altStart = _canvasTargetsRef$cur2.altStart, - start = _canvasTargetsRef$cur2.start, - end = _canvasTargetsRef$cur2.end, - duration = _canvasTargetsRef$cur2.duration; - var leftBlockEl = document.getElementById('left-block'); - var rightBlockEl = document.getElementById('right-block'); - if (!isMultiSourceRef.current) { - var leftBlock = start * 100 / duration; - var rightBlock = (duration - end) * 100 / duration; + // Concat the existing transcripts list and transcripts from the manifest and + // group them by canvasId + groupedTrs = groupByIndex(allTranscripts.concat(manifestTranscripts), 'canvasId', 'items'); + allTranscripts = groupedTrs; + } - // Set player.isClipped to use in the ended event to decide to advance to next - rightBlock > 0 ? player.isClipped = true : player.isClipped = false; - if (leftBlockEl) leftBlockEl.style.width = "".concat(leftBlock, "%"); - if (rightBlockEl) { - rightBlockEl.style.width = rightBlock + '%'; - rightBlockEl.style.left = "".concat(100 - rightBlock - leftBlock, "%"); - } - } else { - // Calculate offset of the duration of the current source - var leftOffset = Math.min(100, Math.max(0, 100 * (altStart / totalDuration))); - this.playProgress.el_.style.left = "".concat(leftOffset, "%"); - this.loadProgress.el_.style.left = "".concat(leftOffset, "%"); - // Add CSS class to mark the range from zero as played - this.addClass('played-range'); - document.documentElement.style.setProperty('--range-progress', "calc(".concat(leftOffset, "%)")); - } + // if manifest doesn't have canvases or + // supplementing annotations add original transcript from props + if (!(manifestTranscripts.length === 0 || manifestItems.length === 0)) { + _context2.next = 11; + break; + } + return _context2.abrupt("return", { + title: labelText, + filename: labelText, + url: url, + isMachineGen: isMachineGen, + id: "".concat(labelText, "-").concat(canvasId, "-").concat(index), + format: '' + }); + case 11: + return _context2.abrupt("return", null); + case 12: + case "end": + return _context2.stop(); + } + }, _callee2); + })); + return function (_x9, _x10) { + return _ref6.apply(this, arguments); + }; + }())); + case 3: + sanitizedItems = _context3.sent; + return _context3.abrupt("return", { + canvasId: canvasId, + items: sanitizedItems.filter(function (i) { + return i != null; + }) + }); + case 5: + case "end": + return _context3.stop(); + } + }, _callee3); + })); + return function (_x8) { + return _ref5.apply(this, arguments); + }; + }())); + case 9: + sanitizedTrs = _context4.sent; + // Group all the transcripts by canvasId one last time to eliminate duplicate canvasIds + newTranscripts = groupByIndex(allTranscripts.concat(sanitizedTrs), 'canvasId', 'items'); + return _context4.abrupt("return", newTranscripts); + case 12: + case "end": + return _context4.stop(); } + }, _callee4); + })); + return _sanitizeTranscripts.apply(this, arguments); +} +function groupByIndex(objectArray, indexKey, selectKey) { + return objectArray.reduce(function (acc, obj) { + var existing = acc.filter(function (a) { + return a[indexKey] == obj[indexKey]; + }); + if ((existing === null || existing === void 0 ? void 0 : existing.length) > 0) { + var current = existing[0]; + current[selectKey] = current[selectKey].concat(obj[selectKey]); + } else { + acc.push(obj); } - }, { - key: "convertToTime", - value: function convertToTime(e) { - var _e$nativeEvent$target, _this$totalDuration; - var eSrcElement = e.srcElement; - // When clicked on blocked time point - if (eSrcElement.classList.contains('block-stripes')) { - var _this$canvasTargetsRe = this.canvasTargetsRef.current[0], - altStart = _this$canvasTargetsRe.altStart, - end = _this$canvasTargetsRe.end, - _duration = _this$canvasTargetsRe.duration; - if (eSrcElement.id === 'right-block') { - // For right-block: place time tool-tip at the end of playable range - return { - currentTime: end, - offsetx: end / _duration * this.el().clientWidth - }; - } else { - // For left-block: place time tool-tip at the start of playable range - return { - currentTime: altStart, - offsetx: altStart / _duration * this.el().clientWidth - }; - } - } - var targetX = e.target.getBoundingClientRect().x; - var offsetx = e.nativeEvent != undefined ? e.nativeEvent.offsetX != undefined ? e.nativeEvent.offsetX // iOS and desktop events - : ((_e$nativeEvent$target = e.nativeEvent.targetTouches[0]) === null || _e$nativeEvent$target === void 0 ? void 0 : _e$nativeEvent$target.clientX) - targetX // Android event - : e.offsetX; // fallback in desktop browsers when nativeEvent is undefined - var currentTime; - var duration = (_this$totalDuration = this.totalDuration) !== null && _this$totalDuration !== void 0 ? _this$totalDuration : this.player.duration(); - if (offsetx && offsetx != undefined) { - if (this.isMultiSourceRef.current) { - /** - * Check if the mouse event occurred on the same src range. - * If so, adjust the offset to support altStart for the current src. - */ - var leftOffset = parseFloat(this.playProgress.el_.style.left) / 100 * this.el().clientWidth; - var elClassList = eSrcElement.classList; - var sameSrc = (elClassList === null || elClassList === void 0 ? void 0 : elClassList.length) > 0 ? elClassList.contains('vjs-play-progress') || elClassList.contains('vjs-load-progress') : true; - if (leftOffset > offsetx && sameSrc) { - offsetx = offsetx + leftOffset; - } - } - currentTime = offsetx / this.el().clientWidth * duration; - } - /** - * Parts of LoadProgress element is broken into segments as media loads, and displayed - * as separate div elements with `data-start` and `data-end` attributes respectively. - * When mouse event occurs on top of such element, add the segment start time to calculated - * current time from event. - */ - if (e.target.hasAttribute('data-start')) { - var _e$target$dataset = e.target.dataset, - start = _e$target$dataset.start; - _e$target$dataset._; - currentTime = currentTime + parseFloat(start); - offsetx = currentTime * this.el().clientWidth / this.totalDuration; - } - return { - currentTime: currentTime, - offsetx: offsetx - }; - } - }, { - key: "abortableTimeupdateHandler", - value: - /** - * A wrapper function around the time update interval, to cancel - * intermediate updates via the time interval when player is - * waiting to fetch stream - */ - function abortableTimeupdateHandler() { - var _this4 = this; - var player = this.player, - progressRef = this.progressRef; - player.on('waiting', function () { - if (IS_SAFARI && !IS_MOBILE) { - player.currentTime(progressRef.current); - } - cancelInterval(); - }); - var cancelInterval = function cancelInterval() { - if (internalInterval) { - clearInterval(internalInterval); - } - }; - var internalInterval = setInterval(function () { - _this4.timeUpdateHandler(); - }, 100); - } - }, { - key: "timeUpdateHandler", - value: - // Update progress bar with timeupdate in the player - function timeUpdateHandler() { - var _this5 = this; - var initTimeRef = this.initTimeRef, - player = this.player; - if (player.isDisposed() || player.ended() || player == null) { - return; - } - var curTime; - // Initially update progress from the prop passed from Ramp, - // this accounts for structured navigation when switching canvases - if (initTimeRef.current > 0 && player.currentTime() == 0) { - curTime = initTimeRef.current; - player.currentTime(initTimeRef.current); - } else { - curTime = player.currentTime(); - } - // Use debounced updates since, Safari desktop browsers need the extra - // update on 'seeked' event to timely update the progress bar. - if (IS_SAFARI && !IS_MOBILE && player.paused()) { - debounce_1(function () { - _this5.onTimeUpdate(curTime); - }); - } else { - this.onTimeUpdate(curTime); - } - this.setInitTime(0); - } - }, { - key: "onTimeUpdate", - value: function onTimeUpdate(curTime) { - // This state update caused weird lagging behaviors when using the iOS native - // video player. iOS player handles its own progress bar, so we can skip the - // update here only for video. - var iOS = this.player.hasClass("vjs-ios-native-fs"); - if (!(iOS && !this.player.audioOnlyMode_)) { - this.setProgress(curTime); - } - this.handleTimeUpdate(curTime); - } - }, { - key: "handleTimeUpdate", - value: - /** - * Update CSS for the input range's track while the media - * is playing - * @param {Number} curTime current time of the player - */ - function handleTimeUpdate(curTime) { - var _srcIndexRef$current; - var player = this.player, - el_ = this.el_, - canvasTargetsRef = this.canvasTargetsRef, - srcIndexRef = this.srcIndexRef; - - // Avoid null player instance when Video.js is getting initialized - if (!el_ || !player || !canvasTargetsRef.current) { - return; - } - var _canvasTargetsRef$cur3 = canvasTargetsRef.current[(_srcIndexRef$current = srcIndexRef.current) !== null && _srcIndexRef$current !== void 0 ? _srcIndexRef$current : 0], - start = _canvasTargetsRef$cur3.start, - end = _canvasTargetsRef$cur3.end, - duration = _canvasTargetsRef$cur3.duration; - - // Restrict access to the intended range in the media file - if (curTime < start) { - player.currentTime(start); - } - if (curTime >= end && !player.paused() && !player.isDisposed()) { - // Trigger ended event when playable range < duration of the - // full media. e.g. clipped playlist items - if (end < duration) { - player.trigger('ended'); - } - - // On the next play event set the time to start or a seeked time - // in between the 'ended' event and 'play' event - // Reference: https://github.com/videojs/video.js/blob/main/src/js/control-bar/play-toggle.js#L128 - player.one('play', function () { - var time = player.currentTime(); - if (time < end) { - player.currentTime(time); - } else { - player.currentTime(start); - } - }); - } - } - }]); - return VideoJSProgress; -}(SeekBar); -videojs__default["default"].registerComponent('VideoJSProgress', VideoJSProgress); - -function _createSuper$5(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$5(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } -function _isNativeReflectConstruct$5() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } -var TimeDisplay = videojs__default["default"].getComponent('TimeDisplay'); + return acc; + }, []); +} /** - * Custom component to display the current time of the player - * @param {Object} props - * @param {Object} props.player VideoJS player instance - * @param {Object} props.options options passed into component - * options: { srcIndex, targets } + * Parse a given transcript file into a format the Transcript component + * can render on the UI. E.g.: text file -> returns null, so that the Google + * doc viewer is rendered, IIIF manifest -> extract and parse transcript data + * within the manifest. + * @param {String} url URL of the transcript file selected + * @param {Number} canvasIndex Current canvas rendered in the player + * @param {String} format transcript file format read from Annotation + * @returns {Object} Array of trancript data objects with download URL */ -var VideoJSCurrentTime = /*#__PURE__*/function (_TimeDisplay) { - _inherits(VideoJSCurrentTime, _TimeDisplay); - var _super = _createSuper$5(VideoJSCurrentTime); - function VideoJSCurrentTime(player, options) { - var _this; - _classCallCheck(this, VideoJSCurrentTime); - _this = _super.call(this, player, options); - _this.addClass('vjs-time-control vjs-current-time-display'); - _this.setAttribute('role', 'presentation'); - _this.player = player; - _this.options = options; - _this.initTimeRef = /*#__PURE__*/React__default["default"].createRef(); - _this.initTimeRef.current = options.currentTime; - _this.playerInterval; - _this.player.on('loadstart', function () { - _this.playerInterval = setInterval(function () { - _this.handleTimeUpdate(); - }, 100); - }); - _this.player.on('seeked', function () { - if (IS_SAFARI && !IS_MOBILE) { - _this.updateTextNode_(player.currentTime()); - } - }); +function parseTranscriptData(_x3, _x4, _x5) { + return _parseTranscriptData.apply(this, arguments); +} - // Update our timer after the user leaves full screen - _this.player.on('fullscreenchange', function () { - if (!player.isFullscreen()) { - _this.updateTextNode_(player.currentTime()); - } - }); - _this.player.on('dispose', function () { - clearInterval(_this.playerInterval); - }); - return _this; - } - _createClass(VideoJSCurrentTime, [{ - key: "buildCSSClass", - value: function buildCSSClass() { - return 'current-time'; - } - }, { - key: "setInitTime", - value: function setInitTime(t) { - this.initTimeRef.current = t; - } - }, { - key: "handleTimeUpdate", - value: function handleTimeUpdate() { - var player = this.player, - initTimeRef = this.initTimeRef; - var targets = player.targets, - srcIndex = player.srcIndex; - if (!player || player.isDisposed() || !targets) { - return; - } - var iOS = player.hasClass('vjs-ios-native-fs'); - var time; - // Update time from the given initial time if it is not zero - if (initTimeRef.current > 0 && player.currentTime() == 0) { - time = initTimeRef.current; - } else { - time = player.currentTime(); - } - var _targets = targets[srcIndex !== null && srcIndex !== void 0 ? srcIndex : 0], - start = _targets.start, - altStart = _targets.altStart; - if (altStart != start && srcIndex > 0) { - time = time + altStart; - } - // This state update caused weird lagging behaviors when using the iOS native - // video player. iOS player handles its own time, so we can skip the update here - // video items. - if (!(iOS && !player.audioOnlyMode_)) { - this.updateTextNode_(time); - } - this.setInitTime(0); - } - }]); - return VideoJSCurrentTime; -}(TimeDisplay); -videojs__default["default"].registerComponent('VideoJSCurrentTime', VideoJSCurrentTime); +/** + * Parse MS word documents into HTML markdown using mammoth.js + * https://www.npmjs.com/package/mammoth + * @param {Object} response response from the fetch request + * @returns {Array} html markdown for the word document contents + */ +function _parseTranscriptData() { + _parseTranscriptData = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee5(url, canvasIndex, format) { + var tData, tUrl, contentType, fileData, fromContentType, fromAnnotFormat, fileType, urlExt, filteredExt, textData, textLines, jsonData, json, parsedText, _parseTimedText, _tData, tType; + return regenerator.wrap(function _callee5$(_context5) { + while (1) switch (_context5.prev = _context5.next) { + case 0: + tData = []; + tUrl = url; // Validate given URL + if (!(url === undefined)) { + _context5.next = 4; + break; + } + return _context5.abrupt("return", { + tData: tData, + tUrl: tUrl, + tType: TRANSCRIPT_TYPES.invalid + }); + case 4: + contentType = null; + fileData = null; // get file type + _context5.next = 8; + return fetch(url).then(handleFetchErrors).then(function (response) { + contentType = response.headers.get('Content-Type'); + fileData = response; + })["catch"](function (error) { + console.error('transcript-parser -> parseTranscriptData() -> fetching transcript -> ', error); + }); + case 8: + if (!(contentType == null)) { + _context5.next = 10; + break; + } + return _context5.abrupt("return", { + tData: [], + tUrl: tUrl, + tType: TRANSCRIPT_TYPES.invalid + }); + case 10: + /* + Use the Annotation format in the IIIF Manifest, file extension, and the + Content-Type in headers of the fetch request to determine the file type. + These are checked with priority descending in the order of Annotation format, + Content-Type in headers, and file extension in the resource URI. + */ + fromContentType = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { + return tm.type.includes(contentType.split(';')[0]); + }); + fromAnnotFormat = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { + return tm.type.includes(format); + }); + fileType = ''; + if ((fromAnnotFormat === null || fromAnnotFormat === void 0 ? void 0 : fromAnnotFormat.length) > 0) { + fileType = fromAnnotFormat[0].ext; + } else if (fromContentType.length > 0) { + fileType = fromContentType[0].ext; + } else { + urlExt = url.split('.').reverse()[0]; // Only use this if it exists in the supported list of file types for the component + filteredExt = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { + return tm.ext === urlExt; + }); + fileType = filteredExt.length > 0 ? urlExt : ''; + } -function _createSuper$4(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$4(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } -function _isNativeReflectConstruct$4() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } -var MenuButton = videojs__default["default"].getComponent('MenuButton'); -var MenuItem = videojs__default["default"].getComponent('MenuItem'); -var VideoJSFileDownload = /*#__PURE__*/function (_MenuButton) { - _inherits(VideoJSFileDownload, _MenuButton); - var _super = _createSuper$4(VideoJSFileDownload); - function VideoJSFileDownload(player, options) { - var _this; - _classCallCheck(this, VideoJSFileDownload); - _this = _super.call(this, player, options); - // Add SVG icon through CSS class - _this.addClass("vjs-file-download"); - _this.setAttribute('data-testid', 'videojs-file-download'); - // Use Video.js' stock SVG instead of setting it using CSS - _this.setIcon('file-download'); - return _this; - } - _createClass(VideoJSFileDownload, [{ - key: "createItems", - value: function createItems() { - var options_ = this.options_, - player_ = this.player_; - var files = options_.files; - if ((files === null || files === void 0 ? void 0 : files.length) > 0) { - return files.map(function (file) { - var item = new MenuItem(player_, { - label: file.label + // Return empty array to display an error message + if (!(canvasIndex === undefined)) { + _context5.next = 16; + break; + } + return _context5.abrupt("return", { + tData: tData, + tUrl: tUrl, + tType: TRANSCRIPT_TYPES.noTranscript }); - item.handleClick = function () { - fileDownload(file.id, file.filename, file.fileExt); - }; - return item; - }); - } else { - return []; + case 16: + _context5.t0 = fileType; + _context5.next = _context5.t0 === 'json' ? 19 : _context5.t0 === 'txt' ? 28 : _context5.t0 === 'srt' ? 39 : _context5.t0 === 'vtt' ? 39 : _context5.t0 === 'docx' ? 49 : 53; + break; + case 19: + _context5.next = 21; + return fileData.json(); + case 21: + jsonData = _context5.sent; + if (!((jsonData === null || jsonData === void 0 ? void 0 : jsonData.type) === 'Manifest')) { + _context5.next = 26; + break; + } + return _context5.abrupt("return", parseManifestTranscript(jsonData, url, canvasIndex)); + case 26: + json = parseJSONData(jsonData); + return _context5.abrupt("return", { + tData: json.tData, + tUrl: tUrl, + tType: json.tType, + tFileExt: fileType + }); + case 28: + _context5.next = 30; + return fileData.text(); + case 30: + textData = _context5.sent; + textLines = textData.split('\n'); + if (!(textLines.length == 0)) { + _context5.next = 36; + break; + } + return _context5.abrupt("return", { + tData: [], + tUrl: url, + tType: TRANSCRIPT_TYPES.noTranscript + }); + case 36: + parsedText = buildNonTimedText(textLines); + return _context5.abrupt("return", { + tData: parsedText, + tUrl: url, + tType: TRANSCRIPT_TYPES.plainText, + tFileExt: fileType + }); + case 38: + case 39: + _context5.next = 41; + return fileData.text(); + case 41: + textData = _context5.sent; + textLines = textData.split('\n'); + if (!(textLines.length == 0)) { + _context5.next = 47; + break; + } + return _context5.abrupt("return", { + tData: [], + tUrl: url, + tType: TRANSCRIPT_TYPES.noTranscript + }); + case 47: + _parseTimedText = parseTimedText(textData, fileType === 'srt'), _tData = _parseTimedText.tData, tType = _parseTimedText.tType; + return _context5.abrupt("return", { + tData: _tData, + tUrl: url, + tType: tType, + tFileExt: fileType + }); + case 49: + _context5.next = 51; + return parseWordFile(fileData); + case 51: + tData = _context5.sent; + return _context5.abrupt("return", { + tData: splitIntoElements(tData), + tUrl: url, + tType: TRANSCRIPT_TYPES.docx, + tFileExt: fileType + }); + case 53: + return _context5.abrupt("return", { + tData: [], + tUrl: url, + tType: TRANSCRIPT_TYPES.noSupport + }); + case 54: + case "end": + return _context5.stop(); } - } - }]); - return VideoJSFileDownload; -}(MenuButton); -videojs__default["default"].registerComponent('VideoJSFileDownload', VideoJSFileDownload); - -function _createSuper$3(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$3(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } -function _isNativeReflectConstruct$3() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } -var Button$2 = videojs__default["default"].getComponent('Button'); -var VideoJSNextButton = /*#__PURE__*/function (_Button) { - _inherits(VideoJSNextButton, _Button); - var _super = _createSuper$3(VideoJSNextButton); - function VideoJSNextButton(player, options) { - var _this; - _classCallCheck(this, VideoJSNextButton); - _this = _super.call(this, player, options); - // Use Video.js' stock SVG instead of setting it using CSS - _this.setIcon('next-item'); - _this.addClass('vjs-play-control vjs-control'); - _this.setAttribute('data-testid', 'videojs-next-button'); - _this.controlText('Next'); - _this.options = options; - _this.player = player; - _this.cIndex = options.canvasIndex; - - // Handle player reload or source change events - _this.player.on('loadstart', function () { - _this.updateComponent(); - }); - return _this; + }, _callee5); + })); + return _parseTranscriptData.apply(this, arguments); +} +function parseWordFile(_x6) { + return _parseWordFile.apply(this, arguments); +} +/** + * Parse json data into Transcript component friendly + * format + * @param {Object} jsonData array of JSON objects + * @returns {Object} + */ +function _parseWordFile() { + _parseWordFile = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee6(response) { + var tData, data, arrayBuffer; + return regenerator.wrap(function _callee6$(_context6) { + while (1) switch (_context6.prev = _context6.next) { + case 0: + tData = null; + _context6.next = 3; + return response.blob(); + case 3: + data = _context6.sent; + arrayBuffer = new File([data], name, { + type: response.headers.get('content-type') + }); + _context6.next = 7; + return mammoth__default["default"].convertToHtml({ + arrayBuffer: arrayBuffer + }).then(function (result) { + tData = result.value; + })["catch"](function (err) { + console.error(err); + }); + case 7: + return _context6.abrupt("return", tData); + case 8: + case "end": + return _context6.stop(); + } + }, _callee6); + })); + return _parseWordFile.apply(this, arguments); +} +function parseJSONData(jsonData) { + if (jsonData.length == 0) { + return { + tData: [], + tType: TRANSCRIPT_TYPES.noTranscript + }; } - _createClass(VideoJSNextButton, [{ - key: "updateComponent", - value: function updateComponent() { - var player = this.player; - if (player && player != undefined) { - var _player$children; - // When canvasIndex property is not set in the player instance use dataset. - // This happens rarely, but when it does previous button cannot be used. - if (player.canvasIndex === undefined && ((_player$children = player.children()) === null || _player$children === void 0 ? void 0 : _player$children.length) > 0) { - this.cIndex = Number(player.children()[0].dataset.canvasindex); - } else { - this.cIndex = player.canvasIndex; + var tData = []; + var _iterator = _createForOfIteratorHelper$2(jsonData), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var jd = _step.value; + if (jd.speaker) { + var speaker = jd.speaker, + spans = jd.spans; + var _iterator2 = _createForOfIteratorHelper$2(spans), + _step2; + try { + for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { + var span = _step2.value; + span.speaker = speaker; + tData.push(span); + } + } catch (err) { + _iterator2.e(err); + } finally { + _iterator2.f(); + } + } else { + var _iterator3 = _createForOfIteratorHelper$2(jd.spans), + _step3; + try { + for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { + var _span = _step3.value; + _span.format = 'text/plain'; + _span.tag = TRANSCRIPT_CUE_TYPES.timedCue; + tData.push(_span); + } + } catch (err) { + _iterator3.e(err); + } finally { + _iterator3.f(); } - } - if (this.options.playerFocusElement === 'nextBtn') { - this.el().focus(); - } - } - }, { - key: "handleClick", - value: function handleClick() { - this.handleNextClick(false); - } - }, { - key: "handleKeyDown", - value: function handleKeyDown(e) { - if (e.which === 32 || e.which === 13) { - e.stopPropagation(); - this.handleNextClick(true); - } - } - }, { - key: "handleNextClick", - value: function handleNextClick(isKeyDown) { - if (this.cIndex != this.options.lastCanvasIndex) { - this.options.switchPlayer(this.cIndex + 1, true, isKeyDown ? 'nextBtn' : ''); } } - }]); - return VideoJSNextButton; -}(Button$2); -videojs__default["default"].registerComponent('VideoJSNextButton', VideoJSNextButton); - -function _createSuper$2(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$2(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } -function _isNativeReflectConstruct$2() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } -var Button$1 = videojs__default["default"].getComponent('Button'); -var VideoJSPreviousButton = /*#__PURE__*/function (_Button) { - _inherits(VideoJSPreviousButton, _Button); - var _super = _createSuper$2(VideoJSPreviousButton); - function VideoJSPreviousButton(player, options) { - var _this; - _classCallCheck(this, VideoJSPreviousButton); - _this = _super.call(this, player, options); - // Use Video.js' stock SVG instead of setting it using CSS - _this.setIcon('previous-item'); - _this.addClass('vjs-play-control vjs-control'); - _this.setAttribute('data-testid', 'videojs-previous-button'); - _this.options = options; - _this.player = player; - _this.cIndex = options.canvasIndex; + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + return { + tData: tData, + tType: TRANSCRIPT_TYPES.timedText + }; +} - // Handle player reload or source change events - _this.player.on('loadstart', function () { - _this.updateComponent(); - }); - return _this; +/* Parsing annotations when transcript data is fed from a IIIF manifest */ +/** + * Parse a IIIF manifest and extracts the transcript data. + * IIIF manifests can present transcript data in a couple of different ways. + * 1. Using 'rendering' prop to link to an external file + * a. when the external file contains only text + * b. when the external file contains annotations + * 2. Using IIIF 'annotations' within the manifest + * @param {Object} manifest IIIF manifest data + * @param {String} manifestURL IIIF manifest URL + * @param {Number} canvasIndex Current canvas index + * @returns {Object} object with the structure; + * { tData: transcript data, tUrl: file url } + */ +function parseManifestTranscript(manifest, manifestURL, canvasIndex) { + var _manifest$items; + var tData = []; + var tUrl = manifestURL; + var isExternalAnnotation = false; + var annotations = []; + if (manifest.annotations) { + annotations = getAnnotations(manifest.annotations, 'supplementing'); + } else if (((_manifest$items = manifest.items) === null || _manifest$items === void 0 ? void 0 : _manifest$items.length) > 0) { + var _manifest$items$canva; + annotations = getAnnotations((_manifest$items$canva = manifest.items[canvasIndex]) === null || _manifest$items$canva === void 0 ? void 0 : _manifest$items$canva.annotations, 'supplementing'); } - _createClass(VideoJSPreviousButton, [{ - key: "updateComponent", - value: function updateComponent() { - var player = this.player; - if (player && player != undefined) { - var _player$children; - // When canvasIndex property is not set in the player instance use dataset. - // This happens rarely, but when it does previous button cannot be used. - if (player.canvasIndex === undefined && ((_player$children = player.children()) === null || _player$children === void 0 ? void 0 : _player$children.length) > 0) { - this.cIndex = Number(player.children()[0].dataset.canvasindex); - } else { - this.cIndex = player.canvasIndex; - } - } - this.controlText(this.cIndex == 0 ? 'Replay' : 'Previous'); - if (this.options.playerFocusElement === 'previousBtn') { - this.el().focus(); - } - } - }, { - key: "handleClick", - value: function handleClick() { - this.handlePreviousClick(false); - } - }, { - key: "handleKeyDown", - value: function handleKeyDown(e) { - if (e.which === 32 || e.which === 13) { - e.stopPropagation(); - this.handlePreviousClick(true); - } - } - }, { - key: "handlePreviousClick", - value: function handlePreviousClick(isKeyDown) { - if (this.cIndex > -1 && this.cIndex != 0) { - this.options.switchPlayer(this.cIndex - 1, true, isKeyDown ? 'previousBtn' : ''); - } else if (this.cIndex == 0) { - this.player.currentTime(0); - } + + // determine whether annotations point to an external resource or + // a list of transcript fragments + if (annotations.length > 0) { + var annotation = annotations[0]; + var tType = annotation.body.type; + if (tType == 'TextualBody') { + isExternalAnnotation = false; + } else { + isExternalAnnotation = true; } - }]); - return VideoJSPreviousButton; -}(Button$1); -videojs__default["default"].registerComponent('VideoJSPreviousButton', VideoJSPreviousButton); + } else { + return { + tData: [], + tUrl: tUrl, + tType: TRANSCRIPT_TYPES.noTranscript + }; + } + if (isExternalAnnotation) { + var _annotation = annotations[0]; + return parseExternalAnnotations(_annotation); + } else { + tData = createTData(annotations); + return { + tData: tData, + tUrl: tUrl, + tType: TRANSCRIPT_TYPES.timedText, + tFileExt: 'json' + }; + } +} -function _createSuper$1(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$1(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } -function _isNativeReflectConstruct$1() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } -var vjsComponent = videojs__default["default"].getComponent('Component'); -var VideoJSTitleLink = /*#__PURE__*/function (_vjsComponent) { - _inherits(VideoJSTitleLink, _vjsComponent); - var _super = _createSuper$1(VideoJSTitleLink); - function VideoJSTitleLink(player, options) { - var _this; - _classCallCheck(this, VideoJSTitleLink); - _this = _super.call(this, player, options); - _this.setAttribute('data-testid', 'videojs-title-link'); - _this.addClass('vjs-title-bar'); - _this.options = options; - _this.player = player; - - // Handle player reload or source change events - _this.player.on('loadstart', function () { - _this.updateComponent(); - }); - return _this; - } - _createClass(VideoJSTitleLink, [{ - key: "updateComponent", - value: function updateComponent() { - var player = this.player; - if (player && player != undefined && player.canvasLink) { - var _player$canvasLink = player.canvasLink, - label = _player$canvasLink.label, - id = _player$canvasLink.id; - var title = label; - var href = null; - /** - * Avalon canvas ids are of the form 'http://host.edu/media_objects/#mo_id/manifest/canvas/#section_id`. - * Accessible url is 'http://host.edu/media_objects/#mo_id/section/#section_id' so we convert the canvas - * id for avalon manifest, but must assume other implementers will have the id as an actionable link. - */ - if (id.includes('manifest/canvas')) { - href = id.replace('manifest/canvas', 'section'); - } else { - href = id; - } - var link = videojs__default["default"].dom.createEl('a', { - className: 'vjs-title-link', - href: href, - target: '_blank', - rel: 'noreferrer noopener', - innerHTML: title - }); - if (this.el().hasChildNodes()) { - this.el().replaceChildren(link); - } else { - this.el().appendChild(link); - } +/** + * Parse annotation linking to external resources like WebVTT, SRT, Text, and + * AnnotationPage .json files + * @param {Annotation} annotation Annotation from the manifest + * @returns {Object} object with the structure { tData: [], tUrl: '', tType: '' } + */ +function parseExternalAnnotations(_x7) { + return _parseExternalAnnotations.apply(this, arguments); +} +/** + * Converts Annotation to the common format that the + * transcripts component expects + * @param {Array} annotations array of Annotations + * @returns {Array} array of JSON objects + * Structure of the JSON object is as follows; + * { + * begin: 0, + * end: 60, + * text: 'Transcript text', + * format: 'text/plain', + * } + */ +function _parseExternalAnnotations() { + _parseExternalAnnotations = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee7(annotation) { + var tData, type, tBody, tUrl, tType, tFormat, tFileExt; + return regenerator.wrap(function _callee7$(_context7) { + while (1) switch (_context7.prev = _context7.next) { + case 0: + tData = []; + type = ''; + tBody = annotation.body; + tUrl = tBody.id; + tType = tBody.type; + tFormat = tBody.format; + tFileExt = ''; + /** When external file contains text data */ + if (!(tType === 'Text')) { + _context7.next = 12; + break; + } + _context7.next = 10; + return fetch(tUrl).then(handleFetchErrors).then(function (response) { + return response.text(); + }).then(function (data) { + if (TRANSCRIPT_MIME_TYPES.webvtt.includes(tFormat) || TRANSCRIPT_MIME_TYPES.srt.includes(tFormat)) { + var parsed = parseTimedText(data, TRANSCRIPT_MIME_TYPES.srt.includes(tFormat)); + tData = parsed.tData; + type = parsed.tType; + tFileExt = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { + return tm.type.includes(tFormat); + })[0].ext; + } else { + var textLines = data.split('\n'); + tData = buildNonTimedText(textLines); + type = TRANSCRIPT_TYPES.plainText; + tFileExt = 'txt'; + } + })["catch"](function (error) { + console.error('transcript-parser -> parseExternalAnnotations() -> fetching external transcript -> ', error); + throw error; + }); + case 10: + _context7.next = 15; + break; + case 12: + if (!(tType === 'AnnotationPage')) { + _context7.next = 15; + break; + } + _context7.next = 15; + return fetch(tUrl).then(handleFetchErrors).then(function (response) { + return response.json(); + }).then(function (data) { + var annotations = getAnnotations([data], 'supplementing'); + tData = createTData(annotations); + type = TRANSCRIPT_TYPES.timedText; + tFileExt = 'json'; + })["catch"](function (error) { + console.error('transcript-parser -> parseExternalAnnotations() -> fetching annotations -> ', error); + throw error; + }); + case 15: + return _context7.abrupt("return", { + tData: tData, + tUrl: tUrl, + tType: type, + tFileExt: tFileExt + }); + case 16: + case "end": + return _context7.stop(); } + }, _callee7); + })); + return _parseExternalAnnotations.apply(this, arguments); +} +function createTData(annotations) { + var tData = []; + annotations.map(function (a) { + if (a.id != null) { + var tBody = a.body; + var _getMediaFragment = getMediaFragment(a.target), + start = _getMediaFragment.start, + end = _getMediaFragment.end; + tData.push({ + text: tBody.value, + format: tBody.format, + begin: parseFloat(start), + end: parseFloat(end), + tag: TRANSCRIPT_CUE_TYPES.timedCue + }); } - }]); - return VideoJSTitleLink; -}(vjsComponent); -vjsComponent.registerComponent('VideoJSTitleLink', VideoJSTitleLink); + }); + return tData; +} -function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } -function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +/** + * Parsing transcript data from a given file with timed text + * @param {Object} fileData content in the transcript file + * @param {Boolean} isSRT given transcript file is an SRT + * @returns {Array} array of JSON objects of the following + * structure; + * { + * begin: '00:00:00.000', + * end: '00:01:00.000', + * text: 'Transcript text sample' + * tag: NOTE || TIMED_CUE + * } + */ +function parseTimedText(fileData) { + var isSRT = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + var tData = []; + var noteLines = []; -// SVG icons for zoom-in and zoom-out icons as strings -var zoomOutIconSVG = "\n\n \n \n \n \n \n \n"; -var zoomInIconSVG = "\n\n \n \n \n \n \n \n"; + // split file content into lines + var lines = fileData.split('\n'); -// Function to inject SVGs into the DOM -function injectSVGIcons() { - var svgContainer = document.createElement('div'); - svgContainer.style.display = 'none'; - svgContainer.innerHTML = "".concat(zoomOutIconSVG).concat(zoomInIconSVG, ""); - document.body.appendChild(svgContainer); -} + // For SRT files all of the file content is considered as cues + var cueLines = lines; + if (!isSRT) { + var _validateWebVTT = validateWebVTT(lines), + valid = _validateWebVTT.valid, + cue_lines = _validateWebVTT.cue_lines, + notes = _validateWebVTT.notes; + if (!valid) { + console.error('Invalid WebVTT file'); + return { + tData: [], + tType: TRANSCRIPT_TYPES.invalidVTT + }; + } + cueLines = cue_lines; + noteLines = notes; + } + var groups = groupTimedTextLines(cueLines); -// Call the function to inject SVG icons -injectSVGIcons(); -var Button = videojs__default["default"].getComponent('Button'); + // Add back the NOTE(s) in the header block + groups.unshift.apply(groups, _toConsumableArray(noteLines)); + var hasInvalidTimestamp = false; + for (var i = 0; i < groups.length;) { + var line = parseTimedTextLine(groups[i], isSRT); + if (!line) { + hasInvalidTimestamp || (hasInvalidTimestamp = true); + break; + } else { + tData.push(line); + i++; + } + } + return { + tData: hasInvalidTimestamp ? null : tData, + tType: hasInvalidTimestamp ? TRANSCRIPT_TYPES.invalidTimestamp : TRANSCRIPT_TYPES.timedText + }; +} /** - * Custom VideoJS component for displaying track view when - * there are tracks/structure timespans in the current Canvas - * @param {Object} options - * @param {Number} options.trackScrubberRef React ref to track scrubber element - * @param {Number} options.timeToolRef React ref to time tooltip element - * @param {Boolean} options.isPlaylist flag to indicate a playlist Manifest or not + * Validate WebVTT file with its header content + * @param {Array} lines WebVTT file content split into lines + * @returns {Boolean} */ -var VideoJSTrackScrubber = /*#__PURE__*/function (_Button) { - _inherits(VideoJSTrackScrubber, _Button); - var _super = _createSuper(VideoJSTrackScrubber); - function VideoJSTrackScrubber(player, options) { - var _this; - _classCallCheck(this, VideoJSTrackScrubber); - _this = _super.call(this, player, options); - _this.setAttribute('data-testid', 'videojs-track-scrubber-button'); - _this.addClass('vjs-button vjs-track-scrubber'); - _this.controlText('Toggle track scrubber'); - _this.el().innerHTML = "\n \n \n "; - _this.options = options; - _this.player = player; - _this.playerInterval; - _this.zoomedOutRef = /*#__PURE__*/React__default["default"].createRef(); - _this.currentTrackRef = /*#__PURE__*/React__default["default"].createRef(); - - // Attach interval on first load for time updates - _this.player.on('ready', function () { - if (_this.options.trackScrubberRef.current) { - _this.playerInterval = setInterval(function () { - _this.handleTimeUpdate(); - }, 100); - _this.attachListeners(); - } - }); +function validateWebVTT(lines) { + var firstLine = lines.shift().trim(); + if ((firstLine === null || firstLine === void 0 ? void 0 : firstLine.length) == 6 && firstLine === 'WEBVTT') { + var _validateWebVTTHeader = validateWebVTTHeaders(lines), + valid = _validateWebVTTHeader.valid, + cue_lines = _validateWebVTTHeader.cue_lines, + notes = _validateWebVTTHeader.notes; + return { + valid: valid, + cue_lines: cue_lines, + notes: notes + }; + } else { + return { + valid: false, + cue_lines: [], + notes: [] + }; + } +} - /* - When player is fully built and the trackScrubber element is initialized, - call method to mount React component. - */ - _this.player.on('loadstart', function () { - if (_this.options.trackScrubberRef.current) { - _this.updateComponent(); - if (!_this.playerInterval) { - _this.playerInterval = setInterval(function () { - _this.handleTimeUpdate(); - }, 100); - } - } - }); +/** + * Validate the text between 'WEBVTT' at the start and start of + * VTT cues. It looks for REGION and STYLE blocks and skips over these + * blocks. This doesn't validate the content within these blocks. + * When there's text in the header not followed by the keywords REGION and + * STYLE the WebVTT file is marked invalid. + * @param {Array} lines WebVTT file content split into lines + * @returns + */ +function validateWebVTTHeaders(lines) { + var endOfHeadersIndex = 0; + var firstCueIndex = 0; + var hasTextBeforeCues = false; + var notesInHeader = []; - // Hide track scrubber if it is displayed when player is going fullscreen - _this.player.on('fullscreenchange', function () { - if (_this.player.isFullscreen() && !_this.zoomedOutRef.current) { - var tempZoom = _this.zoomedOutRef.current; - _this.setZoomedOut(!tempZoom); + // Remove line numbers for vtt cues + lines = lines.filter(function (l) { + return Number(l) ? false : true; + }); + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + // Skip REGION and STYLE blocks as these are related to displaying cues as overlays + if (/^REGION$/.test(line.toUpperCase()) || /^STYLE$/.test(line.toUpperCase())) { + // Increment until an empty line is encountered within the header block + i++; + while (i < lines.length && (!lines[i] == '\r' || !lines[i] == '\n' || !lines[i] == '\r\n')) { + i++; } - }); - - // Clean up interval when player is disposed - _this.player.on('dispose', function () { - clearInterval(_this.playerInterval); - }); - return _this; - } - _createClass(VideoJSTrackScrubber, [{ - key: "setCurrentTrack", - value: function setCurrentTrack(t) { - this.currentTrackRef.current = t; + endOfHeadersIndex = i; } - }, { - key: "setZoomedOut", - value: function setZoomedOut(z) { - this.zoomedOutRef.current = z; - if (z) { - this.options.trackScrubberRef.current.classList.add('hidden'); - this.el().innerHTML = "\n \n \n "; - } else { - this.options.trackScrubberRef.current.classList.remove('hidden'); - this.el().innerHTML = "\n \n \n "; + // Gather comments presented as NOTE(s) in the header block to be displayed as transcript + else if (/^NOTE$/.test(line.toUpperCase())) { + var noteText = line; + i++; + // Increment until an empty line is encountered within the NOTE block + while (i < lines.length && (!lines[i] == '\r' || !lines[i] == '\n' || !lines[i] == '\r\n')) { + noteText = "".concat(noteText, "
").concat(lines[i].trim()); + i++; } + notesInHeader.push({ + times: '', + line: noteText, + tag: TRANSCRIPT_CUE_TYPES.note + }); } - }, { - key: "attachListeners", - value: function attachListeners() { - var _this2 = this; - var trackScrubberRef = this.options.trackScrubberRef; - this.updateComponent(); - if (trackScrubberRef.current) { - // Initialize the track scrubber's current time and duration - this.populateTrackScrubber(); - this.updateTrackScrubberProgressBar(); - var pointerDragged = false; - // Attach mouse pointer events to track scrubber progress bar - var _trackScrubberRef$cur = _slicedToArray(trackScrubberRef.current.children, 3); - _trackScrubberRef$cur[0]; - var progressBar = _trackScrubberRef$cur[1]; - _trackScrubberRef$cur[2]; - progressBar.addEventListener('mouseenter', function (e) { - _this2.handleMouseMove(e); - }); - /* - Using pointerup, pointermove, pointerdown events instead of - mouseup, mousemove, mousedown events to make it work with both - mouse pointer and touch events - */ - progressBar.addEventListener('pointerup', function (e) { - if (pointerDragged) { - _this2.handleSetProgress(e); - } - }); - progressBar.addEventListener('pointermove', function (e) { - _this2.handleMouseMove(e); - pointerDragged = true; - }); - progressBar.addEventListener('pointerdown', function (e) { - // Only handle left click event - if (e.which === 1) { - _this2.handleSetProgress(e); - pointerDragged = false; - } - }); - } + // Terminate validation once the first cue is reached + else if (line.includes('-->')) { + // Break the loop when it reaches the first vtt cue + firstCueIndex = i; + break; } - }, { - key: "updateComponent", - value: function updateComponent() { - // Reset refs to initial value - this.zoomedOutRef.current = true; - this.currentTrackRef.current = {}; + // Flag to check for invalid text before cue lines + else if (typeof line === 'string' && line.trim().length != 0) { + hasTextBeforeCues = true; } + } - /** - * Keydown event handler for the track button on the player controls, - * when using keyboard navigation - * @param {Event} e keydown event - */ - }, { - key: "handleKeyDown", - value: function handleKeyDown(e) { - if (e.which === 32 || e.which === 13) { - e.preventDefault(); - this.handleTrackScrubberClick(); - e.stopPropagation(); - } - } - }, { - key: "handleClick", - value: function handleClick() { - this.handleTrackScrubberClick(); - } + // Return the cues and comments in the header block when the given WebVTT is valid + if (firstCueIndex > endOfHeadersIndex && !hasTextBeforeCues) { + return { + valid: true, + cue_lines: lines.slice(firstCueIndex), + notes: notesInHeader + }; + } else { + return { + valid: false + }; + } +} - /** - * Click event handler for the track button on the player controls - */ - }, { - key: "handleTrackScrubberClick", - value: function handleTrackScrubberClick() { - var currentTrackRef = this.currentTrackRef, - player = this.player, - options = this.options; - // When player is not fully loaded on the page don't show the track scrubber - if (!options.trackScrubberRef.current || !currentTrackRef.current) return; +/** + * Group multi line transcript text values alongside the relevant + * timestamp values. E.g. converts, + * [ + * "00:00:00.000 --> 00:01:00.000", "Transcript", " from multiple lines", + * "00:03:00.000 --> 00:04:00.000", "Next transcript text", + * "NOTE This is a comment" + * ] + * into + * [ + * { times: "00:00:00.000 --> 00:01:00.000", line: "Transcript from multiple lines", tag: "TIMED_CUE" }, + * { times: "00:03:00.000 --> 00:04:00.000", line: "Next transcript text", tag: "TIMED_CUE" }, + * { times: "", line: "NOTE This is a comment", tag: "NOTE" } + * ] + * @param {Array} lines array of lines in the WebVTT file + * @returns {Array} + */ +function groupTimedTextLines(lines) { + var groups = []; + var i; + for (i = 0; i < lines.length; i++) { + var line = lines[i]; + var t = {}; + if (line.includes('-->') || /^NOTE/.test(line)) { + var isNote = /^NOTE/.test(line); + t.times = isNote ? "" : line; + t.tag = isNote ? TRANSCRIPT_CUE_TYPES.note : TRANSCRIPT_CUE_TYPES.timedCue; + // Make sure there is a single space separating NOTE from the comment for single or multi-line comments + t.line = isNote ? line.replace(/^NOTE\s*/, 'NOTE ') : ''; + i++; - // If player is fullscreen exit before displaying track scrubber - if (player.isFullscreen()) { - player.exitFullscreen(); + // Increment until an empty line is encountered marking the end of the block + while (i < lines.length && !(lines[i] == '\r' || lines[i] == '\n' || lines[i] == '\r\n' || lines[i] == '')) { + t.line += lines[i].endsWith('-') ? lines[i] : lines[i].replace(/\s*$/, ' '); + i++; } - var tempZoom = this.zoomedOutRef.current; - this.setZoomedOut(!tempZoom); + t.line = t.line.trimEnd(); + groups.push(t); } + } + return groups; +} - /** - * Event handler for VideoJS player instance's 'timeupdate' event, which - * updates the track scrubber from player state. - */ - }, { - key: "handleTimeUpdate", - value: function handleTimeUpdate() { - var _player$markers$getMa; - var player = this.player, - options = this.options, - zoomedOutRef = this.zoomedOutRef; - // Hide track-scrubber for inaccessible item if it is open - if (player.canvasIsEmpty && !zoomedOutRef.current) { - this.setZoomedOut(true); - } - if (player.isDisposed() || player.ended()) return; - /* - Get the current track from the player.markers created from the structure timespans. - In playlists, markers are timepoint information representing highlighting annotations, - therefore omit reading markers information for track scrubber in playlist contexts. - */ - var playerCurrentTime = player.currentTime(); - if (player.markers && typeof player.markers !== 'function' && typeof player.markers.getMarkers === 'function' && ((_player$markers$getMa = player.markers.getMarkers()) === null || _player$markers$getMa === void 0 ? void 0 : _player$markers$getMa.length) > 0 && !options.isPlaylist) { - this.readPlayerMarkers(); - } else { - var _player$playableDurat, _player$altStart; - this.setCurrentTrack({ - duration: (_player$playableDurat = player.playableDuration) !== null && _player$playableDurat !== void 0 ? _player$playableDurat : player.duration(), - time: (_player$altStart = player.altStart) !== null && _player$altStart !== void 0 ? _player$altStart : 0, - key: '', - text: 'Complete media file' - }); - playerCurrentTime = player.srcIndex && player.srcIndex > 0 ? playerCurrentTime + player.altStart : playerCurrentTime; - } - this.updateTrackScrubberProgressBar(playerCurrentTime); - } - /** - * Calculate the progress and current time within the track and - * update them accordingly when the player's 'timeupdate' event fires. - * @param {Number} currentTime player's current time - */ - }, { - key: "updateTrackScrubberProgressBar", - value: function updateTrackScrubberProgressBar() { - var currentTime = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; - var player = this.player, - currentTrackRef = this.currentTrackRef; - // Handle Safari which emits the timeupdate event really quickly - if (!currentTrackRef.current) { - if (player.markers && typeof player.markers.getMarkers === 'function') { - this.readPlayerMarkers(); - } - } - var altStart = player.altStart, - srcIndex = player.srcIndex; - // Calculate corresponding time and played percentage values within track - var trackoffset = srcIndex > 0 ? currentTime - currentTrackRef.current.time + altStart : currentTime - currentTrackRef.current.time; - var trackpercent = Math.min(100, Math.max(0, 100 * trackoffset / currentTrackRef.current.duration)); - this.populateTrackScrubber(trackoffset, trackpercent); - } - }, { - key: "populateTrackScrubber", - value: - /** - * Update the track scrubber's current time, duration and played percentage - * when it is visible in UI. - * @param {Number} currentTime current time corresponding to the track - * @param {Number} playedPercentage elapsed time percentage of the track duration - */ - function populateTrackScrubber() { - var currentTime = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; - var playedPercentage = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; - var trackScrubberRef = this.options.trackScrubberRef; - if (!trackScrubberRef.current) { - return; +/** + * Create a JSON object from the transcript data + * @param {Object} obj + * @param {String} obj.times string with time information + * @param {String} obj.line string with transcript text + * @returns {Object} of the format; + * { + * begin: 0, + * end: 60, + * text: 'Transcript text sample', + * tag: NOTE || TIMED_CUE + * } + */ +function parseTimedTextLine(_ref, isSRT) { + var times = _ref.times, + line = _ref.line, + tag = _ref.tag; + var timestampRegex; + if (isSRT) { + // SRT allows using comma for milliseconds while WebVTT does not + timestampRegex = SRT_TIMESTAMP_REGEX; + } else { + timestampRegex = VTT_TIMESTAMP_REGEX; + } + switch (tag) { + case TRANSCRIPT_CUE_TYPES.note: + return { + begin: 0, + end: 0, + text: line, + tag: tag + }; + case TRANSCRIPT_CUE_TYPES.timedCue: + var _times$split = times.split(' --> '), + _times$split2 = _slicedToArray(_times$split, 2), + start = _times$split2[0], + end = _times$split2[1]; + // FIXME:: remove any styles for now, refine this + end = end.split(' ')[0]; + if (!start.match(timestampRegex) || !end.match(timestampRegex)) { + console.error('Invalid timestamp in line with text; ', line); + return null; } - var _trackScrubberRef$cur2 = _slicedToArray(trackScrubberRef.current.children, 3), - currentTimeDisplay = _trackScrubberRef$cur2[0]; - _trackScrubberRef$cur2[1]; - var durationDisplay = _trackScrubberRef$cur2[2]; - - // Set the elapsed time percentage in the progress bar of track scrubber - document.documentElement.style.setProperty('--range-scrubber', "calc(".concat(playedPercentage, "%)")); + return { + begin: timeToS(start), + end: timeToS(end), + text: line, + tag: tag + }; + default: + return null; + } +} - // Update the track duration - durationDisplay.innerHTML = timeToHHmmss(this.currentTrackRef.current.duration); - // Update current time elapsed within the current track - var cleanTime = !isNaN(currentTime) && currentTime > 0 ? currentTime : 0; - currentTimeDisplay.innerHTML = timeToHHmmss(cleanTime); - } - }, { - key: "readPlayerMarkers", - value: function readPlayerMarkers() { - var tracks = this.player.markers.getMarkers().filter(function (m) { - return m["class"] == 'ramp--track-marker--fragment'; +/** + * Parse the content search response from the search service, and then use it to calculate + * number of search hits for each transcripts, and create a list of matched transcript + * lines for the search in the current transcript + * @param {Object} response JSON response from content search API + * @param {String} query search query from transcript search + * @param {Array} trancripts content of the displayed transcript with ids + * @param {String} selectedTranscript url of the selected transcript + * @returns a list of matched transcript lines for the current search + */ +var parseContentSearchResponse = function parseContentSearchResponse(response, query, trancripts, selectedTranscript) { + var _response$items; + if (!response || response === undefined) return []; + var hitCounts = []; + var searchHits = []; + if (((_response$items = response.items) === null || _response$items === void 0 ? void 0 : _response$items.length) > 0) { + var items = response.items; + items.map(function (item) { + var anno = new manifesto_js.Annotation(item); + // Exclude annotations without supplementing motivation + if (anno.getMotivation() != 'supplementing') return; + var target = anno.getTarget(); + var targetURI = getCanvasId(target); + var value = anno.getBody()[0].getProperty('value'); + var hitCount = getHitCountForCue(value, query, true); + searchHits.push({ + target: target, + targetURI: targetURI, + value: value, + hitCount: hitCount }); - if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0) { - this.setCurrentTrack(tracks[0]); - } - } - }, { - key: "handleMouseMove", - value: - /** - * Event handler for mouseenter and mousemove pointer events on the - * the track scrubber. This sets the time tooltip value and its offset - * position in the UI. - * @param {Event} e pointer event for user interaction - */ - function handleMouseMove(e) { - var timeToolRef = this.options.timeToolRef; - if (!timeToolRef.current) { - return; - } - var time = this.getTrackTime(e); - - // When hovering over the border of the track scrubber, convertTime() returns infinity, - // since e.target.clientWidth is zero. Use this value to not show the tooltip when this - // occurs. - if (isFinite(time)) { - // Calculate the horizontal position of the time tooltip using the event's offsetX property - var offset = e.offsetX - timeToolRef.current.offsetWidth / 2; // deduct 0.5 x width of tooltip element - timeToolRef.current.style.left = offset + 'px'; + }); + } + // Group search responses by transcript + var allSearchHits = groupBy(searchHits, 'targetURI'); - // Set text in the tooltip as the time relevant to the pointer event's position - timeToolRef.current.innerHTML = timeToHHmmss(time); - } - } - }, { - key: "handleSetProgress", - value: - /** - * Event handler for mousedown event on the track scrubber. This sets the - * progress percentage within track scrubber and update the player's current time - * when user clicks on a point within the track scrubber. - * @param {Event} e pointer event for user interaction - */ - function handleSetProgress(e) { - var currentTrackRef = this.currentTrackRef, - player = this.player; - if (!currentTrackRef.current) { - return; - } - var trackoffset = this.getTrackTime(e); - if (trackoffset != undefined) { - // Calculate percentage of the progress based on the pointer position's - // time and duration of the track - var trackpercent = Math.min(100, Math.max(0, 100 * (trackoffset / currentTrackRef.current.duration))); + // Calculate search hit count for each transcript in the Canvas + for (var _i = 0, _Object$entries = Object.entries(allSearchHits); _i < _Object$entries.length; _i++) { + var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), + key = _Object$entries$_i[0], + value = _Object$entries$_i[1]; + hitCounts.push({ + transcriptURL: key, + numberOfHits: value.reduce(function (acc, a) { + return acc + a.hitCount; + }, 0) + }); + } - // Set the elapsed time in the scrubber progress bar - document.documentElement.style.setProperty('--range-scrubber', "calc(".concat(trackpercent, "%)")); + // Get all the matching transcript lines with the query in the current transcript + var matchedTranscriptLines = getMatchedTranscriptLines(allSearchHits[selectedTranscript], query, trancripts); + return { + matchedTranscriptLines: matchedTranscriptLines, + hitCounts: hitCounts, + allSearchHits: allSearchHits + }; +}; - // Set player's current time with respective to the altStart for clipped items - var playerCurrentTime = player.isClipped ? trackoffset + currentTrackRef.current.time : trackoffset; - player.currentTime(playerCurrentTime); - } - } - }, { - key: "getTrackTime", - value: - /** - * Convert pointer position on track scrubber to a time value - * @param {Event} e pointer event for user interaction - * @returns {Number} time corresponding to the pointer position - */ - function getTrackTime(e) { - var currentTrackRef = this.currentTrackRef; - if (!currentTrackRef.current) { - return; +/** + * Create a list matched transcript lines for the current search for the displayed transcript + * @param {Array} searchHits a list of matched transcript lines with ids from the current transcript + * @param {String} query search query + * @param {Array} transcripts list of all the transcript lines from the current transcript + * @returns a list of matched transcrip lines in the current transcript + */ +var getMatchedTranscriptLines = function getMatchedTranscriptLines(searchHits, query, transcripts) { + var qStr = query.trim().toLocaleLowerCase(); + var transcriptLines = []; + if (searchHits === undefined) return; + var traversedIds = []; + searchHits.map(function (item, index) { + var target = item.target, + value = item.value; + // Read time offsets and text of the search hit + var timeRange = getMediaFragment(target); + + // Replace all HTML tags + var mappedText = value.replace(/<\/?[^>]+>/gi, ''); + var start = 0, + end = 0; + var transcriptId = undefined; + if (timeRange != undefined) { + // For timed-text + start = timeRange.start; + end = timeRange.end; + transcriptId = transcripts.findIndex(function (t) { + return t.begin == start && t.end == end; + }); + var queryText = qStr.match(/[a-zA-Z]+/gi) ? qStr.match(/[a-zA-Z]+/gi)[0] : qStr; + var matchOffset = mappedText.toLocaleLowerCase().indexOf(queryText); + if (matchOffset !== -1 && transcriptId != undefined) { + var match = markMatchedParts(value, qStr, item.hitCount, true); + transcriptLines.push({ + tag: TRANSCRIPT_CUE_TYPES.timedCue, + begin: start, + end: end, + id: transcriptId, + match: match, + matchCount: item.hitCount, + text: value + }); } - var offsetx = e.offsetX; - if (offsetx && offsetx != undefined) { - var time = offsetx / e.target.clientWidth * currentTrackRef.current.duration; - return time; + } else { + /** + * For non timed text, there's no unique id to match the search response to the transcript + * lines in the UI. So use filter() method instead of findIndex() method to get all matching + * transcript lines in the display. + * Use traversedIds array to remember the ids of already processed transcript lines in the list + * to avoid duplication in the matches. + */ + var hitsInfo = matchPartsInUntimedText(transcripts, mappedText, qStr, traversedIds); + traversedIds = hitsInfo.traversedIds; + transcriptLines = [].concat(_toConsumableArray(transcriptLines), _toConsumableArray(hitsInfo.hits)); + + /** + * When backend has a single block of text which is chuncked in the UI this helps to + * traverse all transcript cues. + */ + while (index === searchHits.length - 1 && ((_traversedIds = traversedIds) === null || _traversedIds === void 0 ? void 0 : _traversedIds.length) < transcripts.length) { + var _traversedIds; + var _hitsInfo = matchPartsInUntimedText(transcripts, mappedText, qStr, traversedIds); + traversedIds = _hitsInfo.traversedIds; + transcriptLines = [].concat(_toConsumableArray(transcriptLines), _toConsumableArray(_hitsInfo.hits)); } } - }]); - return VideoJSTrackScrubber; -}(Button); -videojs__default["default"].registerComponent('VideoJSTrackScrubber', VideoJSTrackScrubber); + }); + return transcriptLines; +}; -function _createForOfIteratorHelper$2(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$2(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } -function _unsupportedIterableToArray$2(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$2(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$2(o, minLen); } -function _arrayLikeToArray$2(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } -function ownKeys$4(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } -function _objectSpread$4(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$4(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$4(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } -require('@silvermine/videojs-quality-selector')(videojs__default["default"]); -// import vjsYo from './vjsYo'; +/** + * Build a list of matched indexed transcript lines from content search response. + * In Avalon, docx and plain text files are chunked by paragraphs seperated by 2 or + * more new line characters. So, depending on the way the file is formatted the search + * response could include chunks of the text or the full text. + * In the library (mammoth) used in Transcript component to display docx files; the text is chunked + * into paragraphs seperated by one or more new line characters. + * And the search response doesn't include any text styling in the docx files. Therefore the + * text with style information is reformatted to include text highlights from the search response. + * This function uses the search response to calculate the hit counts and mark them for each indexed transcript + * line in the front-end to get the correct counts. + * @param {Array} transcripts indexed transcript text in UI + * @param {String} mappedText matched text from content search + * @param {String} query search query entered by the user + * @param {Array} traversedIds already included transcript indices + * @returns a list of matched transcript lines + */ +var matchPartsInUntimedText = function matchPartsInUntimedText(transcripts, mappedText, query, traversedIds) { + var escapedQ = buildRegexReadyText(query, true, false); + // Get hit counts for the current text, ignore matches with query preceded by - or ' + var qRegex = new RegExp(String.raw(_templateObject$1 || (_templateObject$1 = _taggedTemplateLiteral(["\b", "\b"], ["\\b", "\\b"])), escapedQ), 'gi'); + var matched = []; + // Start from the next cue after the last traveresed cue in the transcript + var lastTraversedId = traversedIds[traversedIds.length - 1] + 1 || 0; -function VideoJSPlayer(_ref) { - var isVideo = _ref.isVideo; - _ref.hasMultipleCanvases; - var isPlaylist = _ref.isPlaylist, - trackScrubberRef = _ref.trackScrubberRef, - scrubberTooltipRef = _ref.scrubberTooltipRef, - tracks = _ref.tracks, - placeholderText = _ref.placeholderText, - renderingFiles = _ref.renderingFiles, - enableFileDownload = _ref.enableFileDownload, - loadPrevOrNext = _ref.loadPrevOrNext, - lastCanvasIndex = _ref.lastCanvasIndex, - enableTitleLink = _ref.enableTitleLink, - videoJSLangMap = _ref.videoJSLangMap, - options = _ref.options; - var playerState = usePlayerState(); - var playerDispatch = usePlayerDispatch(); - var manifestState = useManifestState(); - var manifestDispatch = useManifestDispatch(); - var canvasDuration = manifestState.canvasDuration, - canvasIndex = manifestState.canvasIndex, - canvasLink = manifestState.canvasLink, - currentNavItem = manifestState.currentNavItem, - hasMultiItems = manifestState.hasMultiItems, - srcIndex = manifestState.srcIndex, - targets = manifestState.targets, - autoAdvance = manifestState.autoAdvance, - playlist = manifestState.playlist, - structures = manifestState.structures, - canvasSegments = manifestState.canvasSegments, - hasStructure = manifestState.hasStructure, - canvasIsEmpty = manifestState.canvasIsEmpty; - var isClicked = playerState.isClicked, - isEnded = playerState.isEnded, - isPlaying = playerState.isPlaying, - player = playerState.player, - searchMarkers = playerState.searchMarkers, - currentTime = playerState.currentTime; - var _React$useState = React__default["default"].useState(canvasIndex), - _React$useState2 = _slicedToArray(_React$useState, 2); - _React$useState2[0]; - var _setCIndex = _React$useState2[1]; - var _React$useState3 = React__default["default"].useState(false), - _React$useState4 = _slicedToArray(_React$useState3, 2), - isReady = _React$useState4[0], - _setIsReady = _React$useState4[1]; - var _React$useState5 = React__default["default"].useState(''), - _React$useState6 = _slicedToArray(_React$useState5, 2), - activeId = _React$useState6[0], - _setActiveId = _React$useState6[1]; - var _useLocalStorage = useLocalStorage('startVolume', 1), - _useLocalStorage2 = _slicedToArray(_useLocalStorage, 2), - startVolume = _useLocalStorage2[0], - setStartVolume = _useLocalStorage2[1]; - var _useLocalStorage3 = useLocalStorage('startQuality', null), - _useLocalStorage4 = _slicedToArray(_useLocalStorage3, 2), - startQuality = _useLocalStorage4[0], - setStartQuality = _useLocalStorage4[1]; - var _useLocalStorage5 = useLocalStorage('startMuted', false), - _useLocalStorage6 = _slicedToArray(_useLocalStorage5, 2), - startMuted = _useLocalStorage6[0], - setStartMuted = _useLocalStorage6[1]; - var _useLocalStorage7 = useLocalStorage('startCaptioned', true), - _useLocalStorage8 = _slicedToArray(_useLocalStorage7, 2), - startCaptioned = _useLocalStorage8[0], - setStartCaptioned = _useLocalStorage8[1]; - var _React$useState7 = React__default["default"].useState(null), - _React$useState8 = _slicedToArray(_React$useState7, 2), - fragmentMarker = _React$useState8[0], - setFragmentMarker = _React$useState8[1]; - var _React$useState9 = React__default["default"].useState(CANVAS_MESSAGE_TIMEOUT / 1000), - _React$useState10 = _slicedToArray(_React$useState9, 2), - messageTime = _React$useState10[0], - setMessageTime = _React$useState10[1]; - var videoJSRef = React__default["default"].useRef(null); - var playerRef = React__default["default"].useRef(null); - var autoAdvanceRef = React__default["default"].useRef(); - autoAdvanceRef.current = autoAdvance; - var srcIndexRef = React__default["default"].useRef(); - srcIndexRef.current = srcIndex; - var activeIdRef = React__default["default"].useRef(); - activeIdRef.current = activeId; - var setActiveId = function setActiveId(id) { - _setActiveId(id); - activeIdRef.current = id; - }; - var currentTimeRef = React__default["default"].useRef(); - currentTimeRef.current = currentTime; - var isReadyRef = React__default["default"].useRef(); - isReadyRef.current = isReady; - var setIsReady = function setIsReady(r) { - _setIsReady(r); - isReadyRef.current = r; - }; - var currentNavItemRef = React__default["default"].useRef(); - currentNavItemRef.current = currentNavItem; - var canvasIsEmptyRef = React__default["default"].useRef(); - canvasIsEmptyRef.current = canvasIsEmpty; - var canvasDurationRef = React__default["default"].useRef(); - canvasDurationRef.current = canvasDuration; - var canvasLinkRef = React__default["default"].useRef(); - canvasLinkRef.current = canvasLink; - var isPlayingRef = React__default["default"].useRef(); - isPlayingRef.current = isPlaying; - var isEndedRef = React__default["default"].useRef(); - isEndedRef.current = isEnded; - var cIndexRef = React__default["default"].useRef(); - cIndexRef.current = canvasIndex; - var setCIndex = function setCIndex(i) { - _setCIndex(i); - cIndexRef.current = i; + /** + * For untimed text the search response text could be either, + * - mapped one to one with the cue text in Transcript component + * - include a part of the cue text in Transcript component + * When none of these work check if the cue text contains the search query + */ + for (var i = lastTraversedId; i < transcripts.length; i++) { + var t = transcripts[i]; + var cleanedText = t.text.replace(/<\/?[^>]+>/gi, '').trim(); + var matches = _toConsumableArray(cleanedText.matchAll(qRegex)); + var mappedTextCleaned = mappedText.trim(); + if (mappedTextCleaned == cleanedText || mappedTextCleaned.includes(cleanedText) && (matches === null || matches === void 0 ? void 0 : matches.length) > 0) { + t.matchCount = matches === null || matches === void 0 ? void 0 : matches.length; + matched.push(t); + traversedIds.push(t.id); + break; + } else if ((matches === null || matches === void 0 ? void 0 : matches.length) > 0) { + var _ref2; + t.matchCount = (_ref2 = _toConsumableArray(mappedTextCleaned.matchAll(qRegex))) === null || _ref2 === void 0 ? void 0 : _ref2.length; + matched.push(t); + traversedIds.push(t.id); + break; + } else { + traversedIds.push(t.id); + } + } + var hits = []; + matched.map(function (m) { + var value = addStyledHighlights(m.textDisplayed, query); + var match = markMatchedParts(value, query, m.matchCount, true); + hits.push({ + tag: TRANSCRIPT_CUE_TYPES.nonTimedLine, + begin: undefined, + end: undefined, + id: m.id, + match: match, + matchCount: m.matchCount, + text: value + }); + }); + return { + hits: hits, + traversedIds: traversedIds }; - var captionsOnRef = React__default["default"].useRef(); - var activeTrackRef = React__default["default"].useRef(); - var canvasSegmentsRef = React__default["default"].useRef(); - canvasSegmentsRef.current = canvasSegments; - var structuresRef = React__default["default"].useRef(); - structuresRef.current = structures; - var messageIntervalRef = React__default["default"].useRef(null); - - // Dispose Video.js instance when VideoJSPlayer component is removed - React__default["default"].useEffect(function () { - return function () { - if (playerRef.current != null) { - playerRef.current.dispose(); - document.removeEventListener('keydown', playerHotKeys); - setIsReady(false); - } - }; - }, []); +}; +/** + * Generic function to mark the matched transcript text in the cue where the output has + * surrounding the matched parts + * within the cue. + * @param {String} text matched transcript text/cue + * @param {String} query current search query + * @param {Numner} hitCount number of hits returned in the search response + * @param {Boolean} hasHighlight boolean flag to indicate text has tags + * @returns matched cue with HTML tags added for marking the hightlight + */ +var markMatchedParts = function markMatchedParts(text, query, hitCount) { + var hasHighlight = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; + if (text === undefined || !text) return; + var count = 0; + var replacerFn = function replacerFn(match) { + var cleanedMatch = match.replace(/<\/?[^>]+>/gi, ''); + // Only add highlights to search hits in the search response + if (count < hitCount) { + count++; + return "".concat(cleanedMatch, ""); + } else { + return cleanedMatch; + } + }; + var queryFormatted = query; /** - * Initialize Video.js when for the first page load or update - * src and other properties of the existing Video.js instance - * on Canvas change + * Content search response for a phrase search like 'Mr. Bungle' gives the response + * with highlights in the matched text as Mr. Bungle. + * So reconstruct the search query in the UI to match this phrase in the response. */ - React__default["default"].useEffect(function () { - var _options$sources, _options$sources2; - setCIndex(canvasIndex); + if (hasHighlight) { + queryFormatted = buildRegexReadyText(query); + } - // Set selected quality from localStorage in Video.js options - setSelectedQuality(options.sources); + /** + * Content search API returns cues including "Mr. Bungle" as matches for both search queries + * "mr bungle" and "mr. bungle". + * When "mr bungle" is searched this function handles highlighting since the regex fails to + * identify the matches in the cues. + */ + var altReplace = function altReplace() { + var matches = _toConsumableArray(text.matchAll(/<\/?[^>]+>/gi)); + if ((matches === null || matches === void 0 ? void 0 : matches.length) === 0) return; + var startIndex = 0; + var newStr = ''; + for (var j = 0; j < matches.length && count < hitCount;) { + // Set offset to count matches based on the # of words in the phrase search query + var splitQ = query.split(/[\s-,\?]/); + var offset = (splitQ === null || splitQ === void 0 ? void 0 : splitQ.length) > 0 ? (splitQ === null || splitQ === void 0 ? void 0 : splitQ.length) * 2 - 1 : 1; + if (matches[j] === undefined && matches[j + offset] === undefined) return; - // Video.js player is only initialized on initial page load - if (!playerRef.current && ((_options$sources = options.sources) === null || _options$sources === void 0 ? void 0 : _options$sources.length) > 0) { - videojs__default["default"].addLanguage(options.language, JSON.parse(videoJSLangMap)); - buildTracksHTML(); - - // Turn Video.js logging off and handle errors in this code, to avoid - // cluttering the console when loading inaccessible items. - videojs__default["default"].log.level('off'); - var _player = playerRef.current = videojs__default["default"](videoJSRef.current, options, function () { - playerInitSetup(playerRef.current); - }); - - /* Another way to add a component to the controlBar */ - // player.getChild('controlBar').addChild('vjsYo', {}); - - playerDispatch({ - player: _player, - type: 'updatePlayer' - }); - - // Update player status in state only when pause is initiate by the user - _player.controlBar.getChild('PlayToggle').on('pointerdown', function () { - handlePause(); - }); - _player.on('pointerdown', function (e) { - var elementTag = e.target.nodeName.toLowerCase(); - if (elementTag == 'video') { - handlePause(); - } - }); - } else if (playerRef.current && ((_options$sources2 = options.sources) === null || _options$sources2 === void 0 ? void 0 : _options$sources2.length) > 0) { - var _player2$markers; - // Update the existing Video.js player on consecutive Canvas changes - var _player2 = playerRef.current; - - // Reset markers - if (activeIdRef.current) (_player2$markers = _player2.markers) === null || _player2$markers === void 0 ? void 0 : _player2$markers.removeAll(); - setActiveId(null); - - // Block player while metadata is loaded when canvas is not empty - if (!canvasIsEmptyRef.current) { - _player2.addClass('vjs-disabled'); - setIsReady(false); - updatePlayer(_player2); - playerLoadedMetadata(_player2); - playerDispatch({ - player: _player2, - type: 'updatePlayer' - }); - } else { - // Mark as ready to for inaccessible canvas (empty) - setIsReady(true); + // Indices of start and end of the highlighted text including tags + var firstIndex = matches[j].index; + var lastIndex = matches[j + offset].index + matches[j + offset][0].length; + var prefix = text.slice(startIndex, firstIndex); + var cleanedMatch = text.slice(firstIndex, lastIndex).replace(/<\/?[^>]+>/gi, ''); + newStr = "".concat(newStr).concat(prefix, "").concat(cleanedMatch, ""); + startIndex = lastIndex; + j = +(offset + 1); + count++; + if (j == matches.length) { + newStr = "".concat(newStr).concat(text.slice(startIndex)); } } - }, [options.sources, videoJSRef]); - React__default["default"].useEffect(function () { - // Clear existing interval for inaccessible message display - clearDisplayTimeInterval(); - if (playerRef.current) { - // Show/hide control bar for valid/inaccessible items respectively - if (canvasIsEmptyRef.current) { - var _currentNavItemRef$cu; - // Set the player's aspect ratio to video - playerRef.current.audioOnlyMode(false); - playerRef.current.canvasIsEmpty = true; - playerRef.current.aspectRatio('16:9'); - playerRef.current.controlBar.addClass('vjs-hidden'); - playerRef.current.removeClass('vjs-disabled'); - playerRef.current.pause(); - /** - * Update the activeId to update the active item in the structured navigation. - * For playable items this is updated in the timeupdate handler. - */ - setActiveId((_currentNavItemRef$cu = currentNavItemRef.current) === null || _currentNavItemRef$cu === void 0 ? void 0 : _currentNavItemRef$cu.id); - } else { - // Reveal control bar; needed when loading a Canvas after an inaccessible item - playerRef.current.controlBar.removeClass('vjs-hidden'); - } + return newStr; + }; + try { + var _ref3; + var queryRegex = new RegExp(String.raw(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["", ""])), queryFormatted), 'gi'); + if (((_ref3 = _toConsumableArray(text.matchAll(queryRegex))) === null || _ref3 === void 0 ? void 0 : _ref3.length) === 0) { + var highlighted = altReplace(); + return highlighted; + } else { + return text.replace(queryRegex, replacerFn); } + } catch (e) { + console.log('Error building RegExp for query: ', query); + } +}; - // Start interval for inaccessible message display - if (canvasIsEmptyRef.current && !messageIntervalRef.current) { - setMessageTime(CANVAS_MESSAGE_TIMEOUT / 1000); - createDisplayTimeInterval(); - } - }, [cIndexRef.current, canvasIsEmptyRef.current, currentNavItemRef.current]); +/** + * For docx files the content search response text doesn't have the formatted + * styles in the Word document (e.g. bold text wrapped in tags). So, + * use the styled text formatted with mammoth in the UI to add highlights from + * the content search response. + * @param {String} text string to be formatted + * @param {String} query string to find and replace with tags + * @returns a string formatted with highlights + */ +var addStyledHighlights = function addStyledHighlights(text, query) { + if (text === undefined || !text) return; + var replacerFn = function replacerFn(match) { + var cleanedMatch = buildRegexReadyText(match, false, true); + return cleanedMatch; + }; - /** - * Clear/create display timer interval when auto-advance is turned - * off/on respectively - */ - React__default["default"].useEffect(function () { - if (!autoAdvance) { - clearDisplayTimeInterval(); - } else if (autoAdvance && !messageIntervalRef.current && canvasIsEmpty) { - setMessageTime(CANVAS_MESSAGE_TIMEOUT / 1000); - createDisplayTimeInterval(); - } - }, [autoAdvance]); + // Regex to get matches in the text while ignoring matches with query preceded by - or ' + var queryregex = new RegExp(String.raw(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["\b", "\b"], ["\\b", "\\b"])), buildRegexReadyText(query, true, false)), 'gi'); + var styled = text.replace(queryregex, replacerFn); + return styled; +}; - // update markers in player - React__default["default"].useEffect(function () { - if (playerRef.current && playerRef.current.markers && isReadyRef.current) { - var _playlist$markers, _playerRef$current$ma; - // markers plugin not yet initialized - if (typeof playerRef.current.markers === 'function') { - playerRef.current.markers({ - markerTip: { - display: false, - // true, - text: function text(marker) { - return marker.text; - } - }, - markerStyle: {}, - markers: [] - }); - } - var playlistMarkers = []; - if (playlist !== null && playlist !== void 0 && (_playlist$markers = playlist.markers) !== null && _playlist$markers !== void 0 && _playlist$markers.length) { - var canvasMarkers = playlist.markers.filter(function (m) { - return m.canvasIndex === canvasIndex; - })[0].canvasMarkers; - playlistMarkers = canvasMarkers.map(function (m) { - return { - time: parseFloat(m.time), - text: m.value, - "class": 'ramp--track-marker--playlist' - }; - }); - } - (_playerRef$current$ma = playerRef.current.markers) === null || _playerRef$current$ma === void 0 ? void 0 : _playerRef$current$ma.removeAll(); - playerRef.current.markers.add([].concat(_toConsumableArray(fragmentMarker ? [fragmentMarker] : []), _toConsumableArray(searchMarkers), _toConsumableArray(playlistMarkers))); - } - }, [fragmentMarker, searchMarkers, canvasDuration, canvasIndex, playerRef.current, isReadyRef.current]); +/** + * Format a given string by escaping punctuations characters and grouping + * punctuations and text, to make it feasible to be used to build a regular + * expression accurately. + * @param {String} text string to be formatted with hightlights + * @param {Boolean} regExpReady flag to indicate the usage of the output as a regular exp + * @param {Boolean} addHightlight flag to indicate to/not to add tags + * @returns string with tags + */ +var buildRegexReadyText = function buildRegexReadyText(text) { + var regExpReady = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + var addHightlight = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; + // Text matches in the string + var matches = _toConsumableArray(text.matchAll(/[a-zA-Z']+/gi)); + // Punctuation matches in the string + var punctuationMatches = _toConsumableArray(text.matchAll(/([.+?"^${}\-|[\]\\])/g)); /** - * Build track HTML for Video.js player on initial page load + * If no punctuations are found within the text return text with highlights + * For RegExp ready strings: ignore matches followed by - or ' + * e.g. omit matches as "Bungle's" when search query is "bungle" */ - var buildTracksHTML = function buildTracksHTML() { - if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0 && videoJSRef.current) { - tracks.map(function (t) { - var trackEl = document.createElement('track'); - trackEl.setAttribute('key', t.key); - trackEl.setAttribute('src', t.src); - trackEl.setAttribute('kind', t.kind); - trackEl.setAttribute('label', t.label); - trackEl.setAttribute('srclang', t.srclang); - videoJSRef.current.appendChild(trackEl); - }); + if ((punctuationMatches === null || punctuationMatches === void 0 ? void 0 : punctuationMatches.length) === 0) { + var textFormatted = addHightlight ? text.split(' ').map(function (t) { + return "".concat(t, ""); + }).join(' ') : text; + var textRegex = regExpReady ? "".concat(textFormatted, "(?!['w*])") : textFormatted; + return textRegex; + } + var highlighted = ''; + var startIndex = 0; + var i = 0; + while (i < matches.length) { + var match = matches[i]; + var textMatch = addHightlight ? "".concat(match[0], "") : match[0]; + /** + * When build RegExp ready string with punctuation blocks in the given string; + * - use * quantifier for blocks either at the start/end of the string to match zero or more times + * - use + quantifier for blocks in the middle of the string to match one or more times + * This pattern is build according the response from the content search API results. + */ + var punctMatch = startIndex === 0 ? "(".concat(text.slice(startIndex, match.index), ")*") : "(".concat(text.slice(startIndex, match.index), ")+"); + highlighted = regExpReady ? "".concat(highlighted).concat(punctMatch, "(").concat(textMatch, ")") : "".concat(highlighted).concat(text.slice(startIndex, match.index)).concat(textMatch); + startIndex = match.index + match[0].length; + if (i === (matches === null || matches === void 0 ? void 0 : matches.length) - 1) { + highlighted = regExpReady ? "".concat(highlighted, "(").concat(text.slice(startIndex), ")*") : "".concat(highlighted).concat(text.slice(startIndex)); } + i++; + } + + // Escape punctuation characters in string for RegExp ready strings + var escapePunctuation = function escapePunctuation(str) { + var punctuationRegex = /([.?^${}|[\]\\])/g; + return str.replace(punctuationRegex, '\\$1'); }; - var updatePlayer = function updatePlayer(player) { - player.duration(canvasDurationRef.current); - player.src(options.sources); - player.poster(options.poster); - player.canvasIndex = cIndexRef.current; - player.canvasIsEmpty = canvasIsEmptyRef.current; - player.srcIndex = srcIndex; - player.targets = targets; - if (enableTitleLink) { - player.canvasLink = canvasLinkRef.current; - } + return regExpReady ? escapePunctuation(highlighted) : highlighted; +}; - // Update textTracks in the player - var oldTracks = player.remoteTextTracks(); - var i = oldTracks.length; - while (i--) { - player.removeRemoteTextTrack(oldTracks[i]); - } - if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0 && isVideo) { - tracks.forEach(function (track) { - player.addRemoteTextTrack(track, false); - }); - } +/** + * Calculate hit counts for each matched transcript cue + * @param {String} text matched transcript cue text + * @param {String} query search query from UI + * @param {Boolean} hasHighlight flag indicating has tags or not + * @returns + */ +var getHitCountForCue = function getHitCountForCue(text, query) { + var _ref4; + var hasHighlight = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + /* + Content search API highlights each word in the given phrase in the response. + Threfore, use first word in the query seperated by a white space to get the hit + counts for each cue. + Use regex with any punctuation followed by a white space to split the query. + e.g. query: Mr. bungle => search response: Mr. Bungle + */ + var partialQ = query.split(/[\s.,!?;:]/)[0]; + var cleanedPartialQ = partialQ.replace(/[\[\]\-]/gi, ''); + var hitTerm = hasHighlight ? buildRegexReadyText(partialQ) : cleanedPartialQ; + var highlightedTerm = new RegExp(String.raw(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral(["", ""])), hitTerm), 'gi'); + var hitCount = (_ref4 = _toConsumableArray(text.matchAll(highlightedTerm))) === null || _ref4 === void 0 ? void 0 : _ref4.length; + return hitCount; +}; - /* - Update player control bar for; - - track scrubber button - - appearance of the player: big play button and aspect ratio of the player - based on media type - - volume panel based on media type - - file download menu - */ - if (player.getChild('controlBar') != null && !canvasIsEmpty) { - var controlBar = player.getChild('controlBar'); - // Index of the full-screen toggle in the player's control bar - var fullscreenIndex = controlBar.children().findIndex(function (c) { - return c.name_ == 'FullscreenToggle'; - }); - /* - Track-scrubber button: remove if the Manifest is not a playlist manifest - or the current Canvas doesn't have structure items. Or add back in if it's - not present otherwise. - */ - if (!(hasStructure || playlist.isPlaylist)) { - controlBar.removeChild('videoJSTrackScrubber'); - } else if (!controlBar.getChild('videoJSTrackScrubber')) { - // Add track-scrubber button after duration display if it is not available - controlBar.addChild('videoJSTrackScrubber', { - trackScrubberRef: trackScrubberRef, - timeToolRef: scrubberTooltipRef - }); - } - if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0 && isVideo && !controlBar.getChild('subsCapsButton')) { - var captionIndex = IS_MOBILE ? controlBar.children().findIndex(function (c) { - return c.name_ == 'MuteToggle'; - }) : controlBar.children().findIndex(function (c) { - return c.name_ == 'VolumePanel'; - }); - var subsCapBtn = controlBar.addChild('subsCapsButton', {}, captionIndex + 1); - // Add CSS to mark captions-on - subsCapBtn.children_[0].addClass('captions-on'); - } +// TODO:: Could be used for marking search hits in Word Doc transcripts? +var splitIntoElements = function splitIntoElements(htmlContent) { + // Create a temporary DOM element to parse the HTML + var tempDiv = document.createElement('div'); + tempDiv.innerHTML = htmlContent; + + // Convert child nodes into an array + var elements = buildNonTimedText(Array.from(tempDiv.childNodes), true); + return elements; +}; + +/** + * Build non-timed transcript text content chunks into a JSON array + * with relevant information for display. These are then used by + * search module to convert the transcript content into an index. + * @param {Array} cues a list of trascript cues + * @param {Boolean} isHTML flag to detect inlined HTML in cues + * @returns a list of JSON objects for each cue + */ +var buildNonTimedText = function buildNonTimedText(cues) { + var isHTML = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + var indexedCues = []; + cues.map(function (c) { + indexedCues.push({ + text: isHTML ? c.innerText : c, + tag: TRANSCRIPT_CUE_TYPES.nonTimedLine, + textDisplayed: isHTML ? lib.decode(c.innerHTML) : c + }); + }); + return indexedCues; +}; +function ownKeys$5(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } +function _objectSpread$5(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$5(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$5(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } + +/** + * Disable each marker when one of the markers in the table + * is being edited reading isEditing value from global + * state and read presence of annotation service in the Manifest. + * @returns { + * isDisabled: Boolean, + * hasAnnotationService: Boolean + * } + */ +var useMarkers = function useMarkers() { + var manifestState = React.useContext(ManifestStateContext); + var _manifestState$playli = manifestState.playlist, + isEditing = _manifestState$playli.isEditing, + hasAnnotationService = _manifestState$playli.hasAnnotationService; + var isDisabled = React.useMemo(function () { + return isEditing; + }, [isEditing]); + return { + isDisabled: isDisabled, + hasAnnotationService: hasAnnotationService + }; +}; + +/** + * Read player and related updates as player is changed in + * global state + * @returns { + * canvasIndex: number, + * canvasIsEmpty: bool, + * isMultiCanvased: bool, + * lastCanvasIndex: number, + * player: object + * getCurrentTime: func, + * } + */ +var useMediaPlayer = function useMediaPlayer() { + var manifestState = React.useContext(ManifestStateContext); + var playerState = React.useContext(PlayerStateContext); + var player = playerState.player; + var allCanvases = manifestState.allCanvases, + canvasIndex = manifestState.canvasIndex, + canvasIsEmpty = manifestState.canvasIsEmpty; + + // Deduct 1 from length to compare against canvasIndex, which starts from 0 + var lastCanvasIndex = React.useMemo(function () { + var _ref; + return (_ref = (allCanvases === null || allCanvases === void 0 ? void 0 : allCanvases.length) - 1) !== null && _ref !== void 0 ? _ref : 0; + }, [allCanvases]); + var isMultiCanvased = React.useMemo(function () { + return (allCanvases === null || allCanvases === void 0 ? void 0 : allCanvases.length) - 1 > 0 ? true : false; + }, [allCanvases]); + + // Wrapper function to get player's time for creating a new playlist marker + var getCurrentTime = React.useCallback(function () { + if (player) { + return player.currentTime(); + } else { + return 0; + } + }, [player]); + return { + canvasIndex: canvasIndex, + canvasIsEmpty: canvasIsEmpty, + isMultiCanvased: isMultiCanvased, + lastCanvasIndex: lastCanvasIndex, + player: player, + getCurrentTime: getCurrentTime + }; +}; + +/** + * Read Canvas information and update state to reload player on + * Canvas changes + * @param {Object} obj + * @param {Boolean} obj.enableFileDownload + * @param {Boolean} obj.withCredentials + * @param {Number} obj.lastCanvasIndex + * @returns { + * isMultiSourced: bool, + * isPlaylist: bool, + * isVideo: bool, + * nextItemClicked: func, + * playerConfig: obj, + * ready: bool, + * renderingFiles: array, + * srcIndex: number, + * switchPlayer: func + * } + */ +var useSetupPlayer = function useSetupPlayer(_ref2) { + var _ref2$enableFileDownl = _ref2.enableFileDownload, + enableFileDownload = _ref2$enableFileDownl === void 0 ? false : _ref2$enableFileDownl, + _ref2$withCredentials = _ref2.withCredentials, + withCredentials = _ref2$withCredentials === void 0 ? false : _ref2$withCredentials, + lastCanvasIndex = _ref2.lastCanvasIndex; + var manifestDispatch = React.useContext(ManifestDispatchContext); + var playerDispatch = React.useContext(PlayerDispatchContext); + var manifestState = React.useContext(ManifestStateContext); + var allCanvases = manifestState.allCanvases, + autoAdvance = manifestState.autoAdvance, + canvasIndex = manifestState.canvasIndex, + customStart = manifestState.customStart, + manifest = manifestState.manifest, + playlist = manifestState.playlist, + renderings = manifestState.renderings, + srcIndex = manifestState.srcIndex; + var isPlaylist = playlist.isPlaylist; + var _useShowInaccessibleM = useShowInaccessibleMessage({ + lastCanvasIndex: lastCanvasIndex + }), + clearDisplayTimeInterval = _useShowInaccessibleM.clearDisplayTimeInterval, + createDisplayTimeInterval = _useShowInaccessibleM.createDisplayTimeInterval; + var _useState = React.useState(), + _useState2 = _slicedToArray(_useState, 2), + isVideo = _useState2[0], + setIsVideo = _useState2[1]; + var _useState3 = React.useState({ + error: '', + sources: [], + tracks: [], + poster: null, + targets: [] + }), + _useState4 = _slicedToArray(_useState3, 2), + playerConfig = _useState4[0], + setPlayerConfig = _useState4[1]; + var _useState5 = React.useState(), + _useState6 = _slicedToArray(_useState5, 2), + isMultiSourced = _useState6[0], + setIsMultiSourced = _useState6[1]; + var _useState7 = React.useState(true), + _useState8 = _slicedToArray(_useState7, 2), + firstLoad = _useState8[0], + setFirstLoad = _useState8[1]; + var _useState9 = React.useState(false), + _useState10 = _slicedToArray(_useState9, 2), + ready = _useState10[0], + setReady = _useState10[1]; + var renderingFiles = React.useMemo(function () { + if (enableFileDownload && renderings != {}) { + var _renderings$manifest, _renderings$canvas$ca; + return renderings === null || renderings === void 0 ? void 0 : (_renderings$manifest = renderings.manifest) === null || _renderings$manifest === void 0 ? void 0 : _renderings$manifest.concat(renderings === null || renderings === void 0 ? void 0 : (_renderings$canvas$ca = renderings.canvas[canvasIndex]) === null || _renderings$canvas$ca === void 0 ? void 0 : _renderings$canvas$ca.files); + } else { + return []; + } + }, [renderings, canvasIndex]); + React.useEffect(function () { + if (manifest) { /* - Change player's appearance when switching between audio and video canvases. - For audio: player height is reduced and big play button is removed - For video: player aspect ratio is set to 16:9 and has the centered big play button + Always start from the start time relevant to the Canvas only in playlist contexts, + because canvases related to playlist items always start from the given start. + With regular manifests, the start time could be different when using structured + navigation to switch between canvases. */ - if (!isVideo) { - player.audioOnlyMode(true); - player.addClass('vjs-audio'); - player.height(player.controlBar.height()); - player.removeChild('bigPlayButton'); - } else { - player.audioOnlyMode(false); - player.removeClass('vjs-audio'); - player.aspectRatio('16:9'); - player.addChild('bigPlayButton'); + if (canvasIndex == undefined || canvasIndex < 0) { + throw new Error('Invalid canvas index. Please check your Manifest.'); } + initCanvas(canvasIndex, isPlaylist); + } + return function () { + setReady(false); + playerDispatch({ + player: null, + type: 'updatePlayer' + }); + }; + }, [manifest, canvasIndex]); - /* - Re-add volumePanel/muteToggle icon: ensures the correct order of controls - on player reload. - On mobile device browsers, the volume panel is replaced by muteToggle - for both audio and video. - */ - if (!IS_MOBILE) { - controlBar.removeChild('VolumePanel'); - controlBar.addChild('VolumePanel'); - /* - Trigger ready event to reset the volume slider in the refreshed - volume panel. This is needed on player reload, since volume slider - is set on either 'ready' or 'volumechange' events. - */ - player.trigger('volumechange'); + /** + * Initialize the next Canvas to be viewed in the player instance + * @param {Number} canvasId index of the Canvas to be loaded into the player + * @param {Boolean} fromStart flag to indicate how to start new player instance + */ + var initCanvas = function initCanvas(canvasId, fromStart) { + clearDisplayTimeInterval(); + var _getMediaInfo = getMediaInfo({ + manifest: manifest, + canvasIndex: canvasId, + startTime: canvasId === customStart.startIndex && firstLoad ? customStart.startTime : 0, + srcIndex: srcIndex, + isPlaylist: isPlaylist + }), + isMultiSource = _getMediaInfo.isMultiSource, + sources = _getMediaInfo.sources, + tracks = _getMediaInfo.tracks, + canvasTargets = _getMediaInfo.canvasTargets, + mediaType = _getMediaInfo.mediaType, + error = _getMediaInfo.error, + poster = _getMediaInfo.poster; + if (withCredentials) { + sources.map(function (source) { + return source.withCredentials = true; + }); + } + setIsVideo(mediaType === 'video'); + manifestDispatch({ + canvasTargets: canvasTargets, + type: 'canvasTargets' + }); + manifestDispatch({ + isMultiSource: isMultiSource, + type: 'hasMultipleItems' + }); + + // Set the current time in player from the canvas details + if (fromStart) { + if ((canvasTargets === null || canvasTargets === void 0 ? void 0 : canvasTargets.length) > 0) { + playerDispatch({ + currentTime: canvasTargets[0].altStart, + type: 'setCurrentTime' + }); } else { - controlBar.removeChild('MuteToggle'); - controlBar.addChild('MuteToggle'); + playerDispatch({ + currentTime: 0, + type: 'setCurrentTime' + }); } - if (enableFileDownload) { - var fileDownloadIndex = controlBar.children().findIndex(function (c) { - return c.name_ == 'VideoJSFileDownload'; - }) || fullscreenIndex + 1; - controlBar.removeChild('videoJSFileDownload'); - if ((renderingFiles === null || renderingFiles === void 0 ? void 0 : renderingFiles.length) > 0) { - var fileOptions = { - title: 'Download Files', - controlText: 'Alternate resource download', - files: renderingFiles - }; - controlBar.addChild('videoJSFileDownload', _objectSpread$4({}, fileOptions), fileDownloadIndex); - } + } + setPlayerConfig(_objectSpread$5(_objectSpread$5({}, playerConfig), {}, { + error: error, + sources: sources, + tracks: tracks, + poster: poster, + targets: canvasTargets + })); + var currentCanvas = allCanvases.find(function (c) { + return c.canvasIndex === canvasId; + }); + if (!currentCanvas.isEmpty) { + // Manifest is taken from manifest state, and is a basic object at this point + // lacking the getLabel() function so we manually retrieve the first label. + var manifestLabel = manifest.label ? Object.values(manifest.label)[0][0] : ''; + // Filter out falsy items in case canvas.label is null or an empty string + var titleText = [manifestLabel, currentCanvas.label].filter(Boolean).join(' - '); + manifestDispatch({ + canvasDuration: currentCanvas.duration, + type: 'canvasDuration' + }); + manifestDispatch({ + canvasLink: { + label: titleText, + id: currentCanvas.canvasId + }, + type: 'canvasLink' + }); + manifestDispatch({ + type: 'setCanvasIsEmpty', + isEmpty: false + }); + } else { + playerDispatch({ + type: 'updatePlayer' + }); + manifestDispatch({ + type: 'setCanvasIsEmpty', + isEmpty: true + }); + // Set poster as playerConfig.error to be used for empty Canvas message in VideoJSPlayer + setPlayerConfig(_objectSpread$5(_objectSpread$5({}, playerConfig), {}, { + error: poster + })); + // Create timer to display the message when autoadvance is ON + if (autoAdvance) { + createDisplayTimeInterval(); } } + setIsMultiSourced(isMultiSource || false); + error ? setReady(false) : setReady(true); + // Reset firstLoad flag after customStart is used on initial load + setFirstLoad(false); }; /** - * Setup on loadedmetadata event is broken out of initial setup function, - * since this needs to be called when reloading the player on Canvas change - * @param {Object} player Video.js player instance + * Switch player when navigating across canvases + * @param {Number} index canvas index to be loaded into the player + * @param {Boolean} fromStart flag to indicate set player start time to zero or not + * @param {String} focusElement element to be focused within the player when using + * next or previous buttons with keyboard */ - var playerLoadedMetadata = function playerLoadedMetadata(player) { - player.one('loadedmetadata', function () { - console.log('Player loadedmetadata'); - player.duration(canvasDurationRef.current); - isEndedRef.current ? player.currentTime(0) : player.currentTime(currentTimeRef.current); - if (isEndedRef.current || isPlayingRef.current) { - /* - iOS devices lockdown the ability for unmuted audio and video media to autoplay. - They accomplish this by capturing any programmatic play events and returning - a rejected Promise. In certain versions of iOS, this rejected promise would - cause a runtime error within Ramp. This error would cause the error boundary - handling to trigger, forcing a user to reload the player/page. By silently - catching the rejected Promise we are able to provide a more seamless user - experience, where the user can manually play the media or change to a different - section. - */ - var promise = player.play(); - if (promise !== undefined) { - promise.then(function (_) { - // Autoplay - })["catch"](function (error) { - // Prevent error from triggering error boundary - }); - } - } - if (isVideo) { - setUpCaptions(player); - } - - /* - Set playable duration within the given media file and alternate start time as - player properties. These values are read by track-scrubber component to build - and update the track-scrubber progress and time in the UI. - */ - var mediaRange = getMediaFragment(player.src(), canvasDurationRef.current); - if (mediaRange != undefined) { - player.playableDuration = mediaRange.end - mediaRange.start; - player.altStart = mediaRange.start; - } else { - player.playableDuration = canvasDurationRef.current; - player.altStart = targets[srcIndex].altStart; - } - player.canvasIndex = cIndexRef.current; - setIsReady(true); - - /** - * Update currentNavItem on loadedmetadata event in Safari, as it doesn't - * trigger the 'timeupdate' event intermittently on load. - */ - if (IS_SAFARI) { - handleTimeUpdate(); - } - - /** - * When either player/browser tab is muted Safari and Chrome in iOS doesn't seem to - * load enough data related to audio-only media for the Video.js instance to play - * on page load. - * Since, it is not possible to detect muted tabs in JS the condition avoids - * checking for muted state altogether. - * Without this, Safari will not reach player.readyState() = 4, the state - * which indicates the player that enough data is available on the media - * for playback. - */ - if (!isVideo && (IS_SAFARI || IS_IOS) && player.readyState() != 4) { - player.load(); - } - - // Reveal player if not revealed on 'progress' event, allowing user to - // interact with the player since enough data is available for playback - if (player.hasClass('vjs-disabled')) { - player.removeClass('vjs-disabled'); - } - }); + var switchPlayer = function switchPlayer(index, fromStart) { + var focusElement = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; + if (index != undefined && index > -1 && index <= lastCanvasIndex) { + manifestDispatch({ + canvasIndex: index, + type: 'switchCanvas' + }); + initCanvas(index, fromStart); + playerDispatch({ + element: focusElement, + type: 'setPlayerFocusElement' + }); + } }; /** - * Setup player with player-related information parsed from the IIIF - * Manifest Canvas. This gets called on both initial page load and each - * Canvas switch to setup and update player respectively. - * @param {Object} player current player instance from Video.js + * Switch src in the player when seeked to a time range within a + * different item in the same canvas + * @param {Number} srcindex new srcIndex + * @param {Number} value current time of the player */ - var playerInitSetup = function playerInitSetup(player) { - player.on('ready', function () { - console.log('Player ready'); - - // Add this class in mobile/tablet devices to always show the control bar, - // since the inactivityTimeout is flaky in some browsers - if (IS_MOBILE || IS_IPAD) { - player.controlBar.addClass('vjs-mobile-visible'); - } - player.muted(startMuted); - player.volume(startVolume); - player.canvasIndex = cIndexRef.current; - player.duration(canvasDurationRef.current); - player.srcIndex = srcIndex; - player.targets = targets; - if (enableTitleLink) { - player.canvasLink = canvasLinkRef.current; - } - // Need to set this once experimentalSvgIcons option in Video.js options was enabled - player.getChild('controlBar').qualitySelector.setIcon('cog'); - }); - playerLoadedMetadata(player); - player.on('progress', function () { - // Reveal player if not revealed on 'loadedmetadata' event, allowing user to - // interact with the player since enough data is available for playback - if (player.hasClass('vjs-disabled')) { - player.removeClass('vjs-disabled'); - } - }); - player.on('canplay', function () { - // Reset isEnded flag - playerDispatch({ - isEnded: false, - type: 'setIsEnded' - }); - }); - player.on('play', function () { - playerDispatch({ - isPlaying: true, - type: 'setPlayingStatus' - }); - }); - player.on('timeupdate', function () { - handleTimeUpdate(); - }); - player.on('ended', function () { - /** - * Checking against isReadyRef stops from delayed events being executed - * when transitioning from a Canvas to the next. - * Checking against isPlayingRef.current to distinguish whether this event - * triggered intentionally, because Video.js seem to trigger this event when - * switching to a media file with a shorter duration in Safari browsers. - */ - setTimeout(function () { - if (isReadyRef.current && isPlayingRef.current) { - playerDispatch({ - isEnded: true, - type: 'setIsEnded' - }); - player.pause(); - if (!canvasIsEmptyRef.current) handleEnded(); - } - }, 100); - }); - player.on('volumechange', function () { - setStartMuted(player.muted()); - setStartVolume(player.volume()); - }); - player.on('qualityRequested', function (e, quality) { - setStartQuality(quality.label); + var nextItemClicked = function nextItemClicked(srcindex, value) { + playerDispatch({ + currentTime: value, + type: 'setCurrentTime' }); - // Use error event listener for inaccessible item display - player.on('error', function (e) { - var error = player.error(); - var errorMessage = 'Something went wrong. Please try again later or contact support for help.'; - // Handle different error codes - switch (error.code) { - case 1: - console.error('MEDIA_ERR_ABORTED: The fetching process for the media resource was aborted by the user agent\ - at the user’s request.'); - break; - case 2: - errorMessage = 'The media could not be loaded due to a network error. Please try again later.'; - console.error('MEDIA_ERR_NETWORK: A network error caused the user agent to stop fetching the media resource,\ - after the resource was established to be usable.'); - break; - case 3: - errorMessage = 'Media is corrupt or has features not supported by the browser. \ - Please try a different media or contact support for help.'; - console.error('MEDIA_ERR_DECODE: An error occurred while decoding the media resource, after\ - the resource was established to be usable.'); - break; - case 4: - errorMessage = 'Media could not be loaded. Network error or media format is not supported.'; - console.error('MEDIA_ERR_SRC_NOT_SUPPORTED: The media resource indicated by the src attribute was not suitable.'); - break; - default: - console.error('An unknown error occurred.'); - break; - } - // Show dismissable error display modal from Video.js - var errorDisplay = player.getChild('ErrorDisplay'); - if (errorDisplay) { - errorDisplay.contentEl().innerText = errorMessage; - errorDisplay.removeClass('vjs-hidden'); - player.removeClass('vjs-error'); - player.removeClass('vjs-disabled'); - } - e.stopPropagation(); + manifestDispatch({ + srcIndex: srcindex, + type: 'setSrcIndex' }); + }; + return { + isMultiSourced: isMultiSourced, + isPlaylist: isPlaylist, + isVideo: isVideo, + nextItemClicked: nextItemClicked, + playerConfig: playerConfig, + ready: ready, + renderingFiles: renderingFiles, + srcIndex: srcIndex, + switchPlayer: switchPlayer + }; +}; + +/** + * Initialize and update VideoJS instance on global state changes when + * Canvas changes + * @param {Object} obj + * @param {Object} obj.options VideoJS options + * @param {Function} obj.playerInitSetup VideoJS initialize setup func + * @param {String} obj.startQuality selected quality stored in local storage + * @param {Array} obj.tracks text tracks for the selected Canvas + * @param {Function} obj.updatePlayer VideoJS update func on Canvas change + * @param {Object} obj.videoJSRef React ref for video tag on page + * @param {String} obj.videoJSLangMap VideoJS language for set language + * @returns { + * activeId: string, + * fragmentMarker: obj, + * isReadyRef: obj, + * playerRef: obj, + * setActiveId: func, + * setFragmentMarker: func, + * setIsReady: func, + * } + */ +var useVideoJSPlayer = function useVideoJSPlayer(_ref3) { + var options = _ref3.options, + playerInitSetup = _ref3.playerInitSetup, + startQuality = _ref3.startQuality, + tracks = _ref3.tracks, + updatePlayer = _ref3.updatePlayer, + videoJSRef = _ref3.videoJSRef, + videoJSLangMap = _ref3.videoJSLangMap; + var manifestState = React.useContext(ManifestStateContext); + var playerState = React.useContext(PlayerStateContext); + var playerDispatch = React.useContext(PlayerDispatchContext); + var canvasDuration = manifestState.canvasDuration, + canvasIndex = manifestState.canvasIndex, + canvasIsEmpty = manifestState.canvasIsEmpty, + currentNavItem = manifestState.currentNavItem, + playlist = manifestState.playlist; + var currentTime = playerState.currentTime, + isClicked = playerState.isClicked, + isPlaying = playerState.isPlaying, + player = playerState.player, + searchMarkers = playerState.searchMarkers; + var _useState11 = React.useState(''), + _useState12 = _slicedToArray(_useState11, 2), + activeId = _useState12[0], + setActiveId = _useState12[1]; + var _useState13 = React.useState(null), + _useState14 = _slicedToArray(_useState13, 2), + fragmentMarker = _useState14[0], + setFragmentMarker = _useState14[1]; + // Needs to maintain this in a state variable for useEffect for marker updates + var _useState15 = React.useState(false), + _useState16 = _slicedToArray(_useState15, 2), + isReady = _useState16[0], + _setIsReady = _useState16[1]; + var isReadyRef = React.useRef(isReady); + var setIsReady = function setIsReady(r) { + _setIsReady(r); + isReadyRef.current = r; + }; + var playerRef = React.useRef(null); + React.useEffect(function () { /* This event handler helps to execute hotkeys functions related to 'keydown' events before any user interactions with the player or when focused on other non-input elements on the page */ document.addEventListener('keydown', function (event) { - var result = playerHotKeys(event, player, canvasIsEmptyRef.current); + var result = playerHotKeys(event, playerRef.current, canvasIsEmpty); // Update player status in global state switch (result) { case HOTKEY_ACTION_OUTPUT.pause: @@ -7227,94 +7144,157 @@ function VideoJSPlayer(_ref) { break; } }); - }; - - /** - * Setup captions for the player based on context - * @param {Object} player Video.js player instance - */ - var setUpCaptions = function setUpCaptions(player) { - var _textTracks$tracks_; - var textTracks = player.textTracks(); - /* - Filter the text track Video.js adds with an empty label and language - when nativeTextTracks are enabled for iPhones and iPads. - Related links, Video.js => https://github.com/videojs/video.js/issues/2808 and - in Apple => https://developer.apple.com/library/archive/qa/qa1801/_index.html - */ - if (IS_MOBILE && !IS_ANDROID) { - textTracks.on('addtrack', function () { - for (var i = 0; i < textTracks.length; i++) { - if (textTracks[i].language === '' && textTracks[i].label === '') { - player.textTracks().removeTrack(textTracks[i]); - } - /** - * This enables the caption in the native iOS player first playback. - * Only enable caption when captions are turned on. - * First caption is already turned on in the code block below, so read it - * from activeTrackRef - */ - if (startCaptioned && activeTrackRef.current) { - textTracks.tracks_.filter(function (t) { - return t.label === activeTrackRef.current.label && t.language === activeTrackRef.current.language; - })[0].mode = 'showing'; - } - } - }); - } - // Turn first caption/subtitle ON and turn captions ON indicator via CSS on first load - if (((_textTracks$tracks_ = textTracks.tracks_) === null || _textTracks$tracks_ === void 0 ? void 0 : _textTracks$tracks_.length) > 0) { - var firstSubCap = null; - // Flag to identify first valid caption for resource - var onFirstCap = false; - // Disable all text tracks to avoid multiple selections and pick the first one as default - for (var i = 0; i < textTracks.tracks_.length; i++) { - var t = textTracks.tracks_[i]; - if ((t.kind === 'subtitles' || t.kind === 'captions') && t.language != '' && t.label != '') { - t.mode = 'disabled'; - if (!onFirstCap) firstSubCap = t; - onFirstCap = true; - } + // Dispose Video.js instance when VideoJSPlayer component is removed + return function () { + if (player) { + player.dispose(); + document.removeEventListener('keydown', playerHotKeys); + setIsReady(false); } + }; + }, []); - // Enable the first caption when captions are enabled in the session - if (firstSubCap && startCaptioned) { - firstSubCap.mode = 'showing'; - activeTrackRef.current = firstSubCap; - handleCaptionChange(true); - } - } + // Update VideoJS instance on Canvas change + React.useEffect(function () { + var _options$sources, _options$sources2; + // Set selected quality from localStorage in Video.js options + setSelectedQuality(options.sources); - // Add/remove CSS to indicate captions/subtitles is turned on - textTracks.on('change', function () { - var trackModes = []; - for (var _i = 0; _i < textTracks.tracks_.length; _i++) { - var _textTracks$_i = textTracks[_i], - mode = _textTracks$_i.mode, - label = _textTracks$_i.label, - kind = _textTracks$_i.kind; - trackModes.push(textTracks[_i].mode); - if (mode === 'showing' && label != '' && (kind === 'subtitles' || kind === 'captions')) { - activeTrackRef.current = textTracks[_i]; + // Video.js player is only initialized on initial page load + if (!playerRef.current && ((_options$sources = options.sources) === null || _options$sources === void 0 ? void 0 : _options$sources.length) > 0) { + videojs__default["default"].addLanguage(options.language, JSON.parse(videoJSLangMap)); + buildTracksHTML(); + + // Turn Video.js logging off and handle errors in this code, to avoid + // cluttering the console when loading inaccessible items. + videojs__default["default"].log.level('off'); + var _player = playerRef.current = videojs__default["default"](videoJSRef.current, options, function () { + playerInitSetup(playerRef.current); + }); + + /* Another way to add a component to the controlBar */ + // player.getChild('controlBar').addChild('vjsYo', {}); + + playerDispatch({ + player: _player, + type: 'updatePlayer' + }); + + // Update player status in state only when pause is initiate by the user + _player.controlBar.getChild('PlayToggle').on('pointerdown', function () { + handlePause(); + }); + _player.on('pointerdown', function (e) { + var elementTag = e.target.nodeName.toLowerCase(); + if (elementTag == 'video') { + handlePause(); } + }); + } else if (playerRef.current && ((_options$sources2 = options.sources) === null || _options$sources2 === void 0 ? void 0 : _options$sources2.length) > 0) { + var _player2$markers; + // Update the existing Video.js player on consecutive Canvas changes + var _player2 = playerRef.current; + + // Reset markers + if (activeId) (_player2$markers = _player2.markers) === null || _player2$markers === void 0 ? void 0 : _player2$markers.removeAll(); + setActiveId(null); + + // Block player while metadata is loaded when canvas is not empty + if (!canvasIsEmpty) { + _player2.addClass('vjs-disabled'); + setIsReady(false); + updatePlayer(_player2); + playerDispatch({ + player: _player2, + type: 'updatePlayer' + }); + } else { + // Mark as ready to for inaccessible canvas (empty) + setIsReady(true); } - var subsOn = trackModes.includes('showing') ? true : false; - handleCaptionChange(subsOn); - setStartCaptioned(subsOn); - }); - }; + } + }, [options.sources, videoJSRef]); + React.useEffect(function () { + if (player) { + // Show/hide control bar for valid/inaccessible items respectively + if (canvasIsEmpty) { + // Set the player's aspect ratio to video + player.audioOnlyMode(false); + player.canvasIsEmpty = true; + player.aspectRatio('16:9'); + player.controlBar.addClass('vjs-hidden'); + player.removeClass('vjs-disabled'); + player.pause(); + /** + * Update the activeId to update the active item in the structured navigation. + * For playable items this is updated in the timeupdate handler. + */ + setActiveId(currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.id); + } else { + // Reveal control bar; needed when loading a Canvas after an inaccessible item + player.controlBar.removeClass('vjs-hidden'); + } + } + }, [canvasIndex, canvasIsEmpty, currentNavItem]); - /** - * Setting the current time of the player when using structure navigation - */ - React__default["default"].useEffect(function () { - if (playerRef.current !== null && isReadyRef.current) { - playerRef.current.currentTime(currentTimeRef.current, playerDispatch({ + // Setting the current time of the player when using structure navigation + React.useEffect(function () { + if (playerRef.current) { + playerRef.current.currentTime(currentTime, playerDispatch({ type: 'resetClick' })); } - }, [isClicked, isReady]); + }, [isClicked]); + + // Update VideoJS player's markers for search hits/playlist markers/structure navigation + React.useEffect(function () { + if (playerRef.current && playerRef.current.markers && isReady) { + var _playlist$markers, _playerRef$current$ma; + // markers plugin not yet initialized + if (typeof playerRef.current.markers === 'function') { + playerRef.current.markers({ + markerTip: { + display: false, + // true, + text: function text(marker) { + return marker.text; + } + }, + markerStyle: {}, + markers: [] + }); + } + var playlistMarkers = []; + if (playlist !== null && playlist !== void 0 && (_playlist$markers = playlist.markers) !== null && _playlist$markers !== void 0 && _playlist$markers.length) { + var canvasMarkers = playlist.markers.filter(function (m) { + return m.canvasIndex === canvasIndex; + })[0].canvasMarkers; + playlistMarkers = canvasMarkers.map(function (m) { + return { + time: parseFloat(m.time), + text: m.value, + "class": 'ramp--track-marker--playlist' + }; + }); + } + (_playerRef$current$ma = playerRef.current.markers) === null || _playerRef$current$ma === void 0 ? void 0 : _playerRef$current$ma.removeAll(); + playerRef.current.markers.add([].concat(_toConsumableArray(fragmentMarker ? [fragmentMarker] : []), _toConsumableArray(searchMarkers), _toConsumableArray(playlistMarkers))); + } + }, [fragmentMarker, searchMarkers, canvasDuration, canvasIndex, playerRef.current, isReady]); + + /** + * Update global state only when a user pause the player by using the + * player interface or keyboard shortcuts + */ + var handlePause = function handlePause() { + if (isPlaying) { + playerDispatch({ + isPlaying: false, + type: 'setPlayingStatus' + }); + } + }; var setSelectedQuality = function setSelectedQuality(sources) { //iterate through sources and find source that matches startQuality and source currently marked selected //if found set selected attribute on matching source then remove from currently marked one @@ -7331,3040 +7311,3808 @@ function VideoJSPlayer(_ref) { }; /** - * Add CSS class to icon to indicate captions are on/off in player control bar - * @param {Boolean} subsOn flag to indicate captions are on/off + * Build track HTML for Video.js player on initial page load */ - var handleCaptionChange = function handleCaptionChange(subsOn) { - var player = playerRef.current; - /** - * When subsCapsButton is not setup on Video.js initialization step, and is - * later added in updatePlayer() function player.controlBar.getChild() method - * needs to be used to access it. - */ - var subsCapsBtn = player.controlBar.getChild('subsCapsButton'); - /* - For audio instances Video.js is setup to not to build the CC button - in Ramp's player control bar. - */ - if (subsCapsBtn == undefined || !subsCapsBtn || !(subsCapsBtn !== null && subsCapsBtn !== void 0 && subsCapsBtn.children_)) { - return; - } - if (subsOn) { - subsCapsBtn.children_[0].addClass('captions-on'); - captionsOnRef.current = true; - } else { - subsCapsBtn.children_[0].removeClass('captions-on'); - captionsOnRef.current = false; + var buildTracksHTML = function buildTracksHTML() { + if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0 && videoJSRef.current) { + tracks.map(function (t) { + var trackEl = document.createElement('track'); + trackEl.setAttribute('key', t.key); + trackEl.setAttribute('src', t.src); + trackEl.setAttribute('kind', t.kind); + trackEl.setAttribute('label', t.label); + trackEl.setAttribute('srclang', t.srclang); + videoJSRef.current.appendChild(trackEl); + }); } }; + return { + activeId: activeId, + fragmentMarker: fragmentMarker, + isReadyRef: isReadyRef, + playerRef: playerRef, + setActiveId: setActiveId, + setFragmentMarker: setFragmentMarker, + setIsReady: setIsReady + }; +}; + +/** + * Handle display of inaccessible message timer and interval for + * countdown + * @param {Object} obj + * @param {Number} obj.lastCanvasIndex + * @returns { + * messageTime: number, + * clearCanvasMessageTimer: func, + * createCanvasMessageTimer: func + * } + */ +var useShowInaccessibleMessage = function useShowInaccessibleMessage(_ref4) { + var lastCanvasIndex = _ref4.lastCanvasIndex; + var manifestDispatch = React.useContext(ManifestDispatchContext); + var manifestState = React.useContext(ManifestStateContext); + var autoAdvance = manifestState.autoAdvance, + canvasIndex = manifestState.canvasIndex, + canvasIsEmpty = manifestState.canvasIsEmpty; + var _useState17 = React.useState(CANVAS_MESSAGE_TIMEOUT / 1000), + _useState18 = _slicedToArray(_useState17, 2), + messageTime = _useState18[0], + setMessageTime = _useState18[1]; + var canvasIndexRef = React.useRef(); + canvasIndexRef.current = React.useMemo(function () { + return canvasIndex; + }, [canvasIndex]); + var messageIntervalRef = React.useRef(null); + React.useEffect(function () { + // Clear existing interval for inaccessible message display + clearDisplayTimeInterval(); + if (canvasIsEmpty && !messageIntervalRef.current && autoAdvance) { + setMessageTime(CANVAS_MESSAGE_TIMEOUT / 1000); + createDisplayTimeInterval(); + } + }, [canvasIndex, autoAdvance, canvasIsEmpty]); /** - * Handle the 'ended' event fired by the player when a section comes to - * an end. If there are sections ahead move onto the next canvas and - * change the player and the state accordingly. - * Throttle helps to cancel the delayed function call triggered by ended event and - * load the correct item into the player, when the user clicks on a different item - * (not the next item in list) when the current item is coming to its end. + * Create an interval to run every second to update display for the timer + * for inaccessible canvas message display. Using useCallback to cache the + * function as this doesn't need to change with component re-renders */ - var handleEnded = React__default["default"].useMemo(function () { - return throttle_1(function () { - var isLastCanvas = cIndexRef.current === lastCanvasIndex; - /** - * Do nothing if Canvas is not multi-sourced AND autoAdvance is turned off - * OR current Canvas is the last Canvas in the Manifest - */ - if ((!autoAdvanceRef.current || isLastCanvas) && !hasMultiItems) { - return; + var createDisplayTimeInterval = React.useCallback(function () { + var createTime = new Date().getTime(); + messageIntervalRef.current = setInterval(function () { + var now = new Date().getTime(); + var timeRemaining = (CANVAS_MESSAGE_TIMEOUT - (now - createTime)) / 1000; + if (timeRemaining > 0) { + setMessageTime(Math.ceil(timeRemaining)); } else { - var _structuresRef$curren; - // Remove all the existing structure related markers in the player - if (playerRef.current && playerRef.current.markers) { - playerRef.current.pause(); - setFragmentMarker(null); - playerRef.current.markers.removeAll(); + // Advance to next Canvas when timer ends + if (canvasIndexRef.current < lastCanvasIndex && autoAdvance) { + manifestDispatch({ + canvasIndex: canvasIndexRef.current + 1, + type: 'switchCanvas' + }); } - if (hasMultiItems) { - // When there are multiple sources in a single canvas - // advance to next source - if (srcIndex + 1 < targets.length) { - manifestDispatch({ - srcIndex: srcIndex + 1, - type: 'setSrcIndex' - }); - playerDispatch({ - currentTime: 0, - type: 'setCurrentTime' - }); - playerRef.current.play(); - } else { - return; - } - } else if (((_structuresRef$curren = structuresRef.current) === null || _structuresRef$curren === void 0 ? void 0 : _structuresRef$curren.length) > 0) { - var nextItem = structuresRef.current[cIndexRef.current + 1]; - if (nextItem) { - manifestDispatch({ - canvasIndex: cIndexRef.current + 1, - type: 'switchCanvas' - }); - - // Reset startTime and currentTime to zero - playerDispatch({ - startTime: 0, - type: 'setTimeFragment' - }); - playerDispatch({ - currentTime: 0, - type: 'setCurrentTime' - }); + clearDisplayTimeInterval(); + } + }, 1000); + }); - // Get first timespan in the next canvas - var firstTimespanInNextCanvas = canvasSegmentsRef.current.filter(function (t) { - return t.canvasIndex === nextItem.canvasIndex && t.itemIndex === 1; - }); - // If the nextItem doesn't have an ID (a Canvas media fragment) pick the first timespan - // in the next Canvas - var nextFirstItem = nextItem.id != undefined ? nextItem : firstTimespanInNextCanvas[0]; - var start = 0; - if (nextFirstItem != undefined && nextFirstItem.id != undefined) { - start = getMediaFragment(nextFirstItem.id, canvasDurationRef.current).start; - } + // Cleanup interval created for timer display for inaccessible message + var clearDisplayTimeInterval = React.useCallback(function () { + clearInterval(messageIntervalRef.current); + messageIntervalRef.current = null; + }); + return { + messageTime: messageTime, + clearDisplayTimeInterval: clearDisplayTimeInterval, + createDisplayTimeInterval: createDisplayTimeInterval + }; +}; - // If there's a timespan item at the start of the next canvas - // mark it as the currentNavItem. Otherwise empty out the currentNavItem. - if (start === 0) { - manifestDispatch({ - item: nextFirstItem, - type: 'switchItem' - }); - } else if (nextFirstItem.isEmpty) { - // Switch the currentNavItem and clear isEnded flag - manifestDispatch({ - item: nextFirstItem, - type: 'switchItem' - }); - playerRef.current.currentTime(start); - // Only play if the next item is not an inaccessible item - if (!nextItem.isEmpty) playerRef.current.play(); - } - } - } - } +/** + * Handle global state updates and local state updates for structured + * navigation related components based on the user interactions and + * player status updates + * @param {Object} obj + * @param {Number} obj.itemIndex + * @param {Boolean} obj.isRoot + * @param {String} obj.itemId URL of the struct item + * @param {Object} obj.liRef React ref for li element for struct item + * @param {Object} obj.sectionRef React ref for collapsible ul element + * @param {Boolean} obj.isCanvas + * @param {Number} obj.canvasDuration + * @param {Function} obj.setIsOpen + * @returns + */ +var useActiveStructure = function useActiveStructure(_ref5) { + var itemIndex = _ref5.itemIndex, + isRoot = _ref5.isRoot, + itemId = _ref5.itemId, + liRef = _ref5.liRef, + sectionRef = _ref5.sectionRef, + isCanvas = _ref5.isCanvas, + canvasDuration = _ref5.canvasDuration, + setIsOpen = _ref5.setIsOpen; + var playerDispatch = React.useContext(PlayerDispatchContext); + var manifestState = React.useContext(ManifestStateContext); + var canvasIndex = manifestState.canvasIndex, + currentNavItem = manifestState.currentNavItem, + playlist = manifestState.playlist; + var isPlaylist = playlist.isPlaylist; + var isActiveLi = React.useMemo(function () { + return itemId != undefined && (currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.id) === itemId && (isPlaylist || !isCanvas) && (currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.canvasIndex) === canvasIndex + 1 ? true : false; + }, [currentNavItem, canvasIndex]); + var isActiveSection = React.useMemo(function () { + var isCurrentSection = canvasIndex + 1 === itemIndex; + // Do not mark root range as active + if (isCurrentSection && !isRoot) { + // Collapse the section in structured navigation + setIsOpen(true); + return true; + } else { + return false; + } + }, [canvasIndex]); + var handleClick = React.useCallback(function (e) { + e.preventDefault(); + e.stopPropagation(); + var _getMediaFragment = getMediaFragment(itemId, canvasDuration), + start = _getMediaFragment.start, + end = _getMediaFragment.end; + var inRange = checkSrcRange({ + start: start, + end: end + }, { + end: canvasDuration }); - }, [cIndexRef.current]); - - /** - * Handle the 'timeUpdate' event emitted by VideoJS player. - * The current time of the playhead used to show structure in the player's - * time rail as the playhead arrives at a start time of an existing structure - * item. When the current time is inside an item, that time fragment is highlighted - * in the player's time rail. - * Using throttle helps for smooth updates by cancelling and cleaning up intermediate - * delayed function calls. - */ - var handleTimeUpdate = React__default["default"].useMemo(function () { - return throttle_1(function () { - var player = playerRef.current; - if (player !== null && isReadyRef.current) { - var _player$currentTime; - var playerTime = (_player$currentTime = player.currentTime()) !== null && _player$currentTime !== void 0 ? _player$currentTime : currentTimeRef.current; - if (hasMultiItems && srcIndexRef.current > 0) { - playerTime = playerTime + targets[srcIndexRef.current].altStart; - } - var activeSegment = getActiveSegment(playerTime); - // the active segment has changed - if (activeIdRef.current !== (activeSegment === null || activeSegment === void 0 ? void 0 : activeSegment.id)) { - if (activeSegment === null) { - /** - * Clear currentNavItem and other related state variables to update the tracker - * in structure navigation and highlights within the player. - */ - manifestDispatch({ - item: null, - type: 'switchItem' - }); - setActiveId(null); - setFragmentMarker(null); - } else { - // Set the active segment in state - manifestDispatch({ - item: activeSegment, - type: 'switchItem' - }); - setActiveId(activeSegment.id); - if (!isPlaylist && player.markers) { - var _getMediaFragment = getMediaFragment(activeSegment.id, activeSegment.canvasDuration), - start = _getMediaFragment.start, - end = _getMediaFragment.end; - playerDispatch({ - endTime: end, - startTime: start, - type: 'setTimeFragment' - }); - if (start !== end) { - // don't let marker extend past the end of the canvas - var markerEnd = end > activeSegment.canvasDuration ? activeSegment.canvasDuration : end; - setFragmentMarker({ - time: start, - duration: markerEnd - start, - text: start, - "class": 'ramp--track-marker--fragment' - }); - } else { - // to prevent zero duration fragments I suppose - setFragmentMarker(null); - } - } else if (fragmentMarker !== null) { - setFragmentMarker(null); - } - } - } - } - }, 10); - }, []); - - /** - * Update global state only when a user pause the player by using the - * player interface or keyboard shortcuts - */ - var handlePause = function handlePause() { - if (isPlayingRef.current) { + /* + Only continue the click action if not both start and end times of + the timespan are not outside Canvas' duration + */ + if (inRange) { playerDispatch({ - isPlaying: false, - type: 'setPlayingStatus' + clickedUrl: itemId, + type: 'navClick' }); - } - }; - - /** - * Toggle play/pause on video touch for mobile browsers - * @param {Object} e onTouchEnd event - */ - var mobilePlayToggle = function mobilePlayToggle(e) { - if (e.changedTouches[0].clientX == touchX && e.changedTouches[0].clientY == touchY) { - if (player.paused()) { - player.play(); - } else { - player.pause(); + liRef.current.isClicked = true; + if (sectionRef.current) { + sectionRef.current.isClicked = true; } } + }); + return { + isActiveSection: isActiveSection, + isActiveLi: isActiveLi, + handleClick: handleClick, + canvasIndex: canvasIndex, + currentNavItem: currentNavItem, + isPlaylist: isPlaylist }; - - /** - * Save coordinates of touch start for comparison to touch end to prevent play/pause - * when user is scrolling. - * @param {Object} e onTouchStart event - */ - var touchX = null; - var touchY = null; - var saveTouchStartCoords = function saveTouchStartCoords(e) { - touchX = e.touches[0].clientX; - touchY = e.touches[0].clientY; +}; +var useTranscripts = function useTranscripts(_ref6) { + var manifestUrl = _ref6.manifestUrl, + playerID = _ref6.playerID, + setCurrentTime = _ref6.setCurrentTime, + transcripts = _ref6.transcripts; + var manifestState = React.useContext(ManifestStateContext); + var playerState = React.useContext(PlayerStateContext); + var NO_TRANSCRIPTS_MSG = 'No valid Transcript(s) found, please check again.'; + var INVALID_URL_MSG = 'Invalid URL for transcript, please check again.'; + var INVALID_VTT = 'Invalid WebVTT file, please check again.'; + var INVALID_TIMESTAMP = 'Invalid timestamp format in cue(s), please check again.'; + var NO_SUPPORT_MSG = 'Transcript format is not supported, please check again.'; + var abortController = new AbortController(); + var canvasIndexRef = React.useRef(); + var setCanvasIndex = function setCanvasIndex(c) { + abortController.abort(); + canvasIndexRef.current = c; }; + var playerRef = React.useRef(null); + var playerIntervalRef = React.useRef(null); + var _useState19 = React.useState(true), + _useState20 = _slicedToArray(_useState19, 2), + isEmpty = _useState20[0], + setIsEmpty = _useState20[1]; + var _useState21 = React.useState(true), + _useState22 = _slicedToArray(_useState21, 2), + isLoading = _useState22[0], + setIsLoading = _useState22[1]; + var _useState23 = React.useState([]), + _useState24 = _slicedToArray(_useState23, 2), + transcript = _useState24[0], + setTranscript = _useState24[1]; + var _useState25 = React.useState([]), + _useState26 = _slicedToArray(_useState25, 2), + transcriptsList = _useState26[0], + setTranscriptsList = _useState26[1]; + var _useState27 = React.useState({ + title: null, + filename: null, + id: null, + tUrl: null, + tType: null, + tFileExt: null, + isMachineGen: false, + tError: null + }), + _useState28 = _slicedToArray(_useState27, 2), + transcriptInfo = _useState28[0], + setTranscriptInfo = _useState28[1]; + var _useState29 = React.useState([]), + _useState30 = _slicedToArray(_useState29, 2), + canvasTranscripts = _useState30[0], + setCanvasTranscripts = _useState30[1]; + // Store transcript data in state to avoid re-requesting file contents + var _useState31 = React.useState([]), + _useState32 = _slicedToArray(_useState31, 2), + cachedTranscripts = _useState32[0], + setCachedTranscripts = _useState32[1]; + var _useState33 = React.useState(), + _useState34 = _slicedToArray(_useState33, 2), + selectedTranscript = _useState34[0], + setSelectedTranscript = _useState34[1]; /** - * Get the segment, which encapsulates the current time of the playhead, - * from a list of media fragments in the current canvas. - * @param {Number} time playhead's current time + * Start an interval at the start of the component to poll the + * canvasindex attribute changes in the player on the page */ - var getActiveSegment = function getActiveSegment(time) { - // Adjust time for multi-item canvases - var currentTime = time; - if (hasMultiItems) { - currentTime = currentTime + targets[srcIndex].altStart; - } - if (playlist.isPlaylist) { - // For playlists timespans and canvasIdex are mapped one-to-one - return canvasSegmentsRef.current[cIndexRef.current]; + React.useEffect(function () { + if (manifestState && playerState) { + canvasIndexRef.current = manifestState.canvasIndex; + playerRef.current = playerState.player; } else { - // Find the relevant media segment from the structure - var _iterator = _createForOfIteratorHelper$2(canvasSegmentsRef.current), - _step; - try { - for (_iterator.s(); !(_step = _iterator.n()).done;) { - var segment = _step.value; - var id = segment.id, - isCanvas = segment.isCanvas, - _canvasIndex = segment.canvasIndex; - if (_canvasIndex == cIndexRef.current + 1) { - // Canvases without structure has the Canvas information - // in Canvas-level item as a navigable link - if (isCanvas) { - return segment; - } - var segmentRange = getMediaFragment(id, canvasDuration); - var isInRange = checkSrcRange(segmentRange, canvasDuration); - var isInSegment = currentTime >= segmentRange.start && currentTime < segmentRange.end; - if (isInSegment && isInRange) { - return segment; - } + playerIntervalRef.current = setInterval(function () { + var domPlayer = document.getElementById(playerID); + if (!domPlayer) { + console.warn("Cannot find player, ".concat(playerID, " on page. Transcript synchronization is disabled")); + // Inaccessible canvas => stop loading spinner + setIsLoading(false); + } else { + if (domPlayer.player) playerRef.current = domPlayer.player;else playerRef.current = domPlayer; + } + if (playerRef.current) { + var cIndex = parseInt(playerRef.current.canvasIndex); + if (Number.isNaN(cIndex)) cIndex = 0; + if (cIndex !== canvasIndexRef.current) { + // Clear the transcript text in the component + setTranscript([]); + setCanvasIndex(cIndex); + setCurrentTime(playerRef.current.currentTime()); } } - } catch (err) { - _iterator.e(err); - } finally { - _iterator.f(); - } - return null; + }, 500); + } + if (playerRef.current) { + playerRef.current.on('timeupdate', function () { + setCurrentTime(playerRef.current.currentTime()); + }); + } + }, [manifestState]); + React.useEffect(function () { + if ((transcripts === null || transcripts === void 0 ? void 0 : transcripts.length) === 0 && !manifestUrl) { + // When both required props are invalid + setIsLoading(false); + setTranscript([]); + setTranscriptInfo({ + tType: TRANSCRIPT_TYPES.noTranscript, + id: '', + tError: NO_TRANSCRIPTS_MSG + }); + } else { + loadTranscripts(transcripts); } - }; - /** - * Create an interval to run every second to update display for the timer - * for inaccessible canvas message display. Using useCallback to cache the - * function as this doesn't need to change with component re-renders - */ - var createDisplayTimeInterval = React__default["default"].useCallback(function () { - if (!autoAdvanceRef.current) return; - var createTime = new Date().getTime(); - messageIntervalRef.current = setInterval(function () { - var now = new Date().getTime(); - var timeRemaining = (CANVAS_MESSAGE_TIMEOUT - (now - createTime)) / 1000; - if (timeRemaining > 0) { - setMessageTime(Math.ceil(timeRemaining)); - } else { - clearDisplayTimeInterval(); - } - }, 1000); + // Clean up state when the component unmounts + return function () { + clearInterval(playerIntervalRef.current); + }; }, []); /** - * Cleanup interval created for timer display for inaccessible message + * If a list of transcripts is given in the props, then sanitize them + * to match the expected format in the component. + * If not fallback to reading transcripts from a given manifest URL. + * @param {Array} transcripts list of transcripts from props */ - var clearDisplayTimeInterval = React__default["default"].useCallback(function () { - clearInterval(messageIntervalRef.current); - messageIntervalRef.current = null; - }); - return /*#__PURE__*/React__default["default"].createElement("div", null, /*#__PURE__*/React__default["default"].createElement("div", { - "data-vjs-player": true, - "data-canvasindex": cIndexRef.current - }, canvasIsEmptyRef.current && /*#__PURE__*/React__default["default"].createElement("div", { - "data-testid": "inaccessible-message-display" - // These styles needs to be inline for the poster to display within the Video boundaries - , - style: { - position: !playerRef.current ? 'relative' : 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - fontSize: 'medium', - color: '#fff', - backgroundColor: 'black', - zIndex: 101, - aspectRatio: !playerRef.current ? '16/9' : '', - textAlign: 'center' - } - }, /*#__PURE__*/React__default["default"].createElement("p", { - className: "ramp--media-player_inaccessible-message-content", - "data-testid": "inaccessible-message-content", - dangerouslySetInnerHTML: { - __html: placeholderText - } - }), /*#__PURE__*/React__default["default"].createElement("div", { - className: "ramp--media-player_inaccessible-message-buttons" - }, canvasIndex >= 1 && /*#__PURE__*/React__default["default"].createElement("button", { - "aria-label": "Go back to previous item", - onClick: function onClick() { - return loadPrevOrNext(canvasIndex - 1, true); - }, - "data-testid": "inaccessible-previous-button" - }, /*#__PURE__*/React__default["default"].createElement(SectionButtonIcon, { - flip: true - }), " Previous"), canvasIndex != lastCanvasIndex && /*#__PURE__*/React__default["default"].createElement("button", { - "aria-label": "Go to next item", - onClick: function onClick() { - return loadPrevOrNext(canvasIndex + 1, true); - }, - "data-testid": "inaccessible-next-button" - }, "Next ", /*#__PURE__*/React__default["default"].createElement(SectionButtonIcon, null))), canvasIndex != lastCanvasIndex && /*#__PURE__*/React__default["default"].createElement("p", { - "data-testid": "inaccessible-message-timer", - className: "ramp--media-player_inaccessible-message-timer ".concat(autoAdvanceRef.current ? '' : 'hidden') - }, "Next item in ".concat(messageTime, " second").concat(messageTime === 1 ? '' : 's'))), /*#__PURE__*/React__default["default"].createElement("video", { - "data-testid": "videojs-".concat(isVideo ? 'video' : 'audio', "-element"), - "data-canvasindex": cIndexRef.current, - ref: videoJSRef, - className: "video-js vjs-big-play-centered vjs-theme-ramp vjs-disabled ".concat(IS_ANDROID ? 'is-mobile' : ''), - onTouchStart: saveTouchStartCoords, - onTouchEnd: mobilePlayToggle, - style: { - display: "".concat(canvasIsEmptyRef.current ? 'none' : '') + var loadTranscripts = /*#__PURE__*/function () { + var _ref7 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee(transcripts) { + var allTranscripts; + return regenerator.wrap(function _callee$(_context) { + while (1) switch (_context.prev = _context.next) { + case 0: + if (!((transcripts === null || transcripts === void 0 ? void 0 : transcripts.length) > 0 + // transcripts prop is processed first if given + )) { + _context.next = 6; + break; + } + _context.next = 3; + return sanitizeTranscripts(transcripts); + case 3: + _context.t0 = _context.sent; + _context.next = 9; + break; + case 6: + _context.next = 8; + return readSupplementingAnnotations(manifestUrl); + case 8: + _context.t0 = _context.sent; + case 9: + allTranscripts = _context.t0; + setTranscriptsList(allTranscripts !== null && allTranscripts !== void 0 ? allTranscripts : []); + initTranscriptData(allTranscripts !== null && allTranscripts !== void 0 ? allTranscripts : []); + case 12: + case "end": + return _context.stop(); + } + }, _callee); + })); + return function loadTranscripts(_x) { + return _ref7.apply(this, arguments); + }; + }(); + var initTranscriptData = function initTranscriptData(allTranscripts) { + var _getCanvasT, _getTItems; + // When canvasIndex updates -> return + if (abortController.signal.aborted) return; + var getCanvasT = function getCanvasT(tr) { + return tr.filter(function (t) { + return t.canvasId == canvasIndexRef.current; + }); + }; + var getTItems = function getTItems(tr) { + return getCanvasT(tr)[0].items; + }; + /** + * When transcripts prop is empty + * OR the respective canvas doesn't have transcript data + * OR canvas' transcript items list is empty + */ + if (!(allTranscripts !== null && allTranscripts !== void 0 && allTranscripts.length) > 0 || !((_getCanvasT = getCanvasT(allTranscripts)) !== null && _getCanvasT !== void 0 && _getCanvasT.length) > 0 || !((_getTItems = getTItems(allTranscripts)) !== null && _getTItems !== void 0 && _getTItems.length) > 0) { + setIsEmpty(true); + setTranscript([]); + setStateVar(undefined); + } else { + setIsEmpty(false); + var cTranscripts = getCanvasT(allTranscripts)[0]; + setCanvasTranscripts(cTranscripts.items); + setStateVar(cTranscripts.items[0]); } - })), (hasStructure || playlist.isPlaylist) && /*#__PURE__*/React__default["default"].createElement("div", { - className: "vjs-track-scrubber-container hidden", - ref: trackScrubberRef, - id: "track_scrubber" - }, /*#__PURE__*/React__default["default"].createElement("p", { - className: "vjs-time track-currenttime", - role: "presentation" - }), /*#__PURE__*/React__default["default"].createElement("span", { - type: "range", - "aria-label": "Track scrubber", - role: "slider", - tabIndex: 0, - className: "vjs-track-scrubber", - style: { - width: '100%' + }; + React.useEffect(function () { + if ((transcriptsList === null || transcriptsList === void 0 ? void 0 : transcriptsList.length) > 0 && canvasIndexRef.current != undefined) { + var cTranscripts = transcriptsList.filter(function (tr) { + return tr.canvasId == canvasIndexRef.current; + })[0]; + setCanvasTranscripts(cTranscripts.items); + setStateVar(cTranscripts.items[0]); } - }, !IS_TOUCH_ONLY && /*#__PURE__*/React__default["default"].createElement("span", { - className: "tooltiptext", - ref: scrubberTooltipRef, - "aria-hidden": true, - role: "presentation" - })), /*#__PURE__*/React__default["default"].createElement("p", { - className: "vjs-time track-duration", - role: "presentation" - }))); -} -VideoJSPlayer.propTypes = { - isVideo: PropTypes.bool, - hasMultipleCanvases: PropTypes.bool, - isPlaylist: PropTypes.bool, - trackScrubberRef: PropTypes.object, - scrubberTooltipRef: PropTypes.object, - tracks: PropTypes.array, - placeholderText: PropTypes.string, - renderingFiles: PropTypes.array, - enableFileDownload: PropTypes.bool, - cancelAutoAdvance: PropTypes.func, - loadPrevOrNext: PropTypes.func, - lastCanvasIndex: PropTypes.number, - videoJSOptions: PropTypes.object -}; + }, [canvasIndexRef.current]); // helps to load initial transcript with async req -var Play = "Play"; -var Pause = "Pause"; -var Replay = "Replay"; -var Duration = "Duration"; -var LIVE = "LIVE"; -var Loaded = "Loaded"; -var Progress = "Progress"; -var Fullscreen = "Fullscreen"; -var Mute = "Mute"; -var Unmute = "Unmute"; -var Subtitles = "Subtitles"; -var Captions = "Captions"; -var Chapters = "Chapters"; -var Descriptions = "Descriptions"; -var Close = "Close"; -var Text = "Text"; -var White = "White"; -var Black = "Black"; -var Red = "Red"; -var Green = "Green"; -var Blue = "Blue"; -var Yellow = "Yellow"; -var Magenta = "Magenta"; -var Cyan = "Cyan"; -var Background = "Background"; -var Window = "Window"; -var Transparent = "Transparent"; -var Opaque = "Opaque"; -var None = "None"; -var Raised = "Raised"; -var Depressed = "Depressed"; -var Uniform = "Uniform"; -var Casual = "Casual"; -var Script = "Script"; -var Reset = "Reset"; -var Done = "Done"; -var Color = "Color"; -var Opacity = "Opacity"; -var en = { - "Audio Player": "Audio Player", - "Video Player": "Video Player", - Play: Play, - Pause: Pause, - Replay: Replay, - "Current Time": "Current Time", - Duration: Duration, - "Remaining Time": "Remaining Time", - "Stream Type": "Stream Type", - LIVE: LIVE, - "Seek to live, currently behind live": "Seek to live, currently behind live", - "Seek to live, currently playing live": "Seek to live, currently playing live", - Loaded: Loaded, - Progress: Progress, - "Progress Bar": "Progress Bar", - "progress bar timing: currentTime={1} duration={2}": "{1} of {2}", - Fullscreen: Fullscreen, - "Exit Fullscreen": "Exit Fullscreen", - Mute: Mute, - Unmute: Unmute, - "Playback Rate": "Playback Rate", - Subtitles: Subtitles, - "subtitles off": "subtitles off", - Captions: Captions, - "captions off": "captions off", - Chapters: Chapters, - Descriptions: Descriptions, - "descriptions off": "descriptions off", - "Audio Track": "Audio Track", - "Volume Level": "Volume Level", - "You aborted the media playback": "You aborted the media playback", - "A network error caused the media download to fail part-way.": "A network error caused the media download to fail part-way.", - "The media could not be loaded, either because the server or network failed or because the format is not supported.": "The media could not be loaded, either because the server or network failed or because the format is not supported.", - "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.", - "No compatible source was found for this media.": "No compatible source was found for this media.", - "The media is encrypted and we do not have the keys to decrypt it.": "The media is encrypted and we do not have the keys to decrypt it.", - "Play Video": "Play Video", - Close: Close, - "Close Modal Dialog": "Close Modal Dialog", - "Modal Window": "Modal Window", - "This is a modal window": "This is a modal window", - "This modal can be closed by pressing the Escape key or activating the close button.": "This modal can be closed by pressing the Escape key or activating the close button.", - ", opens captions settings dialog": ", opens captions settings dialog", - ", opens subtitles settings dialog": ", opens subtitles settings dialog", - ", opens descriptions settings dialog": ", opens descriptions settings dialog", - ", selected": ", selected", - "captions settings": "captions settings", - "subtitles settings": "subtitles settings", - "descriptions settings": "descriptions settings", - Text: Text, - White: White, - Black: Black, - Red: Red, - Green: Green, - Blue: Blue, - Yellow: Yellow, - Magenta: Magenta, - Cyan: Cyan, - Background: Background, - Window: Window, - Transparent: Transparent, - "Semi-Transparent": "Semi-Transparent", - Opaque: Opaque, - "Font Size": "Font Size", - "Text Edge Style": "Text Edge Style", - None: None, - Raised: Raised, - Depressed: Depressed, - Uniform: Uniform, - "Drop shadow": "Drop shadow", - "Font Family": "Font Family", - "Proportional Sans-Serif": "Proportional Sans-Serif", - "Monospace Sans-Serif": "Monospace Sans-Serif", - "Proportional Serif": "Proportional Serif", - "Monospace Serif": "Monospace Serif", - Casual: Casual, - Script: Script, - "Small Caps": "Small Caps", - Reset: Reset, - "restore all settings to the default values": "restore all settings to the default values", - Done: Done, - "Caption Settings Dialog": "Caption Settings Dialog", - "Beginning of dialog window. Escape will cancel and close the window.": "Beginning of dialog window. Escape will cancel and close the window.", - "End of dialog window.": "End of dialog window.", - "{1} is loading.": "{1} is loading.", - "Exit Picture-in-Picture": "Exit Picture-in-Picture", - "Picture-in-Picture": "Picture-in-Picture", - "No content": "No content", - Color: Color, - Opacity: Opacity, - "Text Background": "Text Background", - "Caption Area Background": "Caption Area Background", - "Playing in Picture-in-Picture": "Playing in Picture-in-Picture", - "Skip backward {1} seconds": "Skip backward {1} seconds", - "Skip forward {1} seconds": "Skip forward {1} seconds" -}; - -function ownKeys$3(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } -function _objectSpread$3(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$3(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$3(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } -var PLAYER_ID = "iiif-media-player"; -var MediaPlayer = function MediaPlayer(_ref) { - var _ref$enableFileDownlo = _ref.enableFileDownload, - enableFileDownload = _ref$enableFileDownlo === void 0 ? false : _ref$enableFileDownlo, - _ref$enablePIP = _ref.enablePIP, - enablePIP = _ref$enablePIP === void 0 ? false : _ref$enablePIP, - _ref$enablePlaybackRa = _ref.enablePlaybackRate, - enablePlaybackRate = _ref$enablePlaybackRa === void 0 ? false : _ref$enablePlaybackRa, - _ref$enableTitleLink = _ref.enableTitleLink, - enableTitleLink = _ref$enableTitleLink === void 0 ? false : _ref$enableTitleLink, - _ref$withCredentials = _ref.withCredentials, - withCredentials = _ref$withCredentials === void 0 ? false : _ref$withCredentials, - _ref$language = _ref.language, - language = _ref$language === void 0 ? 'en' : _ref$language; - var manifestState = useManifestState(); - var playerState = usePlayerState(); - var playerDispatch = usePlayerDispatch(); - var manifestDispatch = useManifestDispatch(); - var _useErrorBoundary = reactErrorBoundary.useErrorBoundary(), - showBoundary = _useErrorBoundary.showBoundary; - var _React$useState = React__default["default"].useState({ - error: '', - sources: [], - tracks: [], - poster: null - }), - _React$useState2 = _slicedToArray(_React$useState, 2), - playerConfig = _React$useState2[0], - setPlayerConfig = _React$useState2[1]; - var _React$useState3 = React__default["default"].useState(true), - _React$useState4 = _slicedToArray(_React$useState3, 2), - firstLoad = _React$useState4[0], - setFirstLoad = _React$useState4[1]; - var _React$useState5 = React__default["default"].useState(false), - _React$useState6 = _slicedToArray(_React$useState5, 2), - ready = _React$useState6[0], - setReady = _React$useState6[1]; - var _React$useState7 = React__default["default"].useState(canvasIndex), - _React$useState8 = _slicedToArray(_React$useState7, 2), - cIndex = _React$useState8[0], - setCIndex = _React$useState8[1]; - var _React$useState9 = React__default["default"].useState(), - _React$useState10 = _slicedToArray(_React$useState9, 2), - isMultiSourced = _React$useState10[0], - setIsMultiSourced = _React$useState10[1]; - var _React$useState11 = React__default["default"].useState(false), - _React$useState12 = _slicedToArray(_React$useState11, 2), - isMultiCanvased = _React$useState12[0], - setIsMultiCanvased = _React$useState12[1]; - var _React$useState13 = React__default["default"].useState(0), - _React$useState14 = _slicedToArray(_React$useState13, 2), - lastCanvasIndex = _React$useState14[0], - setLastCanvasIndex = _React$useState14[1]; - var _React$useState15 = React__default["default"].useState(), - _React$useState16 = _slicedToArray(_React$useState15, 2), - isVideo = _React$useState16[0], - setIsVideo = _React$useState16[1]; - var _React$useState17 = React__default["default"].useState(), - _React$useState18 = _slicedToArray(_React$useState17, 2), - options = _React$useState18[0], - setOptions = _React$useState18[1]; - var _React$useState19 = React__default["default"].useState(), - _React$useState20 = _slicedToArray(_React$useState19, 2), - renderingFiles = _React$useState20[0], - setRenderingFiles = _React$useState20[1]; - var canvasIndex = manifestState.canvasIndex, - allCanvases = manifestState.allCanvases, - manifest = manifestState.manifest, - canvasIsEmpty = manifestState.canvasIsEmpty, - srcIndex = manifestState.srcIndex, - targets = manifestState.targets, - playlist = manifestState.playlist, - autoAdvance = manifestState.autoAdvance, - hasStructure = manifestState.hasStructure, - customStart = manifestState.customStart, - renderings = manifestState.renderings; - var playerFocusElement = playerState.playerFocusElement, - currentTime = playerState.currentTime; - var currentTimeRef = React__default["default"].useRef(); - currentTimeRef.current = currentTime; - var canvasIndexRef = React__default["default"].useRef(); - canvasIndexRef.current = canvasIndex; - var autoAdvanceRef = React__default["default"].useRef(); - autoAdvanceRef.current = autoAdvance; - var lastCanvasIndexRef = React__default["default"].useRef(); - lastCanvasIndexRef.current = lastCanvasIndex; - var trackScrubberRef = React__default["default"].useRef(); - var timeToolRef = React__default["default"].useRef(); - var videoJSLangMap = React__default["default"].useRef('{}'); - var canvasMessageTimerRef = React__default["default"].useRef(null); - - // FIXME:: Dynamic language imports break with rollup configuration when packaging - // Using dynamic imports to enforce code-splitting in webpack - // https://webpack.js.org/api/module-methods/#dynamic-expressions-in-import - var loadVideoJSLanguageMap = React__default["default"].useMemo(function () { - return /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee() { - var resources; - return regenerator.wrap(function _callee$(_context) { - while (1) switch (_context.prev = _context.next) { + var setStateVar = /*#__PURE__*/function () { + var _ref8 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee2(transcript) { + var _transcript, id, title, filename, url, isMachineGen, format, cached, _cached$, tData, tFileExt, tType, tError; + return regenerator.wrap(function _callee2$(_context2) { + while (1) switch (_context2.prev = _context2.next) { case 0: - _context.prev = 0; - _context.next = 3; - return (function (t) { return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(t)); }); })("video.js/dist/lang/".concat(language, ".json")); - case 3: - resources = _context.sent; - videoJSLangMap.current = JSON.stringify(resources); - _context.next = 11; + if (!(!transcript || transcript == undefined)) { + _context2.next = 5; + break; + } + setIsEmpty(true); + setIsLoading(false); + setTranscriptInfo({ + tType: TRANSCRIPT_TYPES.noTranscript, + id: '', + tError: NO_TRANSCRIPTS_MSG + }); + return _context2.abrupt("return"); + case 5: + // set isEmpty flag to render transcripts UI + setIsEmpty(false); + _transcript = transcript, id = _transcript.id, title = _transcript.title, filename = _transcript.filename, url = _transcript.url, isMachineGen = _transcript.isMachineGen, format = _transcript.format; // Check cached transcript data + cached = cachedTranscripts.filter(function (ct) { + return ct.id == id && ct.canvasId == canvasIndexRef.current; + }); + if (!((cached === null || cached === void 0 ? void 0 : cached.length) > 0)) { + _context2.next = 15; + break; + } + // Load cached transcript data into the component + _cached$ = cached[0], tData = _cached$.tData, tFileExt = _cached$.tFileExt, tType = _cached$.tType, tError = _cached$.tError; + setTranscript(tData); + setTranscriptInfo({ + title: title, + filename: filename, + id: id, + isMachineGen: isMachineGen, + tType: tType, + tUrl: url, + tFileExt: tFileExt, + tError: tError + }); + setSelectedTranscript(url); + _context2.next = 17; break; - case 7: - _context.prev = 7; - _context.t0 = _context["catch"](0); - console.warn("".concat(language, " is not available, defaulting to English")); - videoJSLangMap.current = JSON.stringify(en); - case 11: + case 15: + _context2.next = 17; + return Promise.resolve(parseTranscriptData(url, canvasIndexRef.current, format)).then(function (value) { + if (value != null) { + var _tData = value.tData, + tUrl = value.tUrl, + _tType = value.tType, + _tFileExt = value.tFileExt; + var newError = ''; + switch (_tType) { + case TRANSCRIPT_TYPES.invalid: + newError = INVALID_URL_MSG; + break; + case TRANSCRIPT_TYPES.noTranscript: + newError = NO_TRANSCRIPTS_MSG; + break; + case TRANSCRIPT_TYPES.noSupport: + newError = NO_SUPPORT_MSG; + break; + case TRANSCRIPT_TYPES.invalidVTT: + newError = INVALID_VTT; + break; + case TRANSCRIPT_TYPES.invalidTimestamp: + newError = INVALID_TIMESTAMP; + break; + } + setTranscript(_tData); + setTranscriptInfo({ + title: title, + filename: filename, + id: id, + isMachineGen: isMachineGen, + tType: _tType, + tUrl: tUrl, + tFileExt: _tFileExt, + tError: newError + }); + setSelectedTranscript(tUrl); + transcript = _objectSpread$5(_objectSpread$5({}, transcript), {}, { + tType: _tType, + tData: _tData, + tFileExt: _tFileExt, + canvasId: canvasIndexRef.current, + tError: newError + }); + // Cache the transcript info + setCachedTranscripts([].concat(_toConsumableArray(cachedTranscripts), [transcript])); + } + }); + case 17: + setIsLoading(false); + case 18: case "end": - return _context.stop(); + return _context2.stop(); + } + }, _callee2); + })); + return function setStateVar(_x2) { + return _ref8.apply(this, arguments); + }; + }(); + var selectTranscript = React.useCallback(function (selectedId) { + var selectedTranscript = canvasTranscripts.filter(function (tr) { + return tr.id === selectedId; + }); + setStateVar(selectedTranscript[0]); + }, [canvasTranscripts]); + return { + canvasIndexRef: canvasIndexRef, + canvasTranscripts: canvasTranscripts, + isEmpty: isEmpty, + isLoading: isLoading, + NO_SUPPORT_MSG: NO_SUPPORT_MSG, + playerRef: playerRef, + selectedTranscript: selectedTranscript, + selectTranscript: selectTranscript, + transcript: transcript, + transcriptInfo: transcriptInfo + }; +}; + +var classCallCheck = createCommonjsModule(function (module) { +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +} +module.exports = _classCallCheck, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var _classCallCheck = /*@__PURE__*/getDefaultExportFromCjs(classCallCheck); + +var createClass = createCommonjsModule(function (module) { +function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, toPropertyKey(descriptor.key), descriptor); + } +} +function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + Object.defineProperty(Constructor, "prototype", { + writable: false + }); + return Constructor; +} +module.exports = _createClass, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var _createClass = /*@__PURE__*/getDefaultExportFromCjs(createClass); + +var assertThisInitialized = createCommonjsModule(function (module) { +function _assertThisInitialized(self) { + if (self === void 0) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + return self; +} +module.exports = _assertThisInitialized, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var _assertThisInitialized = /*@__PURE__*/getDefaultExportFromCjs(assertThisInitialized); + +var getPrototypeOf = createCommonjsModule(function (module) { +function _getPrototypeOf(o) { + module.exports = _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { + return o.__proto__ || Object.getPrototypeOf(o); + }, module.exports.__esModule = true, module.exports["default"] = module.exports; + return _getPrototypeOf(o); +} +module.exports = _getPrototypeOf, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var _getPrototypeOf = /*@__PURE__*/getDefaultExportFromCjs(getPrototypeOf); + +var superPropBase = createCommonjsModule(function (module) { +function _superPropBase(object, property) { + while (!Object.prototype.hasOwnProperty.call(object, property)) { + object = getPrototypeOf(object); + if (object === null) break; + } + return object; +} +module.exports = _superPropBase, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var get = createCommonjsModule(function (module) { +function _get() { + if (typeof Reflect !== "undefined" && Reflect.get) { + module.exports = _get = Reflect.get.bind(), module.exports.__esModule = true, module.exports["default"] = module.exports; + } else { + module.exports = _get = function _get(target, property, receiver) { + var base = superPropBase(target, property); + if (!base) return; + var desc = Object.getOwnPropertyDescriptor(base, property); + if (desc.get) { + return desc.get.call(arguments.length < 3 ? target : receiver); + } + return desc.value; + }, module.exports.__esModule = true, module.exports["default"] = module.exports; + } + return _get.apply(this, arguments); +} +module.exports = _get, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var _get = /*@__PURE__*/getDefaultExportFromCjs(get); + +var setPrototypeOf = createCommonjsModule(function (module) { +function _setPrototypeOf(o, p) { + module.exports = _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { + o.__proto__ = p; + return o; + }, module.exports.__esModule = true, module.exports["default"] = module.exports; + return _setPrototypeOf(o, p); +} +module.exports = _setPrototypeOf, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var inherits = createCommonjsModule(function (module) { +function _inherits(subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function"); + } + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + writable: true, + configurable: true + } + }); + Object.defineProperty(subClass, "prototype", { + writable: false + }); + if (superClass) setPrototypeOf(subClass, superClass); +} +module.exports = _inherits, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var _inherits = /*@__PURE__*/getDefaultExportFromCjs(inherits); + +var possibleConstructorReturn = createCommonjsModule(function (module) { +var _typeof = _typeof_1["default"]; + +function _possibleConstructorReturn(self, call) { + if (call && (_typeof(call) === "object" || typeof call === "function")) { + return call; + } else if (call !== void 0) { + throw new TypeError("Derived constructors may only return object or undefined"); + } + return assertThisInitialized(self); +} +module.exports = _possibleConstructorReturn, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var _possibleConstructorReturn = /*@__PURE__*/getDefaultExportFromCjs(possibleConstructorReturn); + +function _createSuper$6(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$6(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } +function _isNativeReflectConstruct$6() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +var SeekBar = videojs__default["default"].getComponent('SeekBar'); + +/** + * Custom component to show progress of playback built on top of + * Video.js' SeekBar component. This customization allows to display + * multiple-sources in a single Canvas as a contiguous time-block for + * the sum of durations of each source and clipped playlist items with + * blocked ranges. + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} props.options + * @param {Number} props.options.nextItemClicked callback func to switch current source + * when displaying multiple sources in a single instance + */ +var VideoJSProgress = /*#__PURE__*/function (_SeekBar) { + _inherits(VideoJSProgress, _SeekBar); + var _super = _createSuper$6(VideoJSProgress); + function VideoJSProgress(player, options) { + var _this; + _classCallCheck(this, VideoJSProgress); + _this = _super.call(this, player, options); + /** + * Set start values for progress bar + * @param {Number} start canvas start time + */ + _defineProperty(_assertThisInitialized(_this), "initializeProgress", function (start) { + _this.setProgress(start); + _this.setInitTime(start); + _this.player.currentTime(start); + }); + _this.addClass('vjs-custom-progress-bar'); + _this.setAttribute('data-testid', 'videojs-custom-progressbar'); + _this.setAttribute('tabindex', 0); + _this.player = player; + _this.options = options; + _this.selectSource = _this.options.nextItemClicked; + _this.playerEventListener; + _this.initTimeRef = /*#__PURE__*/React.createRef(); + _this.progressRef = /*#__PURE__*/React.createRef(); + _this.canvasTargetsRef = /*#__PURE__*/React.createRef(); + _this.srcIndexRef = /*#__PURE__*/React.createRef(); + _this.isMultiSourceRef = /*#__PURE__*/React.createRef(); + _this.currentTimeRef = /*#__PURE__*/React.createRef(); + _this.pointerDragged = false; + _this.totalDuration; + + // Retreive child elements in SeekBar to use for custom updates + _this.playProgress = _this.getChild('PlayProgressBar'); + _this.loadProgress = _this.getChild('LoadProgressBar'); + _this.player.on('ready', function () { + _this.initializeEl(); + _this.updateComponent(); + }); + _this.player.on('loadstart', function () { + _this.updateComponent(); + _this.buildProgressBar(); + }); + _this.player.on('loadeddata', function () { + _this.setInitTime(_this.player.currentTime()); + }); + + // Update our progress bar after the user leaves full screen + _this.player.on('fullscreenchange', function (e) { + if (!_this.player.isFullscreen()) { + _this.setProgress(_this.player.currentTime()); + } + }); + + // Clear interval upon player disposal + _this.player.on('dispose', function () { + clearInterval(_this.playerEventListener); + }); + return _this; + } + _createClass(VideoJSProgress, [{ + key: "setInitTime", + value: function setInitTime(t) { + this.initTimeRef.current = t; + } + }, { + key: "setSrcIndex", + value: function setSrcIndex(i) { + this.srcIndexRef.current = i; + } + }, { + key: "setProgress", + value: function setProgress(p) { + this.progressRef.current = p; + } + }, { + key: "setCanvasTargets", + value: function setCanvasTargets(t) { + this.canvasTargetsRef.current = t; + this.totalDuration = t.reduce(function (acc, c) { + return acc + c.duration; + }, 0); + } + }, { + key: "setIsMultiSource", + value: function setIsMultiSource(m) { + this.isMultiSourceRef.current = m; + } + }, { + key: "setCurrentTime", + value: function setCurrentTime(t) { + this.currentTimeRef.current = t; + } + }, { + key: "updateComponent", + value: + // Update component's variables on Canvas changes + function updateComponent() { + var _this2 = this; + var _this$player = this.player, + srcIndex = _this$player.srcIndex, + targets = _this$player.targets; + this.setSrcIndex(srcIndex); + this.setCanvasTargets(targets); + var cTimes = targets[srcIndex]; + if (cTimes.customStart > cTimes.start) { + this.initializeProgress(cTimes.customStart); + } else { + this.initializeProgress(cTimes.start); + } + this.setIsMultiSource((targets === null || targets === void 0 ? void 0 : targets.length) > 1 ? true : false); + if (!this.playerEventListener) { + /** + * Using a time interval instead of 'timeupdate event in VideoJS, because Safari + * and other browsers in MacOS stops firing the 'timeupdate' event consistently + * after a while + */ + this.playerEventListener = setInterval(function () { + /** + * Abortable inerval for Safari desktop browsers, for a smoother scrubbing + * experience. + * Mobile devices are excluded since they use native iOS player. + */ + if (IS_SAFARI && !IS_IPHONE) { + _this2.abortableTimeupdateHandler(); + } else { + _this2.timeUpdateHandler(); + } + }, 100); + } + } + + /** + * Use Video.js' update function to update time in mobile devices + * when changing Canvases. + * TODO:: this can probably removed by customizing PlayProgressBar and + * LoadProgressBar components? + */ + }, { + key: "update", + value: function update() { + // Need this to make the other updates work + _get(_getPrototypeOf(VideoJSProgress.prototype), "update", this).call(this); + // Explicitly update played range variable on reload for touch devices + if (IS_TOUCH_ONLY && this.player.currentTime() === 0) { + this.removeClass('played-range'); + document.documentElement.style.setProperty('--range-progress', "calc(".concat(0, "%)")); + } + if (IS_MOBILE && IS_SAFARI && this.player.paused()) { + var _this$player$structSt; + var structStart = (_this$player$structSt = this.player.structStart) !== null && _this$player$structSt !== void 0 ? _this$player$structSt : 0; + if (structStart != 0 && this.player.currentTime() === 0) { + this.player.currentTime(structStart); + var played = Math.min(100, Math.max(0, 100 * (structStart / this.totalDuration))); + this.addClass('played-range'); + document.documentElement.style.setProperty('--range-progress', "calc(".concat(played, "%)")); + this.player.structStart = 0; + } + } else { + return; + } + } + }, { + key: "initializeEl", + value: + // Create progress bar using Video.js' SeekBar component + function initializeEl() { + var _this3 = this; + /** + * Build and append placeholder elements to show blocked ranges, + * especially used in playlist context to present clipped items. + */ + var leftBlock = videojs__default["default"].dom.createEl('div', { + className: 'block-stripes', + role: 'presentation', + id: 'left-block' + }); + var rightBlock = videojs__default["default"].dom.createEl('div', { + className: 'block-stripes', + role: 'presentation', + id: 'right-block' + }); + this.el().appendChild(leftBlock); + this.el().appendChild(rightBlock); + + /** + * Add eventlisteners to handle time tool-tip display and progress updates. + * Using pointerup, pointermove, pointerdown events instead of mouseup, + * mousemove, mousedown events to make it work with both mouse pointer + * and touch events. + */ + this.el().addEventListener('mouseenter', function (e) { + _this3.handleMouseMove(e); + }); + this.el().addEventListener('pointerup', function (e) { + if (_this3.pointerDragged) { + _this3.handleMouseUp(e); + } + }); + this.el().addEventListener('pointermove', function (e) { + _this3.handleMouseMove(e); + _this3.pointerDragged = true; + }); + this.el().addEventListener('pointerdown', function (e) { + _this3.handleMouseDown(e); + _this3.pointerDragged = false; + }); + } + }, { + key: "handleMouseMove", + value: function handleMouseMove(e) { + var _this$convertToTime = this.convertToTime(e), + currentTime = _this$convertToTime.currentTime, + offsetx = _this$convertToTime.offsetx; + if (currentTime != undefined) this.setCurrentTime(currentTime); + var mouseTimeDisplay = this.getChild('MouseTimeDisplay'); + if (mouseTimeDisplay) { + var timeTooltip = mouseTimeDisplay.getChild('TimeTooltip'); + var toolTipEl = timeTooltip.el_; + if (currentTime) { + toolTipEl.innerHTML = timeToHHmmss(currentTime); } - }, _callee, null, [[0, 7]]); - })); - }, [language]); - React__default["default"].useEffect(function () { - if (manifest) { - try { - loadVideoJSLanguageMap(); - /* - Always start from the start time relevant to the Canvas only in playlist contexts, - because canvases related to playlist items always start from the given start. - With regular manifests, the start time could be different when using structured - navigation to switch between canvases. - */ - if (canvasIndex == undefined || canvasIndex < 0) { - throw new Error('Invalid canvas index. Please check your Manifest.'); + var pullTooltip = toolTipEl.clientWidth / 2; + toolTipEl.style.left = "".concat(offsetx - pullTooltip, "px"); + } + } + }, { + key: "handleMouseDown", + value: function handleMouseDown(e) { + // Do nothing when right-click is pressed + if (!IS_TOUCH_ONLY && e.buttons === 2) return; + var _this$convertToTime2 = this.convertToTime(e), + currentTime = _this$convertToTime2.currentTime; + _this$convertToTime2._; + var clickedSrc; + if (this.isMultiSourceRef.current) { + clickedSrc = this.canvasTargetsRef.current.find(function (t) { + var virtualEnd = t.altStart + t.duration; + if (currentTime >= t.altStart && currentTime <= virtualEnd) { + return t; + } + }); + } + if (clickedSrc) { + var _clickedSrc$sIndex, _clickedSrc; + var clickedIndex = (_clickedSrc$sIndex = (_clickedSrc = clickedSrc) === null || _clickedSrc === void 0 ? void 0 : _clickedSrc.sIndex) !== null && _clickedSrc$sIndex !== void 0 ? _clickedSrc$sIndex : 0; + if (clickedIndex != this.srcIndexRef.current) { + this.selectSource(clickedSrc.sIndex, currentTime - clickedSrc.altStart); + this.setSrcIndex(clickedIndex); + } else { + this.player.currentTime(currentTime - clickedSrc.altStart); } - initCanvas(canvasIndex, playlist.isPlaylist); + } else { + this.player.currentTime(currentTime); + } - // Deduct 1 from length to compare against canvasIndex, which starts from 0 - var lastIndex = (allCanvases === null || allCanvases === void 0 ? void 0 : allCanvases.length) - 1; - setIsMultiCanvased(lastIndex > 0); - setLastCanvasIndex(lastIndex || 0); - } catch (e) { - showBoundary(e); + /** + * For touch devices, player.currentTime() update doesn't show the + * played range, even though the player's currentTime is properly set. + * Therefore, update the CSS here explicitly. + */ + if (IS_TOUCH_ONLY) { + var played = Math.min(100, Math.max(0, 100 * (currentTime / this.totalDuration))); + this.player.currentTime(currentTime); + this.addClass('played-range'); + document.documentElement.style.setProperty('--range-progress', "calc(".concat(played, "%)")); } } - return function () { - setReady(false); - setCIndex(0); - playerDispatch({ - player: null, - type: 'updatePlayer' - }); - }; - }, [manifest, canvasIndex, srcIndex]); + }, { + key: "handleMouseUp", + value: function handleMouseUp(e) { + this.handleMouseDown(e); + } + }, { + key: "buildProgressBar", + value: function buildProgressBar() { + var _canvasTargetsRef$cur; + // Reset progress-bar for played range on player reload + this.removeClass('played-range'); + var canvasTargetsRef = this.canvasTargetsRef, + isMultiSourceRef = this.isMultiSourceRef, + player = this.player, + srcIndexRef = this.srcIndexRef, + totalDuration = this.totalDuration; + if (((_canvasTargetsRef$cur = canvasTargetsRef.current) === null || _canvasTargetsRef$cur === void 0 ? void 0 : _canvasTargetsRef$cur.length) > 0) { + var _canvasTargetsRef$cur2 = canvasTargetsRef.current[srcIndexRef.current], + altStart = _canvasTargetsRef$cur2.altStart, + start = _canvasTargetsRef$cur2.start, + end = _canvasTargetsRef$cur2.end, + duration = _canvasTargetsRef$cur2.duration; + var leftBlockEl = document.getElementById('left-block'); + var rightBlockEl = document.getElementById('right-block'); + if (!isMultiSourceRef.current) { + var leftBlock = start * 100 / duration; + var rightBlock = (duration - end) * 100 / duration; - /** - * Handle the display timer for the inaccessbile message when autoplay is turned - * on/off while the current item is a restricted item - */ - React__default["default"].useEffect(function () { - if (canvasIsEmpty) { - // Clear the existing timer when the autoplay is turned off when displaying - // inaccessible message - if (!autoAdvance && canvasMessageTimerRef.current) { - clearCanvasMessageTimer(); - } else { - // Create a timer to advance to the next Canvas when autoplay is turned - // on when inaccessible message is been displayed - createCanvasMessageTimer(); + // player.isClipped is used in VideoJSTrackScrbber to display accurate + // times for clipped items + rightBlock > 0 ? player.isClipped = true : player.isClipped = false; + if (leftBlockEl) leftBlockEl.style.width = "".concat(leftBlock, "%"); + if (rightBlockEl) { + rightBlockEl.style.width = rightBlock + '%'; + rightBlockEl.style.left = "".concat(100 - rightBlock - leftBlock, "%"); + } + } else { + // Offset of the duration of the current source for multi-source canvases + var leftOffset = Math.min(100, Math.max(0, 100 * (altStart / totalDuration))); + this.playProgress.el_.style.left = "".concat(leftOffset, "%"); + this.loadProgress.el_.style.left = "".concat(leftOffset, "%"); + // Add CSS class to mark the range from zero as played + this.addClass('played-range'); + document.documentElement.style.setProperty('--range-progress', "calc(".concat(leftOffset, "%)")); + } } } - }, [autoAdvanceRef.current]); - - /** - * Initialize the next Canvas to be viewed in the player instance - * @param {Number} canvasId index of the Canvas to be loaded into the player - * @param {Boolean} fromStart flag to indicate how to start new player instance - */ - var initCanvas = function initCanvas(canvasId, fromStart) { - clearCanvasMessageTimer(); - try { - var _getMediaInfo = getMediaInfo({ - manifest: manifest, - canvasIndex: canvasId, - startTime: canvasId === customStart.startIndex && firstLoad ? customStart.startTime : 0, - srcIndex: srcIndex, - isPlaylist: playlist.isPlaylist - }), - isMultiSource = _getMediaInfo.isMultiSource, - sources = _getMediaInfo.sources, - tracks = _getMediaInfo.tracks, - canvasTargets = _getMediaInfo.canvasTargets, - mediaType = _getMediaInfo.mediaType, - error = _getMediaInfo.error, - poster = _getMediaInfo.poster; - setIsVideo(mediaType === 'video'); - manifestDispatch({ - canvasTargets: canvasTargets, - type: 'canvasTargets' - }); - manifestDispatch({ - isMultiSource: isMultiSource, - type: 'hasMultipleItems' - }); - // Set the current time in player from the canvas details - if (fromStart) { - if ((canvasTargets === null || canvasTargets === void 0 ? void 0 : canvasTargets.length) > 0) { - playerDispatch({ - currentTime: canvasTargets[0].altStart, - type: 'setCurrentTime' - }); + /** + * Convert mouse event's offset to timepoint value in the progressbar, + * taking into account blocked ranges, and multi-source canvases. + * @param {Event} e mouse event + * @returns {currentTime: Number, offsetx: Number} + */ + }, { + key: "convertToTime", + value: function convertToTime(e) { + var _e$nativeEvent$target, _this$totalDuration; + var eSrcElement = e.srcElement; + // When clicked on blocked time point + if (eSrcElement.classList.contains('block-stripes')) { + var _this$canvasTargetsRe = this.canvasTargetsRef.current[0], + altStart = _this$canvasTargetsRe.altStart, + end = _this$canvasTargetsRe.end, + _duration = _this$canvasTargetsRe.duration; + if (eSrcElement.id === 'right-block') { + // For right-block: place time tool-tip at the end of playable range + return { + currentTime: end, + offsetx: end / _duration * this.el().clientWidth + }; } else { - playerDispatch({ - currentTime: 0, - type: 'setCurrentTime' - }); + // For left-block: place time tool-tip at the start of playable range + return { + currentTime: altStart, + offsetx: altStart / _duration * this.el().clientWidth + }; } } - setPlayerConfig(_objectSpread$3(_objectSpread$3({}, playerConfig), {}, { - error: error, - sources: sources, - tracks: tracks, - poster: poster - })); - var currentCanvas = allCanvases.find(function (c) { - return c.canvasIndex === canvasId; - }); - if (!currentCanvas.isEmpty) { - // Manifest is taken from manifest state, and is a basic object at this point - // lacking the getLabel() function so we manually retrieve the first label. - var manifestLabel = manifest.label ? Object.values(manifest.label)[0][0] : ''; - // Filter out falsy items in case canvas.label is null or an empty string - var titleText = [manifestLabel, currentCanvas.label].filter(Boolean).join(' - '); - manifestDispatch({ - canvasDuration: currentCanvas.duration, - type: 'canvasDuration' - }); - manifestDispatch({ - canvasLink: { - label: titleText, - id: currentCanvas.canvasId - }, - type: 'canvasLink' - }); - manifestDispatch({ - type: 'setCanvasIsEmpty', - isEmpty: false - }); - } else { - playerDispatch({ - type: 'updatePlayer' - }); - manifestDispatch({ - type: 'setCanvasIsEmpty', - isEmpty: true - }); - // Set poster as playerConfig.error to be used for empty Canvas message in VideoJSPlayer - setPlayerConfig(_objectSpread$3(_objectSpread$3({}, playerConfig), {}, { - error: poster - })); - // Create timer to display the message when autoadvance is ON - if (autoAdvanceRef.current) { - createCanvasMessageTimer(); + var targetX = e.target.getBoundingClientRect().x; + var offsetx = e.nativeEvent != undefined ? e.nativeEvent.offsetX != undefined ? e.nativeEvent.offsetX // iOS and desktop events + : ((_e$nativeEvent$target = e.nativeEvent.targetTouches[0]) === null || _e$nativeEvent$target === void 0 ? void 0 : _e$nativeEvent$target.clientX) - targetX // Android event + : e.offsetX; // fallback in desktop browsers when nativeEvent is undefined + var currentTime; + var duration = (_this$totalDuration = this.totalDuration) !== null && _this$totalDuration !== void 0 ? _this$totalDuration : this.player.duration(); + if (offsetx && offsetx != undefined) { + if (this.isMultiSourceRef.current) { + /** + * Check if the mouse event occurred on the same src range. + * If so, adjust the offset to support altStart for the current src. + */ + var leftOffset = parseFloat(this.playProgress.el_.style.left) / 100 * this.el().clientWidth; + var elClassList = eSrcElement.classList; + var sameSrc = (elClassList === null || elClassList === void 0 ? void 0 : elClassList.length) > 0 ? elClassList.contains('vjs-play-progress') || elClassList.contains('vjs-load-progress') : true; + if (leftOffset > offsetx && sameSrc) { + offsetx = offsetx + leftOffset; + } } + currentTime = offsetx / this.el().clientWidth * duration; } - setIsMultiSourced(isMultiSource || false); - setCIndex(canvasId); - if (enableFileDownload && renderings != {}) { - var _renderings$canvas$ca; - setRenderingFiles(renderings.manifest.concat((_renderings$canvas$ca = renderings.canvas[canvasId]) === null || _renderings$canvas$ca === void 0 ? void 0 : _renderings$canvas$ca.files)); - } - error ? setReady(false) : setReady(true); - } catch (e) { - showBoundary(e); - } - }; - - /** - * Switch src in the player when seeked to a time range within a - * different item in the same canvas - * @param {Number} srcindex new srcIndex - * @param {Number} value current time of the player - */ - var nextItemClicked = function nextItemClicked(srcindex, value) { - playerDispatch({ - currentTime: value, - type: 'setCurrentTime' - }); - manifestDispatch({ - srcIndex: srcindex, - type: 'setSrcIndex' - }); - }; - - /** - * Create timer to display the inaccessible Canvas message - */ - var createCanvasMessageTimer = function createCanvasMessageTimer() { - canvasMessageTimerRef.current = setTimeout(function () { - if (canvasIndexRef.current < lastCanvasIndexRef.current && autoAdvanceRef.current) { - manifestDispatch({ - canvasIndex: canvasIndexRef.current + 1, - type: 'switchCanvas' - }); + /** + * Parts of LoadProgress element is broken into segments as media loads, and displayed + * as separate div elements with `data-start` and `data-end` attributes respectively. + * When mouse event occurs on top of such element, add the segment start time to calculated + * current time from event. + */ + if (e.target.hasAttribute('data-start')) { + var _e$target$dataset = e.target.dataset, + start = _e$target$dataset.start; + _e$target$dataset._; + currentTime = currentTime + parseFloat(start); + offsetx = currentTime * this.el().clientWidth / this.totalDuration; } - }, CANVAS_MESSAGE_TIMEOUT); - }; - - /** - * Clear existing timer to display the inaccessible Canvas message - */ - var clearCanvasMessageTimer = function clearCanvasMessageTimer() { - if (canvasMessageTimerRef.current) { - clearTimeout(canvasMessageTimerRef.current); - canvasMessageTimerRef.current = null; + return { + currentTime: currentTime, + offsetx: offsetx + }; } - }; - - /** - * Switch player when navigating across canvases - * @param {Number} index canvas index to be loaded into the player - * @param {Boolean} fromStart flag to indicate set player start time to zero or not - * @param {String} focusElement element to be focused within the player when using - * next or previous buttons with keyboard - */ - var switchPlayer = function switchPlayer(index, fromStart) { - var focusElement = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; - if (index != undefined && index > -1 && canvasIndexRef.current != index && index <= lastCanvasIndexRef.current) { - manifestDispatch({ - canvasIndex: index, - type: 'switchCanvas' - }); - initCanvas(index, fromStart); - playerDispatch({ - element: focusElement, - type: 'setPlayerFocusElement' + }, { + key: "abortableTimeupdateHandler", + value: + /** + * A wrapper function around the time update interval, to cancel + * intermediate updates via the time interval when player is + * waiting to fetch stream + */ + function abortableTimeupdateHandler() { + var _this4 = this; + var player = this.player, + progressRef = this.progressRef; + player.on('waiting', function () { + if (IS_SAFARI && !IS_MOBILE) { + player.currentTime(progressRef.current); + } + cancelInterval(); }); + var cancelInterval = function cancelInterval() { + if (internalInterval) { + clearInterval(internalInterval); + } + }; + var internalInterval = setInterval(function () { + _this4.timeUpdateHandler(); + }, 100); } - }; - React__default["default"].useEffect(function () { - var hlsOptions = withCredentials ? { - hls: { - withCredentials: true - } - } : {}; - var videoJsOptions; - // Only build the full set of option for the first playable Canvas since - // these options are only used on the initia Video.js instance creation - if (firstLoad && ready && !canvasIsEmpty) { - // Configuration options for Video.js instantiation - videoJsOptions = !canvasIsEmpty ? { - aspectRatio: isVideo ? '16:9' : '1:0', - audioOnlyMode: !isVideo, - autoplay: false, - bigPlayButton: isVideo, - id: PLAYER_ID, - playbackRates: enablePlaybackRate ? [0.5, 0.75, 1, 1.5, 2] : [], - experimentalSvgIcons: true, - // Setting inactivity timeout to zero in mobile and tablet devices translates to - // user is always active. And the control bar is not hidden when user is active. - // With this user can always use the controls when the media is playing. - inactivityTimeout: IS_MOBILE || IS_TOUCH_ONLY ? 0 : 2000, - poster: isVideo ? playerConfig.poster : null, - controls: true, - fluid: true, - language: language, - controlBar: { - // Define and order control bar controls - // See https://docs.videojs.com/tutorial-components.html for options of what - // seem to be supported controls - children: [isMultiCanvased ? 'videoJSPreviousButton' : '', 'playToggle', isMultiCanvased ? 'videoJSNextButton' : '', 'videoJSProgress', 'videoJSCurrentTime', 'timeDivider', 'durationDisplay', - // These icons are in reverse order to support `float: inline-end` in CSS - 'fullscreenToggle', enableFileDownload ? 'videoJSFileDownload' : '', enablePIP ? 'pictureInPictureToggle' : '', enablePlaybackRate ? 'playbackRateMenuButton' : '', 'qualitySelector', hasStructure || playlist.isPlaylist ? 'videoJSTrackScrubber' : '', playerConfig.tracks.length > 0 && isVideo ? 'subsCapsButton' : '', IS_MOBILE ? 'muteToggle' : 'volumePanel' - // 'vjsYo', custom component - ], - - videoJSProgress: { - srcIndex: srcIndex, - targets: targets, - currentTime: currentTime || 0, - nextItemClicked: nextItemClicked - }, - videoJSCurrentTime: { - srcIndex: srcIndex, - targets: targets, - currentTime: currentTime || 0 - } - }, - sources: isMultiSourced ? [playerConfig.sources[srcIndex]] : playerConfig.sources, - // Enable native text track functionality in iPhones and iPads - html5: _objectSpread$3(_objectSpread$3({}, hlsOptions), {}, { - nativeTextTracks: IS_MOBILE && !IS_ANDROID - }), - // Make error display modal dismissable - errorDisplay: { - uncloseable: false - }, - /* - Setting this option helps to override VideoJS's default 'keydown' event handler, whenever - the focus is on a native VideoJS control icon (e.g. play toggle). - E.g. click event on 'playtoggle' sets the focus on the play/pause button, - which has VideoJS's 'handleKeydown' event handler attached to it. Therefore, as long as the - focus is on the play/pause button the 'keydown' event will pass through VideoJS's default - 'keydown' event handler, without ever reaching the 'keydown' handler setup on the document - in Ramp code. - When this option is setup VideoJS's 'handleKeydown' event handler passes the event to the - function setup under the 'hotkeys' option when the native player controls are focused. - In Safari, this works without using 'hotkeys' option, therefore only set this in other browsers. - */ - userActions: { - hotkeys: !IS_SAFARI ? function (e) { - playerHotKeys(e, this); - } : undefined - }, - videoJSTitleLink: enableTitleLink - } : { - sources: [] - }; // Empty configurations for empty canvases - - // Add file download to toolbar when it is enabled via props - if (enableFileDownload && !canvasIsEmpty) { - videoJsOptions.controlBar.videoJSFileDownload = { - title: 'Download Files', - controlText: 'Alternate resource download', - files: renderingFiles - }; + }, { + key: "timeUpdateHandler", + value: + // Update progress bar with timeupdate in the player + function timeUpdateHandler() { + var _this5 = this; + var initTimeRef = this.initTimeRef, + player = this.player; + if (player.isDisposed() || player.ended() || player == null) { + return; } - if (isMultiCanvased && !canvasIsEmpty) { - videoJsOptions.controlBar.videoJSPreviousButton = { - canvasIndex: canvasIndex, - switchPlayer: switchPlayer, - playerFocusElement: playerFocusElement - }; - videoJsOptions.controlBar.videoJSNextButton = { - canvasIndex: canvasIndex, - lastCanvasIndex: lastCanvasIndexRef.current, - switchPlayer: switchPlayer, - playerFocusElement: playerFocusElement - }; + var curTime; + // Initially update progress from the prop passed from Ramp, + // this accounts for structured navigation when switching canvases + if (initTimeRef.current > 0 && player.currentTime() == 0) { + curTime = initTimeRef.current; + player.currentTime(initTimeRef.current); + } else { + curTime = player.currentTime(); } - // Iniitialize track scrubber button when the current Canvas has - // structure timespans or the given Manifest is a playlist Manifest - if ((hasStructure || playlist.isPlaylist) && !canvasIsEmpty) { - videoJsOptions.controlBar.videoJSTrackScrubber = { - trackScrubberRef: trackScrubberRef, - timeToolRef: timeToolRef, - isPlaylist: playlist.isPlaylist - }; + // Use debounced updates since, Safari desktop browsers need the extra + // update on 'seeked' event to timely update the progress bar. + if (IS_SAFARI && !IS_MOBILE && player.paused()) { + debounce_1(function () { + _this5.onTimeUpdate(curTime); + }); + } else { + this.onTimeUpdate(curTime); } - setFirstLoad(false); - } else { - videoJsOptions = { - sources: isMultiSourced ? [playerConfig.sources[srcIndex]] : playerConfig.sources, - poster: isVideo ? playerConfig.poster : null - }; + this.setInitTime(0); } - setOptions(videoJsOptions); - }, [ready, cIndex, srcIndex, canvasIsEmpty, currentTime]); - if (ready && options != undefined || canvasIsEmpty) { - return /*#__PURE__*/React__default["default"].createElement("div", { - "data-testid": "media-player", - className: "ramp--media_player", - role: "presentation" - }, /*#__PURE__*/React__default["default"].createElement(VideoJSPlayer, { - isVideo: isVideo, - hasMultipleCanvases: isMultiCanvased, - isPlaylist: playlist.isPlaylist, - trackScrubberRef: trackScrubberRef, - scrubberTooltipRef: timeToolRef, - tracks: playerConfig.tracks, - placeholderText: playerConfig.error, - renderingFiles: renderingFiles, - enableFileDownload: enableFileDownload, - loadPrevOrNext: switchPlayer, - lastCanvasIndex: lastCanvasIndex, - enableTitleLink: enableTitleLink, - videoJSLangMap: videoJSLangMap.current, - options: options - })); - } else { - return null; - } -}; -MediaPlayer.propTypes = { - enableFileDownload: PropTypes.bool, - enablePIP: PropTypes.bool, - enablePlaybackRate: PropTypes.bool, - enableTitleLink: PropTypes.bool, - withCredentials: PropTypes.bool, - language: PropTypes.string -}; - -var _extends_1 = createCommonjsModule(function (module) { -function _extends() { - module.exports = _extends = Object.assign ? Object.assign.bind() : function (target) { - for (var i = 1; i < arguments.length; i++) { - var source = arguments[i]; - for (var key in source) { - if (Object.prototype.hasOwnProperty.call(source, key)) { - target[key] = source[key]; - } + }, { + key: "onTimeUpdate", + value: function onTimeUpdate(curTime) { + // This state update caused weird lagging behaviors when using the iOS native + // video player. iOS player handles its own progress bar, so we can skip the + // update here only for video. + var iOS = this.player.hasClass("vjs-ios-native-fs"); + if (!(iOS && !this.player.audioOnlyMode_)) { + this.setProgress(curTime); } + this.handleTimeUpdate(curTime); } - return target; - }, module.exports.__esModule = true, module.exports["default"] = module.exports; - return _extends.apply(this, arguments); -} -module.exports = _extends, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); + }, { + key: "handleTimeUpdate", + value: + /** + * Update CSS for the input range's track while the media + * is playing + * @param {Number} curTime current time of the player + */ + function handleTimeUpdate(curTime) { + var _srcIndexRef$current; + var player = this.player, + el_ = this.el_, + canvasTargetsRef = this.canvasTargetsRef, + srcIndexRef = this.srcIndexRef; -var _extends = /*@__PURE__*/getDefaultExportFromCjs(_extends_1); + // Avoid null player instance when Video.js is getting initialized + if (!el_ || !player || !canvasTargetsRef.current) { + return; + } + var _canvasTargetsRef$cur3 = canvasTargetsRef.current[(_srcIndexRef$current = srcIndexRef.current) !== null && _srcIndexRef$current !== void 0 ? _srcIndexRef$current : 0], + start = _canvasTargetsRef$cur3.start, + end = _canvasTargetsRef$cur3.end, + duration = _canvasTargetsRef$cur3.duration; -var SectionHeading = function SectionHeading(_ref) { - var duration = _ref.duration, - label = _ref.label, - itemIndex = _ref.itemIndex, - canvasIndex = _ref.canvasIndex, - sectionRef = _ref.sectionRef, - itemId = _ref.itemId, - isRoot = _ref.isRoot, - handleClick = _ref.handleClick, - structureContainerRef = _ref.structureContainerRef; - var itemLabelRef = React__default["default"].useRef(); - itemLabelRef.current = label; + // Restrict access to the intended range in the media file + if (curTime < start) { + player.currentTime(start); + } + if (curTime >= end && !player.paused() && !player.isDisposed()) { + // Trigger ended event when playable range < duration of the + // full media. e.g. clipped playlist items + if (end < duration) { + player.trigger('ended'); + } - /* - Auto-scroll active section into view only when user is not - actively interacting with structured navigation - */ - React__default["default"].useEffect(function () { - if (canvasIndex + 1 === itemIndex && sectionRef.current && sectionRef.current.isClicked != undefined && !sectionRef.current.isClicked && structureContainerRef.current.isScrolling != undefined && !structureContainerRef.current.isScrolling) { - autoScroll(sectionRef.current, structureContainerRef); + // On the next play event set the time to start or a seeked time + // in between the 'ended' event and 'play' event + // Reference: https://github.com/videojs/video.js/blob/main/src/js/control-bar/play-toggle.js#L128 + player.one('play', function () { + var time = player.currentTime(); + if (time < end) { + player.currentTime(time); + } else { + player.currentTime(start); + } + }); + } } - sectionRef.current.isClicked = false; - }, [canvasIndex]); - var sectionClassName = "ramp--structured-nav__section".concat(canvasIndex + 1 === itemIndex ? ' active' : ''); - if (itemId != undefined) { - return /*#__PURE__*/React__default["default"].createElement("div", { - className: sectionClassName, - role: "listitem", - "data-testid": "listitem-section", - ref: sectionRef, - "data-mediafrag": itemId, - "data-label": itemLabelRef.current - }, /*#__PURE__*/React__default["default"].createElement("button", { - "data-testid": "listitem-section-button", - ref: sectionRef, - onClick: handleClick - }, /*#__PURE__*/React__default["default"].createElement("span", { - className: "ramp--structured-nav__title", - "aria-label": itemLabelRef.current - }, "".concat(itemIndex, ". "), itemLabelRef.current, duration != '' && /*#__PURE__*/React__default["default"].createElement("span", { - className: "ramp--structured-nav__section-duration" - }, duration)))); - } else { - return /*#__PURE__*/React__default["default"].createElement("div", { - className: sectionClassName, - "data-testid": "listitem-section", - ref: sectionRef, - "data-label": itemLabelRef.current - }, /*#__PURE__*/React__default["default"].createElement("span", { - className: "ramp--structured-nav__section-title", - role: "listitem", - "data-testid": "listitem-section-span", - "aria-label": itemLabelRef.current - }, isRoot ? '' : "".concat(itemIndex, ". "), itemLabelRef.current, duration != '' && /*#__PURE__*/React__default["default"].createElement("span", { - className: "ramp--structured-nav__section-duration" - }, duration))); - } -}; -SectionHeading.propTypes = { - itemIndex: PropTypes.number.isRequired, - canvasIndex: PropTypes.number, - duration: PropTypes.string.isRequired, - label: PropTypes.string.isRequired, - sectionRef: PropTypes.object.isRequired, - itemId: PropTypes.string, - isRoot: PropTypes.bool, - handleClick: PropTypes.func.isRequired, - structureContainerRef: PropTypes.object.isRequired -}; + }]); + return VideoJSProgress; +}(SeekBar); +videojs__default["default"].registerComponent('VideoJSProgress', VideoJSProgress); -var ListItem = function ListItem(_ref) { - var duration = _ref.duration, - id = _ref.id, - isTitle = _ref.isTitle, - isCanvas = _ref.isCanvas, - isClickable = _ref.isClickable, - isEmpty = _ref.isEmpty, - label = _ref.label, - summary = _ref.summary, - homepage = _ref.homepage, - isRoot = _ref.isRoot, - items = _ref.items, - itemIndex = _ref.itemIndex, - rangeId = _ref.rangeId, - canvasDuration = _ref.canvasDuration, - sectionRef = _ref.sectionRef, - structureContainerRef = _ref.structureContainerRef; - var playerDispatch = usePlayerDispatch(); - var _useManifestState = useManifestState(), - canvasIndex = _useManifestState.canvasIndex, - currentNavItem = _useManifestState.currentNavItem, - playlist = _useManifestState.playlist; - var isPlaylist = playlist.isPlaylist; - var itemIdRef = React__default["default"].useRef(); - itemIdRef.current = id; - var itemLabelRef = React__default["default"].useRef(); - itemLabelRef.current = label; - var itemSummaryRef = React__default["default"].useRef(); - itemSummaryRef.current = summary; - var subMenu = items && items.length > 0 ? /*#__PURE__*/React__default["default"].createElement(List, { - items: items, - sectionRef: sectionRef, - structureContainerRef: structureContainerRef - }) : null; - var liRef = React__default["default"].useRef(null); - var handleClick = React__default["default"].useCallback(function (e) { - e.preventDefault(); - e.stopPropagation(); - var _getMediaFragment = getMediaFragment(itemIdRef.current, canvasDuration), - start = _getMediaFragment.start, - end = _getMediaFragment.end; - var inRange = checkSrcRange({ - start: start, - end: end - }, { - end: canvasDuration +function _createSuper$5(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$5(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } +function _isNativeReflectConstruct$5() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +var TimeDisplay = videojs__default["default"].getComponent('TimeDisplay'); + +/** + * Custom component to display the current time of the player + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} options + * @param {Number} options.currentTime + */ +var VideoJSCurrentTime = /*#__PURE__*/function (_TimeDisplay) { + _inherits(VideoJSCurrentTime, _TimeDisplay); + var _super = _createSuper$5(VideoJSCurrentTime); + function VideoJSCurrentTime(player, options) { + var _this; + _classCallCheck(this, VideoJSCurrentTime); + _this = _super.call(this, player, options); + _this.addClass('vjs-time-control vjs-current-time-display'); + _this.setAttribute('role', 'presentation'); + _this.player = player; + _this.options = options; + _this.initTimeRef = /*#__PURE__*/React.createRef(); + _this.initTimeRef.current = options.currentTime; + _this.playerInterval; + _this.player.on('loadstart', function () { + _this.playerInterval = setInterval(function () { + _this.handleTimeUpdate(); + }, 100); }); - /* - Only continue the click action if not both start and end times of - the timespan are not outside Canvas' duration - */ - if (inRange) { - playerDispatch({ - clickedUrl: itemIdRef.current, - type: 'navClick' - }); - liRef.current.isClicked = true; - if (sectionRef.current) { - sectionRef.current.isClicked = true; + _this.player.on('seeked', function () { + if (IS_SAFARI && !IS_MOBILE) { + _this.updateTextNode_(player.currentTime()); } + }); + + // Update our timer after the user leaves full screen + _this.player.on('fullscreenchange', function () { + if (!player.isFullscreen()) { + _this.updateTextNode_(player.currentTime()); + } + }); + + // Clean interval upon player dispose + _this.player.on('dispose', function () { + clearInterval(_this.playerInterval); + }); + return _this; + } + _createClass(VideoJSCurrentTime, [{ + key: "buildCSSClass", + value: function buildCSSClass() { + return 'current-time'; } - }); - React__default["default"].useEffect(function () { - /* - Auto-scroll active structure item into view only when user is not actively - interacting with structured navigation - */ - if (liRef.current && (currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.id) == itemIdRef.current && liRef.current.isClicked != undefined && !liRef.current.isClicked && structureContainerRef.current.isScrolling != undefined && !structureContainerRef.current.isScrolling) { - autoScroll(liRef.current, structureContainerRef); + }, { + key: "setInitTime", + value: function setInitTime(t) { + this.initTimeRef.current = t; } - // Reset isClicked if active structure item is set - if (liRef.current) { - liRef.current.isClicked = false; + }, { + key: "handleTimeUpdate", + value: function handleTimeUpdate() { + var player = this.player, + initTimeRef = this.initTimeRef; + var targets = player.targets, + srcIndex = player.srcIndex; + if (!player || player.isDisposed() || !targets) { + return; + } + var iOS = player.hasClass('vjs-ios-native-fs'); + var time; + // Update time from the given initial time if it is not zero + if (initTimeRef.current > 0 && player.currentTime() == 0) { + time = initTimeRef.current; + } else { + time = player.currentTime(); + } + var _targets = targets[srcIndex !== null && srcIndex !== void 0 ? srcIndex : 0], + start = _targets.start, + altStart = _targets.altStart; + if (altStart != start && srcIndex > 0) { + time = time + altStart; + } + // This state update caused weird lagging behaviors when using the iOS native + // video player. iOS player handles its own time, so we can skip the update here + // video items. + if (!(iOS && !player.audioOnlyMode_)) { + this.updateTextNode_(time); + } + this.setInitTime(0); } - }, [currentNavItem]); - var renderListItem = function renderListItem() { - return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, { - key: rangeId - }, isCanvas && !isPlaylist ? /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement(SectionHeading, { - itemIndex: itemIndex, - canvasIndex: canvasIndex, - duration: duration, - label: label, - sectionRef: sectionRef, - itemId: itemIdRef.current, - isRoot: isRoot, - handleClick: handleClick, - structureContainerRef: structureContainerRef - })) : /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, isTitle ? /*#__PURE__*/React__default["default"].createElement("span", { - className: "ramp--structured-nav__item-title", - role: "listitem", - "aria-label": itemLabelRef.current - }, itemLabelRef.current) : /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, { - key: id - }, /*#__PURE__*/React__default["default"].createElement("div", { - className: "tracker" - }), isClickable ? /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, isEmpty && /*#__PURE__*/React__default["default"].createElement(LockedSVGIcon, null), /*#__PURE__*/React__default["default"].createElement("a", { - role: "listitem", - href: homepage && homepage != '' ? homepage : itemIdRef.current, - onClick: handleClick - }, "".concat(itemIndex, ". "), itemLabelRef.current, " ", duration.length > 0 ? " (".concat(duration, ")") : '')) : /*#__PURE__*/React__default["default"].createElement("span", { - role: "listitem", - "aria-label": itemLabelRef.current - }, itemLabelRef.current)))); - }; - if (label != '') { - return /*#__PURE__*/React__default["default"].createElement("li", { - "data-testid": "list-item", - ref: liRef, - className: 'ramp--structured-nav__list-item' + "".concat(itemIdRef.current != undefined && (currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.id) === itemIdRef.current && (isPlaylist || !isCanvas) && (currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.canvasIndex) === canvasIndex + 1 ? ' active' : ''), - "data-label": itemLabelRef.current, - "data-summary": itemSummaryRef.current - }, renderListItem(), subMenu); - } else { - return null; - } -}; -ListItem.propTypes = { - duration: PropTypes.string.isRequired, - id: PropTypes.string, - isTitle: PropTypes.bool.isRequired, - isCanvas: PropTypes.bool.isRequired, - isClickable: PropTypes.bool.isRequired, - isEmpty: PropTypes.bool.isRequired, - label: PropTypes.string.isRequired, - summary: PropTypes.string, - homepage: PropTypes.string, - isRoot: PropTypes.bool, - items: PropTypes.array.isRequired, - itemIndex: PropTypes.number, - rangeId: PropTypes.string.isRequired, - canvasDuration: PropTypes.number.isRequired, - sectionRef: PropTypes.object.isRequired, - structureContainerRef: PropTypes.object.isRequired -}; + }]); + return VideoJSCurrentTime; +}(TimeDisplay); +videojs__default["default"].registerComponent('VideoJSCurrentTime', VideoJSCurrentTime); -var List = function List(_ref) { - var items = _ref.items, - sectionRef = _ref.sectionRef, - structureContainerRef = _ref.structureContainerRef; - var collapsibleContent = /*#__PURE__*/React__default["default"].createElement("ul", { - "data-testid": "list", - className: "ramp--structured-nav__list", - role: "presentation" - }, items.map(function (item, index) { - if (item) { - return /*#__PURE__*/React__default["default"].createElement(ListItem, _extends({}, item, { - sectionRef: sectionRef, - key: index, - structureContainerRef: structureContainerRef - })); +function _createSuper$4(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$4(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } +function _isNativeReflectConstruct$4() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +var MenuButton = videojs__default["default"].getComponent('MenuButton'); +var MenuItem = videojs__default["default"].getComponent('MenuItem'); + +/** + * Custom component to display rendering files as downloadable + * associated with the current Canvas. This control is enabled + * in the player's control-bar via 'enableFileDownload' prop in + * MediaPlayer component. + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} props.options + * @param {Number} props.options.files list of rendering files + */ +var VideoJSFileDownload = /*#__PURE__*/function (_MenuButton) { + _inherits(VideoJSFileDownload, _MenuButton); + var _super = _createSuper$4(VideoJSFileDownload); + function VideoJSFileDownload(player, options) { + var _this; + _classCallCheck(this, VideoJSFileDownload); + _this = _super.call(this, player, options); + // Add SVG icon through CSS class + _this.addClass("vjs-file-download"); + _this.setAttribute('data-testid', 'videojs-file-download'); + // Use Video.js' stock SVG instead of setting it using CSS + _this.setIcon('file-download'); + return _this; + } + _createClass(VideoJSFileDownload, [{ + key: "createItems", + value: function createItems() { + var options_ = this.options_, + player_ = this.player_; + var files = options_.files; + if ((files === null || files === void 0 ? void 0 : files.length) > 0) { + return files.map(function (file) { + var item = new MenuItem(player_, { + label: file.label + }); + item.handleClick = function () { + fileDownload(file.id, file.filename, file.fileExt); + }; + return item; + }); + } else { + return []; + } } - })); - return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, collapsibleContent); -}; -List.propTypes = { - items: PropTypes.array.isRequired, - sectionRef: PropTypes.object.isRequired, - structureContainerRef: PropTypes.object.isRequired -}; + }]); + return VideoJSFileDownload; +}(MenuButton); +videojs__default["default"].registerComponent('VideoJSFileDownload', VideoJSFileDownload); -function _createForOfIteratorHelper$1(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$1(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } -function _unsupportedIterableToArray$1(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$1(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$1(o, minLen); } -function _arrayLikeToArray$1(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } -var StructuredNavigation = function StructuredNavigation() { - var _structureItemsRef$cu; - var manifestDispatch = useManifestDispatch(); - var playerDispatch = usePlayerDispatch(); - var _usePlayerState = usePlayerState(), - clickedUrl = _usePlayerState.clickedUrl, - isClicked = _usePlayerState.isClicked, - isPlaying = _usePlayerState.isPlaying, - player = _usePlayerState.player; - var _useManifestState = useManifestState(), - allCanvases = _useManifestState.allCanvases, - canvasDuration = _useManifestState.canvasDuration, - canvasIndex = _useManifestState.canvasIndex, - hasMultiItems = _useManifestState.hasMultiItems, - targets = _useManifestState.targets, - manifest = _useManifestState.manifest, - playlist = _useManifestState.playlist, - canvasIsEmpty = _useManifestState.canvasIsEmpty, - canvasSegments = _useManifestState.canvasSegments; - var _useErrorBoundary = reactErrorBoundary.useErrorBoundary(), - showBoundary = _useErrorBoundary.showBoundary; - var canvasStructRef = React__default["default"].useRef(); - var structureItemsRef = React__default["default"].useRef(); - var canvasIsEmptyRef = React__default["default"].useRef(canvasIsEmpty); - var hasRootRangeRef = React__default["default"].useRef(false); - var structureContainerRef = React__default["default"].useRef(); - var scrollableStructure = React__default["default"].useRef(); - React__default["default"].useEffect(function () { - // Update currentTime and canvasIndex in state if a - // custom start time and(or) canvas is given in manifest - if (manifest) { - try { - var _getStructureRanges = getStructureRanges(manifest, allCanvases, playlist.isPlaylist), - structures = _getStructureRanges.structures, - timespans = _getStructureRanges.timespans, - markRoot = _getStructureRanges.markRoot; - structureItemsRef.current = structures; - canvasStructRef.current = structures; - hasRootRangeRef.current = markRoot; - // Remove root-level structure item from navigation calculations - if ((structures === null || structures === void 0 ? void 0 : structures.length) > 0 && structures[0].isRoot) { - canvasStructRef.current = structures[0].items; +function _createSuper$3(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$3(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } +function _isNativeReflectConstruct$3() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +var Button$2 = videojs__default["default"].getComponent('Button'); + +/** + * Custom VideoJS button component for navigating to the next Canvas when there + * are multiple canvases in a given Manifest + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} props.options + * @param {Number} props.options.canvasIndex current Canvas index + * @param {Number} props.options.lastCanvasIndex index of the last Canvas in the Manifest + * @param {Function} props.options.switchPlayer callback func to update Canvas change in state + */ +var VideoJSNextButton = /*#__PURE__*/function (_Button) { + _inherits(VideoJSNextButton, _Button); + var _super = _createSuper$3(VideoJSNextButton); + function VideoJSNextButton(player, options) { + var _this; + _classCallCheck(this, VideoJSNextButton); + _this = _super.call(this, player, options); + // Use Video.js' stock SVG instead of setting it using CSS + _this.setIcon('next-item'); + _this.addClass('vjs-play-control vjs-control'); + _this.setAttribute('data-testid', 'videojs-next-button'); + _this.controlText('Next'); + _this.options = options; + _this.player = player; + _this.cIndex = options.canvasIndex; + + // Handle player reload or source change events + _this.player.on('loadstart', function () { + _this.updateComponent(); + }); + return _this; + } + _createClass(VideoJSNextButton, [{ + key: "updateComponent", + value: function updateComponent() { + var player = this.player; + if (player && player != undefined) { + var _player$children; + // When canvasIndex property is not set in the player instance use dataset. + // This happens rarely, but when it does previous button cannot be used. + if (player.canvasIndex === undefined && ((_player$children = player.children()) === null || _player$children === void 0 ? void 0 : _player$children.length) > 0) { + this.cIndex = Number(player.children()[0].dataset.canvasindex); + } else { + this.cIndex = player.canvasIndex; } - manifestDispatch({ - structures: canvasStructRef.current, - type: 'setStructures' - }); - manifestDispatch({ - timespans: timespans, - type: 'setCanvasSegments' - }); - structureContainerRef.current.isScrolling = false; - } catch (error) { - showBoundary(error); } } - }, [manifest]); - - // Set currentNavItem when current Canvas is an inaccessible/empty item - React__default["default"].useEffect(function () { - if (canvasIsEmpty && playlist.isPlaylist) { - manifestDispatch({ - item: canvasSegments[canvasIndex], - type: 'switchItem' - }); + }, { + key: "handleClick", + value: function handleClick() { + this.handleNextClick(false); } - }, [canvasIsEmpty, canvasIndex]); - React__default["default"].useEffect(function () { - if (isClicked) { - var clickedItem = canvasSegments.filter(function (c) { - return c.id === clickedUrl; - }); - if ((clickedItem === null || clickedItem === void 0 ? void 0 : clickedItem.length) > 0) { - // Only update the current nav item for timespans - // Eliminate Canvas level items unless the structure is empty - var _clickedItem$ = clickedItem[0], - isCanvas = _clickedItem$.isCanvas, - items = _clickedItem$.items; - if (!isCanvas || items.length == 0 && isCanvas) { - manifestDispatch({ - item: clickedItem[0], - type: 'switchItem' - }); - } - } - var currentCanvasIndex = allCanvases.findIndex(function (c) { - return c.canvasURL === getCanvasId(clickedUrl); - }); - var timeFragment = getMediaFragment(clickedUrl, canvasDuration); - - // Invalid time fragment - if (!timeFragment || timeFragment == undefined) { - console.error('StructuredNavigation -> invalid media fragment in structure item -> ', timeFragment); - return; - } - var timeFragmentStart = timeFragment.start; - if (hasMultiItems) { - var _getCanvasTarget = getCanvasTarget(targets, timeFragment, canvasDuration), - srcIndex = _getCanvasTarget.srcIndex, - fragmentStart = _getCanvasTarget.fragmentStart; - timeFragmentStart = fragmentStart; - manifestDispatch({ - srcIndex: srcIndex, - type: 'setSrcIndex' - }); - } else { - // When clicked structure item is not in the current canvas - if (canvasIndex != currentCanvasIndex && currentCanvasIndex > -1) { - manifestDispatch({ - canvasIndex: currentCanvasIndex, - type: 'switchCanvas' - }); - canvasIsEmptyRef.current = canvasStructRef.current[currentCanvasIndex].isEmpty; - } + }, { + key: "handleKeyDown", + value: function handleKeyDown(e) { + if (e.which === 32 || e.which === 13) { + e.stopPropagation(); + this.handleNextClick(true); } - if (player && !canvasIsEmptyRef.current) { - player.currentTime(timeFragmentStart); - playerDispatch({ - startTime: timeFragment.start, - endTime: timeFragment.end, - type: 'setTimeFragment' - }); - - // Use this value in iOS to set the initial progress - // in the custom progress bar - player.structStart = timeFragmentStart; - playerDispatch({ - currentTime: timeFragmentStart, - type: 'setCurrentTime' - }); - // Setting userActive to true shows timerail breifly, helps - // to visualize the structure in player while playing - if (isPlaying) player.userActive(true); - } else if (canvasIsEmptyRef.current) { - // Reset isClicked in state for - // inaccessible items (empty canvases) - playerDispatch({ - type: 'resetClick' - }); + } + }, { + key: "handleNextClick", + value: function handleNextClick(isKeyDown) { + if (this.cIndex != this.options.lastCanvasIndex) { + this.options.switchPlayer(this.cIndex + 1, true, isKeyDown ? 'nextBtn' : ''); } } - }, [isClicked, player]); + }]); + return VideoJSNextButton; +}(Button$2); +videojs__default["default"].registerComponent('VideoJSNextButton', VideoJSNextButton); - // Structured nav is populated by the time the player hook fires so we listen for - // that to run the check on whether the structured nav is scrollable. - React__default["default"].useEffect(function () { - if (structureContainerRef.current) { - var elem = structureContainerRef.current; - var structureBorder = structureContainerRef.current.parentElement; - var structureEnd = Math.abs(elem.scrollHeight - (elem.scrollTop + elem.clientHeight)) <= 1; - scrollableStructure.current = !structureEnd; - if (structureBorder) { - resizeObserver.observe(structureBorder); +function _createSuper$2(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$2(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } +function _isNativeReflectConstruct$2() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +var Button$1 = videojs__default["default"].getComponent('Button'); + +/** + * Custom VideoJS button component for navigating to the previous Canvas when there + * are multiple canvases in a given Manifest + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} props.options + * @param {Number} props.options.canvasIndex current Canvas index + * @param {Function} props.options.switchPlayer callback func to update Canvas change in state + */ +var VideoJSPreviousButton = /*#__PURE__*/function (_Button) { + _inherits(VideoJSPreviousButton, _Button); + var _super = _createSuper$2(VideoJSPreviousButton); + function VideoJSPreviousButton(player, options) { + var _this; + _classCallCheck(this, VideoJSPreviousButton); + _this = _super.call(this, player, options); + // Use Video.js' stock SVG instead of setting it using CSS + _this.setIcon('previous-item'); + _this.addClass('vjs-play-control vjs-control'); + _this.setAttribute('data-testid', 'videojs-previous-button'); + _this.options = options; + _this.player = player; + _this.cIndex = options.canvasIndex; + + // Handle player reload or source change events + _this.player.on('loadstart', function () { + _this.updateComponent(); + }); + return _this; + } + _createClass(VideoJSPreviousButton, [{ + key: "updateComponent", + value: function updateComponent() { + var player = this.player; + if (player && player != undefined) { + var _player$children; + // When canvasIndex property is not set in the player instance use dataset. + // This happens rarely, but when it does previous button cannot be used. + if (player.canvasIndex === undefined && ((_player$children = player.children()) === null || _player$children === void 0 ? void 0 : _player$children.length) > 0) { + this.cIndex = Number(player.children()[0].dataset.canvasindex); + } else { + this.cIndex = player.canvasIndex; + } } + this.controlText(this.cIndex == 0 ? 'Replay' : 'Previous'); } - }, [player]); - - // Update scrolling indicators when end of scrolling has been reached - var handleScrollable = function handleScrollable(e) { - var elem = e.target; - if (elem.classList.contains('ramp--structured-nav__border')) { - elem = elem.firstChild; + }, { + key: "handleClick", + value: function handleClick() { + this.handlePreviousClick(false); } - var scrollMsg = elem.nextSibling; - var structureEnd = Math.abs(elem.scrollHeight - (elem.scrollTop + elem.clientHeight)) <= 1; - if (elem && structureEnd && elem.classList.contains('scrollable')) { - elem.classList.remove('scrollable'); - } else if (elem && !structureEnd && !elem.classList.contains('scrollable')) { - elem.classList.add('scrollable'); + }, { + key: "handleKeyDown", + value: function handleKeyDown(e) { + if (e.which === 32 || e.which === 13) { + e.stopPropagation(); + this.handlePreviousClick(true); + } } - if (scrollMsg && structureEnd && scrollMsg.classList.contains('scrollable')) { - scrollMsg.classList.remove('scrollable'); - } else if (scrollMsg && !structureEnd && !scrollMsg.classList.contains('scrollable')) { - scrollMsg.classList.add('scrollable'); + }, { + key: "handlePreviousClick", + value: function handlePreviousClick(isKeyDown) { + if (this.cIndex > -1 && this.cIndex != 0) { + this.options.switchPlayer(this.cIndex - 1, true, isKeyDown ? 'previousBtn' : ''); + } else if (this.cIndex == 0) { + this.player.currentTime(0); + } } - }; + }]); + return VideoJSPreviousButton; +}(Button$1); +videojs__default["default"].registerComponent('VideoJSPreviousButton', VideoJSPreviousButton); - // Update scrolling indicators when structured nav is resized - var resizeObserver = new ResizeObserver(function (entries) { - var _iterator = _createForOfIteratorHelper$1(entries), - _step; - try { - for (_iterator.s(); !(_step = _iterator.n()).done;) { - var entry = _step.value; - handleScrollable(entry); +function _createSuper$1(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$1(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } +function _isNativeReflectConstruct$1() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +var vjsComponent = videojs__default["default"].getComponent('Component'); + +/** + * Custom component to display title of the current item in the player + * in an overlay. + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} props.options + */ +var VideoJSTitleLink = /*#__PURE__*/function (_vjsComponent) { + _inherits(VideoJSTitleLink, _vjsComponent); + var _super = _createSuper$1(VideoJSTitleLink); + function VideoJSTitleLink(player, options) { + var _this; + _classCallCheck(this, VideoJSTitleLink); + _this = _super.call(this, player, options); + _this.setAttribute('data-testid', 'videojs-title-link'); + _this.addClass('vjs-title-bar'); + _this.options = options; + _this.player = player; + + // Handle player reload or source change events + _this.player.on('loadstart', function () { + _this.updateComponent(); + }); + return _this; + } + _createClass(VideoJSTitleLink, [{ + key: "updateComponent", + value: function updateComponent() { + var player = this.player; + if (player && player != undefined && player.canvasLink) { + var _player$canvasLink = player.canvasLink, + label = _player$canvasLink.label, + id = _player$canvasLink.id; + var title = label; + var href = null; + /** + * Avalon canvas ids are of the form 'http://host.edu/media_objects/#mo_id/manifest/canvas/#section_id`. + * Accessible url is 'http://host.edu/media_objects/#mo_id/section/#section_id' so we convert the canvas + * id for avalon manifest, but must assume other implementers will have the id as an actionable link. + */ + if (id.includes('manifest/canvas')) { + href = id.replace('manifest/canvas', 'section'); + } else { + href = id; + } + var link = videojs__default["default"].dom.createEl('a', { + className: 'vjs-title-link', + href: href, + target: '_blank', + rel: 'noreferrer noopener', + innerHTML: title + }); + if (this.el().hasChildNodes()) { + this.el().replaceChildren(link); + } else { + this.el().appendChild(link); + } } - } catch (err) { - _iterator.e(err); - } finally { - _iterator.f(); } - }); - if (!manifest) { - return /*#__PURE__*/React__default["default"].createElement("p", null, "No manifest - Please provide a valid manifest."); - } + }]); + return VideoJSTitleLink; +}(vjsComponent); +vjsComponent.registerComponent('VideoJSTitleLink', VideoJSTitleLink); - // Check for scrolling on initial render and build appropriate element class - var divClass = ''; - var spanClass = ''; - if (scrollableStructure.current) { - divClass = "ramp--structured-nav scrollable"; - spanClass = "scrollable"; - } else { - divClass = "ramp--structured-nav"; - } - if (playlist !== null && playlist !== void 0 && playlist.isPlaylist) { - divClass += " playlist-items"; - } - divClass += hasRootRangeRef.current ? " ramp--structured-nav-with_root" : ""; +function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - /** - * Update isScrolling flag within structure container ref, which is - * used by ListItem and SectionHeading components to decide to/not to - * auto scroll the content - * @param {Boolean} state - */ - var handleMouseOver = function handleMouseOver(state) { - structureContainerRef.current.isScrolling = state; - }; - return /*#__PURE__*/React__default["default"].createElement("div", { - className: "ramp--structured-nav__border" - }, /*#__PURE__*/React__default["default"].createElement("div", { - "data-testid": "structured-nav", - className: divClass, - ref: structureContainerRef, - role: "list", - "aria-label": "Structural content", - onScroll: handleScrollable, - onMouseLeave: function onMouseLeave() { - return handleMouseOver(false); - }, - onMouseOver: function onMouseOver() { - return handleMouseOver(true); - } - }, ((_structureItemsRef$cu = structureItemsRef.current) === null || _structureItemsRef$cu === void 0 ? void 0 : _structureItemsRef$cu.length) > 0 ? structureItemsRef.current.map(function (item, index) { - return /*#__PURE__*/React__default["default"].createElement(List, { - items: [item], - sectionRef: /*#__PURE__*/React__default["default"].createRef(), - key: index, - structureContainerRef: structureContainerRef - }); - }) : /*#__PURE__*/React__default["default"].createElement("p", { - className: "ramp--no-structure" - }, "There are no structures in the manifest")), /*#__PURE__*/React__default["default"].createElement("span", { - className: spanClass - }, "Scroll to see more")); -}; -StructuredNavigation.propTypes = {}; +// SVG icons for zoom-in and zoom-out icons as strings +var zoomOutIconSVG = "\n\n \n \n \n \n \n \n"; +var zoomInIconSVG = "\n\n \n \n \n \n \n \n"; -var objectWithoutPropertiesLoose = createCommonjsModule(function (module) { -function _objectWithoutPropertiesLoose(source, excluded) { - if (source == null) return {}; - var target = {}; - var sourceKeys = Object.keys(source); - var key, i; - for (i = 0; i < sourceKeys.length; i++) { - key = sourceKeys[i]; - if (excluded.indexOf(key) >= 0) continue; - target[key] = source[key]; - } - return target; +// Function to inject SVGs into the DOM +function injectSVGIcons() { + var svgContainer = document.createElement('div'); + svgContainer.style.display = 'none'; + svgContainer.innerHTML = "".concat(zoomOutIconSVG).concat(zoomInIconSVG, ""); + document.body.appendChild(svgContainer); } -module.exports = _objectWithoutPropertiesLoose, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); -var objectWithoutProperties = createCommonjsModule(function (module) { -function _objectWithoutProperties(source, excluded) { - if (source == null) return {}; - var target = objectWithoutPropertiesLoose(source, excluded); - var key, i; - if (Object.getOwnPropertySymbols) { - var sourceSymbolKeys = Object.getOwnPropertySymbols(source); - for (i = 0; i < sourceSymbolKeys.length; i++) { - key = sourceSymbolKeys[i]; - if (excluded.indexOf(key) >= 0) continue; - if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; - target[key] = source[key]; - } - } - return target; -} -module.exports = _objectWithoutProperties, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); +// Call the function to inject SVG icons +injectSVGIcons(); +var Button = videojs__default["default"].getComponent('Button'); -var _objectWithoutProperties = /*@__PURE__*/getDefaultExportFromCjs(objectWithoutProperties); +/** + * Custom VideoJS component for displaying track view when there are + * tracks/structure timespans in the current Canvas. + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} props.options + * @param {Number} props.options.trackScrubberRef React ref to track scrubber element + * @param {Number} props.options.timeToolRef React ref to time tooltip element + * @param {Boolean} props.options.isPlaylist flag to indicate a playlist Manifest or not + */ +var VideoJSTrackScrubber = /*#__PURE__*/function (_Button) { + _inherits(VideoJSTrackScrubber, _Button); + var _super = _createSuper(VideoJSTrackScrubber); + function VideoJSTrackScrubber(player, options) { + var _this; + _classCallCheck(this, VideoJSTrackScrubber); + _this = _super.call(this, player, options); + _this.setAttribute('data-testid', 'videojs-track-scrubber-button'); + _this.addClass('vjs-button vjs-track-scrubber'); + _this.controlText('Toggle track scrubber'); + _this.el().innerHTML = "\n \n \n "; + _this.options = options; + _this.player = player; + _this.playerInterval; + _this.zoomedOutRef = /*#__PURE__*/React.createRef(); + _this.currentTrackRef = /*#__PURE__*/React.createRef(); -var taggedTemplateLiteral = createCommonjsModule(function (module) { -function _taggedTemplateLiteral(strings, raw) { - if (!raw) { - raw = strings.slice(0); - } - return Object.freeze(Object.defineProperties(strings, { - raw: { - value: Object.freeze(raw) - } - })); -} -module.exports = _taggedTemplateLiteral, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); + // Attach interval on first load for time updates + _this.player.on('ready', function () { + if (_this.options.trackScrubberRef.current) { + _this.playerInterval = setInterval(function () { + _this.handleTimeUpdate(); + }, 100); + } + }); -var _taggedTemplateLiteral = /*@__PURE__*/getDefaultExportFromCjs(taggedTemplateLiteral); + /* + When player is fully built and the trackScrubber element is initialized, + call method to mount React component. + */ + _this.player.on('loadstart', function () { + if (_this.options.trackScrubberRef.current) { + _this.updateComponent(); + if (!_this.playerInterval) { + _this.playerInterval = setInterval(function () { + _this.handleTimeUpdate(); + }, 100); + } + } + }); -var _templateObject$1, _templateObject2, _templateObject3, _templateObject4; -function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } -function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } -function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } + // Hide track scrubber if it is displayed when player is going fullscreen + _this.player.on('fullscreenchange', function () { + if (_this.player.isFullscreen() && !_this.zoomedOutRef.current) { + var tempZoom = _this.zoomedOutRef.current; + _this.setZoomedOut(!tempZoom); + } + }); -// ENum for supported transcript MIME types -var TRANSCRIPT_MIME_TYPES = { - webvtt: ['text/vtt'], - srt: ['application/x-subrip', 'text/srt'], - text: ['text/plain'], - json: ['application/json'], - docx: ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'] -}; -var VTT_TIMESTAMP_REGEX = /^(?:\d{2}:)?\d{2}:\d{2}(?:\.\d+)/g; -// SRT allows using comma for milliseconds while WebVTT does not -var SRT_TIMESTAMP_REGEX = /^(?:\d{2}:)?\d{2}:\d{2}(?:[.,]\d+)/g; -var TRANSCRIPT_MIME_EXTENSIONS = [{ - type: TRANSCRIPT_MIME_TYPES.json, - ext: 'json' -}, { - type: TRANSCRIPT_MIME_TYPES.webvtt, - ext: 'vtt' -}, { - type: TRANSCRIPT_MIME_TYPES.text, - ext: 'txt' -}, { - type: TRANSCRIPT_MIME_TYPES.docx, - ext: 'docx' -}, { - type: TRANSCRIPT_MIME_TYPES.srt, - ext: 'srt' -}]; + // Clean up interval when player is disposed + _this.player.on('dispose', function () { + clearInterval(_this.playerInterval); + }); + return _this; + } + _createClass(VideoJSTrackScrubber, [{ + key: "setCurrentTrack", + value: function setCurrentTrack(t) { + this.currentTrackRef.current = t; + } + }, { + key: "setZoomedOut", + value: function setZoomedOut(z) { + this.zoomedOutRef.current = z; + if (z) { + this.options.trackScrubberRef.current.classList.add('hidden'); + this.el().innerHTML = "\n \n \n "; + } else { + this.options.trackScrubberRef.current.classList.remove('hidden'); + this.el().innerHTML = "\n \n \n "; + } + } + }, { + key: "attachListeners", + value: function attachListeners() { + var _this2 = this; + var trackScrubberRef = this.options.trackScrubberRef; + if (trackScrubberRef.current) { + // Initialize the track scrubber's current time and duration + this.populateTrackScrubber(); + this.updateTrackScrubberProgressBar(); + var pointerDragged = false; + // Attach mouse pointer events to track scrubber progress bar + var _trackScrubberRef$cur = _slicedToArray(trackScrubberRef.current.children, 3); + _trackScrubberRef$cur[0]; + var progressBar = _trackScrubberRef$cur[1]; + _trackScrubberRef$cur[2]; + progressBar.addEventListener('mouseenter', function (e) { + _this2.handleMouseMove(e); + }); + /* + Using pointerup, pointermove, pointerdown events instead of + mouseup, mousemove, mousedown events to make it work with both + mouse pointer and touch events + */ + progressBar.addEventListener('pointerup', function (e) { + if (pointerDragged) { + _this2.handleSetProgress(e); + } + }); + progressBar.addEventListener('pointermove', function (e) { + _this2.handleMouseMove(e); + pointerDragged = true; + }); + progressBar.addEventListener('pointerdown', function (e) { + // Only handle left click event + if (e.which === 1) { + _this2.handleSetProgress(e); + pointerDragged = false; + } + }); + } + } + }, { + key: "updateComponent", + value: function updateComponent() { + // Reset refs to initial value + this.zoomedOutRef.current = true; + this.currentTrackRef.current = {}; + this.attachListeners(); + } -// ENum for describing transcript types include invalid and no transcript info -var TRANSCRIPT_TYPES = { - invalidTimestamp: -4, - invalidVTT: -3, - noSupport: -2, - invalid: -1, - noTranscript: 0, - timedText: 1, - plainText: 2, - docx: 3 -}; + /** + * Keydown event handler for the track button on the player controls, + * when using keyboard navigation + * @param {Event} e keydown event + */ + }, { + key: "handleKeyDown", + value: function handleKeyDown(e) { + if (e.which === 32 || e.which === 13) { + e.preventDefault(); + this.handleTrackScrubberClick(); + e.stopPropagation(); + } + } + }, { + key: "handleClick", + value: function handleClick() { + this.handleTrackScrubberClick(); + } -// ENum for types transcript text lines in a time-synced transcript -var TRANSCRIPT_CUE_TYPES = { - note: 'NOTE', - timedCue: 'TIMED_CUE', - nonTimedLine: 'NON_TIMED_LINE' -}; + /** + * Click event handler for the track button on the player controls + */ + }, { + key: "handleTrackScrubberClick", + value: function handleTrackScrubberClick() { + var currentTrackRef = this.currentTrackRef, + player = this.player, + options = this.options; + // When player is not fully loaded on the page don't show the track scrubber + if (!options.trackScrubberRef.current || !currentTrackRef.current) return; -/** - * Parse the transcript information in the Manifest presented as supplementing annotations - * @param {String} manifestURL IIIF Presentation 3.0 manifest URL - * @param {String} title optional title given in the transcripts list in props - * @returns {Array} array of supplementing annotations for transcripts for all - * canvases in the Manifest - */ -function readSupplementingAnnotations(_x) { - return _readSupplementingAnnotations.apply(this, arguments); -} + // If player is fullscreen exit before displaying track scrubber + if (player.isFullscreen()) { + player.exitFullscreen(); + } + var tempZoom = this.zoomedOutRef.current; + this.setZoomedOut(!tempZoom); + } -/** - * Refine and sanitize the user provided transcripts list in the props. If there are manifests - * in the given array process them to find supplementing annotations in the manifest and - * them to the transcripts array to be displayed in the component. - * @param {Array} transcripts list of transcripts from Transcript component's props - * @returns {Array} a refined transcripts array for each canvas with the following json - * structure; - * { canvasId: , items: [{ title, filename, url, isMachineGen, id }]} - */ -function _readSupplementingAnnotations() { - _readSupplementingAnnotations = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee(manifestURL) { - var title, - data, - _args = arguments; - return regenerator.wrap(function _callee$(_context) { - while (1) switch (_context.prev = _context.next) { - case 0: - title = _args.length > 1 && _args[1] !== undefined ? _args[1] : ''; - _context.next = 3; - return fetch(manifestURL).then(function (response) { - var fileType = response.headers.get('Content-Type'); - if (fileType.includes('application/json')) { - var jsonData = response.json(); - return jsonData; - } else { - // Avoid throwing an error when fetched file is not a JSON - return {}; - } - }).then(function (manifest) { - var canvases = manifest.items; - var newTranscriptsList = []; - if ((canvases === null || canvases === void 0 ? void 0 : canvases.length) > 0) { - canvases.map(function (canvas, index) { - var annotations = getAnnotations(canvas.annotations, 'supplementing'); - var canvasTranscripts = []; - if (annotations.length > 0) { - var annotBody = annotations[0].body; - if (annotBody.type === 'TextualBody') { - var label = title.length > 0 ? title : annotBody.label ? getLabelValue(annotBody.label) : "Canvas-".concat(index); - var _identifyMachineGen = identifyMachineGen(label), - isMachineGen = _identifyMachineGen.isMachineGen, - labelText = _identifyMachineGen.labelText; - canvasTranscripts.push({ - url: annotBody.id === undefined ? manifestURL : annotBody.id, - title: labelText, - isMachineGen: isMachineGen, - id: "".concat(labelText, "-").concat(index), - format: '' - }); - } else { - annotations.forEach(function (annotation, i) { - var annotBody = annotation.body; - var label = ''; - var filename = ''; - if (annotBody.label && Object.keys(annotBody.label).length > 0) { - var languages = Object.keys(annotBody.label); - if ((languages === null || languages === void 0 ? void 0 : languages.length) > 1) { - // If there are multiple labels for an annotation assume the first - // is the one intended for default display. - label = getLabelValue(annotBody.label); - // Assume that an unassigned language is meant to be the downloadable filename - filename = annotBody.label.hasOwnProperty('none') ? getLabelValue(annotBody.label.none[0]) : label; - } else { - // If there is a single label, use for both label and downloadable filename - label = getLabelValue(annotBody.label); - } - } else { - label = "".concat(i); - } - var id = annotBody.id; - var sType = identifySupplementingAnnotation(id); - var _identifyMachineGen2 = identifyMachineGen(label), - isMachineGen = _identifyMachineGen2.isMachineGen, - labelText = _identifyMachineGen2.labelText; - if (filename === '') { - filename = labelText; - } - if (sType === 1 || sType === 3) { - canvasTranscripts.push({ - title: labelText, - filename: filename, - url: id, - isMachineGen: isMachineGen, - id: "".concat(labelText, "-").concat(index, "-").concat(i), - format: annotBody.format || '' - }); - } - }); - } - } - newTranscriptsList.push({ - canvasId: index, - items: canvasTranscripts - }); - }); - } - return newTranscriptsList; - })["catch"](function (error) { - console.error('transcript-parser -> readSupplementingAnnotations() -> error fetching transcript resource at, ', manifestURL); - return []; - }); - case 3: - data = _context.sent; - return _context.abrupt("return", data); - case 5: - case "end": - return _context.stop(); + /** + * Event handler for VideoJS player instance's 'timeupdate' event, which + * updates the track scrubber from player state. + */ + }, { + key: "handleTimeUpdate", + value: function handleTimeUpdate() { + var _player$markers$getMa; + var player = this.player, + options = this.options, + zoomedOutRef = this.zoomedOutRef; + // Hide track-scrubber for inaccessible item if it is open + if (player.canvasIsEmpty && !zoomedOutRef.current) { + this.setZoomedOut(true); + } + if (player.isDisposed() || player.ended()) return; + /* + Get the current track from the player.markers created from the structure timespans. + In playlists, markers are timepoint information representing highlighting annotations, + therefore omit reading markers information for track scrubber in playlist contexts. + */ + var playerCurrentTime = player.currentTime(); + if (player.markers && typeof player.markers !== 'function' && typeof player.markers.getMarkers === 'function' && ((_player$markers$getMa = player.markers.getMarkers()) === null || _player$markers$getMa === void 0 ? void 0 : _player$markers$getMa.length) > 0 && !options.isPlaylist) { + this.readPlayerMarkers(); + } else { + var _player$playableDurat, _player$altStart; + this.setCurrentTrack({ + duration: (_player$playableDurat = player.playableDuration) !== null && _player$playableDurat !== void 0 ? _player$playableDurat : player.duration(), + time: (_player$altStart = player.altStart) !== null && _player$altStart !== void 0 ? _player$altStart : 0, + key: '', + text: 'Complete media file' + }); + playerCurrentTime = player.srcIndex && player.srcIndex > 0 ? playerCurrentTime + player.altStart : playerCurrentTime; + } + this.updateTrackScrubberProgressBar(playerCurrentTime); + } + /** + * Calculate the progress and current time within the track and + * update them accordingly when the player's 'timeupdate' event fires. + * @param {Number} currentTime player's current time + */ + }, { + key: "updateTrackScrubberProgressBar", + value: function updateTrackScrubberProgressBar() { + var currentTime = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var player = this.player, + currentTrackRef = this.currentTrackRef; + // Handle Safari which emits the timeupdate event really quickly + if (!currentTrackRef.current) { + if (player.markers && typeof player.markers.getMarkers === 'function') { + this.readPlayerMarkers(); + } + } + var altStart = player.altStart, + srcIndex = player.srcIndex; + // Calculate corresponding time and played percentage values within track + var trackoffset = srcIndex > 0 ? currentTime - currentTrackRef.current.time + altStart : currentTime - currentTrackRef.current.time; + var trackpercent = Math.min(100, Math.max(0, 100 * trackoffset / currentTrackRef.current.duration)); + this.populateTrackScrubber(trackoffset, trackpercent); + } + }, { + key: "populateTrackScrubber", + value: + /** + * Update the track scrubber's current time, duration and played percentage + * when it is visible in UI. + * @param {Number} currentTime current time corresponding to the track + * @param {Number} playedPercentage elapsed time percentage of the track duration + */ + function populateTrackScrubber() { + var currentTime = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var playedPercentage = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + var trackScrubberRef = this.options.trackScrubberRef; + if (!trackScrubberRef.current) { + return; } - }, _callee); - })); - return _readSupplementingAnnotations.apply(this, arguments); -} -function sanitizeTranscripts(_x2) { - return _sanitizeTranscripts.apply(this, arguments); -} + var _trackScrubberRef$cur2 = _slicedToArray(trackScrubberRef.current.children, 3), + currentTimeDisplay = _trackScrubberRef$cur2[0]; + _trackScrubberRef$cur2[1]; + var durationDisplay = _trackScrubberRef$cur2[2]; -/** - * Group a nested JSON object array by a given property name - * @param {Array} objectArray nested array to reduced - * @param {String} indexKey property name to be used to group elements in the array - * @param {String} selectKey property to be selected from the objects to accumulated - * @returns {Array} - */ -function _sanitizeTranscripts() { - _sanitizeTranscripts = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee4(transcripts) { - var allTranscripts, sanitizedTrs, newTranscripts; - return regenerator.wrap(function _callee4$(_context4) { - while (1) switch (_context4.prev = _context4.next) { - case 0: - if (!(!transcripts || transcripts == undefined || transcripts.length == 0)) { - _context4.next = 5; - break; - } - console.error('No transcripts given as input'); - return _context4.abrupt("return", []); - case 5: - allTranscripts = []; // Build an empty list for each canvasId from the given transcripts prop - transcripts.map(function (trs) { - return allTranscripts.push({ - canvasId: trs.canvasId, - items: [] - }); - }); + // Set the elapsed time percentage in the progress bar of track scrubber + document.documentElement.style.setProperty('--range-scrubber', "calc(".concat(playedPercentage, "%)")); - // Process the async function to resolve manifest URLs in the given transcripts array - // parallely to extract supplementing annotations in the manifests - _context4.next = 9; - return Promise.all(transcripts.map( /*#__PURE__*/function () { - var _ref5 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee3(transcript) { - var canvasId, items, sanitizedItems; - return regenerator.wrap(function _callee3$(_context3) { - while (1) switch (_context3.prev = _context3.next) { - case 0: - canvasId = transcript.canvasId, items = transcript.items; - _context3.next = 3; - return Promise.all(items.map( /*#__PURE__*/function () { - var _ref6 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee2(item, index) { - var title, url, manifestTranscripts, _identifyMachineGen3, isMachineGen, labelText, manifestItems, groupedTrs; - return regenerator.wrap(function _callee2$(_context2) { - while (1) switch (_context2.prev = _context2.next) { - case 0: - title = item.title, url = item.url; // For each item in the list check if it is a manifest and parse - // the it to identify any supplementing annotations in the - // manifest for each canvas - _context2.next = 3; - return readSupplementingAnnotations(url, title); - case 3: - manifestTranscripts = _context2.sent; - _identifyMachineGen3 = identifyMachineGen(title), isMachineGen = _identifyMachineGen3.isMachineGen, labelText = _identifyMachineGen3.labelText; - manifestItems = []; - if ((manifestTranscripts === null || manifestTranscripts === void 0 ? void 0 : manifestTranscripts.length) > 0) { - manifestItems = manifestTranscripts.map(function (mt) { - return mt.items; - }).flat(); + // Update the track duration + durationDisplay.innerHTML = timeToHHmmss(this.currentTrackRef.current.duration); + // Update current time elapsed within the current track + var cleanTime = !isNaN(currentTime) && currentTime > 0 ? currentTime : 0; + currentTimeDisplay.innerHTML = timeToHHmmss(cleanTime); + } + }, { + key: "readPlayerMarkers", + value: function readPlayerMarkers() { + var tracks = this.player.markers.getMarkers().filter(function (m) { + return m["class"] == 'ramp--track-marker--fragment'; + }); + if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0) { + this.setCurrentTrack(tracks[0]); + } + } + }, { + key: "handleMouseMove", + value: + /** + * Event handler for mouseenter and mousemove pointer events on the + * the track scrubber. This sets the time tooltip value and its offset + * position in the UI. + * @param {Event} e pointer event for user interaction + */ + function handleMouseMove(e) { + var timeToolRef = this.options.timeToolRef; + if (!timeToolRef.current) { + return; + } + var time = this.getTrackTime(e); - // Concat the existing transcripts list and transcripts from the manifest and - // group them by canvasId - groupedTrs = groupByIndex(allTranscripts.concat(manifestTranscripts), 'canvasId', 'items'); - allTranscripts = groupedTrs; - } + // When hovering over the border of the track scrubber, convertTime() returns infinity, + // since e.target.clientWidth is zero. Use this value to not show the tooltip when this + // occurs. + if (isFinite(time)) { + // Calculate the horizontal position of the time tooltip using the event's offsetX property + var offset = e.offsetX - timeToolRef.current.offsetWidth / 2; // deduct 0.5 x width of tooltip element + timeToolRef.current.style.left = offset + 'px'; - // if manifest doesn't have canvases or - // supplementing annotations add original transcript from props - if (!(manifestTranscripts.length === 0 || manifestItems.length === 0)) { - _context2.next = 11; - break; - } - return _context2.abrupt("return", { - title: labelText, - filename: labelText, - url: url, - isMachineGen: isMachineGen, - id: "".concat(labelText, "-").concat(canvasId, "-").concat(index), - format: '' - }); - case 11: - return _context2.abrupt("return", null); - case 12: - case "end": - return _context2.stop(); - } - }, _callee2); - })); - return function (_x9, _x10) { - return _ref6.apply(this, arguments); - }; - }())); - case 3: - sanitizedItems = _context3.sent; - return _context3.abrupt("return", { - canvasId: canvasId, - items: sanitizedItems.filter(function (i) { - return i != null; - }) - }); - case 5: - case "end": - return _context3.stop(); - } - }, _callee3); - })); - return function (_x8) { - return _ref5.apply(this, arguments); - }; - }())); - case 9: - sanitizedTrs = _context4.sent; - // Group all the transcripts by canvasId one last time to eliminate duplicate canvasIds - newTranscripts = groupByIndex(allTranscripts.concat(sanitizedTrs), 'canvasId', 'items'); - return _context4.abrupt("return", newTranscripts); - case 12: - case "end": - return _context4.stop(); + // Set text in the tooltip as the time relevant to the pointer event's position + timeToolRef.current.innerHTML = timeToHHmmss(time); } - }, _callee4); - })); - return _sanitizeTranscripts.apply(this, arguments); -} -function groupByIndex(objectArray, indexKey, selectKey) { - return objectArray.reduce(function (acc, obj) { - var existing = acc.filter(function (a) { - return a[indexKey] == obj[indexKey]; - }); - if ((existing === null || existing === void 0 ? void 0 : existing.length) > 0) { - var current = existing[0]; - current[selectKey] = current[selectKey].concat(obj[selectKey]); - } else { - acc.push(obj); } - return acc; - }, []); -} + }, { + key: "handleSetProgress", + value: + /** + * Event handler for mousedown event on the track scrubber. This sets the + * progress percentage within track scrubber and update the player's current time + * when user clicks on a point within the track scrubber. + * @param {Event} e pointer event for user interaction + */ + function handleSetProgress(e) { + var currentTrackRef = this.currentTrackRef, + player = this.player; + if (!currentTrackRef.current) { + return; + } + var trackoffset = this.getTrackTime(e); + if (trackoffset != undefined) { + // Calculate percentage of the progress based on the pointer position's + // time and duration of the track + var trackpercent = Math.min(100, Math.max(0, 100 * (trackoffset / currentTrackRef.current.duration))); + + // Set the elapsed time in the scrubber progress bar + document.documentElement.style.setProperty('--range-scrubber', "calc(".concat(trackpercent, "%)")); + + // Set player's current time with respective to the altStart for clipped items + var playerCurrentTime = player.isClipped ? trackoffset + currentTrackRef.current.time : trackoffset; + player.currentTime(playerCurrentTime); + } + } + }, { + key: "getTrackTime", + value: + /** + * Convert pointer position on track scrubber to a time value + * @param {Event} e pointer event for user interaction + * @returns {Number} time corresponding to the pointer position + */ + function getTrackTime(e) { + var currentTrackRef = this.currentTrackRef; + if (!currentTrackRef.current) { + return; + } + var offsetx = e.offsetX; + if (offsetx && offsetx != undefined) { + var time = offsetx / e.target.clientWidth * currentTrackRef.current.duration; + return time; + } + } + }]); + return VideoJSTrackScrubber; +}(Button); +videojs__default["default"].registerComponent('VideoJSTrackScrubber', VideoJSTrackScrubber); + +function _createForOfIteratorHelper$1(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$1(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } +function _unsupportedIterableToArray$1(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$1(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$1(o, minLen); } +function _arrayLikeToArray$1(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } +function ownKeys$4(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } +function _objectSpread$4(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$4(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$4(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } +require('@silvermine/videojs-quality-selector')(videojs__default["default"]); +// import vjsYo from './vjsYo'; /** - * Parse a given transcript file into a format the Transcript component - * can render on the UI. E.g.: text file -> returns null, so that the Google - * doc viewer is rendered, IIIF manifest -> extract and parse transcript data - * within the manifest. - * @param {String} url URL of the transcript file selected - * @param {Number} canvasIndex Current canvas rendered in the player - * @param {String} format transcript file format read from Annotation - * @returns {Object} Array of trancript data objects with download URL + * Module to setup VideoJS instance on initial page load and update + * on successive player reloads on Canvas changes. + * @param {Object} props + * @param {Boolean} props.isVideo + * @param {Boolean} props.isPlaylist + * @param {Object} props.trackScrubberRef + * @param {Object} props.scrubberTooltipRef + * @param {Array} props.tracks + * @param {String} props.placeholderText + * @param {Array} props.renderingFiles + * @param {Boolean} props.enableFileDownload + * @param {Function} props.loadPrevOrNext + * @param {Number} props.lastCanvasIndex + * @param {Boolean} props.enableTitleLink + * @param {String} props.videoJSLangMap + * @param {Object} props.options */ -function parseTranscriptData(_x3, _x4, _x5) { - return _parseTranscriptData.apply(this, arguments); -} +function VideoJSPlayer(_ref) { + var enableFileDownload = _ref.enableFileDownload, + enableTitleLink = _ref.enableTitleLink, + isVideo = _ref.isVideo, + options = _ref.options, + placeholderText = _ref.placeholderText, + scrubberTooltipRef = _ref.scrubberTooltipRef, + tracks = _ref.tracks, + trackScrubberRef = _ref.trackScrubberRef, + videoJSLangMap = _ref.videoJSLangMap, + withCredentials = _ref.withCredentials; + var playerState = usePlayerState(); + var playerDispatch = usePlayerDispatch(); + var manifestState = useManifestState(); + var manifestDispatch = useManifestDispatch(); + var canvasDuration = manifestState.canvasDuration, + canvasLink = manifestState.canvasLink, + hasMultiItems = manifestState.hasMultiItems, + targets = manifestState.targets, + autoAdvance = manifestState.autoAdvance, + structures = manifestState.structures, + canvasSegments = manifestState.canvasSegments, + hasStructure = manifestState.hasStructure; + var isEnded = playerState.isEnded, + isPlaying = playerState.isPlaying, + currentTime = playerState.currentTime; + var _useLocalStorage = useLocalStorage('startVolume', 1), + _useLocalStorage2 = _slicedToArray(_useLocalStorage, 2), + startVolume = _useLocalStorage2[0], + setStartVolume = _useLocalStorage2[1]; + var _useLocalStorage3 = useLocalStorage('startMuted', false), + _useLocalStorage4 = _slicedToArray(_useLocalStorage3, 2), + startMuted = _useLocalStorage4[0], + setStartMuted = _useLocalStorage4[1]; + var _useLocalStorage5 = useLocalStorage('startCaptioned', true), + _useLocalStorage6 = _slicedToArray(_useLocalStorage5, 2), + startCaptioned = _useLocalStorage6[0], + setStartCaptioned = _useLocalStorage6[1]; + var _useLocalStorage7 = useLocalStorage('startQuality', null), + _useLocalStorage8 = _slicedToArray(_useLocalStorage7, 2), + startQuality = _useLocalStorage8[0], + setStartQuality = _useLocalStorage8[1]; + var videoJSRef = React.useRef(null); + var captionsOnRef = React.useRef(); + var activeTrackRef = React.useRef(); + var _useMediaPlayer = useMediaPlayer(), + canvasIndex = _useMediaPlayer.canvasIndex, + canvasIsEmpty = _useMediaPlayer.canvasIsEmpty, + lastCanvasIndex = _useMediaPlayer.lastCanvasIndex; + var _useSetupPlayer = useSetupPlayer({ + enableFileDownload: enableFileDownload, + withCredentials: withCredentials, + lastCanvasIndex: lastCanvasIndex + }), + isPlaylist = _useSetupPlayer.isPlaylist, + renderingFiles = _useSetupPlayer.renderingFiles, + srcIndex = _useSetupPlayer.srcIndex, + switchPlayer = _useSetupPlayer.switchPlayer; + var _useShowInaccessibleM = useShowInaccessibleMessage({ + lastCanvasIndex: lastCanvasIndex + }), + messageTime = _useShowInaccessibleM.messageTime; + var canvasIsEmptyRef = React.useRef(); + canvasIsEmptyRef.current = React.useMemo(function () { + return canvasIsEmpty; + }, [canvasIsEmpty]); + var isEndedRef = React.useRef(); + isEndedRef.current = React.useMemo(function () { + return isEnded; + }, [isEnded]); + var isPlayingRef = React.useRef(); + isPlayingRef.current = React.useMemo(function () { + return isPlaying; + }, [isPlaying]); + var autoAdvanceRef = React.useRef(); + autoAdvanceRef.current = React.useMemo(function () { + return autoAdvance; + }, [autoAdvance]); + var srcIndexRef = React.useRef(); + srcIndexRef.current = React.useMemo(function () { + return srcIndex; + }, [srcIndex]); + var currentTimeRef = React.useRef(); + currentTimeRef.current = React.useMemo(function () { + return currentTime; + }, [currentTime]); -/** - * Parse MS word documents into HTML markdown using mammoth.js - * https://www.npmjs.com/package/mammoth - * @param {Object} response response from the fetch request - * @returns {Array} html markdown for the word document contents - */ -function _parseTranscriptData() { - _parseTranscriptData = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee5(url, canvasIndex, format) { - var tData, tUrl, contentType, fileData, fromContentType, fromAnnotFormat, fileType, urlExt, filteredExt, textData, textLines, jsonData, json, parsedText, _parseTimedText, _tData, tType; - return regenerator.wrap(function _callee5$(_context5) { - while (1) switch (_context5.prev = _context5.next) { - case 0: - tData = []; - tUrl = url; // Validate given URL - if (!(url === undefined)) { - _context5.next = 4; - break; - } - return _context5.abrupt("return", { - tData: tData, - tUrl: tUrl, - tType: TRANSCRIPT_TYPES.invalid - }); - case 4: - contentType = null; - fileData = null; // get file type - _context5.next = 8; - return fetch(url).then(handleFetchErrors).then(function (response) { - contentType = response.headers.get('Content-Type'); - fileData = response; - })["catch"](function (error) { - console.error('transcript-parser -> parseTranscriptData() -> fetching transcript -> ', error); - }); - case 8: - if (!(contentType == null)) { - _context5.next = 10; - break; - } - return _context5.abrupt("return", { - tData: [], - tUrl: tUrl, - tType: TRANSCRIPT_TYPES.invalid - }); - case 10: - /* - Use the Annotation format in the IIIF Manifest, file extension, and the - Content-Type in headers of the fetch request to determine the file type. - These are checked with priority descending in the order of Annotation format, - Content-Type in headers, and file extension in the resource URI. - */ - fromContentType = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { - return tm.type.includes(contentType.split(';')[0]); - }); - fromAnnotFormat = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { - return tm.type.includes(format); - }); - fileType = ''; - if ((fromAnnotFormat === null || fromAnnotFormat === void 0 ? void 0 : fromAnnotFormat.length) > 0) { - fileType = fromAnnotFormat[0].ext; - } else if (fromContentType.length > 0) { - fileType = fromContentType[0].ext; - } else { - urlExt = url.split('.').reverse()[0]; // Only use this if it exists in the supported list of file types for the component - filteredExt = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { - return tm.ext === urlExt; - }); - fileType = filteredExt.length > 0 ? urlExt : ''; - } + /** + * Setup player with player-related information parsed from the IIIF + * Manifest Canvas. This gets called on both initial page load and each + * Canvas switch to setup and update player respectively. + * @param {Object} player current player instance from Video.js + */ + var playerInitSetup = function playerInitSetup(player) { + player.on('ready', function () { + console.log('Player ready'); - // Return empty array to display an error message - if (!(canvasIndex === undefined)) { - _context5.next = 16; - break; - } - return _context5.abrupt("return", { - tData: tData, - tUrl: tUrl, - tType: TRANSCRIPT_TYPES.noTranscript - }); - case 16: - _context5.t0 = fileType; - _context5.next = _context5.t0 === 'json' ? 19 : _context5.t0 === 'txt' ? 28 : _context5.t0 === 'srt' ? 39 : _context5.t0 === 'vtt' ? 39 : _context5.t0 === 'docx' ? 49 : 53; - break; - case 19: - _context5.next = 21; - return fileData.json(); - case 21: - jsonData = _context5.sent; - if (!((jsonData === null || jsonData === void 0 ? void 0 : jsonData.type) === 'Manifest')) { - _context5.next = 26; - break; - } - return _context5.abrupt("return", parseManifestTranscript(jsonData, url, canvasIndex)); - case 26: - json = parseJSONData(jsonData); - return _context5.abrupt("return", { - tData: json.tData, - tUrl: tUrl, - tType: json.tType, - tFileExt: fileType - }); - case 28: - _context5.next = 30; - return fileData.text(); - case 30: - textData = _context5.sent; - textLines = textData.split('\n'); - if (!(textLines.length == 0)) { - _context5.next = 36; - break; - } - return _context5.abrupt("return", { - tData: [], - tUrl: url, - tType: TRANSCRIPT_TYPES.noTranscript - }); - case 36: - parsedText = buildNonTimedText(textLines); - return _context5.abrupt("return", { - tData: parsedText, - tUrl: url, - tType: TRANSCRIPT_TYPES.plainText, - tFileExt: fileType - }); - case 38: - case 39: - _context5.next = 41; - return fileData.text(); - case 41: - textData = _context5.sent; - textLines = textData.split('\n'); - if (!(textLines.length == 0)) { - _context5.next = 47; - break; - } - return _context5.abrupt("return", { - tData: [], - tUrl: url, - tType: TRANSCRIPT_TYPES.noTranscript - }); - case 47: - _parseTimedText = parseTimedText(textData, fileType === 'srt'), _tData = _parseTimedText.tData, tType = _parseTimedText.tType; - return _context5.abrupt("return", { - tData: _tData, - tUrl: url, - tType: tType, - tFileExt: fileType - }); - case 49: - _context5.next = 51; - return parseWordFile(fileData); - case 51: - tData = _context5.sent; - return _context5.abrupt("return", { - tData: splitIntoElements(tData), - tUrl: url, - tType: TRANSCRIPT_TYPES.docx, - tFileExt: fileType - }); - case 53: - return _context5.abrupt("return", { - tData: [], - tUrl: url, - tType: TRANSCRIPT_TYPES.noSupport + // Add this class in mobile/tablet devices to always show the control bar, + // since the inactivityTimeout is flaky in some browsers + if (IS_MOBILE || IS_IPAD) { + player.controlBar.addClass('vjs-mobile-visible'); + } + player.muted(startMuted); + player.volume(startVolume); + player.canvasIndex = cIndexRef.current; + player.duration(canvasDuration); + player.srcIndex = srcIndex; + player.targets = targets; + if (enableTitleLink) player.canvasLink = canvasLink; + + // Need to set this once experimentalSvgIcons option in Video.js options was enabled + player.getChild('controlBar').qualitySelector.setIcon('cog'); + }); + player.on('progress', function () { + // Reveal player if not revealed on 'loadedmetadata' event, allowing user to + // interact with the player since enough data is available for playback + if (player.hasClass('vjs-disabled')) { + player.removeClass('vjs-disabled'); + } + }); + player.on('canplay', function () { + // Reset isEnded flag + playerDispatch({ + isEnded: false, + type: 'setIsEnded' + }); + }); + player.on('play', function () { + playerDispatch({ + isPlaying: true, + type: 'setPlayingStatus' + }); + }); + player.on('timeupdate', function () { + handleTimeUpdate(); + }); + player.on('ended', function () { + /** + * Checking against isReadyRef.current stops from delayed events being executed + * when transitioning from a Canvas to the next. + * Checking against isPlayingRef.current to distinguish whether this event + * triggered intentionally, because Video.js seem to trigger this event when + * switching to a media file with a shorter duration in Safari browsers. + */ + setTimeout(function () { + if (isReadyRef.current && isPlayingRef.current) { + playerDispatch({ + isEnded: true, + type: 'setIsEnded' }); - case 54: - case "end": - return _context5.stop(); - } - }, _callee5); - })); - return _parseTranscriptData.apply(this, arguments); -} -function parseWordFile(_x6) { - return _parseWordFile.apply(this, arguments); -} -/** - * Parse json data into Transcript component friendly - * format - * @param {Object} jsonData array of JSON objects - * @returns {Object} - */ -function _parseWordFile() { - _parseWordFile = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee6(response) { - var tData, data, arrayBuffer; - return regenerator.wrap(function _callee6$(_context6) { - while (1) switch (_context6.prev = _context6.next) { - case 0: - tData = null; - _context6.next = 3; - return response.blob(); + player.pause(); + if (!canvasIsEmptyRef.current) handleEnded(); + } + }, 100); + }); + player.on('volumechange', function () { + setStartMuted(player.muted()); + setStartVolume(player.volume()); + }); + player.on('qualityRequested', function (e, quality) { + setStartQuality(quality.label); + }); + // Use error event listener for inaccessible item display + player.on('error', function (e) { + var error = player.error(); + var errorMessage = 'Something went wrong. Please try again later or contact support for help.'; + // Handle different error codes + switch (error.code) { + case 1: + console.error('MEDIA_ERR_ABORTED: The fetching process for the media resource was aborted by the user agent\ + at the user’s request.'); + break; + case 2: + errorMessage = 'The media could not be loaded due to a network error. Please try again later.'; + console.error('MEDIA_ERR_NETWORK: A network error caused the user agent to stop fetching the media resource,\ + after the resource was established to be usable.'); + break; case 3: - data = _context6.sent; - arrayBuffer = new File([data], name, { - type: response.headers.get('content-type') - }); - _context6.next = 7; - return mammoth__default["default"].convertToHtml({ - arrayBuffer: arrayBuffer - }).then(function (result) { - tData = result.value; - })["catch"](function (err) { - console.error(err); - }); - case 7: - return _context6.abrupt("return", tData); - case 8: - case "end": - return _context6.stop(); + errorMessage = 'Media is corrupt or has features not supported by the browser. \ + Please try a different media or contact support for help.'; + console.error('MEDIA_ERR_DECODE: An error occurred while decoding the media resource, after\ + the resource was established to be usable.'); + break; + case 4: + errorMessage = 'Media could not be loaded. Network error or media format is not supported.'; + console.error('MEDIA_ERR_SRC_NOT_SUPPORTED: The media resource indicated by the src attribute was not suitable.'); + break; + default: + console.error('An unknown error occurred.'); + break; } - }, _callee6); - })); - return _parseWordFile.apply(this, arguments); -} -function parseJSONData(jsonData) { - if (jsonData.length == 0) { - return { - tData: [], - tType: TRANSCRIPT_TYPES.noTranscript - }; - } - var tData = []; - var _iterator = _createForOfIteratorHelper(jsonData), - _step; - try { - for (_iterator.s(); !(_step = _iterator.n()).done;) { - var jd = _step.value; - if (jd.speaker) { - var speaker = jd.speaker, - spans = jd.spans; - var _iterator2 = _createForOfIteratorHelper(spans), - _step2; - try { - for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { - var span = _step2.value; - span.speaker = speaker; - tData.push(span); - } - } catch (err) { - _iterator2.e(err); - } finally { - _iterator2.f(); - } + // Show dismissable error display modal from Video.js + var errorDisplay = player.getChild('ErrorDisplay'); + if (errorDisplay) { + errorDisplay.contentEl().innerText = errorMessage; + errorDisplay.removeClass('vjs-hidden'); + player.removeClass('vjs-error'); + player.removeClass('vjs-disabled'); + } + e.stopPropagation(); + }); + playerLoadedMetadata(player); + }; + + /** + * Update player properties and data when player is reloaded with + * source change, i.e. Canvas change + * @param {Object} player + */ + var updatePlayer = function updatePlayer(player) { + player.duration(canvasDuration); + player.src(options.sources); + player.poster(options.poster); + player.canvasIndex = cIndexRef.current; + player.canvasIsEmpty = canvasIsEmptyRef.current; + player.srcIndex = srcIndex; + player.targets = targets; + if (enableTitleLink) player.canvasLink = canvasLink; + + // Update textTracks in the player + var oldTracks = player.remoteTextTracks(); + var i = oldTracks.length; + while (i--) { + player.removeRemoteTextTrack(oldTracks[i]); + } + if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0 && isVideo) { + tracks.forEach(function (track) { + player.addRemoteTextTrack(track, false); + }); + } + + /* + Update player control bar for; + - track scrubber button + - appearance of the player: big play button and aspect ratio of the player + based on media type + - volume panel based on media type + - file download menu + */ + if (player.getChild('controlBar') != null && !canvasIsEmpty) { + var controlBar = player.getChild('controlBar'); + // Index of the full-screen toggle in the player's control bar + var fullscreenIndex = controlBar.children().findIndex(function (c) { + return c.name_ == 'FullscreenToggle'; + }); + /* + Track-scrubber button: remove if the Manifest is not a playlist manifest + or the current Canvas doesn't have structure items. Or add back in if it's + not present otherwise. + */ + if (!(hasStructure || isPlaylist)) { + controlBar.removeChild('videoJSTrackScrubber'); + } else if (!controlBar.getChild('videoJSTrackScrubber')) { + // Add track-scrubber button after duration display if it is not available + controlBar.addChild('videoJSTrackScrubber', { + trackScrubberRef: trackScrubberRef, + timeToolRef: scrubberTooltipRef + }); + } + if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0 && isVideo && !controlBar.getChild('subsCapsButton')) { + var captionIndex = IS_MOBILE ? controlBar.children().findIndex(function (c) { + return c.name_ == 'MuteToggle'; + }) : controlBar.children().findIndex(function (c) { + return c.name_ == 'VolumePanel'; + }); + var subsCapBtn = controlBar.addChild('subsCapsButton', {}, captionIndex + 1); + // Add CSS to mark captions-on + subsCapBtn.children_[0].addClass('captions-on'); + } + + /* + Change player's appearance when switching between audio and video canvases. + For audio: player height is reduced and big play button is removed + For video: player aspect ratio is set to 16:9 and has the centered big play button + */ + if (!isVideo) { + player.audioOnlyMode(true); + player.addClass('vjs-audio'); + player.height(player.controlBar.height()); + player.removeChild('bigPlayButton'); } else { - var _iterator3 = _createForOfIteratorHelper(jd.spans), - _step3; - try { - for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { - var _span = _step3.value; - _span.format = 'text/plain'; - _span.tag = TRANSCRIPT_CUE_TYPES.timedCue; - tData.push(_span); - } - } catch (err) { - _iterator3.e(err); - } finally { - _iterator3.f(); + player.audioOnlyMode(false); + player.removeClass('vjs-audio'); + player.aspectRatio('16:9'); + player.addChild('bigPlayButton'); + } + + /* + Re-add volumePanel/muteToggle icon: ensures the correct order of controls + on player reload. + On mobile device browsers, the volume panel is replaced by muteToggle + for both audio and video. + */ + if (!IS_MOBILE) { + controlBar.removeChild('VolumePanel'); + controlBar.addChild('VolumePanel'); + /* + Trigger ready event to reset the volume slider in the refreshed + volume panel. This is needed on player reload, since volume slider + is set on either 'ready' or 'volumechange' events. + */ + player.trigger('volumechange'); + } else { + controlBar.removeChild('MuteToggle'); + controlBar.addChild('MuteToggle'); + } + if (enableFileDownload) { + var fileDownloadIndex = controlBar.children().findIndex(function (c) { + return c.name_ == 'VideoJSFileDownload'; + }) || fullscreenIndex + 1; + controlBar.removeChild('videoJSFileDownload'); + if ((renderingFiles === null || renderingFiles === void 0 ? void 0 : renderingFiles.length) > 0) { + var fileOptions = { + title: 'Download Files', + controlText: 'Alternate resource download', + files: renderingFiles + }; + controlBar.addChild('videoJSFileDownload', _objectSpread$4({}, fileOptions), fileDownloadIndex); } } } - } catch (err) { - _iterator.e(err); - } finally { - _iterator.f(); - } - return { - tData: tData, - tType: TRANSCRIPT_TYPES.timedText + playerLoadedMetadata(player); }; -} - -/* Parsing annotations when transcript data is fed from a IIIF manifest */ -/** - * Parse a IIIF manifest and extracts the transcript data. - * IIIF manifests can present transcript data in a couple of different ways. - * 1. Using 'rendering' prop to link to an external file - * a. when the external file contains only text - * b. when the external file contains annotations - * 2. Using IIIF 'annotations' within the manifest - * @param {Object} manifest IIIF manifest data - * @param {String} manifestURL IIIF manifest URL - * @param {Number} canvasIndex Current canvas index - * @returns {Object} object with the structure; - * { tData: transcript data, tUrl: file url } - */ -function parseManifestTranscript(manifest, manifestURL, canvasIndex) { - var _manifest$items; - var tData = []; - var tUrl = manifestURL; - var isExternalAnnotation = false; - var annotations = []; - if (manifest.annotations) { - annotations = getAnnotations(manifest.annotations, 'supplementing'); - } else if (((_manifest$items = manifest.items) === null || _manifest$items === void 0 ? void 0 : _manifest$items.length) > 0) { - var _manifest$items$canva; - annotations = getAnnotations((_manifest$items$canva = manifest.items[canvasIndex]) === null || _manifest$items$canva === void 0 ? void 0 : _manifest$items$canva.annotations, 'supplementing'); - } - - // determine whether annotations point to an external resource or - // a list of transcript fragments - if (annotations.length > 0) { - var annotation = annotations[0]; - var tType = annotation.body.type; - if (tType == 'TextualBody') { - isExternalAnnotation = false; - } else { - isExternalAnnotation = true; - } - } else { - return { - tData: [], - tUrl: tUrl, - tType: TRANSCRIPT_TYPES.noTranscript - }; - } - if (isExternalAnnotation) { - var _annotation = annotations[0]; - return parseExternalAnnotations(_annotation); - } else { - tData = createTData(annotations); - return { - tData: tData, - tUrl: tUrl, - tType: TRANSCRIPT_TYPES.timedText, - tFileExt: 'json' - }; - } -} -/** - * Parse annotation linking to external resources like WebVTT, SRT, Text, and - * AnnotationPage .json files - * @param {Annotation} annotation Annotation from the manifest - * @returns {Object} object with the structure { tData: [], tUrl: '', tType: '' } - */ -function parseExternalAnnotations(_x7) { - return _parseExternalAnnotations.apply(this, arguments); -} -/** - * Converts Annotation to the common format that the - * transcripts component expects - * @param {Array} annotations array of Annotations - * @returns {Array} array of JSON objects - * Structure of the JSON object is as follows; - * { - * begin: 0, - * end: 60, - * text: 'Transcript text', - * format: 'text/plain', - * } - */ -function _parseExternalAnnotations() { - _parseExternalAnnotations = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee7(annotation) { - var tData, type, tBody, tUrl, tType, tFormat, tFileExt; - return regenerator.wrap(function _callee7$(_context7) { - while (1) switch (_context7.prev = _context7.next) { - case 0: - tData = []; - type = ''; - tBody = annotation.body; - tUrl = tBody.id; - tType = tBody.type; - tFormat = tBody.format; - tFileExt = ''; - /** When external file contains text data */ - if (!(tType === 'Text')) { - _context7.next = 12; - break; - } - _context7.next = 10; - return fetch(tUrl).then(handleFetchErrors).then(function (response) { - return response.text(); - }).then(function (data) { - if (TRANSCRIPT_MIME_TYPES.webvtt.includes(tFormat) || TRANSCRIPT_MIME_TYPES.srt.includes(tFormat)) { - var parsed = parseTimedText(data, TRANSCRIPT_MIME_TYPES.srt.includes(tFormat)); - tData = parsed.tData; - type = parsed.tType; - tFileExt = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { - return tm.type.includes(tFormat); - })[0].ext; - } else { - var textLines = data.split('\n'); - tData = buildNonTimedText(textLines); - type = TRANSCRIPT_TYPES.plainText; - tFileExt = 'txt'; - } - })["catch"](function (error) { - console.error('transcript-parser -> parseExternalAnnotations() -> fetching external transcript -> ', error); - throw error; - }); - case 10: - _context7.next = 15; - break; - case 12: - if (!(tType === 'AnnotationPage')) { - _context7.next = 15; - break; - } - _context7.next = 15; - return fetch(tUrl).then(handleFetchErrors).then(function (response) { - return response.json(); - }).then(function (data) { - var annotations = getAnnotations([data], 'supplementing'); - tData = createTData(annotations); - type = TRANSCRIPT_TYPES.timedText; - tFileExt = 'json'; + /** + * Setup on loadedmetadata event is broken out of initial setup function, + * since this needs to be called when reloading the player on Canvas change + * @param {Object} player Video.js player instance + */ + var playerLoadedMetadata = function playerLoadedMetadata(player) { + player.one('loadedmetadata', function () { + console.log('Player loadedmetadata'); + player.duration(canvasDuration); + isEndedRef.current ? player.currentTime(0) : player.currentTime(currentTimeRef.current); + if (isEndedRef.current || isPlayingRef.current) { + /* + iOS devices lockdown the ability for unmuted audio and video media to autoplay. + They accomplish this by capturing any programmatic play events and returning + a rejected Promise. In certain versions of iOS, this rejected promise would + cause a runtime error within Ramp. This error would cause the error boundary + handling to trigger, forcing a user to reload the player/page. By silently + catching the rejected Promise we are able to provide a more seamless user + experience, where the user can manually play the media or change to a different + section. + */ + var promise = player.play(); + if (promise !== undefined) { + promise.then(function (_) { + // Autoplay })["catch"](function (error) { - console.error('transcript-parser -> parseExternalAnnotations() -> fetching annotations -> ', error); - throw error; - }); - case 15: - return _context7.abrupt("return", { - tData: tData, - tUrl: tUrl, - tType: type, - tFileExt: tFileExt + // Prevent error from triggering error boundary }); - case 16: - case "end": - return _context7.stop(); + } + } + if (isVideo) { + setUpCaptions(player); } - }, _callee7); - })); - return _parseExternalAnnotations.apply(this, arguments); -} -function createTData(annotations) { - var tData = []; - annotations.map(function (a) { - if (a.id != null) { - var tBody = a.body; - var _getMediaFragment = getMediaFragment(a.target), - start = _getMediaFragment.start, - end = _getMediaFragment.end; - tData.push({ - text: tBody.value, - format: tBody.format, - begin: parseFloat(start), - end: parseFloat(end), - tag: TRANSCRIPT_CUE_TYPES.timedCue - }); - } - }); - return tData; -} - -/** - * Parsing transcript data from a given file with timed text - * @param {Object} fileData content in the transcript file - * @param {Boolean} isSRT given transcript file is an SRT - * @returns {Array} array of JSON objects of the following - * structure; - * { - * begin: '00:00:00.000', - * end: '00:01:00.000', - * text: 'Transcript text sample' - * tag: NOTE || TIMED_CUE - * } - */ -function parseTimedText(fileData) { - var isSRT = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - var tData = []; - var noteLines = []; - - // split file content into lines - var lines = fileData.split('\n'); - // For SRT files all of the file content is considered as cues - var cueLines = lines; - if (!isSRT) { - var _validateWebVTT = validateWebVTT(lines), - valid = _validateWebVTT.valid, - cue_lines = _validateWebVTT.cue_lines, - notes = _validateWebVTT.notes; - if (!valid) { - console.error('Invalid WebVTT file'); - return { - tData: [], - tType: TRANSCRIPT_TYPES.invalidVTT - }; - } - cueLines = cue_lines; - noteLines = notes; - } - var groups = groupTimedTextLines(cueLines); + /* + Set playable duration within the given media file and alternate start time as + player properties. These values are read by track-scrubber component to build + and update the track-scrubber progress and time in the UI. + */ + var mediaRange = getMediaFragment(player.src(), canvasDuration); + if (mediaRange != undefined) { + player.playableDuration = mediaRange.end - mediaRange.start; + player.altStart = mediaRange.start; + } else { + player.playableDuration = canvasDuration; + player.altStart = targets[srcIndex].altStart; + } + player.canvasIndex = cIndexRef.current; + setIsReady(true); - // Add back the NOTE(s) in the header block - groups.unshift.apply(groups, _toConsumableArray(noteLines)); - var hasInvalidTimestamp = false; - for (var i = 0; i < groups.length;) { - var line = parseTimedTextLine(groups[i], isSRT); - if (!line) { - hasInvalidTimestamp || (hasInvalidTimestamp = true); - break; - } else { - tData.push(line); - i++; - } - } - return { - tData: hasInvalidTimestamp ? null : tData, - tType: hasInvalidTimestamp ? TRANSCRIPT_TYPES.invalidTimestamp : TRANSCRIPT_TYPES.timedText + /** + * Update currentNavItem on loadedmetadata event in Safari, as it doesn't + * trigger the 'timeupdate' event intermittently on load. + */ + if (IS_SAFARI) { + handleTimeUpdate(); + } + + /** + * When either player/browser tab is muted Safari and Chrome in iOS doesn't seem to + * load enough data related to audio-only media for the Video.js instance to play + * on page load. + * Since, it is not possible to detect muted tabs in JS the condition avoids + * checking for muted state altogether. + * Without this, Safari will not reach player.readyState() = 4, the state + * which indicates the player that enough data is available on the media + * for playback. + */ + if (!isVideo && (IS_SAFARI || IS_IOS) && player.readyState() != 4) { + player.load(); + } + + // Reveal player if not revealed on 'progress' event, allowing user to + // interact with the player since enough data is available for playback + if (player.hasClass('vjs-disabled')) { + player.removeClass('vjs-disabled'); + } + }); }; -} + var _useVideoJSPlayer = useVideoJSPlayer({ + options: options, + playerInitSetup: playerInitSetup, + updatePlayer: updatePlayer, + startQuality: startQuality, + tracks: tracks, + videoJSRef: videoJSRef, + videoJSLangMap: videoJSLangMap + }), + activeId = _useVideoJSPlayer.activeId, + fragmentMarker = _useVideoJSPlayer.fragmentMarker, + isReadyRef = _useVideoJSPlayer.isReadyRef, + playerRef = _useVideoJSPlayer.playerRef, + setActiveId = _useVideoJSPlayer.setActiveId, + setFragmentMarker = _useVideoJSPlayer.setFragmentMarker, + setIsReady = _useVideoJSPlayer.setIsReady; + var cIndexRef = React.useRef(); + cIndexRef.current = React.useMemo(function () { + return canvasIndex; + }, [canvasIndex]); + var activeIdRef = React.useRef(); + activeIdRef.current = React.useMemo(function () { + return activeId; + }, [activeId]); -/** - * Validate WebVTT file with its header content - * @param {Array} lines WebVTT file content split into lines - * @returns {Boolean} - */ -function validateWebVTT(lines) { - var firstLine = lines.shift().trim(); - if ((firstLine === null || firstLine === void 0 ? void 0 : firstLine.length) == 6 && firstLine === 'WEBVTT') { - var _validateWebVTTHeader = validateWebVTTHeaders(lines), - valid = _validateWebVTTHeader.valid, - cue_lines = _validateWebVTTHeader.cue_lines, - notes = _validateWebVTTHeader.notes; - return { - valid: valid, - cue_lines: cue_lines, - notes: notes - }; - } else { - return { - valid: false, - cue_lines: [], - notes: [] - }; - } -} + /** + * Setup captions for the player based on context + * @param {Object} player Video.js player instance + */ + var setUpCaptions = function setUpCaptions(player) { + var _textTracks$tracks_; + var textTracks = player.textTracks(); + /* + Filter the text track Video.js adds with an empty label and language + when nativeTextTracks are enabled for iPhones and iPads. + Related links, Video.js => https://github.com/videojs/video.js/issues/2808 and + in Apple => https://developer.apple.com/library/archive/qa/qa1801/_index.html + */ + if (IS_MOBILE && !IS_ANDROID) { + textTracks.on('addtrack', function () { + for (var i = 0; i < textTracks.length; i++) { + if (textTracks[i].language === '' && textTracks[i].label === '') { + player.textTracks().removeTrack(textTracks[i]); + } + /** + * This enables the caption in the native iOS player first playback. + * Only enable caption when captions are turned on. + * First caption is already turned on in the code block below, so read it + * from activeTrackRef + */ + if (startCaptioned && activeTrackRef.current) { + textTracks.tracks_.filter(function (t) { + return t.label === activeTrackRef.current.label && t.language === activeTrackRef.current.language; + })[0].mode = 'showing'; + } + } + }); + } -/** - * Validate the text between 'WEBVTT' at the start and start of - * VTT cues. It looks for REGION and STYLE blocks and skips over these - * blocks. This doesn't validate the content within these blocks. - * When there's text in the header not followed by the keywords REGION and - * STYLE the WebVTT file is marked invalid. - * @param {Array} lines WebVTT file content split into lines - * @returns - */ -function validateWebVTTHeaders(lines) { - var endOfHeadersIndex = 0; - var firstCueIndex = 0; - var hasTextBeforeCues = false; - var notesInHeader = []; + // Turn first caption/subtitle ON and turn captions ON indicator via CSS on first load + if (((_textTracks$tracks_ = textTracks.tracks_) === null || _textTracks$tracks_ === void 0 ? void 0 : _textTracks$tracks_.length) > 0) { + var firstSubCap = null; + // Flag to identify first valid caption for resource + var onFirstCap = false; + // Disable all text tracks to avoid multiple selections and pick the first one as default + for (var i = 0; i < textTracks.tracks_.length; i++) { + var t = textTracks.tracks_[i]; + if ((t.kind === 'subtitles' || t.kind === 'captions') && t.language != '' && t.label != '') { + t.mode = 'disabled'; + if (!onFirstCap) firstSubCap = t; + onFirstCap = true; + } + } - // Remove line numbers for vtt cues - lines = lines.filter(function (l) { - return Number(l) ? false : true; - }); - for (var i = 0; i < lines.length; i++) { - var line = lines[i]; - // Skip REGION and STYLE blocks as these are related to displaying cues as overlays - if (/^REGION$/.test(line.toUpperCase()) || /^STYLE$/.test(line.toUpperCase())) { - // Increment until an empty line is encountered within the header block - i++; - while (i < lines.length && (!lines[i] == '\r' || !lines[i] == '\n' || !lines[i] == '\r\n')) { - i++; + // Enable the first caption when captions are enabled in the session + if (firstSubCap && startCaptioned) { + firstSubCap.mode = 'showing'; + activeTrackRef.current = firstSubCap; + handleCaptionChange(true); } - endOfHeadersIndex = i; } - // Gather comments presented as NOTE(s) in the header block to be displayed as transcript - else if (/^NOTE$/.test(line.toUpperCase())) { - var noteText = line; - i++; - // Increment until an empty line is encountered within the NOTE block - while (i < lines.length && (!lines[i] == '\r' || !lines[i] == '\n' || !lines[i] == '\r\n')) { - noteText = "".concat(noteText, "
").concat(lines[i].trim()); - i++; + + // Add/remove CSS to indicate captions/subtitles is turned on + textTracks.on('change', function () { + var trackModes = []; + for (var _i = 0; _i < textTracks.tracks_.length; _i++) { + var _textTracks$_i = textTracks[_i], + mode = _textTracks$_i.mode, + label = _textTracks$_i.label, + kind = _textTracks$_i.kind; + trackModes.push(textTracks[_i].mode); + if (mode === 'showing' && label != '' && (kind === 'subtitles' || kind === 'captions')) { + activeTrackRef.current = textTracks[_i]; + } } - notesInHeader.push({ - times: '', - line: noteText, - tag: TRANSCRIPT_CUE_TYPES.note - }); - } - // Terminate validation once the first cue is reached - else if (line.includes('-->')) { - // Break the loop when it reaches the first vtt cue - firstCueIndex = i; - break; + var subsOn = trackModes.includes('showing') ? true : false; + handleCaptionChange(subsOn); + setStartCaptioned(subsOn); + }); + }; + + /** + * Add CSS class to icon to indicate captions are on/off in player control bar + * @param {Boolean} subsOn flag to indicate captions are on/off + */ + var handleCaptionChange = function handleCaptionChange(subsOn) { + var player = playerRef.current; + /** + * When subsCapsButton is not setup on Video.js initialization step, and is + * later added in updatePlayer() function player.controlBar.getChild() method + * needs to be used to access it. + */ + var subsCapsBtn = player.controlBar.getChild('subsCapsButton'); + /* + For audio instances Video.js is setup to not to build the CC button + in Ramp's player control bar. + */ + if (subsCapsBtn == undefined || !subsCapsBtn || !(subsCapsBtn !== null && subsCapsBtn !== void 0 && subsCapsBtn.children_)) { + return; } - // Flag to check for invalid text before cue lines - else if (typeof line === 'string' && line.trim().length != 0) { - hasTextBeforeCues = true; + if (subsOn) { + subsCapsBtn.children_[0].addClass('captions-on'); + captionsOnRef.current = true; + } else { + subsCapsBtn.children_[0].removeClass('captions-on'); + captionsOnRef.current = false; } - } + }; + + /** + * Handle the 'ended' event fired by the player when a section comes to + * an end. If there are sections ahead move onto the next canvas and + * change the player and the state accordingly. + * Throttle helps to cancel the delayed function call triggered by ended event and + * load the correct item into the player, when the user clicks on a different item + * (not the next item in list) when the current item is coming to its end. + */ + var handleEnded = React.useMemo(function () { + return throttle_1(function () { + var isLastCanvas = cIndexRef.current === lastCanvasIndex; + /** + * Do nothing if Canvas is not multi-sourced AND autoAdvance is turned off + * OR current Canvas is the last Canvas in the Manifest + */ + if ((!autoAdvanceRef.current || isLastCanvas) && !hasMultiItems) { + return; + } else { + // Remove all the existing structure related markers in the player + if (playerRef.current && playerRef.current.markers) { + playerRef.current.pause(); + setFragmentMarker(null); + playerRef.current.markers.removeAll(); + } + if (hasMultiItems) { + // When there are multiple sources in a single canvas + // advance to next source + if (srcIndex + 1 < targets.length) { + manifestDispatch({ + srcIndex: srcIndex + 1, + type: 'setSrcIndex' + }); + playerDispatch({ + currentTime: 0, + type: 'setCurrentTime' + }); + playerRef.current.play(); + } else { + return; + } + } else if ((structures === null || structures === void 0 ? void 0 : structures.length) > 0) { + var nextItem = structures[cIndexRef.current + 1]; + if (nextItem) { + manifestDispatch({ + canvasIndex: cIndexRef.current + 1, + type: 'switchCanvas' + }); - // Return the cues and comments in the header block when the given WebVTT is valid - if (firstCueIndex > endOfHeadersIndex && !hasTextBeforeCues) { - return { - valid: true, - cue_lines: lines.slice(firstCueIndex), - notes: notesInHeader - }; - } else { - return { - valid: false - }; - } -} + // Reset startTime and currentTime to zero + playerDispatch({ + startTime: 0, + type: 'setTimeFragment' + }); + playerDispatch({ + currentTime: 0, + type: 'setCurrentTime' + }); -/** - * Group multi line transcript text values alongside the relevant - * timestamp values. E.g. converts, - * [ - * "00:00:00.000 --> 00:01:00.000", "Transcript", " from multiple lines", - * "00:03:00.000 --> 00:04:00.000", "Next transcript text", - * "NOTE This is a comment" - * ] - * into - * [ - * { times: "00:00:00.000 --> 00:01:00.000", line: "Transcript from multiple lines", tag: "TIMED_CUE" }, - * { times: "00:03:00.000 --> 00:04:00.000", line: "Next transcript text", tag: "TIMED_CUE" }, - * { times: "", line: "NOTE This is a comment", tag: "NOTE" } - * ] - * @param {Array} lines array of lines in the WebVTT file - * @returns {Array} - */ -function groupTimedTextLines(lines) { - var groups = []; - var i; - for (i = 0; i < lines.length; i++) { - var line = lines[i]; - var t = {}; - if (line.includes('-->') || /^NOTE/.test(line)) { - var isNote = /^NOTE/.test(line); - t.times = isNote ? "" : line; - t.tag = isNote ? TRANSCRIPT_CUE_TYPES.note : TRANSCRIPT_CUE_TYPES.timedCue; - // Make sure there is a single space separating NOTE from the comment for single or multi-line comments - t.line = isNote ? line.replace(/^NOTE\s*/, 'NOTE ') : ''; - i++; + // Get first timespan in the next canvas + var firstTimespanInNextCanvas = canvasSegments.filter(function (t) { + return t.canvasIndex === nextItem.canvasIndex && t.itemIndex === 1; + }); + // If the nextItem doesn't have an ID (a Canvas media fragment) pick the first timespan + // in the next Canvas + var nextFirstItem = nextItem.id != undefined ? nextItem : firstTimespanInNextCanvas[0]; + var start = 0; + if (nextFirstItem != undefined && nextFirstItem.id != undefined) { + start = getMediaFragment(nextFirstItem.id, canvasDuration).start; + } - // Increment until an empty line is encountered marking the end of the block - while (i < lines.length && !(lines[i] == '\r' || lines[i] == '\n' || lines[i] == '\r\n' || lines[i] == '')) { - t.line += lines[i].endsWith('-') ? lines[i] : lines[i].replace(/\s*$/, ' '); - i++; + // If there's a timespan item at the start of the next canvas + // mark it as the currentNavItem. Otherwise empty out the currentNavItem. + if (start === 0) { + manifestDispatch({ + item: nextFirstItem, + type: 'switchItem' + }); + } else if (nextFirstItem.isEmpty) { + // Switch the currentNavItem and clear isEnded flag + manifestDispatch({ + item: nextFirstItem, + type: 'switchItem' + }); + playerRef.current.currentTime(start); + // Only play if the next item is not an inaccessible item + if (!nextItem.isEmpty) playerRef.current.play(); + } + } + } + } + }); + }, [cIndexRef.current]); + + /** + * Handle the 'timeUpdate' event emitted by VideoJS player. + * The current time of the playhead used to show structure in the player's + * time rail as the playhead arrives at a start time of an existing structure + * item. When the current time is inside an item, that time fragment is highlighted + * in the player's time rail. + * Using throttle helps for smooth updates by cancelling and cleaning up intermediate + * delayed function calls. + */ + var handleTimeUpdate = React.useMemo(function () { + return throttle_1(function () { + var player = playerRef.current; + if (player && isReadyRef.current) { + var _player$currentTime; + var playerTime = (_player$currentTime = player.currentTime()) !== null && _player$currentTime !== void 0 ? _player$currentTime : currentTimeRef.current; + if (hasMultiItems && srcIndexRef.current > 0) { + playerTime = playerTime + targets[srcIndexRef.current].altStart; + } + var activeSegment = getActiveSegment(playerTime); + // the active segment has changed + if (activeIdRef.current !== (activeSegment === null || activeSegment === void 0 ? void 0 : activeSegment.id)) { + if (!activeSegment) { + /** + * Clear currentNavItem and other related state variables to update the tracker + * in structure navigation and highlights within the player. + */ + manifestDispatch({ + item: null, + type: 'switchItem' + }); + setActiveId(null); + setFragmentMarker(null); + } else { + // Set the active segment in state + manifestDispatch({ + item: activeSegment, + type: 'switchItem' + }); + setActiveId(activeSegment.id); + if (!isPlaylist && player.markers) { + var _getMediaFragment = getMediaFragment(activeSegment.id, activeSegment.canvasDuration), + start = _getMediaFragment.start, + end = _getMediaFragment.end; + playerDispatch({ + endTime: end, + startTime: start, + type: 'setTimeFragment' + }); + if (start !== end) { + // don't let marker extend past the end of the canvas + var markerEnd = end > activeSegment.canvasDuration ? activeSegment.canvasDuration : end; + setFragmentMarker({ + time: start, + duration: markerEnd - start, + text: start, + "class": 'ramp--track-marker--fragment' + }); + } else { + // to prevent zero duration fragments I suppose + setFragmentMarker(null); + } + } else if (fragmentMarker !== null) { + setFragmentMarker(null); + } + } + } + } + }, 10); + }, []); + + /** + * Toggle play/pause on video touch for mobile browsers + * @param {Object} e onTouchEnd event + */ + var mobilePlayToggle = function mobilePlayToggle(e) { + var player = playerRef.current; + if (e.changedTouches[0].clientX == touchX && e.changedTouches[0].clientY == touchY) { + if (player.paused()) { + player.play(); + } else { + player.pause(); } - t.line = t.line.trimEnd(); - groups.push(t); } - } - return groups; -} + }; -/** - * Create a JSON object from the transcript data - * @param {Object} obj - * @param {String} obj.times string with time information - * @param {String} obj.line string with transcript text - * @returns {Object} of the format; - * { - * begin: 0, - * end: 60, - * text: 'Transcript text sample', - * tag: NOTE || TIMED_CUE - * } - */ -function parseTimedTextLine(_ref, isSRT) { - var times = _ref.times, - line = _ref.line, - tag = _ref.tag; - var timestampRegex; - if (isSRT) { - // SRT allows using comma for milliseconds while WebVTT does not - timestampRegex = SRT_TIMESTAMP_REGEX; - } else { - timestampRegex = VTT_TIMESTAMP_REGEX; - } - switch (tag) { - case TRANSCRIPT_CUE_TYPES.note: - return { - begin: 0, - end: 0, - text: line, - tag: tag - }; - case TRANSCRIPT_CUE_TYPES.timedCue: - var _times$split = times.split(' --> '), - _times$split2 = _slicedToArray(_times$split, 2), - start = _times$split2[0], - end = _times$split2[1]; - // FIXME:: remove any styles for now, refine this - end = end.split(' ')[0]; - if (!start.match(timestampRegex) || !end.match(timestampRegex)) { - console.error('Invalid timestamp in line with text; ', line); - return null; + /** + * Save coordinates of touch start for comparison to touch end to prevent play/pause + * when user is scrolling. + * @param {Object} e onTouchStart event + */ + var touchX = null; + var touchY = null; + var saveTouchStartCoords = function saveTouchStartCoords(e) { + touchX = e.touches[0].clientX; + touchY = e.touches[0].clientY; + }; + + /** + * Get the segment, which encapsulates the current time of the playhead, + * from a list of media fragments in the current canvas. + * @param {Number} time playhead's current time + */ + var getActiveSegment = function getActiveSegment(time) { + if (isPlaylist) { + // For playlists timespans and canvasIdex are mapped one-to-one + return canvasSegments[cIndexRef.current]; + } else { + // Find the relevant media segment from the structure + var _iterator = _createForOfIteratorHelper$1(canvasSegments), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var segment = _step.value; + var id = segment.id, + isCanvas = segment.isCanvas, + _canvasIndex = segment.canvasIndex; + if (_canvasIndex == cIndexRef.current + 1) { + // Canvases without structure has the Canvas information + // in Canvas-level item as a navigable link + if (isCanvas) { + return segment; + } + var segmentRange = getMediaFragment(id, canvasDuration); + var isInRange = checkSrcRange(segmentRange, canvasDuration); + var isInSegment = time >= segmentRange.start && time < segmentRange.end; + if (isInSegment && isInRange) { + return segment; + } + } + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); } - return { - begin: timeToS(start), - end: timeToS(end), - text: line, - tag: tag - }; - default: return null; - } + } + }; + + /** + * Click event handler for previous/next buttons in inaccessible + * message display + * @param {Number} c updated Canvas index upon event trigger + */ + var handlePrevNextClick = function handlePrevNextClick(c) { + switchPlayer(c, true); + }; + + /** + * Keydown event handler for previou/next buttons in inaccessible + * message display. + * IMPORTANT: btnName param should be either 'nextBtn' or 'previousBtn' + * @param {Event} e keydown event + * @param {Number} c update Canvas index upon event trigger + * @param {String} btnName name of the pressed button + */ + var handlePrevNextKeydown = function handlePrevNextKeydown(e, c, btnName) { + if (e.which === 32 || e.which === 13) { + switchPlayer(c, true, btnName); + } + }; + return /*#__PURE__*/React__default["default"].createElement("div", null, /*#__PURE__*/React__default["default"].createElement("div", { + "data-vjs-player": true, + "data-canvasindex": cIndexRef.current + }, canvasIsEmptyRef.current && /*#__PURE__*/React__default["default"].createElement("div", { + "data-testid": "inaccessible-message-display" + // These styles needs to be inline for the poster to display within the Video boundaries + , + style: { + position: !playerRef.current ? 'relative' : 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + fontSize: 'medium', + textAlign: 'center', + color: '#fff', + backgroundColor: 'black', + zIndex: 101, + aspectRatio: !playerRef.current ? '16/9' : '' + } + }, /*#__PURE__*/React__default["default"].createElement("p", { + className: "ramp--media-player_inaccessible-message-content", + "data-testid": "inaccessible-message-content", + dangerouslySetInnerHTML: { + __html: placeholderText + } + }), /*#__PURE__*/React__default["default"].createElement("div", { + className: "ramp--media-player_inaccessible-message-buttons" + }, canvasIndex >= 1 && /*#__PURE__*/React__default["default"].createElement("button", { + "aria-label": "Go back to previous item", + onClick: function onClick() { + return handlePrevNextClick(canvasIndex - 1); + }, + onKeyDown: function onKeyDown(e) { + return handlePrevNextKeydown(e, canvasIndex - 1, 'previousBtn'); + }, + "data-testid": "inaccessible-previous-button" + }, /*#__PURE__*/React__default["default"].createElement(SectionButtonIcon, { + flip: true + }), " Previous"), canvasIndex != lastCanvasIndex && /*#__PURE__*/React__default["default"].createElement("button", { + "aria-label": "Go to next item", + onClick: function onClick() { + return handlePrevNextClick(canvasIndex + 1); + }, + onKeyDown: function onKeyDown(e) { + return handlePrevNextKeydown(e, canvasIndex + 1, 'nextBtn'); + }, + "data-testid": "inaccessible-next-button" + }, "Next ", /*#__PURE__*/React__default["default"].createElement(SectionButtonIcon, null))), canvasIndex != lastCanvasIndex && /*#__PURE__*/React__default["default"].createElement("p", { + "data-testid": "inaccessible-message-timer", + className: cx__default["default"]('ramp--media-player_inaccessible-message-timer', autoAdvanceRef.current ? '' : 'hidden') + }, "Next item in ".concat(messageTime, " second").concat(messageTime === 1 ? '' : 's'))), /*#__PURE__*/React__default["default"].createElement("video", { + "data-testid": "videojs-".concat(isVideo ? 'video' : 'audio', "-element"), + "data-canvasindex": cIndexRef.current, + ref: videoJSRef, + className: cx__default["default"]('video-js vjs-big-play-centered vjs-theme-ramp vjs-disabled', IS_ANDROID ? 'is-mobile' : ''), + onTouchStart: saveTouchStartCoords, + onTouchEnd: mobilePlayToggle, + style: { + display: "".concat(canvasIsEmptyRef.current ? 'none' : '') + } + })), (hasStructure || isPlaylist) && /*#__PURE__*/React__default["default"].createElement("div", { + className: "vjs-track-scrubber-container hidden", + ref: trackScrubberRef, + id: "track_scrubber" + }, /*#__PURE__*/React__default["default"].createElement("p", { + className: "vjs-time track-currenttime", + role: "presentation" + }), /*#__PURE__*/React__default["default"].createElement("span", { + type: "range", + "aria-label": "Track scrubber", + role: "slider", + tabIndex: 0, + className: "vjs-track-scrubber", + style: { + width: '100%' + } + }, !IS_TOUCH_ONLY && /*#__PURE__*/React__default["default"].createElement("span", { + className: "tooltiptext", + ref: scrubberTooltipRef, + "aria-hidden": true, + role: "presentation" + })), /*#__PURE__*/React__default["default"].createElement("p", { + className: "vjs-time track-duration", + role: "presentation" + }))); } +VideoJSPlayer.propTypes = { + enableFileDownload: PropTypes.bool, + enableTitleLink: PropTypes.bool, + isVideo: PropTypes.bool, + options: PropTypes.object, + placeholderText: PropTypes.string, + scrubberTooltipRef: PropTypes.object, + tracks: PropTypes.array, + trackScrubberRef: PropTypes.object, + videoJSLangMap: PropTypes.string, + withCredentials: PropTypes.bool +}; + +var Play = "Play"; +var Pause = "Pause"; +var Replay = "Replay"; +var Duration = "Duration"; +var LIVE = "LIVE"; +var Loaded = "Loaded"; +var Progress = "Progress"; +var Fullscreen = "Fullscreen"; +var Mute = "Mute"; +var Unmute = "Unmute"; +var Subtitles = "Subtitles"; +var Captions = "Captions"; +var Chapters = "Chapters"; +var Descriptions = "Descriptions"; +var Close = "Close"; +var Text = "Text"; +var White = "White"; +var Black = "Black"; +var Red = "Red"; +var Green = "Green"; +var Blue = "Blue"; +var Yellow = "Yellow"; +var Magenta = "Magenta"; +var Cyan = "Cyan"; +var Background = "Background"; +var Window = "Window"; +var Transparent = "Transparent"; +var Opaque = "Opaque"; +var None = "None"; +var Raised = "Raised"; +var Depressed = "Depressed"; +var Uniform = "Uniform"; +var Casual = "Casual"; +var Script = "Script"; +var Reset = "Reset"; +var Done = "Done"; +var Color = "Color"; +var Opacity = "Opacity"; +var en = { + "Audio Player": "Audio Player", + "Video Player": "Video Player", + Play: Play, + Pause: Pause, + Replay: Replay, + "Current Time": "Current Time", + Duration: Duration, + "Remaining Time": "Remaining Time", + "Stream Type": "Stream Type", + LIVE: LIVE, + "Seek to live, currently behind live": "Seek to live, currently behind live", + "Seek to live, currently playing live": "Seek to live, currently playing live", + Loaded: Loaded, + Progress: Progress, + "Progress Bar": "Progress Bar", + "progress bar timing: currentTime={1} duration={2}": "{1} of {2}", + Fullscreen: Fullscreen, + "Exit Fullscreen": "Exit Fullscreen", + Mute: Mute, + Unmute: Unmute, + "Playback Rate": "Playback Rate", + Subtitles: Subtitles, + "subtitles off": "subtitles off", + Captions: Captions, + "captions off": "captions off", + Chapters: Chapters, + Descriptions: Descriptions, + "descriptions off": "descriptions off", + "Audio Track": "Audio Track", + "Volume Level": "Volume Level", + "You aborted the media playback": "You aborted the media playback", + "A network error caused the media download to fail part-way.": "A network error caused the media download to fail part-way.", + "The media could not be loaded, either because the server or network failed or because the format is not supported.": "The media could not be loaded, either because the server or network failed or because the format is not supported.", + "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.", + "No compatible source was found for this media.": "No compatible source was found for this media.", + "The media is encrypted and we do not have the keys to decrypt it.": "The media is encrypted and we do not have the keys to decrypt it.", + "Play Video": "Play Video", + Close: Close, + "Close Modal Dialog": "Close Modal Dialog", + "Modal Window": "Modal Window", + "This is a modal window": "This is a modal window", + "This modal can be closed by pressing the Escape key or activating the close button.": "This modal can be closed by pressing the Escape key or activating the close button.", + ", opens captions settings dialog": ", opens captions settings dialog", + ", opens subtitles settings dialog": ", opens subtitles settings dialog", + ", opens descriptions settings dialog": ", opens descriptions settings dialog", + ", selected": ", selected", + "captions settings": "captions settings", + "subtitles settings": "subtitles settings", + "descriptions settings": "descriptions settings", + Text: Text, + White: White, + Black: Black, + Red: Red, + Green: Green, + Blue: Blue, + Yellow: Yellow, + Magenta: Magenta, + Cyan: Cyan, + Background: Background, + Window: Window, + Transparent: Transparent, + "Semi-Transparent": "Semi-Transparent", + Opaque: Opaque, + "Font Size": "Font Size", + "Text Edge Style": "Text Edge Style", + None: None, + Raised: Raised, + Depressed: Depressed, + Uniform: Uniform, + "Drop shadow": "Drop shadow", + "Font Family": "Font Family", + "Proportional Sans-Serif": "Proportional Sans-Serif", + "Monospace Sans-Serif": "Monospace Sans-Serif", + "Proportional Serif": "Proportional Serif", + "Monospace Serif": "Monospace Serif", + Casual: Casual, + Script: Script, + "Small Caps": "Small Caps", + Reset: Reset, + "restore all settings to the default values": "restore all settings to the default values", + Done: Done, + "Caption Settings Dialog": "Caption Settings Dialog", + "Beginning of dialog window. Escape will cancel and close the window.": "Beginning of dialog window. Escape will cancel and close the window.", + "End of dialog window.": "End of dialog window.", + "{1} is loading.": "{1} is loading.", + "Exit Picture-in-Picture": "Exit Picture-in-Picture", + "Picture-in-Picture": "Picture-in-Picture", + "No content": "No content", + Color: Color, + Opacity: Opacity, + "Text Background": "Text Background", + "Caption Area Background": "Caption Area Background", + "Playing in Picture-in-Picture": "Playing in Picture-in-Picture", + "Skip backward {1} seconds": "Skip backward {1} seconds", + "Skip forward {1} seconds": "Skip forward {1} seconds" +}; + +function ownKeys$3(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } +function _objectSpread$3(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$3(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$3(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } +var PLAYER_ID = "iiif-media-player"; /** - * Parse the content search response from the search service, and then use it to calculate - * number of search hits for each transcripts, and create a list of matched transcript - * lines for the search in the current transcript - * @param {Object} response JSON response from content search API - * @param {String} query search query from transcript search - * @param {Array} trancripts content of the displayed transcript with ids - * @param {String} selectedTranscript url of the selected transcript - * @returns a list of matched transcript lines for the current search + * Parse resource related information form the current canvas in manifest, + * and build an options object for Video.js using that information. + * @param {Object} props + * @param {Boolean} props.enableFileDownload + * @param {Boolean} props.enablePIP + * @param {Boolean} props.enablePlaybackRate + * @param {Boolean} props.enableTitleLink + * @param {Boolean} props.withCredentials + * @param {String} props.language */ -var parseContentSearchResponse = function parseContentSearchResponse(response, query, trancripts, selectedTranscript) { - var _response$items; - if (!response || response === undefined) return []; - var hitCounts = []; - var searchHits = []; - if (((_response$items = response.items) === null || _response$items === void 0 ? void 0 : _response$items.length) > 0) { - var items = response.items; - items.map(function (item) { - var anno = new manifesto_js.Annotation(item); - // Exclude annotations without supplementing motivation - if (anno.getMotivation() != 'supplementing') return; - var target = anno.getTarget(); - var targetURI = getCanvasId(target); - var value = anno.getBody()[0].getProperty('value'); - var hitCount = getHitCountForCue(value, query, true); - searchHits.push({ - target: target, - targetURI: targetURI, - value: value, - hitCount: hitCount - }); - }); - } - // Group search responses by transcript - var allSearchHits = groupBy(searchHits, 'targetURI'); +var MediaPlayer = function MediaPlayer(_ref) { + var _ref$enableFileDownlo = _ref.enableFileDownload, + enableFileDownload = _ref$enableFileDownlo === void 0 ? false : _ref$enableFileDownlo, + _ref$enablePIP = _ref.enablePIP, + enablePIP = _ref$enablePIP === void 0 ? false : _ref$enablePIP, + _ref$enablePlaybackRa = _ref.enablePlaybackRate, + enablePlaybackRate = _ref$enablePlaybackRa === void 0 ? false : _ref$enablePlaybackRa, + _ref$enableTitleLink = _ref.enableTitleLink, + enableTitleLink = _ref$enableTitleLink === void 0 ? false : _ref$enableTitleLink, + _ref$withCredentials = _ref.withCredentials, + withCredentials = _ref$withCredentials === void 0 ? false : _ref$withCredentials, + _ref$language = _ref.language, + language = _ref$language === void 0 ? 'en' : _ref$language; + var manifestState = useManifestState(); + var playerState = usePlayerState(); + var _useErrorBoundary = reactErrorBoundary.useErrorBoundary(), + showBoundary = _useErrorBoundary.showBoundary; + var srcIndex = manifestState.srcIndex, + hasStructure = manifestState.hasStructure, + playlist = manifestState.playlist; + var isPlaylist = playlist.isPlaylist; + playerState.playerFocusElement; + var currentTime = playerState.currentTime; + var trackScrubberRef = React.useRef(); + var timeToolRef = React.useRef(); + var videoJSLangMap = React.useRef('{}'); + var _useMediaPlayer = useMediaPlayer(), + canvasIsEmpty = _useMediaPlayer.canvasIsEmpty, + canvasIndex = _useMediaPlayer.canvasIndex, + isMultiCanvased = _useMediaPlayer.isMultiCanvased, + lastCanvasIndex = _useMediaPlayer.lastCanvasIndex; + var _useSetupPlayer = useSetupPlayer({ + enableFileDownload: enableFileDownload, + withCredentials: withCredentials, + lastCanvasIndex: lastCanvasIndex + }), + isMultiSourced = _useSetupPlayer.isMultiSourced, + isVideo = _useSetupPlayer.isVideo, + playerConfig = _useSetupPlayer.playerConfig, + ready = _useSetupPlayer.ready, + renderingFiles = _useSetupPlayer.renderingFiles, + nextItemClicked = _useSetupPlayer.nextItemClicked, + switchPlayer = _useSetupPlayer.switchPlayer; + var error = playerConfig.error, + poster = playerConfig.poster, + sources = playerConfig.sources, + targets = playerConfig.targets, + tracks = playerConfig.tracks; - // Calculate search hit count for each transcript in the Canvas - for (var _i = 0, _Object$entries = Object.entries(allSearchHits); _i < _Object$entries.length; _i++) { - var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), - key = _Object$entries$_i[0], - value = _Object$entries$_i[1]; - hitCounts.push({ - transcriptURL: key, - numberOfHits: value.reduce(function (acc, a) { - return acc + a.hitCount; - }, 0) + // Using dynamic imports to enforce code-splitting in webpack + // https://webpack.js.org/api/module-methods/#dynamic-expressions-in-import + var loadVideoJSLanguageMap = React.useMemo(function () { + return /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee() { + var resources; + return regenerator.wrap(function _callee$(_context) { + while (1) switch (_context.prev = _context.next) { + case 0: + _context.prev = 0; + _context.next = 3; + return (function (t) { return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require(t)); }); })("video.js/dist/lang/".concat(language, ".json")); + case 3: + resources = _context.sent; + videoJSLangMap.current = JSON.stringify(resources); + _context.next = 11; + break; + case 7: + _context.prev = 7; + _context.t0 = _context["catch"](0); + console.warn("".concat(language, " is not available, defaulting to English")); + videoJSLangMap.current = JSON.stringify(en); + case 11: + case "end": + return _context.stop(); + } + }, _callee, null, [[0, 7]]); + })); + }, [language]); + React.useEffect(function () { + try { + loadVideoJSLanguageMap(); + } catch (e) { + showBoundary(e); + } + }, []); + + // Default VideoJS options not updated with the Canvas data + var defaultOptions = React.useMemo(function () { + return { + autoplay: false, + id: PLAYER_ID, + playbackRates: enablePlaybackRate ? [0.5, 0.75, 1, 1.5, 2] : [], + experimentalSvgIcons: true, + controls: true, + fluid: true, + language: language, + // Setting inactivity timeout to zero in mobile and tablet devices translates to + // user is always active. And the control bar is not hidden when user is active. + // With this user can always use the controls when the media is playing. + inactivityTimeout: IS_MOBILE || IS_TOUCH_ONLY ? 0 : 2000, + // Enable native text track functionality in iPhones and iPads + html5: { + nativeTextTracks: IS_MOBILE && !IS_ANDROID + }, + // Make error display modal dismissable + errorDisplay: { + uncloseable: false + }, + /* + Setting this option helps to override VideoJS's default 'keydown' event handler, whenever + the focus is on a native VideoJS control icon (e.g. play toggle). + E.g. click event on 'playtoggle' sets the focus on the play/pause button, + which has VideoJS's 'handleKeydown' event handler attached to it. Therefore, as long as the + focus is on the play/pause button the 'keydown' event will pass through VideoJS's default + 'keydown' event handler, without ever reaching the 'keydown' handler setup on the document + in Ramp code. + When this option is setup VideoJS's 'handleKeydown' event handler passes the event to the + function setup under the 'hotkeys' option when the native player controls are focused. + In Safari, this works without using 'hotkeys' option, therefore only set this in other browsers. + */ + userActions: { + hotkeys: !IS_SAFARI ? function (e) { + playerHotKeys(e, this); + } : undefined + }, + videoJSTitleLink: enableTitleLink + }; + }, [language, enablePlaybackRate, enableTitleLink]); + + // Build VideoJS options for the current Canvas from defaultOptions + var videoJSOptions = React.useMemo(function () { + return !canvasIsEmpty ? _objectSpread$3(_objectSpread$3({}, defaultOptions), {}, { + aspectRatio: isVideo ? '16:9' : '1:0', + audioOnlyMode: !isVideo, + bigPlayButton: isVideo, + poster: isVideo ? poster : null, + controlBar: { + // Define and order control bar controls + // See https://docs.videojs.com/tutorial-components.html for options of what + // seem to be supported controls + children: [isMultiCanvased ? 'videoJSPreviousButton' : '', 'playToggle', isMultiCanvased ? 'videoJSNextButton' : '', 'videoJSProgress', 'videoJSCurrentTime', 'timeDivider', 'durationDisplay', + // These icons are in reverse order to support `float: inline-end` in CSS + 'fullscreenToggle', enableFileDownload ? 'videoJSFileDownload' : '', enablePIP ? 'pictureInPictureToggle' : '', enablePlaybackRate ? 'playbackRateMenuButton' : '', 'qualitySelector', hasStructure || isPlaylist ? 'videoJSTrackScrubber' : '', tracks.length > 0 && isVideo ? 'subsCapsButton' : '', IS_MOBILE ? 'muteToggle' : 'volumePanel' + // 'vjsYo', custom component + ], + + videoJSProgress: { + srcIndex: srcIndex, + targets: targets, + currentTime: currentTime !== null && currentTime !== void 0 ? currentTime : 0, + nextItemClicked: nextItemClicked + }, + videoJSCurrentTime: { + srcIndex: srcIndex, + targets: targets, + currentTime: currentTime || 0 + }, + videoJSFileDownload: enableFileDownload && { + title: 'Download Files', + controlText: 'Alternate resource download', + files: renderingFiles + }, + videoJSPreviousButton: isMultiCanvased && { + canvasIndex: canvasIndex, + switchPlayer: switchPlayer + }, + videoJSNextButton: isMultiCanvased && { + canvasIndex: canvasIndex, + lastCanvasIndex: lastCanvasIndex, + switchPlayer: switchPlayer + }, + videoJSTrackScrubber: (hasStructure || isPlaylist) && { + trackScrubberRef: trackScrubberRef, + timeToolRef: timeToolRef, + isPlaylist: isPlaylist + } + }, + sources: isMultiSourced ? [sources[srcIndex]] : sources + }) : _objectSpread$3(_objectSpread$3({}, defaultOptions), {}, { + sources: [] }); + }, [isVideo, playerConfig, srcIndex]); + if (ready && videoJSOptions != undefined || canvasIsEmpty) { + return /*#__PURE__*/React__default["default"].createElement("div", { + "data-testid": "media-player", + className: "ramp--media_player", + role: "presentation" + }, /*#__PURE__*/React__default["default"].createElement(VideoJSPlayer, { + enableFileDownload: enableFileDownload, + enableTitleLink: enableTitleLink, + isVideo: isVideo, + options: videoJSOptions, + placeholderText: error, + scrubberTooltipRef: timeToolRef, + tracks: tracks, + trackScrubberRef: trackScrubberRef, + videoJSLangMap: videoJSLangMap.current, + withCredentials: withCredentials + })); + } else { + return null; } - - // Get all the matching transcript lines with the query in the current transcript - var matchedTranscriptLines = getMatchedTranscriptLines(allSearchHits[selectedTranscript], query, trancripts); - return { - matchedTranscriptLines: matchedTranscriptLines, - hitCounts: hitCounts, - allSearchHits: allSearchHits - }; }; +MediaPlayer.propTypes = { + enableFileDownload: PropTypes.bool, + enablePIP: PropTypes.bool, + enablePlaybackRate: PropTypes.bool, + enableTitleLink: PropTypes.bool, + withCredentials: PropTypes.bool, + language: PropTypes.string +}; + +var _extends_1 = createCommonjsModule(function (module) { +function _extends() { + module.exports = _extends = Object.assign ? Object.assign.bind() : function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + return target; + }, module.exports.__esModule = true, module.exports["default"] = module.exports; + return _extends.apply(this, arguments); +} +module.exports = _extends, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var _extends = /*@__PURE__*/getDefaultExportFromCjs(_extends_1); /** - * Create a list matched transcript lines for the current search for the displayed transcript - * @param {Array} searchHits a list of matched transcript lines with ids from the current transcript - * @param {String} query search query - * @param {Array} transcripts list of all the transcript lines from the current transcript - * @returns a list of matched transcrip lines in the current transcript + * Build leaf-level nodes in the structures in Manifest. These nodes can be + * either timespans (with media fragment) or titles (w/o media fragment). + * @param {Object} props + * @param {Number} props.duration duration of the item + * @param {String} props.id media fragemnt of the item + * @param {Boolean} props.isTitle flag to indicate item w/o mediafragment + * @param {Boolean} props.isCanvas flag to indicate item is at Canvas-level + * @param {Boolean} props.isClickable flag to indicate item is within resource duration + * @param {Boolean} props.isEmpty flag to indicate Canvas associated with item is inaccessible + * @param {String} props.label text label of the item + * @param {String} props.summary summary associated with the item (in playlist context) + * @param {String} props.homepage homepage associated with the item (in playlist context) + * @param {Array} props.items list of children for the item + * @param {Number} props.itemIndex index of the item within the section/canvas + * @param {String} props.rangeId unique id of the item + * @param {Number} props.canvasDuration duration of the Canvas associated with the item + * @param {Object} props.sectionRef React ref of the section element associated with the item + * @param {Object} props.structureContainerRef React ref of the structure container */ -var getMatchedTranscriptLines = function getMatchedTranscriptLines(searchHits, query, transcripts) { - var qStr = query.trim().toLocaleLowerCase(); - var transcriptLines = []; - if (searchHits === undefined) return; - var traversedIds = []; - searchHits.map(function (item, index) { - var target = item.target, - value = item.value; - // Read time offsets and text of the search hit - var timeRange = getMediaFragment(target); - - // Replace all HTML tags - var mappedText = value.replace(/<\/?[^>]+>/gi, ''); - var start = 0, - end = 0; - var transcriptId = undefined; - if (timeRange != undefined) { - // For timed-text - start = timeRange.start; - end = timeRange.end; - transcriptId = transcripts.findIndex(function (t) { - return t.begin == start && t.end == end; - }); - var queryText = qStr.match(/[a-zA-Z]+/gi) ? qStr.match(/[a-zA-Z]+/gi)[0] : qStr; - var matchOffset = mappedText.toLocaleLowerCase().indexOf(queryText); - if (matchOffset !== -1 && transcriptId != undefined) { - var match = markMatchedParts(value, qStr, item.hitCount, true); - transcriptLines.push({ - tag: TRANSCRIPT_CUE_TYPES.timedCue, - begin: start, - end: end, - id: transcriptId, - match: match, - matchCount: item.hitCount, - text: value - }); - } - } else { - /** - * For non timed text, there's no unique id to match the search response to the transcript - * lines in the UI. So use filter() method instead of findIndex() method to get all matching - * transcript lines in the display. - * Use traversedIds array to remember the ids of already processed transcript lines in the list - * to avoid duplication in the matches. - */ - var hitsInfo = matchPartsInUntimedText(transcripts, mappedText, qStr, traversedIds); - traversedIds = hitsInfo.traversedIds; - transcriptLines = [].concat(_toConsumableArray(transcriptLines), _toConsumableArray(hitsInfo.hits)); +var ListItem = function ListItem(_ref) { + var duration = _ref.duration, + id = _ref.id, + isTitle = _ref.isTitle, + isCanvas = _ref.isCanvas, + isClickable = _ref.isClickable, + isEmpty = _ref.isEmpty, + label = _ref.label, + summary = _ref.summary, + homepage = _ref.homepage, + items = _ref.items, + itemIndex = _ref.itemIndex, + rangeId = _ref.rangeId, + canvasDuration = _ref.canvasDuration, + sectionRef = _ref.sectionRef, + structureContainerRef = _ref.structureContainerRef; + var liRef = React.useRef(null); + var _useActiveStructure = useActiveStructure({ + itemId: id, + liRef: liRef, + sectionRef: sectionRef, + isCanvas: isCanvas, + canvasDuration: canvasDuration + }), + handleClick = _useActiveStructure.handleClick, + isActiveLi = _useActiveStructure.isActiveLi, + currentNavItem = _useActiveStructure.currentNavItem, + isPlaylist = _useActiveStructure.isPlaylist; + var subMenu = items && items.length > 0 ? /*#__PURE__*/React__default["default"].createElement(List, { + items: items, + sectionRef: sectionRef, + structureContainerRef: structureContainerRef, + isPlaylist: isPlaylist + }) : null; - /** - * When backend has a single block of text which is chuncked in the UI this helps to - * traverse all transcript cues. - */ - while (index === searchHits.length - 1 && ((_traversedIds = traversedIds) === null || _traversedIds === void 0 ? void 0 : _traversedIds.length) < transcripts.length) { - var _traversedIds; - var _hitsInfo = matchPartsInUntimedText(transcripts, mappedText, qStr, traversedIds); - traversedIds = _hitsInfo.traversedIds; - transcriptLines = [].concat(_toConsumableArray(transcriptLines), _toConsumableArray(_hitsInfo.hits)); - } + /* + Auto-scroll active structure item into view only when user is not actively + interacting with structured navigation + */ + React.useEffect(function () { + if (liRef.current && (currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.id) == id && liRef.current.isClicked != undefined && !liRef.current.isClicked && structureContainerRef.current.isScrolling != undefined && !structureContainerRef.current.isScrolling) { + autoScroll(liRef.current, structureContainerRef); } - }); - return transcriptLines; + // Reset isClicked if active structure item is set + if (liRef.current) { + liRef.current.isClicked = false; + } + }, [currentNavItem]); + var renderListItem = function renderListItem() { + return /*#__PURE__*/React__default["default"].createElement(React.Fragment, { + key: rangeId + }, /*#__PURE__*/React__default["default"].createElement(React.Fragment, null, isTitle ? /*#__PURE__*/React__default["default"].createElement("span", { + className: "ramp--structured-nav__item-title", + role: "listitem", + "aria-label": label + }, label) : /*#__PURE__*/React__default["default"].createElement(React.Fragment, { + key: id + }, /*#__PURE__*/React__default["default"].createElement("div", { + className: "tracker" + }), isClickable ? /*#__PURE__*/React__default["default"].createElement(React.Fragment, null, isEmpty && /*#__PURE__*/React__default["default"].createElement(LockedSVGIcon, null), /*#__PURE__*/React__default["default"].createElement("a", { + role: "listitem", + href: homepage && homepage != '' ? homepage : id, + onClick: handleClick + }, "".concat(itemIndex, ". "), label, " ", duration.length > 0 ? " (".concat(duration, ")") : '')) : /*#__PURE__*/React__default["default"].createElement("span", { + role: "listitem", + "aria-label": label + }, label)))); + }; + if (label != '') { + return /*#__PURE__*/React__default["default"].createElement("li", { + "data-testid": "list-item", + ref: liRef, + className: cx__default["default"]('ramp--structured-nav__list-item', isActiveLi ? 'active' : ''), + "data-label": label, + "data-summary": summary + }, renderListItem(), subMenu); + } else { + return null; + } +}; +ListItem.propTypes = { + duration: PropTypes.string.isRequired, + id: PropTypes.string, + isTitle: PropTypes.bool.isRequired, + isCanvas: PropTypes.bool.isRequired, + isClickable: PropTypes.bool.isRequired, + isEmpty: PropTypes.bool.isRequired, + label: PropTypes.string.isRequired, + summary: PropTypes.string, + homepage: PropTypes.string, + items: PropTypes.array.isRequired, + itemIndex: PropTypes.number, + rangeId: PropTypes.string.isRequired, + canvasDuration: PropTypes.number.isRequired, + sectionRef: PropTypes.object.isRequired, + structureContainerRef: PropTypes.object.isRequired }; /** - * Build a list of matched indexed transcript lines from content search response. - * In Avalon, docx and plain text files are chunked by paragraphs seperated by 2 or - * more new line characters. So, depending on the way the file is formatted the search - * response could include chunks of the text or the full text. - * In the library (mammoth) used in Transcript component to display docx files; the text is chunked - * into paragraphs seperated by one or more new line characters. - * And the search response doesn't include any text styling in the docx files. Therefore the - * text with style information is reformatted to include text highlights from the search response. - * This function uses the search response to calculate the hit counts and mark them for each indexed transcript - * line in the front-end to get the correct counts. - * @param {Array} transcripts indexed transcript text in UI - * @param {String} mappedText matched text from content search - * @param {String} query search query entered by the user - * @param {Array} traversedIds already included transcript indices - * @returns a list of matched transcript lines + * Build Canvas level range items. When the range has child elements nested make it + * collapsible. + * @param {Object} props + * @param {Number} props.duration range duration + * @param {Boolean} props.hasChildren flag to indicate presence of child structure in range + * @param {String} props.itemId media fragment if associated with the range + * @param {Number} props.itemIndex index of the canvas in structures + * @param {Array} props.items list of children structure items in range + * @param {Boolean} props.isRoot flag to indicate root range on top of structures + * @param {String} props.label text label to be displayed + * @param {Object} props.sectionRef React ref of the section element associated with the item + * @param {Object} props.structureContainerRef React ref of the structure container */ -var matchPartsInUntimedText = function matchPartsInUntimedText(transcripts, mappedText, query, traversedIds) { - var escapedQ = buildRegexReadyText(query, true, false); - // Get hit counts for the current text, ignore matches with query preceded by - or ' - var qRegex = new RegExp(String.raw(_templateObject$1 || (_templateObject$1 = _taggedTemplateLiteral(["\b", "\b"], ["\\b", "\\b"])), escapedQ), 'gi'); - var matched = []; - // Start from the next cue after the last traveresed cue in the transcript - var lastTraversedId = traversedIds[traversedIds.length - 1] + 1 || 0; +var SectionHeading = function SectionHeading(_ref) { + var duration = _ref.duration, + _ref$hasChildren = _ref.hasChildren, + hasChildren = _ref$hasChildren === void 0 ? false : _ref$hasChildren, + itemId = _ref.itemId, + itemIndex = _ref.itemIndex, + items = _ref.items, + _ref$isRoot = _ref.isRoot, + isRoot = _ref$isRoot === void 0 ? false : _ref$isRoot, + label = _ref.label, + sectionRef = _ref.sectionRef, + structureContainerRef = _ref.structureContainerRef; + var _useState = React.useState(false), + _useState2 = _slicedToArray(_useState, 2), + isOpen = _useState2[0], + setIsOpen = _useState2[1]; + var toggleOpen = function toggleOpen(e) { + setIsOpen(!isOpen); + sectionRef.current.isOpen = true; + }; + var _useActiveStructure = useActiveStructure({ + itemIndex: itemIndex, + isRoot: isRoot, + itemId: itemId, + liRef: sectionRef, + sectionRef: sectionRef, + isCanvas: true, + canvasDuration: duration, + setIsOpen: setIsOpen + }), + isActiveSection = _useActiveStructure.isActiveSection, + canvasIndex = _useActiveStructure.canvasIndex, + handleClick = _useActiveStructure.handleClick, + isPlaylist = _useActiveStructure.isPlaylist; - /** - * For untimed text the search response text could be either, - * - mapped one to one with the cue text in Transcript component - * - include a part of the cue text in Transcript component - * When none of these work check if the cue text contains the search query - */ - for (var i = lastTraversedId; i < transcripts.length; i++) { - var t = transcripts[i]; - var cleanedText = t.text.replace(/<\/?[^>]+>/gi, '').trim(); - var matches = _toConsumableArray(cleanedText.matchAll(qRegex)); - var mappedTextCleaned = mappedText.trim(); - if (mappedTextCleaned == cleanedText || mappedTextCleaned.includes(cleanedText) && (matches === null || matches === void 0 ? void 0 : matches.length) > 0) { - t.matchCount = matches === null || matches === void 0 ? void 0 : matches.length; - matched.push(t); - traversedIds.push(t.id); - break; - } else if ((matches === null || matches === void 0 ? void 0 : matches.length) > 0) { - var _ref2; - t.matchCount = (_ref2 = _toConsumableArray(mappedTextCleaned.matchAll(qRegex))) === null || _ref2 === void 0 ? void 0 : _ref2.length; - matched.push(t); - traversedIds.push(t.id); - break; - } else { - traversedIds.push(t.id); + /* + Auto-scroll active section into view only when user is not + actively interacting with structured navigation + */ + React.useEffect(function () { + if (canvasIndex + 1 === itemIndex && sectionRef.current && sectionRef.current.isClicked != undefined && !sectionRef.current.isClicked && structureContainerRef.current.isScrolling != undefined && !structureContainerRef.current.isScrolling) { + autoScroll(sectionRef.current, structureContainerRef); } - } - var hits = []; - matched.map(function (m) { - var value = addStyledHighlights(m.textDisplayed, query); - var match = markMatchedParts(value, query, m.matchCount, true); - hits.push({ - tag: TRANSCRIPT_CUE_TYPES.nonTimedLine, - begin: undefined, - end: undefined, - id: m.id, - match: match, - matchCount: m.matchCount, - text: value - }); - }); - return { - hits: hits, - traversedIds: traversedIds + sectionRef.current.isClicked = false; + }, [canvasIndex]); + var collapsibleButton = function collapsibleButton() { + return /*#__PURE__*/React__default["default"].createElement("button", { + className: "collapse-expand-button", + "data-testid": "section-collapse-icon", + onClick: toggleOpen + }, /*#__PURE__*/React__default["default"].createElement("i", { + className: cx__default["default"]('arrow', isOpen ? 'up' : 'down') + })); }; + return /*#__PURE__*/React__default["default"].createElement("div", { + className: cx__default["default"]('ramp--structured-nav__section', isActiveSection ? 'active' : ''), + role: "listitem", + "data-testid": "listitem-section", + ref: sectionRef, + "data-label": label, + "data-mediafrag": itemId !== null && itemId !== void 0 ? itemId : '' + }, /*#__PURE__*/React__default["default"].createElement("div", { + className: "section-head-buttons" + }, /*#__PURE__*/React__default["default"].createElement("button", { + "data-testid": itemId == undefined ? "listitem-section-span" : "listitem-section-button", + ref: sectionRef, + onClick: handleClick, + className: cx__default["default"]('ramp--structured-nav__section-title', !itemId && 'not-clickable') + }, /*#__PURE__*/React__default["default"].createElement("span", { + className: "ramp--structured-nav__title", + "aria-label": label, + role: "listitem" + }, isRoot ? '' : "".concat(itemIndex, ". "), label, duration != '' && /*#__PURE__*/React__default["default"].createElement("span", { + className: "ramp--structured-nav__section-duration" + }, duration))), hasChildren && collapsibleButton()), isOpen && hasChildren && /*#__PURE__*/React__default["default"].createElement(List, { + items: items, + sectionRef: sectionRef, + key: itemId, + structureContainerRef: structureContainerRef, + isPlaylist: isPlaylist + })); +}; +SectionHeading.propTypes = { + itemIndex: PropTypes.number.isRequired, + canvasIndex: PropTypes.number, + duration: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + sectionRef: PropTypes.object.isRequired, + itemId: PropTypes.string, + isRoot: PropTypes.bool, + structureContainerRef: PropTypes.object.isRequired, + hasChildren: PropTypes.bool, + items: PropTypes.array }; /** - * Generic function to mark the matched transcript text in the cue where the output has - * surrounding the matched parts - * within the cue. - * @param {String} text matched transcript text/cue - * @param {String} query current search query - * @param {Numner} hitCount number of hits returned in the search response - * @param {Boolean} hasHighlight boolean flag to indicate text has tags - * @returns matched cue with HTML tags added for marking the hightlight + * Build a section of structure using a
    HTML element, this can represent + * a title Range with children or canvas Range with children. + * This element helps build the nested ranges in structures, as it is being + * called recuresively in ListItem and SectionHeading components. + * @param {Object} props + * @param {Array} props.items a list of structure items under a title/section + * @param {Object} props.sectionRef React ref of the current section/canvas + * @param {Object} props.structureContainerRef React ref of the parent container + * @param {Boolean} props.isPlaylist */ -var markMatchedParts = function markMatchedParts(text, query, hitCount) { - var hasHighlight = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; - if (text === undefined || !text) return; - var count = 0; - var replacerFn = function replacerFn(match) { - var cleanedMatch = match.replace(/<\/?[^>]+>/gi, ''); - // Only add highlights to search hits in the search response - if (count < hitCount) { - count++; - return "".concat(cleanedMatch, ""); +var List = function List(_ref) { + var items = _ref.items, + sectionRef = _ref.sectionRef, + structureContainerRef = _ref.structureContainerRef, + isPlaylist = _ref.isPlaylist; + var collapsibleContent = /*#__PURE__*/React__default["default"].createElement("ul", { + "data-testid": "list", + className: "ramp--structured-nav__list", + role: "presentation" + }, items.map(function (item, index) { + // Render canvas items as SectionHeadings in non-playlist contexts + if (item.isCanvas && !isPlaylist) { + var _item$items; + return /*#__PURE__*/React__default["default"].createElement(SectionHeading, { + key: "".concat(item.label, "-").concat(index), + itemIndex: index + 1, + duration: item.duration, + label: item.label, + sectionRef: sectionRef, + itemId: item.id, + isRoot: item.isRoot, + structureContainerRef: structureContainerRef, + hasChildren: ((_item$items = item.items) === null || _item$items === void 0 ? void 0 : _item$items.length) > 0, + items: item.items + }); } else { - return cleanedMatch; + return /*#__PURE__*/React__default["default"].createElement(ListItem, _extends({}, item, { + sectionRef: sectionRef, + key: index, + structureContainerRef: structureContainerRef + })); + } + })); + return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, collapsibleContent); +}; +List.propTypes = { + items: PropTypes.array.isRequired, + sectionRef: PropTypes.object.isRequired, + structureContainerRef: PropTypes.object.isRequired, + isPlaylist: PropTypes.bool.isRequired +}; + +function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } +function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } +function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } + +/** + * Parse structures property in the Manifest, and build UI as needed. + * For playlists: structures is displayed as a list of items. + * For all the other manifests: each Canvas Range is highlighted as a section in the + * display and their child elements are displayed in collapsible UI elements + * respectively. + */ +var StructuredNavigation = function StructuredNavigation() { + var _structureItemsRef$cu; + var manifestDispatch = useManifestDispatch(); + var playerDispatch = usePlayerDispatch(); + var _usePlayerState = usePlayerState(), + clickedUrl = _usePlayerState.clickedUrl, + isClicked = _usePlayerState.isClicked, + isPlaying = _usePlayerState.isPlaying, + player = _usePlayerState.player; + var _useManifestState = useManifestState(), + allCanvases = _useManifestState.allCanvases, + canvasDuration = _useManifestState.canvasDuration, + canvasIndex = _useManifestState.canvasIndex, + hasMultiItems = _useManifestState.hasMultiItems, + targets = _useManifestState.targets, + manifest = _useManifestState.manifest, + playlist = _useManifestState.playlist, + canvasIsEmpty = _useManifestState.canvasIsEmpty, + canvasSegments = _useManifestState.canvasSegments; + var _useErrorBoundary = reactErrorBoundary.useErrorBoundary(), + showBoundary = _useErrorBoundary.showBoundary; + var canvasStructRef = React.useRef(); + var structureItemsRef = React.useRef(); + var canvasIsEmptyRef = React.useRef(canvasIsEmpty); + var hasRootRangeRef = React.useRef(false); + var structureContainerRef = React.useRef(); + var scrollableStructure = React.useRef(); + React.useEffect(function () { + // Update currentTime and canvasIndex in state if a + // custom start time and(or) canvas is given in manifest + if (manifest) { + try { + var _getStructureRanges = getStructureRanges(manifest, allCanvases, playlist.isPlaylist), + structures = _getStructureRanges.structures, + timespans = _getStructureRanges.timespans, + markRoot = _getStructureRanges.markRoot; + structureItemsRef.current = structures; + canvasStructRef.current = structures; + hasRootRangeRef.current = markRoot; + // Remove root-level structure item from navigation calculations + if ((structures === null || structures === void 0 ? void 0 : structures.length) > 0 && structures[0].isRoot) { + canvasStructRef.current = structures[0].items; + } + manifestDispatch({ + structures: canvasStructRef.current, + type: 'setStructures' + }); + manifestDispatch({ + timespans: timespans, + type: 'setCanvasSegments' + }); + structureContainerRef.current.isScrolling = false; + } catch (error) { + showBoundary(error); + } } - }; - var queryFormatted = query; - /** - * Content search response for a phrase search like 'Mr. Bungle' gives the response - * with highlights in the matched text as Mr. Bungle. - * So reconstruct the search query in the UI to match this phrase in the response. - */ - if (hasHighlight) { - queryFormatted = buildRegexReadyText(query); - } + }, [manifest]); - /** - * Content search API returns cues including "Mr. Bungle" as matches for both search queries - * "mr bungle" and "mr. bungle". - * When "mr bungle" is searched this function handles highlighting since the regex fails to - * identify the matches in the cues. - */ - var altReplace = function altReplace() { - var matches = _toConsumableArray(text.matchAll(/<\/?[^>]+>/gi)); - if ((matches === null || matches === void 0 ? void 0 : matches.length) === 0) return; - var startIndex = 0; - var newStr = ''; - for (var j = 0; j < matches.length && count < hitCount;) { - // Set offset to count matches based on the # of words in the phrase search query - var splitQ = query.split(/[\s-,\?]/); - var offset = (splitQ === null || splitQ === void 0 ? void 0 : splitQ.length) > 0 ? (splitQ === null || splitQ === void 0 ? void 0 : splitQ.length) * 2 - 1 : 1; - if (matches[j] === undefined && matches[j + offset] === undefined) return; + // Set currentNavItem when current Canvas is an inaccessible/empty item + React.useEffect(function () { + if (canvasIsEmpty && playlist.isPlaylist) { + manifestDispatch({ + item: canvasSegments[canvasIndex], + type: 'switchItem' + }); + } + }, [canvasIsEmpty, canvasIndex]); + React.useEffect(function () { + if (isClicked) { + var clickedItem = canvasSegments.filter(function (c) { + return c.id === clickedUrl; + }); + if ((clickedItem === null || clickedItem === void 0 ? void 0 : clickedItem.length) > 0) { + // Only update the current nav item for timespans + // Eliminate Canvas level items unless the structure is empty + var _clickedItem$ = clickedItem[0], + isCanvas = _clickedItem$.isCanvas, + items = _clickedItem$.items; + if (!isCanvas || items.length == 0 && isCanvas) { + manifestDispatch({ + item: clickedItem[0], + type: 'switchItem' + }); + } + } + var currentCanvasIndex = allCanvases.findIndex(function (c) { + return c.canvasURL === getCanvasId(clickedUrl); + }); + var timeFragment = getMediaFragment(clickedUrl, canvasDuration); - // Indices of start and end of the highlighted text including tags - var firstIndex = matches[j].index; - var lastIndex = matches[j + offset].index + matches[j + offset][0].length; - var prefix = text.slice(startIndex, firstIndex); - var cleanedMatch = text.slice(firstIndex, lastIndex).replace(/<\/?[^>]+>/gi, ''); - newStr = "".concat(newStr).concat(prefix, "").concat(cleanedMatch, ""); - startIndex = lastIndex; - j = +(offset + 1); - count++; - if (j == matches.length) { - newStr = "".concat(newStr).concat(text.slice(startIndex)); + // Invalid time fragment + if (!timeFragment || timeFragment == undefined) { + console.error('StructuredNavigation -> invalid media fragment in structure item -> ', timeFragment); + return; + } + var timeFragmentStart = timeFragment.start; + if (hasMultiItems) { + var _getCanvasTarget = getCanvasTarget(targets, timeFragment, canvasDuration), + srcIndex = _getCanvasTarget.srcIndex, + fragmentStart = _getCanvasTarget.fragmentStart; + timeFragmentStart = fragmentStart; + manifestDispatch({ + srcIndex: srcIndex, + type: 'setSrcIndex' + }); + } else { + // When clicked structure item is not in the current canvas + if (canvasIndex != currentCanvasIndex && currentCanvasIndex > -1) { + manifestDispatch({ + canvasIndex: currentCanvasIndex, + type: 'switchCanvas' + }); + canvasIsEmptyRef.current = canvasStructRef.current[currentCanvasIndex].isEmpty; + } + } + if (player && !canvasIsEmptyRef.current) { + player.currentTime(timeFragmentStart); + playerDispatch({ + startTime: timeFragment.start, + endTime: timeFragment.end, + type: 'setTimeFragment' + }); + + // Use this value in iOS to set the initial progress + // in the custom progress bar + player.structStart = timeFragmentStart; + playerDispatch({ + currentTime: timeFragmentStart, + type: 'setCurrentTime' + }); + // Setting userActive to true shows timerail breifly, helps + // to visualize the structure in player while playing + if (isPlaying) player.userActive(true); + } else if (canvasIsEmptyRef.current) { + // Reset isClicked in state for + // inaccessible items (empty canvases) + playerDispatch({ + type: 'resetClick' + }); } } - return newStr; - }; - try { - var _ref3; - var queryRegex = new RegExp(String.raw(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["", ""])), queryFormatted), 'gi'); - if (((_ref3 = _toConsumableArray(text.matchAll(queryRegex))) === null || _ref3 === void 0 ? void 0 : _ref3.length) === 0) { - var highlighted = altReplace(); - return highlighted; - } else { - return text.replace(queryRegex, replacerFn); + }, [isClicked, player]); + + // Structured nav is populated by the time the player hook fires so we listen for + // that to run the check on whether the structured nav is scrollable. + React.useEffect(function () { + if (structureContainerRef.current) { + var elem = structureContainerRef.current; + var structureBorder = structureContainerRef.current.parentElement; + var structureEnd = Math.abs(elem.scrollHeight - (elem.scrollTop + elem.clientHeight)) <= 1; + scrollableStructure.current = !structureEnd; + if (structureBorder) { + resizeObserver.observe(structureBorder); + } } - } catch (e) { - console.log('Error building RegExp for query: ', query); - } -}; + }, [player]); -/** - * For docx files the content search response text doesn't have the formatted - * styles in the Word document (e.g. bold text wrapped in tags). So, - * use the styled text formatted with mammoth in the UI to add highlights from - * the content search response. - * @param {String} text string to be formatted - * @param {String} query string to find and replace with tags - * @returns a string formatted with highlights - */ -var addStyledHighlights = function addStyledHighlights(text, query) { - if (text === undefined || !text) return; - var replacerFn = function replacerFn(match) { - var cleanedMatch = buildRegexReadyText(match, false, true); - return cleanedMatch; + // Update scrolling indicators when end of scrolling has been reached + var handleScrollable = function handleScrollable(e) { + var elem = e.target; + if (elem.classList.contains('ramp--structured-nav__border')) { + elem = elem.firstChild; + } + var scrollMsg = elem.nextSibling; + var structureEnd = Math.abs(elem.scrollHeight - (elem.scrollTop + elem.clientHeight)) <= 1; + if (elem && structureEnd && elem.classList.contains('scrollable')) { + elem.classList.remove('scrollable'); + } else if (elem && !structureEnd && !elem.classList.contains('scrollable')) { + elem.classList.add('scrollable'); + } + if (scrollMsg && structureEnd && scrollMsg.classList.contains('scrollable')) { + scrollMsg.classList.remove('scrollable'); + } else if (scrollMsg && !structureEnd && !scrollMsg.classList.contains('scrollable')) { + scrollMsg.classList.add('scrollable'); + } }; - // Regex to get matches in the text while ignoring matches with query preceded by - or ' - var queryregex = new RegExp(String.raw(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["\b", "\b"], ["\\b", "\\b"])), buildRegexReadyText(query, true, false)), 'gi'); - var styled = text.replace(queryregex, replacerFn); - return styled; -}; - -/** - * Format a given string by escaping punctuations characters and grouping - * punctuations and text, to make it feasible to be used to build a regular - * expression accurately. - * @param {String} text string to be formatted with hightlights - * @param {Boolean} regExpReady flag to indicate the usage of the output as a regular exp - * @param {Boolean} addHightlight flag to indicate to/not to add tags - * @returns string with tags - */ -var buildRegexReadyText = function buildRegexReadyText(text) { - var regExpReady = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - var addHightlight = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; - // Text matches in the string - var matches = _toConsumableArray(text.matchAll(/[a-zA-Z']+/gi)); - // Punctuation matches in the string - var punctuationMatches = _toConsumableArray(text.matchAll(/([.+?"^${}\-|[\]\\])/g)); - - /** - * If no punctuations are found within the text return text with highlights - * For RegExp ready strings: ignore matches followed by - or ' - * e.g. omit matches as "Bungle's" when search query is "bungle" - */ - if ((punctuationMatches === null || punctuationMatches === void 0 ? void 0 : punctuationMatches.length) === 0) { - var textFormatted = addHightlight ? text.split(' ').map(function (t) { - return "".concat(t, ""); - }).join(' ') : text; - var textRegex = regExpReady ? "".concat(textFormatted, "(?!['w*])") : textFormatted; - return textRegex; - } - var highlighted = ''; - var startIndex = 0; - var i = 0; - while (i < matches.length) { - var match = matches[i]; - var textMatch = addHightlight ? "".concat(match[0], "") : match[0]; - /** - * When build RegExp ready string with punctuation blocks in the given string; - * - use * quantifier for blocks either at the start/end of the string to match zero or more times - * - use + quantifier for blocks in the middle of the string to match one or more times - * This pattern is build according the response from the content search API results. - */ - var punctMatch = startIndex === 0 ? "(".concat(text.slice(startIndex, match.index), ")*") : "(".concat(text.slice(startIndex, match.index), ")+"); - highlighted = regExpReady ? "".concat(highlighted).concat(punctMatch, "(").concat(textMatch, ")") : "".concat(highlighted).concat(text.slice(startIndex, match.index)).concat(textMatch); - startIndex = match.index + match[0].length; - if (i === (matches === null || matches === void 0 ? void 0 : matches.length) - 1) { - highlighted = regExpReady ? "".concat(highlighted, "(").concat(text.slice(startIndex), ")*") : "".concat(highlighted).concat(text.slice(startIndex)); + // Update scrolling indicators when structured nav is resized + var resizeObserver = new ResizeObserver(function (entries) { + var _iterator = _createForOfIteratorHelper(entries), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var entry = _step.value; + handleScrollable(entry); + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); } - i++; + }); + if (!manifest) { + return /*#__PURE__*/React__default["default"].createElement("p", null, "No manifest - Please provide a valid manifest."); } - // Escape punctuation characters in string for RegExp ready strings - var escapePunctuation = function escapePunctuation(str) { - var punctuationRegex = /([.?^${}|[\]\\])/g; - return str.replace(punctuationRegex, '\\$1'); + /** + * Update isScrolling flag within structure container ref, which is + * used by ListItem and SectionHeading components to decide to/not to + * auto scroll the content + * @param {Boolean} state + */ + var handleMouseOver = function handleMouseOver(state) { + structureContainerRef.current.isScrolling = state; }; - return regExpReady ? escapePunctuation(highlighted) : highlighted; + return /*#__PURE__*/React__default["default"].createElement("div", { + className: "ramp--structured-nav" + }, /*#__PURE__*/React__default["default"].createElement("div", { + className: "ramp--structured-nav__border" + }, /*#__PURE__*/React__default["default"].createElement("div", { + "data-testid": "structured-nav", + className: cx__default["default"]('ramp--structured-nav__content', scrollableStructure.current && 'scrollable', (playlist === null || playlist === void 0 ? void 0 : playlist.isPlaylist) && 'playlist-items', hasRootRangeRef.current && 'ramp--structured-nav__content-with_root'), + ref: structureContainerRef, + role: "list", + "aria-label": "Structural content", + onScroll: handleScrollable, + onMouseLeave: function onMouseLeave() { + return handleMouseOver(false); + }, + onMouseOver: function onMouseOver() { + return handleMouseOver(true); + } + }, ((_structureItemsRef$cu = structureItemsRef.current) === null || _structureItemsRef$cu === void 0 ? void 0 : _structureItemsRef$cu.length) > 0 ? structureItemsRef.current.map(function (item, index) { + var _item$items; + return ( + /* For playlist views omit the accordion style display of + structure for canvas-level items */ + item.isCanvas && !playlist.isPlaylist ? /*#__PURE__*/React__default["default"].createElement(SectionHeading, { + key: "".concat(item.label, "-").concat(index), + itemIndex: index + 1, + duration: item.duration, + label: item.label, + sectionRef: /*#__PURE__*/React.createRef(), + itemId: item.id, + isRoot: item.isRoot, + structureContainerRef: structureContainerRef, + hasChildren: ((_item$items = item.items) === null || _item$items === void 0 ? void 0 : _item$items.length) > 0, + items: item.items + }) : /*#__PURE__*/React__default["default"].createElement(List, { + items: [item], + sectionRef: /*#__PURE__*/React.createRef(), + key: "".concat(item.label, "-").concat(index), + structureContainerRef: structureContainerRef, + isPlaylist: playlist.isPlaylist + }) + ); + }) : /*#__PURE__*/React__default["default"].createElement("p", { + className: "ramp--no-structure" + }, "There are no structures in the manifest")), /*#__PURE__*/React__default["default"].createElement("span", { + className: cx__default["default"](scrollableStructure.current && 'scrollable') + }, "Scroll to see more"))); }; +StructuredNavigation.propTypes = {}; -/** - * Calculate hit counts for each matched transcript cue - * @param {String} text matched transcript cue text - * @param {String} query search query from UI - * @param {Boolean} hasHighlight flag indicating has tags or not - * @returns - */ -var getHitCountForCue = function getHitCountForCue(text, query) { - var _ref4; - var hasHighlight = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; - /* - Content search API highlights each word in the given phrase in the response. - Threfore, use first word in the query seperated by a white space to get the hit - counts for each cue. - Use regex with any punctuation followed by a white space to split the query. - e.g. query: Mr. bungle => search response: Mr. Bungle - */ - var partialQ = query.split(/[\s.,!?;:]/)[0]; - var cleanedPartialQ = partialQ.replace(/[\[\]\-]/gi, ''); - var hitTerm = hasHighlight ? buildRegexReadyText(partialQ) : cleanedPartialQ; - var highlightedTerm = new RegExp(String.raw(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral(["", ""])), hitTerm), 'gi'); - var hitCount = (_ref4 = _toConsumableArray(text.matchAll(highlightedTerm))) === null || _ref4 === void 0 ? void 0 : _ref4.length; - return hitCount; -}; +var objectWithoutPropertiesLoose = createCommonjsModule(function (module) { +function _objectWithoutPropertiesLoose(source, excluded) { + if (source == null) return {}; + var target = {}; + var sourceKeys = Object.keys(source); + var key, i; + for (i = 0; i < sourceKeys.length; i++) { + key = sourceKeys[i]; + if (excluded.indexOf(key) >= 0) continue; + target[key] = source[key]; + } + return target; +} +module.exports = _objectWithoutPropertiesLoose, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); -// TODO:: Could be used for marking search hits in Word Doc transcripts? -var splitIntoElements = function splitIntoElements(htmlContent) { - // Create a temporary DOM element to parse the HTML - var tempDiv = document.createElement('div'); - tempDiv.innerHTML = htmlContent; +var objectWithoutProperties = createCommonjsModule(function (module) { +function _objectWithoutProperties(source, excluded) { + if (source == null) return {}; + var target = objectWithoutPropertiesLoose(source, excluded); + var key, i; + if (Object.getOwnPropertySymbols) { + var sourceSymbolKeys = Object.getOwnPropertySymbols(source); + for (i = 0; i < sourceSymbolKeys.length; i++) { + key = sourceSymbolKeys[i]; + if (excluded.indexOf(key) >= 0) continue; + if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; + target[key] = source[key]; + } + } + return target; +} +module.exports = _objectWithoutProperties, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); - // Convert child nodes into an array - var elements = buildNonTimedText(Array.from(tempDiv.childNodes), true); - return elements; -}; +var _objectWithoutProperties = /*@__PURE__*/getDefaultExportFromCjs(objectWithoutProperties); /** - * Build non-timed transcript text content chunks into a JSON array - * with relevant information for display. These are then used by - * search module to convert the transcript content into an index. - * @param {Array} cues a list of trascript cues - * @param {Boolean} isHTML flag to detect inlined HTML in cues - * @returns a list of JSON objects for each cue + * Build the file download button for the displayed transcript file + * in the transcript viewer. + * @param {Object} props + * @param {String} fileUrl downloadable link to the file in server + * @param {String} fileName + * @param {Boolean} machineGenerated set to true for machine generated files + * @param {String} fileExt extension of the file */ -var buildNonTimedText = function buildNonTimedText(cues) { - var isHTML = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - var indexedCues = []; - cues.map(function (c) { - indexedCues.push({ - text: isHTML ? c.innerText : c, - tag: TRANSCRIPT_CUE_TYPES.nonTimedLine, - textDisplayed: isHTML ? lib.decode(c.innerHTML) : c - }); - }); - return indexedCues; -}; - var TranscriptDownloader = function TranscriptDownloader(_ref) { var fileUrl = _ref.fileUrl, fileName = _ref.fileName, @@ -10389,6 +11137,14 @@ TranscriptDownloader.propTypes = { fileExt: PropTypes.string }; +/** + * Build seletor and downloader for transcripts in the current Canvas + * @param {Object} props + * @param {Function} props.selectTranscript callback func to update transcript selection + * @param {Array} props.transcriptData list of the information for each transcirpt in the Canvas + * @param {Object} props.transcriptInfo information of the selected transcript + * @param {Boolean} props.noTranscript flag to indicate unsupported transcript selection + */ var TranscriptSelector = function TranscriptSelector(_ref) { var selectTranscript = _ref.selectTranscript, transcriptData = _ref.transcriptData, @@ -10445,8 +11201,17 @@ TranscriptSelector.propTypes = { }).isRequired, noTranscript: PropTypes.bool.isRequired }; -var TranscriptSelector$1 = /*#__PURE__*/React__default["default"].memo(TranscriptSelector); +var TranscriptSelector$1 = /*#__PURE__*/React.memo(TranscriptSelector); +/** + * Build search within UI in the transcript search and handle user queries + * @param {Object} props + * @param {Object} props.searchResults result set from the current search + * @param {String} props.searchQuery search query entered by the user + * @param {Number} props.focusedMatchIndex index of the focused the search hit + * @param {Function} props.setFocusedMatchIndex callback func to update focused match in search hits + * @param {Function} props.setSearchQuery callback func to set search query + */ var TranscriptSearch = function TranscriptSearch(_ref) { var searchResults = _ref.searchResults, _ref$searchQuery = _ref.searchQuery, @@ -10551,6 +11316,21 @@ var _excluded$1 = ["showSearch", "setAutoScrollEnabled", "autoScrollEnabled", "s function ownKeys$2(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread$2(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$2(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$2(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } var MACHINE_GEN_MESSAGE = 'Machine-generated transcript may contain errors.'; + +/** + * Build menu for the displaying transcript search, search navigation, + * and transcript selector + * @param {Object} props + * @param {Boolean} props.showSearch show/hide search UI + * @param {Function} props.setAutoScrollEnabled callback func to change auto-scroll preference + * @param {Boolean} props.autoScrollEnabled flag to indicate auto-scroll transcript check + * @param {String} props.searchQuery user entered search query + * @param {Function} props.setSearchQuery callback func to update search query + * @param {Object} props.searchResults result set from the current search + * @param {Number} props.focusedMatchIndex index of the focused search hit + * @param {Function} props.setFocusedMatchIndex callback func to update focused search hit with + * search navigation + */ var TranscriptMenu = function TranscriptMenu(_ref) { var showSearch = _ref.showSearch, setAutoScrollEnabled = _ref.setAutoScrollEnabled, @@ -11038,11 +11818,6 @@ var useFocusedMatch = function useFocusedMatch(_ref6) { var _excluded = ["initialSearchQuery"]; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } -var NO_TRANSCRIPTS_MSG = 'No valid Transcript(s) found, please check again.'; -var INVALID_URL_MSG = 'Invalid URL for transcript, please check again.'; -var INVALID_VTT = 'Invalid WebVTT file, please check again.'; -var INVALID_TIMESTAMP = 'Invalid timestamp format in cue(s), please check again.'; -var NO_SUPPORT = 'Transcript format is not supported, please check again.'; var buildSpeakerText = function buildSpeakerText(item) { var isDocx = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var text = isDocx ? item.textDisplayed : item.text; @@ -11055,7 +11830,7 @@ var buildSpeakerText = function buildSpeakerText(item) { return text; } }; -var TranscriptLine = /*#__PURE__*/React__default["default"].memo(function (_ref) { +var TranscriptLine = /*#__PURE__*/React.memo(function (_ref) { var item = _ref.item, goToItem = _ref.goToItem, isActive = _ref.isActive, @@ -11066,17 +11841,17 @@ var TranscriptLine = /*#__PURE__*/React__default["default"].memo(function (_ref) transcriptContainerRef = _ref.transcriptContainerRef, isNonTimedText = _ref.isNonTimedText, focusedMatchIndex = _ref.focusedMatchIndex; - var itemRef = React__default["default"].useRef(null); + var itemRef = React.useRef(null); var isFocused = item.id === focusedMatchId; - var wasFocusedRef = React__default["default"].useRef(isFocused); - var wasActiveRef = React__default["default"].useRef(isActive); + var wasFocusedRef = React.useRef(isFocused); + var wasActiveRef = React.useRef(isActive); // React ref to store previous focusedMatchIndex - var prevFocusedIndexRef = React__default["default"].useRef(-1); + var prevFocusedIndexRef = React.useRef(-1); // React ref to store previous focusedMatchId - var prevFocusedIdRef = React__default["default"].useRef(-1); + var prevFocusedIdRef = React.useRef(-1); // React ref to iterate through multiple hits within a focused cue - var activeRelativeCountRef = React__default["default"].useRef(0); - React__default["default"].useEffect(function () { + var activeRelativeCountRef = React.useRef(0); + React.useEffect(function () { var doScroll = false; var prevFocused = prevFocusedIdRef.current; if (isActive && !wasActiveRef.current) { @@ -11110,7 +11885,7 @@ var TranscriptLine = /*#__PURE__*/React__default["default"].memo(function (_ref) * Add a border highlight to the current focused search hit when using search * result navigation, when there are multiple hits within a focused cue */ - React__default["default"].useEffect(function () { + React.useEffect(function () { if (itemRef.current && isFocused) { // Find all highlights within the focused cue var highlights = itemRef.current.querySelectorAll('.ramp--transcript_highlight'); @@ -11194,7 +11969,7 @@ var TranscriptLine = /*#__PURE__*/React__default["default"].memo(function (_ref) return null; } }); -var TranscriptList = /*#__PURE__*/React__default["default"].memo(function (_ref2) { +var TranscriptList = /*#__PURE__*/React.memo(function (_ref2) { var seekPlayer = _ref2.seekPlayer, currentTime = _ref2.currentTime, searchResults = _ref2.searchResults, @@ -11205,11 +11980,11 @@ var TranscriptList = /*#__PURE__*/React__default["default"].memo(function (_ref2 showNotes = _ref2.showNotes, transcriptContainerRef = _ref2.transcriptContainerRef, focusedMatchIndex = _ref2.focusedMatchIndex; - var _React$useState = React__default["default"].useState(null), - _React$useState2 = _slicedToArray(_React$useState, 2), - manuallyActivatedItemId = _React$useState2[0], - setManuallyActivatedItem = _React$useState2[1]; - var goToItem = React__default["default"].useCallback(function (item) { + var _useState = React.useState(null), + _useState2 = _slicedToArray(_useState, 2), + manuallyActivatedItemId = _useState2[0], + setManuallyActivatedItem = _useState2[1]; + var goToItem = React.useCallback(function (item) { if (typeof item.begin === 'number') { seekPlayer(item.begin); setManuallyActivatedItem(null); @@ -11262,11 +12037,14 @@ var TranscriptList = /*#__PURE__*/React__default["default"].memo(function (_ref2 }); /** - * - * @param {String} param0 ID of the HTML element for the player on page - * @param {String} param1 manifest URL to read transcripts from - * @param {Object} param2 transcripts resource - * @returns + * Parse and display transcript content for the current Canvas. + * @param {Object} props + * @param {String} props.playerID + * @param {String} props.manifestUrl + * @param {Boolean} props.showNotes + * @param {Object} props.showNotes + * @param {Object} props.search + * @param {Array} props.transcripts */ var Transcript = function Transcript(_ref3) { var playerID = _ref3.playerID, @@ -11277,361 +12055,79 @@ var Transcript = function Transcript(_ref3) { search = _ref3$search === void 0 ? {} : _ref3$search, _ref3$transcripts = _ref3.transcripts, transcripts = _ref3$transcripts === void 0 ? [] : _ref3$transcripts; - var _React$useState3 = React__default["default"].useState([]), - _React$useState4 = _slicedToArray(_React$useState3, 2), - transcriptsList = _React$useState4[0], - setTranscriptsList = _React$useState4[1]; - var _React$useState5 = React__default["default"].useState([]), - _React$useState6 = _slicedToArray(_React$useState5, 2), - canvasTranscripts = _React$useState6[0], - setCanvasTranscripts = _React$useState6[1]; - var _React$useState7 = React__default["default"].useState([]), - _React$useState8 = _slicedToArray(_React$useState7, 2), - transcript = _React$useState8[0], - setTranscript = _React$useState8[1]; - var _React$useState9 = React__default["default"].useState({ - title: null, - filename: null, - id: null, - tUrl: null, - tType: null, - tFileExt: null, - isMachineGen: false, - tError: null - }), - _React$useState10 = _slicedToArray(_React$useState9, 2), - transcriptInfo = _React$useState10[0], - setTranscriptInfo = _React$useState10[1]; - var _React$useState11 = React__default["default"].useState(), - _React$useState12 = _slicedToArray(_React$useState11, 2), - selectedTranscript = _React$useState12[0], - setSelectedTranscript = _React$useState12[1]; - var _React$useState13 = React__default["default"].useState(true), - _React$useState14 = _slicedToArray(_React$useState13, 2), - isLoading = _React$useState14[0], - setIsLoading = _React$useState14[1]; - // Store transcript data in state to avoid re-requesting file contents - var _React$useState15 = React__default["default"].useState([]), - _React$useState16 = _slicedToArray(_React$useState15, 2), - cachedTranscripts = _React$useState16[0], - setCachedTranscripts = _React$useState16[1]; - - /* - Enable search only for timed text as it is only working for these transcripts - TODO:: remove 'isSearchable' if/when search is supported for other formats - */ - var _useSearchOpts = useSearchOpts(_objectSpread(_objectSpread({}, search), {}, { - isSearchable: transcriptInfo.tType === TRANSCRIPT_TYPES.timedText || transcriptInfo.tType === TRANSCRIPT_TYPES.docx || transcriptInfo.tType === TRANSCRIPT_TYPES.plainText, - showMarkers: transcriptInfo.tType === TRANSCRIPT_TYPES.timedText - })), - initialSearchQuery = _useSearchOpts.initialSearchQuery, - searchOpts = _objectWithoutProperties(_useSearchOpts, _excluded); - var _React$useState17 = React__default["default"].useState(initialSearchQuery), - _React$useState18 = _slicedToArray(_React$useState17, 2), - searchQuery = _React$useState18[0], - setSearchQuery = _React$useState18[1]; - var _React$useState19 = React__default["default"].useState(-1), - _React$useState20 = _slicedToArray(_React$useState19, 2), - _canvasIndex = _React$useState20[0], - _setCanvasIndex = _React$useState20[1]; - var canvasIndexRef = React__default["default"].useRef(_canvasIndex); - var setCanvasIndex = function setCanvasIndex(c) { - abortController.abort(); - canvasIndexRef.current = c; - _setCanvasIndex(c); // force re-render - }; - - var searchResults = useFilteredTranscripts(_objectSpread(_objectSpread({}, searchOpts), {}, { - query: searchQuery, - transcripts: transcript, - canvasIndex: canvasIndexRef.current, - selectedTranscript: selectedTranscript - })); - var _useFocusedMatch = useFocusedMatch({ - searchResults: searchResults - }), - focusedMatchId = _useFocusedMatch.focusedMatchId, - setFocusedMatchId = _useFocusedMatch.setFocusedMatchId, - focusedMatchIndex = _useFocusedMatch.focusedMatchIndex, - setFocusedMatchIndex = _useFocusedMatch.setFocusedMatchIndex; - var tanscriptHitCounts = useSearchCounts({ - searchResults: searchResults, - canvasTranscripts: canvasTranscripts, - searchQuery: searchQuery - }); - var _React$useState21 = React__default["default"].useState(true), - _React$useState22 = _slicedToArray(_React$useState21, 2), - isEmpty = _React$useState22[0], - setIsEmpty = _React$useState22[1]; - var _React$useState23 = React__default["default"].useState(true), - _React$useState24 = _slicedToArray(_React$useState23, 2), - _autoScrollEnabled = _React$useState24[0], - _setAutoScrollEnabled = _React$useState24[1]; - var autoScrollEnabledRef = React__default["default"].useRef(_autoScrollEnabled); - var setAutoScrollEnabled = function setAutoScrollEnabled(a) { - autoScrollEnabledRef.current = a; - _setAutoScrollEnabled(a); // force re-render - }; - - var abortController = new AbortController(); - var playerIntervalRef = React__default["default"].useRef(null); - var playerRef = React__default["default"].useRef(null); - var transcriptContainerRef = React__default["default"].useRef(); - var _React$useState25 = React__default["default"].useState(-1), - _React$useState26 = _slicedToArray(_React$useState25, 2), - currentTime = _React$useState26[0], - _setCurrentTime = _React$useState26[1]; - var setCurrentTime = React__default["default"].useMemo(function () { + var _useState3 = React.useState(-1), + _useState4 = _slicedToArray(_useState3, 2), + currentTime = _useState4[0], + _setCurrentTime = _useState4[1]; + var setCurrentTime = React.useMemo(function () { return throttle_1(_setCurrentTime, 50); - }, []); - var seekPlayer = React__default["default"].useCallback(function (time) { - setCurrentTime(time); // so selecting an item works in tests - if (playerRef.current) playerRef.current.currentTime = time; - }, []); - - /** - * Start an interval at the start of the component to poll the - * canvasindex attribute changes in the player on the page - */ - React__default["default"].useEffect(function () { - playerIntervalRef.current = setInterval(function () { - var domPlayer = document.getElementById(playerID); - if (!domPlayer) { - console.warn("Cannot find player, ".concat(playerID, " on page. Transcript synchronization is disabled")); - // Inaccessible canvas => stop loading spinner - setIsLoading(false); - } else { - if (domPlayer.children[0]) playerRef.current = domPlayer.children[0];else playerRef.current = domPlayer; - } - if (playerRef.current) { - var cIndex = parseInt(playerRef.current.dataset['canvasindex']); - if (Number.isNaN(cIndex)) cIndex = 0; - if (cIndex !== canvasIndexRef.current) { - // Clear the transcript text in the component - setTranscript([]); - setCanvasIndex(cIndex); - setCurrentTime(playerRef.current.currentTime); - playerRef.current.addEventListener('timeupdate', function () { - setCurrentTime(playerRef.current.currentTime); - }); - } - } - }, 500); - }, []); - React__default["default"].useEffect(function () { - // Clean up state when the component unmounts - return function () { - clearInterval(playerIntervalRef.current); - }; - }, []); - - /** - * If a list of transcripts is given in the props, then sanitize them - * to match the expected format in the component. - * If not fallback to reading transcripts from a given manifest URL. - * @param {Array} transcripts list of transcripts from props - */ - var loadTranscripts = /*#__PURE__*/function () { - var _ref4 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee(transcripts) { - var allTranscripts; - return regenerator.wrap(function _callee$(_context) { - while (1) switch (_context.prev = _context.next) { - case 0: - if (!((transcripts === null || transcripts === void 0 ? void 0 : transcripts.length) > 0 - // transcripts prop is processed first if given - )) { - _context.next = 6; - break; - } - _context.next = 3; - return sanitizeTranscripts(transcripts); - case 3: - _context.t0 = _context.sent; - _context.next = 9; - break; - case 6: - _context.next = 8; - return readSupplementingAnnotations(manifestUrl); - case 8: - _context.t0 = _context.sent; - case 9: - allTranscripts = _context.t0; - setTranscriptsList(allTranscripts !== null && allTranscripts !== void 0 ? allTranscripts : []); - initTranscriptData(allTranscripts !== null && allTranscripts !== void 0 ? allTranscripts : []); - case 12: - case "end": - return _context.stop(); - } - }, _callee); - })); - return function loadTranscripts(_x) { - return _ref4.apply(this, arguments); - }; - }(); - React__default["default"].useEffect(function () { - if ((transcripts === null || transcripts === void 0 ? void 0 : transcripts.length) === 0 && !manifestUrl) { - // When both required props are invalid - setIsLoading(false); - setTranscript([]); - setTranscriptInfo({ - tType: TRANSCRIPT_TYPES.noTranscript, - id: '', - tError: NO_TRANSCRIPTS_MSG - }); - } else { - loadTranscripts(transcripts); - } - }, [canvasIndexRef.current]); // helps to load initial transcript with async req + }, []); - React__default["default"].useEffect(function () { - if ((transcriptsList === null || transcriptsList === void 0 ? void 0 : transcriptsList.length) > 0 && canvasIndexRef.current != undefined) { - var cTranscripts = transcriptsList.filter(function (tr) { - return tr.canvasId == canvasIndexRef.current; - })[0]; - setCanvasTranscripts(cTranscripts.items); - setStateVar(cTranscripts.items[0]); - } - }, [canvasIndexRef.current]); - var initTranscriptData = function initTranscriptData(allTranscripts) { - var _getCanvasT, _getTItems; - // When canvasIndex updates -> return - if (abortController.signal.aborted) return; - var getCanvasT = function getCanvasT(tr) { - return tr.filter(function (t) { - return t.canvasId == _canvasIndex; - }); - }; - var getTItems = function getTItems(tr) { - return getCanvasT(tr)[0].items; - }; - /** - * When transcripts prop is empty - * OR the respective canvas doesn't have transcript data - * OR canvas' transcript items list is empty - */ - if (!(allTranscripts !== null && allTranscripts !== void 0 && allTranscripts.length) > 0 || !((_getCanvasT = getCanvasT(allTranscripts)) !== null && _getCanvasT !== void 0 && _getCanvasT.length) > 0 || !((_getTItems = getTItems(allTranscripts)) !== null && _getTItems !== void 0 && _getTItems.length) > 0) { - setIsEmpty(true); - setTranscript([]); - setStateVar(undefined); - } else { - setIsEmpty(false); - var cTranscripts = getCanvasT(allTranscripts)[0]; - setCanvasTranscripts(cTranscripts.items); - setStateVar(cTranscripts.items[0]); - } + // Read and parse transcript(s) as state changes + var _useTranscripts = useTranscripts({ + manifestUrl: manifestUrl, + playerID: playerID, + setCurrentTime: setCurrentTime, + transcripts: transcripts + }), + canvasIndexRef = _useTranscripts.canvasIndexRef, + canvasTranscripts = _useTranscripts.canvasTranscripts, + isEmpty = _useTranscripts.isEmpty, + isLoading = _useTranscripts.isLoading, + NO_SUPPORT_MSG = _useTranscripts.NO_SUPPORT_MSG, + playerRef = _useTranscripts.playerRef, + selectedTranscript = _useTranscripts.selectedTranscript, + selectTranscript = _useTranscripts.selectTranscript, + transcript = _useTranscripts.transcript, + transcriptInfo = _useTranscripts.transcriptInfo; + + /* + Enable search only for timed text as it is only working for these transcripts + TODO:: remove 'isSearchable' if/when search is supported for other formats + */ + var _useSearchOpts = useSearchOpts(_objectSpread(_objectSpread({}, search), {}, { + isSearchable: transcriptInfo.tType === TRANSCRIPT_TYPES.timedText || transcriptInfo.tType === TRANSCRIPT_TYPES.docx || transcriptInfo.tType === TRANSCRIPT_TYPES.plainText, + showMarkers: transcriptInfo.tType === TRANSCRIPT_TYPES.timedText + })), + initialSearchQuery = _useSearchOpts.initialSearchQuery, + searchOpts = _objectWithoutProperties(_useSearchOpts, _excluded); + var _useState5 = React.useState(initialSearchQuery), + _useState6 = _slicedToArray(_useState5, 2), + searchQuery = _useState6[0], + setSearchQuery = _useState6[1]; + var searchResults = useFilteredTranscripts(_objectSpread(_objectSpread({}, searchOpts), {}, { + query: searchQuery, + transcripts: transcript, + canvasIndex: canvasIndexRef.current, + selectedTranscript: selectedTranscript + })); + var _useFocusedMatch = useFocusedMatch({ + searchResults: searchResults + }), + focusedMatchId = _useFocusedMatch.focusedMatchId, + setFocusedMatchId = _useFocusedMatch.setFocusedMatchId, + focusedMatchIndex = _useFocusedMatch.focusedMatchIndex, + setFocusedMatchIndex = _useFocusedMatch.setFocusedMatchIndex; + var tanscriptHitCounts = useSearchCounts({ + searchResults: searchResults, + canvasTranscripts: canvasTranscripts, + searchQuery: searchQuery + }); + var _useState7 = React.useState(true), + _useState8 = _slicedToArray(_useState7, 2), + _autoScrollEnabled = _useState8[0], + _setAutoScrollEnabled = _useState8[1]; + var autoScrollEnabledRef = React.useRef(_autoScrollEnabled); + var setAutoScrollEnabled = function setAutoScrollEnabled(a) { + autoScrollEnabledRef.current = a; + _setAutoScrollEnabled(a); // force re-render }; - var selectTranscript = React__default["default"].useCallback(function (selectedId) { - var selectedTranscript = canvasTranscripts.filter(function (tr) { - return tr.id === selectedId; - }); - setStateVar(selectedTranscript[0]); - }, [canvasTranscripts]); - var setStateVar = /*#__PURE__*/function () { - var _ref5 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee2(transcript) { - var _transcript, id, title, filename, url, isMachineGen, format, cached, _cached$, tData, tFileExt, tType, tError; - return regenerator.wrap(function _callee2$(_context2) { - while (1) switch (_context2.prev = _context2.next) { - case 0: - if (!(!transcript || transcript == undefined)) { - _context2.next = 5; - break; - } - setIsEmpty(true); - setIsLoading(false); - setTranscriptInfo({ - tType: TRANSCRIPT_TYPES.noTranscript, - id: '', - tError: NO_TRANSCRIPTS_MSG - }); - return _context2.abrupt("return"); - case 5: - // set isEmpty flag to render transcripts UI - setIsEmpty(false); - _transcript = transcript, id = _transcript.id, title = _transcript.title, filename = _transcript.filename, url = _transcript.url, isMachineGen = _transcript.isMachineGen, format = _transcript.format; // Check cached transcript data - cached = cachedTranscripts.filter(function (ct) { - return ct.id == id && ct.canvasId == canvasIndexRef.current; - }); - if (!((cached === null || cached === void 0 ? void 0 : cached.length) > 0)) { - _context2.next = 15; - break; - } - // Load cached transcript data into the component - _cached$ = cached[0], tData = _cached$.tData, tFileExt = _cached$.tFileExt, tType = _cached$.tType, tError = _cached$.tError; - setTranscript(tData); - setTranscriptInfo({ - title: title, - filename: filename, - id: id, - isMachineGen: isMachineGen, - tType: tType, - tUrl: url, - tFileExt: tFileExt, - tError: tError - }); - setSelectedTranscript(url); - _context2.next = 17; - break; - case 15: - _context2.next = 17; - return Promise.resolve(parseTranscriptData(url, canvasIndexRef.current, format)).then(function (value) { - if (value != null) { - var _tData = value.tData, - tUrl = value.tUrl, - _tType = value.tType, - _tFileExt = value.tFileExt; - var newError = ''; - switch (_tType) { - case TRANSCRIPT_TYPES.invalid: - newError = INVALID_URL_MSG; - break; - case TRANSCRIPT_TYPES.noTranscript: - newError = NO_TRANSCRIPTS_MSG; - break; - case TRANSCRIPT_TYPES.noSupport: - newError = NO_SUPPORT; - break; - case TRANSCRIPT_TYPES.invalidVTT: - newError = INVALID_VTT; - break; - case TRANSCRIPT_TYPES.invalidTimestamp: - newError = INVALID_TIMESTAMP; - break; - } - setTranscript(_tData); - setTranscriptInfo({ - title: title, - filename: filename, - id: id, - isMachineGen: isMachineGen, - tType: _tType, - tUrl: tUrl, - tFileExt: _tFileExt, - tError: newError - }); - setSelectedTranscript(tUrl); - transcript = _objectSpread(_objectSpread({}, transcript), {}, { - tType: _tType, - tData: _tData, - tFileExt: _tFileExt, - canvasId: canvasIndexRef.current, - tError: newError - }); - // Cache the transcript info - setCachedTranscripts([].concat(_toConsumableArray(cachedTranscripts), [transcript])); - } - }); - case 17: - setIsLoading(false); - case 18: - case "end": - return _context2.stop(); - } - }, _callee2); - })); - return function setStateVar(_x2) { - return _ref5.apply(this, arguments); - }; - }(); + + var transcriptContainerRef = React.useRef(); + var seekPlayer = React.useCallback(function (time) { + setCurrentTime(time); // so selecting an item works in tests + if (playerRef.current) playerRef.current.currentTime(time); + }, []); if (!isLoading) { var _transcriptInfo$tErro; return /*#__PURE__*/React__default["default"].createElement("div", { @@ -11643,7 +12139,7 @@ var Transcript = function Transcript(_ref3) { selectTranscript: selectTranscript, transcriptData: tanscriptHitCounts, transcriptInfo: transcriptInfo, - noTranscript: ((_transcriptInfo$tErro = transcriptInfo.tError) === null || _transcriptInfo$tErro === void 0 ? void 0 : _transcriptInfo$tErro.length) > 0 && transcriptInfo.tError != NO_SUPPORT, + noTranscript: ((_transcriptInfo$tErro = transcriptInfo.tError) === null || _transcriptInfo$tErro === void 0 ? void 0 : _transcriptInfo$tErro.length) > 0 && transcriptInfo.tError != NO_SUPPORT_MSG, setAutoScrollEnabled: setAutoScrollEnabled, setFocusedMatchIndex: setFocusedMatchIndex, focusedMatchIndex: focusedMatchIndex, @@ -11652,7 +12148,7 @@ var Transcript = function Transcript(_ref3) { searchQuery: searchQuery, setSearchQuery: setSearchQuery }), /*#__PURE__*/React__default["default"].createElement("div", { - className: "transcript_content ".concat(transcript ? '' : 'static'), + className: cx__default["default"]('transcript_content', transcript ? '' : 'static'), "data-testid": "transcript_content_".concat(transcriptInfo.tType), role: "list", "aria-label": "Attached Transcript content", @@ -11700,11 +12196,16 @@ Transcript.propTypes = { }; /** - * @param {Boolean} param0 display only Canvas metadata when set to true with other props are default - * @param {Boolean} param1 display both Manifest and Canvas metadata when set to true - * @param {Boolean} param2 hide the title in the metadata when set to false, defaults to true - * @param {Boolean} param3 hide the heading UI component when set to false, defaults to true - * @returns + * Parse and display metadata, rights, and requiredStatement information + * related to the current resource. The display of the scope of this information + * can be customized using props as needed. + * @param {Object} props + * @param {Boolean} props.displayOnlyCanvasMetadata + * @param {Boolean} props.displayAllMetadata + * @param {Boolean} props.displayTitle + * @param {Boolean} props.showHeading + * @param {String} props.itemHeading + * @param {String} props.sectionHeaading */ var MetadataDisplay = function MetadataDisplay(_ref) { var _ref$displayOnlyCanva = _ref.displayOnlyCanvasMetadata, @@ -11722,38 +12223,42 @@ var MetadataDisplay = function MetadataDisplay(_ref) { var _useManifestState = useManifestState(), manifest = _useManifestState.manifest, canvasIndex = _useManifestState.canvasIndex; - var _React$useState = React__default["default"].useState(), - _React$useState2 = _slicedToArray(_React$useState, 2), - manifestMetadata = _React$useState2[0], - setManifestMetadata = _React$useState2[1]; + var _useState = React.useState(), + _useState2 = _slicedToArray(_useState, 2), + manifestMetadata = _useState2[0], + setManifestMetadata = _useState2[1]; // Metadata for all Canavases in state - var _React$useState3 = React__default["default"].useState(), - _React$useState4 = _slicedToArray(_React$useState3, 2); - _React$useState4[0]; - var _setCanvasesMetadata = _React$useState4[1]; + var _useState3 = React.useState(), + _useState4 = _slicedToArray(_useState3, 2); + _useState4[0]; + var _setCanvasesMetadata = _useState4[1]; // Current Canvas metadata in state - var _React$useState5 = React__default["default"].useState(), - _React$useState6 = _slicedToArray(_React$useState5, 2), - canvasMetadata = _React$useState6[0], - setCanvasMetadata = _React$useState6[1]; + var _useState5 = React.useState(), + _useState6 = _slicedToArray(_useState5, 2), + canvasMetadata = _useState6[0], + setCanvasMetadata = _useState6[1]; // Boolean flags set according to user props to hide/show metadata - var _React$useState7 = React__default["default"].useState(), - _React$useState8 = _slicedToArray(_React$useState7, 2), - showManifestMetadata = _React$useState8[0], - setShowManifestMetadata = _React$useState8[1]; - var _React$useState9 = React__default["default"].useState(), - _React$useState10 = _slicedToArray(_React$useState9, 2), - showCanvasMetadata = _React$useState10[0], - setShowCanvasMetadata = _React$useState10[1]; - var _React$useState11 = React__default["default"].useState(), - _React$useState12 = _slicedToArray(_React$useState11, 2), - manifestRights = _React$useState12[0], - setManifestRights = _React$useState12[1]; - var _React$useState13 = React__default["default"].useState(), - _React$useState14 = _slicedToArray(_React$useState13, 2), - canvasRights = _React$useState14[0], - setCanvasRights = _React$useState14[1]; - var canvasesMetadataRef = React__default["default"].useRef(); + var _useState7 = React.useState(), + _useState8 = _slicedToArray(_useState7, 2), + showManifestMetadata = _useState8[0], + setShowManifestMetadata = _useState8[1]; + var _useState9 = React.useState(), + _useState10 = _slicedToArray(_useState9, 2), + showCanvasMetadata = _useState10[0], + setShowCanvasMetadata = _useState10[1]; + var _useState11 = React.useState(), + _useState12 = _slicedToArray(_useState11, 2), + manifestRights = _useState12[0], + setManifestRights = _useState12[1]; + var _useState13 = React.useState(), + _useState14 = _slicedToArray(_useState13, 2), + canvasRights = _useState14[0], + setCanvasRights = _useState14[1]; + var _useState15 = React.useState(false), + _useState16 = _slicedToArray(_useState15, 2), + hasMetadata = _useState16[0], + setHasMetadata = _useState16[1]; + var canvasesMetadataRef = React.useRef(); var setCanvasesMetadata = function setCanvasesMetadata(m) { _setCanvasesMetadata(m); canvasesMetadataRef.current = m; @@ -11763,7 +12268,7 @@ var MetadataDisplay = function MetadataDisplay(_ref) { * and/or Canvases based on the input props and set the initial set(s) of * metadata in the component's state */ - React__default["default"].useEffect(function () { + React.useEffect(function () { if (manifest) { var _parsedMetadata$right; // Display Canvas metadata only when specified in the props @@ -11781,6 +12286,7 @@ var MetadataDisplay = function MetadataDisplay(_ref) { setCanvasMetadataInState(); } if (showManifest) { + var _manifestMeta; var manifestMeta = parsedMetadata.manifestMetadata; if (!displayTitle) { manifestMeta = manifestMeta.filter(function (md) { @@ -11788,6 +12294,7 @@ var MetadataDisplay = function MetadataDisplay(_ref) { }); } setManifestMetadata(manifestMeta); + setHasMetadata(((_manifestMeta = manifestMeta) === null || _manifestMeta === void 0 ? void 0 : _manifestMeta.length) > 0); } if (((_parsedMetadata$right = parsedMetadata.rights) === null || _parsedMetadata$right === void 0 ? void 0 : _parsedMetadata$right.length) > 0) { setManifestRights(parsedMetadata.rights); @@ -11800,7 +12307,7 @@ var MetadataDisplay = function MetadataDisplay(_ref) { * in the component's state listening to the canvasIndex changes in the central * state */ - React__default["default"].useEffect(function () { + React.useEffect(function () { if (canvasIndex >= 0 && showCanvasMetadata) { setCanvasMetadataInState(); } @@ -11814,6 +12321,7 @@ var MetadataDisplay = function MetadataDisplay(_ref) { return m.canvasindex === canvasIndex; })[0]; if (canvasData != undefined) { + var _metadata; var metadata = canvasData.metadata, rights = canvasData.rights; if (!displayTitle && metadata != undefined) { @@ -11822,23 +12330,17 @@ var MetadataDisplay = function MetadataDisplay(_ref) { }); } setCanvasMetadata(metadata); + setHasMetadata(((_metadata = metadata) === null || _metadata === void 0 ? void 0 : _metadata.length) > 0); if (rights != undefined && (rights === null || rights === void 0 ? void 0 : rights.length) > 0) { setCanvasRights(rights); } } }; - /** - * Distinguish whether there is any metadata to be displayed - * @returns {Boolean} - */ - var hasMetadata = function hasMetadata() { - return (canvasMetadata === null || canvasMetadata === void 0 ? void 0 : canvasMetadata.length) > 0 || (manifestMetadata === null || manifestMetadata === void 0 ? void 0 : manifestMetadata.length) > 0; - }; var buildMetadata = function buildMetadata(metadata) { var metadataPairs = []; if ((metadata === null || metadata === void 0 ? void 0 : metadata.length) > 0) { metadata.map(function (md, index) { - metadataPairs.push( /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, { + metadataPairs.push( /*#__PURE__*/React__default["default"].createElement(React.Fragment, { key: index }, /*#__PURE__*/React__default["default"].createElement("dt", null, md.label), /*#__PURE__*/React__default["default"].createElement("dd", { dangerouslySetInnerHTML: { @@ -11849,21 +12351,31 @@ var MetadataDisplay = function MetadataDisplay(_ref) { } return /*#__PURE__*/React__default["default"].createElement("dl", null, metadataPairs); }; + var manifestMetadataBlock = React.useMemo(function () { + if (showManifestMetadata && (manifestMetadata === null || manifestMetadata === void 0 ? void 0 : manifestMetadata.length) > 0) { + return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, displayAllMetadata && /*#__PURE__*/React__default["default"].createElement("span", null, itemHeading), buildMetadata(manifestMetadata), (manifestRights === null || manifestRights === void 0 ? void 0 : manifestRights.length) > 0 && /*#__PURE__*/React__default["default"].createElement("span", { + className: "ramp--metadata-rights-heading", + "data-testid": "manifest-rights" + }, "Rights"), buildMetadata(manifestRights)); + } + }, [manifestMetadata]); + var canvasMetadataBlock = React.useMemo(function () { + if (showCanvasMetadata && (canvasMetadata === null || canvasMetadata === void 0 ? void 0 : canvasMetadata.length) > 0) { + return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, displayAllMetadata && /*#__PURE__*/React__default["default"].createElement("span", null, sectionHeaading), buildMetadata(canvasMetadata), (canvasRights === null || canvasRights === void 0 ? void 0 : canvasRights.length) > 0 && /*#__PURE__*/React__default["default"].createElement("span", { + className: "ramp--metadata-rights-heading", + "data-testid": "canvas-rights" + }, "Rights"), buildMetadata(canvasRights)); + } + }, [canvasMetadata]); return /*#__PURE__*/React__default["default"].createElement("div", { "data-testid": "metadata-display", className: "ramp--metadata-display" }, showHeading && /*#__PURE__*/React__default["default"].createElement("div", { className: "ramp--metadata-display-title", "data-testid": "metadata-display-title" - }, /*#__PURE__*/React__default["default"].createElement("h4", null, "Details")), hasMetadata() && /*#__PURE__*/React__default["default"].createElement("div", { + }, /*#__PURE__*/React__default["default"].createElement("h4", null, "Details")), hasMetadata ? /*#__PURE__*/React__default["default"].createElement("div", { className: "ramp--metadata-display-content" - }, showManifestMetadata && (manifestMetadata === null || manifestMetadata === void 0 ? void 0 : manifestMetadata.length) > 0 && /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, displayAllMetadata && /*#__PURE__*/React__default["default"].createElement("span", null, itemHeading), buildMetadata(manifestMetadata), (manifestRights === null || manifestRights === void 0 ? void 0 : manifestRights.length) > 0 && /*#__PURE__*/React__default["default"].createElement("span", { - className: "ramp--metadata-rights-heading", - "data-testid": "manifest-rights" - }, "Rights"), buildMetadata(manifestRights)), showCanvasMetadata && (canvasMetadata === null || canvasMetadata === void 0 ? void 0 : canvasMetadata.length) > 0 && /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, displayAllMetadata && /*#__PURE__*/React__default["default"].createElement("span", null, sectionHeaading), buildMetadata(canvasMetadata), (canvasRights === null || canvasRights === void 0 ? void 0 : canvasRights.length) > 0 && /*#__PURE__*/React__default["default"].createElement("span", { - className: "ramp--metadata-rights-heading", - "data-testid": "canvas-rights" - }, "Rights"), buildMetadata(canvasRights))), !hasMetadata() && /*#__PURE__*/React__default["default"].createElement("div", { + }, manifestMetadataBlock, canvasMetadataBlock) : /*#__PURE__*/React__default["default"].createElement("div", { "data-testid": "metadata-display-message", className: "ramp--metadata-display-message" }, /*#__PURE__*/React__default["default"].createElement("p", null, "No valid Metadata is in the Manifest/Canvas(es)"))); @@ -11877,6 +12389,14 @@ MetadataDisplay.propTypes = { sectionHeaading: PropTypes.string }; +/** + * Display supplemental files as downloadable links, referenced in both + * manifest and at each canvas as rendering files. + * @param {Object} props + * @param {String} props.itemHeading + * @param {String} props.sectionHeaading + * @param {Boolean} props.showHeading + */ var SupplementalFiles = function SupplementalFiles(_ref) { var _ref$itemHeading = _ref.itemHeading, itemHeading = _ref$itemHeading === void 0 ? "Item files" : _ref$itemHeading, @@ -11932,17 +12452,11 @@ var SupplementalFiles = function SupplementalFiles(_ref) { event.preventDefault(); fileDownload(file.id, file.filename, file.fileExt, file.isMachineGen); }; - return React.useMemo(function () { - return /*#__PURE__*/React__default["default"].createElement("div", { - "data-testid": "supplemental-files", - className: "ramp--supplemental-files" - }, showHeading && /*#__PURE__*/React__default["default"].createElement("div", { - className: "ramp--supplemental-files-heading", - "data-testid": "supplemental-files-heading" - }, /*#__PURE__*/React__default["default"].createElement("h4", null, "Files")), hasFiles && /*#__PURE__*/React__default["default"].createElement("div", { + var filesDisplay = React.useMemo(function () { + return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, hasFiles && /*#__PURE__*/React__default["default"].createElement("div", { className: "ramp--supplemental-files-display-content", "data-testid": "supplemental-files-display-content" - }, Array.isArray(manifestSupplementalFiles) && manifestSupplementalFiles.length > 0 && /*#__PURE__*/React__default["default"].createElement(React.Fragment, null, /*#__PURE__*/React__default["default"].createElement("h4", null, itemHeading), /*#__PURE__*/React__default["default"].createElement("dl", { + }, Array.isArray(manifestSupplementalFiles) && manifestSupplementalFiles.length > 0 && /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement("h4", null, itemHeading), /*#__PURE__*/React__default["default"].createElement("dl", { key: "item-files" }, manifestSupplementalFiles.map(function (file, index) { return /*#__PURE__*/React__default["default"].createElement(React.Fragment, { @@ -11956,7 +12470,7 @@ var SupplementalFiles = function SupplementalFiles(_ref) { return handleDownload(e, file); } }, file.label))); - }))), Array.isArray(canvasSupplementalFiles) && hasSectionFiles && /*#__PURE__*/React__default["default"].createElement(React.Fragment, null, /*#__PURE__*/React__default["default"].createElement("h4", null, sectionHeading), canvasSupplementalFiles.map(function (canvasFiles, idx) { + }))), Array.isArray(canvasSupplementalFiles) && hasSectionFiles && /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement("h4", null, sectionHeading), canvasSupplementalFiles.map(function (canvasFiles, idx) { var files = canvasFiles.files; return files.length > 0 && /*#__PURE__*/React__default["default"].createElement("dl", { key: "section-".concat(idx, "-label") @@ -11978,8 +12492,22 @@ var SupplementalFiles = function SupplementalFiles(_ref) { className: "ramp--supplemental-files-empty" }, /*#__PURE__*/React__default["default"].createElement("p", null, "No Supplemental file(s) in Manifest"))); }, [hasFiles, hasSectionFiles]); + return /*#__PURE__*/React__default["default"].createElement("div", { + "data-testid": "supplemental-files", + className: "ramp--supplemental-files" + }, showHeading && /*#__PURE__*/React__default["default"].createElement("div", { + className: "ramp--supplemental-files-heading", + "data-testid": "supplemental-files-heading" + }, /*#__PURE__*/React__default["default"].createElement("h4", null, "Files")), filesDisplay); }; +/** + * A toggle button to enable/disable auto-play across multiple + * canvases + * @param {Object} props + * @param {String} props.label + * @param {Boolean} props.showLabel + */ var AutoAdvanceToggle = function AutoAdvanceToggle(_ref) { var _ref$label = _ref.label, label = _ref$label === void 0 ? "Autoplay" : _ref$label, @@ -11994,97 +12522,74 @@ var AutoAdvanceToggle = function AutoAdvanceToggle(_ref) { type: "setAutoAdvance" }); }; - return React__default["default"].useMemo(function () { - return /*#__PURE__*/React__default["default"].createElement("div", { - "data-testid": "auto-advance", - className: "ramp--auto-advance" - }, showLabel && /*#__PURE__*/React__default["default"].createElement("span", { - className: "ramp--auto-advance-label", - "data-testid": "auto-advance-label", - htmlFor: "auto-advance-toggle", - id: "auto-advance-toggle-label" - }, label), /*#__PURE__*/React__default["default"].createElement("label", { - className: "ramp--auto-advance-toggle", - "aria-labelledby": "auto-advance-toggle-label" - }, /*#__PURE__*/React__default["default"].createElement("input", { + var toggleButton = React.useMemo(function () { + return /*#__PURE__*/React__default["default"].createElement("input", { "data-testid": "auto-advance-toggle", name: "auto-advance-toggle", type: "checkbox", checked: autoAdvance, "aria-label": label, onChange: handleChange - }), /*#__PURE__*/React__default["default"].createElement("span", { - className: "slider round" - }))); + }); }, [autoAdvance]); + return /*#__PURE__*/React__default["default"].createElement("div", { + "data-testid": "auto-advance", + className: "ramp--auto-advance" + }, showLabel && /*#__PURE__*/React__default["default"].createElement("span", { + className: "ramp--auto-advance-label", + "data-testid": "auto-advance-label", + htmlFor: "auto-advance-toggle", + id: "auto-advance-toggle-label" + }, label), /*#__PURE__*/React__default["default"].createElement("label", { + className: "ramp--auto-advance-toggle", + "aria-labelledby": "auto-advance-toggle-label" + }, toggleButton, /*#__PURE__*/React__default["default"].createElement("span", { + className: "slider round" + }))); }; AutoAdvanceToggle.propTypes = { label: PropTypes.string, showLabel: PropTypes.bool }; -var useMarkers = function useMarkers() { - var manifestState = React.useContext(ManifestStateContext); - var isEditing = manifestState.playlist.isEditing; - var isDisabled = React.useMemo(function () { - return isEditing; - }, [isEditing]); - return { - isDisabled: isDisabled - }; -}; -var usePlayer = function usePlayer() { - var playerState = React.useContext(PlayerStateContext); - var player; - if (playerState) { - player = playerState.player; - } - var playerRef = React.useRef(); - playerRef.current = React.useMemo(function () { - return player; - }, [player]); - var getCurrentTime = React.useCallback(function () { - if (playerRef.current) { - return playerRef.current.currentTime(); - } else { - return 0; - } - }, [playerRef.current]); - return { - getCurrentTime: getCurrentTime, - player: playerRef.current - }; -}; - +/** + * Build and handle creation of new markers for playlists. This component is rendered + * on page when the user has permissions to create new markers in a given playlist Manifest. + * @param {Object} props + * @param {String} props.newMarkerEndpoint annotationService to POST create markers request + * @param {Number} props.canvasId URI of the current Canvas + * @param {Function} props.handleCreate callback function to update global state + * @param {String} props.csrfToken token to authenticate POST request + */ var CreateMarker = function CreateMarker(_ref) { var newMarkerEndpoint = _ref.newMarkerEndpoint, canvasId = _ref.canvasId, handleCreate = _ref.handleCreate, csrfToken = _ref.csrfToken; - var _React$useState = React__default["default"].useState(false), - _React$useState2 = _slicedToArray(_React$useState, 2), - isOpen = _React$useState2[0], - setIsOpen = _React$useState2[1]; - var _React$useState3 = React__default["default"].useState(false), - _React$useState4 = _slicedToArray(_React$useState3, 2), - isValid = _React$useState4[0], - setIsValid = _React$useState4[1]; - var _React$useState5 = React__default["default"].useState(false), - _React$useState6 = _slicedToArray(_React$useState5, 2), - saveError = _React$useState6[0], - setSaveError = _React$useState6[1]; - var _React$useState7 = React__default["default"].useState(''), - _React$useState8 = _slicedToArray(_React$useState7, 2), - errorMessage = _React$useState8[0], - setErrorMessage = _React$useState8[1]; - var _React$useState9 = React__default["default"].useState(), - _React$useState10 = _slicedToArray(_React$useState9, 2), - markerTime = _React$useState10[0], - setMarkerTime = _React$useState10[1]; + var _useState = React.useState(false), + _useState2 = _slicedToArray(_useState, 2), + isOpen = _useState2[0], + setIsOpen = _useState2[1]; + var _useState3 = React.useState(false), + _useState4 = _slicedToArray(_useState3, 2), + isValid = _useState4[0], + setIsValid = _useState4[1]; + var _useState5 = React.useState(false), + _useState6 = _slicedToArray(_useState5, 2), + saveError = _useState6[0], + setSaveError = _useState6[1]; + var _useState7 = React.useState(''), + _useState8 = _slicedToArray(_useState7, 2), + errorMessage = _useState8[0], + setErrorMessage = _useState8[1]; + var _useState9 = React.useState(), + _useState10 = _slicedToArray(_useState9, 2), + markerTime = _useState10[0], + setMarkerTime = _useState10[1]; var controller; - var _usePlayer = usePlayer(), - getCurrentTime = _usePlayer.getCurrentTime; - React__default["default"].useEffect(function () { + var _useMediaPlayer = useMediaPlayer(), + getCurrentTime = _useMediaPlayer.getCurrentTime; + React.useEffect(function () { // Close new marker form on Canvas change setIsOpen(false); @@ -12099,7 +12604,7 @@ var CreateMarker = function CreateMarker(_ref) { validateTime(currentTime); setIsOpen(true); }; - var handleCreateSubmit = React__default["default"].useCallback(function (e) { + var handleCreateSubmit = React.useCallback(function (e) { e.preventDefault(); var form = e.target; var formData = new FormData(form); @@ -12151,7 +12656,7 @@ var CreateMarker = function CreateMarker(_ref) { setErrorMessage('Marker creation failed.'); }); }, [canvasId]); - var handleCreateCancel = React__default["default"].useCallback(function () { + var handleCreateCancel = React.useCallback(function () { setIsOpen(false); setIsValid(false); setErrorMessage(''); @@ -12192,7 +12697,7 @@ var CreateMarker = function CreateMarker(_ref) { id: "new-marker-time", "data-testid": "create-marker-timestamp", type: "text", - className: "ramp--markers-display__create-marker ".concat(isValid ? 'time-valid' : 'time-invalid'), + className: cx__default["default"]('ramp--markers-display__create-marker', isValid ? 'time-valid' : 'time-invalid'), name: "time", value: markerTime, onChange: validateTime @@ -12214,63 +12719,75 @@ var CreateMarker = function CreateMarker(_ref) { CreateMarker.propTypes = { newMarkerEndpoint: PropTypes.string.isRequired, canvasId: PropTypes.string, - handleCreate: PropTypes.func.isRequired + handleCreate: PropTypes.func.isRequired, + csrfToken: PropTypes.string }; +/** + * Build a table row for each 'highlighting; annotation in the current Canvas in the Manifest. + * These are timepoint annotations. When user has permissions to edit annotations, an actions + * column is populated for each annotation with edit and delete actions. + * @param {Object} props + * @param {Object} props.marker each marker parsed from annotations + * @param {Function} props.handleSubmit callback func to update state on marker edit action + * @param {Function} props.handleDelete callback func to update state on marker delete action + * @param {Function} props.toggleIsEditing callback function to update global state + * @param {String} props.csrfToken token to authenticate POST request + */ var MarkerRow = function MarkerRow(_ref) { var marker = _ref.marker, handleSubmit = _ref.handleSubmit, handleDelete = _ref.handleDelete, - hasAnnotationService = _ref.hasAnnotationService, toggleIsEditing = _ref.toggleIsEditing, csrfToken = _ref.csrfToken; - var _React$useState = React__default["default"].useState(false), - _React$useState2 = _slicedToArray(_React$useState, 2), - editing = _React$useState2[0], - setEditing = _React$useState2[1]; - var _React$useState3 = React__default["default"].useState(true), - _React$useState4 = _slicedToArray(_React$useState3, 2), - isValid = _React$useState4[0], - setIsValid = _React$useState4[1]; - var _React$useState5 = React__default["default"].useState(), - _React$useState6 = _slicedToArray(_React$useState5, 2), - tempMarker = _React$useState6[0], - setTempMarker = _React$useState6[1]; - var _React$useState7 = React__default["default"].useState(false), - _React$useState8 = _slicedToArray(_React$useState7, 2), - deleting = _React$useState8[0], - setDeleting = _React$useState8[1]; - var _React$useState9 = React__default["default"].useState(false), - _React$useState10 = _slicedToArray(_React$useState9, 2), - saveError = _React$useState10[0], - setSaveError = _React$useState10[1]; - var _React$useState11 = React__default["default"].useState(''), - _React$useState12 = _slicedToArray(_React$useState11, 2), - errorMessage = _React$useState12[0], - setErrorMessage = _React$useState12[1]; + var _useState = React.useState(false), + _useState2 = _slicedToArray(_useState, 2), + editing = _useState2[0], + setEditing = _useState2[1]; + var _useState3 = React.useState(true), + _useState4 = _slicedToArray(_useState3, 2), + isValid = _useState4[0], + setIsValid = _useState4[1]; + var _useState5 = React.useState(), + _useState6 = _slicedToArray(_useState5, 2), + tempMarker = _useState6[0], + setTempMarker = _useState6[1]; + var _useState7 = React.useState(false), + _useState8 = _slicedToArray(_useState7, 2), + deleting = _useState8[0], + setDeleting = _useState8[1]; + var _useState9 = React.useState(false), + _useState10 = _slicedToArray(_useState9, 2), + saveError = _useState10[0], + setSaveError = _useState10[1]; + var _useState11 = React.useState(''), + _useState12 = _slicedToArray(_useState11, 2), + errorMessage = _useState12[0], + setErrorMessage = _useState12[1]; var controller; var _useMarkers = useMarkers(), + hasAnnotationService = _useMarkers.hasAnnotationService, isDisabled = _useMarkers.isDisabled; - var _usePlayer = usePlayer(), - player = _usePlayer.player; + var _useMediaPlayer = useMediaPlayer(), + player = _useMediaPlayer.player; // Remove all fetch requests on unmount - React__default["default"].useEffect(function () { + React.useEffect(function () { return function () { var _controller; (_controller = controller) === null || _controller === void 0 ? void 0 : _controller.abort(); }; }, []); - React__default["default"].useEffect(function () { + React.useEffect(function () { setMarkerLabel(marker.value); setMarkerTime(marker.timeStr); }, [marker]); - var markerLabelRef = React__default["default"].useRef(marker.value); + var markerLabelRef = React.useRef(marker.value); var setMarkerLabel = function setMarkerLabel(label) { markerLabelRef.current = label; }; - var markerOffsetRef = React__default["default"].useRef(timeToS(marker.timeStr)); - var markerTimeRef = React__default["default"].useRef(marker.timeStr); + var markerOffsetRef = React.useRef(timeToS(marker.timeStr)); + var markerTimeRef = React.useRef(marker.timeStr); var setMarkerTime = function setMarkerTime(time) { markerTimeRef.current = time; markerOffsetRef.current = timeToS(time); @@ -12399,7 +12916,7 @@ var MarkerRow = function MarkerRow(_ref) { setEditing(false); toggleIsEditing(false); }; - var handleMarkerClick = React__default["default"].useCallback(function (e) { + var handleMarkerClick = React.useCallback(function (e) { e.preventDefault(); var currentTime = parseFloat(e.target.dataset['offset']); if (player) { @@ -12418,7 +12935,7 @@ var MarkerRow = function MarkerRow(_ref) { }, name: "label" })), /*#__PURE__*/React__default["default"].createElement("td", null, /*#__PURE__*/React__default["default"].createElement("input", { - className: "ramp--markers-display__edit-marker ".concat(isValid ? 'time-valid' : 'time-invalid'), + className: cx__default["default"]('ramp--markers-display__edit-marker', isValid ? 'time-valid' : 'time-invalid'), id: "time", "data-testid": "edit-timestamp", defaultValue: markerTimeRef.current, @@ -12489,10 +13006,17 @@ MarkerRow.propTypes = { marker: PropTypes.object.isRequired, handleSubmit: PropTypes.func.isRequired, handleDelete: PropTypes.func.isRequired, - hasAnnotationService: PropTypes.bool.isRequired, - toggleIsEditing: PropTypes.func.isRequired + toggleIsEditing: PropTypes.func.isRequired, + csrfToken: PropTypes.string }; +/** + * Display timepoint annotations associated with the current Canvas + * in a tabular format. + * @param {Object} props + * @param {Boolean} props.showHeading + * @param {String} props.headingText + */ var MarkersDisplay = function MarkersDisplay(_ref) { var _document$getElements; var _ref$showHeading = _ref.showHeading, @@ -12514,6 +13038,8 @@ var MarkersDisplay = function MarkersDisplay(_ref) { var _useErrorBoundary = reactErrorBoundary.useErrorBoundary(), showBoundary = _useErrorBoundary.showBoundary; var canvasIdRef = React.useRef(); + + // Using a ref updates markers table immediately after marker edit/creation var canvasPlaylistsMarkersRef = React.useRef([]); var setCanvasMarkers = function setCanvasMarkers(list) { setCanvasPlaylistsMarkers.apply(void 0, _toConsumableArray(list)); @@ -12577,33 +13103,40 @@ var MarkersDisplay = function MarkersDisplay(_ref) { type: 'setIsEditing' }); }); - return React.useMemo(function () { - return /*#__PURE__*/React__default["default"].createElement("div", { - className: "ramp--markers-display", - "data-testid": "markers-display" - }, showHeading && /*#__PURE__*/React__default["default"].createElement("div", { - className: "ramp--markers-display__title", - "data-testid": "markers-display-title" - }, /*#__PURE__*/React__default["default"].createElement("h4", null, headingText)), hasAnnotationService && /*#__PURE__*/React__default["default"].createElement(CreateMarker, { - newMarkerEndpoint: annotationServiceId, - canvasId: canvasIdRef.current, - handleCreate: handleCreate, - csrfToken: csrfToken - }), canvasPlaylistsMarkersRef.current.length > 0 && /*#__PURE__*/React__default["default"].createElement("table", { - className: "ramp--markers-display_table", - "data-testid": "markers-display-table" - }, /*#__PURE__*/React__default["default"].createElement("thead", null, /*#__PURE__*/React__default["default"].createElement("tr", null, /*#__PURE__*/React__default["default"].createElement("th", null, "Name"), /*#__PURE__*/React__default["default"].createElement("th", null, "Time"), hasAnnotationService && /*#__PURE__*/React__default["default"].createElement("th", null, "Actions"))), /*#__PURE__*/React__default["default"].createElement("tbody", null, canvasPlaylistsMarkersRef.current.map(function (marker, index) { - return /*#__PURE__*/React__default["default"].createElement(MarkerRow, { - key: index, - marker: marker, - handleSubmit: handleSubmit, - handleDelete: handleDelete, - hasAnnotationService: hasAnnotationService, - toggleIsEditing: toggleIsEditing, + var createMarker = React.useMemo(function () { + if (hasAnnotationService) { + return /*#__PURE__*/React__default["default"].createElement(CreateMarker, { + newMarkerEndpoint: annotationServiceId, + canvasId: canvasIdRef.current, + handleCreate: handleCreate, csrfToken: csrfToken }); - })))); - }, [canvasPlaylistsMarkersRef.current, csrfToken]); + } + }, [hasAnnotationService, canvasIdRef.current, csrfToken]); + var markersTable = React.useMemo(function () { + if (canvasPlaylistsMarkersRef.current.length > 0) { + return /*#__PURE__*/React__default["default"].createElement("table", { + className: "ramp--markers-display_table", + "data-testid": "markers-display-table" + }, /*#__PURE__*/React__default["default"].createElement("thead", null, /*#__PURE__*/React__default["default"].createElement("tr", null, /*#__PURE__*/React__default["default"].createElement("th", null, "Name"), /*#__PURE__*/React__default["default"].createElement("th", null, "Time"), hasAnnotationService && /*#__PURE__*/React__default["default"].createElement("th", null, "Actions"))), /*#__PURE__*/React__default["default"].createElement("tbody", null, canvasPlaylistsMarkersRef.current.map(function (marker, index) { + return /*#__PURE__*/React__default["default"].createElement(MarkerRow, { + key: index, + marker: marker, + handleSubmit: handleSubmit, + handleDelete: handleDelete, + toggleIsEditing: toggleIsEditing, + csrfToken: csrfToken + }); + }))); + } + }, [canvasPlaylistsMarkersRef.current]); + return /*#__PURE__*/React__default["default"].createElement("div", { + className: "ramp--markers-display", + "data-testid": "markers-display" + }, showHeading && /*#__PURE__*/React__default["default"].createElement("div", { + className: "ramp--markers-display__title", + "data-testid": "markers-display-title" + }, /*#__PURE__*/React__default["default"].createElement("h4", null, headingText)), createMarker, markersTable); }; MarkersDisplay.propTypes = { showHeading: PropTypes.bool, diff --git a/dist/ramp.cjs.min.js b/dist/ramp.cjs.min.js index 62b90c73..837b52ef 100644 --- a/dist/ramp.cjs.min.js +++ b/dist/ramp.cjs.min.js @@ -1 +1 @@ -"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),t=require("manifesto.js"),r=require("mime-db"),n=require("sanitize-html"),a=require("react-error-boundary"),i=require("video.js"),o=require("classnames"),l=require("mammoth");function s(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function c(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var n=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,n.get?n:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var u=s(e),d=s(r),f=s(n),p=s(i),m=s(o),v=s(l),h="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function g(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function y(e,t,r){return e(r={path:t,exports:{},require:function(e,t){return function(){throw new Error("Dynamic requires are not currently supported by @rollup/plugin-commonjs")}(null==t&&r.path)}},r.exports),r.exports}var b=y((function(e){e.exports=function(e){if(Array.isArray(e))return e},e.exports.__esModule=!0,e.exports.default=e.exports})),x=y((function(e){e.exports=function(e,t){var r=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=r){var n,a,i,o,l=[],s=!0,c=!1;try{if(i=(r=r.call(e)).next,0===t){if(Object(r)!==r)return;s=!1}else for(;!(s=(n=i.call(r)).done)&&(l.push(n.value),l.length!==t);s=!0);}catch(e){c=!0,a=e}finally{try{if(!s&&null!=r.return&&(o=r.return(),Object(o)!==o))return}finally{if(c)throw a}}return l}},e.exports.__esModule=!0,e.exports.default=e.exports})),k=y((function(e){e.exports=function(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r",""":'"',"'":"'","&":"&"},characters:{"<":"<",">":">",'"':""","'":"'","&":"&"}},html4:{entities:{"'":"'"," ":" "," ":" ","¡":"¡","¡":"¡","¢":"¢","¢":"¢","£":"£","£":"£","¤":"¤","¤":"¤","¥":"¥","¥":"¥","¦":"¦","¦":"¦","§":"§","§":"§","¨":"¨","¨":"¨","©":"©","©":"©","ª":"ª","ª":"ª","«":"«","«":"«","¬":"¬","¬":"¬","­":"­","­":"­","®":"®","®":"®","¯":"¯","¯":"¯","°":"°","°":"°","±":"±","±":"±","²":"²","²":"²","³":"³","³":"³","´":"´","´":"´","µ":"µ","µ":"µ","¶":"¶","¶":"¶","·":"·","·":"·","¸":"¸","¸":"¸","¹":"¹","¹":"¹","º":"º","º":"º","»":"»","»":"»","¼":"¼","¼":"¼","½":"½","½":"½","¾":"¾","¾":"¾","¿":"¿","¿":"¿","À":"À","À":"À","Á":"Á","Á":"Á","Â":"Â","Â":"Â","Ã":"Ã","Ã":"Ã","Ä":"Ä","Ä":"Ä","Å":"Å","Å":"Å","Æ":"Æ","Æ":"Æ","Ç":"Ç","Ç":"Ç","È":"È","È":"È","É":"É","É":"É","Ê":"Ê","Ê":"Ê","Ë":"Ë","Ë":"Ë","Ì":"Ì","Ì":"Ì","Í":"Í","Í":"Í","Î":"Î","Î":"Î","Ï":"Ï","Ï":"Ï","Ð":"Ð","Ð":"Ð","Ñ":"Ñ","Ñ":"Ñ","Ò":"Ò","Ò":"Ò","Ó":"Ó","Ó":"Ó","Ô":"Ô","Ô":"Ô","Õ":"Õ","Õ":"Õ","Ö":"Ö","Ö":"Ö","×":"×","×":"×","Ø":"Ø","Ø":"Ø","Ù":"Ù","Ù":"Ù","Ú":"Ú","Ú":"Ú","Û":"Û","Û":"Û","Ü":"Ü","Ü":"Ü","Ý":"Ý","Ý":"Ý","Þ":"Þ","Þ":"Þ","ß":"ß","ß":"ß","à":"à","à":"à","á":"á","á":"á","â":"â","â":"â","ã":"ã","ã":"ã","ä":"ä","ä":"ä","å":"å","å":"å","æ":"æ","æ":"æ","ç":"ç","ç":"ç","è":"è","è":"è","é":"é","é":"é","ê":"ê","ê":"ê","ë":"ë","ë":"ë","ì":"ì","ì":"ì","í":"í","í":"í","î":"î","î":"î","ï":"ï","ï":"ï","ð":"ð","ð":"ð","ñ":"ñ","ñ":"ñ","ò":"ò","ò":"ò","ó":"ó","ó":"ó","ô":"ô","ô":"ô","õ":"õ","õ":"õ","ö":"ö","ö":"ö","÷":"÷","÷":"÷","ø":"ø","ø":"ø","ù":"ù","ù":"ù","ú":"ú","ú":"ú","û":"û","û":"û","ü":"ü","ü":"ü","ý":"ý","ý":"ý","þ":"þ","þ":"þ","ÿ":"ÿ","ÿ":"ÿ",""":'"',""":'"',"&":"&","&":"&","<":"<","<":"<",">":">",">":">","Œ":"Œ","œ":"œ","Š":"Š","š":"š","Ÿ":"Ÿ","ˆ":"ˆ","˜":"˜"," ":" "," ":" "," ":" ","‌":"‌","‍":"‍","‎":"‎","‏":"‏","–":"–","—":"—","‘":"‘","’":"’","‚":"‚","“":"“","”":"”","„":"„","†":"†","‡":"‡","‰":"‰","‹":"‹","›":"›","€":"€","ƒ":"ƒ","Α":"Α","Β":"Β","Γ":"Γ","Δ":"Δ","Ε":"Ε","Ζ":"Ζ","Η":"Η","Θ":"Θ","Ι":"Ι","Κ":"Κ","Λ":"Λ","Μ":"Μ","Ν":"Ν","Ξ":"Ξ","Ο":"Ο","Π":"Π","Ρ":"Ρ","Σ":"Σ","Τ":"Τ","Υ":"Υ","Φ":"Φ","Χ":"Χ","Ψ":"Ψ","Ω":"Ω","α":"α","β":"β","γ":"γ","δ":"δ","ε":"ε","ζ":"ζ","η":"η","θ":"θ","ι":"ι","κ":"κ","λ":"λ","μ":"μ","ν":"ν","ξ":"ξ","ο":"ο","π":"π","ρ":"ρ","ς":"ς","σ":"σ","τ":"τ","υ":"υ","φ":"φ","χ":"χ","ψ":"ψ","ω":"ω","ϑ":"ϑ","ϒ":"ϒ","ϖ":"ϖ","•":"•","…":"…","′":"′","″":"″","‾":"‾","⁄":"⁄","℘":"℘","ℑ":"ℑ","ℜ":"ℜ","™":"™","ℵ":"ℵ","←":"←","↑":"↑","→":"→","↓":"↓","↔":"↔","↵":"↵","⇐":"⇐","⇑":"⇑","⇒":"⇒","⇓":"⇓","⇔":"⇔","∀":"∀","∂":"∂","∃":"∃","∅":"∅","∇":"∇","∈":"∈","∉":"∉","∋":"∋","∏":"∏","∑":"∑","−":"−","∗":"∗","√":"√","∝":"∝","∞":"∞","∠":"∠","∧":"∧","∨":"∨","∩":"∩","∪":"∪","∫":"∫","∴":"∴","∼":"∼","≅":"≅","≈":"≈","≠":"≠","≡":"≡","≤":"≤","≥":"≥","⊂":"⊂","⊃":"⊃","⊄":"⊄","⊆":"⊆","⊇":"⊇","⊕":"⊕","⊗":"⊗","⊥":"⊥","⋅":"⋅","⌈":"⌈","⌉":"⌉","⌊":"⌊","⌋":"⌋","⟨":"〈","⟩":"〉","◊":"◊","♠":"♠","♣":"♣","♥":"♥","♦":"♦"},characters:{"'":"'"," ":" ","¡":"¡","¢":"¢","£":"£","¤":"¤","¥":"¥","¦":"¦","§":"§","¨":"¨","©":"©","ª":"ª","«":"«","¬":"¬","­":"­","®":"®","¯":"¯","°":"°","±":"±","²":"²","³":"³","´":"´","µ":"µ","¶":"¶","·":"·","¸":"¸","¹":"¹","º":"º","»":"»","¼":"¼","½":"½","¾":"¾","¿":"¿","À":"À","Á":"Á","Â":"Â","Ã":"Ã","Ä":"Ä","Å":"Å","Æ":"Æ","Ç":"Ç","È":"È","É":"É","Ê":"Ê","Ë":"Ë","Ì":"Ì","Í":"Í","Î":"Î","Ï":"Ï","Ð":"Ð","Ñ":"Ñ","Ò":"Ò","Ó":"Ó","Ô":"Ô","Õ":"Õ","Ö":"Ö","×":"×","Ø":"Ø","Ù":"Ù","Ú":"Ú","Û":"Û","Ü":"Ü","Ý":"Ý","Þ":"Þ","ß":"ß","à":"à","á":"á","â":"â","ã":"ã","ä":"ä","å":"å","æ":"æ","ç":"ç","è":"è","é":"é","ê":"ê","ë":"ë","ì":"ì","í":"í","î":"î","ï":"ï","ð":"ð","ñ":"ñ","ò":"ò","ó":"ó","ô":"ô","õ":"õ","ö":"ö","÷":"÷","ø":"ø","ù":"ù","ú":"ú","û":"û","ü":"ü","ý":"ý","þ":"þ","ÿ":"ÿ",'"':""","&":"&","<":"<",">":">","Œ":"Œ","œ":"œ","Š":"Š","š":"š","Ÿ":"Ÿ","ˆ":"ˆ","˜":"˜"," ":" "," ":" "," ":" ","‌":"‌","‍":"‍","‎":"‎","‏":"‏","–":"–","—":"—","‘":"‘","’":"’","‚":"‚","“":"“","”":"”","„":"„","†":"†","‡":"‡","‰":"‰","‹":"‹","›":"›","€":"€","ƒ":"ƒ","Α":"Α","Β":"Β","Γ":"Γ","Δ":"Δ","Ε":"Ε","Ζ":"Ζ","Η":"Η","Θ":"Θ","Ι":"Ι","Κ":"Κ","Λ":"Λ","Μ":"Μ","Ν":"Ν","Ξ":"Ξ","Ο":"Ο","Π":"Π","Ρ":"Ρ","Σ":"Σ","Τ":"Τ","Υ":"Υ","Φ":"Φ","Χ":"Χ","Ψ":"Ψ","Ω":"Ω","α":"α","β":"β","γ":"γ","δ":"δ","ε":"ε","ζ":"ζ","η":"η","θ":"θ","ι":"ι","κ":"κ","λ":"λ","μ":"μ","ν":"ν","ξ":"ξ","ο":"ο","π":"π","ρ":"ρ","ς":"ς","σ":"σ","τ":"τ","υ":"υ","φ":"φ","χ":"χ","ψ":"ψ","ω":"ω","ϑ":"ϑ","ϒ":"ϒ","ϖ":"ϖ","•":"•","…":"…","′":"′","″":"″","‾":"‾","⁄":"⁄","℘":"℘","ℑ":"ℑ","ℜ":"ℜ","™":"™","ℵ":"ℵ","←":"←","↑":"↑","→":"→","↓":"↓","↔":"↔","↵":"↵","⇐":"⇐","⇑":"⇑","⇒":"⇒","⇓":"⇓","⇔":"⇔","∀":"∀","∂":"∂","∃":"∃","∅":"∅","∇":"∇","∈":"∈","∉":"∉","∋":"∋","∏":"∏","∑":"∑","−":"−","∗":"∗","√":"√","∝":"∝","∞":"∞","∠":"∠","∧":"∧","∨":"∨","∩":"∩","∪":"∪","∫":"∫","∴":"∴","∼":"∼","≅":"≅","≈":"≈","≠":"≠","≡":"≡","≤":"≤","≥":"≥","⊂":"⊂","⊃":"⊃","⊄":"⊄","⊆":"⊆","⊇":"⊇","⊕":"⊕","⊗":"⊗","⊥":"⊥","⋅":"⋅","⌈":"⌈","⌉":"⌉","⌊":"⌊","⌋":"⌋","〈":"⟨","〉":"⟩","◊":"◊","♠":"♠","♣":"♣","♥":"♥","♦":"♦"}},html5:{entities:{"Æ":"Æ","Æ":"Æ","&":"&","&":"&","Á":"Á","Á":"Á","Ă":"Ă","Â":"Â","Â":"Â","А":"А","𝔄":"𝔄","À":"À","À":"À","Α":"Α","Ā":"Ā","⩓":"⩓","Ą":"Ą","𝔸":"𝔸","⁡":"⁡","Å":"Å","Å":"Å","𝒜":"𝒜","≔":"≔","Ã":"Ã","Ã":"Ã","Ä":"Ä","Ä":"Ä","∖":"∖","⫧":"⫧","⌆":"⌆","Б":"Б","∵":"∵","ℬ":"ℬ","Β":"Β","𝔅":"𝔅","𝔹":"𝔹","˘":"˘","ℬ":"ℬ","≎":"≎","Ч":"Ч","©":"©","©":"©","Ć":"Ć","⋒":"⋒","ⅅ":"ⅅ","ℭ":"ℭ","Č":"Č","Ç":"Ç","Ç":"Ç","Ĉ":"Ĉ","∰":"∰","Ċ":"Ċ","¸":"¸","·":"·","ℭ":"ℭ","Χ":"Χ","⊙":"⊙","⊖":"⊖","⊕":"⊕","⊗":"⊗","∲":"∲","”":"”","’":"’","∷":"∷","⩴":"⩴","≡":"≡","∯":"∯","∮":"∮","ℂ":"ℂ","∐":"∐","∳":"∳","⨯":"⨯","𝒞":"𝒞","⋓":"⋓","≍":"≍","ⅅ":"ⅅ","⤑":"⤑","Ђ":"Ђ","Ѕ":"Ѕ","Џ":"Џ","‡":"‡","↡":"↡","⫤":"⫤","Ď":"Ď","Д":"Д","∇":"∇","Δ":"Δ","𝔇":"𝔇","´":"´","˙":"˙","˝":"˝","`":"`","˜":"˜","⋄":"⋄","ⅆ":"ⅆ","𝔻":"𝔻","¨":"¨","⃜":"⃜","≐":"≐","∯":"∯","¨":"¨","⇓":"⇓","⇐":"⇐","⇔":"⇔","⫤":"⫤","⟸":"⟸","⟺":"⟺","⟹":"⟹","⇒":"⇒","⊨":"⊨","⇑":"⇑","⇕":"⇕","∥":"∥","↓":"↓","⤓":"⤓","⇵":"⇵","̑":"̑","⥐":"⥐","⥞":"⥞","↽":"↽","⥖":"⥖","⥟":"⥟","⇁":"⇁","⥗":"⥗","⊤":"⊤","↧":"↧","⇓":"⇓","𝒟":"𝒟","Đ":"Đ","Ŋ":"Ŋ","Ð":"Ð","Ð":"Ð","É":"É","É":"É","Ě":"Ě","Ê":"Ê","Ê":"Ê","Э":"Э","Ė":"Ė","𝔈":"𝔈","È":"È","È":"È","∈":"∈","Ē":"Ē","◻":"◻","▫":"▫","Ę":"Ę","𝔼":"𝔼","Ε":"Ε","⩵":"⩵","≂":"≂","⇌":"⇌","ℰ":"ℰ","⩳":"⩳","Η":"Η","Ë":"Ë","Ë":"Ë","∃":"∃","ⅇ":"ⅇ","Ф":"Ф","𝔉":"𝔉","◼":"◼","▪":"▪","𝔽":"𝔽","∀":"∀","ℱ":"ℱ","ℱ":"ℱ","Ѓ":"Ѓ",">":">",">":">","Γ":"Γ","Ϝ":"Ϝ","Ğ":"Ğ","Ģ":"Ģ","Ĝ":"Ĝ","Г":"Г","Ġ":"Ġ","𝔊":"𝔊","⋙":"⋙","𝔾":"𝔾","≥":"≥","⋛":"⋛","≧":"≧","⪢":"⪢","≷":"≷","⩾":"⩾","≳":"≳","𝒢":"𝒢","≫":"≫","Ъ":"Ъ","ˇ":"ˇ","^":"^","Ĥ":"Ĥ","ℌ":"ℌ","ℋ":"ℋ","ℍ":"ℍ","─":"─","ℋ":"ℋ","Ħ":"Ħ","≎":"≎","≏":"≏","Е":"Е","IJ":"IJ","Ё":"Ё","Í":"Í","Í":"Í","Î":"Î","Î":"Î","И":"И","İ":"İ","ℑ":"ℑ","Ì":"Ì","Ì":"Ì","ℑ":"ℑ","Ī":"Ī","ⅈ":"ⅈ","⇒":"⇒","∬":"∬","∫":"∫","⋂":"⋂","⁣":"⁣","⁢":"⁢","Į":"Į","𝕀":"𝕀","Ι":"Ι","ℐ":"ℐ","Ĩ":"Ĩ","І":"І","Ï":"Ï","Ï":"Ï","Ĵ":"Ĵ","Й":"Й","𝔍":"𝔍","𝕁":"𝕁","𝒥":"𝒥","Ј":"Ј","Є":"Є","Х":"Х","Ќ":"Ќ","Κ":"Κ","Ķ":"Ķ","К":"К","𝔎":"𝔎","𝕂":"𝕂","𝒦":"𝒦","Љ":"Љ","<":"<","<":"<","Ĺ":"Ĺ","Λ":"Λ","⟪":"⟪","ℒ":"ℒ","↞":"↞","Ľ":"Ľ","Ļ":"Ļ","Л":"Л","⟨":"⟨","←":"←","⇤":"⇤","⇆":"⇆","⌈":"⌈","⟦":"⟦","⥡":"⥡","⇃":"⇃","⥙":"⥙","⌊":"⌊","↔":"↔","⥎":"⥎","⊣":"⊣","↤":"↤","⥚":"⥚","⊲":"⊲","⧏":"⧏","⊴":"⊴","⥑":"⥑","⥠":"⥠","↿":"↿","⥘":"⥘","↼":"↼","⥒":"⥒","⇐":"⇐","⇔":"⇔","⋚":"⋚","≦":"≦","≶":"≶","⪡":"⪡","⩽":"⩽","≲":"≲","𝔏":"𝔏","⋘":"⋘","⇚":"⇚","Ŀ":"Ŀ","⟵":"⟵","⟷":"⟷","⟶":"⟶","⟸":"⟸","⟺":"⟺","⟹":"⟹","𝕃":"𝕃","↙":"↙","↘":"↘","ℒ":"ℒ","↰":"↰","Ł":"Ł","≪":"≪","⤅":"⤅","М":"М"," ":" ","ℳ":"ℳ","𝔐":"𝔐","∓":"∓","𝕄":"𝕄","ℳ":"ℳ","Μ":"Μ","Њ":"Њ","Ń":"Ń","Ň":"Ň","Ņ":"Ņ","Н":"Н","​":"​","​":"​","​":"​","​":"​","≫":"≫","≪":"≪"," ":"\n","𝔑":"𝔑","⁠":"⁠"," ":" ","ℕ":"ℕ","⫬":"⫬","≢":"≢","≭":"≭","∦":"∦","∉":"∉","≠":"≠","≂̸":"≂̸","∄":"∄","≯":"≯","≱":"≱","≧̸":"≧̸","≫̸":"≫̸","≹":"≹","⩾̸":"⩾̸","≵":"≵","≎̸":"≎̸","≏̸":"≏̸","⋪":"⋪","⧏̸":"⧏̸","⋬":"⋬","≮":"≮","≰":"≰","≸":"≸","≪̸":"≪̸","⩽̸":"⩽̸","≴":"≴","⪢̸":"⪢̸","⪡̸":"⪡̸","⊀":"⊀","⪯̸":"⪯̸","⋠":"⋠","∌":"∌","⋫":"⋫","⧐̸":"⧐̸","⋭":"⋭","⊏̸":"⊏̸","⋢":"⋢","⊐̸":"⊐̸","⋣":"⋣","⊂⃒":"⊂⃒","⊈":"⊈","⊁":"⊁","⪰̸":"⪰̸","⋡":"⋡","≿̸":"≿̸","⊃⃒":"⊃⃒","⊉":"⊉","≁":"≁","≄":"≄","≇":"≇","≉":"≉","∤":"∤","𝒩":"𝒩","Ñ":"Ñ","Ñ":"Ñ","Ν":"Ν","Œ":"Œ","Ó":"Ó","Ó":"Ó","Ô":"Ô","Ô":"Ô","О":"О","Ő":"Ő","𝔒":"𝔒","Ò":"Ò","Ò":"Ò","Ō":"Ō","Ω":"Ω","Ο":"Ο","𝕆":"𝕆","“":"“","‘":"‘","⩔":"⩔","𝒪":"𝒪","Ø":"Ø","Ø":"Ø","Õ":"Õ","Õ":"Õ","⨷":"⨷","Ö":"Ö","Ö":"Ö","‾":"‾","⏞":"⏞","⎴":"⎴","⏜":"⏜","∂":"∂","П":"П","𝔓":"𝔓","Φ":"Φ","Π":"Π","±":"±","ℌ":"ℌ","ℙ":"ℙ","⪻":"⪻","≺":"≺","⪯":"⪯","≼":"≼","≾":"≾","″":"″","∏":"∏","∷":"∷","∝":"∝","𝒫":"𝒫","Ψ":"Ψ",""":'"',""":'"',"𝔔":"𝔔","ℚ":"ℚ","𝒬":"𝒬","⤐":"⤐","®":"®","®":"®","Ŕ":"Ŕ","⟫":"⟫","↠":"↠","⤖":"⤖","Ř":"Ř","Ŗ":"Ŗ","Р":"Р","ℜ":"ℜ","∋":"∋","⇋":"⇋","⥯":"⥯","ℜ":"ℜ","Ρ":"Ρ","⟩":"⟩","→":"→","⇥":"⇥","⇄":"⇄","⌉":"⌉","⟧":"⟧","⥝":"⥝","⇂":"⇂","⥕":"⥕","⌋":"⌋","⊢":"⊢","↦":"↦","⥛":"⥛","⊳":"⊳","⧐":"⧐","⊵":"⊵","⥏":"⥏","⥜":"⥜","↾":"↾","⥔":"⥔","⇀":"⇀","⥓":"⥓","⇒":"⇒","ℝ":"ℝ","⥰":"⥰","⇛":"⇛","ℛ":"ℛ","↱":"↱","⧴":"⧴","Щ":"Щ","Ш":"Ш","Ь":"Ь","Ś":"Ś","⪼":"⪼","Š":"Š","Ş":"Ş","Ŝ":"Ŝ","С":"С","𝔖":"𝔖","↓":"↓","←":"←","→":"→","↑":"↑","Σ":"Σ","∘":"∘","𝕊":"𝕊","√":"√","□":"□","⊓":"⊓","⊏":"⊏","⊑":"⊑","⊐":"⊐","⊒":"⊒","⊔":"⊔","𝒮":"𝒮","⋆":"⋆","⋐":"⋐","⋐":"⋐","⊆":"⊆","≻":"≻","⪰":"⪰","≽":"≽","≿":"≿","∋":"∋","∑":"∑","⋑":"⋑","⊃":"⊃","⊇":"⊇","⋑":"⋑","Þ":"Þ","Þ":"Þ","™":"™","Ћ":"Ћ","Ц":"Ц"," ":"\t","Τ":"Τ","Ť":"Ť","Ţ":"Ţ","Т":"Т","𝔗":"𝔗","∴":"∴","Θ":"Θ","  ":"  "," ":" ","∼":"∼","≃":"≃","≅":"≅","≈":"≈","𝕋":"𝕋","⃛":"⃛","𝒯":"𝒯","Ŧ":"Ŧ","Ú":"Ú","Ú":"Ú","↟":"↟","⥉":"⥉","Ў":"Ў","Ŭ":"Ŭ","Û":"Û","Û":"Û","У":"У","Ű":"Ű","𝔘":"𝔘","Ù":"Ù","Ù":"Ù","Ū":"Ū","_":"_","⏟":"⏟","⎵":"⎵","⏝":"⏝","⋃":"⋃","⊎":"⊎","Ų":"Ų","𝕌":"𝕌","↑":"↑","⤒":"⤒","⇅":"⇅","↕":"↕","⥮":"⥮","⊥":"⊥","↥":"↥","⇑":"⇑","⇕":"⇕","↖":"↖","↗":"↗","ϒ":"ϒ","Υ":"Υ","Ů":"Ů","𝒰":"𝒰","Ũ":"Ũ","Ü":"Ü","Ü":"Ü","⊫":"⊫","⫫":"⫫","В":"В","⊩":"⊩","⫦":"⫦","⋁":"⋁","‖":"‖","‖":"‖","∣":"∣","|":"|","❘":"❘","≀":"≀"," ":" ","𝔙":"𝔙","𝕍":"𝕍","𝒱":"𝒱","⊪":"⊪","Ŵ":"Ŵ","⋀":"⋀","𝔚":"𝔚","𝕎":"𝕎","𝒲":"𝒲","𝔛":"𝔛","Ξ":"Ξ","𝕏":"𝕏","𝒳":"𝒳","Я":"Я","Ї":"Ї","Ю":"Ю","Ý":"Ý","Ý":"Ý","Ŷ":"Ŷ","Ы":"Ы","𝔜":"𝔜","𝕐":"𝕐","𝒴":"𝒴","Ÿ":"Ÿ","Ж":"Ж","Ź":"Ź","Ž":"Ž","З":"З","Ż":"Ż","​":"​","Ζ":"Ζ","ℨ":"ℨ","ℤ":"ℤ","𝒵":"𝒵","á":"á","á":"á","ă":"ă","∾":"∾","∾̳":"∾̳","∿":"∿","â":"â","â":"â","´":"´","´":"´","а":"а","æ":"æ","æ":"æ","⁡":"⁡","𝔞":"𝔞","à":"à","à":"à","ℵ":"ℵ","ℵ":"ℵ","α":"α","ā":"ā","⨿":"⨿","&":"&","&":"&","∧":"∧","⩕":"⩕","⩜":"⩜","⩘":"⩘","⩚":"⩚","∠":"∠","⦤":"⦤","∠":"∠","∡":"∡","⦨":"⦨","⦩":"⦩","⦪":"⦪","⦫":"⦫","⦬":"⦬","⦭":"⦭","⦮":"⦮","⦯":"⦯","∟":"∟","⊾":"⊾","⦝":"⦝","∢":"∢","Å":"Å","⍼":"⍼","ą":"ą","𝕒":"𝕒","≈":"≈","⩰":"⩰","⩯":"⩯","≊":"≊","≋":"≋","'":"'","≈":"≈","≊":"≊","å":"å","å":"å","𝒶":"𝒶","*":"*","≈":"≈","≍":"≍","ã":"ã","ã":"ã","ä":"ä","ä":"ä","∳":"∳","⨑":"⨑","⫭":"⫭","≌":"≌","϶":"϶","‵":"‵","∽":"∽","⋍":"⋍","⊽":"⊽","⌅":"⌅","⌅":"⌅","⎵":"⎵","⎶":"⎶","≌":"≌","б":"б","„":"„","∵":"∵","∵":"∵","⦰":"⦰","϶":"϶","ℬ":"ℬ","β":"β","ℶ":"ℶ","≬":"≬","𝔟":"𝔟","⋂":"⋂","◯":"◯","⋃":"⋃","⨀":"⨀","⨁":"⨁","⨂":"⨂","⨆":"⨆","★":"★","▽":"▽","△":"△","⨄":"⨄","⋁":"⋁","⋀":"⋀","⤍":"⤍","⧫":"⧫","▪":"▪","▴":"▴","▾":"▾","◂":"◂","▸":"▸","␣":"␣","▒":"▒","░":"░","▓":"▓","█":"█","=⃥":"=⃥","≡⃥":"≡⃥","⌐":"⌐","𝕓":"𝕓","⊥":"⊥","⊥":"⊥","⋈":"⋈","╗":"╗","╔":"╔","╖":"╖","╓":"╓","═":"═","╦":"╦","╩":"╩","╤":"╤","╧":"╧","╝":"╝","╚":"╚","╜":"╜","╙":"╙","║":"║","╬":"╬","╣":"╣","╠":"╠","╫":"╫","╢":"╢","╟":"╟","⧉":"⧉","╕":"╕","╒":"╒","┐":"┐","┌":"┌","─":"─","╥":"╥","╨":"╨","┬":"┬","┴":"┴","⊟":"⊟","⊞":"⊞","⊠":"⊠","╛":"╛","╘":"╘","┘":"┘","└":"└","│":"│","╪":"╪","╡":"╡","╞":"╞","┼":"┼","┤":"┤","├":"├","‵":"‵","˘":"˘","¦":"¦","¦":"¦","𝒷":"𝒷","⁏":"⁏","∽":"∽","⋍":"⋍","\":"\\","⧅":"⧅","⟈":"⟈","•":"•","•":"•","≎":"≎","⪮":"⪮","≏":"≏","≏":"≏","ć":"ć","∩":"∩","⩄":"⩄","⩉":"⩉","⩋":"⩋","⩇":"⩇","⩀":"⩀","∩︀":"∩︀","⁁":"⁁","ˇ":"ˇ","⩍":"⩍","č":"č","ç":"ç","ç":"ç","ĉ":"ĉ","⩌":"⩌","⩐":"⩐","ċ":"ċ","¸":"¸","¸":"¸","⦲":"⦲","¢":"¢","¢":"¢","·":"·","𝔠":"𝔠","ч":"ч","✓":"✓","✓":"✓","χ":"χ","○":"○","⧃":"⧃","ˆ":"ˆ","≗":"≗","↺":"↺","↻":"↻","®":"®","Ⓢ":"Ⓢ","⊛":"⊛","⊚":"⊚","⊝":"⊝","≗":"≗","⨐":"⨐","⫯":"⫯","⧂":"⧂","♣":"♣","♣":"♣",":":":","≔":"≔","≔":"≔",",":",","@":"@","∁":"∁","∘":"∘","∁":"∁","ℂ":"ℂ","≅":"≅","⩭":"⩭","∮":"∮","𝕔":"𝕔","∐":"∐","©":"©","©":"©","℗":"℗","↵":"↵","✗":"✗","𝒸":"𝒸","⫏":"⫏","⫑":"⫑","⫐":"⫐","⫒":"⫒","⋯":"⋯","⤸":"⤸","⤵":"⤵","⋞":"⋞","⋟":"⋟","↶":"↶","⤽":"⤽","∪":"∪","⩈":"⩈","⩆":"⩆","⩊":"⩊","⊍":"⊍","⩅":"⩅","∪︀":"∪︀","↷":"↷","⤼":"⤼","⋞":"⋞","⋟":"⋟","⋎":"⋎","⋏":"⋏","¤":"¤","¤":"¤","↶":"↶","↷":"↷","⋎":"⋎","⋏":"⋏","∲":"∲","∱":"∱","⌭":"⌭","⇓":"⇓","⥥":"⥥","†":"†","ℸ":"ℸ","↓":"↓","‐":"‐","⊣":"⊣","⤏":"⤏","˝":"˝","ď":"ď","д":"д","ⅆ":"ⅆ","‡":"‡","⇊":"⇊","⩷":"⩷","°":"°","°":"°","δ":"δ","⦱":"⦱","⥿":"⥿","𝔡":"𝔡","⇃":"⇃","⇂":"⇂","⋄":"⋄","⋄":"⋄","♦":"♦","♦":"♦","¨":"¨","ϝ":"ϝ","⋲":"⋲","÷":"÷","÷":"÷","÷":"÷","⋇":"⋇","⋇":"⋇","ђ":"ђ","⌞":"⌞","⌍":"⌍","$":"$","𝕕":"𝕕","˙":"˙","≐":"≐","≑":"≑","∸":"∸","∔":"∔","⊡":"⊡","⌆":"⌆","↓":"↓","⇊":"⇊","⇃":"⇃","⇂":"⇂","⤐":"⤐","⌟":"⌟","⌌":"⌌","𝒹":"𝒹","ѕ":"ѕ","⧶":"⧶","đ":"đ","⋱":"⋱","▿":"▿","▾":"▾","⇵":"⇵","⥯":"⥯","⦦":"⦦","џ":"џ","⟿":"⟿","⩷":"⩷","≑":"≑","é":"é","é":"é","⩮":"⩮","ě":"ě","≖":"≖","ê":"ê","ê":"ê","≕":"≕","э":"э","ė":"ė","ⅇ":"ⅇ","≒":"≒","𝔢":"𝔢","⪚":"⪚","è":"è","è":"è","⪖":"⪖","⪘":"⪘","⪙":"⪙","⏧":"⏧","ℓ":"ℓ","⪕":"⪕","⪗":"⪗","ē":"ē","∅":"∅","∅":"∅","∅":"∅"," ":" "," ":" "," ":" ","ŋ":"ŋ"," ":" ","ę":"ę","𝕖":"𝕖","⋕":"⋕","⧣":"⧣","⩱":"⩱","ε":"ε","ε":"ε","ϵ":"ϵ","≖":"≖","≕":"≕","≂":"≂","⪖":"⪖","⪕":"⪕","=":"=","≟":"≟","≡":"≡","⩸":"⩸","⧥":"⧥","≓":"≓","⥱":"⥱","ℯ":"ℯ","≐":"≐","≂":"≂","η":"η","ð":"ð","ð":"ð","ë":"ë","ë":"ë","€":"€","!":"!","∃":"∃","ℰ":"ℰ","ⅇ":"ⅇ","≒":"≒","ф":"ф","♀":"♀","ffi":"ffi","ff":"ff","ffl":"ffl","𝔣":"𝔣","fi":"fi","fj":"fj","♭":"♭","fl":"fl","▱":"▱","ƒ":"ƒ","𝕗":"𝕗","∀":"∀","⋔":"⋔","⫙":"⫙","⨍":"⨍","½":"½","½":"½","⅓":"⅓","¼":"¼","¼":"¼","⅕":"⅕","⅙":"⅙","⅛":"⅛","⅔":"⅔","⅖":"⅖","¾":"¾","¾":"¾","⅗":"⅗","⅜":"⅜","⅘":"⅘","⅚":"⅚","⅝":"⅝","⅞":"⅞","⁄":"⁄","⌢":"⌢","𝒻":"𝒻","≧":"≧","⪌":"⪌","ǵ":"ǵ","γ":"γ","ϝ":"ϝ","⪆":"⪆","ğ":"ğ","ĝ":"ĝ","г":"г","ġ":"ġ","≥":"≥","⋛":"⋛","≥":"≥","≧":"≧","⩾":"⩾","⩾":"⩾","⪩":"⪩","⪀":"⪀","⪂":"⪂","⪄":"⪄","⋛︀":"⋛︀","⪔":"⪔","𝔤":"𝔤","≫":"≫","⋙":"⋙","ℷ":"ℷ","ѓ":"ѓ","≷":"≷","⪒":"⪒","⪥":"⪥","⪤":"⪤","≩":"≩","⪊":"⪊","⪊":"⪊","⪈":"⪈","⪈":"⪈","≩":"≩","⋧":"⋧","𝕘":"𝕘","`":"`","ℊ":"ℊ","≳":"≳","⪎":"⪎","⪐":"⪐",">":">",">":">","⪧":"⪧","⩺":"⩺","⋗":"⋗","⦕":"⦕","⩼":"⩼","⪆":"⪆","⥸":"⥸","⋗":"⋗","⋛":"⋛","⪌":"⪌","≷":"≷","≳":"≳","≩︀":"≩︀","≩︀":"≩︀","⇔":"⇔"," ":" ","½":"½","ℋ":"ℋ","ъ":"ъ","↔":"↔","⥈":"⥈","↭":"↭","ℏ":"ℏ","ĥ":"ĥ","♥":"♥","♥":"♥","…":"…","⊹":"⊹","𝔥":"𝔥","⤥":"⤥","⤦":"⤦","⇿":"⇿","∻":"∻","↩":"↩","↪":"↪","𝕙":"𝕙","―":"―","𝒽":"𝒽","ℏ":"ℏ","ħ":"ħ","⁃":"⁃","‐":"‐","í":"í","í":"í","⁣":"⁣","î":"î","î":"î","и":"и","е":"е","¡":"¡","¡":"¡","⇔":"⇔","𝔦":"𝔦","ì":"ì","ì":"ì","ⅈ":"ⅈ","⨌":"⨌","∭":"∭","⧜":"⧜","℩":"℩","ij":"ij","ī":"ī","ℑ":"ℑ","ℐ":"ℐ","ℑ":"ℑ","ı":"ı","⊷":"⊷","Ƶ":"Ƶ","∈":"∈","℅":"℅","∞":"∞","⧝":"⧝","ı":"ı","∫":"∫","⊺":"⊺","ℤ":"ℤ","⊺":"⊺","⨗":"⨗","⨼":"⨼","ё":"ё","į":"į","𝕚":"𝕚","ι":"ι","⨼":"⨼","¿":"¿","¿":"¿","𝒾":"𝒾","∈":"∈","⋹":"⋹","⋵":"⋵","⋴":"⋴","⋳":"⋳","∈":"∈","⁢":"⁢","ĩ":"ĩ","і":"і","ï":"ï","ï":"ï","ĵ":"ĵ","й":"й","𝔧":"𝔧","ȷ":"ȷ","𝕛":"𝕛","𝒿":"𝒿","ј":"ј","є":"є","κ":"κ","ϰ":"ϰ","ķ":"ķ","к":"к","𝔨":"𝔨","ĸ":"ĸ","х":"х","ќ":"ќ","𝕜":"𝕜","𝓀":"𝓀","⇚":"⇚","⇐":"⇐","⤛":"⤛","⤎":"⤎","≦":"≦","⪋":"⪋","⥢":"⥢","ĺ":"ĺ","⦴":"⦴","ℒ":"ℒ","λ":"λ","⟨":"⟨","⦑":"⦑","⟨":"⟨","⪅":"⪅","«":"«","«":"«","←":"←","⇤":"⇤","⤟":"⤟","⤝":"⤝","↩":"↩","↫":"↫","⤹":"⤹","⥳":"⥳","↢":"↢","⪫":"⪫","⤙":"⤙","⪭":"⪭","⪭︀":"⪭︀","⤌":"⤌","❲":"❲","{":"{","[":"[","⦋":"⦋","⦏":"⦏","⦍":"⦍","ľ":"ľ","ļ":"ļ","⌈":"⌈","{":"{","л":"л","⤶":"⤶","“":"“","„":"„","⥧":"⥧","⥋":"⥋","↲":"↲","≤":"≤","←":"←","↢":"↢","↽":"↽","↼":"↼","⇇":"⇇","↔":"↔","⇆":"⇆","⇋":"⇋","↭":"↭","⋋":"⋋","⋚":"⋚","≤":"≤","≦":"≦","⩽":"⩽","⩽":"⩽","⪨":"⪨","⩿":"⩿","⪁":"⪁","⪃":"⪃","⋚︀":"⋚︀","⪓":"⪓","⪅":"⪅","⋖":"⋖","⋚":"⋚","⪋":"⪋","≶":"≶","≲":"≲","⥼":"⥼","⌊":"⌊","𝔩":"𝔩","≶":"≶","⪑":"⪑","↽":"↽","↼":"↼","⥪":"⥪","▄":"▄","љ":"љ","≪":"≪","⇇":"⇇","⌞":"⌞","⥫":"⥫","◺":"◺","ŀ":"ŀ","⎰":"⎰","⎰":"⎰","≨":"≨","⪉":"⪉","⪉":"⪉","⪇":"⪇","⪇":"⪇","≨":"≨","⋦":"⋦","⟬":"⟬","⇽":"⇽","⟦":"⟦","⟵":"⟵","⟷":"⟷","⟼":"⟼","⟶":"⟶","↫":"↫","↬":"↬","⦅":"⦅","𝕝":"𝕝","⨭":"⨭","⨴":"⨴","∗":"∗","_":"_","◊":"◊","◊":"◊","⧫":"⧫","(":"(","⦓":"⦓","⇆":"⇆","⌟":"⌟","⇋":"⇋","⥭":"⥭","‎":"‎","⊿":"⊿","‹":"‹","𝓁":"𝓁","↰":"↰","≲":"≲","⪍":"⪍","⪏":"⪏","[":"[","‘":"‘","‚":"‚","ł":"ł","<":"<","<":"<","⪦":"⪦","⩹":"⩹","⋖":"⋖","⋋":"⋋","⋉":"⋉","⥶":"⥶","⩻":"⩻","⦖":"⦖","◃":"◃","⊴":"⊴","◂":"◂","⥊":"⥊","⥦":"⥦","≨︀":"≨︀","≨︀":"≨︀","∺":"∺","¯":"¯","¯":"¯","♂":"♂","✠":"✠","✠":"✠","↦":"↦","↦":"↦","↧":"↧","↤":"↤","↥":"↥","▮":"▮","⨩":"⨩","м":"м","—":"—","∡":"∡","𝔪":"𝔪","℧":"℧","µ":"µ","µ":"µ","∣":"∣","*":"*","⫰":"⫰","·":"·","·":"·","−":"−","⊟":"⊟","∸":"∸","⨪":"⨪","⫛":"⫛","…":"…","∓":"∓","⊧":"⊧","𝕞":"𝕞","∓":"∓","𝓂":"𝓂","∾":"∾","μ":"μ","⊸":"⊸","⊸":"⊸","⋙̸":"⋙̸","≫⃒":"≫⃒","≫̸":"≫̸","⇍":"⇍","⇎":"⇎","⋘̸":"⋘̸","≪⃒":"≪⃒","≪̸":"≪̸","⇏":"⇏","⊯":"⊯","⊮":"⊮","∇":"∇","ń":"ń","∠⃒":"∠⃒","≉":"≉","⩰̸":"⩰̸","≋̸":"≋̸","ʼn":"ʼn","≉":"≉","♮":"♮","♮":"♮","ℕ":"ℕ"," ":" "," ":" ","≎̸":"≎̸","≏̸":"≏̸","⩃":"⩃","ň":"ň","ņ":"ņ","≇":"≇","⩭̸":"⩭̸","⩂":"⩂","н":"н","–":"–","≠":"≠","⇗":"⇗","⤤":"⤤","↗":"↗","↗":"↗","≐̸":"≐̸","≢":"≢","⤨":"⤨","≂̸":"≂̸","∄":"∄","∄":"∄","𝔫":"𝔫","≧̸":"≧̸","≱":"≱","≱":"≱","≧̸":"≧̸","⩾̸":"⩾̸","⩾̸":"⩾̸","≵":"≵","≯":"≯","≯":"≯","⇎":"⇎","↮":"↮","⫲":"⫲","∋":"∋","⋼":"⋼","⋺":"⋺","∋":"∋","њ":"њ","⇍":"⇍","≦̸":"≦̸","↚":"↚","‥":"‥","≰":"≰","↚":"↚","↮":"↮","≰":"≰","≦̸":"≦̸","⩽̸":"⩽̸","⩽̸":"⩽̸","≮":"≮","≴":"≴","≮":"≮","⋪":"⋪","⋬":"⋬","∤":"∤","𝕟":"𝕟","¬":"¬","¬":"¬","∉":"∉","⋹̸":"⋹̸","⋵̸":"⋵̸","∉":"∉","⋷":"⋷","⋶":"⋶","∌":"∌","∌":"∌","⋾":"⋾","⋽":"⋽","∦":"∦","∦":"∦","⫽⃥":"⫽⃥","∂̸":"∂̸","⨔":"⨔","⊀":"⊀","⋠":"⋠","⪯̸":"⪯̸","⊀":"⊀","⪯̸":"⪯̸","⇏":"⇏","↛":"↛","⤳̸":"⤳̸","↝̸":"↝̸","↛":"↛","⋫":"⋫","⋭":"⋭","⊁":"⊁","⋡":"⋡","⪰̸":"⪰̸","𝓃":"𝓃","∤":"∤","∦":"∦","≁":"≁","≄":"≄","≄":"≄","∤":"∤","∦":"∦","⋢":"⋢","⋣":"⋣","⊄":"⊄","⫅̸":"⫅̸","⊈":"⊈","⊂⃒":"⊂⃒","⊈":"⊈","⫅̸":"⫅̸","⊁":"⊁","⪰̸":"⪰̸","⊅":"⊅","⫆̸":"⫆̸","⊉":"⊉","⊃⃒":"⊃⃒","⊉":"⊉","⫆̸":"⫆̸","≹":"≹","ñ":"ñ","ñ":"ñ","≸":"≸","⋪":"⋪","⋬":"⋬","⋫":"⋫","⋭":"⋭","ν":"ν","#":"#","№":"№"," ":" ","⊭":"⊭","⤄":"⤄","≍⃒":"≍⃒","⊬":"⊬","≥⃒":"≥⃒",">⃒":">⃒","⧞":"⧞","⤂":"⤂","≤⃒":"≤⃒","<⃒":"<⃒","⊴⃒":"⊴⃒","⤃":"⤃","⊵⃒":"⊵⃒","∼⃒":"∼⃒","⇖":"⇖","⤣":"⤣","↖":"↖","↖":"↖","⤧":"⤧","Ⓢ":"Ⓢ","ó":"ó","ó":"ó","⊛":"⊛","⊚":"⊚","ô":"ô","ô":"ô","о":"о","⊝":"⊝","ő":"ő","⨸":"⨸","⊙":"⊙","⦼":"⦼","œ":"œ","⦿":"⦿","𝔬":"𝔬","˛":"˛","ò":"ò","ò":"ò","⧁":"⧁","⦵":"⦵","Ω":"Ω","∮":"∮","↺":"↺","⦾":"⦾","⦻":"⦻","‾":"‾","⧀":"⧀","ō":"ō","ω":"ω","ο":"ο","⦶":"⦶","⊖":"⊖","𝕠":"𝕠","⦷":"⦷","⦹":"⦹","⊕":"⊕","∨":"∨","↻":"↻","⩝":"⩝","ℴ":"ℴ","ℴ":"ℴ","ª":"ª","ª":"ª","º":"º","º":"º","⊶":"⊶","⩖":"⩖","⩗":"⩗","⩛":"⩛","ℴ":"ℴ","ø":"ø","ø":"ø","⊘":"⊘","õ":"õ","õ":"õ","⊗":"⊗","⨶":"⨶","ö":"ö","ö":"ö","⌽":"⌽","∥":"∥","¶":"¶","¶":"¶","∥":"∥","⫳":"⫳","⫽":"⫽","∂":"∂","п":"п","%":"%",".":".","‰":"‰","⊥":"⊥","‱":"‱","𝔭":"𝔭","φ":"φ","ϕ":"ϕ","ℳ":"ℳ","☎":"☎","π":"π","⋔":"⋔","ϖ":"ϖ","ℏ":"ℏ","ℎ":"ℎ","ℏ":"ℏ","+":"+","⨣":"⨣","⊞":"⊞","⨢":"⨢","∔":"∔","⨥":"⨥","⩲":"⩲","±":"±","±":"±","⨦":"⨦","⨧":"⨧","±":"±","⨕":"⨕","𝕡":"𝕡","£":"£","£":"£","≺":"≺","⪳":"⪳","⪷":"⪷","≼":"≼","⪯":"⪯","≺":"≺","⪷":"⪷","≼":"≼","⪯":"⪯","⪹":"⪹","⪵":"⪵","⋨":"⋨","≾":"≾","′":"′","ℙ":"ℙ","⪵":"⪵","⪹":"⪹","⋨":"⋨","∏":"∏","⌮":"⌮","⌒":"⌒","⌓":"⌓","∝":"∝","∝":"∝","≾":"≾","⊰":"⊰","𝓅":"𝓅","ψ":"ψ"," ":" ","𝔮":"𝔮","⨌":"⨌","𝕢":"𝕢","⁗":"⁗","𝓆":"𝓆","ℍ":"ℍ","⨖":"⨖","?":"?","≟":"≟",""":'"',""":'"',"⇛":"⇛","⇒":"⇒","⤜":"⤜","⤏":"⤏","⥤":"⥤","∽̱":"∽̱","ŕ":"ŕ","√":"√","⦳":"⦳","⟩":"⟩","⦒":"⦒","⦥":"⦥","⟩":"⟩","»":"»","»":"»","→":"→","⥵":"⥵","⇥":"⇥","⤠":"⤠","⤳":"⤳","⤞":"⤞","↪":"↪","↬":"↬","⥅":"⥅","⥴":"⥴","↣":"↣","↝":"↝","⤚":"⤚","∶":"∶","ℚ":"ℚ","⤍":"⤍","❳":"❳","}":"}","]":"]","⦌":"⦌","⦎":"⦎","⦐":"⦐","ř":"ř","ŗ":"ŗ","⌉":"⌉","}":"}","р":"р","⤷":"⤷","⥩":"⥩","”":"”","”":"”","↳":"↳","ℜ":"ℜ","ℛ":"ℛ","ℜ":"ℜ","ℝ":"ℝ","▭":"▭","®":"®","®":"®","⥽":"⥽","⌋":"⌋","𝔯":"𝔯","⇁":"⇁","⇀":"⇀","⥬":"⥬","ρ":"ρ","ϱ":"ϱ","→":"→","↣":"↣","⇁":"⇁","⇀":"⇀","⇄":"⇄","⇌":"⇌","⇉":"⇉","↝":"↝","⋌":"⋌","˚":"˚","≓":"≓","⇄":"⇄","⇌":"⇌","‏":"‏","⎱":"⎱","⎱":"⎱","⫮":"⫮","⟭":"⟭","⇾":"⇾","⟧":"⟧","⦆":"⦆","𝕣":"𝕣","⨮":"⨮","⨵":"⨵",")":")","⦔":"⦔","⨒":"⨒","⇉":"⇉","›":"›","𝓇":"𝓇","↱":"↱","]":"]","’":"’","’":"’","⋌":"⋌","⋊":"⋊","▹":"▹","⊵":"⊵","▸":"▸","⧎":"⧎","⥨":"⥨","℞":"℞","ś":"ś","‚":"‚","≻":"≻","⪴":"⪴","⪸":"⪸","š":"š","≽":"≽","⪰":"⪰","ş":"ş","ŝ":"ŝ","⪶":"⪶","⪺":"⪺","⋩":"⋩","⨓":"⨓","≿":"≿","с":"с","⋅":"⋅","⊡":"⊡","⩦":"⩦","⇘":"⇘","⤥":"⤥","↘":"↘","↘":"↘","§":"§","§":"§",";":";","⤩":"⤩","∖":"∖","∖":"∖","✶":"✶","𝔰":"𝔰","⌢":"⌢","♯":"♯","щ":"щ","ш":"ш","∣":"∣","∥":"∥","­":"­","­":"­","σ":"σ","ς":"ς","ς":"ς","∼":"∼","⩪":"⩪","≃":"≃","≃":"≃","⪞":"⪞","⪠":"⪠","⪝":"⪝","⪟":"⪟","≆":"≆","⨤":"⨤","⥲":"⥲","←":"←","∖":"∖","⨳":"⨳","⧤":"⧤","∣":"∣","⌣":"⌣","⪪":"⪪","⪬":"⪬","⪬︀":"⪬︀","ь":"ь","/":"/","⧄":"⧄","⌿":"⌿","𝕤":"𝕤","♠":"♠","♠":"♠","∥":"∥","⊓":"⊓","⊓︀":"⊓︀","⊔":"⊔","⊔︀":"⊔︀","⊏":"⊏","⊑":"⊑","⊏":"⊏","⊑":"⊑","⊐":"⊐","⊒":"⊒","⊐":"⊐","⊒":"⊒","□":"□","□":"□","▪":"▪","▪":"▪","→":"→","𝓈":"𝓈","∖":"∖","⌣":"⌣","⋆":"⋆","☆":"☆","★":"★","ϵ":"ϵ","ϕ":"ϕ","¯":"¯","⊂":"⊂","⫅":"⫅","⪽":"⪽","⊆":"⊆","⫃":"⫃","⫁":"⫁","⫋":"⫋","⊊":"⊊","⪿":"⪿","⥹":"⥹","⊂":"⊂","⊆":"⊆","⫅":"⫅","⊊":"⊊","⫋":"⫋","⫇":"⫇","⫕":"⫕","⫓":"⫓","≻":"≻","⪸":"⪸","≽":"≽","⪰":"⪰","⪺":"⪺","⪶":"⪶","⋩":"⋩","≿":"≿","∑":"∑","♪":"♪","¹":"¹","¹":"¹","²":"²","²":"²","³":"³","³":"³","⊃":"⊃","⫆":"⫆","⪾":"⪾","⫘":"⫘","⊇":"⊇","⫄":"⫄","⟉":"⟉","⫗":"⫗","⥻":"⥻","⫂":"⫂","⫌":"⫌","⊋":"⊋","⫀":"⫀","⊃":"⊃","⊇":"⊇","⫆":"⫆","⊋":"⊋","⫌":"⫌","⫈":"⫈","⫔":"⫔","⫖":"⫖","⇙":"⇙","⤦":"⤦","↙":"↙","↙":"↙","⤪":"⤪","ß":"ß","ß":"ß","⌖":"⌖","τ":"τ","⎴":"⎴","ť":"ť","ţ":"ţ","т":"т","⃛":"⃛","⌕":"⌕","𝔱":"𝔱","∴":"∴","∴":"∴","θ":"θ","ϑ":"ϑ","ϑ":"ϑ","≈":"≈","∼":"∼"," ":" ","≈":"≈","∼":"∼","þ":"þ","þ":"þ","˜":"˜","×":"×","×":"×","⊠":"⊠","⨱":"⨱","⨰":"⨰","∭":"∭","⤨":"⤨","⊤":"⊤","⌶":"⌶","⫱":"⫱","𝕥":"𝕥","⫚":"⫚","⤩":"⤩","‴":"‴","™":"™","▵":"▵","▿":"▿","◃":"◃","⊴":"⊴","≜":"≜","▹":"▹","⊵":"⊵","◬":"◬","≜":"≜","⨺":"⨺","⨹":"⨹","⧍":"⧍","⨻":"⨻","⏢":"⏢","𝓉":"𝓉","ц":"ц","ћ":"ћ","ŧ":"ŧ","≬":"≬","↞":"↞","↠":"↠","⇑":"⇑","⥣":"⥣","ú":"ú","ú":"ú","↑":"↑","ў":"ў","ŭ":"ŭ","û":"û","û":"û","у":"у","⇅":"⇅","ű":"ű","⥮":"⥮","⥾":"⥾","𝔲":"𝔲","ù":"ù","ù":"ù","↿":"↿","↾":"↾","▀":"▀","⌜":"⌜","⌜":"⌜","⌏":"⌏","◸":"◸","ū":"ū","¨":"¨","¨":"¨","ų":"ų","𝕦":"𝕦","↑":"↑","↕":"↕","↿":"↿","↾":"↾","⊎":"⊎","υ":"υ","ϒ":"ϒ","υ":"υ","⇈":"⇈","⌝":"⌝","⌝":"⌝","⌎":"⌎","ů":"ů","◹":"◹","𝓊":"𝓊","⋰":"⋰","ũ":"ũ","▵":"▵","▴":"▴","⇈":"⇈","ü":"ü","ü":"ü","⦧":"⦧","⇕":"⇕","⫨":"⫨","⫩":"⫩","⊨":"⊨","⦜":"⦜","ϵ":"ϵ","ϰ":"ϰ","∅":"∅","ϕ":"ϕ","ϖ":"ϖ","∝":"∝","↕":"↕","ϱ":"ϱ","ς":"ς","⊊︀":"⊊︀","⫋︀":"⫋︀","⊋︀":"⊋︀","⫌︀":"⫌︀","ϑ":"ϑ","⊲":"⊲","⊳":"⊳","в":"в","⊢":"⊢","∨":"∨","⊻":"⊻","≚":"≚","⋮":"⋮","|":"|","|":"|","𝔳":"𝔳","⊲":"⊲","⊂⃒":"⊂⃒","⊃⃒":"⊃⃒","𝕧":"𝕧","∝":"∝","⊳":"⊳","𝓋":"𝓋","⫋︀":"⫋︀","⊊︀":"⊊︀","⫌︀":"⫌︀","⊋︀":"⊋︀","⦚":"⦚","ŵ":"ŵ","⩟":"⩟","∧":"∧","≙":"≙","℘":"℘","𝔴":"𝔴","𝕨":"𝕨","℘":"℘","≀":"≀","≀":"≀","𝓌":"𝓌","⋂":"⋂","◯":"◯","⋃":"⋃","▽":"▽","𝔵":"𝔵","⟺":"⟺","⟷":"⟷","ξ":"ξ","⟸":"⟸","⟵":"⟵","⟼":"⟼","⋻":"⋻","⨀":"⨀","𝕩":"𝕩","⨁":"⨁","⨂":"⨂","⟹":"⟹","⟶":"⟶","𝓍":"𝓍","⨆":"⨆","⨄":"⨄","△":"△","⋁":"⋁","⋀":"⋀","ý":"ý","ý":"ý","я":"я","ŷ":"ŷ","ы":"ы","¥":"¥","¥":"¥","𝔶":"𝔶","ї":"ї","𝕪":"𝕪","𝓎":"𝓎","ю":"ю","ÿ":"ÿ","ÿ":"ÿ","ź":"ź","ž":"ž","з":"з","ż":"ż","ℨ":"ℨ","ζ":"ζ","𝔷":"𝔷","ж":"ж","⇝":"⇝","𝕫":"𝕫","𝓏":"𝓏","‍":"‍","‌":"‌"},characters:{"Æ":"Æ","&":"&","Á":"Á","Ă":"Ă","Â":"Â","А":"А","𝔄":"𝔄","À":"À","Α":"Α","Ā":"Ā","⩓":"⩓","Ą":"Ą","𝔸":"𝔸","⁡":"⁡","Å":"Å","𝒜":"𝒜","≔":"≔","Ã":"Ã","Ä":"Ä","∖":"∖","⫧":"⫧","⌆":"⌆","Б":"Б","∵":"∵","ℬ":"ℬ","Β":"Β","𝔅":"𝔅","𝔹":"𝔹","˘":"˘","≎":"≎","Ч":"Ч","©":"©","Ć":"Ć","⋒":"⋒","ⅅ":"ⅅ","ℭ":"ℭ","Č":"Č","Ç":"Ç","Ĉ":"Ĉ","∰":"∰","Ċ":"Ċ","¸":"¸","·":"·","Χ":"Χ","⊙":"⊙","⊖":"⊖","⊕":"⊕","⊗":"⊗","∲":"∲","”":"”","’":"’","∷":"∷","⩴":"⩴","≡":"≡","∯":"∯","∮":"∮","ℂ":"ℂ","∐":"∐","∳":"∳","⨯":"⨯","𝒞":"𝒞","⋓":"⋓","≍":"≍","⤑":"⤑","Ђ":"Ђ","Ѕ":"Ѕ","Џ":"Џ","‡":"‡","↡":"↡","⫤":"⫤","Ď":"Ď","Д":"Д","∇":"∇","Δ":"Δ","𝔇":"𝔇","´":"´","˙":"˙","˝":"˝","`":"`","˜":"˜","⋄":"⋄","ⅆ":"ⅆ","𝔻":"𝔻","¨":"¨","⃜":"⃜","≐":"≐","⇓":"⇓","⇐":"⇐","⇔":"⇔","⟸":"⟸","⟺":"⟺","⟹":"⟹","⇒":"⇒","⊨":"⊨","⇑":"⇑","⇕":"⇕","∥":"∥","↓":"↓","⤓":"⤓","⇵":"⇵","̑":"̑","⥐":"⥐","⥞":"⥞","↽":"↽","⥖":"⥖","⥟":"⥟","⇁":"⇁","⥗":"⥗","⊤":"⊤","↧":"↧","𝒟":"𝒟","Đ":"Đ","Ŋ":"Ŋ","Ð":"Ð","É":"É","Ě":"Ě","Ê":"Ê","Э":"Э","Ė":"Ė","𝔈":"𝔈","È":"È","∈":"∈","Ē":"Ē","◻":"◻","▫":"▫","Ę":"Ę","𝔼":"𝔼","Ε":"Ε","⩵":"⩵","≂":"≂","⇌":"⇌","ℰ":"ℰ","⩳":"⩳","Η":"Η","Ë":"Ë","∃":"∃","ⅇ":"ⅇ","Ф":"Ф","𝔉":"𝔉","◼":"◼","▪":"▪","𝔽":"𝔽","∀":"∀","ℱ":"ℱ","Ѓ":"Ѓ",">":">","Γ":"Γ","Ϝ":"Ϝ","Ğ":"Ğ","Ģ":"Ģ","Ĝ":"Ĝ","Г":"Г","Ġ":"Ġ","𝔊":"𝔊","⋙":"⋙","𝔾":"𝔾","≥":"≥","⋛":"⋛","≧":"≧","⪢":"⪢","≷":"≷","⩾":"⩾","≳":"≳","𝒢":"𝒢","≫":"≫","Ъ":"Ъ","ˇ":"ˇ","^":"^","Ĥ":"Ĥ","ℌ":"ℌ","ℋ":"ℋ","ℍ":"ℍ","─":"─","Ħ":"Ħ","≏":"≏","Е":"Е","IJ":"IJ","Ё":"Ё","Í":"Í","Î":"Î","И":"И","İ":"İ","ℑ":"ℑ","Ì":"Ì","Ī":"Ī","ⅈ":"ⅈ","∬":"∬","∫":"∫","⋂":"⋂","⁣":"⁣","⁢":"⁢","Į":"Į","𝕀":"𝕀","Ι":"Ι","ℐ":"ℐ","Ĩ":"Ĩ","І":"І","Ï":"Ï","Ĵ":"Ĵ","Й":"Й","𝔍":"𝔍","𝕁":"𝕁","𝒥":"𝒥","Ј":"Ј","Є":"Є","Х":"Х","Ќ":"Ќ","Κ":"Κ","Ķ":"Ķ","К":"К","𝔎":"𝔎","𝕂":"𝕂","𝒦":"𝒦","Љ":"Љ","<":"<","Ĺ":"Ĺ","Λ":"Λ","⟪":"⟪","ℒ":"ℒ","↞":"↞","Ľ":"Ľ","Ļ":"Ļ","Л":"Л","⟨":"⟨","←":"←","⇤":"⇤","⇆":"⇆","⌈":"⌈","⟦":"⟦","⥡":"⥡","⇃":"⇃","⥙":"⥙","⌊":"⌊","↔":"↔","⥎":"⥎","⊣":"⊣","↤":"↤","⥚":"⥚","⊲":"⊲","⧏":"⧏","⊴":"⊴","⥑":"⥑","⥠":"⥠","↿":"↿","⥘":"⥘","↼":"↼","⥒":"⥒","⋚":"⋚","≦":"≦","≶":"≶","⪡":"⪡","⩽":"⩽","≲":"≲","𝔏":"𝔏","⋘":"⋘","⇚":"⇚","Ŀ":"Ŀ","⟵":"⟵","⟷":"⟷","⟶":"⟶","𝕃":"𝕃","↙":"↙","↘":"↘","↰":"↰","Ł":"Ł","≪":"≪","⤅":"⤅","М":"М"," ":" ","ℳ":"ℳ","𝔐":"𝔐","∓":"∓","𝕄":"𝕄","Μ":"Μ","Њ":"Њ","Ń":"Ń","Ň":"Ň","Ņ":"Ņ","Н":"Н","​":"​","\n":" ","𝔑":"𝔑","⁠":"⁠"," ":" ","ℕ":"ℕ","⫬":"⫬","≢":"≢","≭":"≭","∦":"∦","∉":"∉","≠":"≠","≂̸":"≂̸","∄":"∄","≯":"≯","≱":"≱","≧̸":"≧̸","≫̸":"≫̸","≹":"≹","⩾̸":"⩾̸","≵":"≵","≎̸":"≎̸","≏̸":"≏̸","⋪":"⋪","⧏̸":"⧏̸","⋬":"⋬","≮":"≮","≰":"≰","≸":"≸","≪̸":"≪̸","⩽̸":"⩽̸","≴":"≴","⪢̸":"⪢̸","⪡̸":"⪡̸","⊀":"⊀","⪯̸":"⪯̸","⋠":"⋠","∌":"∌","⋫":"⋫","⧐̸":"⧐̸","⋭":"⋭","⊏̸":"⊏̸","⋢":"⋢","⊐̸":"⊐̸","⋣":"⋣","⊂⃒":"⊂⃒","⊈":"⊈","⊁":"⊁","⪰̸":"⪰̸","⋡":"⋡","≿̸":"≿̸","⊃⃒":"⊃⃒","⊉":"⊉","≁":"≁","≄":"≄","≇":"≇","≉":"≉","∤":"∤","𝒩":"𝒩","Ñ":"Ñ","Ν":"Ν","Œ":"Œ","Ó":"Ó","Ô":"Ô","О":"О","Ő":"Ő","𝔒":"𝔒","Ò":"Ò","Ō":"Ō","Ω":"Ω","Ο":"Ο","𝕆":"𝕆","“":"“","‘":"‘","⩔":"⩔","𝒪":"𝒪","Ø":"Ø","Õ":"Õ","⨷":"⨷","Ö":"Ö","‾":"‾","⏞":"⏞","⎴":"⎴","⏜":"⏜","∂":"∂","П":"П","𝔓":"𝔓","Φ":"Φ","Π":"Π","±":"±","ℙ":"ℙ","⪻":"⪻","≺":"≺","⪯":"⪯","≼":"≼","≾":"≾","″":"″","∏":"∏","∝":"∝","𝒫":"𝒫","Ψ":"Ψ",'"':""","𝔔":"𝔔","ℚ":"ℚ","𝒬":"𝒬","⤐":"⤐","®":"®","Ŕ":"Ŕ","⟫":"⟫","↠":"↠","⤖":"⤖","Ř":"Ř","Ŗ":"Ŗ","Р":"Р","ℜ":"ℜ","∋":"∋","⇋":"⇋","⥯":"⥯","Ρ":"Ρ","⟩":"⟩","→":"→","⇥":"⇥","⇄":"⇄","⌉":"⌉","⟧":"⟧","⥝":"⥝","⇂":"⇂","⥕":"⥕","⌋":"⌋","⊢":"⊢","↦":"↦","⥛":"⥛","⊳":"⊳","⧐":"⧐","⊵":"⊵","⥏":"⥏","⥜":"⥜","↾":"↾","⥔":"⥔","⇀":"⇀","⥓":"⥓","ℝ":"ℝ","⥰":"⥰","⇛":"⇛","ℛ":"ℛ","↱":"↱","⧴":"⧴","Щ":"Щ","Ш":"Ш","Ь":"Ь","Ś":"Ś","⪼":"⪼","Š":"Š","Ş":"Ş","Ŝ":"Ŝ","С":"С","𝔖":"𝔖","↑":"↑","Σ":"Σ","∘":"∘","𝕊":"𝕊","√":"√","□":"□","⊓":"⊓","⊏":"⊏","⊑":"⊑","⊐":"⊐","⊒":"⊒","⊔":"⊔","𝒮":"𝒮","⋆":"⋆","⋐":"⋐","⊆":"⊆","≻":"≻","⪰":"⪰","≽":"≽","≿":"≿","∑":"∑","⋑":"⋑","⊃":"⊃","⊇":"⊇","Þ":"Þ","™":"™","Ћ":"Ћ","Ц":"Ц","\t":" ","Τ":"Τ","Ť":"Ť","Ţ":"Ţ","Т":"Т","𝔗":"𝔗","∴":"∴","Θ":"Θ","  ":"  "," ":" ","∼":"∼","≃":"≃","≅":"≅","≈":"≈","𝕋":"𝕋","⃛":"⃛","𝒯":"𝒯","Ŧ":"Ŧ","Ú":"Ú","↟":"↟","⥉":"⥉","Ў":"Ў","Ŭ":"Ŭ","Û":"Û","У":"У","Ű":"Ű","𝔘":"𝔘","Ù":"Ù","Ū":"Ū",_:"_","⏟":"⏟","⎵":"⎵","⏝":"⏝","⋃":"⋃","⊎":"⊎","Ų":"Ų","𝕌":"𝕌","⤒":"⤒","⇅":"⇅","↕":"↕","⥮":"⥮","⊥":"⊥","↥":"↥","↖":"↖","↗":"↗","ϒ":"ϒ","Υ":"Υ","Ů":"Ů","𝒰":"𝒰","Ũ":"Ũ","Ü":"Ü","⊫":"⊫","⫫":"⫫","В":"В","⊩":"⊩","⫦":"⫦","⋁":"⋁","‖":"‖","∣":"∣","|":"|","❘":"❘","≀":"≀"," ":" ","𝔙":"𝔙","𝕍":"𝕍","𝒱":"𝒱","⊪":"⊪","Ŵ":"Ŵ","⋀":"⋀","𝔚":"𝔚","𝕎":"𝕎","𝒲":"𝒲","𝔛":"𝔛","Ξ":"Ξ","𝕏":"𝕏","𝒳":"𝒳","Я":"Я","Ї":"Ї","Ю":"Ю","Ý":"Ý","Ŷ":"Ŷ","Ы":"Ы","𝔜":"𝔜","𝕐":"𝕐","𝒴":"𝒴","Ÿ":"Ÿ","Ж":"Ж","Ź":"Ź","Ž":"Ž","З":"З","Ż":"Ż","Ζ":"Ζ","ℨ":"ℨ","ℤ":"ℤ","𝒵":"𝒵","á":"á","ă":"ă","∾":"∾","∾̳":"∾̳","∿":"∿","â":"â","а":"а","æ":"æ","𝔞":"𝔞","à":"à","ℵ":"ℵ","α":"α","ā":"ā","⨿":"⨿","∧":"∧","⩕":"⩕","⩜":"⩜","⩘":"⩘","⩚":"⩚","∠":"∠","⦤":"⦤","∡":"∡","⦨":"⦨","⦩":"⦩","⦪":"⦪","⦫":"⦫","⦬":"⦬","⦭":"⦭","⦮":"⦮","⦯":"⦯","∟":"∟","⊾":"⊾","⦝":"⦝","∢":"∢","⍼":"⍼","ą":"ą","𝕒":"𝕒","⩰":"⩰","⩯":"⩯","≊":"≊","≋":"≋","'":"'","å":"å","𝒶":"𝒶","*":"*","ã":"ã","ä":"ä","⨑":"⨑","⫭":"⫭","≌":"≌","϶":"϶","‵":"‵","∽":"∽","⋍":"⋍","⊽":"⊽","⌅":"⌅","⎶":"⎶","б":"б","„":"„","⦰":"⦰","β":"β","ℶ":"ℶ","≬":"≬","𝔟":"𝔟","◯":"◯","⨀":"⨀","⨁":"⨁","⨂":"⨂","⨆":"⨆","★":"★","▽":"▽","△":"△","⨄":"⨄","⤍":"⤍","⧫":"⧫","▴":"▴","▾":"▾","◂":"◂","▸":"▸","␣":"␣","▒":"▒","░":"░","▓":"▓","█":"█","=⃥":"=⃥","≡⃥":"≡⃥","⌐":"⌐","𝕓":"𝕓","⋈":"⋈","╗":"╗","╔":"╔","╖":"╖","╓":"╓","═":"═","╦":"╦","╩":"╩","╤":"╤","╧":"╧","╝":"╝","╚":"╚","╜":"╜","╙":"╙","║":"║","╬":"╬","╣":"╣","╠":"╠","╫":"╫","╢":"╢","╟":"╟","⧉":"⧉","╕":"╕","╒":"╒","┐":"┐","┌":"┌","╥":"╥","╨":"╨","┬":"┬","┴":"┴","⊟":"⊟","⊞":"⊞","⊠":"⊠","╛":"╛","╘":"╘","┘":"┘","└":"└","│":"│","╪":"╪","╡":"╡","╞":"╞","┼":"┼","┤":"┤","├":"├","¦":"¦","𝒷":"𝒷","⁏":"⁏","\\":"\","⧅":"⧅","⟈":"⟈","•":"•","⪮":"⪮","ć":"ć","∩":"∩","⩄":"⩄","⩉":"⩉","⩋":"⩋","⩇":"⩇","⩀":"⩀","∩︀":"∩︀","⁁":"⁁","⩍":"⩍","č":"č","ç":"ç","ĉ":"ĉ","⩌":"⩌","⩐":"⩐","ċ":"ċ","⦲":"⦲","¢":"¢","𝔠":"𝔠","ч":"ч","✓":"✓","χ":"χ","○":"○","⧃":"⧃","ˆ":"ˆ","≗":"≗","↺":"↺","↻":"↻","Ⓢ":"Ⓢ","⊛":"⊛","⊚":"⊚","⊝":"⊝","⨐":"⨐","⫯":"⫯","⧂":"⧂","♣":"♣",":":":",",":",","@":"@","∁":"∁","⩭":"⩭","𝕔":"𝕔","℗":"℗","↵":"↵","✗":"✗","𝒸":"𝒸","⫏":"⫏","⫑":"⫑","⫐":"⫐","⫒":"⫒","⋯":"⋯","⤸":"⤸","⤵":"⤵","⋞":"⋞","⋟":"⋟","↶":"↶","⤽":"⤽","∪":"∪","⩈":"⩈","⩆":"⩆","⩊":"⩊","⊍":"⊍","⩅":"⩅","∪︀":"∪︀","↷":"↷","⤼":"⤼","⋎":"⋎","⋏":"⋏","¤":"¤","∱":"∱","⌭":"⌭","⥥":"⥥","†":"†","ℸ":"ℸ","‐":"‐","⤏":"⤏","ď":"ď","д":"д","⇊":"⇊","⩷":"⩷","°":"°","δ":"δ","⦱":"⦱","⥿":"⥿","𝔡":"𝔡","♦":"♦","ϝ":"ϝ","⋲":"⋲","÷":"÷","⋇":"⋇","ђ":"ђ","⌞":"⌞","⌍":"⌍",$:"$","𝕕":"𝕕","≑":"≑","∸":"∸","∔":"∔","⊡":"⊡","⌟":"⌟","⌌":"⌌","𝒹":"𝒹","ѕ":"ѕ","⧶":"⧶","đ":"đ","⋱":"⋱","▿":"▿","⦦":"⦦","џ":"џ","⟿":"⟿","é":"é","⩮":"⩮","ě":"ě","≖":"≖","ê":"ê","≕":"≕","э":"э","ė":"ė","≒":"≒","𝔢":"𝔢","⪚":"⪚","è":"è","⪖":"⪖","⪘":"⪘","⪙":"⪙","⏧":"⏧","ℓ":"ℓ","⪕":"⪕","⪗":"⪗","ē":"ē","∅":"∅"," ":" "," ":" "," ":" ","ŋ":"ŋ"," ":" ","ę":"ę","𝕖":"𝕖","⋕":"⋕","⧣":"⧣","⩱":"⩱","ε":"ε","ϵ":"ϵ","=":"=","≟":"≟","⩸":"⩸","⧥":"⧥","≓":"≓","⥱":"⥱","ℯ":"ℯ","η":"η","ð":"ð","ë":"ë","€":"€","!":"!","ф":"ф","♀":"♀","ffi":"ffi","ff":"ff","ffl":"ffl","𝔣":"𝔣","fi":"fi",fj:"fj","♭":"♭","fl":"fl","▱":"▱","ƒ":"ƒ","𝕗":"𝕗","⋔":"⋔","⫙":"⫙","⨍":"⨍","½":"½","⅓":"⅓","¼":"¼","⅕":"⅕","⅙":"⅙","⅛":"⅛","⅔":"⅔","⅖":"⅖","¾":"¾","⅗":"⅗","⅜":"⅜","⅘":"⅘","⅚":"⅚","⅝":"⅝","⅞":"⅞","⁄":"⁄","⌢":"⌢","𝒻":"𝒻","⪌":"⪌","ǵ":"ǵ","γ":"γ","⪆":"⪆","ğ":"ğ","ĝ":"ĝ","г":"г","ġ":"ġ","⪩":"⪩","⪀":"⪀","⪂":"⪂","⪄":"⪄","⋛︀":"⋛︀","⪔":"⪔","𝔤":"𝔤","ℷ":"ℷ","ѓ":"ѓ","⪒":"⪒","⪥":"⪥","⪤":"⪤","≩":"≩","⪊":"⪊","⪈":"⪈","⋧":"⋧","𝕘":"𝕘","ℊ":"ℊ","⪎":"⪎","⪐":"⪐","⪧":"⪧","⩺":"⩺","⋗":"⋗","⦕":"⦕","⩼":"⩼","⥸":"⥸","≩︀":"≩︀","ъ":"ъ","⥈":"⥈","↭":"↭","ℏ":"ℏ","ĥ":"ĥ","♥":"♥","…":"…","⊹":"⊹","𝔥":"𝔥","⤥":"⤥","⤦":"⤦","⇿":"⇿","∻":"∻","↩":"↩","↪":"↪","𝕙":"𝕙","―":"―","𝒽":"𝒽","ħ":"ħ","⁃":"⁃","í":"í","î":"î","и":"и","е":"е","¡":"¡","𝔦":"𝔦","ì":"ì","⨌":"⨌","∭":"∭","⧜":"⧜","℩":"℩","ij":"ij","ī":"ī","ı":"ı","⊷":"⊷","Ƶ":"Ƶ","℅":"℅","∞":"∞","⧝":"⧝","⊺":"⊺","⨗":"⨗","⨼":"⨼","ё":"ё","į":"į","𝕚":"𝕚","ι":"ι","¿":"¿","𝒾":"𝒾","⋹":"⋹","⋵":"⋵","⋴":"⋴","⋳":"⋳","ĩ":"ĩ","і":"і","ï":"ï","ĵ":"ĵ","й":"й","𝔧":"𝔧","ȷ":"ȷ","𝕛":"𝕛","𝒿":"𝒿","ј":"ј","є":"є","κ":"κ","ϰ":"ϰ","ķ":"ķ","к":"к","𝔨":"𝔨","ĸ":"ĸ","х":"х","ќ":"ќ","𝕜":"𝕜","𝓀":"𝓀","⤛":"⤛","⤎":"⤎","⪋":"⪋","⥢":"⥢","ĺ":"ĺ","⦴":"⦴","λ":"λ","⦑":"⦑","⪅":"⪅","«":"«","⤟":"⤟","⤝":"⤝","↫":"↫","⤹":"⤹","⥳":"⥳","↢":"↢","⪫":"⪫","⤙":"⤙","⪭":"⪭","⪭︀":"⪭︀","⤌":"⤌","❲":"❲","{":"{","[":"[","⦋":"⦋","⦏":"⦏","⦍":"⦍","ľ":"ľ","ļ":"ļ","л":"л","⤶":"⤶","⥧":"⥧","⥋":"⥋","↲":"↲","≤":"≤","⇇":"⇇","⋋":"⋋","⪨":"⪨","⩿":"⩿","⪁":"⪁","⪃":"⪃","⋚︀":"⋚︀","⪓":"⪓","⋖":"⋖","⥼":"⥼","𝔩":"𝔩","⪑":"⪑","⥪":"⥪","▄":"▄","љ":"љ","⥫":"⥫","◺":"◺","ŀ":"ŀ","⎰":"⎰","≨":"≨","⪉":"⪉","⪇":"⪇","⋦":"⋦","⟬":"⟬","⇽":"⇽","⟼":"⟼","↬":"↬","⦅":"⦅","𝕝":"𝕝","⨭":"⨭","⨴":"⨴","∗":"∗","◊":"◊","(":"(","⦓":"⦓","⥭":"⥭","‎":"‎","⊿":"⊿","‹":"‹","𝓁":"𝓁","⪍":"⪍","⪏":"⪏","‚":"‚","ł":"ł","⪦":"⪦","⩹":"⩹","⋉":"⋉","⥶":"⥶","⩻":"⩻","⦖":"⦖","◃":"◃","⥊":"⥊","⥦":"⥦","≨︀":"≨︀","∺":"∺","¯":"¯","♂":"♂","✠":"✠","▮":"▮","⨩":"⨩","м":"м","—":"—","𝔪":"𝔪","℧":"℧","µ":"µ","⫰":"⫰","−":"−","⨪":"⨪","⫛":"⫛","⊧":"⊧","𝕞":"𝕞","𝓂":"𝓂","μ":"μ","⊸":"⊸","⋙̸":"⋙̸","≫⃒":"≫⃒","⇍":"⇍","⇎":"⇎","⋘̸":"⋘̸","≪⃒":"≪⃒","⇏":"⇏","⊯":"⊯","⊮":"⊮","ń":"ń","∠⃒":"∠⃒","⩰̸":"⩰̸","≋̸":"≋̸","ʼn":"ʼn","♮":"♮","⩃":"⩃","ň":"ň","ņ":"ņ","⩭̸":"⩭̸","⩂":"⩂","н":"н","–":"–","⇗":"⇗","⤤":"⤤","≐̸":"≐̸","⤨":"⤨","𝔫":"𝔫","↮":"↮","⫲":"⫲","⋼":"⋼","⋺":"⋺","њ":"њ","≦̸":"≦̸","↚":"↚","‥":"‥","𝕟":"𝕟","¬":"¬","⋹̸":"⋹̸","⋵̸":"⋵̸","⋷":"⋷","⋶":"⋶","⋾":"⋾","⋽":"⋽","⫽⃥":"⫽⃥","∂̸":"∂̸","⨔":"⨔","↛":"↛","⤳̸":"⤳̸","↝̸":"↝̸","𝓃":"𝓃","⊄":"⊄","⫅̸":"⫅̸","⊅":"⊅","⫆̸":"⫆̸","ñ":"ñ","ν":"ν","#":"#","№":"№"," ":" ","⊭":"⊭","⤄":"⤄","≍⃒":"≍⃒","⊬":"⊬","≥⃒":"≥⃒",">⃒":">⃒","⧞":"⧞","⤂":"⤂","≤⃒":"≤⃒","<⃒":"<⃒","⊴⃒":"⊴⃒","⤃":"⤃","⊵⃒":"⊵⃒","∼⃒":"∼⃒","⇖":"⇖","⤣":"⤣","⤧":"⤧","ó":"ó","ô":"ô","о":"о","ő":"ő","⨸":"⨸","⦼":"⦼","œ":"œ","⦿":"⦿","𝔬":"𝔬","˛":"˛","ò":"ò","⧁":"⧁","⦵":"⦵","⦾":"⦾","⦻":"⦻","⧀":"⧀","ō":"ō","ω":"ω","ο":"ο","⦶":"⦶","𝕠":"𝕠","⦷":"⦷","⦹":"⦹","∨":"∨","⩝":"⩝","ℴ":"ℴ","ª":"ª","º":"º","⊶":"⊶","⩖":"⩖","⩗":"⩗","⩛":"⩛","ø":"ø","⊘":"⊘","õ":"õ","⨶":"⨶","ö":"ö","⌽":"⌽","¶":"¶","⫳":"⫳","⫽":"⫽","п":"п","%":"%",".":".","‰":"‰","‱":"‱","𝔭":"𝔭","φ":"φ","ϕ":"ϕ","☎":"☎","π":"π","ϖ":"ϖ","ℎ":"ℎ","+":"+","⨣":"⨣","⨢":"⨢","⨥":"⨥","⩲":"⩲","⨦":"⨦","⨧":"⨧","⨕":"⨕","𝕡":"𝕡","£":"£","⪳":"⪳","⪷":"⪷","⪹":"⪹","⪵":"⪵","⋨":"⋨","′":"′","⌮":"⌮","⌒":"⌒","⌓":"⌓","⊰":"⊰","𝓅":"𝓅","ψ":"ψ"," ":" ","𝔮":"𝔮","𝕢":"𝕢","⁗":"⁗","𝓆":"𝓆","⨖":"⨖","?":"?","⤜":"⤜","⥤":"⥤","∽̱":"∽̱","ŕ":"ŕ","⦳":"⦳","⦒":"⦒","⦥":"⦥","»":"»","⥵":"⥵","⤠":"⤠","⤳":"⤳","⤞":"⤞","⥅":"⥅","⥴":"⥴","↣":"↣","↝":"↝","⤚":"⤚","∶":"∶","❳":"❳","}":"}","]":"]","⦌":"⦌","⦎":"⦎","⦐":"⦐","ř":"ř","ŗ":"ŗ","р":"р","⤷":"⤷","⥩":"⥩","↳":"↳","▭":"▭","⥽":"⥽","𝔯":"𝔯","⥬":"⥬","ρ":"ρ","ϱ":"ϱ","⇉":"⇉","⋌":"⋌","˚":"˚","‏":"‏","⎱":"⎱","⫮":"⫮","⟭":"⟭","⇾":"⇾","⦆":"⦆","𝕣":"𝕣","⨮":"⨮","⨵":"⨵",")":")","⦔":"⦔","⨒":"⨒","›":"›","𝓇":"𝓇","⋊":"⋊","▹":"▹","⧎":"⧎","⥨":"⥨","℞":"℞","ś":"ś","⪴":"⪴","⪸":"⪸","š":"š","ş":"ş","ŝ":"ŝ","⪶":"⪶","⪺":"⪺","⋩":"⋩","⨓":"⨓","с":"с","⋅":"⋅","⩦":"⩦","⇘":"⇘","§":"§",";":";","⤩":"⤩","✶":"✶","𝔰":"𝔰","♯":"♯","щ":"щ","ш":"ш","­":"­","σ":"σ","ς":"ς","⩪":"⩪","⪞":"⪞","⪠":"⪠","⪝":"⪝","⪟":"⪟","≆":"≆","⨤":"⨤","⥲":"⥲","⨳":"⨳","⧤":"⧤","⌣":"⌣","⪪":"⪪","⪬":"⪬","⪬︀":"⪬︀","ь":"ь","/":"/","⧄":"⧄","⌿":"⌿","𝕤":"𝕤","♠":"♠","⊓︀":"⊓︀","⊔︀":"⊔︀","𝓈":"𝓈","☆":"☆","⊂":"⊂","⫅":"⫅","⪽":"⪽","⫃":"⫃","⫁":"⫁","⫋":"⫋","⊊":"⊊","⪿":"⪿","⥹":"⥹","⫇":"⫇","⫕":"⫕","⫓":"⫓","♪":"♪","¹":"¹","²":"²","³":"³","⫆":"⫆","⪾":"⪾","⫘":"⫘","⫄":"⫄","⟉":"⟉","⫗":"⫗","⥻":"⥻","⫂":"⫂","⫌":"⫌","⊋":"⊋","⫀":"⫀","⫈":"⫈","⫔":"⫔","⫖":"⫖","⇙":"⇙","⤪":"⤪","ß":"ß","⌖":"⌖","τ":"τ","ť":"ť","ţ":"ţ","т":"т","⌕":"⌕","𝔱":"𝔱","θ":"θ","ϑ":"ϑ","þ":"þ","×":"×","⨱":"⨱","⨰":"⨰","⌶":"⌶","⫱":"⫱","𝕥":"𝕥","⫚":"⫚","‴":"‴","▵":"▵","≜":"≜","◬":"◬","⨺":"⨺","⨹":"⨹","⧍":"⧍","⨻":"⨻","⏢":"⏢","𝓉":"𝓉","ц":"ц","ћ":"ћ","ŧ":"ŧ","⥣":"⥣","ú":"ú","ў":"ў","ŭ":"ŭ","û":"û","у":"у","ű":"ű","⥾":"⥾","𝔲":"𝔲","ù":"ù","▀":"▀","⌜":"⌜","⌏":"⌏","◸":"◸","ū":"ū","ų":"ų","𝕦":"𝕦","υ":"υ","⇈":"⇈","⌝":"⌝","⌎":"⌎","ů":"ů","◹":"◹","𝓊":"𝓊","⋰":"⋰","ũ":"ũ","ü":"ü","⦧":"⦧","⫨":"⫨","⫩":"⫩","⦜":"⦜","⊊︀":"⊊︀","⫋︀":"⫋︀","⊋︀":"⊋︀","⫌︀":"⫌︀","в":"в","⊻":"⊻","≚":"≚","⋮":"⋮","𝔳":"𝔳","𝕧":"𝕧","𝓋":"𝓋","⦚":"⦚","ŵ":"ŵ","⩟":"⩟","≙":"≙","℘":"℘","𝔴":"𝔴","𝕨":"𝕨","𝓌":"𝓌","𝔵":"𝔵","ξ":"ξ","⋻":"⋻","𝕩":"𝕩","𝓍":"𝓍","ý":"ý","я":"я","ŷ":"ŷ","ы":"ы","¥":"¥","𝔶":"𝔶","ї":"ї","𝕪":"𝕪","𝓎":"𝓎","ю":"ю","ÿ":"ÿ","ź":"ź","ž":"ž","з":"з","ż":"ż","ζ":"ζ","𝔷":"𝔷","ж":"ж","⇝":"⇝","𝕫":"𝕫","𝓏":"𝓏","‍":"‍","‌":"‌"}}}})),P=y((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.numericUnicodeMap={0:65533,128:8364,130:8218,131:402,132:8222,133:8230,134:8224,135:8225,136:710,137:8240,138:352,139:8249,140:338,142:381,145:8216,146:8217,147:8220,148:8221,149:8226,150:8211,151:8212,152:732,153:8482,154:353,155:8250,156:339,158:382,159:376}})),L=y((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.fromCodePoint=String.fromCodePoint||function(e){return String.fromCharCode(Math.floor((e-65536)/1024)+55296,(e-65536)%1024+56320)},t.getCodePoint=String.prototype.codePointAt?function(e,t){return e.codePointAt(t)}:function(e,t){return 1024*(e.charCodeAt(t)-55296)+e.charCodeAt(t+1)-56320+65536},t.highSurrogateFrom=55296,t.highSurrogateTo=56319})),_=M,N=P,F=L,B=y((function(e,t){var r=h&&h.__assign||function(){return r=Object.assign||function(e){for(var t,r=1,n=arguments.length;r'"&]/g,nonAscii:/(?:[<>'"&\u0080-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g,nonAsciiPrintable:/(?:[<>'"&\x01-\x08\x11-\x15\x17-\x1F\x7f-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g,extensive:/(?:[\x01-\x0c\x0e-\x1f\x21-\x2c\x2e-\x2f\x3a-\x40\x5b-\x60\x7b-\x7d\x7f-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g},i={mode:"specialChars",level:"all",numeric:"decimal"};t.encode=function(e,t){var r=void 0===(c=(s=void 0===t?i:t).mode)?"specialChars":c,o=void 0===(p=s.numeric)?"decimal":p,l=void 0===(m=s.level)?"all":m;if(!e)return"";var s,c,u=a[r],d=n[l].characters,f="hexadecimal"===o;if(u.lastIndex=0,s=u.exec(e)){c="";var p=0;do{p!==s.index&&(c+=e.substring(p,s.index));var m,v=d[m=s[0]];if(!v){var h=m.length>1?F.getCodePoint(m,0):m.charCodeAt(0);v=(f?"&#x"+h.toString(16):"&#"+h)+";"}c+=v,p=s.index+m.length}while(s=u.exec(e));p!==e.length&&(c+=e.substring(p))}else c=e;return c};var o={scope:"body",level:"all"},l=/&(?:#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+);/g,s=/&(?:#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+)[;=]?/g,c={xml:{strict:l,attribute:s,body:_.bodyRegExps.xml},html4:{strict:l,attribute:s,body:_.bodyRegExps.html4},html5:{strict:l,attribute:s,body:_.bodyRegExps.html5}},u=r(r({},c),{all:c.html5}),d=String.fromCharCode,f=d(65533),p={level:"all"};t.decodeEntity=function(e,t){var r=void 0===(a=(void 0===t?p:t).level)?"all":a;if(!e)return"";var a=e;e[e.length-1];var i=n[r].entities[e];if(i)a=i;else if("&"===e[0]&&"#"===e[1]){var o=e[2],l="x"==o||"X"==o?parseInt(e.substr(3),16):parseInt(e.substr(2));a=l>=1114111?f:l>65535?F.fromCodePoint(l):d(N.numericUnicodeMap[l]||l)}return a},t.decode=function(e,t){var r=void 0===t?o:t,a=r.level,i=void 0===a?"all":a,l=r.scope,s=void 0===l?"xml"===i?"strict":"body":l;if(!e)return"";var c=u[i][s],p=n[i].entities,m="attribute"===s,v="strict"===s;c.lastIndex=0;var h,g=c.exec(e);if(g){h="";var y=0;do{y!==g.index&&(h+=e.substring(y,g.index));var b=g[0],x=b,k=b[b.length-1];if(m&&"="===k)x=b;else if(v&&";"!==k)x=b;else{var w=p[b];if(w)x=w;else if("&"===b[0]&&"#"===b[1]){var E=b[2],T="x"==E||"X"==E?parseInt(b.substr(3),16):parseInt(b.substr(2));x=T>=1114111?f:T>65535?F.fromCodePoint(T):d(N.numericUnicodeMap[T]||T)}}h+=x,y=g.index+b.length}while(g=c.exec(e));y!==e.length&&(h+=e.substring(y))}else h=e;return h}})),U=Object.prototype;var V=function(e){var t=e&&e.constructor;return e===("function"==typeof t&&t.prototype||U)};var H=function(e,t){return function(r){return e(t(r))}}(Object.keys,Object),G=Object.prototype.hasOwnProperty;var z=function(e){if(!V(e))return H(e);var t=[];for(var r in Object(e))G.call(e,r)&&"constructor"!=r&&t.push(r);return t},J="object"==typeof h&&h&&h.Object===Object&&h,W="object"==typeof self&&self&&self.Object===Object&&self,Z=J||W||Function("return this")(),Y=Z.Symbol,Q=Object.prototype,X=Q.hasOwnProperty,K=Q.toString,$=Y?Y.toStringTag:void 0;var ee=function(e){var t=X.call(e,$),r=e[$];try{e[$]=void 0;var n=!0}catch(e){}var a=K.call(e);return n&&(t?e[$]=r:delete e[$]),a},te=Object.prototype.toString;var re=function(e){return te.call(e)},ne="[object Null]",ae="[object Undefined]",ie=Y?Y.toStringTag:void 0;var oe=function(e){return null==e?void 0===e?ae:ne:ie&&ie in Object(e)?ee(e):re(e)};var le=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)},se="[object AsyncFunction]",ce="[object Function]",ue="[object GeneratorFunction]",de="[object Proxy]";var fe,pe=function(e){if(!le(e))return!1;var t=oe(e);return t==ce||t==ue||t==se||t==de},me=Z["__core-js_shared__"],ve=(fe=/[^.]+$/.exec(me&&me.keys&&me.keys.IE_PROTO||""))?"Symbol(src)_1."+fe:"";var he=function(e){return!!ve&&ve in e},ge=Function.prototype.toString;var ye=function(e){if(null!=e){try{return ge.call(e)}catch(e){}try{return e+""}catch(e){}}return""},be=/^\[object .+?Constructor\]$/,xe=Function.prototype,ke=Object.prototype,we=xe.toString,Ee=ke.hasOwnProperty,Te=RegExp("^"+we.call(Ee).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");var Se=function(e){return!(!le(e)||he(e))&&(pe(e)?Te:be).test(ye(e))};var Ce=function(e,t){return null==e?void 0:e[t]};var Re=function(e,t){var r=Ce(e,t);return Se(r)?r:void 0},Ie=Re(Z,"DataView"),Oe=Re(Z,"Map"),qe=Re(Z,"Promise"),je=Re(Z,"Set"),Ae=Re(Z,"WeakMap"),De="[object Map]",Me="[object Promise]",Pe="[object Set]",Le="[object WeakMap]",_e="[object DataView]",Ne=ye(Ie),Fe=ye(Oe),Be=ye(qe),Ue=ye(je),Ve=ye(Ae),He=oe;(Ie&&He(new Ie(new ArrayBuffer(1)))!=_e||Oe&&He(new Oe)!=De||qe&&He(qe.resolve())!=Me||je&&He(new je)!=Pe||Ae&&He(new Ae)!=Le)&&(He=function(e){var t=oe(e),r="[object Object]"==t?e.constructor:void 0,n=r?ye(r):"";if(n)switch(n){case Ne:return _e;case Fe:return De;case Be:return Me;case Ue:return Pe;case Ve:return Le}return t});var Ge=He;var ze=function(e){return null!=e&&"object"==typeof e},Je="[object Arguments]";var We=function(e){return ze(e)&&oe(e)==Je},Ze=Object.prototype,Ye=Ze.hasOwnProperty,Qe=Ze.propertyIsEnumerable,Xe=We(function(){return arguments}())?We:function(e){return ze(e)&&Ye.call(e,"callee")&&!Qe.call(e,"callee")},Ke=Array.isArray,$e=9007199254740991;var et=function(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=$e};var tt=function(e){return null!=e&&et(e.length)&&!pe(e)};var rt=function(){return!1},nt=y((function(e,t){var r=t&&!t.nodeType&&t,n=r&&e&&!e.nodeType&&e,a=n&&n.exports===r?Z.Buffer:void 0,i=(a?a.isBuffer:void 0)||rt;e.exports=i})),at={};at["[object Float32Array]"]=at["[object Float64Array]"]=at["[object Int8Array]"]=at["[object Int16Array]"]=at["[object Int32Array]"]=at["[object Uint8Array]"]=at["[object Uint8ClampedArray]"]=at["[object Uint16Array]"]=at["[object Uint32Array]"]=!0,at["[object Arguments]"]=at["[object Array]"]=at["[object ArrayBuffer]"]=at["[object Boolean]"]=at["[object DataView]"]=at["[object Date]"]=at["[object Error]"]=at["[object Function]"]=at["[object Map]"]=at["[object Number]"]=at["[object Object]"]=at["[object RegExp]"]=at["[object Set]"]=at["[object String]"]=at["[object WeakMap]"]=!1;var it=function(e){return ze(e)&&et(e.length)&&!!at[oe(e)]};var ot=function(e){return function(t){return e(t)}},lt=y((function(e,t){var r=t&&!t.nodeType&&t,n=r&&e&&!e.nodeType&&e,a=n&&n.exports===r&&J.process,i=function(){try{var e=n&&n.require&&n.require("util").types;return e||a&&a.binding&&a.binding("util")}catch(e){}}();e.exports=i})),st=lt&<.isTypedArray,ct=st?ot(st):it,ut="[object Map]",dt="[object Set]",ft=Object.prototype.hasOwnProperty;var pt=function(e){if(null==e)return!0;if(tt(e)&&(Ke(e)||"string"==typeof e||"function"==typeof e.splice||nt(e)||ct(e)||Xe(e)))return!e.length;var t=Ge(e);if(t==ut||t==dt)return!e.size;if(V(e))return!z(e).length;for(var r in e)if(ft.call(e,r))return!1;return!0};function mt(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function vt(e){for(var t=1;t1&&void 0!==arguments[1]&&arguments[1],r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(isNaN(e))return"";var n=Math.floor(e/3600),a=Math.floor(e%3600/60),i=e-60*a-3600*n,o="",l=n<10?"0".concat(n):"".concat(n);o=t||n>0?o+"".concat(l,":"):o;var s=a<10?"0".concat(a):"".concat(a);o+="".concat(s,":");var c=r?i.toFixed(3):parseInt(i);return c=i<10?"0".concat(c):"".concat(c),o+="".concat(c)}function St(e){var t=e.split(":").reverse(),r=T(t,3),n=r[0],a=r[1],i=r[2];return(null!=i?3600*parseInt(i):0)+(null!=a?60*parseInt(a):0)+(""===n?0:parseFloat(n.replace(",",".")))}function Ct(e){if(!e.ok)throw new Error(bt);return e}function Rt(e,t){return void 0!==e&&(void 0===t||!(e.start>t.end&&e.end>t.end))}function It(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",n=arguments.length>3&&void 0!==arguments[3]&&arguments[3],a=""===r?t.split(".").reverse()[0]:r;(a.length>4||a.length<3||a===t)&&(a=e.split(".").reverse()[0]);var i=a.length>4||a.length<3?"":a,o=t.endsWith(i)?t.split(".".concat(i))[0]:t;n&&(o="".concat(o," (machine generated)"));var l=""!=i?"".concat(o,".").concat(i):o;if(e.endsWith(a))fetch(e).then((function(e){e.blob().then((function(e){var t=window.URL.createObjectURL(e),r=document.createElement("a");r.href=t,r.download="".concat(l),r.click()}))})).catch((function(e){console.log(e)}));else{var s=document.createElement("a");s.setAttribute("href",e),s.setAttribute("download","".concat(l)),s.style.display="none",document.body.appendChild(s),s.click(),document.body.removeChild(s)}}function Ot(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;if(void 0===e);else{var r=e.split("#t=")[1];if(void 0!==r){var n,a,i,o=/([0-9]*:){1,2}([0-9]{2})(?:((\.|\,)[0-9]{2,3})?)/g;if(r.includes(":")&&(null===(n=D(r.matchAll(/\,/g)))||void 0===n?void 0:n.length)>1){var l=D(r.matchAll(o)),s=2==(null==l?void 0:l.length)?[l[0][0],l[1][0]]:[0,0],c=T(s,2);a=c[0],i=c[1]}else{var u=r.split(","),d=T(u,2);a=d[0],i=d[1]}return void 0===i&&(i=t.toString()),{start:a.match(o)?St(a):Number(a),end:i.match(o)?St(i):Number(i)}}}}function qt(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",r=[];if(!e)return r;if("Canvas"===e.type?r=e.items[0].items:Array.isArray(e)&&(null==e?void 0:e.length)>0&&(r=e[0].items),r&&""!=t){var n=r.filter((function(e){return e.motivation===t}));r=n}return r}function jt(e,t,r){var n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,a=arguments.length>4&&void 0!==arguments[4]&&arguments[4],i=[],o=[],l=!1,s="",c="No resources found in Canvas",u=function(e){var a=function(e,t,r,n){var a=null,i=ht.both,o=Mt(e.label);"supplementing"===n&&(i=Dt(e.id));if(i!=ht.transcript){var l;if(a={src:t>0?"".concat(e.id,"#t=").concat(t,",").concat(r):e.id,key:e.id,type:e.format,kind:e.type,label:o||"auto"},"supplementing"===n)a.srclang=null!==(l=e.language)&&void 0!==l?l:"en",a.kind=e.format.toLowerCase().includes("text/vtt")?"subtitles":"metadata"}return a}(e,n,t,r);a&&a.src&&i.push(a)};if(e&&null!=e){var d,f,p,m,v=qt(e);if(!v)return{resources:i,canvasTargets:o,error:c};if(0===v.length)return{resources:i,canvasTargets:o,isMultiSource:l,poster:Jt(e)};if((null==v?void 0:v.length)>1)v.map((function(e,n){if(e.motivation===r&&(u(e.body),"painting"===r)){l=!0;var a=function(e,t,r){var n=Ot(e.target,t);if(null!=n||!n)return n.id=e.id,isNaN(n.end)&&(n.end=t),n.end=Number((n.end-n.start).toFixed(2)),n.duration=n.end,n.altStart=n.start,n.start=0,n.sIndex=r,n}(e,t,n);o.push(a)}}));else if((null===(d=v[0].body.items)||void 0===d?void 0:d.length)>0&&(null===(f=v[0])||void 0===f?void 0:f.motivation)===r)v[0].body.items.map((function(e){u(e)}));else if(pt(v[0].body)||""==(null===(p=v[0].body)||void 0===p?void 0:p.id)||(null===(m=v[0])||void 0===m?void 0:m.motivation)!==r){if("painting"===r)return{resources:i,error:c,poster:Jt(e),canvasTargets:o}}else u(v[0].body);if(!l&&(null==i?void 0:i.length)>0&&"painting"===r){var h=Ot(i[0].src,t);void 0===h&&(h={start:0,end:t}),h.altStart=h.start,h.duration=t,a||(h=vt(vt({},h),{},{customStart:h.start,start:0,altStart:0})),o.push(h)}return s=Jt(e,!0),{canvasTargets:o,isMultiSource:l,resources:i,poster:s}}return{canvasTargets:o,isMultiSource:l,resources:i,poster:s,error:c}}function At(e){var t=/(\(machine(\s|\-)generated\))/gi;return{isMachineGen:t.test(e),labelText:e.replace(t,"").trim()}}function Dt(e){if(e){var t=e.split("/").reverse()[0];return"transcripts"===t?ht.transcript:"captions"===t?ht.caption:ht.both}}function Mt(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if(e&&"object"===C(e)){var r=Object.keys(e);if(r&&r.length>0){var n,a=r[0],i=t?e[a].join("\n"):null!==(n=e[a][0])&&void 0!==n?n:"";return B.decode(i)}}else if("string"==typeof e)return B.decode(e);return""}function Pt(e){return/^(([0-1][0-9])|([2][0-3])):([0-5][0-9])(:[0-5][0-9](?:[.]\d{1,3})?)?$/.test(e)}function Lt(e,t){var r=arguments.length>2&&void 0!==arguments[2]&&arguments[2],n=e.offsetTop-t.current.offsetTop;if(r)t.current.scrollTop=n;else{var a=t.current.clientHeight-e.clientHeight;t.current.scrollTop=n>a?n-t.current.clientHeight/2:a/2>n?0:n/2}}function _t(e,t,r){var n=null==t?void 0:t.player(),a="",i=document.activeElement,o=i.className.includes("vjs")||i.className.includes("videojs"),l=e.which,s=e.ctrlKey||e.metaKey||e.altKey||e.shiftKey;if((!i||-1===["input","textarea"].indexOf(i.tagName.toLowerCase())&&("tab"!==i.role||37!==l&&39!==l)||o)&&!s&&!r&&null!=n){switch(l){case 32:case 75:e.preventDefault(),n.paused()?(a=gt.play,n.play()):(a=gt.pause,n.pause());break;case 70:e.preventDefault(),n.isAudio()||(n.isFullscreen()?(a=gt.exitFullscreen,n.exitFullscreen()):(a=gt.enterFullscreen,n.requestFullscreen()));break;case 77:e.preventDefault();var c=n.volume(),u=n.lastVolume_();if(0===c){var d=u<.1?.1:u;n.volume(d),a=gt.unmute,n.muted(!1)}else a=gt.mute,n.muted(!n.muted());break;case 37:e.preventDefault(),a=gt.leftArrow,n.currentTime(n.currentTime()-5);break;case 39:e.preventDefault(),a=gt.rightArrow,n.currentTime(n.currentTime()+5);break;case 38:e.preventDefault(),n.muted()&&n.muted(!1),a=gt.upArrow,n.volume(n.volume()+.1);break;case 40:e.preventDefault(),a=gt.downArrow,n.volume(n.volume()-.1);break;default:return}return e.stopPropagation(),a}}function Nt(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!r){if(Array.isArray(e)||(r=function(e,t){if(!e)return;if("string"==typeof e)return Ft(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);"Object"===r&&e.constructor&&(r=e.constructor.name);if("Map"===r||"Set"===r)return Array.from(e);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return Ft(e,t)}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,a=function(){};return{s:a,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,o=!0,l=!1;return{s:function(){r=r.call(e)},n:function(){var e=r.next();return o=e.done,e},e:function(e){l=!0,i=e},f:function(){try{o||null==r.return||r.return()}finally{if(l)throw i}}}}function Ft(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r0&&(i=e.homepage[0].id);try{var o,l=!0,s=null===(o=e.items[0])||void 0===o?void 0:o.items,c="";if((null==s?void 0:s.length)>0){var u,d,f=s[0].body;(null===(u=f.items)||void 0===u?void 0:u.length)>0?c=f.items[0].id:0!=(null===(d=Object.keys(f))||void 0===d?void 0:d.length)&&f.id&&(c=f.id)}var p,m=Number(e.duration);""!=c&&(p=Ot(c,m),l=!1);var v=Mt(e.label)||"Section ".concat(n+1);r.push({canvasIndex:n,canvasId:e.id,canvasURL:e.id.split("#t=")[0],duration:m,range:void 0===p?{start:0,end:m}:p,isEmpty:l,summary:a,homepage:i||"",label:v,searchService:Qt(e)})}catch(t){r.push({canvasIndex:n,canvasId:e.id,canvasURL:e.id.split("#t=")[0],duration:e.duration||0,range:void 0,isEmpty:!0,summary:a,homepage:i||"",label:Mt(e.label)||"Section ".concat(n+1),searchService:Qt(e)})}})),r;throw console.error("iiif-parser -> canvasesInManifest() -> no canvases were found in Manifest"),new Error(bt)}catch(e){throw e}}function Gt(e){var t,r=e.manifest,n=e.canvasIndex,a=e.startTime,i=e.srcIndex,o=void 0===i?0:i,l=e.isPlaylist,s=void 0!==l&&l,c=null,u={canvas:null,sources:[],tracks:[],canvasTargets:[]};if(void 0===n||n<0)return Ut(Ut({},u),{},{error:"Error fetching content"});var d=r.items;if(0==(null==d?void 0:d.length))return Ut(Ut({},u),{},{poster:kt});try{var f=(c=d[n]).annotations;if(void 0===c)throw console.error("iiif-parser -> getMediaInfo() -> canvas undefined -> ",n),new Error(bt);var p=Number(c.duration),m=jt(c,p,"painting",a,s),v=m.resources,h=m.canvasTargets,g=m.isMultiSource,y=m.error,b=m.poster;t=function(e,t,r){var n=!1;if(0===e.length)return[];if(t)e[r].selected=!0;else{var a,i=Nt(e);try{for(i.s();!(a=i.n()).done;){var o=a.value;"auto"!=o.label||n||(n=!0,o.selected=!0)}}catch(e){i.e(e)}finally{i.f()}n||(e[0].selected=!0)}return e}(v,g,o);var x=jt(f,p,"supplementing"),k={sources:t,tracks:x?x.resources:[],canvasTargets:h,isMultiSource:g,error:y,poster:b};if(k.error)return Ut({},k);var w=function(e){var t=e.filter((function(t,r){return e.indexOf(t)===r})),r=1===t.length?t[0].toLowerCase():"video";return r}(k.sources.map((function(e){return e.kind})));return Ut(Ut({},k),{},{error:null,mediaType:w})}catch(y){throw y}}function zt(e){if(e)return e.split("#t=")[0]}function Jt(e){var t,r,n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];try{var a=e.placeholderCanvas;if(!a||null==a)return n?null:(console.error("iiif-parser -> getPlaceholderCanvas() -> placeholderCanvas property not defined"),"This item cannot be played.");var i=a.items[0].items;if((null==i?void 0:i.length)>0&&null!=i[0].body&&"painting"===i[0].motivation){var o=i[0].body;return n?t=o.id:(t=Mt(o.label)||"This item cannot be played.",r=a.duration,Et=r||wt),t}}catch(e){throw e}}function Wt(e,t,r){e="text/srt"===e?"application/x-subrip":e;var n=d.default[e],a=n?n.extensions[0]:e,i=Mt(t)||"Untitled",o=i;Object.keys(t).length>1&&(i=t[Object.keys(t)[0]][0],o=t.none[0]);var l=At(i),s=l.isMachineGen;return l._,{id:r,label:"".concat(i," (.").concat(a,")"),filename:o,fileExt:a,isMachineGen:s}}function Zt(e,t){var r=[];return e&&(null==e?void 0:e.length)>0?(e.map((function(e){var t,n=null===(t=Mt(e.value,!0))||void 0===t?void 0:t.replace(/\n/g,"
    "),a=f.default(n,Ut({},Vt));r.push({label:Mt(e.label),value:a})})),r):(console.log("iiif-parser -> parseMetadata() -> no metadata in ",t),r)}function Yt(e,t){var r=[],n=e.requiredStatement;n&&(r=Zt([n],t));var a=e.rights;if(a){var i=/^(https?:\/\/[^\s]+)|(www\.[^\s]+)/.test(a);r.push({label:"License",value:i?"").concat(a,""):a})}return r}function Qt(e){var t=null;if(e){var r=e.service;if(r&&r.length>0){var n=r.filter((function(e){return"SearchService2"===e.type}));t=(null==n?void 0:n.length)>0?n[0].id:null}}return t}function Xt(e){if(!e)return null;var t,r=e.target.split("#t="),n=T(r,2),a=n[0],i=n[1],o=e.body;return 0===Object.keys(o).length?null:"TextualBody"===(null==o?void 0:o.type)?{id:e.id,time:parseFloat(i),timeStr:Tt(parseFloat(i),!0,!0),canvasId:a,value:null!==(t=null==o?void 0:o.value)&&void 0!==t?t:""}:null}function Kt(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function $t(e){for(var t=1;t0?e.filter((function(e){return e.canvasIndex==t+1&&!e.isCanvas})):[]).length>0}function ar(){var e,t,r,n,a=arguments.length>0&&void 0!==arguments[0]?arguments[0]:rr,i=arguments.length>1?arguments[1]:void 0;switch(i.type){case"updateManifest":var o=i.manifest,l=Ht(o),s=!!(n=o.behavior)&&(null==n?void 0:n.includes("auto-advance")),c=(r=o.label)?Mt(r).includes("[Playlist]"):(console.warn("playlist-parser -> getIsPlaylist() -> manifest.label not found"),!1),u=(null==(e=o.service)?void 0:e.length)>0&&"AnnotationService0"===(null===(t=e[0])||void 0===t?void 0:t.type)?e[0].id:null,d=function(e){try{var t=e.items,r=[];return t&&t.map((function(e,t){var n,a,i=e.annotations;if(i&&0!==(null===(n=i[0])||void 0===n?void 0:n.items.length))if((null===(a=i[0])||void 0===a?void 0:a.items.length)>0){var o=[],l=qt(e.annotations,"highlighting");(null==l?void 0:l.length)>0&&l.map((function(e){var t=Xt(e);t&&o.push(t)})),r.push({canvasMarkers:o,canvasIndex:t})}else r.push({canvasMarkers:[],canvasIndex:t});else r.push({canvasMarkers:[],canvasIndex:t})})),r}catch(e){throw e}}(o);return $t($t({},a),{},{manifest:o,allCanvases:l,autoAdvance:s,playlist:$t($t({},a.playlist),{},{isPlaylist:c,annotationServiceId:u,hasAnnotationService:!!u,markers:d})});case"switchCanvas":return $t($t({},a),{},{canvasIndex:i.canvasIndex,hasStructure:nr(a.canvasSegments,i.canvasIndex)});case"switchItem":return $t($t({},a),{},{currentNavItem:i.item});case"canvasDuration":return $t($t({},a),{},{canvasDuration:i.canvasDuration});case"canvasLink":return $t($t({},a),{},{canvasLink:i.canvasLink});case"canvasTargets":return $t($t({},a),{},{targets:i.canvasTargets});case"hasMultipleItems":return $t($t({},a),{},{hasMultiItems:i.isMultiSource});case"setSrcIndex":return $t($t({},a),{},{srcIndex:i.srcIndex});case"setItemStartTime":return $t($t({},a),{},{startTime:i.startTime});case"setAutoAdvance":return $t($t({},a),{},{autoAdvance:i.autoAdvance});case"setPlaylistMarkers":if(i.markers)return $t($t({},a),{},{playlist:$t($t({},a.playlist),{},{markers:i.markers})});if(i.updatedMarkers)return $t($t({},a),{},{playlist:$t($t({},a.playlist),{},{markers:a.playlist.markers.map((function(e){return e.canvasIndex===a.canvasIndex&&(e.canvasMarkers=i.updatedMarkers),e}))})});case"setIsEditing":return $t($t({},a),{},{playlist:$t($t({},a.playlist),{},{isEditing:i.isEditing})});case"setCanvasIsEmpty":return $t($t({},a),{},{canvasIsEmpty:i.isEmpty});case"setStructures":return $t($t({},a),{},{structures:i.structures});case"setCanvasSegments":var f=i.timespans.filter((function(e){return e.canvasIndex==a.canvasIndex+1&&!e.isCanvas}));return $t($t({},a),{},{canvasSegments:i.timespans,hasStructure:f.length>0});case"setCustomStart":var p=i.customStart,m=p.canvas,v=p.time;return $t($t({},a),{},{customStart:{startIndex:m,startTime:v},canvasIndex:m,hasStructure:nr(a.canvasSegments,m)});case"setRenderingFiles":return $t($t({},a),{},{renderings:$t({},i.renderings)});default:throw new Error("Unhandled action type: ".concat(i.type))}}function ir(e){var t=e.initialState,r=void 0===t?rr:t,n=e.children,a=u.default.useReducer(ar,r),i=T(a,2),o=i[0],l=i[1];return u.default.createElement(er.Provider,{value:o},u.default.createElement(tr.Provider,{value:l},n))}function or(){var e=u.default.useContext(er);if(void 0===e)throw new Error("useManifestState must be used within a ManifestProvider");return e}function lr(){var e=u.default.useContext(tr);if(void 0===e)throw new Error("useManifestDispatch must be used within a ManifestProvider");return e}function sr(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function cr(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:fr,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case"updatePlayer":return cr(cr({},e),{},{player:t.player});case"navClick":return cr(cr({},e),{},{clickedUrl:t.clickedUrl,isClicked:!0});case"resetClick":return cr(cr({},e),{},{isClicked:!1});case"setTimeFragment":return cr(cr({},e),{},{startTime:t.startTime,endTime:t.endTime});case"setSearchMarkers":return cr(cr({},e),{},{searchMarkers:t.payload});case"setPlayingStatus":return cr(cr({},e),{},{isPlaying:t.isPlaying});case"setCaptionStatus":return cr(cr({},e),{},{captionOn:t.captionOn});case"setIsEnded":return cr(cr({},e),{},{isEnded:t.isEnded});case"setCurrentTime":return cr(cr({},e),{},{currentTime:t.currentTime});case"setPlayerFocusElement":return cr(cr({},e),{},{playerFocusElement:t.element?t.element:""});default:throw new Error("Unhandled action type: ".concat(t.type))}}function mr(e){var t=e.initialState,r=void 0===t?fr:t,n=e.children,a=u.default.useReducer(pr,r),i=T(a,2),o=i[0],l=i[1];return u.default.createElement(ur.Provider,{value:o},u.default.createElement(dr.Provider,{value:l},n))}function vr(){var e=u.default.useContext(ur);if(void 0===e)throw new Error("usePlayerState must be used within the PlayerProvider");return e}function hr(){var e=u.default.useContext(dr);if(void 0===e)throw new Error("usePlayerDispatch must be used within the PlayerProvider");return e}var gr=g(y((function(e){function t(e,t,r,n,a,i,o){try{var l=e[i](o),s=l.value}catch(e){return void r(e)}l.done?t(s):Promise.resolve(s).then(n,a)}e.exports=function(e){return function(){var r=this,n=arguments;return new Promise((function(a,i){var o=e.apply(r,n);function l(e){t(o,a,i,l,s,"next",e)}function s(e){t(o,a,i,l,s,"throw",e)}l(void 0)}))}},e.exports.__esModule=!0,e.exports.default=e.exports}))),yr=y((function(e){var t=S.default;function r(){e.exports=r=function(){return n},e.exports.__esModule=!0,e.exports.default=e.exports;var n={},a=Object.prototype,i=a.hasOwnProperty,o=Object.defineProperty||function(e,t,r){e[t]=r.value},l="function"==typeof Symbol?Symbol:{},s=l.iterator||"@@iterator",c=l.asyncIterator||"@@asyncIterator",u=l.toStringTag||"@@toStringTag";function d(e,t,r){return Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}),e[t]}try{d({},"")}catch(e){d=function(e,t,r){return e[t]=r}}function f(e,t,r,n){var a=t&&t.prototype instanceof v?t:v,i=Object.create(a.prototype),l=new I(n||[]);return o(i,"_invoke",{value:T(e,r,l)}),i}function p(e,t,r){try{return{type:"normal",arg:e.call(t,r)}}catch(e){return{type:"throw",arg:e}}}n.wrap=f;var m={};function v(){}function h(){}function g(){}var y={};d(y,s,(function(){return this}));var b=Object.getPrototypeOf,x=b&&b(b(O([])));x&&x!==a&&i.call(x,s)&&(y=x);var k=g.prototype=v.prototype=Object.create(y);function w(e){["next","throw","return"].forEach((function(t){d(e,t,(function(e){return this._invoke(t,e)}))}))}function E(e,r){function n(a,o,l,s){var c=p(e[a],e,o);if("throw"!==c.type){var u=c.arg,d=u.value;return d&&"object"==t(d)&&i.call(d,"__await")?r.resolve(d.__await).then((function(e){n("next",e,l,s)}),(function(e){n("throw",e,l,s)})):r.resolve(d).then((function(e){u.value=e,l(u)}),(function(e){return n("throw",e,l,s)}))}s(c.arg)}var a;o(this,"_invoke",{value:function(e,t){function i(){return new r((function(r,a){n(e,t,r,a)}))}return a=a?a.then(i,i):i()}})}function T(e,t,r){var n="suspendedStart";return function(a,i){if("executing"===n)throw new Error("Generator is already running");if("completed"===n){if("throw"===a)throw i;return q()}for(r.method=a,r.arg=i;;){var o=r.delegate;if(o){var l=S(o,r);if(l){if(l===m)continue;return l}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if("suspendedStart"===n)throw n="completed",r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);n="executing";var s=p(e,t,r);if("normal"===s.type){if(n=r.done?"completed":"suspendedYield",s.arg===m)continue;return{value:s.arg,done:r.done}}"throw"===s.type&&(n="completed",r.method="throw",r.arg=s.arg)}}}function S(e,t){var r=t.method,n=e.iterator[r];if(void 0===n)return t.delegate=null,"throw"===r&&e.iterator.return&&(t.method="return",t.arg=void 0,S(e,t),"throw"===t.method)||"return"!==r&&(t.method="throw",t.arg=new TypeError("The iterator does not provide a '"+r+"' method")),m;var a=p(n,e.iterator,t.arg);if("throw"===a.type)return t.method="throw",t.arg=a.arg,t.delegate=null,m;var i=a.arg;return i?i.done?(t[e.resultName]=i.value,t.next=e.nextLoc,"return"!==t.method&&(t.method="next",t.arg=void 0),t.delegate=null,m):i:(t.method="throw",t.arg=new TypeError("iterator result is not an object"),t.delegate=null,m)}function C(e){var t={tryLoc:e[0]};1 in e&&(t.catchLoc=e[1]),2 in e&&(t.finallyLoc=e[2],t.afterLoc=e[3]),this.tryEntries.push(t)}function R(e){var t=e.completion||{};t.type="normal",delete t.arg,e.completion=t}function I(e){this.tryEntries=[{tryLoc:"root"}],e.forEach(C,this),this.reset(!0)}function O(e){if(e){var t=e[s];if(t)return t.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var r=-1,n=function t(){for(;++r=0;--n){var a=this.tryEntries[n],o=a.completion;if("root"===a.tryLoc)return r("end");if(a.tryLoc<=this.prev){var l=i.call(a,"catchLoc"),s=i.call(a,"finallyLoc");if(l&&s){if(this.prev=0;--r){var n=this.tryEntries[r];if(n.tryLoc<=this.prev&&i.call(n,"finallyLoc")&&this.prev=0;--t){var r=this.tryEntries[t];if(r.finallyLoc===e)return this.complete(r.completion,r.afterLoc),R(r),m}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var r=this.tryEntries[t];if(r.tryLoc===e){var n=r.completion;if("throw"===n.type){var a=n.arg;R(r)}return a}}throw new Error("illegal catch attempt")},delegateYield:function(e,t,r){return this.delegate={iterator:O(e),resultName:t,nextLoc:r},"next"===this.method&&(this.arg=void 0),m}},n}e.exports=r,e.exports.__esModule=!0,e.exports.default=e.exports})),br=yr(),xr=br;try{regeneratorRuntime=br}catch(e){"object"==typeof globalThis?globalThis.regeneratorRuntime=br:Function("r","regeneratorRuntime = r")(br)}function kr(){}function wr(){}wr.resetWarningCache=kr;var Er=function(){function e(e,t,r,n,a,i){if("SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"!==i){var o=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw o.name="Invariant Violation",o}}function t(){return e}e.isRequired=e;var r={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:wr,resetWarningCache:kr};return r.PropTypes=r,r},Tr=y((function(e){e.exports=Er()})),Sr=function(){return u.default.createElement("div",{className:"lds-spinner"},u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null))};function Cr(e){var t,r=e.manifestUrl,n=e.customErrorMessage,i=e.emptyManifestMessage,o=e.startCanvasId,l=e.startCanvasTime,s=e.children,c=e.manifest,d=u.default.useState(c),f=T(d,2),p=f[0],m=f[1],v=lr(),h=hr(),g=a.useErrorBoundary().showBoundary,y=function(){var e=gr(xr.mark((function e(r){var n,a;return xr.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return t=new AbortController,n={},a=r.replace(/[\?#].*(?=\/)/i,""),e.prev=3,e.next=6,fetch(a,n,{signal:t.signal}).then((function(e){if(200!=e.status&&201!=e.status)throw new Error("Failed to fetch Manifest. Please check again.");return e.json()})).then((function(e){if(!e)throw new Error(bt);m(e)})).catch((function(e){throw console.log("Error fetching manifest, ",e),new Error("Failed to fetch Manifest. Please check again.")}));case 6:e.next=11;break;case 8:e.prev=8,e.t0=e.catch(3),g(e.t0);case 11:case"end":return e.stop()}}),e,null,[[3,8]])})));return function(t){return e.apply(this,arguments)}}();return u.default.useEffect((function(){return bt=n||yt,function(e){kt=e||xt}(i),!p&&r&&y(r),function(){t&&t.abort()}}),[]),u.default.useEffect((function(){if(p){var e=function(e){var t=[],r=[],n=e.rendering,a=e.items;return n&&n.map((function(e){var r=Wt(e.format,e.label,e.id);t.push(r)})),a&&a.map((function(e,t){var n=e.rendering,a=[];n&&n.map((function(e){var t=Wt(e.format,e.label,e.id);a.push(t)})),r.push({label:Mt(e.label)||"Section ".concat(t+1),files:a})})),{manifest:t,canvas:r}}(p);v({renderings:e,type:"setRenderingFiles"});var t=function(e,t,r){var n=e.start,a={};if(!n&&void 0===t&&void 0===r)return{type:"C",canvas:0,time:0};null!=t||null!=r?(a={id:t,selector:{type:"PointSelector",t:void 0===r?0:r},type:void 0===r?"Canvas":"SpecificResource"},null!=r&&(a.source=t)):n&&(a=n);var i=Ht(e),o=function(e,n,a){var o=a,l=0;if(i&&(null==i?void 0:i.length)>0){if(e)if(void 0===(l=i.findIndex((function(t){return t.canvasId===e})))||l<0)console.warn("Given Canvas was not found in Manifest, ",t),o=0,l=0;else{var s=i[l];if(null!=s.range&&"SpecificResource"===n){var c=s.range,u=c.start,d=c.end;a>=u&&a<=d||(console.warn("Given start time is not within Canvas duration, ",r),o=0)}}}else console.warn("No Canvases in given Manifest"),o=0;return{currentIndex:l,startTime:o}};if(null!=a)switch(a.type){case"Canvas":var l=o(a.id,a.type,0);return{type:"C",canvas:l.currentIndex,time:l.startTime};case"SpecificResource":var s=a.selector.t;return{type:"SR",canvas:(l=o(a.source,a.type,s)).currentIndex,time:l.startTime}}}(p,o,l);v({customStart:t,type:"setCustomStart"}),"SR"==t.type&&h({currentTime:t.time,type:"setCurrentTime"}),v({manifest:p,type:"updateManifest"})}}),[p]),p?u.default.createElement(u.default.Fragment,null,s):u.default.createElement(Sr,null)}function Rr(e){var t=e.error,r=e.resetErrorBoundary;return u.default.createElement("div",{role:"alert",className:"ramp--error-message__alert"},u.default.createElement("span",{className:"ramp--error-message__message",dangerouslySetInnerHTML:{__html:t.message}}),u.default.createElement("button",{className:"ramp--error-message__reset-button",onClick:r},"Try again"))}Cr.propTypes={manifest:Tr.object,customErrorMessage:Tr.string,emptyManifestMessage:Tr.string,manifestUrl:Tr.string,startCanvasId:Tr.string,startCanvasTime:Tr.number,children:Tr.node};var Ir=function(e){e.message;var t=e.children;return u.default.createElement(a.ErrorBoundary,{FallbackComponent:Rr,onReset:function(e){}},t)};function Or(e){var t=e.manifestUrl,r=e.manifest,n=e.customErrorMessage,a=e.emptyManifestMessage,i=e.startCanvasId,o=e.startCanvasTime,l=e.children;return t||r?u.default.createElement(ir,null,u.default.createElement(mr,null,u.default.createElement(Ir,null,u.default.createElement(Cr,{manifestUrl:t,manifest:r,customErrorMessage:n,emptyManifestMessage:a,startCanvasId:i,startCanvasTime:o},l)))):u.default.createElement("p",null,"Please provide a valid manifest.")}Ir.propTypes={message:Tr.string,children:Tr.object},Or.propTypes={manifestUrl:Tr.string,manifest:Tr.object,customErrorMessage:Tr.string,emptyManifestMessage:Tr.string,startCanvasId:Tr.string,startCanvasTime:Tr.number};var qr=function(){return Z.Date.now()},jr=/\s/;var Ar=function(e){for(var t=e.length;t--&&jr.test(e.charAt(t)););return t},Dr=/^\s+/;var Mr=function(e){return e?e.slice(0,Ar(e)+1).replace(Dr,""):e},Pr="[object Symbol]";var Lr=function(e){return"symbol"==typeof e||ze(e)&&oe(e)==Pr},_r=NaN,Nr=/^[-+]0x[0-9a-f]+$/i,Fr=/^0b[01]+$/i,Br=/^0o[0-7]+$/i,Ur=parseInt;var Vr=function(e){if("number"==typeof e)return e;if(Lr(e))return _r;if(le(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=le(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=Mr(e);var r=Fr.test(e);return r||Br.test(e)?Ur(e.slice(2),r?2:8):Nr.test(e)?_r:+e},Hr="Expected a function",Gr=Math.max,zr=Math.min;var Jr=function(e,t,r){var n,a,i,o,l,s,c=0,u=!1,d=!1,f=!0;if("function"!=typeof e)throw new TypeError(Hr);function p(t){var r=n,i=a;return n=a=void 0,c=t,o=e.apply(i,r)}function m(e){var r=e-s;return void 0===s||r>=t||r<0||d&&e-c>=i}function v(){var e=qr();if(m(e))return h(e);l=setTimeout(v,function(e){var r=t-(e-s);return d?zr(r,i-(e-c)):r}(e))}function h(e){return l=void 0,f&&n?p(e):(n=a=void 0,o)}function g(){var e=qr(),r=m(e);if(n=arguments,a=this,s=e,r){if(void 0===l)return function(e){return c=e,l=setTimeout(v,t),u?p(e):o}(s);if(d)return clearTimeout(l),l=setTimeout(v,t),p(s)}return void 0===l&&(l=setTimeout(v,t)),o}return t=Vr(t)||0,le(r)&&(u=!!r.leading,i=(d="maxWait"in r)?Gr(Vr(r.maxWait)||0,t):i,f="trailing"in r?!!r.trailing:f),g.cancel=function(){void 0!==l&&clearTimeout(l),c=0,n=s=a=l=void 0},g.flush=function(){return void 0===l?o:h(qr())},g},Wr="Expected a function";var Zr=function(e,t,r){var n=!0,a=!0;if("function"!=typeof e)throw new TypeError(Wr);return le(r)&&(n="leading"in r?!!r.leading:n,a="trailing"in r?!!r.trailing:a),Jr(e,t,{leading:n,maxWait:t,trailing:a})};y((function(e,t){!function(e){var t=r(e);function r(e){return e&&e.__esModule?e:{default:e}}var n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},a={markerStyle:{width:"7px","border-radius":"30%","background-color":"red"},markerTip:{display:!0,text:function(e){return"Break: "+e.text},time:function(e){return e.time}},breakOverlay:{display:!1,displayTime:3,text:function(e){return"Break overlay: "+e.overlayText},style:{width:"100%",height:"20%","background-color":"rgba(0,0,0,0.7)",color:"white","font-size":"17px"}},onMarkerClick:function(e){},onMarkerReached:function(e,t){},markers:[]};function i(){var e=(new Date).getTime();return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(t){var r=(e+16*Math.random())%16|0;return e=Math.floor(e/16),("x"==t?r:3&r|8).toString(16)}))}function o(e){var t,r={top:0,bottom:0,left:0,width:0,height:0,right:0};try{t=e.getBoundingClientRect()}catch(e){t=r}return t}var l=-1;function s(e){if(!t.default.mergeOptions){var r=function(e){return!!e&&"object"===(void 0===e?"undefined":n(e))&&"[object Object]"===toString.call(e)&&e.constructor===Object},s=function e(t,n){var a={};return[t,n].forEach((function(t){t&&Object.keys(t).forEach((function(n){var i=t[n];r(i)?(r(a[n])||(a[n]={}),a[n]=e(a[n],i)):a[n]=i}))})),a};t.default.mergeOptions=s}t.default.dom.createEl||(t.default.dom.createEl=function(e,r,n){var a=t.default.Player.prototype.dom.createEl(e,r);return n&&Object.keys(n).forEach((function(e){a.setAttribute(e,n[e])})),a});var c=t.default.mergeOptions(a,e),u={},d=[],f=l,p=this,m=null,v=null,h=l;function g(){d.sort((function(e,t){return c.markerTip.time(e)-c.markerTip.time(t)}))}function y(e){e.forEach((function(e){e.key=i(),p.el().querySelector(".vjs-progress-holder").appendChild(k(e)),u[e.key]=e,d.push(e)})),g()}function b(e){return c.markerTip.time(e)/p.duration()*100}function x(e,t){t.className="vjs-marker "+(e.class||""),Object.keys(c.markerStyle).forEach((function(e){t.style[e]=c.markerStyle[e]}));var r=e.time/p.duration();if((r<0||r>1)&&(t.style.display="none"),t.style.left=b(e)+"%",e.duration)t.style.width=e.duration/p.duration()*100+"%",t.style.marginLeft="0px";else{var n=o(t);t.style.marginLeft=n.width/2+"px"}}function k(e){var r=t.default.dom.createEl("div",{},{"data-marker-key":e.key,"data-marker-time":c.markerTip.time(e)});return x(e,r),r.addEventListener("click",(function(t){var r=!1;if("function"==typeof c.onMarkerClick&&(r=!1===c.onMarkerClick(e)),!r){var n=this.getAttribute("data-marker-key");p.currentTime(c.markerTip.time(u[n]))}})),c.markerTip.display&&T(r),r}function w(e){d.forEach((function(t){var r=p.el().querySelector(".vjs-marker[data-marker-key='"+t.key+"']"),n=c.markerTip.time(t);(e||r.getAttribute("data-marker-time")!==n)&&(x(t,r),r.setAttribute("data-marker-time",n))})),g()}function E(e){v&&(h=l,v.style.visibility="hidden"),f=l;var t=[];e.forEach((function(e){var r=d[e];if(r){delete u[r.key],t.push(e);var n=p.el().querySelector(".vjs-marker[data-marker-key='"+r.key+"']");n&&n.parentNode.removeChild(n)}})),t.reverse(),t.forEach((function(e){d.splice(e,1)})),g()}function T(e){e.addEventListener("mouseover",(function(){var t=u[e.getAttribute("data-marker-key")];if(m){c.markerTip.html?m.querySelector(".vjs-tip-inner").innerHTML=c.markerTip.html(t):m.querySelector(".vjs-tip-inner").innerText=c.markerTip.text(t),m.style.left=b(t)+"%";var r=o(m),n=o(e);m.style.marginLeft=-parseFloat(r.width/2)+parseFloat(n.width/4)+"px",m.style.visibility="visible"}})),e.addEventListener("mouseout",(function(){m&&(m.style.visibility="hidden")}))}function S(){m=t.default.dom.createEl("div",{className:"vjs-tip",innerHTML:"
    "}),p.el().querySelector(".vjs-progress-holder").appendChild(m)}function C(){if(c.breakOverlay.display&&!(f<0)){var e=p.currentTime(),t=d[f],r=c.markerTip.time(t);e>=r&&e<=r+c.breakOverlay.displayTime?(h!==f&&(h=f,v&&(v.querySelector(".vjs-break-overlay-text").innerHTML=c.breakOverlay.text(t))),v&&(v.style.visibility="visible")):(h=l,v&&(v.style.visibility="hidden"))}}function R(){v=t.default.dom.createEl("div",{className:"vjs-break-overlay",innerHTML:"
    "}),Object.keys(c.breakOverlay.style).forEach((function(e){v&&(v.style[e]=c.breakOverlay.style[e])})),p.el().appendChild(v),h=l}function I(){O(),C(),e.onTimeUpdateAfterMarkerUpdate&&e.onTimeUpdateAfterMarkerUpdate()}function O(){if(d.length){var t=function(e){return e=c.markerTip.time(d[f])&&r=c.markerTip.time(d[i])&&re){p.currentTime(r);break}}},prev:function(){for(var e=p.currentTime(),t=d.length-1;t>=0;t--){var r=c.markerTip.time(d[t]);if(r+.52&&!window.matchMedia("(pointer: fine").matches,an=dn.mobile||Kr||on),!en){var fn=window.navigator&&window.navigator.userAgent||"";Xr=/iPod/i.test(fn),(Qr=fn.match(/OS (\d+)_/i))&&Qr[1]&&Qr[1],Kr=/Android/i.test(fn),function(){var e=fn.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i);if(!e)return null;var t=e[1]&&parseFloat(e[1]),r=e[2]&&parseFloat(e[2]);t&&r&&parseFloat(e[1]+"."+e[2])}(),/Firefox/i.test(fn),$r=/Edg/i.test(fn),en=/Chrome/i.test(fn)||/CriOS/i.test(fn),tn=!$r&&en,function(){var e=fn.match(/(Chrome|CriOS)\/(\d+)/);e&&e[2]&&parseFloat(e[2])}(),Yr=/MSIE\s(\d+)\.\d/.exec(fn),!(Yr&&parseFloat(Yr[1]))&&/Trident\/7.0/i.test(fn)&&/rv:11.0/.test(fn)&&11,cn=/Tizen/i.test(fn),un=/Web0S/i.test(fn),rn=/Safari/i.test(fn)&&!tn&&!Kr&&!$r&&!cn&&!un,/Windows/i.test(fn),ln=/iPhone/i.test(fn)&&!nn,sn=ln||nn||Xr,on=navigator.maxTouchPoints&&navigator.maxTouchPoints>2&&!window.matchMedia("(pointer: fine").matches,nn=on&&!Kr&&!ln,an=Kr||sn||ln||on||/Mobi/i.test(fn)}var pn=function(t,r){var n=e.useState((function(){return function(e,t){try{var r;return null!==(r=JSON.parse(localStorage.getItem(e)))&&void 0!==r?r:t}catch(e){return t}}(t,r)})),a=T(n,2),i=a[0],o=a[1];return e.useEffect((function(){try{localStorage.setItem(t,JSON.stringify(i))}catch(e){}}),[t,i]),[i,o]},mn=function(){return u.default.createElement("svg",{viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg",style:{fill:"white",height:"1rem",width:"1rem",scale:.8}},u.default.createElement("path",{fillRule:"evenodd",clipRule:"evenodd",d:"M21.1213 2.70705C19.9497 1.53548 18.0503 1.53547 16.8787 2.70705L15.1989 4.38685L7.29289 12.2928C7.16473 12.421 7.07382 12.5816 7.02986 12.7574L6.02986 16.7574C5.94466 17.0982 6.04451 17.4587 6.29289 17.707C6.54127 17.9554 6.90176 18.0553 7.24254 17.9701L11.2425 16.9701C11.4184 16.9261 11.5789 16.8352 11.7071 16.707L19.5556 8.85857L21.2929 7.12126C22.4645 5.94969 22.4645 4.05019 21.2929 2.87862L21.1213 2.70705ZM18.2929 4.12126C18.6834 3.73074 19.3166 3.73074 19.7071 4.12126L19.8787 4.29283C20.2692 4.68336 20.2692 5.31653 19.8787 5.70705L18.8622 6.72357L17.3068 5.10738L18.2929 4.12126ZM15.8923 6.52185L17.4477 8.13804L10.4888 15.097L8.37437 15.6256L8.90296 13.5112L15.8923 6.52185ZM4 7.99994C4 7.44766 4.44772 6.99994 5 6.99994H10C10.5523 6.99994 11 6.55223 11 5.99994C11 5.44766 10.5523 4.99994 10 4.99994H5C3.34315 4.99994 2 6.34309 2 7.99994V18.9999C2 20.6568 3.34315 21.9999 5 21.9999H16C17.6569 21.9999 19 20.6568 19 18.9999V13.9999C19 13.4477 18.5523 12.9999 18 12.9999C17.4477 12.9999 17 13.4477 17 13.9999V18.9999C17 19.5522 16.5523 19.9999 16 19.9999H5C4.44772 19.9999 4 19.5522 4 18.9999V7.99994Z",fill:"#fffff"}))},vn=function(){return u.default.createElement("svg",{viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",stroke:"#ffffff",style:{height:"1rem",width:"1rem",scale:.8}},u.default.createElement("g",{strokeWidth:"0",strokeLinecap:"round",strokeLinejoin:"round"},u.default.createElement("path",{d:"M10 12V17",stroke:"#ffffff",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),u.default.createElement("path",{d:"M14 12V17",stroke:"#ffffff",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),u.default.createElement("path",{d:"M4 7H20",stroke:"#ffffff",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),u.default.createElement("path",{d:"M6 10V18C6 19.6569 7.34315 21 9 21H15C16.6569 21 18 19.6569 18 18V10",stroke:"#ffffff",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),u.default.createElement("path",{d:"M9 5C9 3.89543 9.89543 3 11 3H13C14.1046 3 15 3.89543 15 5V7H9V5Z",stroke:"#ffffff",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})))},hn=function(){return u.default.createElement("svg",{viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",style:{height:"1rem",width:"1rem",scale:.8}},u.default.createElement("g",{strokeWidth:"0",strokeLinecap:"round",strokeLinejoin:"round"},u.default.createElement("path",{id:"Vector",d:"M6 12L10.2426 16.2426L18.727 7.75732",stroke:"#ffffff",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})))},gn=function(){return u.default.createElement("svg",{fill:"#ffffff",viewBox:"0 0 32 32",version:"1.1",xmlns:"http://www.w3.org/2000/svg",style:{height:"1rem",width:"1rem",scale:.8}},u.default.createElement("g",{strokeWidth:"0",strokeLinecap:"round",strokeLinejoin:"round"},u.default.createElement("path",{d:"M19.587 16.001l6.096 6.096c0.396 0.396 0.396 1.039 0 1.435l-2.151 2.151c-0.396 0.396-1.038 0.396-1.435 0l-6.097-6.096-6.097 6.096c-0.396 0.396-1.038 0.396-1.434 0l-2.152-2.151c-0.396-0.396-0.396-1.038 0-1.435l6.097-6.096-6.097-6.097c-0.396-0.396-0.396-1.039 0-1.435l2.153-2.151c0.396-0.396 1.038-0.396 1.434 0l6.096 6.097 6.097-6.097c0.396-0.396 1.038-0.396 1.435 0l2.151 2.152c0.396 0.396 0.396 1.038 0 1.435l-6.096 6.096z"})))},yn=function(e){var t=e.flip,r=void 0!==t&&t;return u.default.createElement("svg",{viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",style:{fill:"white",height:"1.25rem",width:"1.25rem",transform:r?"rotate(180deg)":"rotate(0)"}},u.default.createElement("g",{strokeWidth:"0",strokeLinecap:"round",strokeLinejoin:"round"},u.default.createElement("path",{d:"M4 20L15.3333 12L4 4V20Z",fill:"#ffffff"}),u.default.createElement("path",{d:"M20 4H17.3333V20H20V4Z",fill:"#ffffff"})))},bn=function(){return u.default.createElement("svg",{viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg",style:{height:"0.75rem",width:"0.75rem"},className:"structure-item-locked"},u.default.createElement("g",{strokeWidth:"0",strokeLinecap:"round",strokeLinejoin:"round"},u.default.createElement("path",{fillRule:"evenodd",clipRule:"evenodd",d:"M5.25 10.0546V8C5.25 4.27208 8.27208 1.25 12 1.25C15.7279 1.25 18.75 4.27208 18.75 8V10.0546C19.8648 10.1379 20.5907 10.348 21.1213 10.8787C22 11.7574 22 13.1716 22 16C22 18.8284 22 20.2426 21.1213 21.1213C20.2426 22 18.8284 22 16 22H8C5.17157 22 3.75736 22 2.87868 21.1213C2 20.2426 2 18.8284 2 16C2 13.1716 2 11.7574 2.87868 10.8787C3.40931 10.348 4.13525 10.1379 5.25 10.0546ZM6.75 8C6.75 5.10051 9.10051 2.75 12 2.75C14.8995 2.75 17.25 5.10051 17.25 8V10.0036C16.867 10 16.4515 10 16 10H8C7.54849 10 7.13301 10 6.75 10.0036V8Z",fill:"#000000"})))},xn=function(e){var t=e.flip,r=void 0!==t&&t;return u.default.createElement("svg",{viewBox:"0 0 1024 1024",fill:"#ffffff",xmlns:"http://www.w3.org/2000/svg",style:{height:"1rem",width:"1rem",scale:.8,transform:r?"rotate(180deg)":"rotate(0)"}},u.default.createElement("g",{id:"SVGRepo_bgCarrier",strokeWidth:"0"}),u.default.createElement("g",{id:"SVGRepo_tracerCarrier",strokeLinecap:"round",strokeLinejoin:"round"}),u.default.createElement("g",{id:"SVGRepo_iconCarrier"},u.default.createElement("path",{d:"M256 120.768L306.432 64 768 512l-461.568 448L256 903.232 659.072 512z",fill:"#ffffff"})))},kn=function(){return u.default.createElement("svg",{viewBox:"0 0 24 24",fill:"#fffff",xmlns:"http://www.w3.org/2000/svg",style:{fill:"none",height:"1.25rem",width:"1.25rem"}},u.default.createElement("g",{id:"SVGRepo_bgCarrier",strokeWidth:"0"}),u.default.createElement("g",{id:"SVGRepo_tracerCarrier",strokeLinecap:"round",strokeLinejoin:"round"}),u.default.createElement("g",{id:"SVGRepo_iconCarrier"},u.default.createElement("rect",{width:"24",height:"24",fill:"none"}),u.default.createElement("path",{d:"M5 12V18C5 18.5523 5.44772 19 6 19H18C18.5523 19 19 18.5523 19 18V12",stroke:"#ffffff",strokeLinecap:"round",strokeLinejoin:"round"}),u.default.createElement("path",{d:"M12 3L12 15M12 15L16 11M12 15L8 11",stroke:"#ffffff",strokeLinecap:"round",strokeLinejoin:"round"})))},wn=g(y((function(e){e.exports=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},e.exports.__esModule=!0,e.exports.default=e.exports}))),En=g(y((function(e){function t(e,t){for(var r=0;ra.start?this.initializeProgress(a.customStart):this.initializeProgress(a.start),this.setIsMultiSource((null==n?void 0:n.length)>1),this.playerEventListener||(this.playerEventListener=setInterval((function(){rn&&!ln?e.abortableTimeupdateHandler():e.timeUpdateHandler()}),100))}},{key:"update",value:function(){if(qn(Rn(r.prototype),"update",this).call(this),on&&0===this.player.currentTime()&&(this.removeClass("played-range"),document.documentElement.style.setProperty("--range-progress","calc(".concat(0,"%)"))),an&&rn&&this.player.paused()){var e,t=null!==(e=this.player.structStart)&&void 0!==e?e:0;if(0!=t&&0===this.player.currentTime()){this.player.currentTime(t);var n=Math.min(100,Math.max(0,t/this.totalDuration*100));this.addClass("played-range"),document.documentElement.style.setProperty("--range-progress","calc(".concat(n,"%)")),this.player.structStart=0}}}},{key:"initializeEl",value:function(){var e=this,t=p.default.dom.createEl("div",{className:"block-stripes",role:"presentation",id:"left-block"}),r=p.default.dom.createEl("div",{className:"block-stripes",role:"presentation",id:"right-block"});this.el().appendChild(t),this.el().appendChild(r),this.el().addEventListener("mouseenter",(function(t){e.handleMouseMove(t)})),this.el().addEventListener("pointerup",(function(t){e.pointerDragged&&e.handleMouseUp(t)})),this.el().addEventListener("pointermove",(function(t){e.handleMouseMove(t),e.pointerDragged=!0})),this.el().addEventListener("pointerdown",(function(t){e.handleMouseDown(t),e.pointerDragged=!1}))}},{key:"handleMouseMove",value:function(e){var t=this.convertToTime(e),r=t.currentTime,n=t.offsetx;null!=r&&this.setCurrentTime(r);var a=this.getChild("MouseTimeDisplay");if(a){var i=a.getChild("TimeTooltip").el_;r&&(i.innerHTML=Tt(r));var o=i.clientWidth/2;i.style.left="".concat(n-o,"px")}}},{key:"handleMouseDown",value:function(e){if(on||2!==e.buttons){var t,r=this.convertToTime(e),n=r.currentTime;if(r._,this.isMultiSourceRef.current&&(t=this.canvasTargetsRef.current.find((function(e){var t=e.altStart+e.duration;if(n>=e.altStart&&n<=t)return e}))),t){var a,i,o=null!==(a=null===(i=t)||void 0===i?void 0:i.sIndex)&&void 0!==a?a:0;o!=this.srcIndexRef.current?(this.selectSource(t.sIndex,n-t.altStart),this.setSrcIndex(o)):this.player.currentTime(n-t.altStart)}else this.player.currentTime(n);if(on){var l=Math.min(100,Math.max(0,n/this.totalDuration*100));this.player.currentTime(n),this.addClass("played-range"),document.documentElement.style.setProperty("--range-progress","calc(".concat(l,"%)"))}}}},{key:"handleMouseUp",value:function(e){this.handleMouseDown(e)}},{key:"buildProgressBar",value:function(){var e;this.removeClass("played-range");var t=this.canvasTargetsRef,r=this.isMultiSourceRef,n=this.player,a=this.srcIndexRef,i=this.totalDuration;if((null===(e=t.current)||void 0===e?void 0:e.length)>0){var o=t.current[a.current],l=o.altStart,s=o.start,c=o.end,u=o.duration,d=document.getElementById("left-block"),f=document.getElementById("right-block");if(r.current){var p=Math.min(100,Math.max(0,l/i*100));this.playProgress.el_.style.left="".concat(p,"%"),this.loadProgress.el_.style.left="".concat(p,"%"),this.addClass("played-range"),document.documentElement.style.setProperty("--range-progress","calc(".concat(p,"%)"))}else{var m=100*s/u,v=100*(u-c)/u;n.isClipped=v>0,d&&(d.style.width="".concat(m,"%")),f&&(f.style.width=v+"%",f.style.left="".concat(100-v-m,"%"))}}}},{key:"convertToTime",value:function(e){var t,r,n=e.srcElement;if(n.classList.contains("block-stripes")){var a=this.canvasTargetsRef.current[0],i=a.altStart,o=a.end,l=a.duration;return"right-block"===n.id?{currentTime:o,offsetx:o/l*this.el().clientWidth}:{currentTime:i,offsetx:i/l*this.el().clientWidth}}var s,c=e.target.getBoundingClientRect().x,u=null!=e.nativeEvent?null!=e.nativeEvent.offsetX?e.nativeEvent.offsetX:(null===(t=e.nativeEvent.targetTouches[0])||void 0===t?void 0:t.clientX)-c:e.offsetX,d=null!==(r=this.totalDuration)&&void 0!==r?r:this.player.duration();if(u&&null!=u){if(this.isMultiSourceRef.current){var f=parseFloat(this.playProgress.el_.style.left)/100*this.el().clientWidth,p=n.classList,m=!((null==p?void 0:p.length)>0)||(p.contains("vjs-play-progress")||p.contains("vjs-load-progress"));f>u&&m&&(u+=f)}s=u/this.el().clientWidth*d}if(e.target.hasAttribute("data-start")){var v=e.target.dataset,h=v.start;v._,u=(s+=parseFloat(h))*this.el().clientWidth/this.totalDuration}return{currentTime:s,offsetx:u}}},{key:"abortableTimeupdateHandler",value:function(){var e=this,t=this.player,r=this.progressRef;t.on("waiting",(function(){rn&&!an&&t.currentTime(r.current),n()}));var n=function(){a&&clearInterval(a)},a=setInterval((function(){e.timeUpdateHandler()}),100)}},{key:"timeUpdateHandler",value:function(){var e,t=this,r=this.initTimeRef,n=this.player;n.isDisposed()||n.ended()||null==n||(r.current>0&&0==n.currentTime()?(e=r.current,n.currentTime(r.current)):e=n.currentTime(),rn&&!an&&n.paused()?Jr((function(){t.onTimeUpdate(e)})):this.onTimeUpdate(e),this.setInitTime(0))}},{key:"onTimeUpdate",value:function(e){this.player.hasClass("vjs-ios-native-fs")&&!this.player.audioOnlyMode_||this.setProgress(e),this.handleTimeUpdate(e)}},{key:"handleTimeUpdate",value:function(e){var t,r=this.player,n=this.el_,a=this.canvasTargetsRef,i=this.srcIndexRef;if(n&&r&&a.current){var o=a.current[null!==(t=i.current)&&void 0!==t?t:0],l=o.start,s=o.end,c=o.duration;e=s&&!r.paused()&&!r.isDisposed()&&(s0&&0==e.currentTime()?t.current:e.currentTime();var o=r[null!=n?n:0],l=o.start,s=o.altStart;s!=l&&n>0&&(a+=s),i&&!e.audioOnlyMode_||this.updateTextNode_(a),this.setInitTime(0)}}}]),r}();function Un(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var r,n=Rn(e);if(t){var a=Rn(this).constructor;r=Reflect.construct(n,arguments,a)}else r=n.apply(this,arguments);return Mn(this,r)}}p.default.registerComponent("VideoJSCurrentTime",Bn);var Vn=p.default.getComponent("MenuButton"),Hn=p.default.getComponent("MenuItem"),Gn=function(e){An(r,Vn);var t=Un(r);function r(e,n){var a;return wn(this,r),(a=t.call(this,e,n)).addClass("vjs-file-download"),a.setAttribute("data-testid","videojs-file-download"),a.setIcon("file-download"),a}return En(r,[{key:"createItems",value:function(){var e=this.options_,t=this.player_,r=e.files;return(null==r?void 0:r.length)>0?r.map((function(e){var r=new Hn(t,{label:e.label});return r.handleClick=function(){It(e.id,e.filename,e.fileExt)},r})):[]}}]),r}();function zn(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var r,n=Rn(e);if(t){var a=Rn(this).constructor;r=Reflect.construct(n,arguments,a)}else r=n.apply(this,arguments);return Mn(this,r)}}p.default.registerComponent("VideoJSFileDownload",Gn);var Jn=p.default.getComponent("Button"),Wn=function(e){An(r,Jn);var t=zn(r);function r(e,n){var a;return wn(this,r),(a=t.call(this,e,n)).setIcon("next-item"),a.addClass("vjs-play-control vjs-control"),a.setAttribute("data-testid","videojs-next-button"),a.controlText("Next"),a.options=n,a.player=e,a.cIndex=n.canvasIndex,a.player.on("loadstart",(function(){a.updateComponent()})),a}return En(r,[{key:"updateComponent",value:function(){var e,t=this.player;t&&null!=t&&(void 0===t.canvasIndex&&(null===(e=t.children())||void 0===e?void 0:e.length)>0?this.cIndex=Number(t.children()[0].dataset.canvasindex):this.cIndex=t.canvasIndex);"nextBtn"===this.options.playerFocusElement&&this.el().focus()}},{key:"handleClick",value:function(){this.handleNextClick(!1)}},{key:"handleKeyDown",value:function(e){32!==e.which&&13!==e.which||(e.stopPropagation(),this.handleNextClick(!0))}},{key:"handleNextClick",value:function(e){this.cIndex!=this.options.lastCanvasIndex&&this.options.switchPlayer(this.cIndex+1,!0,e?"nextBtn":"")}}]),r}();function Zn(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var r,n=Rn(e);if(t){var a=Rn(this).constructor;r=Reflect.construct(n,arguments,a)}else r=n.apply(this,arguments);return Mn(this,r)}}p.default.registerComponent("VideoJSNextButton",Wn);var Yn=p.default.getComponent("Button"),Qn=function(e){An(r,Yn);var t=Zn(r);function r(e,n){var a;return wn(this,r),(a=t.call(this,e,n)).setIcon("previous-item"),a.addClass("vjs-play-control vjs-control"),a.setAttribute("data-testid","videojs-previous-button"),a.options=n,a.player=e,a.cIndex=n.canvasIndex,a.player.on("loadstart",(function(){a.updateComponent()})),a}return En(r,[{key:"updateComponent",value:function(){var e,t=this.player;t&&null!=t&&(void 0===t.canvasIndex&&(null===(e=t.children())||void 0===e?void 0:e.length)>0?this.cIndex=Number(t.children()[0].dataset.canvasindex):this.cIndex=t.canvasIndex);this.controlText(0==this.cIndex?"Replay":"Previous"),"previousBtn"===this.options.playerFocusElement&&this.el().focus()}},{key:"handleClick",value:function(){this.handlePreviousClick(!1)}},{key:"handleKeyDown",value:function(e){32!==e.which&&13!==e.which||(e.stopPropagation(),this.handlePreviousClick(!0))}},{key:"handlePreviousClick",value:function(e){this.cIndex>-1&&0!=this.cIndex?this.options.switchPlayer(this.cIndex-1,!0,e?"previousBtn":""):0==this.cIndex&&this.player.currentTime(0)}}]),r}();function Xn(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var r,n=Rn(e);if(t){var a=Rn(this).constructor;r=Reflect.construct(n,arguments,a)}else r=n.apply(this,arguments);return Mn(this,r)}}p.default.registerComponent("VideoJSPreviousButton",Qn);var Kn=p.default.getComponent("Component"),$n=function(e){An(r,Kn);var t=Xn(r);function r(e,n){var a;return wn(this,r),(a=t.call(this,e,n)).setAttribute("data-testid","videojs-title-link"),a.addClass("vjs-title-bar"),a.options=n,a.player=e,a.player.on("loadstart",(function(){a.updateComponent()})),a}return En(r,[{key:"updateComponent",value:function(){var e=this.player;if(e&&null!=e&&e.canvasLink){var t=e.canvasLink,r=t.label,n=t.id,a=r,i=null;i=n.includes("manifest/canvas")?n.replace("manifest/canvas","section"):n;var o=p.default.dom.createEl("a",{className:"vjs-title-link",href:i,target:"_blank",rel:"noreferrer noopener",innerHTML:a});this.el().hasChildNodes()?this.el().replaceChildren(o):this.el().appendChild(o)}}}]),r}();function ea(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var r,n=Rn(e);if(t){var a=Rn(this).constructor;r=Reflect.construct(n,arguments,a)}else r=n.apply(this,arguments);return Mn(this,r)}}Kn.registerComponent("VideoJSTitleLink",$n);var ta,ra='\n\n \n \n \n \n \n \n',na='\n\n \n \n \n \n \n \n';(ta=document.createElement("div")).style.display="none",ta.innerHTML=''.concat(ra).concat(na,""),document.body.appendChild(ta);var aa=p.default.getComponent("Button"),ia=function(e){An(r,aa);var t=ea(r);function r(e,n){var a;return wn(this,r),(a=t.call(this,e,n)).setAttribute("data-testid","videojs-track-scrubber-button"),a.addClass("vjs-button vjs-track-scrubber"),a.controlText("Toggle track scrubber"),a.el().innerHTML='\n \n \n ',a.options=n,a.player=e,a.playerInterval,a.zoomedOutRef=u.default.createRef(),a.currentTrackRef=u.default.createRef(),a.player.on("ready",(function(){a.options.trackScrubberRef.current&&(a.playerInterval=setInterval((function(){a.handleTimeUpdate()}),100),a.attachListeners())})),a.player.on("loadstart",(function(){a.options.trackScrubberRef.current&&(a.updateComponent(),a.playerInterval||(a.playerInterval=setInterval((function(){a.handleTimeUpdate()}),100)))})),a.player.on("fullscreenchange",(function(){if(a.player.isFullscreen()&&!a.zoomedOutRef.current){var e=a.zoomedOutRef.current;a.setZoomedOut(!e)}})),a.player.on("dispose",(function(){clearInterval(a.playerInterval)})),a}return En(r,[{key:"setCurrentTrack",value:function(e){this.currentTrackRef.current=e}},{key:"setZoomedOut",value:function(e){this.zoomedOutRef.current=e,e?(this.options.trackScrubberRef.current.classList.add("hidden"),this.el().innerHTML='\n \n \n '):(this.options.trackScrubberRef.current.classList.remove("hidden"),this.el().innerHTML='\n \n \n ')}},{key:"attachListeners",value:function(){var e=this,t=this.options.trackScrubberRef;if(this.updateComponent(),t.current){this.populateTrackScrubber(),this.updateTrackScrubberProgressBar();var r=!1,n=T(t.current.children,3);n[0];var a=n[1];n[2],a.addEventListener("mouseenter",(function(t){e.handleMouseMove(t)})),a.addEventListener("pointerup",(function(t){r&&e.handleSetProgress(t)})),a.addEventListener("pointermove",(function(t){e.handleMouseMove(t),r=!0})),a.addEventListener("pointerdown",(function(t){1===t.which&&(e.handleSetProgress(t),r=!1)}))}}},{key:"updateComponent",value:function(){this.zoomedOutRef.current=!0,this.currentTrackRef.current={}}},{key:"handleKeyDown",value:function(e){32!==e.which&&13!==e.which||(e.preventDefault(),this.handleTrackScrubberClick(),e.stopPropagation())}},{key:"handleClick",value:function(){this.handleTrackScrubberClick()}},{key:"handleTrackScrubberClick",value:function(){var e=this.currentTrackRef,t=this.player;if(this.options.trackScrubberRef.current&&e.current){t.isFullscreen()&&t.exitFullscreen();var r=this.zoomedOutRef.current;this.setZoomedOut(!r)}}},{key:"handleTimeUpdate",value:function(){var e,t=this.player,r=this.options,n=this.zoomedOutRef;if(t.canvasIsEmpty&&!n.current&&this.setZoomedOut(!0),!t.isDisposed()&&!t.ended()){var a,i,o=t.currentTime();if(t.markers&&"function"!=typeof t.markers&&"function"==typeof t.markers.getMarkers&&(null===(e=t.markers.getMarkers())||void 0===e?void 0:e.length)>0&&!r.isPlaylist)this.readPlayerMarkers();else this.setCurrentTrack({duration:null!==(a=t.playableDuration)&&void 0!==a?a:t.duration(),time:null!==(i=t.altStart)&&void 0!==i?i:0,key:"",text:"Complete media file"}),o=t.srcIndex&&t.srcIndex>0?o+t.altStart:o;this.updateTrackScrubberProgressBar(o)}}},{key:"updateTrackScrubberProgressBar",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,t=this.player,r=this.currentTrackRef;r.current||t.markers&&"function"==typeof t.markers.getMarkers&&this.readPlayerMarkers();var n=t.altStart,a=t.srcIndex>0?e-r.current.time+n:e-r.current.time,i=Math.min(100,Math.max(0,100*a/r.current.duration));this.populateTrackScrubber(a,i)}},{key:"populateTrackScrubber",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,r=this.options.trackScrubberRef;if(r.current){var n=T(r.current.children,3),a=n[0];n[1];var i=n[2];document.documentElement.style.setProperty("--range-scrubber","calc(".concat(t,"%)")),i.innerHTML=Tt(this.currentTrackRef.current.duration);var o=!isNaN(e)&&e>0?e:0;a.innerHTML=Tt(o)}}},{key:"readPlayerMarkers",value:function(){var e=this.player.markers.getMarkers().filter((function(e){return"ramp--track-marker--fragment"==e.class}));(null==e?void 0:e.length)>0&&this.setCurrentTrack(e[0])}},{key:"handleMouseMove",value:function(e){var t=this.options.timeToolRef;if(t.current){var r=this.getTrackTime(e);if(isFinite(r)){var n=e.offsetX-t.current.offsetWidth/2;t.current.style.left=n+"px",t.current.innerHTML=Tt(r)}}}},{key:"handleSetProgress",value:function(e){var t=this.currentTrackRef,r=this.player;if(t.current){var n=this.getTrackTime(e);if(null!=n){var a=Math.min(100,Math.max(0,n/t.current.duration*100));document.documentElement.style.setProperty("--range-scrubber","calc(".concat(a,"%)"));var i=r.isClipped?n+t.current.time:n;r.currentTime(i)}}}},{key:"getTrackTime",value:function(e){var t=this.currentTrackRef;if(t.current){var r=e.offsetX;if(r&&null!=r)return r/e.target.clientWidth*t.current.duration}}}]),r}();function oa(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!r){if(Array.isArray(e)||(r=function(e,t){if(!e)return;if("string"==typeof e)return la(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);"Object"===r&&e.constructor&&(r=e.constructor.name);if("Map"===r||"Set"===r)return Array.from(e);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return la(e,t)}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,a=function(){};return{s:a,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,o=!0,l=!1;return{s:function(){r=r.call(e)},n:function(){var e=r.next();return o=e.done,e},e:function(e){l=!0,i=e},f:function(){try{o||null==r.return||r.return()}finally{if(l)throw i}}}}function la(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r0){p.default.addLanguage(v.language,JSON.parse(m)),Ge(),p.default.log.level("off");var n=Te.current=p.default(Ee.current,v,(function(){We(Te.current)}));g({player:n,type:"updatePlayer"}),n.controlBar.getChild("PlayToggle").on("pointerdown",(function(){$e()})),n.on("pointerdown",(function(e){"video"==e.target.nodeName.toLowerCase()&&$e()}))}else if(Te.current&&(null===(t=v.sources)||void 0===t?void 0:t.length)>0){var a,i=Te.current;Re.current&&(null===(a=i.markers)||void 0===a||a.removeAll()),Ie(null),De.current?je(!0):(i.addClass("vjs-disabled"),je(!1),ze(i),Je(i),g({player:i,type:"updatePlayer"}))}}),[v.sources,Ee]),u.default.useEffect((function(){var e;(at(),Te.current)&&(De.current?(Te.current.audioOnlyMode(!1),Te.current.canvasIsEmpty=!0,Te.current.aspectRatio("16:9"),Te.current.controlBar.addClass("vjs-hidden"),Te.current.removeClass("vjs-disabled"),Te.current.pause(),Ie(null===(e=Ae.current)||void 0===e?void 0:e.id)):Te.current.controlBar.removeClass("vjs-hidden"));De.current&&!He.current&&(we(Et/1e3),nt())}),[Ne.current,De.current,Ae.current]),u.default.useEffect((function(){I?I&&!He.current&&P&&(we(Et/1e3),nt()):at()}),[I]),u.default.useEffect((function(){if(Te.current&&Te.current.markers&&qe.current){var e,t;"function"==typeof Te.current.markers&&Te.current.markers({markerTip:{display:!1,text:function(e){return e.text}},markerStyle:{},markers:[]});var r=[];if(null!=q&&null!==(e=q.markers)&&void 0!==e&&e.length)r=q.markers.filter((function(e){return e.canvasIndex===k}))[0].canvasMarkers.map((function(e){return{time:parseFloat(e.time),text:e.value,class:"ramp--track-marker--playlist"}}));null===(t=Te.current.markers)||void 0===t||t.removeAll(),Te.current.markers.add([].concat(D(ge?[ge]:[]),D(B),D(r)))}}),[ge,B,x,k,Te.current,qe.current]);var Ge=function(){(null==i?void 0:i.length)>0&&Ee.current&&i.map((function(e){var t=document.createElement("track");t.setAttribute("key",e.key),t.setAttribute("src",e.src),t.setAttribute("kind",e.kind),t.setAttribute("label",e.label),t.setAttribute("srclang",e.srclang),Ee.current.appendChild(t)}))},ze=function(e){e.duration(Me.current),e.src(v.sources),e.poster(v.poster),e.canvasIndex=Ne.current,e.canvasIsEmpty=De.current,e.srcIndex=C,e.targets=R,f&&(e.canvasLink=Pe.current);for(var r=e.remoteTextTracks(),o=r.length;o--;)e.removeRemoteTextTrack(r[o]);if((null==i?void 0:i.length)>0&&t&&i.forEach((function(t){e.addRemoteTextTrack(t,!1)})),null!=e.getChild("controlBar")&&!P){var c=e.getChild("controlBar"),u=c.children().findIndex((function(e){return"FullscreenToggle"==e.name_}));if(M||q.isPlaylist?c.getChild("videoJSTrackScrubber")||c.addChild("videoJSTrackScrubber",{trackScrubberRef:n,timeToolRef:a}):c.removeChild("videoJSTrackScrubber"),(null==i?void 0:i.length)>0&&t&&!c.getChild("subsCapsButton")){var d=an?c.children().findIndex((function(e){return"MuteToggle"==e.name_})):c.children().findIndex((function(e){return"VolumePanel"==e.name_}));c.addChild("subsCapsButton",{},d+1).children_[0].addClass("captions-on")}if(t?(e.audioOnlyMode(!1),e.removeClass("vjs-audio"),e.aspectRatio("16:9"),e.addChild("bigPlayButton")):(e.audioOnlyMode(!0),e.addClass("vjs-audio"),e.height(e.controlBar.height()),e.removeChild("bigPlayButton")),an?(c.removeChild("MuteToggle"),c.addChild("MuteToggle")):(c.removeChild("VolumePanel"),c.addChild("VolumePanel"),e.trigger("volumechange")),s){var p=c.children().findIndex((function(e){return"VideoJSFileDownload"==e.name_}))||u+1;if(c.removeChild("videoJSFileDownload"),(null==l?void 0:l.length)>0){var m={title:"Download Files",controlText:"Alternate resource download",files:l};c.addChild("videoJSFileDownload",function(e){for(var t=1;t0){for(var n=null,a=!1,i=0;i0){var r=Ve.current[Ne.current+1];if(r){b({canvasIndex:Ne.current+1,type:"switchCanvas"}),g({startTime:0,type:"setTimeFragment"}),g({currentTime:0,type:"setCurrentTime"});var n=Ue.current.filter((function(e){return e.canvasIndex===r.canvasIndex&&1===e.itemIndex})),a=null!=r.id?r:n[0],i=0;null!=a&&null!=a.id&&(i=Ot(a.id,Me.current).start),0===i?b({item:a,type:"switchItem"}):a.isEmpty&&(b({item:a,type:"switchItem"}),Te.current.currentTime(i),r.isEmpty||Te.current.play())}}}))}),[Ne.current]),Ke=u.default.useMemo((function(){return Zr((function(){var e=Te.current;if(null!==e&&qe.current){var t,n=null!==(t=e.currentTime())&&void 0!==t?t:Oe.current;S&&Ce.current>0&&(n+=R[Ce.current].altStart);var a=rt(n);if(Re.current!==(null==a?void 0:a.id))if(null===a)b({item:null,type:"switchItem"}),Ie(null),ye(null);else if(b({item:a,type:"switchItem"}),Ie(a.id),!r&&e.markers){var i=Ot(a.id,a.canvasDuration),o=i.start,l=i.end;if(g({endTime:l,startTime:o,type:"setTimeFragment"}),o!==l){var s=l>a.canvasDuration?a.canvasDuration:l;ye({time:o,duration:s-o,text:o,class:"ramp--track-marker--fragment"})}else ye(null)}else null!==ge&&ye(null)}}),10)}),[]),$e=function(){Le.current&&g({isPlaying:!1,type:"setPlayingStatus"})},et=null,tt=null,rt=function(e){var t=e;if(S&&(t+=R[C].altStart),q.isPlaylist)return Ue.current[Ne.current];var r,n=oa(Ue.current);try{for(n.s();!(r=n.n()).done;){var a=r.value,i=a.id,o=a.isCanvas;if(a.canvasIndex==Ne.current+1){if(o)return a;var l=Ot(i,x),s=Rt(l,x);if(t>=l.start&&t0?we(Math.ceil(r)):at()}),1e3)}}),[]),at=u.default.useCallback((function(){clearInterval(He.current),He.current=null}));return u.default.createElement("div",null,u.default.createElement("div",{"data-vjs-player":!0,"data-canvasindex":Ne.current},De.current&&u.default.createElement("div",{"data-testid":"inaccessible-message-display",style:{position:Te.current?"absolute":"relative",top:0,left:0,right:0,bottom:0,display:"flex",flexDirection:"column",justifyContent:"center",alignItems:"center",fontSize:"medium",color:"#fff",backgroundColor:"black",zIndex:101,aspectRatio:Te.current?"":"16/9",textAlign:"center"}},u.default.createElement("p",{className:"ramp--media-player_inaccessible-message-content","data-testid":"inaccessible-message-content",dangerouslySetInnerHTML:{__html:o}}),u.default.createElement("div",{className:"ramp--media-player_inaccessible-message-buttons"},k>=1&&u.default.createElement("button",{"aria-label":"Go back to previous item",onClick:function(){return c(k-1,!0)},"data-testid":"inaccessible-previous-button"},u.default.createElement(yn,{flip:!0})," Previous"),k!=d&&u.default.createElement("button",{"aria-label":"Go to next item",onClick:function(){return c(k+1,!0)},"data-testid":"inaccessible-next-button"},"Next ",u.default.createElement(yn,null))),k!=d&&u.default.createElement("p",{"data-testid":"inaccessible-message-timer",className:"ramp--media-player_inaccessible-message-timer ".concat(Se.current?"":"hidden")},"Next item in ".concat(ke," second").concat(1===ke?"":"s"))),u.default.createElement("video",{"data-testid":"videojs-".concat(t?"video":"audio","-element"),"data-canvasindex":Ne.current,ref:Ee,className:"video-js vjs-big-play-centered vjs-theme-ramp vjs-disabled ".concat(Kr?"is-mobile":""),onTouchStart:function(e){et=e.touches[0].clientX,tt=e.touches[0].clientY},onTouchEnd:function(e){e.changedTouches[0].clientX==et&&e.changedTouches[0].clientY==tt&&(F.paused()?F.play():F.pause())},style:{display:"".concat(De.current?"none":"")}})),(M||q.isPlaylist)&&u.default.createElement("div",{className:"vjs-track-scrubber-container hidden",ref:n,id:"track_scrubber"},u.default.createElement("p",{className:"vjs-time track-currenttime",role:"presentation"}),u.default.createElement("span",{type:"range","aria-label":"Track scrubber",role:"slider",tabIndex:0,className:"vjs-track-scrubber",style:{width:"100%"}},!on&&u.default.createElement("span",{className:"tooltiptext",ref:a,"aria-hidden":!0,role:"presentation"})),u.default.createElement("p",{className:"vjs-time track-duration",role:"presentation"})))}p.default.registerComponent("VideoJSTrackScrubber",ia),require("@silvermine/videojs-quality-selector")(p.default),ca.propTypes={isVideo:Tr.bool,hasMultipleCanvases:Tr.bool,isPlaylist:Tr.bool,trackScrubberRef:Tr.object,scrubberTooltipRef:Tr.object,tracks:Tr.array,placeholderText:Tr.string,renderingFiles:Tr.array,enableFileDownload:Tr.bool,cancelAutoAdvance:Tr.func,loadPrevOrNext:Tr.func,lastCanvasIndex:Tr.number,videoJSOptions:Tr.object};var ua={"Audio Player":"Audio Player","Video Player":"Video Player",Play:"Play",Pause:"Pause",Replay:"Replay","Current Time":"Current Time",Duration:"Duration","Remaining Time":"Remaining Time","Stream Type":"Stream Type",LIVE:"LIVE","Seek to live, currently behind live":"Seek to live, currently behind live","Seek to live, currently playing live":"Seek to live, currently playing live",Loaded:"Loaded",Progress:"Progress","Progress Bar":"Progress Bar","progress bar timing: currentTime={1} duration={2}":"{1} of {2}",Fullscreen:"Fullscreen","Exit Fullscreen":"Exit Fullscreen",Mute:"Mute",Unmute:"Unmute","Playback Rate":"Playback Rate",Subtitles:"Subtitles","subtitles off":"subtitles off",Captions:"Captions","captions off":"captions off",Chapters:"Chapters",Descriptions:"Descriptions","descriptions off":"descriptions off","Audio Track":"Audio Track","Volume Level":"Volume Level","You aborted the media playback":"You aborted the media playback","A network error caused the media download to fail part-way.":"A network error caused the media download to fail part-way.","The media could not be loaded, either because the server or network failed or because the format is not supported.":"The media could not be loaded, either because the server or network failed or because the format is not supported.","The media playback was aborted due to a corruption problem or because the media used features your browser did not support.":"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.","No compatible source was found for this media.":"No compatible source was found for this media.","The media is encrypted and we do not have the keys to decrypt it.":"The media is encrypted and we do not have the keys to decrypt it.","Play Video":"Play Video",Close:"Close","Close Modal Dialog":"Close Modal Dialog","Modal Window":"Modal Window","This is a modal window":"This is a modal window","This modal can be closed by pressing the Escape key or activating the close button.":"This modal can be closed by pressing the Escape key or activating the close button.",", opens captions settings dialog":", opens captions settings dialog",", opens subtitles settings dialog":", opens subtitles settings dialog",", opens descriptions settings dialog":", opens descriptions settings dialog",", selected":", selected","captions settings":"captions settings","subtitles settings":"subtitles settings","descriptions settings":"descriptions settings",Text:"Text",White:"White",Black:"Black",Red:"Red",Green:"Green",Blue:"Blue",Yellow:"Yellow",Magenta:"Magenta",Cyan:"Cyan",Background:"Background",Window:"Window",Transparent:"Transparent","Semi-Transparent":"Semi-Transparent",Opaque:"Opaque","Font Size":"Font Size","Text Edge Style":"Text Edge Style",None:"None",Raised:"Raised",Depressed:"Depressed",Uniform:"Uniform","Drop shadow":"Drop shadow","Font Family":"Font Family","Proportional Sans-Serif":"Proportional Sans-Serif","Monospace Sans-Serif":"Monospace Sans-Serif","Proportional Serif":"Proportional Serif","Monospace Serif":"Monospace Serif",Casual:"Casual",Script:"Script","Small Caps":"Small Caps",Reset:"Reset","restore all settings to the default values":"restore all settings to the default values",Done:"Done","Caption Settings Dialog":"Caption Settings Dialog","Beginning of dialog window. Escape will cancel and close the window.":"Beginning of dialog window. Escape will cancel and close the window.","End of dialog window.":"End of dialog window.","{1} is loading.":"{1} is loading.","Exit Picture-in-Picture":"Exit Picture-in-Picture","Picture-in-Picture":"Picture-in-Picture","No content":"No content",Color:"Color",Opacity:"Opacity","Text Background":"Text Background","Caption Area Background":"Caption Area Background","Playing in Picture-in-Picture":"Playing in Picture-in-Picture","Skip backward {1} seconds":"Skip backward {1} seconds","Skip forward {1} seconds":"Skip forward {1} seconds"};function da(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function fa(e){for(var t=1;t0),Y(e||0)}catch(e){x(e)}return function(){D(!1),_(0),y({player:null,type:"updatePlayer"})}}),[ue,se,fe]),u.default.useEffect((function(){de&&(!ve&&Re.current?Ae():je())}),[we.current]);var Oe=function(e,t){Ae();try{var n=Gt({manifest:ue,canvasIndex:e,startTime:e===ge.startIndex&&I?ge.startTime:0,srcIndex:fe,isPlaylist:me.isPlaylist}),a=n.isMultiSource,i=n.sources,o=n.tracks,l=n.canvasTargets,s=n.mediaType,c=n.error,u=n.poster;$("video"===s),b({canvasTargets:l,type:"canvasTargets"}),b({isMultiSource:a,type:"hasMultipleItems"}),t&&((null==l?void 0:l.length)>0?y({currentTime:l[0].altStart,type:"setCurrentTime"}):y({currentTime:0,type:"setCurrentTime"})),S(fa(fa({},E),{},{error:c,sources:i,tracks:o,poster:u}));var d,f=ce.find((function(t){return t.canvasIndex===e}));if(f.isEmpty)y({type:"updatePlayer"}),b({type:"setCanvasIsEmpty",isEmpty:!0}),S(fa(fa({},E),{},{error:u})),we.current&&je();else{var p=[ue.label?Object.values(ue.label)[0][0]:"",f.label].filter(Boolean).join(" - ");b({canvasDuration:f.duration,type:"canvasDuration"}),b({canvasLink:{label:p,id:f.canvasId},type:"canvasLink"}),b({type:"setCanvasIsEmpty",isEmpty:!1})}if(U(a||!1),_(e),r&&ye!={})le(ye.manifest.concat(null===(d=ye.canvas[e])||void 0===d?void 0:d.files));D(!c)}catch(e){x(e)}},qe=function(e,t){y({currentTime:t,type:"setCurrentTime"}),b({srcIndex:e,type:"setSrcIndex"})},je=function(){Re.current=setTimeout((function(){ke.current2&&void 0!==arguments[2]?arguments[2]:"";null!=e&&e>-1&&ke.current!=e&&e<=Ee.current&&(b({canvasIndex:e,type:"switchCanvas"}),Oe(e,t),y({element:r,type:"setPlayerFocusElement"}))};return u.default.useEffect((function(){var e,t=p?{hls:{withCredentials:!0}}:{};I&&A&&!de?(e=de?{sources:[]}:{aspectRatio:K?"16:9":"1:0",audioOnlyMode:!K,autoplay:!1,bigPlayButton:K,id:"iiif-media-player",playbackRates:l?[.5,.75,1,1.5,2]:[],experimentalSvgIcons:!0,inactivityTimeout:an||on?0:2e3,poster:K?E.poster:null,controls:!0,fluid:!0,language:v,controlBar:{children:[G?"videoJSPreviousButton":"","playToggle",G?"videoJSNextButton":"","videoJSProgress","videoJSCurrentTime","timeDivider","durationDisplay","fullscreenToggle",r?"videoJSFileDownload":"",i?"pictureInPictureToggle":"",l?"playbackRateMenuButton":"","qualitySelector",he||me.isPlaylist?"videoJSTrackScrubber":"",E.tracks.length>0&&K?"subsCapsButton":"",an?"muteToggle":"volumePanel"],videoJSProgress:{srcIndex:fe,targets:pe,currentTime:xe||0,nextItemClicked:qe},videoJSCurrentTime:{srcIndex:fe,targets:pe,currentTime:xe||0}},sources:B?[E.sources[fe]]:E.sources,html5:fa(fa({},t),{},{nativeTextTracks:an&&!Kr}),errorDisplay:{uncloseable:!1},userActions:{hotkeys:rn?void 0:function(e){_t(e,this)}},videoJSTitleLink:d},r&&!de&&(e.controlBar.videoJSFileDownload={title:"Download Files",controlText:"Alternate resource download",files:oe}),G&&!de&&(e.controlBar.videoJSPreviousButton={canvasIndex:se,switchPlayer:De,playerFocusElement:be},e.controlBar.videoJSNextButton={canvasIndex:se,lastCanvasIndex:Ee.current,switchPlayer:De,playerFocusElement:be}),!he&&!me.isPlaylist||de||(e.controlBar.videoJSTrackScrubber={trackScrubberRef:Te,timeToolRef:Se,isPlaylist:me.isPlaylist}),O(!1)):e={sources:B?[E.sources[fe]]:E.sources,poster:K?E.poster:null},ne(e)}),[A,L,fe,de,xe]),A&&null!=re||de?u.default.createElement("div",{"data-testid":"media-player",className:"ramp--media_player",role:"presentation"},u.default.createElement(ca,{isVideo:K,hasMultipleCanvases:G,isPlaylist:me.isPlaylist,trackScrubberRef:Te,scrubberTooltipRef:Se,tracks:E.tracks,placeholderText:E.error,renderingFiles:oe,enableFileDownload:r,loadPrevOrNext:De,lastCanvasIndex:Z,enableTitleLink:d,videoJSLangMap:Ce.current,options:re})):null};pa.propTypes={enableFileDownload:Tr.bool,enablePIP:Tr.bool,enablePlaybackRate:Tr.bool,enableTitleLink:Tr.bool,withCredentials:Tr.bool,language:Tr.string};var ma=y((function(e){function t(){return e.exports=t=Object.assign?Object.assign.bind():function(e){for(var t=1;t0?u.default.createElement(ya,{items:f,sectionRef:h,structureContainerRef:g}):null,R=u.default.useRef(null),I=u.default.useCallback((function(e){e.preventDefault(),e.stopPropagation();var t=Ot(E.current,v);Rt({start:t.start,end:t.end},{end:v})&&(y({clickedUrl:E.current,type:"navClick"}),R.current.isClicked=!0,h.current&&(h.current.isClicked=!0))}));u.default.useEffect((function(){!R.current||(null==k?void 0:k.id)!=E.current||null==R.current.isClicked||R.current.isClicked||null==g.current.isScrolling||g.current.isScrolling||Lt(R.current,g),R.current&&(R.current.isClicked=!1)}),[k]);return""!=l?u.default.createElement("li",{"data-testid":"list-item",ref:R,className:"ramp--structured-nav__list-item"+"".concat(null==E.current||(null==k?void 0:k.id)!==E.current||!w&&a||(null==k?void 0:k.canvasIndex)!==x+1?"":" active"),"data-label":T.current,"data-summary":S.current},u.default.createElement(u.default.Fragment,{key:m},a&&!w?u.default.createElement(u.default.Fragment,null,u.default.createElement(ha,{itemIndex:p,canvasIndex:x,duration:t,label:l,sectionRef:h,itemId:E.current,isRoot:d,handleClick:I,structureContainerRef:g})):u.default.createElement(u.default.Fragment,null,n?u.default.createElement("span",{className:"ramp--structured-nav__item-title",role:"listitem","aria-label":T.current},T.current):u.default.createElement(u.default.Fragment,{key:r},u.default.createElement("div",{className:"tracker"}),i?u.default.createElement(u.default.Fragment,null,o&&u.default.createElement(bn,null),u.default.createElement("a",{role:"listitem",href:c&&""!=c?c:E.current,onClick:I},"".concat(p,". "),T.current," ",t.length>0?" (".concat(t,")"):"")):u.default.createElement("span",{role:"listitem","aria-label":T.current},T.current)))),C):null};ga.propTypes={duration:Tr.string.isRequired,id:Tr.string,isTitle:Tr.bool.isRequired,isCanvas:Tr.bool.isRequired,isClickable:Tr.bool.isRequired,isEmpty:Tr.bool.isRequired,label:Tr.string.isRequired,summary:Tr.string,homepage:Tr.string,isRoot:Tr.bool,items:Tr.array.isRequired,itemIndex:Tr.number,rangeId:Tr.string.isRequired,canvasDuration:Tr.number.isRequired,sectionRef:Tr.object.isRequired,structureContainerRef:Tr.object.isRequired};var ya=function(e){var t=e.items,r=e.sectionRef,n=e.structureContainerRef,a=u.default.createElement("ul",{"data-testid":"list",className:"ramp--structured-nav__list",role:"presentation"},t.map((function(e,t){if(e)return u.default.createElement(ga,va({},e,{sectionRef:r,key:t,structureContainerRef:n}))})));return u.default.createElement(u.default.Fragment,null,a)};function ba(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!r){if(Array.isArray(e)||(r=function(e,t){if(!e)return;if("string"==typeof e)return xa(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);"Object"===r&&e.constructor&&(r=e.constructor.name);if("Map"===r||"Set"===r)return Array.from(e);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return xa(e,t)}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,a=function(){};return{s:a,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,o=!0,l=!1;return{s:function(){r=r.call(e)},n:function(){var e=r.next();return o=e.done,e},e:function(e){l=!0,i=e},f:function(){try{o||null==r.return||r.return()}finally{if(l)throw i}}}}function xa(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r2&&void 0!==arguments[2]&&arguments[2],a=[],i=0,o=!1,l=0,s=0,c=function e(t,n){if("no-nav"!=t.getBehavior()){var c,u,d=Mt(t.getLabel().getValue()),f=t.getCanvasIds(),p=i,m=i,v=n==t&&0==l,h=!1,g=!1,y=void 0,b=void 0;o?(u=v||r.length>1&&n==t.parentRange,r.length>1&&n==t.parentRange?l+=1:1==r.length&&(l=1)):u=n==t.parentRange&&null!=r[l-1];var x=t.getDuration();if(null!=x&&!v){var k=x.start;p=x.end-k,u&&(m=p)}if(f.length>0&&(null==r?void 0:r.length)>0){var w=r.filter((function(e){return e.canvasId===zt(f[0])}))[0];if(g=w.isEmpty,y=w.summary,b=w.homepage,h=!0,null!=w.range){var E=w.range,T=E.start,S=E.end;m=S-T,u&&(p=S-T)}}var C=O({label:d,summary:y,isRoot:v,homepage:b,canvasDuration:m,isTitle:0===f.length,rangeId:t.id,id:f.length>0?u?"".concat(f[0].split(",")[0],","):f[0]:void 0,isEmpty:g,isCanvas:u,itemIndex:u?l:void 0,canvasIndex:l,items:(null===(c=t.getRanges())||void 0===c?void 0:c.length)>0?t.getRanges().map((function(t){return e(t,n)})):[],duration:Tt(p),isClickable:h},"homepage",b);return f.length>0&&(s++,u||(C.itemIndex=s),a.push(C)),C}};try{var u=t.parseManifest(e).getAllRanges();if(0===(null==u?void 0:u.length))return{structures:[],timespans:[],markRoot:!1};var d=u[0],f=[],p=d.getBehavior();if(p&&"no-nav"==p)return{structures:[],timespans:[]};if(n||"top"===p){var m=d.getRanges();(null==m?void 0:m.length)>0&&m.map((function(e,t){"no-nav"!=e.getBehavior()&&(s=0,l=t+1,f.push(c(e,d)))}))}else o=!0,i=r.reduce((function(e,t){return e+t.range.end}),0),f.push(c(d,d));var v=o&&(null==r?void 0:r.length)>1;return{structures:f,timespans:a,markRoot:v}}catch(e){throw console.error("iiif-parser -> getStructureRanges() -> error parsing structures"),new Error(bt)}}(g,f,y.isPlaylist),n=e.structures,a=e.timespans,i=e.markRoot;E.current=n,w.current=n,S.current=i,(null==n?void 0:n.length)>0&&n[0].isRoot&&(w.current=n[0].items),r({structures:w.current,type:"setStructures"}),r({timespans:a,type:"setCanvasSegments"}),C.current.isScrolling=!1}catch(e){k(e)}}),[g]),u.default.useEffect((function(){b&&y.isPlaylist&&r({item:x[m],type:"switchItem"})}),[b,m]),u.default.useEffect((function(){if(l){var e=x.filter((function(e){return e.id===o}));if((null==e?void 0:e.length)>0){var t=e[0],a=t.isCanvas,i=t.items;(!a||0==i.length&&a)&&r({item:e[0],type:"switchItem"})}var u=f.findIndex((function(e){return e.canvasURL===zt(o)})),d=Ot(o,p);if(!d||null==d)return void console.error("StructuredNavigation -> invalid media fragment in structure item -> ",d);var g=d.start;if(v){var y=function(e,t,r){var n,a;return e.map((function(i,o){var l=o>0?e[o].altStart:0;isNaN(c)&&(c=r);var s=i.start,c=i.end,u=l+s,d=l+c;t.start>=u&&t.start-1&&(r({canvasIndex:u,type:"switchCanvas"}),T.current=w.current[u].isEmpty);c&&!T.current?(c.currentTime(g),n({startTime:d.start,endTime:d.end,type:"setTimeFragment"}),c.structStart=g,n({currentTime:g,type:"setCurrentTime"}),s&&c.userActive(!0)):T.current&&n({type:"resetClick"})}}),[l,c]),u.default.useEffect((function(){if(C.current){var e=C.current,t=C.current.parentElement,r=Math.abs(e.scrollHeight-(e.scrollTop+e.clientHeight))<=1;R.current=!r,t&&q.observe(t)}}),[c]);var I=function(e){var t=e.target;t.classList.contains("ramp--structured-nav__border")&&(t=t.firstChild);var r=t.nextSibling,n=Math.abs(t.scrollHeight-(t.scrollTop+t.clientHeight))<=1;t&&n&&t.classList.contains("scrollable")?t.classList.remove("scrollable"):!t||n||t.classList.contains("scrollable")||t.classList.add("scrollable"),r&&n&&r.classList.contains("scrollable")?r.classList.remove("scrollable"):!r||n||r.classList.contains("scrollable")||r.classList.add("scrollable")},q=new ResizeObserver((function(e){var t,r=ba(e);try{for(r.s();!(t=r.n()).done;){var n=t.value;I(n)}}catch(e){r.e(e)}finally{r.f()}}));if(!g)return u.default.createElement("p",null,"No manifest - Please provide a valid manifest.");var j="",A="";R.current?(j="ramp--structured-nav scrollable",A="scrollable"):j="ramp--structured-nav",null!=y&&y.isPlaylist&&(j+=" playlist-items"),j+=S.current?" ramp--structured-nav-with_root":"";var D=function(e){C.current.isScrolling=e};return u.default.createElement("div",{className:"ramp--structured-nav__border"},u.default.createElement("div",{"data-testid":"structured-nav",className:j,ref:C,role:"list","aria-label":"Structural content",onScroll:I,onMouseLeave:function(){return D(!1)},onMouseOver:function(){return D(!0)}},(null===(e=E.current)||void 0===e?void 0:e.length)>0?E.current.map((function(e,t){return u.default.createElement(ya,{items:[e],sectionRef:u.default.createRef(),key:t,structureContainerRef:C})})):u.default.createElement("p",{className:"ramp--no-structure"},"There are no structures in the manifest")),u.default.createElement("span",{className:A},"Scroll to see more"))};ka.propTypes={};var wa,Ea,Ta,Sa,Ca=y((function(e){e.exports=function(e,t){if(null==e)return{};var r,n,a={},i=Object.keys(e);for(n=0;n=0||(a[r]=e[r]);return a},e.exports.__esModule=!0,e.exports.default=e.exports})),Ra=g(y((function(e){e.exports=function(e,t){if(null==e)return{};var r,n,a=Ca(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a},e.exports.__esModule=!0,e.exports.default=e.exports}))),Ia=g(y((function(e){e.exports=function(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))},e.exports.__esModule=!0,e.exports.default=e.exports})));function Oa(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!r){if(Array.isArray(e)||(r=function(e,t){if(!e)return;if("string"==typeof e)return qa(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);"Object"===r&&e.constructor&&(r=e.constructor.name);if("Map"===r||"Set"===r)return Array.from(e);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return qa(e,t)}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,a=function(){};return{s:a,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,o=!0,l=!1;return{s:function(){r=r.call(e)},n:function(){var e=r.next();return o=e.done,e},e:function(e){l=!0,i=e},f:function(){try{o||null==r.return||r.return()}finally{if(l)throw i}}}}function qa(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r1&&void 0!==a[1]?a[1]:"",e.next=3,fetch(t).then((function(e){return e.headers.get("Content-Type").includes("application/json")?e.json():{}})).then((function(e){var n=e.items,a=[];return(null==n?void 0:n.length)>0&&n.map((function(e,n){var i=qt(e.annotations,"supplementing"),o=[];if(i.length>0){var l=i[0].body;if("TextualBody"===l.type){var s=At(r.length>0?r:l.label?Mt(l.label):"Canvas-".concat(n)),c=s.isMachineGen,u=s.labelText;o.push({url:void 0===l.id?t:l.id,title:u,isMachineGen:c,id:"".concat(u,"-").concat(n),format:""})}else i.forEach((function(e,t){var r=e.body,a="",i="";if(r.label&&Object.keys(r.label).length>0){var l=Object.keys(r.label);(null==l?void 0:l.length)>1?(a=Mt(r.label),i=r.label.hasOwnProperty("none")?Mt(r.label.none[0]):a):a=Mt(r.label)}else a="".concat(t);var s=r.id,c=Dt(s),u=At(a),d=u.isMachineGen,f=u.labelText;""===i&&(i=f),1!==c&&3!==c||o.push({title:f,filename:i,url:s,isMachineGen:d,id:"".concat(f,"-").concat(n,"-").concat(t),format:r.format||""})}))}a.push({canvasId:n,items:o})})),a})).catch((function(e){return console.error("transcript-parser -> readSupplementingAnnotations() -> error fetching transcript resource at, ",t),[]}));case 3:return n=e.sent,e.abrupt("return",n);case 5:case"end":return e.stop()}}),e)}))),Na.apply(this,arguments)}function Fa(e){return Ba.apply(this,arguments)}function Ba(){return Ba=gr(xr.mark((function e(t){var r,n,a;return xr.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t&&null!=t&&0!=t.length){e.next=5;break}return console.error("No transcripts given as input"),e.abrupt("return",[]);case 5:return r=[],t.map((function(e){return r.push({canvasId:e.canvasId,items:[]})})),e.next=9,Promise.all(t.map(function(){var e=gr(xr.mark((function e(t){var n,a,i;return xr.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=t.canvasId,a=t.items,e.next=3,Promise.all(a.map(function(){var e=gr(xr.mark((function e(t,a){var i,o,l,s,c,u,d,f;return xr.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return i=t.title,o=t.url,e.next=3,_a(o,i);case 3:if(l=e.sent,s=At(i),c=s.isMachineGen,u=s.labelText,d=[],(null==l?void 0:l.length)>0&&(d=l.map((function(e){return e.items})).flat(),f=Ua(r.concat(l),"canvasId","items"),r=f),0!==l.length&&0!==d.length){e.next=11;break}return e.abrupt("return",{title:u,filename:u,url:o,isMachineGen:c,id:"".concat(u,"-").concat(n,"-").concat(a),format:""});case 11:return e.abrupt("return",null);case 12:case"end":return e.stop()}}),e)})));return function(t,r){return e.apply(this,arguments)}}()));case 3:return i=e.sent,e.abrupt("return",{canvasId:n,items:i.filter((function(e){return null!=e}))});case 5:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}()));case 9:return n=e.sent,a=Ua(r.concat(n),"canvasId","items"),e.abrupt("return",a);case 12:case"end":return e.stop()}}),e)}))),Ba.apply(this,arguments)}function Ua(e,t,r){return e.reduce((function(e,n){var a=e.filter((function(e){return e[t]==n[t]}));if((null==a?void 0:a.length)>0){var i=a[0];i[r]=i[r].concat(n[r])}else e.push(n);return e}),[])}function Va(e,t,r){return Ha.apply(this,arguments)}function Ha(){return(Ha=gr(xr.mark((function e(t,r,n){var a,i,o,l,s,c,u,d,f,p,m,v,h,g,y,b,x;return xr.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(a=[],i=t,void 0!==t){e.next=4;break}return e.abrupt("return",{tData:a,tUrl:i,tType:Pa.invalid});case 4:return o=null,l=null,e.next=8,fetch(t).then(Ct).then((function(e){o=e.headers.get("Content-Type"),l=e})).catch((function(e){console.error("transcript-parser -> parseTranscriptData() -> fetching transcript -> ",e)}));case 8:if(null!=o){e.next=10;break}return e.abrupt("return",{tData:[],tUrl:i,tType:Pa.invalid});case 10:if(s=Ma.filter((function(e){return e.type.includes(o.split(";")[0])})),c=Ma.filter((function(e){return e.type.includes(n)})),u="",(null==c?void 0:c.length)>0?u=c[0].ext:s.length>0?u=s[0].ext:(d=t.split(".").reverse()[0],f=Ma.filter((function(e){return e.ext===d})),u=f.length>0?d:""),void 0!==r){e.next=16;break}return e.abrupt("return",{tData:a,tUrl:i,tType:Pa.noTranscript});case 16:e.t0=u,e.next="json"===e.t0?19:"txt"===e.t0?28:"srt"===e.t0||"vtt"===e.t0?39:"docx"===e.t0?49:53;break;case 19:return e.next=21,l.json();case 21:if("Manifest"!==(null==(v=e.sent)?void 0:v.type)){e.next=26;break}return e.abrupt("return",Wa(v,t,r));case 26:return h=Ja(v),e.abrupt("return",{tData:h.tData,tUrl:i,tType:h.tType,tFileExt:u});case 28:return e.next=30,l.text();case 30:if(p=e.sent,0!=(m=p.split("\n")).length){e.next=36;break}return e.abrupt("return",{tData:[],tUrl:t,tType:Pa.noTranscript});case 36:return g=oi(m),e.abrupt("return",{tData:g,tUrl:t,tType:Pa.plainText,tFileExt:u});case 38:case 39:return e.next=41,l.text();case 41:if(p=e.sent,0!=(m=p.split("\n")).length){e.next=47;break}return e.abrupt("return",{tData:[],tUrl:t,tType:Pa.noTranscript});case 47:return y=Qa(p,"srt"===u),b=y.tData,x=y.tType,e.abrupt("return",{tData:b,tUrl:t,tType:x,tFileExt:u});case 49:return e.next=51,Ga(l);case 51:return a=e.sent,e.abrupt("return",{tData:ii(a),tUrl:t,tType:Pa.docx,tFileExt:u});case 53:return e.abrupt("return",{tData:[],tUrl:t,tType:Pa.noSupport});case 54:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Ga(e){return za.apply(this,arguments)}function za(){return za=gr(xr.mark((function e(t){var r,n,a;return xr.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=null,e.next=3,t.blob();case 3:return n=e.sent,a=new File([n],name,{type:t.headers.get("content-type")}),e.next=7,v.default.convertToHtml({arrayBuffer:a}).then((function(e){r=e.value})).catch((function(e){console.error(e)}));case 7:return e.abrupt("return",r);case 8:case"end":return e.stop()}}),e)}))),za.apply(this,arguments)}function Ja(e){if(0==e.length)return{tData:[],tType:Pa.noTranscript};var t,r=[],n=Oa(e);try{for(n.s();!(t=n.n()).done;){var a=t.value;if(a.speaker){var i,o=a.speaker,l=Oa(a.spans);try{for(l.s();!(i=l.n()).done;){var s=i.value;s.speaker=o,r.push(s)}}catch(e){l.e(e)}finally{l.f()}}else{var c,u=Oa(a.spans);try{for(u.s();!(c=u.n()).done;){var d=c.value;d.format="text/plain",d.tag=La.timedCue,r.push(d)}}catch(e){u.e(e)}finally{u.f()}}}}catch(e){n.e(e)}finally{n.f()}return{tData:r,tType:Pa.timedText}}function Wa(e,t,r){var n,a=t,i=[];if(e.annotations)i=qt(e.annotations,"supplementing");else if((null===(n=e.items)||void 0===n?void 0:n.length)>0){var o;i=qt(null===(o=e.items[r])||void 0===o?void 0:o.annotations,"supplementing")}return i.length>0?"TextualBody"!=i[0].body.type?function(e){return Za.apply(this,arguments)}(i[0]):{tData:Ya(i),tUrl:a,tType:Pa.timedText,tFileExt:"json"}:{tData:[],tUrl:a,tType:Pa.noTranscript}}function Za(){return(Za=gr(xr.mark((function e(t){var r,n,a,i,o,l,s;return xr.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(r=[],n="",a=t.body,i=a.id,o=a.type,l=a.format,s="","Text"!==o){e.next=12;break}return e.next=10,fetch(i).then(Ct).then((function(e){return e.text()})).then((function(e){if(ja.webvtt.includes(l)||ja.srt.includes(l)){var t=Qa(e,ja.srt.includes(l));r=t.tData,n=t.tType,s=Ma.filter((function(e){return e.type.includes(l)}))[0].ext}else{var a=e.split("\n");r=oi(a),n=Pa.plainText,s="txt"}})).catch((function(e){throw console.error("transcript-parser -> parseExternalAnnotations() -> fetching external transcript -> ",e),e}));case 10:e.next=15;break;case 12:if("AnnotationPage"!==o){e.next=15;break}return e.next=15,fetch(i).then(Ct).then((function(e){return e.json()})).then((function(e){var t=qt([e],"supplementing");r=Ya(t),n=Pa.timedText,s="json"})).catch((function(e){throw console.error("transcript-parser -> parseExternalAnnotations() -> fetching annotations -> ",e),e}));case 15:return e.abrupt("return",{tData:r,tUrl:i,tType:n,tFileExt:s});case 16:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Ya(e){var t=[];return e.map((function(e){if(null!=e.id){var r=e.body,n=Ot(e.target),a=n.start,i=n.end;t.push({text:r.value,format:r.format,begin:parseFloat(a),end:parseFloat(i),tag:La.timedCue})}})),t}function Qa(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],r=[],n=[],a=e.split("\n"),i=a;if(!t){var o=function(e){var t=e.shift().trim();if(6==(null==t?void 0:t.length)&&"WEBVTT"===t){var r=function(e){var t=0,r=0,n=!1,a=[];e=e.filter((function(e){return!Number(e)}));for(var i=0;i").concat(e[i].trim()),i++;a.push({times:"",line:l,tag:La.note})}else{if(o.includes("--\x3e")){r=i;break}"string"==typeof o&&0!=o.trim().length&&(n=!0)}}return r>t&&!n?{valid:!0,cue_lines:e.slice(r),notes:a}:{valid:!1}}(e);return{valid:r.valid,cue_lines:r.cue_lines,notes:r.notes}}return{valid:!1,cue_lines:[],notes:[]}}(a),l=o.valid,s=o.cue_lines,c=o.notes;if(!l)return console.error("Invalid WebVTT file"),{tData:[],tType:Pa.invalidVTT};i=s,n=c}var u=function(e){var t,r=[];for(t=0;t0&&e.items.map((function(e){var n=new t.Annotation(e);if("supplementing"==n.getMotivation()){var a=n.getTarget(),i=zt(a),o=n.getBody()[0].getProperty("value"),s=ai(o,r,!0);l.push({target:a,targetURI:i,value:o,hitCount:s})}}));for(var s=function(e,t){return e.reduce((function(e,r){return(e[r[t]]=e[r[t]]||[]).push(r),e}),{})}(l,"targetURI"),c=0,u=Object.entries(s);c]+>/gi,""),d=0,f=0,p=void 0;if(null!=c){d=c.start,f=c.end,p=r.findIndex((function(e){return e.begin==d&&e.end==f}));var m=n.match(/[a-zA-Z]+/gi)?n.match(/[a-zA-Z]+/gi)[0]:n;if(-1!==u.toLocaleLowerCase().indexOf(m)&&null!=p){var v=ti(s,n,t.hitCount,!0);a.push({tag:La.timedCue,begin:d,end:f,id:p,match:v,matchCount:t.hitCount,text:s})}}else{var h=ei(r,u,n,i);for(i=h.traversedIds,a=[].concat(D(a),D(h.hits));o===e.length-1&&(null===(g=i)||void 0===g?void 0:g.length)]+>/gi,"").trim(),u=D(c.matchAll(i)),d=t.trim();if(d==c||d.includes(c)&&(null==u?void 0:u.length)>0){s.matchCount=null==u?void 0:u.length,o.push(s),n.push(s.id);break}if((null==u?void 0:u.length)>0){var f;s.matchCount=null===(f=D(d.matchAll(i)))||void 0===f?void 0:f.length,o.push(s),n.push(s.id);break}n.push(s.id)}var p=[];return o.map((function(e){var t=ri(e.textDisplayed,r),n=ti(t,r,e.matchCount,!0);p.push({tag:La.nonTimedLine,begin:void 0,end:void 0,id:e.id,match:n,matchCount:e.matchCount,text:t})})),{hits:p,traversedIds:n}},ti=function(e,t,r){if(void 0!==e&&e){var n=0,a=t;arguments.length>3&&void 0!==arguments[3]&&arguments[3]&&(a=ni(t));try{var i,o=new RegExp(String.raw(Ea||(Ea=Ia(["",""])),a),"gi");return 0===(null===(i=D(e.matchAll(o)))||void 0===i?void 0:i.length)?function(){var a=D(e.matchAll(/<\/?[^>]+>/gi));if(0!==(null==a?void 0:a.length)){for(var i=0,o="",l=0;l0?2*(null==s?void 0:s.length)-1:1;if(void 0===a[l]&&void 0===a[l+c])return;var u=a[l].index,d=a[l+c].index+a[l+c][0].length,f=e.slice(i,u),p=e.slice(u,d).replace(/<\/?[^>]+>/gi,"");o="".concat(o).concat(f,'').concat(p,""),i=d,n++,(l=+(c+1))==a.length&&(o="".concat(o).concat(e.slice(i)))}return o}}():e.replace(o,(function(e){var t=e.replace(/<\/?[^>]+>/gi,"");return n'.concat(t,"")):t}))}catch(e){console.log("Error building RegExp for query: ",t)}}},ri=function(e,t){if(void 0!==e&&e){var r=new RegExp(String.raw(Ta||(Ta=Ia(["\b","\b"],["\\b","\\b"])),ni(t,!0,!1)),"gi"),n=e.replace(r,(function(e){return ni(e,!1,!0)}));return n}},ni=function(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],r=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],n=D(e.matchAll(/[a-zA-Z']+/gi)),a=D(e.matchAll(/([.+?"^${}\-|[\]\\])/g));if(0===(null==a?void 0:a.length)){var i=r?e.split(" ").map((function(e){return"".concat(e,"")})).join(" "):e;return t?"".concat(i,"(?!['w*])"):i}for(var o="",l=0,s=0;s".concat(c[0],"
    "):c[0],d="(".concat(e.slice(l,c.index),0===l?")*":")+");o=t?"".concat(o).concat(d,"(").concat(u,")"):"".concat(o).concat(e.slice(l,c.index)).concat(u),l=c.index+c[0].length,s===(null==n?void 0:n.length)-1&&(o=t?"".concat(o,"(").concat(e.slice(l),")*"):"".concat(o).concat(e.slice(l))),s++}return t?o.replace(/([.?^${}|[\]\\])/g,"\\$1"):o},ai=function(e,t){var r,n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],a=t.split(/[\s.,!?;:]/)[0],i=a.replace(/[\[\]\-]/gi,""),o=n?ni(a):i,l=new RegExp(String.raw(Sa||(Sa=Ia(["",""])),o),"gi");return null===(r=D(e.matchAll(l)))||void 0===r?void 0:r.length},ii=function(e){var t=document.createElement("div");return t.innerHTML=e,oi(Array.from(t.childNodes),!0)},oi=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],r=[];return e.map((function(e){r.push({text:t?e.innerText:e,tag:La.nonTimedLine,textDisplayed:t?B.decode(e.innerHTML):e})})),r},li=function(e){var t=e.fileUrl,r=e.fileName,n=e.machineGenerated,a=e.fileExt;return u.default.createElement("button",{className:"ramp--transcript_menu_button ramp--transcript_downloader","data-testid":"transcript-downloader",onClick:function(e){e.preventDefault(),It(t,r,a,n)},href:"#","aria-label":"Transcript download button"},u.default.createElement(kn,null))};li.propTypes={fileUrl:Tr.string,fileName:Tr.string,machineGenerated:Tr.bool,fileExt:Tr.string};var si=function(e){var t=e.selectTranscript,r=e.transcriptData,n=e.transcriptInfo,a=e.noTranscript,i=n.filename,o=n.id,l=n.tUrl,s=n.tFileExt,c=n.isMachineGen;return r?[u.default.createElement("div",{key:"transcript-selector","data-testid":"transcript-selector",className:"ramp--transcript_selector"},u.default.createElement("select",{"data-testid":"transcript-select-option",value:o||"",onChange:function(e){t(e.target.value)},"aria-label":"Select transcripts","aria-expanded":!1,"aria-haspopup":"true"},r.map((function(e,t){return u.default.createElement("option",{value:e.id,label:"".concat(e.title).concat(e.numberOfHits?" ("+e.numberOfHits+")":""),key:t},"".concat(e.title).concat(e.numberOfHits?" ("+e.numberOfHits+")":""))}))),!a&&u.default.createElement(li,{key:"transcript-downloader",fileUrl:l,fileName:i,fileExt:s,machineGenerated:c}))]:null};si.propTypes={selectTranscript:Tr.func.isRequired,transcriptData:Tr.array.isRequired,transcriptInfo:Tr.shape({title:Tr.string,id:Tr.string,tUrl:Tr.string,tFileExt:Tr.string,isMachineGen:Tr.bool}).isRequired,noTranscript:Tr.bool.isRequired};var ci=u.default.memo(si),ui=function(t){var r=t.searchResults,n=t.searchQuery,a=void 0===n?null:n,i=t.focusedMatchIndex,o=t.setFocusedMatchIndex,l=t.setSearchQuery,s=e.useRef(null);e.useEffect((function(){s.current&&a&&(s.current.value=a)}),[!!s.current]);var c=e.useMemo((function(){return Jr((function(e){l(e.target.value)}),100)}),[]),d=null===a||""===a.replace(/\s/g,""),f=null;return d||(0===r.matchingIds.length?f=u.default.createElement("div",{className:"ramp--transcript_search_navigator"},u.default.createElement("span",{"data-testid":"transcript-search-count",className:"ramp--transcript_search_count"},"no results found in this transcript")):null!==i&&(f=u.default.createElement("div",{className:"ramp--transcript_search_navigator"},u.default.createElement("button",{type:"button","data-testid":"transcript-search-prev",className:"ramp--transcript_menu_button ramp--transcript_search_prev",disabled:0===i,title:"Previous Search Result",onClick:function(e){e.preventDefault(),e.stopPropagation(),i>0&&o(i-1)}},u.default.createElement(xn,{flip:!0})),u.default.createElement("span",{className:"ramp--transcript_search_count","data-testid":"transcript-search-count"},i+1," of ",r.matchingIds.length," results"),u.default.createElement("button",{className:"ramp--transcript_menu_button ramp--transcript_search_next",type:"button","data-testid":"transcript-search-next",disabled:i>=r.matchingIds.length-1,title:"Next Search Result",onClick:function(e){e.preventDefault(),e.stopPropagation(),i0)){n.next=13;break}return u=Ka(c,a,t,r),n.abrupt("return",u);case 13:return n.abrupt("return",{matchedTranscriptLines:[],hitCounts:[],allSearchHits:null});case 16:return n.prev=16,n.t0=n.catch(0),"AbortError"!==n.t0.name&&console.error(n.t0),n.abrupt("return",{matchedTranscriptLines:[],hitCounts:[],allSearchHits:null});case 20:case"end":return n.stop()}}),n,null,[[0,16]])})));return function(e,t){return n.apply(this,arguments)}}()},bi={initialSearchQuery:null,showMarkers:!0,matcherFactory:function(e){var t=e.map((function(e){return e.text.toLocaleLowerCase()}));return function(r,n){var a=new RegExp(String.raw(mi||(mi=Ia(["",""])),r),"i"),i=r.trim().toLocaleLowerCase(),o=t.reduce((function(t,r,n){var o=r.search(a);if(-1!==o){var l=e[n],s=[l.text.slice(0,o),l.text.slice(o,o+i.length),l.text.slice(o+i.length)],c=s[1],u=s[2],d="".concat(s[0],'').concat(c,"").concat(u);return[].concat(D(t),[gi(gi({},l),{},{score:n,match:d,matchCount:1})])}return t}),[]);return{matchedTranscriptLines:o,hitCounts:[],allSearchHits:null}}},sorter:function(e){return e.sort((function(e,t){return e.id-t.id}))},matchesOnly:!1};var xi=["initialSearchQuery"];function ki(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function wi(e){for(var t=1;t1&&void 0!==arguments[1]&&arguments[1]?e.textDisplayed:e.text;return e.match&&(t=e.match),e.speaker?"".concat(e.speaker,": ").concat(t):t},Ci=u.default.memo((function(e){var t=e.item,r=e.goToItem,n=e.isActive,a=e.focusedMatchId,i=e.setFocusedMatchId,o=e.autoScrollEnabled,l=e.showNotes,s=e.transcriptContainerRef,c=e.isNonTimedText,d=e.focusedMatchIndex,f=u.default.useRef(null),p=t.id===a,v=u.default.useRef(p),h=u.default.useRef(n),g=u.default.useRef(-1),y=u.default.useRef(-1),b=u.default.useRef(0);u.default.useEffect((function(){var e=!1,r=y.current;n&&!h.current?o&&(h.current=!0,e=!0):h.current=!1,p&&!v.current?(v.current=!0,e=!0):v.current=!1,e&&f.current&&Lt(f.current,s,!0),b.current=rt?b.current+1:b.current<=0?0:b.current-1,b.current>-1){var r=e[b.current];null!=r&&(r.classList.add("current-hit"),Lt(r,s,!0))}g.current=d}}),[d]);var x=function(e){e.preventDefault(),e.stopPropagation(),t.match&&a!==t.id?i(t.id):null!==a&&t.tag===La.timedCue&&Lt(f.current,s,!0),r(t)};return t.tag===La.note&&l?u.default.createElement("a",{href:"#",ref:f,role:"listitem",onClick:x,className:m.default("ramp--transcript_item",n&&"active",p&&"focused"),"data-testid":"transcript_text",dangerouslySetInnerHTML:{__html:Si(t)}}):t.tag===La.timedCue?u.default.createElement("a",{href:"#",ref:f,role:"listitem",onClick:x,"data-testid":"transcript_item",className:m.default("ramp--transcript_item",n&&"active",p&&"focused")},"number"==typeof t.begin&&u.default.createElement("span",{className:"ramp--transcript_time","data-testid":"transcript_time"},"[",Tt(t.begin,!0),"]"),u.default.createElement("span",{className:"ramp--transcript_text","data-testid":"transcript_text",dangerouslySetInnerHTML:{__html:Si(t)}})):t.tag===La.nonTimedLine?u.default.createElement("a",{href:"#",ref:f,role:"listitem",onClick:x,className:m.default("ramp--transcript_item",n&&"active",p&&"focused"),"data-testid":"transcript_untimed_text"},u.default.createElement("p",{className:"ramp--transcript_untimed_item",dangerouslySetInnerHTML:{__html:Si(t,c)}})):null})),Ri=u.default.memo((function(e){var t,r=e.seekPlayer,n=e.currentTime,a=e.searchResults,i=e.focusedMatchId,o=e.transcriptInfo,l=e.setFocusedMatchId,s=e.autoScrollEnabled,c=e.showNotes,d=e.transcriptContainerRef,f=e.focusedMatchIndex,p=u.default.useState(null),m=T(p,2),v=m[0],h=m[1],g=u.default.useCallback((function(e){"number"==typeof e.begin?(r(e.begin),h(null)):h(e.id)}),[r]);switch(o.tType){case Pa.plainText:t="plain-text";break;case Pa.docx:t="docs";break;case Pa.timedText:t="timed-text";default:t=""}return o.tError?u.default.createElement("p",{key:"no-transcript",id:"no-transcript","data-testid":"no-transcript",role:"note"},o.tError):a.results&&0!==a.results.length?u.default.createElement("div",{"data-testid":"transcript_".concat(t)},a.ids.map((function(e){return u.default.createElement(Ci,{key:e,goToItem:g,focusedMatchId:i,isActive:v===e||"number"==typeof a.results[e].begin&&a.results[e].begin<=n&&n<=a.results[e].end,item:a.results[e],autoScrollEnabled:s,setFocusedMatchId:l,showNotes:c,transcriptContainerRef:d,isNonTimedText:!0,focusedMatchIndex:f})}))):u.default.createElement(Sr,null)})),Ii=function(t){var r,n=t.playerID,a=t.manifestUrl,i=t.showNotes,o=void 0!==i&&i,l=t.search,s=void 0===l?{}:l,c=t.transcripts,d=void 0===c?[]:c,f=u.default.useState([]),p=T(f,2),m=p[0],v=p[1],h=u.default.useState([]),g=T(h,2),y=g[0],b=g[1],x=u.default.useState([]),k=T(x,2),w=k[0],E=k[1],S=u.default.useState({title:null,filename:null,id:null,tUrl:null,tType:null,tFileExt:null,isMachineGen:!1,tError:null}),C=T(S,2),R=C[0],I=C[1],q=u.default.useState(),j=T(q,2),A=j[0],M=j[1],P=u.default.useState(!0),L=T(P,2),_=L[0],N=L[1],F=u.default.useState([]),B=T(F,2),U=B[0],V=B[1],H=(r=wi(wi({},s),{},{isSearchable:R.tType===Pa.timedText||R.tType===Pa.docx||R.tType===Pa.plainText,showMarkers:R.tType===Pa.timedText}))&&r.isSearchable?gi(gi(gi({},bi),r),{},{enabled:!0}):gi(gi({},bi),{},{enabled:!1}),G=H.initialSearchQuery,z=Ra(H,xi),J=u.default.useState(G),W=T(J,2),Z=W[0],Y=W[1],Q=u.default.useState(-1),X=T(Q,2),K=X[0],$=X[1],ee=u.default.useRef(K),te=function(t){var r=t.query,n=t.sorter,a=void 0===n?bi.sorter:n,i=t.enabled,o=void 0===i||i,l=t.transcripts,s=t.canvasIndex,c=t.selectedTranscript,u=t.showMarkers,d=void 0===u?bi.showMarkers:u,f=t.matchesOnly,p=void 0===f?bi.matchesOnly:f,m=t.matcherFactory,v=void 0===m?bi.matcherFactory:m,h=e.useState({results:{},ids:[],matchingIds:[],counts:[]}),g=T(h,2),y=g[0],b=g[1],x=e.useState(),k=T(x,2),w=k[0],E=k[1],S=e.useState(null),C=T(S,2),R=C[0],I=C[1],q=e.useRef(null),j=e.useRef(0),A=e.useMemo((function(){var e=(l||[]).map((function(e,t){return"string"==typeof e?{text:e,id:t}:gi({id:t},e)})),t=e.reduce((function(e,t){return gi(gi({},e),{},O({},t.id,t))}),{}),r=v(e);return null!=w&&null!=w&&(r=yi(w,e,c)),{matcher:r,itemsWithIds:e,itemsIndexed:t}}),[l,v,c]),M=A.matcher,P=A.itemsWithIds,L=A.itemsIndexed,_=e.useContext(dr),N=e.useContext(er);e.useEffect((function(){if(N&&s>=0){var e=N.manifest,t=N.allCanvases,r=null;null!=t&&t.length?r=t[s].searchService:e&&(r=Qt(e)),E(r)}I(null)}),[s]),e.useEffect((function(){q.current&&q.current.abort(),r&&F()}),[r]),e.useEffect((function(){if(!P.length)return _&&_({type:"setSearchMarkers",payload:[]}),void b(gi(gi({},y),{},{results:{},matchingIds:[],ids:[]}));if(!o||!r){_&&_({type:"setSearchMarkers",payload:[]});var e=a(D(P)).map((function(e){return e.id}));return b(gi(gi({},y),{},{results:L,matchingIds:[],ids:e})),void(r||I(null))}if(null!=R){var t=R[c],n=$a(t,r,P);B(n,null==y?void 0:y.counts,R)}else F()}),[M,r,o,a,p,d,_,c]);var F=function(){j.current||clearTimeout(j.current);var e=new AbortController;q.current=e,j.current=setTimeout((function(){Promise.resolve(M(r,q.current)).then((function(t){var r=t.matchedTranscriptLines,n=t.hitCounts,a=t.allSearchHits;e.signal.aborted||B(r,n,a)})).catch((function(e){console.error("Search failed: ",r)}))}))},B=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];I(arguments.length>2&&void 0!==arguments[2]?arguments[2]:null);var n={results:P,matchingIds:[],ids:a(D(P)).map((function(e){return e.id})),counts:(null==t?void 0:t.length)>0?t:[]};if(void 0!==e){var i=e.reduce((function(e,t){return gi(gi({},e),{},O({},t.id,t))}),{}),o=a(D(e),!0),l=[];if(o.map((function(e){if(null!=e.matchCount)for(var t=0;t=4&&n.matchingIds.length<45)&&(u=n.matchingIds.map((function(e){return{time:n.results[e].begin,text:"",class:"ramp--track-marker--search"}}))),_({type:"setSearchMarkers",payload:u})}else _({type:"setSearchMarkers",payload:[]})}}else b(gi({},n))};return y}(wi(wi({},z),{},{query:Z,transcripts:w,canvasIndex:ee.current,selectedTranscript:A})),re=function(t){var r=t.searchResults,n=e.useState(null),a=T(n,2),i=a[0],o=a[1],l=null===i?null:r.matchingIds[i],s=e.useCallback((function(e){var t=r.matchingIds.indexOf(e);o(-1!==t?t:null)}),[r.matchingIds]);return e.useEffect((function(){r.matchingIds.length||null===i?r.matchingIds.length&&null===i?o(0):null!==i&&i>=r.matchingIds.length&&o(r.matchingIds.length-1):o(null)}),[r.matchingIds,i]),e.useEffect((function(){r.matchingIds.length&&i>0&&o(null)}),[r.matchingIds]),{focusedMatchId:l,setFocusedMatchId:s,focusedMatchIndex:i,setFocusedMatchIndex:o}}({searchResults:te}),ne=re.focusedMatchId,ae=re.setFocusedMatchId,ie=re.focusedMatchIndex,oe=re.setFocusedMatchIndex,le=function(e){var t=e.searchResults,r=e.canvasTranscripts,n=e.searchQuery;if(null==t||!t.counts||0===(null==r?void 0:r.length)||null===n)return r;var a=t.counts,i=[];return r.map((function(e){var t,r=(null===(t=a.find((function(t){return t.transcriptURL===e.url})))||void 0===t?void 0:t.numberOfHits)||0;i.push(gi(gi({},e),{},{numberOfHits:r}))})),i}({searchResults:te,canvasTranscripts:y,searchQuery:Z}),se=u.default.useState(!0),ce=T(se,2),ue=ce[0],de=ce[1],fe=u.default.useState(!0),pe=T(fe,2),me=pe[0],ve=pe[1],he=u.default.useRef(me),ge=new AbortController,ye=u.default.useRef(null),be=u.default.useRef(null),xe=u.default.useRef(),ke=u.default.useState(-1),we=T(ke,2),Ee=we[0],Te=we[1],Se=u.default.useMemo((function(){return Zr(Te,50)}),[]),Ce=u.default.useCallback((function(e){Se(e),be.current&&(be.current.currentTime=e)}),[]);u.default.useEffect((function(){ye.current=setInterval((function(){var e,t=document.getElementById(n);if(t?t.children[0]?be.current=t.children[0]:be.current=t:(console.warn("Cannot find player, ".concat(n," on page. Transcript synchronization is disabled")),N(!1)),be.current){var r=parseInt(be.current.dataset.canvasindex);Number.isNaN(r)&&(r=0),r!==ee.current&&(E([]),e=r,ge.abort(),ee.current=e,$(e),Se(be.current.currentTime),be.current.addEventListener("timeupdate",(function(){Se(be.current.currentTime)})))}}),500)}),[]),u.default.useEffect((function(){return function(){clearInterval(ye.current)}}),[]);var Re=function(){var e=gr(xr.mark((function e(t){var r;return xr.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(!((null==t?void 0:t.length)>0)){e.next=6;break}return e.next=3,Fa(t);case 3:e.t0=e.sent,e.next=9;break;case 6:return e.next=8,_a(a);case 8:e.t0=e.sent;case 9:r=e.t0,v(null!=r?r:[]),Oe(null!=r?r:[]);case 12:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}();u.default.useEffect((function(){0!==(null==d?void 0:d.length)||a?Re(d):(N(!1),E([]),I({tType:Pa.noTranscript,id:"",tError:Ei}))}),[ee.current]),u.default.useEffect((function(){if((null==m?void 0:m.length)>0&&null!=ee.current){var e=m.filter((function(e){return e.canvasId==ee.current}))[0];b(e.items),je(e.items[0])}}),[ee.current]);var Ie,Oe=function(e){var t,r;if(!ge.signal.aborted){var n,a=function(e){return e.filter((function(e){return e.canvasId==K}))};if(!(null!=e&&e.length)>0||!(null!==(t=a(e))&&void 0!==t&&t.length)>0||!(null!==(n=e,r=a(n)[0].items)&&void 0!==r&&r.length)>0)de(!0),E([]),je(void 0);else{de(!1);var i=a(e)[0];b(i.items),je(i.items[0])}}},qe=u.default.useCallback((function(e){var t=y.filter((function(t){return t.id===e}));je(t[0])}),[y]),je=function(){var e=gr(xr.mark((function e(t){var r,n,a,i,o,l,s,c,u,d,f,p,m;return xr.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t&&null!=t){e.next=5;break}return de(!0),N(!1),I({tType:Pa.noTranscript,id:"",tError:Ei}),e.abrupt("return");case 5:if(de(!1),n=(r=t).id,a=r.title,i=r.filename,o=r.url,l=r.isMachineGen,s=r.format,!((null==(c=U.filter((function(e){return e.id==n&&e.canvasId==ee.current})))?void 0:c.length)>0)){e.next=15;break}u=c[0],d=u.tData,f=u.tFileExt,p=u.tType,m=u.tError,E(d),I({title:a,filename:i,id:n,isMachineGen:l,tType:p,tUrl:o,tFileExt:f,tError:m}),M(o),e.next=17;break;case 15:return e.next=17,Promise.resolve(Va(o,ee.current,s)).then((function(e){if(null!=e){var r=e.tData,o=e.tUrl,s=e.tType,c=e.tFileExt,u="";switch(s){case Pa.invalid:u="Invalid URL for transcript, please check again.";break;case Pa.noTranscript:u=Ei;break;case Pa.noSupport:u=Ti;break;case Pa.invalidVTT:u="Invalid WebVTT file, please check again.";break;case Pa.invalidTimestamp:u="Invalid timestamp format in cue(s), please check again."}E(r),I({title:a,filename:i,id:n,isMachineGen:l,tType:s,tUrl:o,tFileExt:c,tError:u}),M(o),t=wi(wi({},t),{},{tType:s,tData:r,tFileExt:c,canvasId:ee.current,tError:u}),V([].concat(D(U),[t]))}}));case 17:N(!1);case 18:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}();return _?u.default.createElement(Sr,null):u.default.createElement("div",{className:"ramp--transcript_nav","data-testid":"transcript_nav",key:R.title},!ue&&u.default.createElement(vi,{showSearch:z.enabled,selectTranscript:qe,transcriptData:le,transcriptInfo:R,noTranscript:(null===(Ie=R.tError)||void 0===Ie?void 0:Ie.length)>0&&R.tError!=Ti,setAutoScrollEnabled:function(e){he.current=e,ve(e)},setFocusedMatchIndex:oe,focusedMatchIndex:ie,autoScrollEnabled:he.current,searchResults:te,searchQuery:Z,setSearchQuery:Y}),u.default.createElement("div",{className:"transcript_content ".concat(w?"":"static"),"data-testid":"transcript_content_".concat(R.tType),role:"list","aria-label":"Attached Transcript content",ref:xe},u.default.createElement(Ri,{currentTime:Ee,seekPlayer:Ce,searchResults:te,focusedMatchId:ne,transcriptInfo:R,setFocusedMatchId:ae,autoScrollEnabled:he.current&&null===Z,showNotes:o,transcriptContainerRef:xe,focusedMatchIndex:ie})))};Ii.propTypes={playerID:Tr.string.isRequired,manifestUrl:Tr.string,showSearch:Tr.bool,showNotes:Tr.bool,search:Tr.oneOf([Tr.bool,Tr.shape({initialSearchQuery:Tr.string,showMarkers:Tr.bool,matcherFactory:Tr.func,sorter:Tr.func,matchesOnly:Tr.bool})]),transcripts:Tr.arrayOf(Tr.shape({canvasId:Tr.number.isRequired,items:Tr.arrayOf(Tr.shape({title:Tr.string,url:Tr.string}))}))};var Oi=function(e){var t=e.displayOnlyCanvasMetadata,r=void 0!==t&&t,n=e.displayAllMetadata,a=void 0!==n&&n,i=e.displayTitle,o=void 0===i||i,l=e.showHeading,s=void 0===l||l,c=e.itemHeading,d=void 0===c?"Item Details":c,f=e.sectionHeaading,p=void 0===f?"Section Details":f,m=or(),v=m.manifest,h=m.canvasIndex,g=u.default.useState(),y=T(g,2),b=y[0],x=y[1],k=u.default.useState(),w=T(k,2);w[0];var E=w[1],S=u.default.useState(),C=T(S,2),R=C[0],I=C[1],O=u.default.useState(),q=T(O,2),j=q[0],A=q[1],D=u.default.useState(),M=T(D,2),P=M[0],L=M[1],_=u.default.useState(),N=T(_,2),F=N[0],B=N[1],U=u.default.useState(),V=T(U,2),H=V[0],G=V[1],z=u.default.useRef();u.default.useEffect((function(){if(v){var e,t=r||a;L(t);var n=!r||a;A(n);var i=function(e,t){var r=[],n={canvasMetadata:r,manifestMetadata:[],rights:[]},a=e.items;if(t&&a){for(var i in a){var o=parseInt(i),l=Yt(a[o],"Canvas");r.push({canvasindex:o,metadata:Zt(a[o].metadata,"Canvas"),rights:l})}n.canvasMetadata=r}var s=Zt(e.metadata,"Manifest");n.manifestMetadata=s;var c=Yt(e,"Manifest");return n.rights=c,n}(v,t);if(t&&(s=i.canvasMetadata,E(s),z.current=s,J()),n){var l=i.manifestMetadata;o||(l=l.filter((function(e){return"title"!=e.label.toLowerCase()}))),x(l)}(null===(e=i.rights)||void 0===e?void 0:e.length)>0&&B(i.rights)}var s}),[v]),u.default.useEffect((function(){h>=0&&P&&J()}),[h]);var J=function(){var e=z.current.filter((function(e){return e.canvasindex===h}))[0];if(null!=e){var t=e.metadata,r=e.rights;o||null==t||(t=t.filter((function(e){return"title"!=e.label.toLowerCase()}))),I(t),null!=r&&(null==r?void 0:r.length)>0&&G(r)}},W=function(){return(null==R?void 0:R.length)>0||(null==b?void 0:b.length)>0},Z=function(e){var t=[];return(null==e?void 0:e.length)>0&&e.map((function(e,r){t.push(u.default.createElement(u.default.Fragment,{key:r},u.default.createElement("dt",null,e.label),u.default.createElement("dd",{dangerouslySetInnerHTML:{__html:e.value}})))})),u.default.createElement("dl",null,t)};return u.default.createElement("div",{"data-testid":"metadata-display",className:"ramp--metadata-display"},s&&u.default.createElement("div",{className:"ramp--metadata-display-title","data-testid":"metadata-display-title"},u.default.createElement("h4",null,"Details")),W()&&u.default.createElement("div",{className:"ramp--metadata-display-content"},j&&(null==b?void 0:b.length)>0&&u.default.createElement(u.default.Fragment,null,a&&u.default.createElement("span",null,d),Z(b),(null==F?void 0:F.length)>0&&u.default.createElement("span",{className:"ramp--metadata-rights-heading","data-testid":"manifest-rights"},"Rights"),Z(F)),P&&(null==R?void 0:R.length)>0&&u.default.createElement(u.default.Fragment,null,a&&u.default.createElement("span",null,p),Z(R),(null==H?void 0:H.length)>0&&u.default.createElement("span",{className:"ramp--metadata-rights-heading","data-testid":"canvas-rights"},"Rights"),Z(H))),!W()&&u.default.createElement("div",{"data-testid":"metadata-display-message",className:"ramp--metadata-display-message"},u.default.createElement("p",null,"No valid Metadata is in the Manifest/Canvas(es)")))};Oi.propTypes={displayOnlyCanvasMetadata:Tr.bool,displayAllMetadata:Tr.bool,displayTitle:Tr.bool,showHeading:Tr.bool,itemHeading:Tr.string,sectionHeaading:Tr.string};var qi=function(e){var t=e.label,r=void 0===t?"Autoplay":t,n=e.showLabel,a=void 0===n||n,i=or().autoAdvance,o=lr(),l=function(e){o({autoAdvance:e.target.checked,type:"setAutoAdvance"})};return u.default.useMemo((function(){return u.default.createElement("div",{"data-testid":"auto-advance",className:"ramp--auto-advance"},a&&u.default.createElement("span",{className:"ramp--auto-advance-label","data-testid":"auto-advance-label",htmlFor:"auto-advance-toggle",id:"auto-advance-toggle-label"},r),u.default.createElement("label",{className:"ramp--auto-advance-toggle","aria-labelledby":"auto-advance-toggle-label"},u.default.createElement("input",{"data-testid":"auto-advance-toggle",name:"auto-advance-toggle",type:"checkbox",checked:i,"aria-label":r,onChange:l}),u.default.createElement("span",{className:"slider round"})))}),[i])};qi.propTypes={label:Tr.string,showLabel:Tr.bool};var ji=function(){var t,r=e.useContext(ur);r&&(t=r.player);var n=e.useRef();return n.current=e.useMemo((function(){return t}),[t]),{getCurrentTime:e.useCallback((function(){return n.current?n.current.currentTime():0}),[n.current]),player:n.current}},Ai=function(e){var t,r=e.newMarkerEndpoint,n=e.canvasId,a=e.handleCreate,i=e.csrfToken,o=u.default.useState(!1),l=T(o,2),s=l[0],c=l[1],d=u.default.useState(!1),f=T(d,2),p=f[0],m=f[1],v=u.default.useState(!1),h=T(v,2),g=h[0],y=h[1],b=u.default.useState(""),x=T(b,2),k=x[0],w=x[1],E=u.default.useState(),S=T(E,2),C=S[0],R=S[1],I=ji().getCurrentTime;u.default.useEffect((function(){return c(!1),function(){var e;null===(e=t)||void 0===e||e.abort()}}),[n]);var O=u.default.useCallback((function(e){e.preventDefault();var o=e.target,l=new FormData(o),s=Object.fromEntries(l.entries()),u=s.label,d=s.time,f={type:"Annotation",motivation:"highlighting",body:{type:"TextualBody",format:"text/html",value:u},target:"".concat(n,"#t=").concat(St(d))},p={method:"POST",credentials:"same-origin",headers:{Accept:"application/json"},body:JSON.stringify(f)};void 0!==i&&(p.headers["X-CSRF-Token"]=i),t=new AbortController,fetch(r,p,{signal:t.signal}).then((function(e){if(201!=e.status)throw new Error;return e.json()})).then((function(e){var t=Xt(e);t&&a(t),c(!1)})).catch((function(e){console.error("CreateMarker -> handleCreateMarker() -> failed to create annotation; ",e),y(!0),w("Marker creation failed.")}))}),[n]),q=u.default.useCallback((function(){c(!1),m(!1),w(""),y(!1)})),j=function(e){var t,r,n=null!==(t=null==e||null===(r=e.target)||void 0===r?void 0:r.value)&&void 0!==t?t:e;R(n);var a=Pt(n);m(a)};return u.default.createElement("div",{className:"ramp-markers-display__new-marker"},u.default.createElement("button",{type:"submit",onClick:function(){var e=Tt(I(),!0,!0);j(e),c(!0)},className:"ramp--markers-display__edit-button","data-testid":"create-new-marker-button"},"Add New Marker"),s&&u.default.createElement("form",{className:"ramp--markers-display__new-marker-form",method:"post",onSubmit:O,"data-testid":"create-new-marker-form"},u.default.createElement("table",{className:"create-marker-form-table"},u.default.createElement("tbody",null,u.default.createElement("tr",null,u.default.createElement("td",null,u.default.createElement("label",{htmlFor:"new-marker-title"},"Title:"),u.default.createElement("input",{id:"new-marker-title","data-testid":"create-marker-title",type:"text",className:"ramp--markers-display__create-marker",name:"label"})),u.default.createElement("td",null,u.default.createElement("label",{htmlFor:"new-marker-time"},"Time:"),u.default.createElement("input",{id:"new-marker-time","data-testid":"create-marker-timestamp",type:"text",className:"ramp--markers-display__create-marker ".concat(p?"time-valid":"time-invalid"),name:"time",value:C,onChange:j})),u.default.createElement("td",null,u.default.createElement("div",{className:"marker-actions"},g&&u.default.createElement("p",{className:"ramp--markers-display__error-message"},k),u.default.createElement("button",{type:"submit",className:"ramp--markers-display__edit-button","data-testid":"edit-save-button",disabled:!p},u.default.createElement(hn,null)," Save"),u.default.createElement("button",{className:"ramp--markers-display__edit-button-danger","data-testid":"edit-cancel-button",onClick:q},u.default.createElement(gn,null)," Cancel"))))))))};Ai.propTypes={newMarkerEndpoint:Tr.string.isRequired,canvasId:Tr.string,handleCreate:Tr.func.isRequired};var Di=function(t){var r,n,a=t.marker,i=t.handleSubmit,o=t.handleDelete,l=t.hasAnnotationService,s=t.toggleIsEditing,c=t.csrfToken,d=u.default.useState(!1),f=T(d,2),p=f[0],m=f[1],v=u.default.useState(!0),h=T(v,2),g=h[0],y=h[1],b=u.default.useState(),x=T(b,2),k=x[0],w=x[1],E=u.default.useState(!1),S=T(E,2),C=S[0],R=S[1],I=u.default.useState(!1),O=T(I,2),q=O[0],j=O[1],A=u.default.useState(""),D=T(A,2),M=D[0],P=D[1],L=(n=e.useContext(er).playlist.isEditing,{isDisabled:e.useMemo((function(){return n}),[n])}).isDisabled,_=ji().player;u.default.useEffect((function(){return function(){var e;null===(e=r)||void 0===e||e.abort()}}),[]),u.default.useEffect((function(){F(a.value),V(a.timeStr)}),[a]);var N=u.default.useRef(a.value),F=function(e){N.current=e},B=u.default.useRef(St(a.timeStr)),U=u.default.useRef(a.timeStr),V=function(e){U.current=e,B.current=St(e)},H=function(){j(!1),P("")},G=function(){R(!1),m(!1),s(!1)},z=u.default.useCallback((function(e){e.preventDefault();var t=parseFloat(e.target.dataset.offset);_&&_.currentTime(t)}),[_]);return p?u.default.createElement("tr",null,u.default.createElement("td",null,u.default.createElement("input",{id:"label","data-testid":"edit-label",defaultValue:N.current,type:"text",className:"ramp--markers-display__edit-marker",onChange:function(e){return F(e.target.value)},name:"label"})),u.default.createElement("td",null,u.default.createElement("input",{className:"ramp--markers-display__edit-marker ".concat(g?"time-valid":"time-invalid"),id:"time","data-testid":"edit-timestamp",defaultValue:U.current,type:"text",onChange:function(e){return function(e){var t=Pt(e);y(t),V(e)}(e.target.value)},name:"time"})),u.default.createElement("td",null,u.default.createElement("div",{className:"marker-actions"},q&&u.default.createElement("p",{className:"ramp--markers-display__error-message"},M),u.default.createElement("button",{type:"submit",onClick:function(){var e={type:"Annotation",motivation:"highlighting",body:{type:"TextualBody",format:"text/html",value:N.current},id:a.id,target:"".concat(a.canvasId,"#t=").concat(St(U.current))},t={method:"PUT",credentials:"same-origin",headers:{Accept:"application/json"},body:JSON.stringify(e)};void 0!==c&&(t.headers["X-CSRF-Token"]=c),r=new AbortController,fetch(a.id,t,{signal:r.signal}).then((function(e){if(201!=e.status)throw new Error;i(N.current,U.current,a.id),H(),G()})).catch((function(e){console.error("MarkerRow -> handleEditSubmit -> failed to update annotation; ",e),j(!0),P("Marker update failed")}))},disabled:!g,className:"ramp--markers-display__edit-button","data-testid":"edit-save-button"},u.default.createElement(hn,null)," Save"),u.default.createElement("button",{className:"ramp--markers-display__edit-button-danger","data-testid":"edit-cancel-button",onClick:function(){V(k.time),F(k.label),w({}),H(),G()}},u.default.createElement(gn,null)," Cancel")))):C?u.default.createElement("tr",null,u.default.createElement("td",null,u.default.createElement("a",{href:"".concat(a.canvasId,"#t=").concat(B.current,","),onClick:function(e){return z(e)},"data-offset":B.current},N.current)),u.default.createElement("td",null,U.current),u.default.createElement("td",null,u.default.createElement("div",{className:"marker-actions"},u.default.createElement("p",null,"Are you sure?"),u.default.createElement("button",{type:"submit",className:"ramp--markers-display__edit-button-danger","data-testid":"delete-confirm-button",onClick:function(){var e={method:"DELETE",credentials:"same-origin",headers:{Accept:"application/json"}};void 0!==c&&(e.headers["X-CSRF-Token"]=c),r=new AbortController,fetch(a.id,e,{signal:r.signal}).then((function(e){if(200!=e.status)throw new Error;o(a.id),H(),G()})).catch((function(e){console.error("MarkerRow -> submitDelete() -> failed to delete annotation; ",e),G(),j(!0),P("Marker delete failed."),setTimeout((function(){H()}),1500)}))}},u.default.createElement(hn,null)," Yes"),u.default.createElement("button",{className:"ramp--markers-display__edit-button","data-testid":"delete-cancel-button",onClick:G},u.default.createElement(gn,null)," Cancel")))):u.default.createElement("tr",null,u.default.createElement("td",null,u.default.createElement("a",{href:"".concat(a.canvasId,"#t=").concat(B.current,","),onClick:function(e){return z(e)},"data-offset":B.current},N.current)),u.default.createElement("td",null,U.current),l&&u.default.createElement("td",null,u.default.createElement("div",{className:"marker-actions"},q&&u.default.createElement("p",{className:"ramp--markers-display__error-message"},M),u.default.createElement("button",{onClick:function(){w({time:U.current,label:N.current}),m(!0),s(!0)},className:"ramp--markers-display__edit-button","data-testid":"edit-button",disabled:L},u.default.createElement(mn,null)," Edit"),u.default.createElement("button",{className:"ramp--markers-display__edit-button-danger","data-testid":"delete-button",disabled:L,onClick:function(){R(!0),s(!0)}},u.default.createElement(vn,null)," Delete"))))};Di.propTypes={marker:Tr.object.isRequired,handleSubmit:Tr.func.isRequired,handleDelete:Tr.func.isRequired,hasAnnotationService:Tr.bool.isRequired,toggleIsEditing:Tr.func.isRequired};var Mi=function(t){var r,n=t.showHeading,i=void 0===n||n,o=t.headingText,l=void 0===o?"Markers":o,s=or(),c=s.allCanvases,d=s.canvasIndex,f=s.playlist,p=lr(),m=f.hasAnnotationService,v=f.annotationServiceId,h=f.markers,g=e.useState([]),y=T(g,2);y[0];var b=y[1],x=a.useErrorBoundary().showBoundary,k=e.useRef(),w=e.useRef([]),E=function(e){b.apply(void 0,D(e)),w.current=e},S=null===(r=document.getElementsByName("csrf-token")[0])||void 0===r?void 0:r.content;e.useEffect((function(){try{if((null==h?void 0:h.length)>0){var e=h.filter((function(e){return e.canvasIndex===d}))[0].canvasMarkers;E(e),null!=c&&(null==c?void 0:c.length)>0&&(k.current=c[d].canvasId)}}catch(e){x(e)}}),[d,h]);var C=e.useCallback((function(e,t,r){var n=w.current.map((function(n){return n.id===r&&(n.value=e,n.timeStr=t,n.time=St(t)),n}));E(n),p({updatedMarkers:n,type:"setPlaylistMarkers"})})),R=e.useCallback((function(e){var t=w.current.filter((function(t){return t.id!=e}));E(t),p({updatedMarkers:t,type:"setPlaylistMarkers"})})),I=e.useCallback((function(e){E([].concat(D(w.current),[e])),p({updatedMarkers:w.current,type:"setPlaylistMarkers"})})),O=e.useCallback((function(e){p({isEditing:e,type:"setIsEditing"})}));return e.useMemo((function(){return u.default.createElement("div",{className:"ramp--markers-display","data-testid":"markers-display"},i&&u.default.createElement("div",{className:"ramp--markers-display__title","data-testid":"markers-display-title"},u.default.createElement("h4",null,l)),m&&u.default.createElement(Ai,{newMarkerEndpoint:v,canvasId:k.current,handleCreate:I,csrfToken:S}),w.current.length>0&&u.default.createElement("table",{className:"ramp--markers-display_table","data-testid":"markers-display-table"},u.default.createElement("thead",null,u.default.createElement("tr",null,u.default.createElement("th",null,"Name"),u.default.createElement("th",null,"Time"),m&&u.default.createElement("th",null,"Actions"))),u.default.createElement("tbody",null,w.current.map((function(e,t){return u.default.createElement(Di,{key:t,marker:e,handleSubmit:C,handleDelete:R,hasAnnotationService:m,toggleIsEditing:O,csrfToken:S})})))))}),[w.current,S])};Mi.propTypes={showHeading:Tr.bool,headingText:Tr.string},exports.AutoAdvanceToggle=qi,exports.IIIFPlayer=Or,exports.MarkersDisplay=Mi,exports.MediaPlayer=pa,exports.MetadataDisplay=Oi,exports.StructuredNavigation=ka,exports.SupplementalFiles=function(t){var r=t.itemHeading,n=void 0===r?"Item files":r,i=t.sectionHeading,o=void 0===i?"Section files":i,l=t.showHeading,s=void 0===l||l,c=or().renderings,d=e.useState(),f=T(d,2),p=f[0],m=f[1],v=e.useState(),h=T(v,2),g=h[0],y=h[1],b=e.useState(!1),x=T(b,2),k=x[0],w=x[1],E=e.useState(!1),S=T(E,2),C=S[0],R=S[1],I=a.useErrorBoundary().showBoundary;e.useEffect((function(){try{var e;m(null==c?void 0:c.manifest);var t=null==c?void 0:c.canvas,r=0;t&&(y(t),r=t.reduce((function(e,t){return e+t.files.length}),0),w(r>0)),r>0||(null==c||null===(e=c.manifest)||void 0===e?void 0:e.length)>0?R(!0):R(!1)}catch(e){I(e)}}),[c]);var O=function(e,t){e.preventDefault(),It(t.id,t.filename,t.fileExt,t.isMachineGen)};return e.useMemo((function(){return u.default.createElement("div",{"data-testid":"supplemental-files",className:"ramp--supplemental-files"},s&&u.default.createElement("div",{className:"ramp--supplemental-files-heading","data-testid":"supplemental-files-heading"},u.default.createElement("h4",null,"Files")),C&&u.default.createElement("div",{className:"ramp--supplemental-files-display-content","data-testid":"supplemental-files-display-content"},Array.isArray(p)&&p.length>0&&u.default.createElement(e.Fragment,null,u.default.createElement("h4",null,n),u.default.createElement("dl",{key:"item-files"},p.map((function(t,r){return u.default.createElement(e.Fragment,{key:r},u.default.createElement("dd",{key:"item-file-".concat(r)},u.default.createElement("a",{href:t.id,key:r,onClick:function(e){return O(e,t)}},t.label)))})))),Array.isArray(g)&&k&&u.default.createElement(e.Fragment,null,u.default.createElement("h4",null,o),g.map((function(e,t){var r=e.files;return r.length>0&&u.default.createElement("dl",{key:"section-".concat(t,"-label")},u.default.createElement("dt",{key:e.label},e.label),r.map((function(e,r){return u.default.createElement("dd",{key:"section-".concat(t,"-file-").concat(r)},u.default.createElement("a",{href:e.id,key:r,onClick:function(t){return O(t,e)}},e.label))})))})))),!C&&u.default.createElement("div",{"data-testid":"supplemental-files-empty",className:"ramp--supplemental-files-empty"},u.default.createElement("p",null,"No Supplemental file(s) in Manifest")))}),[C,k])},exports.Transcript=Ii; +"use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),t=require("manifesto.js"),r=require("mime-db"),n=require("sanitize-html"),a=require("react-error-boundary"),i=require("classnames"),o=require("video.js"),s=require("mammoth");function l(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function c(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var n=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,n.get?n:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var u=l(e),d=l(r),f=l(n),p=l(i),m=l(o),v=l(s),h="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function g(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function y(e,t,r){return e(r={path:t,exports:{},require:function(e,t){return function(){throw new Error("Dynamic requires are not currently supported by @rollup/plugin-commonjs")}(null==t&&r.path)}},r.exports),r.exports}var b=y((function(e){e.exports=function(e){if(Array.isArray(e))return e},e.exports.__esModule=!0,e.exports.default=e.exports})),x=y((function(e){e.exports=function(e,t){var r=null==e?null:"undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(null!=r){var n,a,i,o,s=[],l=!0,c=!1;try{if(i=(r=r.call(e)).next,0===t){if(Object(r)!==r)return;l=!1}else for(;!(l=(n=i.call(r)).done)&&(s.push(n.value),s.length!==t);l=!0);}catch(e){c=!0,a=e}finally{try{if(!l&&null!=r.return&&(o=r.return(),Object(o)!==o))return}finally{if(c)throw a}}return s}},e.exports.__esModule=!0,e.exports.default=e.exports})),k=y((function(e){e.exports=function(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r",""":'"',"'":"'","&":"&"},characters:{"<":"<",">":">",'"':""","'":"'","&":"&"}},html4:{entities:{"'":"'"," ":" "," ":" ","¡":"¡","¡":"¡","¢":"¢","¢":"¢","£":"£","£":"£","¤":"¤","¤":"¤","¥":"¥","¥":"¥","¦":"¦","¦":"¦","§":"§","§":"§","¨":"¨","¨":"¨","©":"©","©":"©","ª":"ª","ª":"ª","«":"«","«":"«","¬":"¬","¬":"¬","­":"­","­":"­","®":"®","®":"®","¯":"¯","¯":"¯","°":"°","°":"°","±":"±","±":"±","²":"²","²":"²","³":"³","³":"³","´":"´","´":"´","µ":"µ","µ":"µ","¶":"¶","¶":"¶","·":"·","·":"·","¸":"¸","¸":"¸","¹":"¹","¹":"¹","º":"º","º":"º","»":"»","»":"»","¼":"¼","¼":"¼","½":"½","½":"½","¾":"¾","¾":"¾","¿":"¿","¿":"¿","À":"À","À":"À","Á":"Á","Á":"Á","Â":"Â","Â":"Â","Ã":"Ã","Ã":"Ã","Ä":"Ä","Ä":"Ä","Å":"Å","Å":"Å","Æ":"Æ","Æ":"Æ","Ç":"Ç","Ç":"Ç","È":"È","È":"È","É":"É","É":"É","Ê":"Ê","Ê":"Ê","Ë":"Ë","Ë":"Ë","Ì":"Ì","Ì":"Ì","Í":"Í","Í":"Í","Î":"Î","Î":"Î","Ï":"Ï","Ï":"Ï","Ð":"Ð","Ð":"Ð","Ñ":"Ñ","Ñ":"Ñ","Ò":"Ò","Ò":"Ò","Ó":"Ó","Ó":"Ó","Ô":"Ô","Ô":"Ô","Õ":"Õ","Õ":"Õ","Ö":"Ö","Ö":"Ö","×":"×","×":"×","Ø":"Ø","Ø":"Ø","Ù":"Ù","Ù":"Ù","Ú":"Ú","Ú":"Ú","Û":"Û","Û":"Û","Ü":"Ü","Ü":"Ü","Ý":"Ý","Ý":"Ý","Þ":"Þ","Þ":"Þ","ß":"ß","ß":"ß","à":"à","à":"à","á":"á","á":"á","â":"â","â":"â","ã":"ã","ã":"ã","ä":"ä","ä":"ä","å":"å","å":"å","æ":"æ","æ":"æ","ç":"ç","ç":"ç","è":"è","è":"è","é":"é","é":"é","ê":"ê","ê":"ê","ë":"ë","ë":"ë","ì":"ì","ì":"ì","í":"í","í":"í","î":"î","î":"î","ï":"ï","ï":"ï","ð":"ð","ð":"ð","ñ":"ñ","ñ":"ñ","ò":"ò","ò":"ò","ó":"ó","ó":"ó","ô":"ô","ô":"ô","õ":"õ","õ":"õ","ö":"ö","ö":"ö","÷":"÷","÷":"÷","ø":"ø","ø":"ø","ù":"ù","ù":"ù","ú":"ú","ú":"ú","û":"û","û":"û","ü":"ü","ü":"ü","ý":"ý","ý":"ý","þ":"þ","þ":"þ","ÿ":"ÿ","ÿ":"ÿ",""":'"',""":'"',"&":"&","&":"&","<":"<","<":"<",">":">",">":">","Œ":"Œ","œ":"œ","Š":"Š","š":"š","Ÿ":"Ÿ","ˆ":"ˆ","˜":"˜"," ":" "," ":" "," ":" ","‌":"‌","‍":"‍","‎":"‎","‏":"‏","–":"–","—":"—","‘":"‘","’":"’","‚":"‚","“":"“","”":"”","„":"„","†":"†","‡":"‡","‰":"‰","‹":"‹","›":"›","€":"€","ƒ":"ƒ","Α":"Α","Β":"Β","Γ":"Γ","Δ":"Δ","Ε":"Ε","Ζ":"Ζ","Η":"Η","Θ":"Θ","Ι":"Ι","Κ":"Κ","Λ":"Λ","Μ":"Μ","Ν":"Ν","Ξ":"Ξ","Ο":"Ο","Π":"Π","Ρ":"Ρ","Σ":"Σ","Τ":"Τ","Υ":"Υ","Φ":"Φ","Χ":"Χ","Ψ":"Ψ","Ω":"Ω","α":"α","β":"β","γ":"γ","δ":"δ","ε":"ε","ζ":"ζ","η":"η","θ":"θ","ι":"ι","κ":"κ","λ":"λ","μ":"μ","ν":"ν","ξ":"ξ","ο":"ο","π":"π","ρ":"ρ","ς":"ς","σ":"σ","τ":"τ","υ":"υ","φ":"φ","χ":"χ","ψ":"ψ","ω":"ω","ϑ":"ϑ","ϒ":"ϒ","ϖ":"ϖ","•":"•","…":"…","′":"′","″":"″","‾":"‾","⁄":"⁄","℘":"℘","ℑ":"ℑ","ℜ":"ℜ","™":"™","ℵ":"ℵ","←":"←","↑":"↑","→":"→","↓":"↓","↔":"↔","↵":"↵","⇐":"⇐","⇑":"⇑","⇒":"⇒","⇓":"⇓","⇔":"⇔","∀":"∀","∂":"∂","∃":"∃","∅":"∅","∇":"∇","∈":"∈","∉":"∉","∋":"∋","∏":"∏","∑":"∑","−":"−","∗":"∗","√":"√","∝":"∝","∞":"∞","∠":"∠","∧":"∧","∨":"∨","∩":"∩","∪":"∪","∫":"∫","∴":"∴","∼":"∼","≅":"≅","≈":"≈","≠":"≠","≡":"≡","≤":"≤","≥":"≥","⊂":"⊂","⊃":"⊃","⊄":"⊄","⊆":"⊆","⊇":"⊇","⊕":"⊕","⊗":"⊗","⊥":"⊥","⋅":"⋅","⌈":"⌈","⌉":"⌉","⌊":"⌊","⌋":"⌋","⟨":"〈","⟩":"〉","◊":"◊","♠":"♠","♣":"♣","♥":"♥","♦":"♦"},characters:{"'":"'"," ":" ","¡":"¡","¢":"¢","£":"£","¤":"¤","¥":"¥","¦":"¦","§":"§","¨":"¨","©":"©","ª":"ª","«":"«","¬":"¬","­":"­","®":"®","¯":"¯","°":"°","±":"±","²":"²","³":"³","´":"´","µ":"µ","¶":"¶","·":"·","¸":"¸","¹":"¹","º":"º","»":"»","¼":"¼","½":"½","¾":"¾","¿":"¿","À":"À","Á":"Á","Â":"Â","Ã":"Ã","Ä":"Ä","Å":"Å","Æ":"Æ","Ç":"Ç","È":"È","É":"É","Ê":"Ê","Ë":"Ë","Ì":"Ì","Í":"Í","Î":"Î","Ï":"Ï","Ð":"Ð","Ñ":"Ñ","Ò":"Ò","Ó":"Ó","Ô":"Ô","Õ":"Õ","Ö":"Ö","×":"×","Ø":"Ø","Ù":"Ù","Ú":"Ú","Û":"Û","Ü":"Ü","Ý":"Ý","Þ":"Þ","ß":"ß","à":"à","á":"á","â":"â","ã":"ã","ä":"ä","å":"å","æ":"æ","ç":"ç","è":"è","é":"é","ê":"ê","ë":"ë","ì":"ì","í":"í","î":"î","ï":"ï","ð":"ð","ñ":"ñ","ò":"ò","ó":"ó","ô":"ô","õ":"õ","ö":"ö","÷":"÷","ø":"ø","ù":"ù","ú":"ú","û":"û","ü":"ü","ý":"ý","þ":"þ","ÿ":"ÿ",'"':""","&":"&","<":"<",">":">","Œ":"Œ","œ":"œ","Š":"Š","š":"š","Ÿ":"Ÿ","ˆ":"ˆ","˜":"˜"," ":" "," ":" "," ":" ","‌":"‌","‍":"‍","‎":"‎","‏":"‏","–":"–","—":"—","‘":"‘","’":"’","‚":"‚","“":"“","”":"”","„":"„","†":"†","‡":"‡","‰":"‰","‹":"‹","›":"›","€":"€","ƒ":"ƒ","Α":"Α","Β":"Β","Γ":"Γ","Δ":"Δ","Ε":"Ε","Ζ":"Ζ","Η":"Η","Θ":"Θ","Ι":"Ι","Κ":"Κ","Λ":"Λ","Μ":"Μ","Ν":"Ν","Ξ":"Ξ","Ο":"Ο","Π":"Π","Ρ":"Ρ","Σ":"Σ","Τ":"Τ","Υ":"Υ","Φ":"Φ","Χ":"Χ","Ψ":"Ψ","Ω":"Ω","α":"α","β":"β","γ":"γ","δ":"δ","ε":"ε","ζ":"ζ","η":"η","θ":"θ","ι":"ι","κ":"κ","λ":"λ","μ":"μ","ν":"ν","ξ":"ξ","ο":"ο","π":"π","ρ":"ρ","ς":"ς","σ":"σ","τ":"τ","υ":"υ","φ":"φ","χ":"χ","ψ":"ψ","ω":"ω","ϑ":"ϑ","ϒ":"ϒ","ϖ":"ϖ","•":"•","…":"…","′":"′","″":"″","‾":"‾","⁄":"⁄","℘":"℘","ℑ":"ℑ","ℜ":"ℜ","™":"™","ℵ":"ℵ","←":"←","↑":"↑","→":"→","↓":"↓","↔":"↔","↵":"↵","⇐":"⇐","⇑":"⇑","⇒":"⇒","⇓":"⇓","⇔":"⇔","∀":"∀","∂":"∂","∃":"∃","∅":"∅","∇":"∇","∈":"∈","∉":"∉","∋":"∋","∏":"∏","∑":"∑","−":"−","∗":"∗","√":"√","∝":"∝","∞":"∞","∠":"∠","∧":"∧","∨":"∨","∩":"∩","∪":"∪","∫":"∫","∴":"∴","∼":"∼","≅":"≅","≈":"≈","≠":"≠","≡":"≡","≤":"≤","≥":"≥","⊂":"⊂","⊃":"⊃","⊄":"⊄","⊆":"⊆","⊇":"⊇","⊕":"⊕","⊗":"⊗","⊥":"⊥","⋅":"⋅","⌈":"⌈","⌉":"⌉","⌊":"⌊","⌋":"⌋","〈":"⟨","〉":"⟩","◊":"◊","♠":"♠","♣":"♣","♥":"♥","♦":"♦"}},html5:{entities:{"Æ":"Æ","Æ":"Æ","&":"&","&":"&","Á":"Á","Á":"Á","Ă":"Ă","Â":"Â","Â":"Â","А":"А","𝔄":"𝔄","À":"À","À":"À","Α":"Α","Ā":"Ā","⩓":"⩓","Ą":"Ą","𝔸":"𝔸","⁡":"⁡","Å":"Å","Å":"Å","𝒜":"𝒜","≔":"≔","Ã":"Ã","Ã":"Ã","Ä":"Ä","Ä":"Ä","∖":"∖","⫧":"⫧","⌆":"⌆","Б":"Б","∵":"∵","ℬ":"ℬ","Β":"Β","𝔅":"𝔅","𝔹":"𝔹","˘":"˘","ℬ":"ℬ","≎":"≎","Ч":"Ч","©":"©","©":"©","Ć":"Ć","⋒":"⋒","ⅅ":"ⅅ","ℭ":"ℭ","Č":"Č","Ç":"Ç","Ç":"Ç","Ĉ":"Ĉ","∰":"∰","Ċ":"Ċ","¸":"¸","·":"·","ℭ":"ℭ","Χ":"Χ","⊙":"⊙","⊖":"⊖","⊕":"⊕","⊗":"⊗","∲":"∲","”":"”","’":"’","∷":"∷","⩴":"⩴","≡":"≡","∯":"∯","∮":"∮","ℂ":"ℂ","∐":"∐","∳":"∳","⨯":"⨯","𝒞":"𝒞","⋓":"⋓","≍":"≍","ⅅ":"ⅅ","⤑":"⤑","Ђ":"Ђ","Ѕ":"Ѕ","Џ":"Џ","‡":"‡","↡":"↡","⫤":"⫤","Ď":"Ď","Д":"Д","∇":"∇","Δ":"Δ","𝔇":"𝔇","´":"´","˙":"˙","˝":"˝","`":"`","˜":"˜","⋄":"⋄","ⅆ":"ⅆ","𝔻":"𝔻","¨":"¨","⃜":"⃜","≐":"≐","∯":"∯","¨":"¨","⇓":"⇓","⇐":"⇐","⇔":"⇔","⫤":"⫤","⟸":"⟸","⟺":"⟺","⟹":"⟹","⇒":"⇒","⊨":"⊨","⇑":"⇑","⇕":"⇕","∥":"∥","↓":"↓","⤓":"⤓","⇵":"⇵","̑":"̑","⥐":"⥐","⥞":"⥞","↽":"↽","⥖":"⥖","⥟":"⥟","⇁":"⇁","⥗":"⥗","⊤":"⊤","↧":"↧","⇓":"⇓","𝒟":"𝒟","Đ":"Đ","Ŋ":"Ŋ","Ð":"Ð","Ð":"Ð","É":"É","É":"É","Ě":"Ě","Ê":"Ê","Ê":"Ê","Э":"Э","Ė":"Ė","𝔈":"𝔈","È":"È","È":"È","∈":"∈","Ē":"Ē","◻":"◻","▫":"▫","Ę":"Ę","𝔼":"𝔼","Ε":"Ε","⩵":"⩵","≂":"≂","⇌":"⇌","ℰ":"ℰ","⩳":"⩳","Η":"Η","Ë":"Ë","Ë":"Ë","∃":"∃","ⅇ":"ⅇ","Ф":"Ф","𝔉":"𝔉","◼":"◼","▪":"▪","𝔽":"𝔽","∀":"∀","ℱ":"ℱ","ℱ":"ℱ","Ѓ":"Ѓ",">":">",">":">","Γ":"Γ","Ϝ":"Ϝ","Ğ":"Ğ","Ģ":"Ģ","Ĝ":"Ĝ","Г":"Г","Ġ":"Ġ","𝔊":"𝔊","⋙":"⋙","𝔾":"𝔾","≥":"≥","⋛":"⋛","≧":"≧","⪢":"⪢","≷":"≷","⩾":"⩾","≳":"≳","𝒢":"𝒢","≫":"≫","Ъ":"Ъ","ˇ":"ˇ","^":"^","Ĥ":"Ĥ","ℌ":"ℌ","ℋ":"ℋ","ℍ":"ℍ","─":"─","ℋ":"ℋ","Ħ":"Ħ","≎":"≎","≏":"≏","Е":"Е","IJ":"IJ","Ё":"Ё","Í":"Í","Í":"Í","Î":"Î","Î":"Î","И":"И","İ":"İ","ℑ":"ℑ","Ì":"Ì","Ì":"Ì","ℑ":"ℑ","Ī":"Ī","ⅈ":"ⅈ","⇒":"⇒","∬":"∬","∫":"∫","⋂":"⋂","⁣":"⁣","⁢":"⁢","Į":"Į","𝕀":"𝕀","Ι":"Ι","ℐ":"ℐ","Ĩ":"Ĩ","І":"І","Ï":"Ï","Ï":"Ï","Ĵ":"Ĵ","Й":"Й","𝔍":"𝔍","𝕁":"𝕁","𝒥":"𝒥","Ј":"Ј","Є":"Є","Х":"Х","Ќ":"Ќ","Κ":"Κ","Ķ":"Ķ","К":"К","𝔎":"𝔎","𝕂":"𝕂","𝒦":"𝒦","Љ":"Љ","<":"<","<":"<","Ĺ":"Ĺ","Λ":"Λ","⟪":"⟪","ℒ":"ℒ","↞":"↞","Ľ":"Ľ","Ļ":"Ļ","Л":"Л","⟨":"⟨","←":"←","⇤":"⇤","⇆":"⇆","⌈":"⌈","⟦":"⟦","⥡":"⥡","⇃":"⇃","⥙":"⥙","⌊":"⌊","↔":"↔","⥎":"⥎","⊣":"⊣","↤":"↤","⥚":"⥚","⊲":"⊲","⧏":"⧏","⊴":"⊴","⥑":"⥑","⥠":"⥠","↿":"↿","⥘":"⥘","↼":"↼","⥒":"⥒","⇐":"⇐","⇔":"⇔","⋚":"⋚","≦":"≦","≶":"≶","⪡":"⪡","⩽":"⩽","≲":"≲","𝔏":"𝔏","⋘":"⋘","⇚":"⇚","Ŀ":"Ŀ","⟵":"⟵","⟷":"⟷","⟶":"⟶","⟸":"⟸","⟺":"⟺","⟹":"⟹","𝕃":"𝕃","↙":"↙","↘":"↘","ℒ":"ℒ","↰":"↰","Ł":"Ł","≪":"≪","⤅":"⤅","М":"М"," ":" ","ℳ":"ℳ","𝔐":"𝔐","∓":"∓","𝕄":"𝕄","ℳ":"ℳ","Μ":"Μ","Њ":"Њ","Ń":"Ń","Ň":"Ň","Ņ":"Ņ","Н":"Н","​":"​","​":"​","​":"​","​":"​","≫":"≫","≪":"≪"," ":"\n","𝔑":"𝔑","⁠":"⁠"," ":" ","ℕ":"ℕ","⫬":"⫬","≢":"≢","≭":"≭","∦":"∦","∉":"∉","≠":"≠","≂̸":"≂̸","∄":"∄","≯":"≯","≱":"≱","≧̸":"≧̸","≫̸":"≫̸","≹":"≹","⩾̸":"⩾̸","≵":"≵","≎̸":"≎̸","≏̸":"≏̸","⋪":"⋪","⧏̸":"⧏̸","⋬":"⋬","≮":"≮","≰":"≰","≸":"≸","≪̸":"≪̸","⩽̸":"⩽̸","≴":"≴","⪢̸":"⪢̸","⪡̸":"⪡̸","⊀":"⊀","⪯̸":"⪯̸","⋠":"⋠","∌":"∌","⋫":"⋫","⧐̸":"⧐̸","⋭":"⋭","⊏̸":"⊏̸","⋢":"⋢","⊐̸":"⊐̸","⋣":"⋣","⊂⃒":"⊂⃒","⊈":"⊈","⊁":"⊁","⪰̸":"⪰̸","⋡":"⋡","≿̸":"≿̸","⊃⃒":"⊃⃒","⊉":"⊉","≁":"≁","≄":"≄","≇":"≇","≉":"≉","∤":"∤","𝒩":"𝒩","Ñ":"Ñ","Ñ":"Ñ","Ν":"Ν","Œ":"Œ","Ó":"Ó","Ó":"Ó","Ô":"Ô","Ô":"Ô","О":"О","Ő":"Ő","𝔒":"𝔒","Ò":"Ò","Ò":"Ò","Ō":"Ō","Ω":"Ω","Ο":"Ο","𝕆":"𝕆","“":"“","‘":"‘","⩔":"⩔","𝒪":"𝒪","Ø":"Ø","Ø":"Ø","Õ":"Õ","Õ":"Õ","⨷":"⨷","Ö":"Ö","Ö":"Ö","‾":"‾","⏞":"⏞","⎴":"⎴","⏜":"⏜","∂":"∂","П":"П","𝔓":"𝔓","Φ":"Φ","Π":"Π","±":"±","ℌ":"ℌ","ℙ":"ℙ","⪻":"⪻","≺":"≺","⪯":"⪯","≼":"≼","≾":"≾","″":"″","∏":"∏","∷":"∷","∝":"∝","𝒫":"𝒫","Ψ":"Ψ",""":'"',""":'"',"𝔔":"𝔔","ℚ":"ℚ","𝒬":"𝒬","⤐":"⤐","®":"®","®":"®","Ŕ":"Ŕ","⟫":"⟫","↠":"↠","⤖":"⤖","Ř":"Ř","Ŗ":"Ŗ","Р":"Р","ℜ":"ℜ","∋":"∋","⇋":"⇋","⥯":"⥯","ℜ":"ℜ","Ρ":"Ρ","⟩":"⟩","→":"→","⇥":"⇥","⇄":"⇄","⌉":"⌉","⟧":"⟧","⥝":"⥝","⇂":"⇂","⥕":"⥕","⌋":"⌋","⊢":"⊢","↦":"↦","⥛":"⥛","⊳":"⊳","⧐":"⧐","⊵":"⊵","⥏":"⥏","⥜":"⥜","↾":"↾","⥔":"⥔","⇀":"⇀","⥓":"⥓","⇒":"⇒","ℝ":"ℝ","⥰":"⥰","⇛":"⇛","ℛ":"ℛ","↱":"↱","⧴":"⧴","Щ":"Щ","Ш":"Ш","Ь":"Ь","Ś":"Ś","⪼":"⪼","Š":"Š","Ş":"Ş","Ŝ":"Ŝ","С":"С","𝔖":"𝔖","↓":"↓","←":"←","→":"→","↑":"↑","Σ":"Σ","∘":"∘","𝕊":"𝕊","√":"√","□":"□","⊓":"⊓","⊏":"⊏","⊑":"⊑","⊐":"⊐","⊒":"⊒","⊔":"⊔","𝒮":"𝒮","⋆":"⋆","⋐":"⋐","⋐":"⋐","⊆":"⊆","≻":"≻","⪰":"⪰","≽":"≽","≿":"≿","∋":"∋","∑":"∑","⋑":"⋑","⊃":"⊃","⊇":"⊇","⋑":"⋑","Þ":"Þ","Þ":"Þ","™":"™","Ћ":"Ћ","Ц":"Ц"," ":"\t","Τ":"Τ","Ť":"Ť","Ţ":"Ţ","Т":"Т","𝔗":"𝔗","∴":"∴","Θ":"Θ","  ":"  "," ":" ","∼":"∼","≃":"≃","≅":"≅","≈":"≈","𝕋":"𝕋","⃛":"⃛","𝒯":"𝒯","Ŧ":"Ŧ","Ú":"Ú","Ú":"Ú","↟":"↟","⥉":"⥉","Ў":"Ў","Ŭ":"Ŭ","Û":"Û","Û":"Û","У":"У","Ű":"Ű","𝔘":"𝔘","Ù":"Ù","Ù":"Ù","Ū":"Ū","_":"_","⏟":"⏟","⎵":"⎵","⏝":"⏝","⋃":"⋃","⊎":"⊎","Ų":"Ų","𝕌":"𝕌","↑":"↑","⤒":"⤒","⇅":"⇅","↕":"↕","⥮":"⥮","⊥":"⊥","↥":"↥","⇑":"⇑","⇕":"⇕","↖":"↖","↗":"↗","ϒ":"ϒ","Υ":"Υ","Ů":"Ů","𝒰":"𝒰","Ũ":"Ũ","Ü":"Ü","Ü":"Ü","⊫":"⊫","⫫":"⫫","В":"В","⊩":"⊩","⫦":"⫦","⋁":"⋁","‖":"‖","‖":"‖","∣":"∣","|":"|","❘":"❘","≀":"≀"," ":" ","𝔙":"𝔙","𝕍":"𝕍","𝒱":"𝒱","⊪":"⊪","Ŵ":"Ŵ","⋀":"⋀","𝔚":"𝔚","𝕎":"𝕎","𝒲":"𝒲","𝔛":"𝔛","Ξ":"Ξ","𝕏":"𝕏","𝒳":"𝒳","Я":"Я","Ї":"Ї","Ю":"Ю","Ý":"Ý","Ý":"Ý","Ŷ":"Ŷ","Ы":"Ы","𝔜":"𝔜","𝕐":"𝕐","𝒴":"𝒴","Ÿ":"Ÿ","Ж":"Ж","Ź":"Ź","Ž":"Ž","З":"З","Ż":"Ż","​":"​","Ζ":"Ζ","ℨ":"ℨ","ℤ":"ℤ","𝒵":"𝒵","á":"á","á":"á","ă":"ă","∾":"∾","∾̳":"∾̳","∿":"∿","â":"â","â":"â","´":"´","´":"´","а":"а","æ":"æ","æ":"æ","⁡":"⁡","𝔞":"𝔞","à":"à","à":"à","ℵ":"ℵ","ℵ":"ℵ","α":"α","ā":"ā","⨿":"⨿","&":"&","&":"&","∧":"∧","⩕":"⩕","⩜":"⩜","⩘":"⩘","⩚":"⩚","∠":"∠","⦤":"⦤","∠":"∠","∡":"∡","⦨":"⦨","⦩":"⦩","⦪":"⦪","⦫":"⦫","⦬":"⦬","⦭":"⦭","⦮":"⦮","⦯":"⦯","∟":"∟","⊾":"⊾","⦝":"⦝","∢":"∢","Å":"Å","⍼":"⍼","ą":"ą","𝕒":"𝕒","≈":"≈","⩰":"⩰","⩯":"⩯","≊":"≊","≋":"≋","'":"'","≈":"≈","≊":"≊","å":"å","å":"å","𝒶":"𝒶","*":"*","≈":"≈","≍":"≍","ã":"ã","ã":"ã","ä":"ä","ä":"ä","∳":"∳","⨑":"⨑","⫭":"⫭","≌":"≌","϶":"϶","‵":"‵","∽":"∽","⋍":"⋍","⊽":"⊽","⌅":"⌅","⌅":"⌅","⎵":"⎵","⎶":"⎶","≌":"≌","б":"б","„":"„","∵":"∵","∵":"∵","⦰":"⦰","϶":"϶","ℬ":"ℬ","β":"β","ℶ":"ℶ","≬":"≬","𝔟":"𝔟","⋂":"⋂","◯":"◯","⋃":"⋃","⨀":"⨀","⨁":"⨁","⨂":"⨂","⨆":"⨆","★":"★","▽":"▽","△":"△","⨄":"⨄","⋁":"⋁","⋀":"⋀","⤍":"⤍","⧫":"⧫","▪":"▪","▴":"▴","▾":"▾","◂":"◂","▸":"▸","␣":"␣","▒":"▒","░":"░","▓":"▓","█":"█","=⃥":"=⃥","≡⃥":"≡⃥","⌐":"⌐","𝕓":"𝕓","⊥":"⊥","⊥":"⊥","⋈":"⋈","╗":"╗","╔":"╔","╖":"╖","╓":"╓","═":"═","╦":"╦","╩":"╩","╤":"╤","╧":"╧","╝":"╝","╚":"╚","╜":"╜","╙":"╙","║":"║","╬":"╬","╣":"╣","╠":"╠","╫":"╫","╢":"╢","╟":"╟","⧉":"⧉","╕":"╕","╒":"╒","┐":"┐","┌":"┌","─":"─","╥":"╥","╨":"╨","┬":"┬","┴":"┴","⊟":"⊟","⊞":"⊞","⊠":"⊠","╛":"╛","╘":"╘","┘":"┘","└":"└","│":"│","╪":"╪","╡":"╡","╞":"╞","┼":"┼","┤":"┤","├":"├","‵":"‵","˘":"˘","¦":"¦","¦":"¦","𝒷":"𝒷","⁏":"⁏","∽":"∽","⋍":"⋍","\":"\\","⧅":"⧅","⟈":"⟈","•":"•","•":"•","≎":"≎","⪮":"⪮","≏":"≏","≏":"≏","ć":"ć","∩":"∩","⩄":"⩄","⩉":"⩉","⩋":"⩋","⩇":"⩇","⩀":"⩀","∩︀":"∩︀","⁁":"⁁","ˇ":"ˇ","⩍":"⩍","č":"č","ç":"ç","ç":"ç","ĉ":"ĉ","⩌":"⩌","⩐":"⩐","ċ":"ċ","¸":"¸","¸":"¸","⦲":"⦲","¢":"¢","¢":"¢","·":"·","𝔠":"𝔠","ч":"ч","✓":"✓","✓":"✓","χ":"χ","○":"○","⧃":"⧃","ˆ":"ˆ","≗":"≗","↺":"↺","↻":"↻","®":"®","Ⓢ":"Ⓢ","⊛":"⊛","⊚":"⊚","⊝":"⊝","≗":"≗","⨐":"⨐","⫯":"⫯","⧂":"⧂","♣":"♣","♣":"♣",":":":","≔":"≔","≔":"≔",",":",","@":"@","∁":"∁","∘":"∘","∁":"∁","ℂ":"ℂ","≅":"≅","⩭":"⩭","∮":"∮","𝕔":"𝕔","∐":"∐","©":"©","©":"©","℗":"℗","↵":"↵","✗":"✗","𝒸":"𝒸","⫏":"⫏","⫑":"⫑","⫐":"⫐","⫒":"⫒","⋯":"⋯","⤸":"⤸","⤵":"⤵","⋞":"⋞","⋟":"⋟","↶":"↶","⤽":"⤽","∪":"∪","⩈":"⩈","⩆":"⩆","⩊":"⩊","⊍":"⊍","⩅":"⩅","∪︀":"∪︀","↷":"↷","⤼":"⤼","⋞":"⋞","⋟":"⋟","⋎":"⋎","⋏":"⋏","¤":"¤","¤":"¤","↶":"↶","↷":"↷","⋎":"⋎","⋏":"⋏","∲":"∲","∱":"∱","⌭":"⌭","⇓":"⇓","⥥":"⥥","†":"†","ℸ":"ℸ","↓":"↓","‐":"‐","⊣":"⊣","⤏":"⤏","˝":"˝","ď":"ď","д":"д","ⅆ":"ⅆ","‡":"‡","⇊":"⇊","⩷":"⩷","°":"°","°":"°","δ":"δ","⦱":"⦱","⥿":"⥿","𝔡":"𝔡","⇃":"⇃","⇂":"⇂","⋄":"⋄","⋄":"⋄","♦":"♦","♦":"♦","¨":"¨","ϝ":"ϝ","⋲":"⋲","÷":"÷","÷":"÷","÷":"÷","⋇":"⋇","⋇":"⋇","ђ":"ђ","⌞":"⌞","⌍":"⌍","$":"$","𝕕":"𝕕","˙":"˙","≐":"≐","≑":"≑","∸":"∸","∔":"∔","⊡":"⊡","⌆":"⌆","↓":"↓","⇊":"⇊","⇃":"⇃","⇂":"⇂","⤐":"⤐","⌟":"⌟","⌌":"⌌","𝒹":"𝒹","ѕ":"ѕ","⧶":"⧶","đ":"đ","⋱":"⋱","▿":"▿","▾":"▾","⇵":"⇵","⥯":"⥯","⦦":"⦦","џ":"џ","⟿":"⟿","⩷":"⩷","≑":"≑","é":"é","é":"é","⩮":"⩮","ě":"ě","≖":"≖","ê":"ê","ê":"ê","≕":"≕","э":"э","ė":"ė","ⅇ":"ⅇ","≒":"≒","𝔢":"𝔢","⪚":"⪚","è":"è","è":"è","⪖":"⪖","⪘":"⪘","⪙":"⪙","⏧":"⏧","ℓ":"ℓ","⪕":"⪕","⪗":"⪗","ē":"ē","∅":"∅","∅":"∅","∅":"∅"," ":" "," ":" "," ":" ","ŋ":"ŋ"," ":" ","ę":"ę","𝕖":"𝕖","⋕":"⋕","⧣":"⧣","⩱":"⩱","ε":"ε","ε":"ε","ϵ":"ϵ","≖":"≖","≕":"≕","≂":"≂","⪖":"⪖","⪕":"⪕","=":"=","≟":"≟","≡":"≡","⩸":"⩸","⧥":"⧥","≓":"≓","⥱":"⥱","ℯ":"ℯ","≐":"≐","≂":"≂","η":"η","ð":"ð","ð":"ð","ë":"ë","ë":"ë","€":"€","!":"!","∃":"∃","ℰ":"ℰ","ⅇ":"ⅇ","≒":"≒","ф":"ф","♀":"♀","ffi":"ffi","ff":"ff","ffl":"ffl","𝔣":"𝔣","fi":"fi","fj":"fj","♭":"♭","fl":"fl","▱":"▱","ƒ":"ƒ","𝕗":"𝕗","∀":"∀","⋔":"⋔","⫙":"⫙","⨍":"⨍","½":"½","½":"½","⅓":"⅓","¼":"¼","¼":"¼","⅕":"⅕","⅙":"⅙","⅛":"⅛","⅔":"⅔","⅖":"⅖","¾":"¾","¾":"¾","⅗":"⅗","⅜":"⅜","⅘":"⅘","⅚":"⅚","⅝":"⅝","⅞":"⅞","⁄":"⁄","⌢":"⌢","𝒻":"𝒻","≧":"≧","⪌":"⪌","ǵ":"ǵ","γ":"γ","ϝ":"ϝ","⪆":"⪆","ğ":"ğ","ĝ":"ĝ","г":"г","ġ":"ġ","≥":"≥","⋛":"⋛","≥":"≥","≧":"≧","⩾":"⩾","⩾":"⩾","⪩":"⪩","⪀":"⪀","⪂":"⪂","⪄":"⪄","⋛︀":"⋛︀","⪔":"⪔","𝔤":"𝔤","≫":"≫","⋙":"⋙","ℷ":"ℷ","ѓ":"ѓ","≷":"≷","⪒":"⪒","⪥":"⪥","⪤":"⪤","≩":"≩","⪊":"⪊","⪊":"⪊","⪈":"⪈","⪈":"⪈","≩":"≩","⋧":"⋧","𝕘":"𝕘","`":"`","ℊ":"ℊ","≳":"≳","⪎":"⪎","⪐":"⪐",">":">",">":">","⪧":"⪧","⩺":"⩺","⋗":"⋗","⦕":"⦕","⩼":"⩼","⪆":"⪆","⥸":"⥸","⋗":"⋗","⋛":"⋛","⪌":"⪌","≷":"≷","≳":"≳","≩︀":"≩︀","≩︀":"≩︀","⇔":"⇔"," ":" ","½":"½","ℋ":"ℋ","ъ":"ъ","↔":"↔","⥈":"⥈","↭":"↭","ℏ":"ℏ","ĥ":"ĥ","♥":"♥","♥":"♥","…":"…","⊹":"⊹","𝔥":"𝔥","⤥":"⤥","⤦":"⤦","⇿":"⇿","∻":"∻","↩":"↩","↪":"↪","𝕙":"𝕙","―":"―","𝒽":"𝒽","ℏ":"ℏ","ħ":"ħ","⁃":"⁃","‐":"‐","í":"í","í":"í","⁣":"⁣","î":"î","î":"î","и":"и","е":"е","¡":"¡","¡":"¡","⇔":"⇔","𝔦":"𝔦","ì":"ì","ì":"ì","ⅈ":"ⅈ","⨌":"⨌","∭":"∭","⧜":"⧜","℩":"℩","ij":"ij","ī":"ī","ℑ":"ℑ","ℐ":"ℐ","ℑ":"ℑ","ı":"ı","⊷":"⊷","Ƶ":"Ƶ","∈":"∈","℅":"℅","∞":"∞","⧝":"⧝","ı":"ı","∫":"∫","⊺":"⊺","ℤ":"ℤ","⊺":"⊺","⨗":"⨗","⨼":"⨼","ё":"ё","į":"į","𝕚":"𝕚","ι":"ι","⨼":"⨼","¿":"¿","¿":"¿","𝒾":"𝒾","∈":"∈","⋹":"⋹","⋵":"⋵","⋴":"⋴","⋳":"⋳","∈":"∈","⁢":"⁢","ĩ":"ĩ","і":"і","ï":"ï","ï":"ï","ĵ":"ĵ","й":"й","𝔧":"𝔧","ȷ":"ȷ","𝕛":"𝕛","𝒿":"𝒿","ј":"ј","є":"є","κ":"κ","ϰ":"ϰ","ķ":"ķ","к":"к","𝔨":"𝔨","ĸ":"ĸ","х":"х","ќ":"ќ","𝕜":"𝕜","𝓀":"𝓀","⇚":"⇚","⇐":"⇐","⤛":"⤛","⤎":"⤎","≦":"≦","⪋":"⪋","⥢":"⥢","ĺ":"ĺ","⦴":"⦴","ℒ":"ℒ","λ":"λ","⟨":"⟨","⦑":"⦑","⟨":"⟨","⪅":"⪅","«":"«","«":"«","←":"←","⇤":"⇤","⤟":"⤟","⤝":"⤝","↩":"↩","↫":"↫","⤹":"⤹","⥳":"⥳","↢":"↢","⪫":"⪫","⤙":"⤙","⪭":"⪭","⪭︀":"⪭︀","⤌":"⤌","❲":"❲","{":"{","[":"[","⦋":"⦋","⦏":"⦏","⦍":"⦍","ľ":"ľ","ļ":"ļ","⌈":"⌈","{":"{","л":"л","⤶":"⤶","“":"“","„":"„","⥧":"⥧","⥋":"⥋","↲":"↲","≤":"≤","←":"←","↢":"↢","↽":"↽","↼":"↼","⇇":"⇇","↔":"↔","⇆":"⇆","⇋":"⇋","↭":"↭","⋋":"⋋","⋚":"⋚","≤":"≤","≦":"≦","⩽":"⩽","⩽":"⩽","⪨":"⪨","⩿":"⩿","⪁":"⪁","⪃":"⪃","⋚︀":"⋚︀","⪓":"⪓","⪅":"⪅","⋖":"⋖","⋚":"⋚","⪋":"⪋","≶":"≶","≲":"≲","⥼":"⥼","⌊":"⌊","𝔩":"𝔩","≶":"≶","⪑":"⪑","↽":"↽","↼":"↼","⥪":"⥪","▄":"▄","љ":"љ","≪":"≪","⇇":"⇇","⌞":"⌞","⥫":"⥫","◺":"◺","ŀ":"ŀ","⎰":"⎰","⎰":"⎰","≨":"≨","⪉":"⪉","⪉":"⪉","⪇":"⪇","⪇":"⪇","≨":"≨","⋦":"⋦","⟬":"⟬","⇽":"⇽","⟦":"⟦","⟵":"⟵","⟷":"⟷","⟼":"⟼","⟶":"⟶","↫":"↫","↬":"↬","⦅":"⦅","𝕝":"𝕝","⨭":"⨭","⨴":"⨴","∗":"∗","_":"_","◊":"◊","◊":"◊","⧫":"⧫","(":"(","⦓":"⦓","⇆":"⇆","⌟":"⌟","⇋":"⇋","⥭":"⥭","‎":"‎","⊿":"⊿","‹":"‹","𝓁":"𝓁","↰":"↰","≲":"≲","⪍":"⪍","⪏":"⪏","[":"[","‘":"‘","‚":"‚","ł":"ł","<":"<","<":"<","⪦":"⪦","⩹":"⩹","⋖":"⋖","⋋":"⋋","⋉":"⋉","⥶":"⥶","⩻":"⩻","⦖":"⦖","◃":"◃","⊴":"⊴","◂":"◂","⥊":"⥊","⥦":"⥦","≨︀":"≨︀","≨︀":"≨︀","∺":"∺","¯":"¯","¯":"¯","♂":"♂","✠":"✠","✠":"✠","↦":"↦","↦":"↦","↧":"↧","↤":"↤","↥":"↥","▮":"▮","⨩":"⨩","м":"м","—":"—","∡":"∡","𝔪":"𝔪","℧":"℧","µ":"µ","µ":"µ","∣":"∣","*":"*","⫰":"⫰","·":"·","·":"·","−":"−","⊟":"⊟","∸":"∸","⨪":"⨪","⫛":"⫛","…":"…","∓":"∓","⊧":"⊧","𝕞":"𝕞","∓":"∓","𝓂":"𝓂","∾":"∾","μ":"μ","⊸":"⊸","⊸":"⊸","⋙̸":"⋙̸","≫⃒":"≫⃒","≫̸":"≫̸","⇍":"⇍","⇎":"⇎","⋘̸":"⋘̸","≪⃒":"≪⃒","≪̸":"≪̸","⇏":"⇏","⊯":"⊯","⊮":"⊮","∇":"∇","ń":"ń","∠⃒":"∠⃒","≉":"≉","⩰̸":"⩰̸","≋̸":"≋̸","ʼn":"ʼn","≉":"≉","♮":"♮","♮":"♮","ℕ":"ℕ"," ":" "," ":" ","≎̸":"≎̸","≏̸":"≏̸","⩃":"⩃","ň":"ň","ņ":"ņ","≇":"≇","⩭̸":"⩭̸","⩂":"⩂","н":"н","–":"–","≠":"≠","⇗":"⇗","⤤":"⤤","↗":"↗","↗":"↗","≐̸":"≐̸","≢":"≢","⤨":"⤨","≂̸":"≂̸","∄":"∄","∄":"∄","𝔫":"𝔫","≧̸":"≧̸","≱":"≱","≱":"≱","≧̸":"≧̸","⩾̸":"⩾̸","⩾̸":"⩾̸","≵":"≵","≯":"≯","≯":"≯","⇎":"⇎","↮":"↮","⫲":"⫲","∋":"∋","⋼":"⋼","⋺":"⋺","∋":"∋","њ":"њ","⇍":"⇍","≦̸":"≦̸","↚":"↚","‥":"‥","≰":"≰","↚":"↚","↮":"↮","≰":"≰","≦̸":"≦̸","⩽̸":"⩽̸","⩽̸":"⩽̸","≮":"≮","≴":"≴","≮":"≮","⋪":"⋪","⋬":"⋬","∤":"∤","𝕟":"𝕟","¬":"¬","¬":"¬","∉":"∉","⋹̸":"⋹̸","⋵̸":"⋵̸","∉":"∉","⋷":"⋷","⋶":"⋶","∌":"∌","∌":"∌","⋾":"⋾","⋽":"⋽","∦":"∦","∦":"∦","⫽⃥":"⫽⃥","∂̸":"∂̸","⨔":"⨔","⊀":"⊀","⋠":"⋠","⪯̸":"⪯̸","⊀":"⊀","⪯̸":"⪯̸","⇏":"⇏","↛":"↛","⤳̸":"⤳̸","↝̸":"↝̸","↛":"↛","⋫":"⋫","⋭":"⋭","⊁":"⊁","⋡":"⋡","⪰̸":"⪰̸","𝓃":"𝓃","∤":"∤","∦":"∦","≁":"≁","≄":"≄","≄":"≄","∤":"∤","∦":"∦","⋢":"⋢","⋣":"⋣","⊄":"⊄","⫅̸":"⫅̸","⊈":"⊈","⊂⃒":"⊂⃒","⊈":"⊈","⫅̸":"⫅̸","⊁":"⊁","⪰̸":"⪰̸","⊅":"⊅","⫆̸":"⫆̸","⊉":"⊉","⊃⃒":"⊃⃒","⊉":"⊉","⫆̸":"⫆̸","≹":"≹","ñ":"ñ","ñ":"ñ","≸":"≸","⋪":"⋪","⋬":"⋬","⋫":"⋫","⋭":"⋭","ν":"ν","#":"#","№":"№"," ":" ","⊭":"⊭","⤄":"⤄","≍⃒":"≍⃒","⊬":"⊬","≥⃒":"≥⃒",">⃒":">⃒","⧞":"⧞","⤂":"⤂","≤⃒":"≤⃒","<⃒":"<⃒","⊴⃒":"⊴⃒","⤃":"⤃","⊵⃒":"⊵⃒","∼⃒":"∼⃒","⇖":"⇖","⤣":"⤣","↖":"↖","↖":"↖","⤧":"⤧","Ⓢ":"Ⓢ","ó":"ó","ó":"ó","⊛":"⊛","⊚":"⊚","ô":"ô","ô":"ô","о":"о","⊝":"⊝","ő":"ő","⨸":"⨸","⊙":"⊙","⦼":"⦼","œ":"œ","⦿":"⦿","𝔬":"𝔬","˛":"˛","ò":"ò","ò":"ò","⧁":"⧁","⦵":"⦵","Ω":"Ω","∮":"∮","↺":"↺","⦾":"⦾","⦻":"⦻","‾":"‾","⧀":"⧀","ō":"ō","ω":"ω","ο":"ο","⦶":"⦶","⊖":"⊖","𝕠":"𝕠","⦷":"⦷","⦹":"⦹","⊕":"⊕","∨":"∨","↻":"↻","⩝":"⩝","ℴ":"ℴ","ℴ":"ℴ","ª":"ª","ª":"ª","º":"º","º":"º","⊶":"⊶","⩖":"⩖","⩗":"⩗","⩛":"⩛","ℴ":"ℴ","ø":"ø","ø":"ø","⊘":"⊘","õ":"õ","õ":"õ","⊗":"⊗","⨶":"⨶","ö":"ö","ö":"ö","⌽":"⌽","∥":"∥","¶":"¶","¶":"¶","∥":"∥","⫳":"⫳","⫽":"⫽","∂":"∂","п":"п","%":"%",".":".","‰":"‰","⊥":"⊥","‱":"‱","𝔭":"𝔭","φ":"φ","ϕ":"ϕ","ℳ":"ℳ","☎":"☎","π":"π","⋔":"⋔","ϖ":"ϖ","ℏ":"ℏ","ℎ":"ℎ","ℏ":"ℏ","+":"+","⨣":"⨣","⊞":"⊞","⨢":"⨢","∔":"∔","⨥":"⨥","⩲":"⩲","±":"±","±":"±","⨦":"⨦","⨧":"⨧","±":"±","⨕":"⨕","𝕡":"𝕡","£":"£","£":"£","≺":"≺","⪳":"⪳","⪷":"⪷","≼":"≼","⪯":"⪯","≺":"≺","⪷":"⪷","≼":"≼","⪯":"⪯","⪹":"⪹","⪵":"⪵","⋨":"⋨","≾":"≾","′":"′","ℙ":"ℙ","⪵":"⪵","⪹":"⪹","⋨":"⋨","∏":"∏","⌮":"⌮","⌒":"⌒","⌓":"⌓","∝":"∝","∝":"∝","≾":"≾","⊰":"⊰","𝓅":"𝓅","ψ":"ψ"," ":" ","𝔮":"𝔮","⨌":"⨌","𝕢":"𝕢","⁗":"⁗","𝓆":"𝓆","ℍ":"ℍ","⨖":"⨖","?":"?","≟":"≟",""":'"',""":'"',"⇛":"⇛","⇒":"⇒","⤜":"⤜","⤏":"⤏","⥤":"⥤","∽̱":"∽̱","ŕ":"ŕ","√":"√","⦳":"⦳","⟩":"⟩","⦒":"⦒","⦥":"⦥","⟩":"⟩","»":"»","»":"»","→":"→","⥵":"⥵","⇥":"⇥","⤠":"⤠","⤳":"⤳","⤞":"⤞","↪":"↪","↬":"↬","⥅":"⥅","⥴":"⥴","↣":"↣","↝":"↝","⤚":"⤚","∶":"∶","ℚ":"ℚ","⤍":"⤍","❳":"❳","}":"}","]":"]","⦌":"⦌","⦎":"⦎","⦐":"⦐","ř":"ř","ŗ":"ŗ","⌉":"⌉","}":"}","р":"р","⤷":"⤷","⥩":"⥩","”":"”","”":"”","↳":"↳","ℜ":"ℜ","ℛ":"ℛ","ℜ":"ℜ","ℝ":"ℝ","▭":"▭","®":"®","®":"®","⥽":"⥽","⌋":"⌋","𝔯":"𝔯","⇁":"⇁","⇀":"⇀","⥬":"⥬","ρ":"ρ","ϱ":"ϱ","→":"→","↣":"↣","⇁":"⇁","⇀":"⇀","⇄":"⇄","⇌":"⇌","⇉":"⇉","↝":"↝","⋌":"⋌","˚":"˚","≓":"≓","⇄":"⇄","⇌":"⇌","‏":"‏","⎱":"⎱","⎱":"⎱","⫮":"⫮","⟭":"⟭","⇾":"⇾","⟧":"⟧","⦆":"⦆","𝕣":"𝕣","⨮":"⨮","⨵":"⨵",")":")","⦔":"⦔","⨒":"⨒","⇉":"⇉","›":"›","𝓇":"𝓇","↱":"↱","]":"]","’":"’","’":"’","⋌":"⋌","⋊":"⋊","▹":"▹","⊵":"⊵","▸":"▸","⧎":"⧎","⥨":"⥨","℞":"℞","ś":"ś","‚":"‚","≻":"≻","⪴":"⪴","⪸":"⪸","š":"š","≽":"≽","⪰":"⪰","ş":"ş","ŝ":"ŝ","⪶":"⪶","⪺":"⪺","⋩":"⋩","⨓":"⨓","≿":"≿","с":"с","⋅":"⋅","⊡":"⊡","⩦":"⩦","⇘":"⇘","⤥":"⤥","↘":"↘","↘":"↘","§":"§","§":"§",";":";","⤩":"⤩","∖":"∖","∖":"∖","✶":"✶","𝔰":"𝔰","⌢":"⌢","♯":"♯","щ":"щ","ш":"ш","∣":"∣","∥":"∥","­":"­","­":"­","σ":"σ","ς":"ς","ς":"ς","∼":"∼","⩪":"⩪","≃":"≃","≃":"≃","⪞":"⪞","⪠":"⪠","⪝":"⪝","⪟":"⪟","≆":"≆","⨤":"⨤","⥲":"⥲","←":"←","∖":"∖","⨳":"⨳","⧤":"⧤","∣":"∣","⌣":"⌣","⪪":"⪪","⪬":"⪬","⪬︀":"⪬︀","ь":"ь","/":"/","⧄":"⧄","⌿":"⌿","𝕤":"𝕤","♠":"♠","♠":"♠","∥":"∥","⊓":"⊓","⊓︀":"⊓︀","⊔":"⊔","⊔︀":"⊔︀","⊏":"⊏","⊑":"⊑","⊏":"⊏","⊑":"⊑","⊐":"⊐","⊒":"⊒","⊐":"⊐","⊒":"⊒","□":"□","□":"□","▪":"▪","▪":"▪","→":"→","𝓈":"𝓈","∖":"∖","⌣":"⌣","⋆":"⋆","☆":"☆","★":"★","ϵ":"ϵ","ϕ":"ϕ","¯":"¯","⊂":"⊂","⫅":"⫅","⪽":"⪽","⊆":"⊆","⫃":"⫃","⫁":"⫁","⫋":"⫋","⊊":"⊊","⪿":"⪿","⥹":"⥹","⊂":"⊂","⊆":"⊆","⫅":"⫅","⊊":"⊊","⫋":"⫋","⫇":"⫇","⫕":"⫕","⫓":"⫓","≻":"≻","⪸":"⪸","≽":"≽","⪰":"⪰","⪺":"⪺","⪶":"⪶","⋩":"⋩","≿":"≿","∑":"∑","♪":"♪","¹":"¹","¹":"¹","²":"²","²":"²","³":"³","³":"³","⊃":"⊃","⫆":"⫆","⪾":"⪾","⫘":"⫘","⊇":"⊇","⫄":"⫄","⟉":"⟉","⫗":"⫗","⥻":"⥻","⫂":"⫂","⫌":"⫌","⊋":"⊋","⫀":"⫀","⊃":"⊃","⊇":"⊇","⫆":"⫆","⊋":"⊋","⫌":"⫌","⫈":"⫈","⫔":"⫔","⫖":"⫖","⇙":"⇙","⤦":"⤦","↙":"↙","↙":"↙","⤪":"⤪","ß":"ß","ß":"ß","⌖":"⌖","τ":"τ","⎴":"⎴","ť":"ť","ţ":"ţ","т":"т","⃛":"⃛","⌕":"⌕","𝔱":"𝔱","∴":"∴","∴":"∴","θ":"θ","ϑ":"ϑ","ϑ":"ϑ","≈":"≈","∼":"∼"," ":" ","≈":"≈","∼":"∼","þ":"þ","þ":"þ","˜":"˜","×":"×","×":"×","⊠":"⊠","⨱":"⨱","⨰":"⨰","∭":"∭","⤨":"⤨","⊤":"⊤","⌶":"⌶","⫱":"⫱","𝕥":"𝕥","⫚":"⫚","⤩":"⤩","‴":"‴","™":"™","▵":"▵","▿":"▿","◃":"◃","⊴":"⊴","≜":"≜","▹":"▹","⊵":"⊵","◬":"◬","≜":"≜","⨺":"⨺","⨹":"⨹","⧍":"⧍","⨻":"⨻","⏢":"⏢","𝓉":"𝓉","ц":"ц","ћ":"ћ","ŧ":"ŧ","≬":"≬","↞":"↞","↠":"↠","⇑":"⇑","⥣":"⥣","ú":"ú","ú":"ú","↑":"↑","ў":"ў","ŭ":"ŭ","û":"û","û":"û","у":"у","⇅":"⇅","ű":"ű","⥮":"⥮","⥾":"⥾","𝔲":"𝔲","ù":"ù","ù":"ù","↿":"↿","↾":"↾","▀":"▀","⌜":"⌜","⌜":"⌜","⌏":"⌏","◸":"◸","ū":"ū","¨":"¨","¨":"¨","ų":"ų","𝕦":"𝕦","↑":"↑","↕":"↕","↿":"↿","↾":"↾","⊎":"⊎","υ":"υ","ϒ":"ϒ","υ":"υ","⇈":"⇈","⌝":"⌝","⌝":"⌝","⌎":"⌎","ů":"ů","◹":"◹","𝓊":"𝓊","⋰":"⋰","ũ":"ũ","▵":"▵","▴":"▴","⇈":"⇈","ü":"ü","ü":"ü","⦧":"⦧","⇕":"⇕","⫨":"⫨","⫩":"⫩","⊨":"⊨","⦜":"⦜","ϵ":"ϵ","ϰ":"ϰ","∅":"∅","ϕ":"ϕ","ϖ":"ϖ","∝":"∝","↕":"↕","ϱ":"ϱ","ς":"ς","⊊︀":"⊊︀","⫋︀":"⫋︀","⊋︀":"⊋︀","⫌︀":"⫌︀","ϑ":"ϑ","⊲":"⊲","⊳":"⊳","в":"в","⊢":"⊢","∨":"∨","⊻":"⊻","≚":"≚","⋮":"⋮","|":"|","|":"|","𝔳":"𝔳","⊲":"⊲","⊂⃒":"⊂⃒","⊃⃒":"⊃⃒","𝕧":"𝕧","∝":"∝","⊳":"⊳","𝓋":"𝓋","⫋︀":"⫋︀","⊊︀":"⊊︀","⫌︀":"⫌︀","⊋︀":"⊋︀","⦚":"⦚","ŵ":"ŵ","⩟":"⩟","∧":"∧","≙":"≙","℘":"℘","𝔴":"𝔴","𝕨":"𝕨","℘":"℘","≀":"≀","≀":"≀","𝓌":"𝓌","⋂":"⋂","◯":"◯","⋃":"⋃","▽":"▽","𝔵":"𝔵","⟺":"⟺","⟷":"⟷","ξ":"ξ","⟸":"⟸","⟵":"⟵","⟼":"⟼","⋻":"⋻","⨀":"⨀","𝕩":"𝕩","⨁":"⨁","⨂":"⨂","⟹":"⟹","⟶":"⟶","𝓍":"𝓍","⨆":"⨆","⨄":"⨄","△":"△","⋁":"⋁","⋀":"⋀","ý":"ý","ý":"ý","я":"я","ŷ":"ŷ","ы":"ы","¥":"¥","¥":"¥","𝔶":"𝔶","ї":"ї","𝕪":"𝕪","𝓎":"𝓎","ю":"ю","ÿ":"ÿ","ÿ":"ÿ","ź":"ź","ž":"ž","з":"з","ż":"ż","ℨ":"ℨ","ζ":"ζ","𝔷":"𝔷","ж":"ж","⇝":"⇝","𝕫":"𝕫","𝓏":"𝓏","‍":"‍","‌":"‌"},characters:{"Æ":"Æ","&":"&","Á":"Á","Ă":"Ă","Â":"Â","А":"А","𝔄":"𝔄","À":"À","Α":"Α","Ā":"Ā","⩓":"⩓","Ą":"Ą","𝔸":"𝔸","⁡":"⁡","Å":"Å","𝒜":"𝒜","≔":"≔","Ã":"Ã","Ä":"Ä","∖":"∖","⫧":"⫧","⌆":"⌆","Б":"Б","∵":"∵","ℬ":"ℬ","Β":"Β","𝔅":"𝔅","𝔹":"𝔹","˘":"˘","≎":"≎","Ч":"Ч","©":"©","Ć":"Ć","⋒":"⋒","ⅅ":"ⅅ","ℭ":"ℭ","Č":"Č","Ç":"Ç","Ĉ":"Ĉ","∰":"∰","Ċ":"Ċ","¸":"¸","·":"·","Χ":"Χ","⊙":"⊙","⊖":"⊖","⊕":"⊕","⊗":"⊗","∲":"∲","”":"”","’":"’","∷":"∷","⩴":"⩴","≡":"≡","∯":"∯","∮":"∮","ℂ":"ℂ","∐":"∐","∳":"∳","⨯":"⨯","𝒞":"𝒞","⋓":"⋓","≍":"≍","⤑":"⤑","Ђ":"Ђ","Ѕ":"Ѕ","Џ":"Џ","‡":"‡","↡":"↡","⫤":"⫤","Ď":"Ď","Д":"Д","∇":"∇","Δ":"Δ","𝔇":"𝔇","´":"´","˙":"˙","˝":"˝","`":"`","˜":"˜","⋄":"⋄","ⅆ":"ⅆ","𝔻":"𝔻","¨":"¨","⃜":"⃜","≐":"≐","⇓":"⇓","⇐":"⇐","⇔":"⇔","⟸":"⟸","⟺":"⟺","⟹":"⟹","⇒":"⇒","⊨":"⊨","⇑":"⇑","⇕":"⇕","∥":"∥","↓":"↓","⤓":"⤓","⇵":"⇵","̑":"̑","⥐":"⥐","⥞":"⥞","↽":"↽","⥖":"⥖","⥟":"⥟","⇁":"⇁","⥗":"⥗","⊤":"⊤","↧":"↧","𝒟":"𝒟","Đ":"Đ","Ŋ":"Ŋ","Ð":"Ð","É":"É","Ě":"Ě","Ê":"Ê","Э":"Э","Ė":"Ė","𝔈":"𝔈","È":"È","∈":"∈","Ē":"Ē","◻":"◻","▫":"▫","Ę":"Ę","𝔼":"𝔼","Ε":"Ε","⩵":"⩵","≂":"≂","⇌":"⇌","ℰ":"ℰ","⩳":"⩳","Η":"Η","Ë":"Ë","∃":"∃","ⅇ":"ⅇ","Ф":"Ф","𝔉":"𝔉","◼":"◼","▪":"▪","𝔽":"𝔽","∀":"∀","ℱ":"ℱ","Ѓ":"Ѓ",">":">","Γ":"Γ","Ϝ":"Ϝ","Ğ":"Ğ","Ģ":"Ģ","Ĝ":"Ĝ","Г":"Г","Ġ":"Ġ","𝔊":"𝔊","⋙":"⋙","𝔾":"𝔾","≥":"≥","⋛":"⋛","≧":"≧","⪢":"⪢","≷":"≷","⩾":"⩾","≳":"≳","𝒢":"𝒢","≫":"≫","Ъ":"Ъ","ˇ":"ˇ","^":"^","Ĥ":"Ĥ","ℌ":"ℌ","ℋ":"ℋ","ℍ":"ℍ","─":"─","Ħ":"Ħ","≏":"≏","Е":"Е","IJ":"IJ","Ё":"Ё","Í":"Í","Î":"Î","И":"И","İ":"İ","ℑ":"ℑ","Ì":"Ì","Ī":"Ī","ⅈ":"ⅈ","∬":"∬","∫":"∫","⋂":"⋂","⁣":"⁣","⁢":"⁢","Į":"Į","𝕀":"𝕀","Ι":"Ι","ℐ":"ℐ","Ĩ":"Ĩ","І":"І","Ï":"Ï","Ĵ":"Ĵ","Й":"Й","𝔍":"𝔍","𝕁":"𝕁","𝒥":"𝒥","Ј":"Ј","Є":"Є","Х":"Х","Ќ":"Ќ","Κ":"Κ","Ķ":"Ķ","К":"К","𝔎":"𝔎","𝕂":"𝕂","𝒦":"𝒦","Љ":"Љ","<":"<","Ĺ":"Ĺ","Λ":"Λ","⟪":"⟪","ℒ":"ℒ","↞":"↞","Ľ":"Ľ","Ļ":"Ļ","Л":"Л","⟨":"⟨","←":"←","⇤":"⇤","⇆":"⇆","⌈":"⌈","⟦":"⟦","⥡":"⥡","⇃":"⇃","⥙":"⥙","⌊":"⌊","↔":"↔","⥎":"⥎","⊣":"⊣","↤":"↤","⥚":"⥚","⊲":"⊲","⧏":"⧏","⊴":"⊴","⥑":"⥑","⥠":"⥠","↿":"↿","⥘":"⥘","↼":"↼","⥒":"⥒","⋚":"⋚","≦":"≦","≶":"≶","⪡":"⪡","⩽":"⩽","≲":"≲","𝔏":"𝔏","⋘":"⋘","⇚":"⇚","Ŀ":"Ŀ","⟵":"⟵","⟷":"⟷","⟶":"⟶","𝕃":"𝕃","↙":"↙","↘":"↘","↰":"↰","Ł":"Ł","≪":"≪","⤅":"⤅","М":"М"," ":" ","ℳ":"ℳ","𝔐":"𝔐","∓":"∓","𝕄":"𝕄","Μ":"Μ","Њ":"Њ","Ń":"Ń","Ň":"Ň","Ņ":"Ņ","Н":"Н","​":"​","\n":" ","𝔑":"𝔑","⁠":"⁠"," ":" ","ℕ":"ℕ","⫬":"⫬","≢":"≢","≭":"≭","∦":"∦","∉":"∉","≠":"≠","≂̸":"≂̸","∄":"∄","≯":"≯","≱":"≱","≧̸":"≧̸","≫̸":"≫̸","≹":"≹","⩾̸":"⩾̸","≵":"≵","≎̸":"≎̸","≏̸":"≏̸","⋪":"⋪","⧏̸":"⧏̸","⋬":"⋬","≮":"≮","≰":"≰","≸":"≸","≪̸":"≪̸","⩽̸":"⩽̸","≴":"≴","⪢̸":"⪢̸","⪡̸":"⪡̸","⊀":"⊀","⪯̸":"⪯̸","⋠":"⋠","∌":"∌","⋫":"⋫","⧐̸":"⧐̸","⋭":"⋭","⊏̸":"⊏̸","⋢":"⋢","⊐̸":"⊐̸","⋣":"⋣","⊂⃒":"⊂⃒","⊈":"⊈","⊁":"⊁","⪰̸":"⪰̸","⋡":"⋡","≿̸":"≿̸","⊃⃒":"⊃⃒","⊉":"⊉","≁":"≁","≄":"≄","≇":"≇","≉":"≉","∤":"∤","𝒩":"𝒩","Ñ":"Ñ","Ν":"Ν","Œ":"Œ","Ó":"Ó","Ô":"Ô","О":"О","Ő":"Ő","𝔒":"𝔒","Ò":"Ò","Ō":"Ō","Ω":"Ω","Ο":"Ο","𝕆":"𝕆","“":"“","‘":"‘","⩔":"⩔","𝒪":"𝒪","Ø":"Ø","Õ":"Õ","⨷":"⨷","Ö":"Ö","‾":"‾","⏞":"⏞","⎴":"⎴","⏜":"⏜","∂":"∂","П":"П","𝔓":"𝔓","Φ":"Φ","Π":"Π","±":"±","ℙ":"ℙ","⪻":"⪻","≺":"≺","⪯":"⪯","≼":"≼","≾":"≾","″":"″","∏":"∏","∝":"∝","𝒫":"𝒫","Ψ":"Ψ",'"':""","𝔔":"𝔔","ℚ":"ℚ","𝒬":"𝒬","⤐":"⤐","®":"®","Ŕ":"Ŕ","⟫":"⟫","↠":"↠","⤖":"⤖","Ř":"Ř","Ŗ":"Ŗ","Р":"Р","ℜ":"ℜ","∋":"∋","⇋":"⇋","⥯":"⥯","Ρ":"Ρ","⟩":"⟩","→":"→","⇥":"⇥","⇄":"⇄","⌉":"⌉","⟧":"⟧","⥝":"⥝","⇂":"⇂","⥕":"⥕","⌋":"⌋","⊢":"⊢","↦":"↦","⥛":"⥛","⊳":"⊳","⧐":"⧐","⊵":"⊵","⥏":"⥏","⥜":"⥜","↾":"↾","⥔":"⥔","⇀":"⇀","⥓":"⥓","ℝ":"ℝ","⥰":"⥰","⇛":"⇛","ℛ":"ℛ","↱":"↱","⧴":"⧴","Щ":"Щ","Ш":"Ш","Ь":"Ь","Ś":"Ś","⪼":"⪼","Š":"Š","Ş":"Ş","Ŝ":"Ŝ","С":"С","𝔖":"𝔖","↑":"↑","Σ":"Σ","∘":"∘","𝕊":"𝕊","√":"√","□":"□","⊓":"⊓","⊏":"⊏","⊑":"⊑","⊐":"⊐","⊒":"⊒","⊔":"⊔","𝒮":"𝒮","⋆":"⋆","⋐":"⋐","⊆":"⊆","≻":"≻","⪰":"⪰","≽":"≽","≿":"≿","∑":"∑","⋑":"⋑","⊃":"⊃","⊇":"⊇","Þ":"Þ","™":"™","Ћ":"Ћ","Ц":"Ц","\t":" ","Τ":"Τ","Ť":"Ť","Ţ":"Ţ","Т":"Т","𝔗":"𝔗","∴":"∴","Θ":"Θ","  ":"  "," ":" ","∼":"∼","≃":"≃","≅":"≅","≈":"≈","𝕋":"𝕋","⃛":"⃛","𝒯":"𝒯","Ŧ":"Ŧ","Ú":"Ú","↟":"↟","⥉":"⥉","Ў":"Ў","Ŭ":"Ŭ","Û":"Û","У":"У","Ű":"Ű","𝔘":"𝔘","Ù":"Ù","Ū":"Ū",_:"_","⏟":"⏟","⎵":"⎵","⏝":"⏝","⋃":"⋃","⊎":"⊎","Ų":"Ų","𝕌":"𝕌","⤒":"⤒","⇅":"⇅","↕":"↕","⥮":"⥮","⊥":"⊥","↥":"↥","↖":"↖","↗":"↗","ϒ":"ϒ","Υ":"Υ","Ů":"Ů","𝒰":"𝒰","Ũ":"Ũ","Ü":"Ü","⊫":"⊫","⫫":"⫫","В":"В","⊩":"⊩","⫦":"⫦","⋁":"⋁","‖":"‖","∣":"∣","|":"|","❘":"❘","≀":"≀"," ":" ","𝔙":"𝔙","𝕍":"𝕍","𝒱":"𝒱","⊪":"⊪","Ŵ":"Ŵ","⋀":"⋀","𝔚":"𝔚","𝕎":"𝕎","𝒲":"𝒲","𝔛":"𝔛","Ξ":"Ξ","𝕏":"𝕏","𝒳":"𝒳","Я":"Я","Ї":"Ї","Ю":"Ю","Ý":"Ý","Ŷ":"Ŷ","Ы":"Ы","𝔜":"𝔜","𝕐":"𝕐","𝒴":"𝒴","Ÿ":"Ÿ","Ж":"Ж","Ź":"Ź","Ž":"Ž","З":"З","Ż":"Ż","Ζ":"Ζ","ℨ":"ℨ","ℤ":"ℤ","𝒵":"𝒵","á":"á","ă":"ă","∾":"∾","∾̳":"∾̳","∿":"∿","â":"â","а":"а","æ":"æ","𝔞":"𝔞","à":"à","ℵ":"ℵ","α":"α","ā":"ā","⨿":"⨿","∧":"∧","⩕":"⩕","⩜":"⩜","⩘":"⩘","⩚":"⩚","∠":"∠","⦤":"⦤","∡":"∡","⦨":"⦨","⦩":"⦩","⦪":"⦪","⦫":"⦫","⦬":"⦬","⦭":"⦭","⦮":"⦮","⦯":"⦯","∟":"∟","⊾":"⊾","⦝":"⦝","∢":"∢","⍼":"⍼","ą":"ą","𝕒":"𝕒","⩰":"⩰","⩯":"⩯","≊":"≊","≋":"≋","'":"'","å":"å","𝒶":"𝒶","*":"*","ã":"ã","ä":"ä","⨑":"⨑","⫭":"⫭","≌":"≌","϶":"϶","‵":"‵","∽":"∽","⋍":"⋍","⊽":"⊽","⌅":"⌅","⎶":"⎶","б":"б","„":"„","⦰":"⦰","β":"β","ℶ":"ℶ","≬":"≬","𝔟":"𝔟","◯":"◯","⨀":"⨀","⨁":"⨁","⨂":"⨂","⨆":"⨆","★":"★","▽":"▽","△":"△","⨄":"⨄","⤍":"⤍","⧫":"⧫","▴":"▴","▾":"▾","◂":"◂","▸":"▸","␣":"␣","▒":"▒","░":"░","▓":"▓","█":"█","=⃥":"=⃥","≡⃥":"≡⃥","⌐":"⌐","𝕓":"𝕓","⋈":"⋈","╗":"╗","╔":"╔","╖":"╖","╓":"╓","═":"═","╦":"╦","╩":"╩","╤":"╤","╧":"╧","╝":"╝","╚":"╚","╜":"╜","╙":"╙","║":"║","╬":"╬","╣":"╣","╠":"╠","╫":"╫","╢":"╢","╟":"╟","⧉":"⧉","╕":"╕","╒":"╒","┐":"┐","┌":"┌","╥":"╥","╨":"╨","┬":"┬","┴":"┴","⊟":"⊟","⊞":"⊞","⊠":"⊠","╛":"╛","╘":"╘","┘":"┘","└":"└","│":"│","╪":"╪","╡":"╡","╞":"╞","┼":"┼","┤":"┤","├":"├","¦":"¦","𝒷":"𝒷","⁏":"⁏","\\":"\","⧅":"⧅","⟈":"⟈","•":"•","⪮":"⪮","ć":"ć","∩":"∩","⩄":"⩄","⩉":"⩉","⩋":"⩋","⩇":"⩇","⩀":"⩀","∩︀":"∩︀","⁁":"⁁","⩍":"⩍","č":"č","ç":"ç","ĉ":"ĉ","⩌":"⩌","⩐":"⩐","ċ":"ċ","⦲":"⦲","¢":"¢","𝔠":"𝔠","ч":"ч","✓":"✓","χ":"χ","○":"○","⧃":"⧃","ˆ":"ˆ","≗":"≗","↺":"↺","↻":"↻","Ⓢ":"Ⓢ","⊛":"⊛","⊚":"⊚","⊝":"⊝","⨐":"⨐","⫯":"⫯","⧂":"⧂","♣":"♣",":":":",",":",","@":"@","∁":"∁","⩭":"⩭","𝕔":"𝕔","℗":"℗","↵":"↵","✗":"✗","𝒸":"𝒸","⫏":"⫏","⫑":"⫑","⫐":"⫐","⫒":"⫒","⋯":"⋯","⤸":"⤸","⤵":"⤵","⋞":"⋞","⋟":"⋟","↶":"↶","⤽":"⤽","∪":"∪","⩈":"⩈","⩆":"⩆","⩊":"⩊","⊍":"⊍","⩅":"⩅","∪︀":"∪︀","↷":"↷","⤼":"⤼","⋎":"⋎","⋏":"⋏","¤":"¤","∱":"∱","⌭":"⌭","⥥":"⥥","†":"†","ℸ":"ℸ","‐":"‐","⤏":"⤏","ď":"ď","д":"д","⇊":"⇊","⩷":"⩷","°":"°","δ":"δ","⦱":"⦱","⥿":"⥿","𝔡":"𝔡","♦":"♦","ϝ":"ϝ","⋲":"⋲","÷":"÷","⋇":"⋇","ђ":"ђ","⌞":"⌞","⌍":"⌍",$:"$","𝕕":"𝕕","≑":"≑","∸":"∸","∔":"∔","⊡":"⊡","⌟":"⌟","⌌":"⌌","𝒹":"𝒹","ѕ":"ѕ","⧶":"⧶","đ":"đ","⋱":"⋱","▿":"▿","⦦":"⦦","џ":"џ","⟿":"⟿","é":"é","⩮":"⩮","ě":"ě","≖":"≖","ê":"ê","≕":"≕","э":"э","ė":"ė","≒":"≒","𝔢":"𝔢","⪚":"⪚","è":"è","⪖":"⪖","⪘":"⪘","⪙":"⪙","⏧":"⏧","ℓ":"ℓ","⪕":"⪕","⪗":"⪗","ē":"ē","∅":"∅"," ":" "," ":" "," ":" ","ŋ":"ŋ"," ":" ","ę":"ę","𝕖":"𝕖","⋕":"⋕","⧣":"⧣","⩱":"⩱","ε":"ε","ϵ":"ϵ","=":"=","≟":"≟","⩸":"⩸","⧥":"⧥","≓":"≓","⥱":"⥱","ℯ":"ℯ","η":"η","ð":"ð","ë":"ë","€":"€","!":"!","ф":"ф","♀":"♀","ffi":"ffi","ff":"ff","ffl":"ffl","𝔣":"𝔣","fi":"fi",fj:"fj","♭":"♭","fl":"fl","▱":"▱","ƒ":"ƒ","𝕗":"𝕗","⋔":"⋔","⫙":"⫙","⨍":"⨍","½":"½","⅓":"⅓","¼":"¼","⅕":"⅕","⅙":"⅙","⅛":"⅛","⅔":"⅔","⅖":"⅖","¾":"¾","⅗":"⅗","⅜":"⅜","⅘":"⅘","⅚":"⅚","⅝":"⅝","⅞":"⅞","⁄":"⁄","⌢":"⌢","𝒻":"𝒻","⪌":"⪌","ǵ":"ǵ","γ":"γ","⪆":"⪆","ğ":"ğ","ĝ":"ĝ","г":"г","ġ":"ġ","⪩":"⪩","⪀":"⪀","⪂":"⪂","⪄":"⪄","⋛︀":"⋛︀","⪔":"⪔","𝔤":"𝔤","ℷ":"ℷ","ѓ":"ѓ","⪒":"⪒","⪥":"⪥","⪤":"⪤","≩":"≩","⪊":"⪊","⪈":"⪈","⋧":"⋧","𝕘":"𝕘","ℊ":"ℊ","⪎":"⪎","⪐":"⪐","⪧":"⪧","⩺":"⩺","⋗":"⋗","⦕":"⦕","⩼":"⩼","⥸":"⥸","≩︀":"≩︀","ъ":"ъ","⥈":"⥈","↭":"↭","ℏ":"ℏ","ĥ":"ĥ","♥":"♥","…":"…","⊹":"⊹","𝔥":"𝔥","⤥":"⤥","⤦":"⤦","⇿":"⇿","∻":"∻","↩":"↩","↪":"↪","𝕙":"𝕙","―":"―","𝒽":"𝒽","ħ":"ħ","⁃":"⁃","í":"í","î":"î","и":"и","е":"е","¡":"¡","𝔦":"𝔦","ì":"ì","⨌":"⨌","∭":"∭","⧜":"⧜","℩":"℩","ij":"ij","ī":"ī","ı":"ı","⊷":"⊷","Ƶ":"Ƶ","℅":"℅","∞":"∞","⧝":"⧝","⊺":"⊺","⨗":"⨗","⨼":"⨼","ё":"ё","į":"į","𝕚":"𝕚","ι":"ι","¿":"¿","𝒾":"𝒾","⋹":"⋹","⋵":"⋵","⋴":"⋴","⋳":"⋳","ĩ":"ĩ","і":"і","ï":"ï","ĵ":"ĵ","й":"й","𝔧":"𝔧","ȷ":"ȷ","𝕛":"𝕛","𝒿":"𝒿","ј":"ј","є":"є","κ":"κ","ϰ":"ϰ","ķ":"ķ","к":"к","𝔨":"𝔨","ĸ":"ĸ","х":"х","ќ":"ќ","𝕜":"𝕜","𝓀":"𝓀","⤛":"⤛","⤎":"⤎","⪋":"⪋","⥢":"⥢","ĺ":"ĺ","⦴":"⦴","λ":"λ","⦑":"⦑","⪅":"⪅","«":"«","⤟":"⤟","⤝":"⤝","↫":"↫","⤹":"⤹","⥳":"⥳","↢":"↢","⪫":"⪫","⤙":"⤙","⪭":"⪭","⪭︀":"⪭︀","⤌":"⤌","❲":"❲","{":"{","[":"[","⦋":"⦋","⦏":"⦏","⦍":"⦍","ľ":"ľ","ļ":"ļ","л":"л","⤶":"⤶","⥧":"⥧","⥋":"⥋","↲":"↲","≤":"≤","⇇":"⇇","⋋":"⋋","⪨":"⪨","⩿":"⩿","⪁":"⪁","⪃":"⪃","⋚︀":"⋚︀","⪓":"⪓","⋖":"⋖","⥼":"⥼","𝔩":"𝔩","⪑":"⪑","⥪":"⥪","▄":"▄","љ":"љ","⥫":"⥫","◺":"◺","ŀ":"ŀ","⎰":"⎰","≨":"≨","⪉":"⪉","⪇":"⪇","⋦":"⋦","⟬":"⟬","⇽":"⇽","⟼":"⟼","↬":"↬","⦅":"⦅","𝕝":"𝕝","⨭":"⨭","⨴":"⨴","∗":"∗","◊":"◊","(":"(","⦓":"⦓","⥭":"⥭","‎":"‎","⊿":"⊿","‹":"‹","𝓁":"𝓁","⪍":"⪍","⪏":"⪏","‚":"‚","ł":"ł","⪦":"⪦","⩹":"⩹","⋉":"⋉","⥶":"⥶","⩻":"⩻","⦖":"⦖","◃":"◃","⥊":"⥊","⥦":"⥦","≨︀":"≨︀","∺":"∺","¯":"¯","♂":"♂","✠":"✠","▮":"▮","⨩":"⨩","м":"м","—":"—","𝔪":"𝔪","℧":"℧","µ":"µ","⫰":"⫰","−":"−","⨪":"⨪","⫛":"⫛","⊧":"⊧","𝕞":"𝕞","𝓂":"𝓂","μ":"μ","⊸":"⊸","⋙̸":"⋙̸","≫⃒":"≫⃒","⇍":"⇍","⇎":"⇎","⋘̸":"⋘̸","≪⃒":"≪⃒","⇏":"⇏","⊯":"⊯","⊮":"⊮","ń":"ń","∠⃒":"∠⃒","⩰̸":"⩰̸","≋̸":"≋̸","ʼn":"ʼn","♮":"♮","⩃":"⩃","ň":"ň","ņ":"ņ","⩭̸":"⩭̸","⩂":"⩂","н":"н","–":"–","⇗":"⇗","⤤":"⤤","≐̸":"≐̸","⤨":"⤨","𝔫":"𝔫","↮":"↮","⫲":"⫲","⋼":"⋼","⋺":"⋺","њ":"њ","≦̸":"≦̸","↚":"↚","‥":"‥","𝕟":"𝕟","¬":"¬","⋹̸":"⋹̸","⋵̸":"⋵̸","⋷":"⋷","⋶":"⋶","⋾":"⋾","⋽":"⋽","⫽⃥":"⫽⃥","∂̸":"∂̸","⨔":"⨔","↛":"↛","⤳̸":"⤳̸","↝̸":"↝̸","𝓃":"𝓃","⊄":"⊄","⫅̸":"⫅̸","⊅":"⊅","⫆̸":"⫆̸","ñ":"ñ","ν":"ν","#":"#","№":"№"," ":" ","⊭":"⊭","⤄":"⤄","≍⃒":"≍⃒","⊬":"⊬","≥⃒":"≥⃒",">⃒":">⃒","⧞":"⧞","⤂":"⤂","≤⃒":"≤⃒","<⃒":"<⃒","⊴⃒":"⊴⃒","⤃":"⤃","⊵⃒":"⊵⃒","∼⃒":"∼⃒","⇖":"⇖","⤣":"⤣","⤧":"⤧","ó":"ó","ô":"ô","о":"о","ő":"ő","⨸":"⨸","⦼":"⦼","œ":"œ","⦿":"⦿","𝔬":"𝔬","˛":"˛","ò":"ò","⧁":"⧁","⦵":"⦵","⦾":"⦾","⦻":"⦻","⧀":"⧀","ō":"ō","ω":"ω","ο":"ο","⦶":"⦶","𝕠":"𝕠","⦷":"⦷","⦹":"⦹","∨":"∨","⩝":"⩝","ℴ":"ℴ","ª":"ª","º":"º","⊶":"⊶","⩖":"⩖","⩗":"⩗","⩛":"⩛","ø":"ø","⊘":"⊘","õ":"õ","⨶":"⨶","ö":"ö","⌽":"⌽","¶":"¶","⫳":"⫳","⫽":"⫽","п":"п","%":"%",".":".","‰":"‰","‱":"‱","𝔭":"𝔭","φ":"φ","ϕ":"ϕ","☎":"☎","π":"π","ϖ":"ϖ","ℎ":"ℎ","+":"+","⨣":"⨣","⨢":"⨢","⨥":"⨥","⩲":"⩲","⨦":"⨦","⨧":"⨧","⨕":"⨕","𝕡":"𝕡","£":"£","⪳":"⪳","⪷":"⪷","⪹":"⪹","⪵":"⪵","⋨":"⋨","′":"′","⌮":"⌮","⌒":"⌒","⌓":"⌓","⊰":"⊰","𝓅":"𝓅","ψ":"ψ"," ":" ","𝔮":"𝔮","𝕢":"𝕢","⁗":"⁗","𝓆":"𝓆","⨖":"⨖","?":"?","⤜":"⤜","⥤":"⥤","∽̱":"∽̱","ŕ":"ŕ","⦳":"⦳","⦒":"⦒","⦥":"⦥","»":"»","⥵":"⥵","⤠":"⤠","⤳":"⤳","⤞":"⤞","⥅":"⥅","⥴":"⥴","↣":"↣","↝":"↝","⤚":"⤚","∶":"∶","❳":"❳","}":"}","]":"]","⦌":"⦌","⦎":"⦎","⦐":"⦐","ř":"ř","ŗ":"ŗ","р":"р","⤷":"⤷","⥩":"⥩","↳":"↳","▭":"▭","⥽":"⥽","𝔯":"𝔯","⥬":"⥬","ρ":"ρ","ϱ":"ϱ","⇉":"⇉","⋌":"⋌","˚":"˚","‏":"‏","⎱":"⎱","⫮":"⫮","⟭":"⟭","⇾":"⇾","⦆":"⦆","𝕣":"𝕣","⨮":"⨮","⨵":"⨵",")":")","⦔":"⦔","⨒":"⨒","›":"›","𝓇":"𝓇","⋊":"⋊","▹":"▹","⧎":"⧎","⥨":"⥨","℞":"℞","ś":"ś","⪴":"⪴","⪸":"⪸","š":"š","ş":"ş","ŝ":"ŝ","⪶":"⪶","⪺":"⪺","⋩":"⋩","⨓":"⨓","с":"с","⋅":"⋅","⩦":"⩦","⇘":"⇘","§":"§",";":";","⤩":"⤩","✶":"✶","𝔰":"𝔰","♯":"♯","щ":"щ","ш":"ш","­":"­","σ":"σ","ς":"ς","⩪":"⩪","⪞":"⪞","⪠":"⪠","⪝":"⪝","⪟":"⪟","≆":"≆","⨤":"⨤","⥲":"⥲","⨳":"⨳","⧤":"⧤","⌣":"⌣","⪪":"⪪","⪬":"⪬","⪬︀":"⪬︀","ь":"ь","/":"/","⧄":"⧄","⌿":"⌿","𝕤":"𝕤","♠":"♠","⊓︀":"⊓︀","⊔︀":"⊔︀","𝓈":"𝓈","☆":"☆","⊂":"⊂","⫅":"⫅","⪽":"⪽","⫃":"⫃","⫁":"⫁","⫋":"⫋","⊊":"⊊","⪿":"⪿","⥹":"⥹","⫇":"⫇","⫕":"⫕","⫓":"⫓","♪":"♪","¹":"¹","²":"²","³":"³","⫆":"⫆","⪾":"⪾","⫘":"⫘","⫄":"⫄","⟉":"⟉","⫗":"⫗","⥻":"⥻","⫂":"⫂","⫌":"⫌","⊋":"⊋","⫀":"⫀","⫈":"⫈","⫔":"⫔","⫖":"⫖","⇙":"⇙","⤪":"⤪","ß":"ß","⌖":"⌖","τ":"τ","ť":"ť","ţ":"ţ","т":"т","⌕":"⌕","𝔱":"𝔱","θ":"θ","ϑ":"ϑ","þ":"þ","×":"×","⨱":"⨱","⨰":"⨰","⌶":"⌶","⫱":"⫱","𝕥":"𝕥","⫚":"⫚","‴":"‴","▵":"▵","≜":"≜","◬":"◬","⨺":"⨺","⨹":"⨹","⧍":"⧍","⨻":"⨻","⏢":"⏢","𝓉":"𝓉","ц":"ц","ћ":"ћ","ŧ":"ŧ","⥣":"⥣","ú":"ú","ў":"ў","ŭ":"ŭ","û":"û","у":"у","ű":"ű","⥾":"⥾","𝔲":"𝔲","ù":"ù","▀":"▀","⌜":"⌜","⌏":"⌏","◸":"◸","ū":"ū","ų":"ų","𝕦":"𝕦","υ":"υ","⇈":"⇈","⌝":"⌝","⌎":"⌎","ů":"ů","◹":"◹","𝓊":"𝓊","⋰":"⋰","ũ":"ũ","ü":"ü","⦧":"⦧","⫨":"⫨","⫩":"⫩","⦜":"⦜","⊊︀":"⊊︀","⫋︀":"⫋︀","⊋︀":"⊋︀","⫌︀":"⫌︀","в":"в","⊻":"⊻","≚":"≚","⋮":"⋮","𝔳":"𝔳","𝕧":"𝕧","𝓋":"𝓋","⦚":"⦚","ŵ":"ŵ","⩟":"⩟","≙":"≙","℘":"℘","𝔴":"𝔴","𝕨":"𝕨","𝓌":"𝓌","𝔵":"𝔵","ξ":"ξ","⋻":"⋻","𝕩":"𝕩","𝓍":"𝓍","ý":"ý","я":"я","ŷ":"ŷ","ы":"ы","¥":"¥","𝔶":"𝔶","ї":"ї","𝕪":"𝕪","𝓎":"𝓎","ю":"ю","ÿ":"ÿ","ź":"ź","ž":"ž","з":"з","ż":"ż","ζ":"ζ","𝔷":"𝔷","ж":"ж","⇝":"⇝","𝕫":"𝕫","𝓏":"𝓏","‍":"‍","‌":"‌"}}}})),A=y((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.numericUnicodeMap={0:65533,128:8364,130:8218,131:402,132:8222,133:8230,134:8224,135:8225,136:710,137:8240,138:352,139:8249,140:338,142:381,145:8216,146:8217,147:8220,148:8221,149:8226,150:8211,151:8212,152:732,153:8482,154:353,155:8250,156:339,158:382,159:376}})),L=y((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.fromCodePoint=String.fromCodePoint||function(e){return String.fromCharCode(Math.floor((e-65536)/1024)+55296,(e-65536)%1024+56320)},t.getCodePoint=String.prototype.codePointAt?function(e,t){return e.codePointAt(t)}:function(e,t){return 1024*(e.charCodeAt(t)-55296)+e.charCodeAt(t+1)-56320+65536},t.highSurrogateFrom=55296,t.highSurrogateTo=56319})),_=P,N=A,F=L,U=y((function(e,t){var r=h&&h.__assign||function(){return r=Object.assign||function(e){for(var t,r=1,n=arguments.length;r'"&]/g,nonAscii:/(?:[<>'"&\u0080-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g,nonAsciiPrintable:/(?:[<>'"&\x01-\x08\x11-\x15\x17-\x1F\x7f-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g,extensive:/(?:[\x01-\x0c\x0e-\x1f\x21-\x2c\x2e-\x2f\x3a-\x40\x5b-\x60\x7b-\x7d\x7f-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g},i={mode:"specialChars",level:"all",numeric:"decimal"};t.encode=function(e,t){var r=void 0===(c=(l=void 0===t?i:t).mode)?"specialChars":c,o=void 0===(p=l.numeric)?"decimal":p,s=void 0===(m=l.level)?"all":m;if(!e)return"";var l,c,u=a[r],d=n[s].characters,f="hexadecimal"===o;if(u.lastIndex=0,l=u.exec(e)){c="";var p=0;do{p!==l.index&&(c+=e.substring(p,l.index));var m,v=d[m=l[0]];if(!v){var h=m.length>1?F.getCodePoint(m,0):m.charCodeAt(0);v=(f?"&#x"+h.toString(16):"&#"+h)+";"}c+=v,p=l.index+m.length}while(l=u.exec(e));p!==e.length&&(c+=e.substring(p))}else c=e;return c};var o={scope:"body",level:"all"},s=/&(?:#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+);/g,l=/&(?:#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+)[;=]?/g,c={xml:{strict:s,attribute:l,body:_.bodyRegExps.xml},html4:{strict:s,attribute:l,body:_.bodyRegExps.html4},html5:{strict:s,attribute:l,body:_.bodyRegExps.html5}},u=r(r({},c),{all:c.html5}),d=String.fromCharCode,f=d(65533),p={level:"all"};t.decodeEntity=function(e,t){var r=void 0===(a=(void 0===t?p:t).level)?"all":a;if(!e)return"";var a=e;e[e.length-1];var i=n[r].entities[e];if(i)a=i;else if("&"===e[0]&&"#"===e[1]){var o=e[2],s="x"==o||"X"==o?parseInt(e.substr(3),16):parseInt(e.substr(2));a=s>=1114111?f:s>65535?F.fromCodePoint(s):d(N.numericUnicodeMap[s]||s)}return a},t.decode=function(e,t){var r=void 0===t?o:t,a=r.level,i=void 0===a?"all":a,s=r.scope,l=void 0===s?"xml"===i?"strict":"body":s;if(!e)return"";var c=u[i][l],p=n[i].entities,m="attribute"===l,v="strict"===l;c.lastIndex=0;var h,g=c.exec(e);if(g){h="";var y=0;do{y!==g.index&&(h+=e.substring(y,g.index));var b=g[0],x=b,k=b[b.length-1];if(m&&"="===k)x=b;else if(v&&";"!==k)x=b;else{var w=p[b];if(w)x=w;else if("&"===b[0]&&"#"===b[1]){var E=b[2],T="x"==E||"X"==E?parseInt(b.substr(3),16):parseInt(b.substr(2));x=T>=1114111?f:T>65535?F.fromCodePoint(T):d(N.numericUnicodeMap[T]||T)}}h+=x,y=g.index+b.length}while(g=c.exec(e));y!==e.length&&(h+=e.substring(y))}else h=e;return h}})),B=Object.prototype;var V=function(e){var t=e&&e.constructor;return e===("function"==typeof t&&t.prototype||B)};var H=function(e,t){return function(r){return e(t(r))}}(Object.keys,Object),G=Object.prototype.hasOwnProperty;var z=function(e){if(!V(e))return H(e);var t=[];for(var r in Object(e))G.call(e,r)&&"constructor"!=r&&t.push(r);return t},J="object"==typeof h&&h&&h.Object===Object&&h,W="object"==typeof self&&self&&self.Object===Object&&self,Z=J||W||Function("return this")(),Y=Z.Symbol,Q=Object.prototype,K=Q.hasOwnProperty,X=Q.toString,$=Y?Y.toStringTag:void 0;var ee=function(e){var t=K.call(e,$),r=e[$];try{e[$]=void 0;var n=!0}catch(e){}var a=X.call(e);return n&&(t?e[$]=r:delete e[$]),a},te=Object.prototype.toString;var re=function(e){return te.call(e)},ne="[object Null]",ae="[object Undefined]",ie=Y?Y.toStringTag:void 0;var oe=function(e){return null==e?void 0===e?ae:ne:ie&&ie in Object(e)?ee(e):re(e)};var se=function(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)},le="[object AsyncFunction]",ce="[object Function]",ue="[object GeneratorFunction]",de="[object Proxy]";var fe,pe=function(e){if(!se(e))return!1;var t=oe(e);return t==ce||t==ue||t==le||t==de},me=Z["__core-js_shared__"],ve=(fe=/[^.]+$/.exec(me&&me.keys&&me.keys.IE_PROTO||""))?"Symbol(src)_1."+fe:"";var he=function(e){return!!ve&&ve in e},ge=Function.prototype.toString;var ye=function(e){if(null!=e){try{return ge.call(e)}catch(e){}try{return e+""}catch(e){}}return""},be=/^\[object .+?Constructor\]$/,xe=Function.prototype,ke=Object.prototype,we=xe.toString,Ee=ke.hasOwnProperty,Te=RegExp("^"+we.call(Ee).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$");var Se=function(e){return!(!se(e)||he(e))&&(pe(e)?Te:be).test(ye(e))};var Ce=function(e,t){return null==e?void 0:e[t]};var Ie=function(e,t){var r=Ce(e,t);return Se(r)?r:void 0},Re=Ie(Z,"DataView"),Oe=Ie(Z,"Map"),qe=Ie(Z,"Promise"),je=Ie(Z,"Set"),Me=Ie(Z,"WeakMap"),De="[object Map]",Pe="[object Promise]",Ae="[object Set]",Le="[object WeakMap]",_e="[object DataView]",Ne=ye(Re),Fe=ye(Oe),Ue=ye(qe),Be=ye(je),Ve=ye(Me),He=oe;(Re&&He(new Re(new ArrayBuffer(1)))!=_e||Oe&&He(new Oe)!=De||qe&&He(qe.resolve())!=Pe||je&&He(new je)!=Ae||Me&&He(new Me)!=Le)&&(He=function(e){var t=oe(e),r="[object Object]"==t?e.constructor:void 0,n=r?ye(r):"";if(n)switch(n){case Ne:return _e;case Fe:return De;case Ue:return Pe;case Be:return Ae;case Ve:return Le}return t});var Ge=He;var ze=function(e){return null!=e&&"object"==typeof e},Je="[object Arguments]";var We=function(e){return ze(e)&&oe(e)==Je},Ze=Object.prototype,Ye=Ze.hasOwnProperty,Qe=Ze.propertyIsEnumerable,Ke=We(function(){return arguments}())?We:function(e){return ze(e)&&Ye.call(e,"callee")&&!Qe.call(e,"callee")},Xe=Array.isArray,$e=9007199254740991;var et=function(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=$e};var tt=function(e){return null!=e&&et(e.length)&&!pe(e)};var rt=function(){return!1},nt=y((function(e,t){var r=t&&!t.nodeType&&t,n=r&&e&&!e.nodeType&&e,a=n&&n.exports===r?Z.Buffer:void 0,i=(a?a.isBuffer:void 0)||rt;e.exports=i})),at={};at["[object Float32Array]"]=at["[object Float64Array]"]=at["[object Int8Array]"]=at["[object Int16Array]"]=at["[object Int32Array]"]=at["[object Uint8Array]"]=at["[object Uint8ClampedArray]"]=at["[object Uint16Array]"]=at["[object Uint32Array]"]=!0,at["[object Arguments]"]=at["[object Array]"]=at["[object ArrayBuffer]"]=at["[object Boolean]"]=at["[object DataView]"]=at["[object Date]"]=at["[object Error]"]=at["[object Function]"]=at["[object Map]"]=at["[object Number]"]=at["[object Object]"]=at["[object RegExp]"]=at["[object Set]"]=at["[object String]"]=at["[object WeakMap]"]=!1;var it=function(e){return ze(e)&&et(e.length)&&!!at[oe(e)]};var ot=function(e){return function(t){return e(t)}},st=y((function(e,t){var r=t&&!t.nodeType&&t,n=r&&e&&!e.nodeType&&e,a=n&&n.exports===r&&J.process,i=function(){try{var e=n&&n.require&&n.require("util").types;return e||a&&a.binding&&a.binding("util")}catch(e){}}();e.exports=i})),lt=st&&st.isTypedArray,ct=lt?ot(lt):it,ut="[object Map]",dt="[object Set]",ft=Object.prototype.hasOwnProperty;var pt=function(e){if(null==e)return!0;if(tt(e)&&(Xe(e)||"string"==typeof e||"function"==typeof e.splice||nt(e)||ct(e)||Ke(e)))return!e.length;var t=Ge(e);if(t==ut||t==dt)return!e.size;if(V(e))return!z(e).length;for(var r in e)if(ft.call(e,r))return!1;return!0};function mt(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function vt(e){for(var t=1;t1&&void 0!==arguments[1]&&arguments[1],r=arguments.length>2&&void 0!==arguments[2]&&arguments[2];if(isNaN(e))return"";var n=Math.floor(e/3600),a=Math.floor(e%3600/60),i=e-60*a-3600*n,o="",s=n<10?"0".concat(n):"".concat(n);o=t||n>0?o+"".concat(s,":"):o;var l=a<10?"0".concat(a):"".concat(a);o+="".concat(l,":");var c=r?i.toFixed(3):parseInt(i);return c=i<10?"0".concat(c):"".concat(c),o+="".concat(c)}function St(e){var t=e.split(":").reverse(),r=T(t,3),n=r[0],a=r[1],i=r[2];return(null!=i?3600*parseInt(i):0)+(null!=a?60*parseInt(a):0)+(""===n?0:parseFloat(n.replace(",",".")))}function Ct(e){if(!e.ok)throw new Error(bt);return e}function It(e,t){return void 0!==e&&(void 0===t||!(e.start>t.end&&e.end>t.end))}function Rt(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"",n=arguments.length>3&&void 0!==arguments[3]&&arguments[3],a=""===r?t.split(".").reverse()[0]:r;(a.length>4||a.length<3||a===t)&&(a=e.split(".").reverse()[0]);var i=a.length>4||a.length<3?"":a,o=t.endsWith(i)?t.split(".".concat(i))[0]:t;n&&(o="".concat(o," (machine generated)"));var s=""!=i?"".concat(o,".").concat(i):o;if(e.endsWith(a))fetch(e).then((function(e){e.blob().then((function(e){var t=window.URL.createObjectURL(e),r=document.createElement("a");r.href=t,r.download="".concat(s),r.click()}))})).catch((function(e){console.log(e)}));else{var l=document.createElement("a");l.setAttribute("href",e),l.setAttribute("download","".concat(s)),l.style.display="none",document.body.appendChild(l),l.click(),document.body.removeChild(l)}}function Ot(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;if(void 0===e);else{var r=e.split("#t=")[1];if(void 0!==r){var n,a,i,o=/([0-9]*:){1,2}([0-9]{2})(?:((\.|\,)[0-9]{2,3})?)/g;if(r.includes(":")&&(null===(n=D(r.matchAll(/\,/g)))||void 0===n?void 0:n.length)>1){var s=D(r.matchAll(o)),l=2==(null==s?void 0:s.length)?[s[0][0],s[1][0]]:[0,0],c=T(l,2);a=c[0],i=c[1]}else{var u=r.split(","),d=T(u,2);a=d[0],i=d[1]}return void 0===i&&(i=t.toString()),{start:a.match(o)?St(a):Number(a),end:i.match(o)?St(i):Number(i)}}}}function qt(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"",r=[];if(!e)return r;if("Canvas"===e.type?r=e.items[0].items:Array.isArray(e)&&(null==e?void 0:e.length)>0&&(r=e[0].items),r&&""!=t){var n=r.filter((function(e){return e.motivation===t}));r=n}return r}function jt(e,t,r){var n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,a=arguments.length>4&&void 0!==arguments[4]&&arguments[4],i=[],o=[],s=!1,l="",c="No resources found in Canvas",u=function(e){var a=function(e,t,r,n){var a=null,i=ht.both,o=Pt(e.label);"supplementing"===n&&(i=Dt(e.id));if(i!=ht.transcript){var s;if(a={src:t>0?"".concat(e.id,"#t=").concat(t,",").concat(r):e.id,key:e.id,type:e.format,kind:e.type,label:o||"auto"},"supplementing"===n)a.srclang=null!==(s=e.language)&&void 0!==s?s:"en",a.kind=e.format.toLowerCase().includes("text/vtt")?"subtitles":"metadata"}return a}(e,n,t,r);a&&a.src&&i.push(a)};if(e&&null!=e){var d,f,p,m,v=qt(e);if(!v)return{resources:i,canvasTargets:o,error:c};if(0===v.length)return{resources:i,canvasTargets:o,isMultiSource:s,poster:Jt(e)};if((null==v?void 0:v.length)>1)v.map((function(e,n){if(e.motivation===r&&(u(e.body),"painting"===r)){s=!0;var a=function(e,t,r){var n=Ot(e.target,t);if(null!=n||!n)return n.id=e.id,isNaN(n.end)&&(n.end=t),n.end=Number((n.end-n.start).toFixed(2)),n.duration=n.end,n.altStart=n.start,n.start=0,n.sIndex=r,n}(e,t,n);o.push(a)}}));else if((null===(d=v[0].body.items)||void 0===d?void 0:d.length)>0&&(null===(f=v[0])||void 0===f?void 0:f.motivation)===r)v[0].body.items.map((function(e){u(e)}));else if(pt(v[0].body)||""==(null===(p=v[0].body)||void 0===p?void 0:p.id)||(null===(m=v[0])||void 0===m?void 0:m.motivation)!==r){if("painting"===r)return{resources:i,error:c,poster:Jt(e),canvasTargets:o}}else u(v[0].body);if(!s&&(null==i?void 0:i.length)>0&&"painting"===r){var h=Ot(i[0].src,t);void 0===h&&(h={start:0,end:t}),h.altStart=h.start,h.duration=t,a||(h=vt(vt({},h),{},{customStart:h.start,start:0,altStart:0})),o.push(h)}return l=Jt(e,!0),{canvasTargets:o,isMultiSource:s,resources:i,poster:l}}return{canvasTargets:o,isMultiSource:s,resources:i,poster:l,error:c}}function Mt(e){var t=/(\(machine(\s|\-)generated\))/gi;return{isMachineGen:t.test(e),labelText:e.replace(t,"").trim()}}function Dt(e){if(e){var t=e.split("/").reverse()[0];return"transcripts"===t?ht.transcript:"captions"===t?ht.caption:ht.both}}function Pt(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1];if(e&&"object"===C(e)){var r=Object.keys(e);if(r&&r.length>0){var n,a=r[0],i=t?e[a].join("\n"):null!==(n=e[a][0])&&void 0!==n?n:"";return U.decode(i)}}else if("string"==typeof e)return U.decode(e);return""}function At(e){return/^(([0-1][0-9])|([2][0-3])):([0-5][0-9])(:[0-5][0-9](?:[.]\d{1,3})?)?$/.test(e)}function Lt(e,t){var r=arguments.length>2&&void 0!==arguments[2]&&arguments[2],n=e.offsetTop-t.current.offsetTop;if(r)t.current.scrollTop=n;else{var a=t.current.clientHeight-e.clientHeight;t.current.scrollTop=n>a?n-t.current.clientHeight/2:a/2>n?0:n/2}}function _t(e,t,r){var n=null==t?void 0:t.player(),a="",i=document.activeElement,o=i.className.includes("vjs")||i.className.includes("videojs"),s=e.which,l=e.ctrlKey||e.metaKey||e.altKey||e.shiftKey;if((!i||-1===["input","textarea"].indexOf(i.tagName.toLowerCase())&&("tab"!==i.role||37!==s&&39!==s)||o)&&!l&&!r&&null!=n){switch(s){case 32:case 75:e.preventDefault(),n.paused()?(a=gt.play,n.play()):(a=gt.pause,n.pause());break;case 70:e.preventDefault(),n.isAudio()||(n.isFullscreen()?(a=gt.exitFullscreen,n.exitFullscreen()):(a=gt.enterFullscreen,n.requestFullscreen()));break;case 77:e.preventDefault();var c=n.volume(),u=n.lastVolume_();if(0===c){var d=u<.1?.1:u;n.volume(d),a=gt.unmute,n.muted(!1)}else a=gt.mute,n.muted(!n.muted());break;case 37:e.preventDefault(),a=gt.leftArrow,n.currentTime(n.currentTime()-5);break;case 39:e.preventDefault(),a=gt.rightArrow,n.currentTime(n.currentTime()+5);break;case 38:e.preventDefault(),n.muted()&&n.muted(!1),a=gt.upArrow,n.volume(n.volume()+.1);break;case 40:e.preventDefault(),a=gt.downArrow,n.volume(n.volume()-.1);break;default:return}return e.stopPropagation(),a}}function Nt(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!r){if(Array.isArray(e)||(r=function(e,t){if(!e)return;if("string"==typeof e)return Ft(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);"Object"===r&&e.constructor&&(r=e.constructor.name);if("Map"===r||"Set"===r)return Array.from(e);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return Ft(e,t)}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,a=function(){};return{s:a,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,o=!0,s=!1;return{s:function(){r=r.call(e)},n:function(){var e=r.next();return o=e.done,e},e:function(e){s=!0,i=e},f:function(){try{o||null==r.return||r.return()}finally{if(s)throw i}}}}function Ft(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r0&&(i=e.homepage[0].id);try{var o,s=!0,l=null===(o=e.items[0])||void 0===o?void 0:o.items,c="";if((null==l?void 0:l.length)>0){var u,d,f=l[0].body;(null===(u=f.items)||void 0===u?void 0:u.length)>0?c=f.items[0].id:0!=(null===(d=Object.keys(f))||void 0===d?void 0:d.length)&&f.id&&(c=f.id)}var p,m=Number(e.duration);""!=c&&(p=Ot(c,m),s=!1);var v=Pt(e.label)||"Section ".concat(n+1);r.push({canvasIndex:n,canvasId:e.id,canvasURL:e.id.split("#t=")[0],duration:m,range:void 0===p?{start:0,end:m}:p,isEmpty:s,summary:a,homepage:i||"",label:v,searchService:Qt(e)})}catch(t){r.push({canvasIndex:n,canvasId:e.id,canvasURL:e.id.split("#t=")[0],duration:e.duration||0,range:void 0,isEmpty:!0,summary:a,homepage:i||"",label:Pt(e.label)||"Section ".concat(n+1),searchService:Qt(e)})}})),r;throw console.error("iiif-parser -> canvasesInManifest() -> no canvases were found in Manifest"),new Error(bt)}catch(e){throw e}}function Gt(e){var t,r=e.manifest,n=e.canvasIndex,a=e.startTime,i=e.srcIndex,o=void 0===i?0:i,s=e.isPlaylist,l=void 0!==s&&s,c=null,u={canvas:null,sources:[],tracks:[],canvasTargets:[]};if(void 0===n||n<0)return Bt(Bt({},u),{},{error:"Error fetching content"});var d=r.items;if(0==(null==d?void 0:d.length))return Bt(Bt({},u),{},{poster:kt});try{var f=(c=d[n]).annotations;if(void 0===c)throw console.error("iiif-parser -> getMediaInfo() -> canvas undefined -> ",n),new Error(bt);var p=Number(c.duration),m=jt(c,p,"painting",a,l),v=m.resources,h=m.canvasTargets,g=m.isMultiSource,y=m.error,b=m.poster;t=function(e,t,r){var n=!1;if(0===e.length)return[];if(t)e[r].selected=!0;else{var a,i=Nt(e);try{for(i.s();!(a=i.n()).done;){var o=a.value;"auto"!=o.label||n||(n=!0,o.selected=!0)}}catch(e){i.e(e)}finally{i.f()}n||(e[0].selected=!0)}return e}(v,g,o);var x=jt(f,p,"supplementing"),k={sources:t,tracks:x?x.resources:[],canvasTargets:h,isMultiSource:g,error:y,poster:b};if(k.error)return Bt({},k);var w=function(e){var t=e.filter((function(t,r){return e.indexOf(t)===r})),r=1===t.length?t[0].toLowerCase():"video";return r}(k.sources.map((function(e){return e.kind})));return Bt(Bt({},k),{},{error:null,mediaType:w})}catch(y){throw y}}function zt(e){if(e)return e.split("#t=")[0]}function Jt(e){var t,r,n=arguments.length>1&&void 0!==arguments[1]&&arguments[1];try{var a=e.placeholderCanvas;if(!a||null==a)return n?null:(console.error("iiif-parser -> getPlaceholderCanvas() -> placeholderCanvas property not defined"),"This item cannot be played.");var i=a.items[0].items;if((null==i?void 0:i.length)>0&&null!=i[0].body&&"painting"===i[0].motivation){var o=i[0].body;return n?t=o.id:(t=Pt(o.label)||"This item cannot be played.",r=a.duration,Et=r||wt),t}}catch(e){throw e}}function Wt(e,t,r){e="text/srt"===e?"application/x-subrip":e;var n=d.default[e],a=n?n.extensions[0]:e,i=Pt(t)||"Untitled",o=i;Object.keys(t).length>1&&(i=t[Object.keys(t)[0]][0],o=t.none[0]);var s=Mt(i),l=s.isMachineGen;return s._,{id:r,label:"".concat(i," (.").concat(a,")"),filename:o,fileExt:a,isMachineGen:l}}function Zt(e,t){var r=[];return e&&(null==e?void 0:e.length)>0?(e.map((function(e){var t,n=null===(t=Pt(e.value,!0))||void 0===t?void 0:t.replace(/\n/g,"
    "),a=f.default(n,Bt({},Vt));r.push({label:Pt(e.label),value:a})})),r):(console.log("iiif-parser -> parseMetadata() -> no metadata in ",t),r)}function Yt(e,t){var r=[],n=e.requiredStatement;n&&(r=Zt([n],t));var a=e.rights;if(a){var i=/^(https?:\/\/[^\s]+)|(www\.[^\s]+)/.test(a);r.push({label:"License",value:i?"").concat(a,""):a})}return r}function Qt(e){var t=null;if(e){var r=e.service;if(r&&r.length>0){var n=r.filter((function(e){return"SearchService2"===e.type}));t=(null==n?void 0:n.length)>0?n[0].id:null}}return t}function Kt(e){if(!e)return null;var t,r=e.target.split("#t="),n=T(r,2),a=n[0],i=n[1],o=e.body;return 0===Object.keys(o).length?null:"TextualBody"===(null==o?void 0:o.type)?{id:e.id,time:parseFloat(i),timeStr:Tt(parseFloat(i),!0,!0),canvasId:a,value:null!==(t=null==o?void 0:o.value)&&void 0!==t?t:""}:null}function Xt(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function $t(e){for(var t=1;t0?e.filter((function(e){return e.canvasIndex==t+1&&!e.isCanvas})):[]).length>0}function ar(){var e,t,r,n,a=arguments.length>0&&void 0!==arguments[0]?arguments[0]:rr,i=arguments.length>1?arguments[1]:void 0;switch(i.type){case"updateManifest":var o=i.manifest,s=Ht(o),l=!!(n=o.behavior)&&(null==n?void 0:n.includes("auto-advance")),c=(r=o.label)?Pt(r).includes("[Playlist]"):(console.warn("playlist-parser -> getIsPlaylist() -> manifest.label not found"),!1),u=(null==(e=o.service)?void 0:e.length)>0&&"AnnotationService0"===(null===(t=e[0])||void 0===t?void 0:t.type)?e[0].id:null,d=function(e){try{var t=e.items,r=[];return t&&t.map((function(e,t){var n,a,i=e.annotations;if(i&&0!==(null===(n=i[0])||void 0===n?void 0:n.items.length))if((null===(a=i[0])||void 0===a?void 0:a.items.length)>0){var o=[],s=qt(e.annotations,"highlighting");(null==s?void 0:s.length)>0&&s.map((function(e){var t=Kt(e);t&&o.push(t)})),r.push({canvasMarkers:o,canvasIndex:t})}else r.push({canvasMarkers:[],canvasIndex:t});else r.push({canvasMarkers:[],canvasIndex:t})})),r}catch(e){throw e}}(o);return $t($t({},a),{},{manifest:o,allCanvases:s,autoAdvance:l,playlist:$t($t({},a.playlist),{},{isPlaylist:c,annotationServiceId:u,hasAnnotationService:!!u,markers:d})});case"switchCanvas":return $t($t({},a),{},{canvasIndex:i.canvasIndex,hasStructure:nr(a.canvasSegments,i.canvasIndex)});case"switchItem":return $t($t({},a),{},{currentNavItem:i.item});case"canvasDuration":return $t($t({},a),{},{canvasDuration:i.canvasDuration});case"canvasLink":return $t($t({},a),{},{canvasLink:i.canvasLink});case"canvasTargets":return $t($t({},a),{},{targets:i.canvasTargets});case"hasMultipleItems":return $t($t({},a),{},{hasMultiItems:i.isMultiSource});case"setSrcIndex":return $t($t({},a),{},{srcIndex:i.srcIndex});case"setItemStartTime":return $t($t({},a),{},{startTime:i.startTime});case"setAutoAdvance":return $t($t({},a),{},{autoAdvance:i.autoAdvance});case"setPlaylistMarkers":if(i.markers)return $t($t({},a),{},{playlist:$t($t({},a.playlist),{},{markers:i.markers})});if(i.updatedMarkers)return $t($t({},a),{},{playlist:$t($t({},a.playlist),{},{markers:a.playlist.markers.map((function(e){return e.canvasIndex===a.canvasIndex&&(e.canvasMarkers=i.updatedMarkers),e}))})});case"setIsEditing":return $t($t({},a),{},{playlist:$t($t({},a.playlist),{},{isEditing:i.isEditing})});case"setCanvasIsEmpty":return $t($t({},a),{},{canvasIsEmpty:i.isEmpty});case"setStructures":return $t($t({},a),{},{structures:i.structures});case"setCanvasSegments":var f=i.timespans.filter((function(e){return e.canvasIndex==a.canvasIndex+1&&!e.isCanvas}));return $t($t({},a),{},{canvasSegments:i.timespans,hasStructure:f.length>0});case"setCustomStart":var p=i.customStart,m=p.canvas,v=p.time;return $t($t({},a),{},{customStart:{startIndex:m,startTime:v},canvasIndex:m,hasStructure:nr(a.canvasSegments,m)});case"setRenderingFiles":return $t($t({},a),{},{renderings:$t({},i.renderings)});default:throw new Error("Unhandled action type: ".concat(i.type))}}function ir(t){var r=t.initialState,n=void 0===r?rr:r,a=t.children,i=e.useReducer(ar,n),o=T(i,2),s=o[0],l=o[1];return u.default.createElement(er.Provider,{value:s},u.default.createElement(tr.Provider,{value:l},a))}function or(){var t=e.useContext(er);if(void 0===t)throw new Error("useManifestState must be used within a ManifestProvider");return t}function sr(){var t=e.useContext(tr);if(void 0===t)throw new Error("useManifestDispatch must be used within a ManifestProvider");return t}function lr(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function cr(e){for(var t=1;t0&&void 0!==arguments[0]?arguments[0]:fr,t=arguments.length>1?arguments[1]:void 0;switch(t.type){case"updatePlayer":return cr(cr({},e),{},{player:t.player});case"navClick":return cr(cr({},e),{},{clickedUrl:t.clickedUrl,isClicked:!0});case"resetClick":return cr(cr({},e),{},{isClicked:!1});case"setTimeFragment":return cr(cr({},e),{},{startTime:t.startTime,endTime:t.endTime});case"setSearchMarkers":return cr(cr({},e),{},{searchMarkers:t.payload});case"setPlayingStatus":return cr(cr({},e),{},{isPlaying:t.isPlaying});case"setCaptionStatus":return cr(cr({},e),{},{captionOn:t.captionOn});case"setIsEnded":return cr(cr({},e),{},{isEnded:t.isEnded});case"setCurrentTime":return cr(cr({},e),{},{currentTime:t.currentTime});case"setPlayerFocusElement":return cr(cr({},e),{},{playerFocusElement:t.element?t.element:""});default:throw new Error("Unhandled action type: ".concat(t.type))}}function mr(t){var r=t.initialState,n=void 0===r?fr:r,a=t.children,i=e.useReducer(pr,n),o=T(i,2),s=o[0],l=o[1];return u.default.createElement(ur.Provider,{value:s},u.default.createElement(dr.Provider,{value:l},a))}function vr(){var t=e.useContext(ur);if(void 0===t)throw new Error("usePlayerState must be used within the PlayerProvider");return t}function hr(){var t=e.useContext(dr);if(void 0===t)throw new Error("usePlayerDispatch must be used within the PlayerProvider");return t}var gr=g(y((function(e){function t(e,t,r,n,a,i,o){try{var s=e[i](o),l=s.value}catch(e){return void r(e)}s.done?t(l):Promise.resolve(l).then(n,a)}e.exports=function(e){return function(){var r=this,n=arguments;return new Promise((function(a,i){var o=e.apply(r,n);function s(e){t(o,a,i,s,l,"next",e)}function l(e){t(o,a,i,s,l,"throw",e)}s(void 0)}))}},e.exports.__esModule=!0,e.exports.default=e.exports}))),yr=y((function(e){var t=S.default;function r(){e.exports=r=function(){return n},e.exports.__esModule=!0,e.exports.default=e.exports;var n={},a=Object.prototype,i=a.hasOwnProperty,o=Object.defineProperty||function(e,t,r){e[t]=r.value},s="function"==typeof Symbol?Symbol:{},l=s.iterator||"@@iterator",c=s.asyncIterator||"@@asyncIterator",u=s.toStringTag||"@@toStringTag";function d(e,t,r){return Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}),e[t]}try{d({},"")}catch(e){d=function(e,t,r){return e[t]=r}}function f(e,t,r,n){var a=t&&t.prototype instanceof v?t:v,i=Object.create(a.prototype),s=new R(n||[]);return o(i,"_invoke",{value:T(e,r,s)}),i}function p(e,t,r){try{return{type:"normal",arg:e.call(t,r)}}catch(e){return{type:"throw",arg:e}}}n.wrap=f;var m={};function v(){}function h(){}function g(){}var y={};d(y,l,(function(){return this}));var b=Object.getPrototypeOf,x=b&&b(b(O([])));x&&x!==a&&i.call(x,l)&&(y=x);var k=g.prototype=v.prototype=Object.create(y);function w(e){["next","throw","return"].forEach((function(t){d(e,t,(function(e){return this._invoke(t,e)}))}))}function E(e,r){function n(a,o,s,l){var c=p(e[a],e,o);if("throw"!==c.type){var u=c.arg,d=u.value;return d&&"object"==t(d)&&i.call(d,"__await")?r.resolve(d.__await).then((function(e){n("next",e,s,l)}),(function(e){n("throw",e,s,l)})):r.resolve(d).then((function(e){u.value=e,s(u)}),(function(e){return n("throw",e,s,l)}))}l(c.arg)}var a;o(this,"_invoke",{value:function(e,t){function i(){return new r((function(r,a){n(e,t,r,a)}))}return a=a?a.then(i,i):i()}})}function T(e,t,r){var n="suspendedStart";return function(a,i){if("executing"===n)throw new Error("Generator is already running");if("completed"===n){if("throw"===a)throw i;return q()}for(r.method=a,r.arg=i;;){var o=r.delegate;if(o){var s=S(o,r);if(s){if(s===m)continue;return s}}if("next"===r.method)r.sent=r._sent=r.arg;else if("throw"===r.method){if("suspendedStart"===n)throw n="completed",r.arg;r.dispatchException(r.arg)}else"return"===r.method&&r.abrupt("return",r.arg);n="executing";var l=p(e,t,r);if("normal"===l.type){if(n=r.done?"completed":"suspendedYield",l.arg===m)continue;return{value:l.arg,done:r.done}}"throw"===l.type&&(n="completed",r.method="throw",r.arg=l.arg)}}}function S(e,t){var r=t.method,n=e.iterator[r];if(void 0===n)return t.delegate=null,"throw"===r&&e.iterator.return&&(t.method="return",t.arg=void 0,S(e,t),"throw"===t.method)||"return"!==r&&(t.method="throw",t.arg=new TypeError("The iterator does not provide a '"+r+"' method")),m;var a=p(n,e.iterator,t.arg);if("throw"===a.type)return t.method="throw",t.arg=a.arg,t.delegate=null,m;var i=a.arg;return i?i.done?(t[e.resultName]=i.value,t.next=e.nextLoc,"return"!==t.method&&(t.method="next",t.arg=void 0),t.delegate=null,m):i:(t.method="throw",t.arg=new TypeError("iterator result is not an object"),t.delegate=null,m)}function C(e){var t={tryLoc:e[0]};1 in e&&(t.catchLoc=e[1]),2 in e&&(t.finallyLoc=e[2],t.afterLoc=e[3]),this.tryEntries.push(t)}function I(e){var t=e.completion||{};t.type="normal",delete t.arg,e.completion=t}function R(e){this.tryEntries=[{tryLoc:"root"}],e.forEach(C,this),this.reset(!0)}function O(e){if(e){var t=e[l];if(t)return t.call(e);if("function"==typeof e.next)return e;if(!isNaN(e.length)){var r=-1,n=function t(){for(;++r=0;--n){var a=this.tryEntries[n],o=a.completion;if("root"===a.tryLoc)return r("end");if(a.tryLoc<=this.prev){var s=i.call(a,"catchLoc"),l=i.call(a,"finallyLoc");if(s&&l){if(this.prev=0;--r){var n=this.tryEntries[r];if(n.tryLoc<=this.prev&&i.call(n,"finallyLoc")&&this.prev=0;--t){var r=this.tryEntries[t];if(r.finallyLoc===e)return this.complete(r.completion,r.afterLoc),I(r),m}},catch:function(e){for(var t=this.tryEntries.length-1;t>=0;--t){var r=this.tryEntries[t];if(r.tryLoc===e){var n=r.completion;if("throw"===n.type){var a=n.arg;I(r)}return a}}throw new Error("illegal catch attempt")},delegateYield:function(e,t,r){return this.delegate={iterator:O(e),resultName:t,nextLoc:r},"next"===this.method&&(this.arg=void 0),m}},n}e.exports=r,e.exports.__esModule=!0,e.exports.default=e.exports})),br=yr(),xr=br;try{regeneratorRuntime=br}catch(e){"object"==typeof globalThis?globalThis.regeneratorRuntime=br:Function("r","regeneratorRuntime = r")(br)}function kr(){}function wr(){}wr.resetWarningCache=kr;var Er=function(){function e(e,t,r,n,a,i){if("SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"!==i){var o=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw o.name="Invariant Violation",o}}function t(){return e}e.isRequired=e;var r={array:e,bigint:e,bool:e,func:e,number:e,object:e,string:e,symbol:e,any:e,arrayOf:t,element:e,elementType:e,instanceOf:t,node:e,objectOf:t,oneOf:t,oneOfType:t,shape:t,exact:t,checkPropTypes:wr,resetWarningCache:kr};return r.PropTypes=r,r},Tr=y((function(e){e.exports=Er()})),Sr=function(){return u.default.createElement("div",{className:"lds-spinner"},u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null),u.default.createElement("div",null))};function Cr(t){var r,n=t.manifestUrl,i=t.customErrorMessage,o=t.emptyManifestMessage,s=t.startCanvasId,l=t.startCanvasTime,c=t.children,d=t.manifest,f=e.useState(d),p=T(f,2),m=p[0],v=p[1],h=sr(),g=hr(),y=a.useErrorBoundary().showBoundary,b=function(){var e=gr(xr.mark((function e(t){var n,a;return xr.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=new AbortController,n={},a=t.replace(/[\?#].*(?=\/)/i,""),e.prev=3,e.next=6,fetch(a,n,{signal:r.signal}).then((function(e){if(200!=e.status&&201!=e.status)throw new Error("Failed to fetch Manifest. Please check again.");return e.json()})).then((function(e){if(!e)throw new Error(bt);v(e)})).catch((function(e){throw console.log("Error fetching manifest, ",e),new Error("Failed to fetch Manifest. Please check again.")}));case 6:e.next=11;break;case 8:e.prev=8,e.t0=e.catch(3),y(e.t0);case 11:case"end":return e.stop()}}),e,null,[[3,8]])})));return function(t){return e.apply(this,arguments)}}();return e.useEffect((function(){return bt=i||yt,function(e){kt=e||xt}(o),!m&&n&&b(n),function(){r&&r.abort()}}),[]),e.useEffect((function(){if(m){var e=function(e){var t=[],r=[],n=e.rendering,a=e.items;return n&&n.map((function(e){var r=Wt(e.format,e.label,e.id);t.push(r)})),a&&a.map((function(e,t){var n=e.rendering,a=[];n&&n.map((function(e){var t=Wt(e.format,e.label,e.id);a.push(t)})),r.push({label:Pt(e.label)||"Section ".concat(t+1),files:a})})),{manifest:t,canvas:r}}(m);h({renderings:e,type:"setRenderingFiles"});var t=function(e,t,r){var n=e.start,a={};if(!n&&void 0===t&&void 0===r)return{type:"C",canvas:0,time:0};null!=t||null!=r?(a={id:t,selector:{type:"PointSelector",t:void 0===r?0:r},type:void 0===r?"Canvas":"SpecificResource"},null!=r&&(a.source=t)):n&&(a=n);var i=Ht(e),o=function(e,n,a){var o=a,s=0;if(i&&(null==i?void 0:i.length)>0){if(e)if(void 0===(s=i.findIndex((function(t){return t.canvasId===e})))||s<0)console.warn("Given Canvas was not found in Manifest, ",t),o=0,s=0;else{var l=i[s];if(null!=l.range&&"SpecificResource"===n){var c=l.range,u=c.start,d=c.end;a>=u&&a<=d||(console.warn("Given start time is not within Canvas duration, ",r),o=0)}}}else console.warn("No Canvases in given Manifest"),o=0;return{currentIndex:s,startTime:o}};if(null!=a)switch(a.type){case"Canvas":var s=o(a.id,a.type,0);return{type:"C",canvas:s.currentIndex,time:s.startTime};case"SpecificResource":var l=a.selector.t;return{type:"SR",canvas:(s=o(a.source,a.type,l)).currentIndex,time:s.startTime}}}(m,s,l);h({customStart:t,type:"setCustomStart"}),"SR"==t.type&&g({currentTime:t.time,type:"setCurrentTime"}),h({manifest:m,type:"updateManifest"})}}),[m]),m?u.default.createElement(u.default.Fragment,null,c):u.default.createElement(Sr,null)}function Ir(e){var t=e.error,r=e.resetErrorBoundary;return u.default.createElement("div",{role:"alert",className:"ramp--error-message__alert"},u.default.createElement("span",{className:"ramp--error-message__message",dangerouslySetInnerHTML:{__html:t.message}}),u.default.createElement("button",{className:"ramp--error-message__reset-button",onClick:r},"Try again"))}Cr.propTypes={manifest:Tr.object,customErrorMessage:Tr.string,emptyManifestMessage:Tr.string,manifestUrl:Tr.string,startCanvasId:Tr.string,startCanvasTime:Tr.number,children:Tr.node};var Rr=function(e){e.message;var t=e.children;return u.default.createElement(a.ErrorBoundary,{FallbackComponent:Ir,onReset:function(e){}},t)};function Or(e){var t=e.manifestUrl,r=e.manifest,n=e.customErrorMessage,a=e.emptyManifestMessage,i=e.startCanvasId,o=e.startCanvasTime,s=e.children;return t||r?u.default.createElement(ir,null,u.default.createElement(mr,null,u.default.createElement(Rr,null,u.default.createElement(Cr,{manifestUrl:t,manifest:r,customErrorMessage:n,emptyManifestMessage:a,startCanvasId:i,startCanvasTime:o},s)))):u.default.createElement("p",null,"Please provide a valid manifest.")}Rr.propTypes={message:Tr.string,children:Tr.object},Or.propTypes={manifestUrl:Tr.string,manifest:Tr.object,customErrorMessage:Tr.string,emptyManifestMessage:Tr.string,startCanvasId:Tr.string,startCanvasTime:Tr.number};var qr=function(){return Z.Date.now()},jr=/\s/;var Mr=function(e){for(var t=e.length;t--&&jr.test(e.charAt(t)););return t},Dr=/^\s+/;var Pr=function(e){return e?e.slice(0,Mr(e)+1).replace(Dr,""):e},Ar="[object Symbol]";var Lr=function(e){return"symbol"==typeof e||ze(e)&&oe(e)==Ar},_r=NaN,Nr=/^[-+]0x[0-9a-f]+$/i,Fr=/^0b[01]+$/i,Ur=/^0o[0-7]+$/i,Br=parseInt;var Vr=function(e){if("number"==typeof e)return e;if(Lr(e))return _r;if(se(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=se(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=Pr(e);var r=Fr.test(e);return r||Ur.test(e)?Br(e.slice(2),r?2:8):Nr.test(e)?_r:+e},Hr="Expected a function",Gr=Math.max,zr=Math.min;var Jr=function(e,t,r){var n,a,i,o,s,l,c=0,u=!1,d=!1,f=!0;if("function"!=typeof e)throw new TypeError(Hr);function p(t){var r=n,i=a;return n=a=void 0,c=t,o=e.apply(i,r)}function m(e){var r=e-l;return void 0===l||r>=t||r<0||d&&e-c>=i}function v(){var e=qr();if(m(e))return h(e);s=setTimeout(v,function(e){var r=t-(e-l);return d?zr(r,i-(e-c)):r}(e))}function h(e){return s=void 0,f&&n?p(e):(n=a=void 0,o)}function g(){var e=qr(),r=m(e);if(n=arguments,a=this,l=e,r){if(void 0===s)return function(e){return c=e,s=setTimeout(v,t),u?p(e):o}(l);if(d)return clearTimeout(s),s=setTimeout(v,t),p(l)}return void 0===s&&(s=setTimeout(v,t)),o}return t=Vr(t)||0,se(r)&&(u=!!r.leading,i=(d="maxWait"in r)?Gr(Vr(r.maxWait)||0,t):i,f="trailing"in r?!!r.trailing:f),g.cancel=function(){void 0!==s&&clearTimeout(s),c=0,n=l=a=s=void 0},g.flush=function(){return void 0===s?o:h(qr())},g},Wr="Expected a function";var Zr=function(e,t,r){var n=!0,a=!0;if("function"!=typeof e)throw new TypeError(Wr);return se(r)&&(n="leading"in r?!!r.leading:n,a="trailing"in r?!!r.trailing:a),Jr(e,t,{leading:n,maxWait:t,trailing:a})};y((function(e,t){!function(e){var t=r(e);function r(e){return e&&e.__esModule?e:{default:e}}var n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},a={markerStyle:{width:"7px","border-radius":"30%","background-color":"red"},markerTip:{display:!0,text:function(e){return"Break: "+e.text},time:function(e){return e.time}},breakOverlay:{display:!1,displayTime:3,text:function(e){return"Break overlay: "+e.overlayText},style:{width:"100%",height:"20%","background-color":"rgba(0,0,0,0.7)",color:"white","font-size":"17px"}},onMarkerClick:function(e){},onMarkerReached:function(e,t){},markers:[]};function i(){var e=(new Date).getTime();return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,(function(t){var r=(e+16*Math.random())%16|0;return e=Math.floor(e/16),("x"==t?r:3&r|8).toString(16)}))}function o(e){var t,r={top:0,bottom:0,left:0,width:0,height:0,right:0};try{t=e.getBoundingClientRect()}catch(e){t=r}return t}var s=-1;function l(e){if(!t.default.mergeOptions){var r=function(e){return!!e&&"object"===(void 0===e?"undefined":n(e))&&"[object Object]"===toString.call(e)&&e.constructor===Object},l=function e(t,n){var a={};return[t,n].forEach((function(t){t&&Object.keys(t).forEach((function(n){var i=t[n];r(i)?(r(a[n])||(a[n]={}),a[n]=e(a[n],i)):a[n]=i}))})),a};t.default.mergeOptions=l}t.default.dom.createEl||(t.default.dom.createEl=function(e,r,n){var a=t.default.Player.prototype.dom.createEl(e,r);return n&&Object.keys(n).forEach((function(e){a.setAttribute(e,n[e])})),a});var c=t.default.mergeOptions(a,e),u={},d=[],f=s,p=this,m=null,v=null,h=s;function g(){d.sort((function(e,t){return c.markerTip.time(e)-c.markerTip.time(t)}))}function y(e){e.forEach((function(e){e.key=i(),p.el().querySelector(".vjs-progress-holder").appendChild(k(e)),u[e.key]=e,d.push(e)})),g()}function b(e){return c.markerTip.time(e)/p.duration()*100}function x(e,t){t.className="vjs-marker "+(e.class||""),Object.keys(c.markerStyle).forEach((function(e){t.style[e]=c.markerStyle[e]}));var r=e.time/p.duration();if((r<0||r>1)&&(t.style.display="none"),t.style.left=b(e)+"%",e.duration)t.style.width=e.duration/p.duration()*100+"%",t.style.marginLeft="0px";else{var n=o(t);t.style.marginLeft=n.width/2+"px"}}function k(e){var r=t.default.dom.createEl("div",{},{"data-marker-key":e.key,"data-marker-time":c.markerTip.time(e)});return x(e,r),r.addEventListener("click",(function(t){var r=!1;if("function"==typeof c.onMarkerClick&&(r=!1===c.onMarkerClick(e)),!r){var n=this.getAttribute("data-marker-key");p.currentTime(c.markerTip.time(u[n]))}})),c.markerTip.display&&T(r),r}function w(e){d.forEach((function(t){var r=p.el().querySelector(".vjs-marker[data-marker-key='"+t.key+"']"),n=c.markerTip.time(t);(e||r.getAttribute("data-marker-time")!==n)&&(x(t,r),r.setAttribute("data-marker-time",n))})),g()}function E(e){v&&(h=s,v.style.visibility="hidden"),f=s;var t=[];e.forEach((function(e){var r=d[e];if(r){delete u[r.key],t.push(e);var n=p.el().querySelector(".vjs-marker[data-marker-key='"+r.key+"']");n&&n.parentNode.removeChild(n)}})),t.reverse(),t.forEach((function(e){d.splice(e,1)})),g()}function T(e){e.addEventListener("mouseover",(function(){var t=u[e.getAttribute("data-marker-key")];if(m){c.markerTip.html?m.querySelector(".vjs-tip-inner").innerHTML=c.markerTip.html(t):m.querySelector(".vjs-tip-inner").innerText=c.markerTip.text(t),m.style.left=b(t)+"%";var r=o(m),n=o(e);m.style.marginLeft=-parseFloat(r.width/2)+parseFloat(n.width/4)+"px",m.style.visibility="visible"}})),e.addEventListener("mouseout",(function(){m&&(m.style.visibility="hidden")}))}function S(){m=t.default.dom.createEl("div",{className:"vjs-tip",innerHTML:"
    "}),p.el().querySelector(".vjs-progress-holder").appendChild(m)}function C(){if(c.breakOverlay.display&&!(f<0)){var e=p.currentTime(),t=d[f],r=c.markerTip.time(t);e>=r&&e<=r+c.breakOverlay.displayTime?(h!==f&&(h=f,v&&(v.querySelector(".vjs-break-overlay-text").innerHTML=c.breakOverlay.text(t))),v&&(v.style.visibility="visible")):(h=s,v&&(v.style.visibility="hidden"))}}function I(){v=t.default.dom.createEl("div",{className:"vjs-break-overlay",innerHTML:"
    "}),Object.keys(c.breakOverlay.style).forEach((function(e){v&&(v.style[e]=c.breakOverlay.style[e])})),p.el().appendChild(v),h=s}function R(){O(),C(),e.onTimeUpdateAfterMarkerUpdate&&e.onTimeUpdateAfterMarkerUpdate()}function O(){if(d.length){var t=function(e){return e=c.markerTip.time(d[f])&&r=c.markerTip.time(d[i])&&re){p.currentTime(r);break}}},prev:function(){for(var e=p.currentTime(),t=d.length-1;t>=0;t--){var r=c.markerTip.time(d[t]);if(r+.52&&!window.matchMedia("(pointer: fine").matches,an=dn.mobile||Xr||on),!en){var fn=window.navigator&&window.navigator.userAgent||"";Kr=/iPod/i.test(fn),(Qr=fn.match(/OS (\d+)_/i))&&Qr[1]&&Qr[1],Xr=/Android/i.test(fn),function(){var e=fn.match(/Android (\d+)(?:\.(\d+))?(?:\.(\d+))*/i);if(!e)return null;var t=e[1]&&parseFloat(e[1]),r=e[2]&&parseFloat(e[2]);t&&r&&parseFloat(e[1]+"."+e[2])}(),/Firefox/i.test(fn),$r=/Edg/i.test(fn),en=/Chrome/i.test(fn)||/CriOS/i.test(fn),tn=!$r&&en,function(){var e=fn.match(/(Chrome|CriOS)\/(\d+)/);e&&e[2]&&parseFloat(e[2])}(),Yr=/MSIE\s(\d+)\.\d/.exec(fn),!(Yr&&parseFloat(Yr[1]))&&/Trident\/7.0/i.test(fn)&&/rv:11.0/.test(fn)&&11,cn=/Tizen/i.test(fn),un=/Web0S/i.test(fn),rn=/Safari/i.test(fn)&&!tn&&!Xr&&!$r&&!cn&&!un,/Windows/i.test(fn),sn=/iPhone/i.test(fn)&&!nn,ln=sn||nn||Kr,on=navigator.maxTouchPoints&&navigator.maxTouchPoints>2&&!window.matchMedia("(pointer: fine").matches,nn=on&&!Xr&&!sn,an=Xr||ln||sn||on||/Mobi/i.test(fn)}var pn,mn,vn,hn,gn=function(t,r){var n=e.useState((function(){return function(e,t){try{var r;return null!==(r=JSON.parse(localStorage.getItem(e)))&&void 0!==r?r:t}catch(e){return t}}(t,r)})),a=T(n,2),i=a[0],o=a[1];return e.useEffect((function(){try{localStorage.setItem(t,JSON.stringify(i))}catch(e){}}),[t,i]),[i,o]},yn=function(){return u.default.createElement("svg",{viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg",style:{fill:"white",height:"1rem",width:"1rem",scale:.8}},u.default.createElement("path",{fillRule:"evenodd",clipRule:"evenodd",d:"M21.1213 2.70705C19.9497 1.53548 18.0503 1.53547 16.8787 2.70705L15.1989 4.38685L7.29289 12.2928C7.16473 12.421 7.07382 12.5816 7.02986 12.7574L6.02986 16.7574C5.94466 17.0982 6.04451 17.4587 6.29289 17.707C6.54127 17.9554 6.90176 18.0553 7.24254 17.9701L11.2425 16.9701C11.4184 16.9261 11.5789 16.8352 11.7071 16.707L19.5556 8.85857L21.2929 7.12126C22.4645 5.94969 22.4645 4.05019 21.2929 2.87862L21.1213 2.70705ZM18.2929 4.12126C18.6834 3.73074 19.3166 3.73074 19.7071 4.12126L19.8787 4.29283C20.2692 4.68336 20.2692 5.31653 19.8787 5.70705L18.8622 6.72357L17.3068 5.10738L18.2929 4.12126ZM15.8923 6.52185L17.4477 8.13804L10.4888 15.097L8.37437 15.6256L8.90296 13.5112L15.8923 6.52185ZM4 7.99994C4 7.44766 4.44772 6.99994 5 6.99994H10C10.5523 6.99994 11 6.55223 11 5.99994C11 5.44766 10.5523 4.99994 10 4.99994H5C3.34315 4.99994 2 6.34309 2 7.99994V18.9999C2 20.6568 3.34315 21.9999 5 21.9999H16C17.6569 21.9999 19 20.6568 19 18.9999V13.9999C19 13.4477 18.5523 12.9999 18 12.9999C17.4477 12.9999 17 13.4477 17 13.9999V18.9999C17 19.5522 16.5523 19.9999 16 19.9999H5C4.44772 19.9999 4 19.5522 4 18.9999V7.99994Z",fill:"#fffff"}))},bn=function(){return u.default.createElement("svg",{viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",stroke:"#ffffff",style:{height:"1rem",width:"1rem",scale:.8}},u.default.createElement("g",{strokeWidth:"0",strokeLinecap:"round",strokeLinejoin:"round"},u.default.createElement("path",{d:"M10 12V17",stroke:"#ffffff",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),u.default.createElement("path",{d:"M14 12V17",stroke:"#ffffff",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),u.default.createElement("path",{d:"M4 7H20",stroke:"#ffffff",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),u.default.createElement("path",{d:"M6 10V18C6 19.6569 7.34315 21 9 21H15C16.6569 21 18 19.6569 18 18V10",stroke:"#ffffff",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"}),u.default.createElement("path",{d:"M9 5C9 3.89543 9.89543 3 11 3H13C14.1046 3 15 3.89543 15 5V7H9V5Z",stroke:"#ffffff",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})))},xn=function(){return u.default.createElement("svg",{viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",style:{height:"1rem",width:"1rem",scale:.8}},u.default.createElement("g",{strokeWidth:"0",strokeLinecap:"round",strokeLinejoin:"round"},u.default.createElement("path",{id:"Vector",d:"M6 12L10.2426 16.2426L18.727 7.75732",stroke:"#ffffff",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round"})))},kn=function(){return u.default.createElement("svg",{fill:"#ffffff",viewBox:"0 0 32 32",version:"1.1",xmlns:"http://www.w3.org/2000/svg",style:{height:"1rem",width:"1rem",scale:.8}},u.default.createElement("g",{strokeWidth:"0",strokeLinecap:"round",strokeLinejoin:"round"},u.default.createElement("path",{d:"M19.587 16.001l6.096 6.096c0.396 0.396 0.396 1.039 0 1.435l-2.151 2.151c-0.396 0.396-1.038 0.396-1.435 0l-6.097-6.096-6.097 6.096c-0.396 0.396-1.038 0.396-1.434 0l-2.152-2.151c-0.396-0.396-0.396-1.038 0-1.435l6.097-6.096-6.097-6.097c-0.396-0.396-0.396-1.039 0-1.435l2.153-2.151c0.396-0.396 1.038-0.396 1.434 0l6.096 6.097 6.097-6.097c0.396-0.396 1.038-0.396 1.435 0l2.151 2.152c0.396 0.396 0.396 1.038 0 1.435l-6.096 6.096z"})))},wn=function(e){var t=e.flip,r=void 0!==t&&t;return u.default.createElement("svg",{viewBox:"0 0 24 24",fill:"none",xmlns:"http://www.w3.org/2000/svg",style:{fill:"white",height:"1.25rem",width:"1.25rem",transform:r?"rotate(180deg)":"rotate(0)"}},u.default.createElement("g",{strokeWidth:"0",strokeLinecap:"round",strokeLinejoin:"round"},u.default.createElement("path",{d:"M4 20L15.3333 12L4 4V20Z",fill:"#ffffff"}),u.default.createElement("path",{d:"M20 4H17.3333V20H20V4Z",fill:"#ffffff"})))},En=function(){return u.default.createElement("svg",{viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg",style:{height:"0.75rem",width:"0.75rem"},className:"structure-item-locked"},u.default.createElement("g",{strokeWidth:"0",strokeLinecap:"round",strokeLinejoin:"round"},u.default.createElement("path",{fillRule:"evenodd",clipRule:"evenodd",d:"M5.25 10.0546V8C5.25 4.27208 8.27208 1.25 12 1.25C15.7279 1.25 18.75 4.27208 18.75 8V10.0546C19.8648 10.1379 20.5907 10.348 21.1213 10.8787C22 11.7574 22 13.1716 22 16C22 18.8284 22 20.2426 21.1213 21.1213C20.2426 22 18.8284 22 16 22H8C5.17157 22 3.75736 22 2.87868 21.1213C2 20.2426 2 18.8284 2 16C2 13.1716 2 11.7574 2.87868 10.8787C3.40931 10.348 4.13525 10.1379 5.25 10.0546ZM6.75 8C6.75 5.10051 9.10051 2.75 12 2.75C14.8995 2.75 17.25 5.10051 17.25 8V10.0036C16.867 10 16.4515 10 16 10H8C7.54849 10 7.13301 10 6.75 10.0036V8Z",fill:"#000000"})))},Tn=function(e){var t=e.flip,r=void 0!==t&&t;return u.default.createElement("svg",{viewBox:"0 0 1024 1024",fill:"#ffffff",xmlns:"http://www.w3.org/2000/svg",style:{height:"1rem",width:"1rem",scale:.8,transform:r?"rotate(180deg)":"rotate(0)"}},u.default.createElement("g",{id:"SVGRepo_bgCarrier",strokeWidth:"0"}),u.default.createElement("g",{id:"SVGRepo_tracerCarrier",strokeLinecap:"round",strokeLinejoin:"round"}),u.default.createElement("g",{id:"SVGRepo_iconCarrier"},u.default.createElement("path",{d:"M256 120.768L306.432 64 768 512l-461.568 448L256 903.232 659.072 512z",fill:"#ffffff"})))},Sn=function(){return u.default.createElement("svg",{viewBox:"0 0 24 24",fill:"#fffff",xmlns:"http://www.w3.org/2000/svg",style:{fill:"none",height:"1.25rem",width:"1.25rem"}},u.default.createElement("g",{id:"SVGRepo_bgCarrier",strokeWidth:"0"}),u.default.createElement("g",{id:"SVGRepo_tracerCarrier",strokeLinecap:"round",strokeLinejoin:"round"}),u.default.createElement("g",{id:"SVGRepo_iconCarrier"},u.default.createElement("rect",{width:"24",height:"24",fill:"none"}),u.default.createElement("path",{d:"M5 12V18C5 18.5523 5.44772 19 6 19H18C18.5523 19 19 18.5523 19 18V12",stroke:"#ffffff",strokeLinecap:"round",strokeLinejoin:"round"}),u.default.createElement("path",{d:"M12 3L12 15M12 15L16 11M12 15L8 11",stroke:"#ffffff",strokeLinecap:"round",strokeLinejoin:"round"})))},Cn=g(y((function(e){e.exports=function(e,t){return t||(t=e.slice(0)),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))},e.exports.__esModule=!0,e.exports.default=e.exports})));function In(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!r){if(Array.isArray(e)||(r=function(e,t){if(!e)return;if("string"==typeof e)return Rn(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);"Object"===r&&e.constructor&&(r=e.constructor.name);if("Map"===r||"Set"===r)return Array.from(e);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return Rn(e,t)}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,a=function(){};return{s:a,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,o=!0,s=!1;return{s:function(){r=r.call(e)},n:function(){var e=r.next();return o=e.done,e},e:function(e){s=!0,i=e},f:function(){try{o||null==r.return||r.return()}finally{if(s)throw i}}}}function Rn(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r1&&void 0!==a[1]?a[1]:"",e.next=3,fetch(t).then((function(e){return e.headers.get("Content-Type").includes("application/json")?e.json():{}})).then((function(e){var n=e.items,a=[];return(null==n?void 0:n.length)>0&&n.map((function(e,n){var i=qt(e.annotations,"supplementing"),o=[];if(i.length>0){var s=i[0].body;if("TextualBody"===s.type){var l=Mt(r.length>0?r:s.label?Pt(s.label):"Canvas-".concat(n)),c=l.isMachineGen,u=l.labelText;o.push({url:void 0===s.id?t:s.id,title:u,isMachineGen:c,id:"".concat(u,"-").concat(n),format:""})}else i.forEach((function(e,t){var r=e.body,a="",i="";if(r.label&&Object.keys(r.label).length>0){var s=Object.keys(r.label);(null==s?void 0:s.length)>1?(a=Pt(r.label),i=r.label.hasOwnProperty("none")?Pt(r.label.none[0]):a):a=Pt(r.label)}else a="".concat(t);var l=r.id,c=Dt(l),u=Mt(a),d=u.isMachineGen,f=u.labelText;""===i&&(i=f),1!==c&&3!==c||o.push({title:f,filename:i,url:l,isMachineGen:d,id:"".concat(f,"-").concat(n,"-").concat(t),format:r.format||""})}))}a.push({canvasId:n,items:o})})),a})).catch((function(e){return console.error("transcript-parser -> readSupplementingAnnotations() -> error fetching transcript resource at, ",t),[]}));case 3:return n=e.sent,e.abrupt("return",n);case 5:case"end":return e.stop()}}),e)}))),Ln.apply(this,arguments)}function _n(e){return Nn.apply(this,arguments)}function Nn(){return Nn=gr(xr.mark((function e(t){var r,n,a;return xr.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t&&null!=t&&0!=t.length){e.next=5;break}return console.error("No transcripts given as input"),e.abrupt("return",[]);case 5:return r=[],t.map((function(e){return r.push({canvasId:e.canvasId,items:[]})})),e.next=9,Promise.all(t.map(function(){var e=gr(xr.mark((function e(t){var n,a,i;return xr.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return n=t.canvasId,a=t.items,e.next=3,Promise.all(a.map(function(){var e=gr(xr.mark((function e(t,a){var i,o,s,l,c,u,d,f;return xr.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return i=t.title,o=t.url,e.next=3,An(o,i);case 3:if(s=e.sent,l=Mt(i),c=l.isMachineGen,u=l.labelText,d=[],(null==s?void 0:s.length)>0&&(d=s.map((function(e){return e.items})).flat(),f=Fn(r.concat(s),"canvasId","items"),r=f),0!==s.length&&0!==d.length){e.next=11;break}return e.abrupt("return",{title:u,filename:u,url:o,isMachineGen:c,id:"".concat(u,"-").concat(n,"-").concat(a),format:""});case 11:return e.abrupt("return",null);case 12:case"end":return e.stop()}}),e)})));return function(t,r){return e.apply(this,arguments)}}()));case 3:return i=e.sent,e.abrupt("return",{canvasId:n,items:i.filter((function(e){return null!=e}))});case 5:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}()));case 9:return n=e.sent,a=Fn(r.concat(n),"canvasId","items"),e.abrupt("return",a);case 12:case"end":return e.stop()}}),e)}))),Nn.apply(this,arguments)}function Fn(e,t,r){return e.reduce((function(e,n){var a=e.filter((function(e){return e[t]==n[t]}));if((null==a?void 0:a.length)>0){var i=a[0];i[r]=i[r].concat(n[r])}else e.push(n);return e}),[])}function Un(e,t,r){return Bn.apply(this,arguments)}function Bn(){return(Bn=gr(xr.mark((function e(t,r,n){var a,i,o,s,l,c,u,d,f,p,m,v,h,g,y,b,x;return xr.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(a=[],i=t,void 0!==t){e.next=4;break}return e.abrupt("return",{tData:a,tUrl:i,tType:Dn.invalid});case 4:return o=null,s=null,e.next=8,fetch(t).then(Ct).then((function(e){o=e.headers.get("Content-Type"),s=e})).catch((function(e){console.error("transcript-parser -> parseTranscriptData() -> fetching transcript -> ",e)}));case 8:if(null!=o){e.next=10;break}return e.abrupt("return",{tData:[],tUrl:i,tType:Dn.invalid});case 10:if(l=Mn.filter((function(e){return e.type.includes(o.split(";")[0])})),c=Mn.filter((function(e){return e.type.includes(n)})),u="",(null==c?void 0:c.length)>0?u=c[0].ext:l.length>0?u=l[0].ext:(d=t.split(".").reverse()[0],f=Mn.filter((function(e){return e.ext===d})),u=f.length>0?d:""),void 0!==r){e.next=16;break}return e.abrupt("return",{tData:a,tUrl:i,tType:Dn.noTranscript});case 16:e.t0=u,e.next="json"===e.t0?19:"txt"===e.t0?28:"srt"===e.t0||"vtt"===e.t0?39:"docx"===e.t0?49:53;break;case 19:return e.next=21,s.json();case 21:if("Manifest"!==(null==(v=e.sent)?void 0:v.type)){e.next=26;break}return e.abrupt("return",zn(v,t,r));case 26:return h=Gn(v),e.abrupt("return",{tData:h.tData,tUrl:i,tType:h.tType,tFileExt:u});case 28:return e.next=30,s.text();case 30:if(p=e.sent,0!=(m=p.split("\n")).length){e.next=36;break}return e.abrupt("return",{tData:[],tUrl:t,tType:Dn.noTranscript});case 36:return g=aa(m),e.abrupt("return",{tData:g,tUrl:t,tType:Dn.plainText,tFileExt:u});case 38:case 39:return e.next=41,s.text();case 41:if(p=e.sent,0!=(m=p.split("\n")).length){e.next=47;break}return e.abrupt("return",{tData:[],tUrl:t,tType:Dn.noTranscript});case 47:return y=Zn(p,"srt"===u),b=y.tData,x=y.tType,e.abrupt("return",{tData:b,tUrl:t,tType:x,tFileExt:u});case 49:return e.next=51,Vn(s);case 51:return a=e.sent,e.abrupt("return",{tData:na(a),tUrl:t,tType:Dn.docx,tFileExt:u});case 53:return e.abrupt("return",{tData:[],tUrl:t,tType:Dn.noSupport});case 54:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Vn(e){return Hn.apply(this,arguments)}function Hn(){return Hn=gr(xr.mark((function e(t){var r,n,a;return xr.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return r=null,e.next=3,t.blob();case 3:return n=e.sent,a=new File([n],name,{type:t.headers.get("content-type")}),e.next=7,v.default.convertToHtml({arrayBuffer:a}).then((function(e){r=e.value})).catch((function(e){console.error(e)}));case 7:return e.abrupt("return",r);case 8:case"end":return e.stop()}}),e)}))),Hn.apply(this,arguments)}function Gn(e){if(0==e.length)return{tData:[],tType:Dn.noTranscript};var t,r=[],n=In(e);try{for(n.s();!(t=n.n()).done;){var a=t.value;if(a.speaker){var i,o=a.speaker,s=In(a.spans);try{for(s.s();!(i=s.n()).done;){var l=i.value;l.speaker=o,r.push(l)}}catch(e){s.e(e)}finally{s.f()}}else{var c,u=In(a.spans);try{for(u.s();!(c=u.n()).done;){var d=c.value;d.format="text/plain",d.tag=Pn.timedCue,r.push(d)}}catch(e){u.e(e)}finally{u.f()}}}}catch(e){n.e(e)}finally{n.f()}return{tData:r,tType:Dn.timedText}}function zn(e,t,r){var n,a=t,i=[];if(e.annotations)i=qt(e.annotations,"supplementing");else if((null===(n=e.items)||void 0===n?void 0:n.length)>0){var o;i=qt(null===(o=e.items[r])||void 0===o?void 0:o.annotations,"supplementing")}return i.length>0?"TextualBody"!=i[0].body.type?function(e){return Jn.apply(this,arguments)}(i[0]):{tData:Wn(i),tUrl:a,tType:Dn.timedText,tFileExt:"json"}:{tData:[],tUrl:a,tType:Dn.noTranscript}}function Jn(){return(Jn=gr(xr.mark((function e(t){var r,n,a,i,o,s,l;return xr.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(r=[],n="",a=t.body,i=a.id,o=a.type,s=a.format,l="","Text"!==o){e.next=12;break}return e.next=10,fetch(i).then(Ct).then((function(e){return e.text()})).then((function(e){if(On.webvtt.includes(s)||On.srt.includes(s)){var t=Zn(e,On.srt.includes(s));r=t.tData,n=t.tType,l=Mn.filter((function(e){return e.type.includes(s)}))[0].ext}else{var a=e.split("\n");r=aa(a),n=Dn.plainText,l="txt"}})).catch((function(e){throw console.error("transcript-parser -> parseExternalAnnotations() -> fetching external transcript -> ",e),e}));case 10:e.next=15;break;case 12:if("AnnotationPage"!==o){e.next=15;break}return e.next=15,fetch(i).then(Ct).then((function(e){return e.json()})).then((function(e){var t=qt([e],"supplementing");r=Wn(t),n=Dn.timedText,l="json"})).catch((function(e){throw console.error("transcript-parser -> parseExternalAnnotations() -> fetching annotations -> ",e),e}));case 15:return e.abrupt("return",{tData:r,tUrl:i,tType:n,tFileExt:l});case 16:case"end":return e.stop()}}),e)})))).apply(this,arguments)}function Wn(e){var t=[];return e.map((function(e){if(null!=e.id){var r=e.body,n=Ot(e.target),a=n.start,i=n.end;t.push({text:r.value,format:r.format,begin:parseFloat(a),end:parseFloat(i),tag:Pn.timedCue})}})),t}function Zn(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],r=[],n=[],a=e.split("\n"),i=a;if(!t){var o=function(e){var t=e.shift().trim();if(6==(null==t?void 0:t.length)&&"WEBVTT"===t){var r=function(e){var t=0,r=0,n=!1,a=[];e=e.filter((function(e){return!Number(e)}));for(var i=0;i").concat(e[i].trim()),i++;a.push({times:"",line:s,tag:Pn.note})}else{if(o.includes("--\x3e")){r=i;break}"string"==typeof o&&0!=o.trim().length&&(n=!0)}}return r>t&&!n?{valid:!0,cue_lines:e.slice(r),notes:a}:{valid:!1}}(e);return{valid:r.valid,cue_lines:r.cue_lines,notes:r.notes}}return{valid:!1,cue_lines:[],notes:[]}}(a),s=o.valid,l=o.cue_lines,c=o.notes;if(!s)return console.error("Invalid WebVTT file"),{tData:[],tType:Dn.invalidVTT};i=l,n=c}var u=function(e){var t,r=[];for(t=0;t0&&e.items.map((function(e){var n=new t.Annotation(e);if("supplementing"==n.getMotivation()){var a=n.getTarget(),i=zt(a),o=n.getBody()[0].getProperty("value"),l=ra(o,r,!0);s.push({target:a,targetURI:i,value:o,hitCount:l})}}));for(var l=function(e,t){return e.reduce((function(e,r){return(e[r[t]]=e[r[t]]||[]).push(r),e}),{})}(s,"targetURI"),c=0,u=Object.entries(l);c]+>/gi,""),d=0,f=0,p=void 0;if(null!=c){d=c.start,f=c.end,p=r.findIndex((function(e){return e.begin==d&&e.end==f}));var m=n.match(/[a-zA-Z]+/gi)?n.match(/[a-zA-Z]+/gi)[0]:n;if(-1!==u.toLocaleLowerCase().indexOf(m)&&null!=p){var v=$n(l,n,t.hitCount,!0);a.push({tag:Pn.timedCue,begin:d,end:f,id:p,match:v,matchCount:t.hitCount,text:l})}}else{var h=Xn(r,u,n,i);for(i=h.traversedIds,a=[].concat(D(a),D(h.hits));o===e.length-1&&(null===(g=i)||void 0===g?void 0:g.length)]+>/gi,"").trim(),u=D(c.matchAll(i)),d=t.trim();if(d==c||d.includes(c)&&(null==u?void 0:u.length)>0){l.matchCount=null==u?void 0:u.length,o.push(l),n.push(l.id);break}if((null==u?void 0:u.length)>0){var f;l.matchCount=null===(f=D(d.matchAll(i)))||void 0===f?void 0:f.length,o.push(l),n.push(l.id);break}n.push(l.id)}var p=[];return o.map((function(e){var t=ea(e.textDisplayed,r),n=$n(t,r,e.matchCount,!0);p.push({tag:Pn.nonTimedLine,begin:void 0,end:void 0,id:e.id,match:n,matchCount:e.matchCount,text:t})})),{hits:p,traversedIds:n}},$n=function(e,t,r){if(void 0!==e&&e){var n=0,a=t;arguments.length>3&&void 0!==arguments[3]&&arguments[3]&&(a=ta(t));try{var i,o=new RegExp(String.raw(mn||(mn=Cn(["",""])),a),"gi");return 0===(null===(i=D(e.matchAll(o)))||void 0===i?void 0:i.length)?function(){var a=D(e.matchAll(/<\/?[^>]+>/gi));if(0!==(null==a?void 0:a.length)){for(var i=0,o="",s=0;s0?2*(null==l?void 0:l.length)-1:1;if(void 0===a[s]&&void 0===a[s+c])return;var u=a[s].index,d=a[s+c].index+a[s+c][0].length,f=e.slice(i,u),p=e.slice(u,d).replace(/<\/?[^>]+>/gi,"");o="".concat(o).concat(f,'').concat(p,""),i=d,n++,(s=+(c+1))==a.length&&(o="".concat(o).concat(e.slice(i)))}return o}}():e.replace(o,(function(e){var t=e.replace(/<\/?[^>]+>/gi,"");return n'.concat(t,"")):t}))}catch(e){console.log("Error building RegExp for query: ",t)}}},ea=function(e,t){if(void 0!==e&&e){var r=new RegExp(String.raw(vn||(vn=Cn(["\b","\b"],["\\b","\\b"])),ta(t,!0,!1)),"gi"),n=e.replace(r,(function(e){return ta(e,!1,!0)}));return n}},ta=function(e){var t=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],r=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],n=D(e.matchAll(/[a-zA-Z']+/gi)),a=D(e.matchAll(/([.+?"^${}\-|[\]\\])/g));if(0===(null==a?void 0:a.length)){var i=r?e.split(" ").map((function(e){return"".concat(e,"")})).join(" "):e;return t?"".concat(i,"(?!['w*])"):i}for(var o="",s=0,l=0;l".concat(c[0],"
    "):c[0],d="(".concat(e.slice(s,c.index),0===s?")*":")+");o=t?"".concat(o).concat(d,"(").concat(u,")"):"".concat(o).concat(e.slice(s,c.index)).concat(u),s=c.index+c[0].length,l===(null==n?void 0:n.length)-1&&(o=t?"".concat(o,"(").concat(e.slice(s),")*"):"".concat(o).concat(e.slice(s))),l++}return t?o.replace(/([.?^${}|[\]\\])/g,"\\$1"):o},ra=function(e,t){var r,n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],a=t.split(/[\s.,!?;:]/)[0],i=a.replace(/[\[\]\-]/gi,""),o=n?ta(a):i,s=new RegExp(String.raw(hn||(hn=Cn(["",""])),o),"gi");return null===(r=D(e.matchAll(s)))||void 0===r?void 0:r.length},na=function(e){var t=document.createElement("div");return t.innerHTML=e,aa(Array.from(t.childNodes),!0)},aa=function(e){var t=arguments.length>1&&void 0!==arguments[1]&&arguments[1],r=[];return e.map((function(e){r.push({text:t?e.innerText:e,tag:Pn.nonTimedLine,textDisplayed:t?U.decode(e.innerHTML):e})})),r};function ia(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function oa(e){for(var t=1;t0}),[n]),l=e.useCallback((function(){return r?r.currentTime():0}),[r]);return{canvasIndex:a,canvasIsEmpty:i,isMultiCanvased:s,lastCanvasIndex:o,player:r,getCurrentTime:l}},la=function(t){var r=t.enableFileDownload,n=void 0!==r&&r,a=t.withCredentials,i=void 0!==a&&a,o=t.lastCanvasIndex,s=e.useContext(tr),l=e.useContext(dr),c=e.useContext(er),u=c.allCanvases,d=c.autoAdvance,f=c.canvasIndex,p=c.customStart,m=c.manifest,v=c.playlist,h=c.renderings,g=c.srcIndex,y=v.isPlaylist,b=ua({lastCanvasIndex:o}),x=b.clearDisplayTimeInterval,k=b.createDisplayTimeInterval,w=e.useState(),E=T(w,2),S=E[0],C=E[1],I=e.useState({error:"",sources:[],tracks:[],poster:null,targets:[]}),R=T(I,2),O=R[0],q=R[1],j=e.useState(),M=T(j,2),D=M[0],P=M[1],A=e.useState(!0),L=T(A,2),_=L[0],N=L[1],F=e.useState(!1),U=T(F,2),B=U[0],V=U[1],H=e.useMemo((function(){var e,t;return n&&h!={}?null==h||null===(e=h.manifest)||void 0===e?void 0:e.concat(null==h||null===(t=h.canvas[f])||void 0===t?void 0:t.files):[]}),[h,f]);e.useEffect((function(){if(m){if(null==f||f<0)throw new Error("Invalid canvas index. Please check your Manifest.");G(f,y)}return function(){V(!1),l({player:null,type:"updatePlayer"})}}),[m,f]);var G=function(e,t){x();var r=Gt({manifest:m,canvasIndex:e,startTime:e===p.startIndex&&_?p.startTime:0,srcIndex:g,isPlaylist:y}),n=r.isMultiSource,a=r.sources,o=r.tracks,c=r.canvasTargets,f=r.mediaType,v=r.error,h=r.poster;i&&a.map((function(e){return e.withCredentials=!0})),C("video"===f),s({canvasTargets:c,type:"canvasTargets"}),s({isMultiSource:n,type:"hasMultipleItems"}),t&&((null==c?void 0:c.length)>0?l({currentTime:c[0].altStart,type:"setCurrentTime"}):l({currentTime:0,type:"setCurrentTime"})),q(oa(oa({},O),{},{error:v,sources:a,tracks:o,poster:h,targets:c}));var b=u.find((function(t){return t.canvasIndex===e}));if(b.isEmpty)l({type:"updatePlayer"}),s({type:"setCanvasIsEmpty",isEmpty:!0}),q(oa(oa({},O),{},{error:h})),d&&k();else{var w=[m.label?Object.values(m.label)[0][0]:"",b.label].filter(Boolean).join(" - ");s({canvasDuration:b.duration,type:"canvasDuration"}),s({canvasLink:{label:w,id:b.canvasId},type:"canvasLink"}),s({type:"setCanvasIsEmpty",isEmpty:!1})}P(n||!1),V(!v),N(!1)};return{isMultiSourced:D,isPlaylist:y,isVideo:S,nextItemClicked:function(e,t){l({currentTime:t,type:"setCurrentTime"}),s({srcIndex:e,type:"setSrcIndex"})},playerConfig:O,ready:B,renderingFiles:H,srcIndex:g,switchPlayer:function(e,t){var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"";null!=e&&e>-1&&e<=o&&(s({canvasIndex:e,type:"switchCanvas"}),G(e,t),l({element:r,type:"setPlayerFocusElement"}))}}},ca=function(t){var r=t.options,n=t.playerInitSetup,a=t.startQuality,i=t.tracks,o=t.updatePlayer,s=t.videoJSRef,l=t.videoJSLangMap,c=e.useContext(er),u=e.useContext(ur),d=e.useContext(dr),f=c.canvasDuration,p=c.canvasIndex,v=c.canvasIsEmpty,h=c.currentNavItem,g=c.playlist,y=u.currentTime,b=u.isClicked,x=u.isPlaying,k=u.player,w=u.searchMarkers,E=e.useState(""),S=T(E,2),C=S[0],I=S[1],R=e.useState(null),O=T(R,2),q=O[0],j=O[1],M=e.useState(!1),P=T(M,2),A=P[0],L=P[1],_=e.useRef(A),N=function(e){L(e),_.current=e},F=e.useRef(null);e.useEffect((function(){return document.addEventListener("keydown",(function(e){if(_t(e,F.current,v)===gt.pause)U()})),function(){k&&(k.dispose(),document.removeEventListener("keydown",_t),N(!1))}}),[]),e.useEffect((function(){var e,t;if(B(r.sources),!F.current&&(null===(e=r.sources)||void 0===e?void 0:e.length)>0){m.default.addLanguage(r.language,JSON.parse(l)),V(),m.default.log.level("off");var a=F.current=m.default(s.current,r,(function(){n(F.current)}));d({player:a,type:"updatePlayer"}),a.controlBar.getChild("PlayToggle").on("pointerdown",(function(){U()})),a.on("pointerdown",(function(e){"video"==e.target.nodeName.toLowerCase()&&U()}))}else if(F.current&&(null===(t=r.sources)||void 0===t?void 0:t.length)>0){var i,c=F.current;C&&(null===(i=c.markers)||void 0===i||i.removeAll()),I(null),v?N(!0):(c.addClass("vjs-disabled"),N(!1),o(c),d({player:c,type:"updatePlayer"}))}}),[r.sources,s]),e.useEffect((function(){k&&(v?(k.audioOnlyMode(!1),k.canvasIsEmpty=!0,k.aspectRatio("16:9"),k.controlBar.addClass("vjs-hidden"),k.removeClass("vjs-disabled"),k.pause(),I(null==h?void 0:h.id)):k.controlBar.removeClass("vjs-hidden"))}),[p,v,h]),e.useEffect((function(){F.current&&F.current.currentTime(y,d({type:"resetClick"}))}),[b]),e.useEffect((function(){if(F.current&&F.current.markers&&A){var e,t;"function"==typeof F.current.markers&&F.current.markers({markerTip:{display:!1,text:function(e){return e.text}},markerStyle:{},markers:[]});var r=[];if(null!=g&&null!==(e=g.markers)&&void 0!==e&&e.length)r=g.markers.filter((function(e){return e.canvasIndex===p}))[0].canvasMarkers.map((function(e){return{time:parseFloat(e.time),text:e.value,class:"ramp--track-marker--playlist"}}));null===(t=F.current.markers)||void 0===t||t.removeAll(),F.current.markers.add([].concat(D(q?[q]:[]),D(w),D(r)))}}),[q,w,f,p,F.current,A]);var U=function(){x&&d({isPlaying:!1,type:"setPlayingStatus"})},B=function(e){var t=null==e?void 0:e.find((function(e){return 1==e.selected})),r=null==e?void 0:e.find((function(e){return e.label==a}));r&&(t.selected=!1,r.selected=!0)},V=function(){(null==i?void 0:i.length)>0&&s.current&&i.map((function(e){var t=document.createElement("track");t.setAttribute("key",e.key),t.setAttribute("src",e.src),t.setAttribute("kind",e.kind),t.setAttribute("label",e.label),t.setAttribute("srclang",e.srclang),s.current.appendChild(t)}))};return{activeId:C,fragmentMarker:q,isReadyRef:_,playerRef:F,setActiveId:I,setFragmentMarker:j,setIsReady:N}},ua=function(t){var r=t.lastCanvasIndex,n=e.useContext(tr),a=e.useContext(er),i=a.autoAdvance,o=a.canvasIndex,s=a.canvasIsEmpty,l=e.useState(Et/1e3),c=T(l,2),u=c[0],d=c[1],f=e.useRef();f.current=e.useMemo((function(){return o}),[o]);var p=e.useRef(null);e.useEffect((function(){v(),s&&!p.current&&i&&(d(Et/1e3),m())}),[o,i,s]);var m=e.useCallback((function(){var e=(new Date).getTime();p.current=setInterval((function(){var t=(new Date).getTime(),a=(Et-(t-e))/1e3;a>0?d(Math.ceil(a)):(f.current0)){e.next=6;break}return e.next=3,_n(t);case 3:e.t0=e.sent,e.next=9;break;case 6:return e.next=8,An(r);case 8:e.t0=e.sent;case 9:n=e.t0,q(null!=n?n:[]),Y(null!=n?n:[]);case 12:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}(),Y=function(e){var t,r;if(!u.signal.aborted){var n,a=function(e){return e.filter((function(e){return e.canvasId==d.current}))};if(!(null!=e&&e.length)>0||!(null!==(t=a(e))&&void 0!==t&&t.length)>0||!(null!==(n=e,r=a(n)[0].items)&&void 0!==r&&r.length)>0)g(!0),C([]),Q(void 0);else{g(!1);var i=a(e)[0];F(i.items),Q(i.items[0])}}};e.useEffect((function(){if((null==O?void 0:O.length)>0&&null!=d.current){var e=O.filter((function(e){return e.canvasId==d.current}))[0];F(e.items),Q(e.items[0])}}),[d.current]);var Q=function(){var e=gr(xr.mark((function e(t){var r,n,a,i,o,s,u,f,p,m,v,h,y;return xr.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:if(t&&null!=t){e.next=5;break}return g(!0),k(!1),A({tType:Dn.noTranscript,id:"",tError:l}),e.abrupt("return");case 5:if(g(!1),n=(r=t).id,a=r.title,i=r.filename,o=r.url,s=r.isMachineGen,u=r.format,!((null==(f=V.filter((function(e){return e.id==n&&e.canvasId==d.current})))?void 0:f.length)>0)){e.next=15;break}p=f[0],m=p.tData,v=p.tFileExt,h=p.tType,y=p.tError,C(m),A({title:a,filename:i,id:n,isMachineGen:s,tType:h,tUrl:o,tFileExt:v,tError:y}),W(o),e.next=17;break;case 15:return e.next=17,Promise.resolve(Un(o,d.current,u)).then((function(e){if(null!=e){var r=e.tData,o=e.tUrl,u=e.tType,f=e.tFileExt,p="";switch(u){case Dn.invalid:p="Invalid URL for transcript, please check again.";break;case Dn.noTranscript:p=l;break;case Dn.noSupport:p=c;break;case Dn.invalidVTT:p="Invalid WebVTT file, please check again.";break;case Dn.invalidTimestamp:p="Invalid timestamp format in cue(s), please check again."}C(r),A({title:a,filename:i,id:n,isMachineGen:s,tType:u,tUrl:o,tFileExt:f,tError:p}),W(o),t=oa(oa({},t),{},{tType:u,tData:r,tFileExt:f,canvasId:d.current,tError:p}),H([].concat(D(V),[t]))}}));case 17:k(!1);case 18:case"end":return e.stop()}}),e)})));return function(t){return e.apply(this,arguments)}}(),K=e.useCallback((function(e){var t=N.filter((function(t){return t.id===e}));Q(t[0])}),[N]);return{canvasIndexRef:d,canvasTranscripts:N,isEmpty:h,isLoading:x,NO_SUPPORT_MSG:c,playerRef:f,selectedTranscript:J,selectTranscript:K,transcript:S,transcriptInfo:P}},pa=g(y((function(e){e.exports=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},e.exports.__esModule=!0,e.exports.default=e.exports}))),ma=g(y((function(e){function t(e,t){for(var r=0;ra.start?this.initializeProgress(a.customStart):this.initializeProgress(a.start),this.setIsMultiSource((null==n?void 0:n.length)>1),this.playerEventListener||(this.playerEventListener=setInterval((function(){rn&&!sn?e.abortableTimeupdateHandler():e.timeUpdateHandler()}),100))}},{key:"update",value:function(){if(ka(ya(n.prototype),"update",this).call(this),on&&0===this.player.currentTime()&&(this.removeClass("played-range"),document.documentElement.style.setProperty("--range-progress","calc(".concat(0,"%)"))),an&&rn&&this.player.paused()){var e,t=null!==(e=this.player.structStart)&&void 0!==e?e:0;if(0!=t&&0===this.player.currentTime()){this.player.currentTime(t);var r=Math.min(100,Math.max(0,t/this.totalDuration*100));this.addClass("played-range"),document.documentElement.style.setProperty("--range-progress","calc(".concat(r,"%)")),this.player.structStart=0}}}},{key:"initializeEl",value:function(){var e=this,t=m.default.dom.createEl("div",{className:"block-stripes",role:"presentation",id:"left-block"}),r=m.default.dom.createEl("div",{className:"block-stripes",role:"presentation",id:"right-block"});this.el().appendChild(t),this.el().appendChild(r),this.el().addEventListener("mouseenter",(function(t){e.handleMouseMove(t)})),this.el().addEventListener("pointerup",(function(t){e.pointerDragged&&e.handleMouseUp(t)})),this.el().addEventListener("pointermove",(function(t){e.handleMouseMove(t),e.pointerDragged=!0})),this.el().addEventListener("pointerdown",(function(t){e.handleMouseDown(t),e.pointerDragged=!1}))}},{key:"handleMouseMove",value:function(e){var t=this.convertToTime(e),r=t.currentTime,n=t.offsetx;null!=r&&this.setCurrentTime(r);var a=this.getChild("MouseTimeDisplay");if(a){var i=a.getChild("TimeTooltip").el_;r&&(i.innerHTML=Tt(r));var o=i.clientWidth/2;i.style.left="".concat(n-o,"px")}}},{key:"handleMouseDown",value:function(e){if(on||2!==e.buttons){var t,r=this.convertToTime(e),n=r.currentTime;if(r._,this.isMultiSourceRef.current&&(t=this.canvasTargetsRef.current.find((function(e){var t=e.altStart+e.duration;if(n>=e.altStart&&n<=t)return e}))),t){var a,i,o=null!==(a=null===(i=t)||void 0===i?void 0:i.sIndex)&&void 0!==a?a:0;o!=this.srcIndexRef.current?(this.selectSource(t.sIndex,n-t.altStart),this.setSrcIndex(o)):this.player.currentTime(n-t.altStart)}else this.player.currentTime(n);if(on){var s=Math.min(100,Math.max(0,n/this.totalDuration*100));this.player.currentTime(n),this.addClass("played-range"),document.documentElement.style.setProperty("--range-progress","calc(".concat(s,"%)"))}}}},{key:"handleMouseUp",value:function(e){this.handleMouseDown(e)}},{key:"buildProgressBar",value:function(){var e;this.removeClass("played-range");var t=this.canvasTargetsRef,r=this.isMultiSourceRef,n=this.player,a=this.srcIndexRef,i=this.totalDuration;if((null===(e=t.current)||void 0===e?void 0:e.length)>0){var o=t.current[a.current],s=o.altStart,l=o.start,c=o.end,u=o.duration,d=document.getElementById("left-block"),f=document.getElementById("right-block");if(r.current){var p=Math.min(100,Math.max(0,s/i*100));this.playProgress.el_.style.left="".concat(p,"%"),this.loadProgress.el_.style.left="".concat(p,"%"),this.addClass("played-range"),document.documentElement.style.setProperty("--range-progress","calc(".concat(p,"%)"))}else{var m=100*l/u,v=100*(u-c)/u;n.isClipped=v>0,d&&(d.style.width="".concat(m,"%")),f&&(f.style.width=v+"%",f.style.left="".concat(100-v-m,"%"))}}}},{key:"convertToTime",value:function(e){var t,r,n=e.srcElement;if(n.classList.contains("block-stripes")){var a=this.canvasTargetsRef.current[0],i=a.altStart,o=a.end,s=a.duration;return"right-block"===n.id?{currentTime:o,offsetx:o/s*this.el().clientWidth}:{currentTime:i,offsetx:i/s*this.el().clientWidth}}var l,c=e.target.getBoundingClientRect().x,u=null!=e.nativeEvent?null!=e.nativeEvent.offsetX?e.nativeEvent.offsetX:(null===(t=e.nativeEvent.targetTouches[0])||void 0===t?void 0:t.clientX)-c:e.offsetX,d=null!==(r=this.totalDuration)&&void 0!==r?r:this.player.duration();if(u&&null!=u){if(this.isMultiSourceRef.current){var f=parseFloat(this.playProgress.el_.style.left)/100*this.el().clientWidth,p=n.classList,m=!((null==p?void 0:p.length)>0)||(p.contains("vjs-play-progress")||p.contains("vjs-load-progress"));f>u&&m&&(u+=f)}l=u/this.el().clientWidth*d}if(e.target.hasAttribute("data-start")){var v=e.target.dataset,h=v.start;v._,u=(l+=parseFloat(h))*this.el().clientWidth/this.totalDuration}return{currentTime:l,offsetx:u}}},{key:"abortableTimeupdateHandler",value:function(){var e=this,t=this.player,r=this.progressRef;t.on("waiting",(function(){rn&&!an&&t.currentTime(r.current),n()}));var n=function(){a&&clearInterval(a)},a=setInterval((function(){e.timeUpdateHandler()}),100)}},{key:"timeUpdateHandler",value:function(){var e,t=this,r=this.initTimeRef,n=this.player;n.isDisposed()||n.ended()||null==n||(r.current>0&&0==n.currentTime()?(e=r.current,n.currentTime(r.current)):e=n.currentTime(),rn&&!an&&n.paused()?Jr((function(){t.onTimeUpdate(e)})):this.onTimeUpdate(e),this.setInitTime(0))}},{key:"onTimeUpdate",value:function(e){this.player.hasClass("vjs-ios-native-fs")&&!this.player.audioOnlyMode_||this.setProgress(e),this.handleTimeUpdate(e)}},{key:"handleTimeUpdate",value:function(e){var t,r=this.player,n=this.el_,a=this.canvasTargetsRef,i=this.srcIndexRef;if(n&&r&&a.current){var o=a.current[null!==(t=i.current)&&void 0!==t?t:0],s=o.start,l=o.end,c=o.duration;e=l&&!r.paused()&&!r.isDisposed()&&(l0&&0==e.currentTime()?t.current:e.currentTime();var o=r[null!=n?n:0],s=o.start,l=o.altStart;l!=s&&n>0&&(a+=l),i&&!e.audioOnlyMode_||this.updateTextNode_(a),this.setInitTime(0)}}}]),n}();function Ma(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var r,n=ya(e);if(t){var a=ya(this).constructor;r=Reflect.construct(n,arguments,a)}else r=n.apply(this,arguments);return Sa(this,r)}}m.default.registerComponent("VideoJSCurrentTime",ja);var Da=m.default.getComponent("MenuButton"),Pa=m.default.getComponent("MenuItem"),Aa=function(e){Ea(r,Da);var t=Ma(r);function r(e,n){var a;return pa(this,r),(a=t.call(this,e,n)).addClass("vjs-file-download"),a.setAttribute("data-testid","videojs-file-download"),a.setIcon("file-download"),a}return ma(r,[{key:"createItems",value:function(){var e=this.options_,t=this.player_,r=e.files;return(null==r?void 0:r.length)>0?r.map((function(e){var r=new Pa(t,{label:e.label});return r.handleClick=function(){Rt(e.id,e.filename,e.fileExt)},r})):[]}}]),r}();function La(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var r,n=ya(e);if(t){var a=ya(this).constructor;r=Reflect.construct(n,arguments,a)}else r=n.apply(this,arguments);return Sa(this,r)}}m.default.registerComponent("VideoJSFileDownload",Aa);var _a=m.default.getComponent("Button"),Na=function(e){Ea(r,_a);var t=La(r);function r(e,n){var a;return pa(this,r),(a=t.call(this,e,n)).setIcon("next-item"),a.addClass("vjs-play-control vjs-control"),a.setAttribute("data-testid","videojs-next-button"),a.controlText("Next"),a.options=n,a.player=e,a.cIndex=n.canvasIndex,a.player.on("loadstart",(function(){a.updateComponent()})),a}return ma(r,[{key:"updateComponent",value:function(){var e,t=this.player;t&&null!=t&&(void 0===t.canvasIndex&&(null===(e=t.children())||void 0===e?void 0:e.length)>0?this.cIndex=Number(t.children()[0].dataset.canvasindex):this.cIndex=t.canvasIndex)}},{key:"handleClick",value:function(){this.handleNextClick(!1)}},{key:"handleKeyDown",value:function(e){32!==e.which&&13!==e.which||(e.stopPropagation(),this.handleNextClick(!0))}},{key:"handleNextClick",value:function(e){this.cIndex!=this.options.lastCanvasIndex&&this.options.switchPlayer(this.cIndex+1,!0,e?"nextBtn":"")}}]),r}();function Fa(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var r,n=ya(e);if(t){var a=ya(this).constructor;r=Reflect.construct(n,arguments,a)}else r=n.apply(this,arguments);return Sa(this,r)}}m.default.registerComponent("VideoJSNextButton",Na);var Ua=m.default.getComponent("Button"),Ba=function(e){Ea(r,Ua);var t=Fa(r);function r(e,n){var a;return pa(this,r),(a=t.call(this,e,n)).setIcon("previous-item"),a.addClass("vjs-play-control vjs-control"),a.setAttribute("data-testid","videojs-previous-button"),a.options=n,a.player=e,a.cIndex=n.canvasIndex,a.player.on("loadstart",(function(){a.updateComponent()})),a}return ma(r,[{key:"updateComponent",value:function(){var e,t=this.player;t&&null!=t&&(void 0===t.canvasIndex&&(null===(e=t.children())||void 0===e?void 0:e.length)>0?this.cIndex=Number(t.children()[0].dataset.canvasindex):this.cIndex=t.canvasIndex);this.controlText(0==this.cIndex?"Replay":"Previous")}},{key:"handleClick",value:function(){this.handlePreviousClick(!1)}},{key:"handleKeyDown",value:function(e){32!==e.which&&13!==e.which||(e.stopPropagation(),this.handlePreviousClick(!0))}},{key:"handlePreviousClick",value:function(e){this.cIndex>-1&&0!=this.cIndex?this.options.switchPlayer(this.cIndex-1,!0,e?"previousBtn":""):0==this.cIndex&&this.player.currentTime(0)}}]),r}();function Va(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var r,n=ya(e);if(t){var a=ya(this).constructor;r=Reflect.construct(n,arguments,a)}else r=n.apply(this,arguments);return Sa(this,r)}}m.default.registerComponent("VideoJSPreviousButton",Ba);var Ha=m.default.getComponent("Component"),Ga=function(e){Ea(r,Ha);var t=Va(r);function r(e,n){var a;return pa(this,r),(a=t.call(this,e,n)).setAttribute("data-testid","videojs-title-link"),a.addClass("vjs-title-bar"),a.options=n,a.player=e,a.player.on("loadstart",(function(){a.updateComponent()})),a}return ma(r,[{key:"updateComponent",value:function(){var e=this.player;if(e&&null!=e&&e.canvasLink){var t=e.canvasLink,r=t.label,n=t.id,a=r,i=null;i=n.includes("manifest/canvas")?n.replace("manifest/canvas","section"):n;var o=m.default.dom.createEl("a",{className:"vjs-title-link",href:i,target:"_blank",rel:"noreferrer noopener",innerHTML:a});this.el().hasChildNodes()?this.el().replaceChildren(o):this.el().appendChild(o)}}}]),r}();function za(e){var t=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(Reflect.construct(Boolean,[],(function(){}))),!0}catch(e){return!1}}();return function(){var r,n=ya(e);if(t){var a=ya(this).constructor;r=Reflect.construct(n,arguments,a)}else r=n.apply(this,arguments);return Sa(this,r)}}Ha.registerComponent("VideoJSTitleLink",Ga);var Ja,Wa='\n\n \n \n \n \n \n \n',Za='\n\n \n \n \n \n \n \n';(Ja=document.createElement("div")).style.display="none",Ja.innerHTML=''.concat(Wa).concat(Za,""),document.body.appendChild(Ja);var Ya=m.default.getComponent("Button"),Qa=function(t){Ea(n,Ya);var r=za(n);function n(t,a){var i;return pa(this,n),(i=r.call(this,t,a)).setAttribute("data-testid","videojs-track-scrubber-button"),i.addClass("vjs-button vjs-track-scrubber"),i.controlText("Toggle track scrubber"),i.el().innerHTML='\n \n \n ',i.options=a,i.player=t,i.playerInterval,i.zoomedOutRef=e.createRef(),i.currentTrackRef=e.createRef(),i.player.on("ready",(function(){i.options.trackScrubberRef.current&&(i.playerInterval=setInterval((function(){i.handleTimeUpdate()}),100))})),i.player.on("loadstart",(function(){i.options.trackScrubberRef.current&&(i.updateComponent(),i.playerInterval||(i.playerInterval=setInterval((function(){i.handleTimeUpdate()}),100)))})),i.player.on("fullscreenchange",(function(){if(i.player.isFullscreen()&&!i.zoomedOutRef.current){var e=i.zoomedOutRef.current;i.setZoomedOut(!e)}})),i.player.on("dispose",(function(){clearInterval(i.playerInterval)})),i}return ma(n,[{key:"setCurrentTrack",value:function(e){this.currentTrackRef.current=e}},{key:"setZoomedOut",value:function(e){this.zoomedOutRef.current=e,e?(this.options.trackScrubberRef.current.classList.add("hidden"),this.el().innerHTML='\n \n \n '):(this.options.trackScrubberRef.current.classList.remove("hidden"),this.el().innerHTML='\n \n \n ')}},{key:"attachListeners",value:function(){var e=this,t=this.options.trackScrubberRef;if(t.current){this.populateTrackScrubber(),this.updateTrackScrubberProgressBar();var r=!1,n=T(t.current.children,3);n[0];var a=n[1];n[2],a.addEventListener("mouseenter",(function(t){e.handleMouseMove(t)})),a.addEventListener("pointerup",(function(t){r&&e.handleSetProgress(t)})),a.addEventListener("pointermove",(function(t){e.handleMouseMove(t),r=!0})),a.addEventListener("pointerdown",(function(t){1===t.which&&(e.handleSetProgress(t),r=!1)}))}}},{key:"updateComponent",value:function(){this.zoomedOutRef.current=!0,this.currentTrackRef.current={},this.attachListeners()}},{key:"handleKeyDown",value:function(e){32!==e.which&&13!==e.which||(e.preventDefault(),this.handleTrackScrubberClick(),e.stopPropagation())}},{key:"handleClick",value:function(){this.handleTrackScrubberClick()}},{key:"handleTrackScrubberClick",value:function(){var e=this.currentTrackRef,t=this.player;if(this.options.trackScrubberRef.current&&e.current){t.isFullscreen()&&t.exitFullscreen();var r=this.zoomedOutRef.current;this.setZoomedOut(!r)}}},{key:"handleTimeUpdate",value:function(){var e,t=this.player,r=this.options,n=this.zoomedOutRef;if(t.canvasIsEmpty&&!n.current&&this.setZoomedOut(!0),!t.isDisposed()&&!t.ended()){var a,i,o=t.currentTime();if(t.markers&&"function"!=typeof t.markers&&"function"==typeof t.markers.getMarkers&&(null===(e=t.markers.getMarkers())||void 0===e?void 0:e.length)>0&&!r.isPlaylist)this.readPlayerMarkers();else this.setCurrentTrack({duration:null!==(a=t.playableDuration)&&void 0!==a?a:t.duration(),time:null!==(i=t.altStart)&&void 0!==i?i:0,key:"",text:"Complete media file"}),o=t.srcIndex&&t.srcIndex>0?o+t.altStart:o;this.updateTrackScrubberProgressBar(o)}}},{key:"updateTrackScrubberProgressBar",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,t=this.player,r=this.currentTrackRef;r.current||t.markers&&"function"==typeof t.markers.getMarkers&&this.readPlayerMarkers();var n=t.altStart,a=t.srcIndex>0?e-r.current.time+n:e-r.current.time,i=Math.min(100,Math.max(0,100*a/r.current.duration));this.populateTrackScrubber(a,i)}},{key:"populateTrackScrubber",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:0,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,r=this.options.trackScrubberRef;if(r.current){var n=T(r.current.children,3),a=n[0];n[1];var i=n[2];document.documentElement.style.setProperty("--range-scrubber","calc(".concat(t,"%)")),i.innerHTML=Tt(this.currentTrackRef.current.duration);var o=!isNaN(e)&&e>0?e:0;a.innerHTML=Tt(o)}}},{key:"readPlayerMarkers",value:function(){var e=this.player.markers.getMarkers().filter((function(e){return"ramp--track-marker--fragment"==e.class}));(null==e?void 0:e.length)>0&&this.setCurrentTrack(e[0])}},{key:"handleMouseMove",value:function(e){var t=this.options.timeToolRef;if(t.current){var r=this.getTrackTime(e);if(isFinite(r)){var n=e.offsetX-t.current.offsetWidth/2;t.current.style.left=n+"px",t.current.innerHTML=Tt(r)}}}},{key:"handleSetProgress",value:function(e){var t=this.currentTrackRef,r=this.player;if(t.current){var n=this.getTrackTime(e);if(null!=n){var a=Math.min(100,Math.max(0,n/t.current.duration*100));document.documentElement.style.setProperty("--range-scrubber","calc(".concat(a,"%)"));var i=r.isClipped?n+t.current.time:n;r.currentTime(i)}}}},{key:"getTrackTime",value:function(e){var t=this.currentTrackRef;if(t.current){var r=e.offsetX;if(r&&null!=r)return r/e.target.clientWidth*t.current.duration}}}]),n}();function Ka(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!r){if(Array.isArray(e)||(r=function(e,t){if(!e)return;if("string"==typeof e)return Xa(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);"Object"===r&&e.constructor&&(r=e.constructor.name);if("Map"===r||"Set"===r)return Array.from(e);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return Xa(e,t)}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,a=function(){};return{s:a,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,o=!0,s=!1;return{s:function(){r=r.call(e)},n:function(){var e=r.next();return o=e.done,e},e:function(e){s=!0,i=e},f:function(){try{o||null==r.return||r.return()}finally{if(s)throw i}}}}function Xa(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r0&&a&&l.forEach((function(t){e.addRemoteTextTrack(t,!1)})),null!=e.getChild("controlBar")&&!X){var u=e.getChild("controlBar"),d=u.children().findIndex((function(e){return"FullscreenToggle"==e.name_}));if(C||te?u.getChild("videoJSTrackScrubber")||u.addChild("videoJSTrackScrubber",{trackScrubberRef:c,timeToolRef:s}):u.removeChild("videoJSTrackScrubber"),(null==l?void 0:l.length)>0&&a&&!u.getChild("subsCapsButton")){var f=an?u.children().findIndex((function(e){return"MuteToggle"==e.name_})):u.children().findIndex((function(e){return"VolumePanel"==e.name_}));u.addChild("subsCapsButton",{},f+1).children_[0].addClass("captions-on")}if(a?(e.audioOnlyMode(!1),e.removeClass("vjs-audio"),e.aspectRatio("16:9"),e.addChild("bigPlayButton")):(e.audioOnlyMode(!0),e.addClass("vjs-audio"),e.height(e.controlBar.height()),e.removeChild("bigPlayButton")),an?(u.removeChild("MuteToggle"),u.addChild("MuteToggle")):(u.removeChild("VolumePanel"),u.addChild("VolumePanel"),e.trigger("volumechange")),r){var p=u.children().findIndex((function(e){return"VideoJSFileDownload"==e.name_}))||d+1;if(u.removeChild("videoJSFileDownload"),(null==re?void 0:re.length)>0){var m={title:"Download Files",controlText:"Alternate resource download",files:re};u.addChild("videoJSFileDownload",function(e){for(var t=1;t0){for(var n=null,a=!1,i=0;i0){var t=E[ke.current+1];if(t){g({canvasIndex:ke.current+1,type:"switchCanvas"}),v({startTime:0,type:"setTimeFragment"}),v({currentTime:0,type:"setCurrentTime"});var r=S.filter((function(e){return e.canvasIndex===t.canvasIndex&&1===e.itemIndex})),n=null!=t.id?t:r[0],a=0;null!=n&&null!=n.id&&(a=Ot(n.id,y).start),0===a?g({item:n,type:"switchItem"}):n.isEmpty&&(g({item:n,type:"switchItem"}),ge.current.currentTime(a),t.isEmpty||ge.current.play())}}}))}),[ke.current]),Ce=e.useMemo((function(){return Zr((function(){var e=ge.current;if(e&&he.current){var t,r=null!==(t=e.currentTime())&&void 0!==t?t:de.current;x&&ue.current>0&&(r+=k[ue.current].altStart);var n=Oe(r);if(we.current!==(null==n?void 0:n.id))if(n)if(g({item:n,type:"switchItem"}),ye(n.id),!te&&e.markers){var a=Ot(n.id,n.canvasDuration),i=a.start,o=a.end;if(v({endTime:o,startTime:i,type:"setTimeFragment"}),i!==o){var s=o>n.canvasDuration?n.canvasDuration:o;be({time:i,duration:s-i,text:i,class:"ramp--track-marker--fragment"})}else be(null)}else null!==ve&&be(null);else g({item:null,type:"switchItem"}),ye(null),be(null)}}),10)}),[]),Ie=null,Re=null,Oe=function(e){if(te)return S[ke.current];var t,r=Ka(S);try{for(r.s();!(t=r.n()).done;){var n=t.value,a=n.id,i=n.isCanvas;if(n.canvasIndex==ke.current+1){if(i)return n;var o=Ot(a,y),s=It(o,y);if(e>=o.start&&e=1&&u.default.createElement("button",{"aria-label":"Go back to previous item",onClick:function(){return qe(K-1)},onKeyDown:function(e){return je(e,K-1,"previousBtn")},"data-testid":"inaccessible-previous-button"},u.default.createElement(wn,{flip:!0})," Previous"),K!=$&&u.default.createElement("button",{"aria-label":"Go to next item",onClick:function(){return qe(K+1)},onKeyDown:function(e){return je(e,K+1,"nextBtn")},"data-testid":"inaccessible-next-button"},"Next ",u.default.createElement(wn,null))),K!=$&&u.default.createElement("p",{"data-testid":"inaccessible-message-timer",className:p.default("ramp--media-player_inaccessible-message-timer",ce.current?"":"hidden")},"Next item in ".concat(ie," second").concat(1===ie?"":"s"))),u.default.createElement("video",{"data-testid":"videojs-".concat(a?"video":"audio","-element"),"data-canvasindex":ke.current,ref:W,className:p.default("video-js vjs-big-play-centered vjs-theme-ramp vjs-disabled",Xr?"is-mobile":""),onTouchStart:function(e){Ie=e.touches[0].clientX,Re=e.touches[0].clientY},onTouchEnd:function(e){var t=ge.current;e.changedTouches[0].clientX==Ie&&e.changedTouches[0].clientY==Re&&(t.paused()?t.play():t.pause())},style:{display:"".concat(oe.current?"none":"")}})),(C||te)&&u.default.createElement("div",{className:"vjs-track-scrubber-container hidden",ref:c,id:"track_scrubber"},u.default.createElement("p",{className:"vjs-time track-currenttime",role:"presentation"}),u.default.createElement("span",{type:"range","aria-label":"Track scrubber",role:"slider",tabIndex:0,className:"vjs-track-scrubber",style:{width:"100%"}},!on&&u.default.createElement("span",{className:"tooltiptext",ref:s,"aria-hidden":!0,role:"presentation"})),u.default.createElement("p",{className:"vjs-time track-duration",role:"presentation"})))}m.default.registerComponent("VideoJSTrackScrubber",Qa),require("@silvermine/videojs-quality-selector")(m.default),ei.propTypes={enableFileDownload:Tr.bool,enableTitleLink:Tr.bool,isVideo:Tr.bool,options:Tr.object,placeholderText:Tr.string,scrubberTooltipRef:Tr.object,tracks:Tr.array,trackScrubberRef:Tr.object,videoJSLangMap:Tr.string,withCredentials:Tr.bool};var ti={"Audio Player":"Audio Player","Video Player":"Video Player",Play:"Play",Pause:"Pause",Replay:"Replay","Current Time":"Current Time",Duration:"Duration","Remaining Time":"Remaining Time","Stream Type":"Stream Type",LIVE:"LIVE","Seek to live, currently behind live":"Seek to live, currently behind live","Seek to live, currently playing live":"Seek to live, currently playing live",Loaded:"Loaded",Progress:"Progress","Progress Bar":"Progress Bar","progress bar timing: currentTime={1} duration={2}":"{1} of {2}",Fullscreen:"Fullscreen","Exit Fullscreen":"Exit Fullscreen",Mute:"Mute",Unmute:"Unmute","Playback Rate":"Playback Rate",Subtitles:"Subtitles","subtitles off":"subtitles off",Captions:"Captions","captions off":"captions off",Chapters:"Chapters",Descriptions:"Descriptions","descriptions off":"descriptions off","Audio Track":"Audio Track","Volume Level":"Volume Level","You aborted the media playback":"You aborted the media playback","A network error caused the media download to fail part-way.":"A network error caused the media download to fail part-way.","The media could not be loaded, either because the server or network failed or because the format is not supported.":"The media could not be loaded, either because the server or network failed or because the format is not supported.","The media playback was aborted due to a corruption problem or because the media used features your browser did not support.":"The media playback was aborted due to a corruption problem or because the media used features your browser did not support.","No compatible source was found for this media.":"No compatible source was found for this media.","The media is encrypted and we do not have the keys to decrypt it.":"The media is encrypted and we do not have the keys to decrypt it.","Play Video":"Play Video",Close:"Close","Close Modal Dialog":"Close Modal Dialog","Modal Window":"Modal Window","This is a modal window":"This is a modal window","This modal can be closed by pressing the Escape key or activating the close button.":"This modal can be closed by pressing the Escape key or activating the close button.",", opens captions settings dialog":", opens captions settings dialog",", opens subtitles settings dialog":", opens subtitles settings dialog",", opens descriptions settings dialog":", opens descriptions settings dialog",", selected":", selected","captions settings":"captions settings","subtitles settings":"subtitles settings","descriptions settings":"descriptions settings",Text:"Text",White:"White",Black:"Black",Red:"Red",Green:"Green",Blue:"Blue",Yellow:"Yellow",Magenta:"Magenta",Cyan:"Cyan",Background:"Background",Window:"Window",Transparent:"Transparent","Semi-Transparent":"Semi-Transparent",Opaque:"Opaque","Font Size":"Font Size","Text Edge Style":"Text Edge Style",None:"None",Raised:"Raised",Depressed:"Depressed",Uniform:"Uniform","Drop shadow":"Drop shadow","Font Family":"Font Family","Proportional Sans-Serif":"Proportional Sans-Serif","Monospace Sans-Serif":"Monospace Sans-Serif","Proportional Serif":"Proportional Serif","Monospace Serif":"Monospace Serif",Casual:"Casual",Script:"Script","Small Caps":"Small Caps",Reset:"Reset","restore all settings to the default values":"restore all settings to the default values",Done:"Done","Caption Settings Dialog":"Caption Settings Dialog","Beginning of dialog window. Escape will cancel and close the window.":"Beginning of dialog window. Escape will cancel and close the window.","End of dialog window.":"End of dialog window.","{1} is loading.":"{1} is loading.","Exit Picture-in-Picture":"Exit Picture-in-Picture","Picture-in-Picture":"Picture-in-Picture","No content":"No content",Color:"Color",Opacity:"Opacity","Text Background":"Text Background","Caption Area Background":"Caption Area Background","Playing in Picture-in-Picture":"Playing in Picture-in-Picture","Skip backward {1} seconds":"Skip backward {1} seconds","Skip forward {1} seconds":"Skip forward {1} seconds"};function ri(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function ni(e){for(var t=1;t0&&P?"subsCapsButton":"",an?"muteToggle":"volumePanel"],videoJSProgress:{srcIndex:x,targets:H,currentTime:null!=E?E:0,nextItemClicked:N},videoJSCurrentTime:{srcIndex:x,targets:H,currentTime:E||0},videoJSFileDownload:n&&{title:"Download Files",controlText:"Alternate resource download",files:_},videoJSPreviousButton:q&&{canvasIndex:O,switchPlayer:F},videoJSNextButton:q&&{canvasIndex:O,lastCanvasIndex:j,switchPlayer:F},videoJSTrackScrubber:(k||w)&&{trackScrubberRef:T,timeToolRef:S,isPlaylist:w}},sources:D?[V[x]]:V})}),[P,A,x]);return L&&null!=W||R?u.default.createElement("div",{"data-testid":"media-player",className:"ramp--media_player",role:"presentation"},u.default.createElement(ei,{enableFileDownload:n,enableTitleLink:f,isVideo:P,options:W,placeholderText:U,scrubberTooltipRef:S,tracks:G,trackScrubberRef:T,videoJSLangMap:C.current,withCredentials:m})):null};ai.propTypes={enableFileDownload:Tr.bool,enablePIP:Tr.bool,enablePlaybackRate:Tr.bool,enableTitleLink:Tr.bool,withCredentials:Tr.bool,language:Tr.string};var ii=y((function(e){function t(){return e.exports=t=Object.assign?Object.assign.bind():function(e){for(var t=1;t0?u.default.createElement(ci,{items:f,sectionRef:g,structureContainerRef:y,isPlaylist:T}):null;e.useEffect((function(){!b.current||(null==E?void 0:E.id)!=n||null==b.current.isClicked||b.current.isClicked||null==y.current.isScrolling||y.current.isScrolling||Lt(b.current,y),b.current&&(b.current.isClicked=!1)}),[E]);return""!=l?u.default.createElement("li",{"data-testid":"list-item",ref:b,className:p.default("ramp--structured-nav__list-item",w?"active":""),"data-label":l,"data-summary":c},u.default.createElement(e.Fragment,{key:v},u.default.createElement(e.Fragment,null,a?u.default.createElement("span",{className:"ramp--structured-nav__item-title",role:"listitem","aria-label":l},l):u.default.createElement(e.Fragment,{key:n},u.default.createElement("div",{className:"tracker"}),o?u.default.createElement(e.Fragment,null,s&&u.default.createElement(En,null),u.default.createElement("a",{role:"listitem",href:d&&""!=d?d:n,onClick:k},"".concat(m,". "),l," ",r.length>0?" (".concat(r,")"):"")):u.default.createElement("span",{role:"listitem","aria-label":l},l)))),S):null};si.propTypes={duration:Tr.string.isRequired,id:Tr.string,isTitle:Tr.bool.isRequired,isCanvas:Tr.bool.isRequired,isClickable:Tr.bool.isRequired,isEmpty:Tr.bool.isRequired,label:Tr.string.isRequired,summary:Tr.string,homepage:Tr.string,items:Tr.array.isRequired,itemIndex:Tr.number,rangeId:Tr.string.isRequired,canvasDuration:Tr.number.isRequired,sectionRef:Tr.object.isRequired,structureContainerRef:Tr.object.isRequired};var li=function(t){var r=t.duration,n=t.hasChildren,a=void 0!==n&&n,i=t.itemId,o=t.itemIndex,s=t.items,l=t.isRoot,c=void 0!==l&&l,d=t.label,f=t.sectionRef,m=t.structureContainerRef,v=e.useState(!1),h=T(v,2),g=h[0],y=h[1],b=function(e){y(!g),f.current.isOpen=!0},x=da({itemIndex:o,isRoot:c,itemId:i,liRef:f,sectionRef:f,isCanvas:!0,canvasDuration:r,setIsOpen:y}),k=x.isActiveSection,w=x.canvasIndex,E=x.handleClick,S=x.isPlaylist;e.useEffect((function(){w+1!==o||!f.current||null==f.current.isClicked||f.current.isClicked||null==m.current.isScrolling||m.current.isScrolling||Lt(f.current,m),f.current.isClicked=!1}),[w]);return u.default.createElement("div",{className:p.default("ramp--structured-nav__section",k?"active":""),role:"listitem","data-testid":"listitem-section",ref:f,"data-label":d,"data-mediafrag":null!=i?i:""},u.default.createElement("div",{className:"section-head-buttons"},u.default.createElement("button",{"data-testid":null==i?"listitem-section-span":"listitem-section-button",ref:f,onClick:E,className:p.default("ramp--structured-nav__section-title",!i&&"not-clickable")},u.default.createElement("span",{className:"ramp--structured-nav__title","aria-label":d,role:"listitem"},c?"":"".concat(o,". "),d,""!=r&&u.default.createElement("span",{className:"ramp--structured-nav__section-duration"},r))),a&&u.default.createElement("button",{className:"collapse-expand-button","data-testid":"section-collapse-icon",onClick:b},u.default.createElement("i",{className:p.default("arrow",g?"up":"down")}))),g&&a&&u.default.createElement(ci,{items:s,sectionRef:f,key:i,structureContainerRef:m,isPlaylist:S}))};li.propTypes={itemIndex:Tr.number.isRequired,canvasIndex:Tr.number,duration:Tr.string.isRequired,label:Tr.string.isRequired,sectionRef:Tr.object.isRequired,itemId:Tr.string,isRoot:Tr.bool,structureContainerRef:Tr.object.isRequired,hasChildren:Tr.bool,items:Tr.array};var ci=function(e){var t=e.items,r=e.sectionRef,n=e.structureContainerRef,a=e.isPlaylist,i=u.default.createElement("ul",{"data-testid":"list",className:"ramp--structured-nav__list",role:"presentation"},t.map((function(e,t){var i;return e.isCanvas&&!a?u.default.createElement(li,{key:"".concat(e.label,"-").concat(t),itemIndex:t+1,duration:e.duration,label:e.label,sectionRef:r,itemId:e.id,isRoot:e.isRoot,structureContainerRef:n,hasChildren:(null===(i=e.items)||void 0===i?void 0:i.length)>0,items:e.items}):u.default.createElement(si,oi({},e,{sectionRef:r,key:t,structureContainerRef:n}))})));return u.default.createElement(u.default.Fragment,null,i)};function ui(e,t){var r="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(!r){if(Array.isArray(e)||(r=function(e,t){if(!e)return;if("string"==typeof e)return di(e,t);var r=Object.prototype.toString.call(e).slice(8,-1);"Object"===r&&e.constructor&&(r=e.constructor.name);if("Map"===r||"Set"===r)return Array.from(e);if("Arguments"===r||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(r))return di(e,t)}(e))||t&&e&&"number"==typeof e.length){r&&(e=r);var n=0,a=function(){};return{s:a,n:function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}},e:function(e){throw e},f:a}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var i,o=!0,s=!1;return{s:function(){r=r.call(e)},n:function(){var e=r.next();return o=e.done,e},e:function(e){s=!0,i=e},f:function(){try{o||null==r.return||r.return()}finally{if(s)throw i}}}}function di(e,t){(null==t||t>e.length)&&(t=e.length);for(var r=0,n=new Array(t);r2&&void 0!==arguments[2]&&arguments[2],a=[],i=0,o=!1,s=0,l=0,c=function e(t,n){if("no-nav"!=t.getBehavior()){var c,u,d=Pt(t.getLabel().getValue()),f=t.getCanvasIds(),p=i,m=i,v=n==t&&0==s,h=!1,g=!1,y=void 0,b=void 0;o?(u=v||r.length>1&&n==t.parentRange,r.length>1&&n==t.parentRange?s+=1:1==r.length&&(s=1)):u=n==t.parentRange&&null!=r[s-1];var x=t.getDuration();if(null!=x&&!v){var k=x.start;p=x.end-k,u&&(m=p)}if(f.length>0&&(null==r?void 0:r.length)>0){var w=r.filter((function(e){return e.canvasId===zt(f[0])}))[0];if(g=w.isEmpty,y=w.summary,b=w.homepage,h=!0,null!=w.range){var E=w.range,T=E.start,S=E.end;m=S-T,u&&(p=S-T)}}var C=O({label:d,summary:y,isRoot:v,homepage:b,canvasDuration:m,isTitle:0===f.length,rangeId:t.id,id:f.length>0?u?"".concat(f[0].split(",")[0],","):f[0]:void 0,isEmpty:g,isCanvas:u,itemIndex:u?s:void 0,canvasIndex:s,items:(null===(c=t.getRanges())||void 0===c?void 0:c.length)>0?t.getRanges().map((function(t){return e(t,n)})):[],duration:Tt(p),isClickable:h},"homepage",b);return f.length>0&&(l++,u||(C.itemIndex=l),a.push(C)),C}};try{var u=t.parseManifest(e).getAllRanges();if(0===(null==u?void 0:u.length))return{structures:[],timespans:[],markRoot:!1};var d=u[0],f=[],p=d.getBehavior();if(p&&"no-nav"==p)return{structures:[],timespans:[]};if(n||"top"===p){var m=d.getRanges();(null==m?void 0:m.length)>0&&m.map((function(e,t){"no-nav"!=e.getBehavior()&&(l=0,s=t+1,f.push(c(e,d)))}))}else o=!0,i=r.reduce((function(e,t){return e+t.range.end}),0),f.push(c(d,d));var v=o&&(null==r?void 0:r.length)>1;return{structures:f,timespans:a,markRoot:v}}catch(e){throw console.error("iiif-parser -> getStructureRanges() -> error parsing structures"),new Error(bt)}}(b,m,x.isPlaylist),r=e.structures,a=e.timespans,i=e.markRoot;S.current=r,T.current=r,I.current=i,(null==r?void 0:r.length)>0&&r[0].isRoot&&(T.current=r[0].items),n({structures:T.current,type:"setStructures"}),n({timespans:a,type:"setCanvasSegments"}),R.current.isScrolling=!1}catch(e){E(e)}}),[b]),e.useEffect((function(){k&&x.isPlaylist&&n({item:w[h],type:"switchItem"})}),[k,h]),e.useEffect((function(){if(l){var e=w.filter((function(e){return e.id===s}));if((null==e?void 0:e.length)>0){var t=e[0],r=t.isCanvas,a=t.items;(!r||0==a.length&&r)&&n({item:e[0],type:"switchItem"})}var o=m.findIndex((function(e){return e.canvasURL===zt(s)})),u=Ot(s,v);if(!u||null==u)return void console.error("StructuredNavigation -> invalid media fragment in structure item -> ",u);var f=u.start;if(g){var p=function(e,t,r){var n,a;return e.map((function(i,o){var s=o>0?e[o].altStart:0;isNaN(c)&&(c=r);var l=i.start,c=i.end,u=s+l,d=s+c;t.start>=u&&t.start-1&&(n({canvasIndex:o,type:"switchCanvas"}),C.current=T.current[o].isEmpty);d&&!C.current?(d.currentTime(f),i({startTime:u.start,endTime:u.end,type:"setTimeFragment"}),d.structStart=f,i({currentTime:f,type:"setCurrentTime"}),c&&d.userActive(!0)):C.current&&i({type:"resetClick"})}}),[l,d]),e.useEffect((function(){if(R.current){var e=R.current,t=R.current.parentElement,r=Math.abs(e.scrollHeight-(e.scrollTop+e.clientHeight))<=1;q.current=!r,t&&M.observe(t)}}),[d]);var j=function(e){var t=e.target;t.classList.contains("ramp--structured-nav__border")&&(t=t.firstChild);var r=t.nextSibling,n=Math.abs(t.scrollHeight-(t.scrollTop+t.clientHeight))<=1;t&&n&&t.classList.contains("scrollable")?t.classList.remove("scrollable"):!t||n||t.classList.contains("scrollable")||t.classList.add("scrollable"),r&&n&&r.classList.contains("scrollable")?r.classList.remove("scrollable"):!r||n||r.classList.contains("scrollable")||r.classList.add("scrollable")},M=new ResizeObserver((function(e){var t,r=ui(e);try{for(r.s();!(t=r.n()).done;){var n=t.value;j(n)}}catch(e){r.e(e)}finally{r.f()}}));if(!b)return u.default.createElement("p",null,"No manifest - Please provide a valid manifest.");var D=function(e){R.current.isScrolling=e};return u.default.createElement("div",{className:"ramp--structured-nav"},u.default.createElement("div",{className:"ramp--structured-nav__border"},u.default.createElement("div",{"data-testid":"structured-nav",className:p.default("ramp--structured-nav__content",q.current&&"scrollable",(null==x?void 0:x.isPlaylist)&&"playlist-items",I.current&&"ramp--structured-nav__content-with_root"),ref:R,role:"list","aria-label":"Structural content",onScroll:j,onMouseLeave:function(){return D(!1)},onMouseOver:function(){return D(!0)}},(null===(r=S.current)||void 0===r?void 0:r.length)>0?S.current.map((function(t,r){var n;return t.isCanvas&&!x.isPlaylist?u.default.createElement(li,{key:"".concat(t.label,"-").concat(r),itemIndex:r+1,duration:t.duration,label:t.label,sectionRef:e.createRef(),itemId:t.id,isRoot:t.isRoot,structureContainerRef:R,hasChildren:(null===(n=t.items)||void 0===n?void 0:n.length)>0,items:t.items}):u.default.createElement(ci,{items:[t],sectionRef:e.createRef(),key:"".concat(t.label,"-").concat(r),structureContainerRef:R,isPlaylist:x.isPlaylist})})):u.default.createElement("p",{className:"ramp--no-structure"},"There are no structures in the manifest")),u.default.createElement("span",{className:p.default(q.current&&"scrollable")},"Scroll to see more")))};fi.propTypes={};var pi=y((function(e){e.exports=function(e,t){if(null==e)return{};var r,n,a={},i=Object.keys(e);for(n=0;n=0||(a[r]=e[r]);return a},e.exports.__esModule=!0,e.exports.default=e.exports})),mi=g(y((function(e){e.exports=function(e,t){if(null==e)return{};var r,n,a=pi(e,t);if(Object.getOwnPropertySymbols){var i=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a},e.exports.__esModule=!0,e.exports.default=e.exports}))),vi=function(e){var t=e.fileUrl,r=e.fileName,n=e.machineGenerated,a=e.fileExt;return u.default.createElement("button",{className:"ramp--transcript_menu_button ramp--transcript_downloader","data-testid":"transcript-downloader",onClick:function(e){e.preventDefault(),Rt(t,r,a,n)},href:"#","aria-label":"Transcript download button"},u.default.createElement(Sn,null))};vi.propTypes={fileUrl:Tr.string,fileName:Tr.string,machineGenerated:Tr.bool,fileExt:Tr.string};var hi=function(e){var t=e.selectTranscript,r=e.transcriptData,n=e.transcriptInfo,a=e.noTranscript,i=n.filename,o=n.id,s=n.tUrl,l=n.tFileExt,c=n.isMachineGen;return r?[u.default.createElement("div",{key:"transcript-selector","data-testid":"transcript-selector",className:"ramp--transcript_selector"},u.default.createElement("select",{"data-testid":"transcript-select-option",value:o||"",onChange:function(e){t(e.target.value)},"aria-label":"Select transcripts","aria-expanded":!1,"aria-haspopup":"true"},r.map((function(e,t){return u.default.createElement("option",{value:e.id,label:"".concat(e.title).concat(e.numberOfHits?" ("+e.numberOfHits+")":""),key:t},"".concat(e.title).concat(e.numberOfHits?" ("+e.numberOfHits+")":""))}))),!a&&u.default.createElement(vi,{key:"transcript-downloader",fileUrl:s,fileName:i,fileExt:l,machineGenerated:c}))]:null};hi.propTypes={selectTranscript:Tr.func.isRequired,transcriptData:Tr.array.isRequired,transcriptInfo:Tr.shape({title:Tr.string,id:Tr.string,tUrl:Tr.string,tFileExt:Tr.string,isMachineGen:Tr.bool}).isRequired,noTranscript:Tr.bool.isRequired};var gi=e.memo(hi),yi=function(t){var r=t.searchResults,n=t.searchQuery,a=void 0===n?null:n,i=t.focusedMatchIndex,o=t.setFocusedMatchIndex,s=t.setSearchQuery,l=e.useRef(null);e.useEffect((function(){l.current&&a&&(l.current.value=a)}),[!!l.current]);var c=e.useMemo((function(){return Jr((function(e){s(e.target.value)}),100)}),[]),d=null===a||""===a.replace(/\s/g,""),f=null;return d||(0===r.matchingIds.length?f=u.default.createElement("div",{className:"ramp--transcript_search_navigator"},u.default.createElement("span",{"data-testid":"transcript-search-count",className:"ramp--transcript_search_count"},"no results found in this transcript")):null!==i&&(f=u.default.createElement("div",{className:"ramp--transcript_search_navigator"},u.default.createElement("button",{type:"button","data-testid":"transcript-search-prev",className:"ramp--transcript_menu_button ramp--transcript_search_prev",disabled:0===i,title:"Previous Search Result",onClick:function(e){e.preventDefault(),e.stopPropagation(),i>0&&o(i-1)}},u.default.createElement(Tn,{flip:!0})),u.default.createElement("span",{className:"ramp--transcript_search_count","data-testid":"transcript-search-count"},i+1," of ",r.matchingIds.length," results"),u.default.createElement("button",{className:"ramp--transcript_menu_button ramp--transcript_search_next",type:"button","data-testid":"transcript-search-next",disabled:i>=r.matchingIds.length-1,title:"Next Search Result",onClick:function(e){e.preventDefault(),e.stopPropagation(),i0)){n.next=13;break}return u=Qn(c,a,t,r),n.abrupt("return",u);case 13:return n.abrupt("return",{matchedTranscriptLines:[],hitCounts:[],allSearchHits:null});case 16:return n.prev=16,n.t0=n.catch(0),"AbortError"!==n.t0.name&&console.error(n.t0),n.abrupt("return",{matchedTranscriptLines:[],hitCounts:[],allSearchHits:null});case 20:case"end":return n.stop()}}),n,null,[[0,16]])})));return function(e,t){return n.apply(this,arguments)}}()},Ii={initialSearchQuery:null,showMarkers:!0,matcherFactory:function(e){var t=e.map((function(e){return e.text.toLocaleLowerCase()}));return function(r,n){var a=new RegExp(String.raw(wi||(wi=Cn(["",""])),r),"i"),i=r.trim().toLocaleLowerCase(),o=t.reduce((function(t,r,n){var o=r.search(a);if(-1!==o){var s=e[n],l=[s.text.slice(0,o),s.text.slice(o,o+i.length),s.text.slice(o+i.length)],c=l[1],u=l[2],d="".concat(l[0],'').concat(c,"").concat(u);return[].concat(D(t),[Si(Si({},s),{},{score:n,match:d,matchCount:1})])}return t}),[]);return{matchedTranscriptLines:o,hitCounts:[],allSearchHits:null}}},sorter:function(e){return e.sort((function(e,t){return e.id-t.id}))},matchesOnly:!1};var Ri=["initialSearchQuery"];function Oi(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function qi(e){for(var t=1;t1&&void 0!==arguments[1]&&arguments[1]?e.textDisplayed:e.text;return e.match&&(t=e.match),e.speaker?"".concat(e.speaker,": ").concat(t):t},Mi=e.memo((function(t){var r=t.item,n=t.goToItem,a=t.isActive,i=t.focusedMatchId,o=t.setFocusedMatchId,s=t.autoScrollEnabled,l=t.showNotes,c=t.transcriptContainerRef,d=t.isNonTimedText,f=t.focusedMatchIndex,m=e.useRef(null),v=r.id===i,h=e.useRef(v),g=e.useRef(a),y=e.useRef(-1),b=e.useRef(-1),x=e.useRef(0);e.useEffect((function(){var e=!1,t=b.current;a&&!g.current?s&&(g.current=!0,e=!0):g.current=!1,v&&!h.current?(h.current=!0,e=!0):h.current=!1,e&&m.current&&Lt(m.current,c,!0),x.current=tt?x.current+1:x.current<=0?0:x.current-1,x.current>-1){var r=e[x.current];null!=r&&(r.classList.add("current-hit"),Lt(r,c,!0))}y.current=f}}),[f]);var k=function(e){e.preventDefault(),e.stopPropagation(),r.match&&i!==r.id?o(r.id):null!==i&&r.tag===Pn.timedCue&&Lt(m.current,c,!0),n(r)};return r.tag===Pn.note&&l?u.default.createElement("a",{href:"#",ref:m,role:"listitem",onClick:k,className:p.default("ramp--transcript_item",a&&"active",v&&"focused"),"data-testid":"transcript_text",dangerouslySetInnerHTML:{__html:ji(r)}}):r.tag===Pn.timedCue?u.default.createElement("a",{href:"#",ref:m,role:"listitem",onClick:k,"data-testid":"transcript_item",className:p.default("ramp--transcript_item",a&&"active",v&&"focused")},"number"==typeof r.begin&&u.default.createElement("span",{className:"ramp--transcript_time","data-testid":"transcript_time"},"[",Tt(r.begin,!0),"]"),u.default.createElement("span",{className:"ramp--transcript_text","data-testid":"transcript_text",dangerouslySetInnerHTML:{__html:ji(r)}})):r.tag===Pn.nonTimedLine?u.default.createElement("a",{href:"#",ref:m,role:"listitem",onClick:k,className:p.default("ramp--transcript_item",a&&"active",v&&"focused"),"data-testid":"transcript_untimed_text"},u.default.createElement("p",{className:"ramp--transcript_untimed_item",dangerouslySetInnerHTML:{__html:ji(r,d)}})):null})),Di=e.memo((function(t){var r,n=t.seekPlayer,a=t.currentTime,i=t.searchResults,o=t.focusedMatchId,s=t.transcriptInfo,l=t.setFocusedMatchId,c=t.autoScrollEnabled,d=t.showNotes,f=t.transcriptContainerRef,p=t.focusedMatchIndex,m=e.useState(null),v=T(m,2),h=v[0],g=v[1],y=e.useCallback((function(e){"number"==typeof e.begin?(n(e.begin),g(null)):g(e.id)}),[n]);switch(s.tType){case Dn.plainText:r="plain-text";break;case Dn.docx:r="docs";break;case Dn.timedText:r="timed-text";default:r=""}return s.tError?u.default.createElement("p",{key:"no-transcript",id:"no-transcript","data-testid":"no-transcript",role:"note"},s.tError):i.results&&0!==i.results.length?u.default.createElement("div",{"data-testid":"transcript_".concat(r)},i.ids.map((function(e){return u.default.createElement(Mi,{key:e,goToItem:y,focusedMatchId:o,isActive:h===e||"number"==typeof i.results[e].begin&&i.results[e].begin<=a&&a<=i.results[e].end,item:i.results[e],autoScrollEnabled:c,setFocusedMatchId:l,showNotes:d,transcriptContainerRef:f,isNonTimedText:!0,focusedMatchIndex:p})}))):u.default.createElement(Sr,null)})),Pi=function(t){var r,n,a=t.playerID,i=t.manifestUrl,o=t.showNotes,s=void 0!==o&&o,l=t.search,c=void 0===l?{}:l,d=t.transcripts,f=void 0===d?[]:d,m=e.useState(-1),v=T(m,2),h=v[0],g=v[1],y=e.useMemo((function(){return Zr(g,50)}),[]),b=fa({manifestUrl:i,playerID:a,setCurrentTime:y,transcripts:f}),x=b.canvasIndexRef,k=b.canvasTranscripts,w=b.isEmpty,E=b.isLoading,S=b.NO_SUPPORT_MSG,C=b.playerRef,I=b.selectedTranscript,R=b.selectTranscript,q=b.transcript,j=b.transcriptInfo,M=(r=qi(qi({},c),{},{isSearchable:j.tType===Dn.timedText||j.tType===Dn.docx||j.tType===Dn.plainText,showMarkers:j.tType===Dn.timedText}))&&r.isSearchable?Si(Si(Si({},Ii),r),{},{enabled:!0}):Si(Si({},Ii),{},{enabled:!1}),P=M.initialSearchQuery,A=mi(M,Ri),L=e.useState(P),_=T(L,2),N=_[0],F=_[1],U=function(t){var r=t.query,n=t.sorter,a=void 0===n?Ii.sorter:n,i=t.enabled,o=void 0===i||i,s=t.transcripts,l=t.canvasIndex,c=t.selectedTranscript,u=t.showMarkers,d=void 0===u?Ii.showMarkers:u,f=t.matchesOnly,p=void 0===f?Ii.matchesOnly:f,m=t.matcherFactory,v=void 0===m?Ii.matcherFactory:m,h=e.useState({results:{},ids:[],matchingIds:[],counts:[]}),g=T(h,2),y=g[0],b=g[1],x=e.useState(),k=T(x,2),w=k[0],E=k[1],S=e.useState(null),C=T(S,2),I=C[0],R=C[1],q=e.useRef(null),j=e.useRef(0),M=e.useMemo((function(){var e=(s||[]).map((function(e,t){return"string"==typeof e?{text:e,id:t}:Si({id:t},e)})),t=e.reduce((function(e,t){return Si(Si({},e),{},O({},t.id,t))}),{}),r=v(e);return null!=w&&null!=w&&(r=Ci(w,e,c)),{matcher:r,itemsWithIds:e,itemsIndexed:t}}),[s,v,c]),P=M.matcher,A=M.itemsWithIds,L=M.itemsIndexed,_=e.useContext(dr),N=e.useContext(er);e.useEffect((function(){if(N&&l>=0){var e=N.manifest,t=N.allCanvases,r=null;null!=t&&t.length?r=t[l].searchService:e&&(r=Qt(e)),E(r)}R(null)}),[l]),e.useEffect((function(){q.current&&q.current.abort(),r&&F()}),[r]),e.useEffect((function(){if(!A.length)return _&&_({type:"setSearchMarkers",payload:[]}),void b(Si(Si({},y),{},{results:{},matchingIds:[],ids:[]}));if(!o||!r){_&&_({type:"setSearchMarkers",payload:[]});var e=a(D(A)).map((function(e){return e.id}));return b(Si(Si({},y),{},{results:L,matchingIds:[],ids:e})),void(r||R(null))}if(null!=I){var t=I[c],n=Kn(t,r,A);U(n,null==y?void 0:y.counts,I)}else F()}),[P,r,o,a,p,d,_,c]);var F=function(){j.current||clearTimeout(j.current);var e=new AbortController;q.current=e,j.current=setTimeout((function(){Promise.resolve(P(r,q.current)).then((function(t){var r=t.matchedTranscriptLines,n=t.hitCounts,a=t.allSearchHits;e.signal.aborted||U(r,n,a)})).catch((function(e){console.error("Search failed: ",r)}))}))},U=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:[];R(arguments.length>2&&void 0!==arguments[2]?arguments[2]:null);var n={results:A,matchingIds:[],ids:a(D(A)).map((function(e){return e.id})),counts:(null==t?void 0:t.length)>0?t:[]};if(void 0!==e){var i=e.reduce((function(e,t){return Si(Si({},e),{},O({},t.id,t))}),{}),o=a(D(e),!0),s=[];if(o.map((function(e){if(null!=e.matchCount)for(var t=0;t=4&&n.matchingIds.length<45)&&(u=n.matchingIds.map((function(e){return{time:n.results[e].begin,text:"",class:"ramp--track-marker--search"}}))),_({type:"setSearchMarkers",payload:u})}else _({type:"setSearchMarkers",payload:[]})}}else b(Si({},n))};return y}(qi(qi({},A),{},{query:N,transcripts:q,canvasIndex:x.current,selectedTranscript:I})),B=function(t){var r=t.searchResults,n=e.useState(null),a=T(n,2),i=a[0],o=a[1],s=null===i?null:r.matchingIds[i],l=e.useCallback((function(e){var t=r.matchingIds.indexOf(e);o(-1!==t?t:null)}),[r.matchingIds]);return e.useEffect((function(){r.matchingIds.length||null===i?r.matchingIds.length&&null===i?o(0):null!==i&&i>=r.matchingIds.length&&o(r.matchingIds.length-1):o(null)}),[r.matchingIds,i]),e.useEffect((function(){r.matchingIds.length&&i>0&&o(null)}),[r.matchingIds]),{focusedMatchId:s,setFocusedMatchId:l,focusedMatchIndex:i,setFocusedMatchIndex:o}}({searchResults:U}),V=B.focusedMatchId,H=B.setFocusedMatchId,G=B.focusedMatchIndex,z=B.setFocusedMatchIndex,J=function(e){var t=e.searchResults,r=e.canvasTranscripts,n=e.searchQuery;if(null==t||!t.counts||0===(null==r?void 0:r.length)||null===n)return r;var a=t.counts,i=[];return r.map((function(e){var t,r=(null===(t=a.find((function(t){return t.transcriptURL===e.url})))||void 0===t?void 0:t.numberOfHits)||0;i.push(Si(Si({},e),{},{numberOfHits:r}))})),i}({searchResults:U,canvasTranscripts:k,searchQuery:N}),W=e.useState(!0),Z=T(W,2),Y=Z[0],Q=Z[1],K=e.useRef(Y),X=e.useRef(),$=e.useCallback((function(e){y(e),C.current&&C.current.currentTime(e)}),[]);return E?u.default.createElement(Sr,null):u.default.createElement("div",{className:"ramp--transcript_nav","data-testid":"transcript_nav",key:j.title},!w&&u.default.createElement(Ei,{showSearch:A.enabled,selectTranscript:R,transcriptData:J,transcriptInfo:j,noTranscript:(null===(n=j.tError)||void 0===n?void 0:n.length)>0&&j.tError!=S,setAutoScrollEnabled:function(e){K.current=e,Q(e)},setFocusedMatchIndex:z,focusedMatchIndex:G,autoScrollEnabled:K.current,searchResults:U,searchQuery:N,setSearchQuery:F}),u.default.createElement("div",{className:p.default("transcript_content",q?"":"static"),"data-testid":"transcript_content_".concat(j.tType),role:"list","aria-label":"Attached Transcript content",ref:X},u.default.createElement(Di,{currentTime:h,seekPlayer:$,searchResults:U,focusedMatchId:V,transcriptInfo:j,setFocusedMatchId:H,autoScrollEnabled:K.current&&null===N,showNotes:s,transcriptContainerRef:X,focusedMatchIndex:G})))};Pi.propTypes={playerID:Tr.string.isRequired,manifestUrl:Tr.string,showSearch:Tr.bool,showNotes:Tr.bool,search:Tr.oneOf([Tr.bool,Tr.shape({initialSearchQuery:Tr.string,showMarkers:Tr.bool,matcherFactory:Tr.func,sorter:Tr.func,matchesOnly:Tr.bool})]),transcripts:Tr.arrayOf(Tr.shape({canvasId:Tr.number.isRequired,items:Tr.arrayOf(Tr.shape({title:Tr.string,url:Tr.string}))}))};var Ai=function(t){var r=t.displayOnlyCanvasMetadata,n=void 0!==r&&r,a=t.displayAllMetadata,i=void 0!==a&&a,o=t.displayTitle,s=void 0===o||o,l=t.showHeading,c=void 0===l||l,d=t.itemHeading,f=void 0===d?"Item Details":d,p=t.sectionHeaading,m=void 0===p?"Section Details":p,v=or(),h=v.manifest,g=v.canvasIndex,y=e.useState(),b=T(y,2),x=b[0],k=b[1],w=e.useState(),E=T(w,2);E[0];var S=E[1],C=e.useState(),I=T(C,2),R=I[0],O=I[1],q=e.useState(),j=T(q,2),M=j[0],D=j[1],P=e.useState(),A=T(P,2),L=A[0],_=A[1],N=e.useState(),F=T(N,2),U=F[0],B=F[1],V=e.useState(),H=T(V,2),G=H[0],z=H[1],J=e.useState(!1),W=T(J,2),Z=W[0],Y=W[1],Q=e.useRef();e.useEffect((function(){if(h){var e,t=n||i;_(t);var r=!n||i;D(r);var a=function(e,t){var r=[],n={canvasMetadata:r,manifestMetadata:[],rights:[]},a=e.items;if(t&&a){for(var i in a){var o=parseInt(i),s=Yt(a[o],"Canvas");r.push({canvasindex:o,metadata:Zt(a[o].metadata,"Canvas"),rights:s})}n.canvasMetadata=r}var l=Zt(e.metadata,"Manifest");n.manifestMetadata=l;var c=Yt(e,"Manifest");return n.rights=c,n}(h,t);if(t&&(c=a.canvasMetadata,S(c),Q.current=c,K()),r){var o,l=a.manifestMetadata;s||(l=l.filter((function(e){return"title"!=e.label.toLowerCase()}))),k(l),Y((null===(o=l)||void 0===o?void 0:o.length)>0)}(null===(e=a.rights)||void 0===e?void 0:e.length)>0&&B(a.rights)}var c}),[h]),e.useEffect((function(){g>=0&&L&&K()}),[g]);var K=function(){var e=Q.current.filter((function(e){return e.canvasindex===g}))[0];if(null!=e){var t,r=e.metadata,n=e.rights;s||null==r||(r=r.filter((function(e){return"title"!=e.label.toLowerCase()}))),O(r),Y((null===(t=r)||void 0===t?void 0:t.length)>0),null!=n&&(null==n?void 0:n.length)>0&&z(n)}},X=function(t){var r=[];return(null==t?void 0:t.length)>0&&t.map((function(t,n){r.push(u.default.createElement(e.Fragment,{key:n},u.default.createElement("dt",null,t.label),u.default.createElement("dd",{dangerouslySetInnerHTML:{__html:t.value}})))})),u.default.createElement("dl",null,r)},$=e.useMemo((function(){if(M&&(null==x?void 0:x.length)>0)return u.default.createElement(u.default.Fragment,null,i&&u.default.createElement("span",null,f),X(x),(null==U?void 0:U.length)>0&&u.default.createElement("span",{className:"ramp--metadata-rights-heading","data-testid":"manifest-rights"},"Rights"),X(U))}),[x]),ee=e.useMemo((function(){if(L&&(null==R?void 0:R.length)>0)return u.default.createElement(u.default.Fragment,null,i&&u.default.createElement("span",null,m),X(R),(null==G?void 0:G.length)>0&&u.default.createElement("span",{className:"ramp--metadata-rights-heading","data-testid":"canvas-rights"},"Rights"),X(G))}),[R]);return u.default.createElement("div",{"data-testid":"metadata-display",className:"ramp--metadata-display"},c&&u.default.createElement("div",{className:"ramp--metadata-display-title","data-testid":"metadata-display-title"},u.default.createElement("h4",null,"Details")),Z?u.default.createElement("div",{className:"ramp--metadata-display-content"},$,ee):u.default.createElement("div",{"data-testid":"metadata-display-message",className:"ramp--metadata-display-message"},u.default.createElement("p",null,"No valid Metadata is in the Manifest/Canvas(es)")))};Ai.propTypes={displayOnlyCanvasMetadata:Tr.bool,displayAllMetadata:Tr.bool,displayTitle:Tr.bool,showHeading:Tr.bool,itemHeading:Tr.string,sectionHeaading:Tr.string};var Li=function(t){var r=t.label,n=void 0===r?"Autoplay":r,a=t.showLabel,i=void 0===a||a,o=or().autoAdvance,s=sr(),l=function(e){s({autoAdvance:e.target.checked,type:"setAutoAdvance"})},c=e.useMemo((function(){return u.default.createElement("input",{"data-testid":"auto-advance-toggle",name:"auto-advance-toggle",type:"checkbox",checked:o,"aria-label":n,onChange:l})}),[o]);return u.default.createElement("div",{"data-testid":"auto-advance",className:"ramp--auto-advance"},i&&u.default.createElement("span",{className:"ramp--auto-advance-label","data-testid":"auto-advance-label",htmlFor:"auto-advance-toggle",id:"auto-advance-toggle-label"},n),u.default.createElement("label",{className:"ramp--auto-advance-toggle","aria-labelledby":"auto-advance-toggle-label"},c,u.default.createElement("span",{className:"slider round"})))};Li.propTypes={label:Tr.string,showLabel:Tr.bool};var _i=function(t){var r,n=t.newMarkerEndpoint,a=t.canvasId,i=t.handleCreate,o=t.csrfToken,s=e.useState(!1),l=T(s,2),c=l[0],d=l[1],f=e.useState(!1),m=T(f,2),v=m[0],h=m[1],g=e.useState(!1),y=T(g,2),b=y[0],x=y[1],k=e.useState(""),w=T(k,2),E=w[0],S=w[1],C=e.useState(),I=T(C,2),R=I[0],O=I[1],q=sa().getCurrentTime;e.useEffect((function(){return d(!1),function(){var e;null===(e=r)||void 0===e||e.abort()}}),[a]);var j=e.useCallback((function(e){e.preventDefault();var t=e.target,s=new FormData(t),l=Object.fromEntries(s.entries()),c=l.label,u=l.time,f={type:"Annotation",motivation:"highlighting",body:{type:"TextualBody",format:"text/html",value:c},target:"".concat(a,"#t=").concat(St(u))},p={method:"POST",credentials:"same-origin",headers:{Accept:"application/json"},body:JSON.stringify(f)};void 0!==o&&(p.headers["X-CSRF-Token"]=o),r=new AbortController,fetch(n,p,{signal:r.signal}).then((function(e){if(201!=e.status)throw new Error;return e.json()})).then((function(e){var t=Kt(e);t&&i(t),d(!1)})).catch((function(e){console.error("CreateMarker -> handleCreateMarker() -> failed to create annotation; ",e),x(!0),S("Marker creation failed.")}))}),[a]),M=e.useCallback((function(){d(!1),h(!1),S(""),x(!1)})),D=function(e){var t,r,n=null!==(t=null==e||null===(r=e.target)||void 0===r?void 0:r.value)&&void 0!==t?t:e;O(n);var a=At(n);h(a)};return u.default.createElement("div",{className:"ramp-markers-display__new-marker"},u.default.createElement("button",{type:"submit",onClick:function(){var e=Tt(q(),!0,!0);D(e),d(!0)},className:"ramp--markers-display__edit-button","data-testid":"create-new-marker-button"},"Add New Marker"),c&&u.default.createElement("form",{className:"ramp--markers-display__new-marker-form",method:"post",onSubmit:j,"data-testid":"create-new-marker-form"},u.default.createElement("table",{className:"create-marker-form-table"},u.default.createElement("tbody",null,u.default.createElement("tr",null,u.default.createElement("td",null,u.default.createElement("label",{htmlFor:"new-marker-title"},"Title:"),u.default.createElement("input",{id:"new-marker-title","data-testid":"create-marker-title",type:"text",className:"ramp--markers-display__create-marker",name:"label"})),u.default.createElement("td",null,u.default.createElement("label",{htmlFor:"new-marker-time"},"Time:"),u.default.createElement("input",{id:"new-marker-time","data-testid":"create-marker-timestamp",type:"text",className:p.default("ramp--markers-display__create-marker",v?"time-valid":"time-invalid"),name:"time",value:R,onChange:D})),u.default.createElement("td",null,u.default.createElement("div",{className:"marker-actions"},b&&u.default.createElement("p",{className:"ramp--markers-display__error-message"},E),u.default.createElement("button",{type:"submit",className:"ramp--markers-display__edit-button","data-testid":"edit-save-button",disabled:!v},u.default.createElement(xn,null)," Save"),u.default.createElement("button",{className:"ramp--markers-display__edit-button-danger","data-testid":"edit-cancel-button",onClick:M},u.default.createElement(kn,null)," Cancel"))))))))};_i.propTypes={newMarkerEndpoint:Tr.string.isRequired,canvasId:Tr.string,handleCreate:Tr.func.isRequired,csrfToken:Tr.string};var Ni=function(t){var r,n=t.marker,a=t.handleSubmit,i=t.handleDelete,o=t.toggleIsEditing,s=t.csrfToken,l=e.useState(!1),c=T(l,2),d=c[0],f=c[1],m=e.useState(!0),v=T(m,2),h=v[0],g=v[1],y=e.useState(),b=T(y,2),x=b[0],k=b[1],w=e.useState(!1),E=T(w,2),S=E[0],C=E[1],I=e.useState(!1),R=T(I,2),O=R[0],q=R[1],j=e.useState(""),M=T(j,2),D=M[0],P=M[1],A=function(){var t=e.useContext(er).playlist,r=t.isEditing,n=t.hasAnnotationService;return{isDisabled:e.useMemo((function(){return r}),[r]),hasAnnotationService:n}}(),L=A.hasAnnotationService,_=A.isDisabled,N=sa().player;e.useEffect((function(){return function(){var e;null===(e=r)||void 0===e||e.abort()}}),[]),e.useEffect((function(){U(n.value),H(n.timeStr)}),[n]);var F=e.useRef(n.value),U=function(e){F.current=e},B=e.useRef(St(n.timeStr)),V=e.useRef(n.timeStr),H=function(e){V.current=e,B.current=St(e)},G=function(){q(!1),P("")},z=function(){C(!1),f(!1),o(!1)},J=e.useCallback((function(e){e.preventDefault();var t=parseFloat(e.target.dataset.offset);N&&N.currentTime(t)}),[N]);return d?u.default.createElement("tr",null,u.default.createElement("td",null,u.default.createElement("input",{id:"label","data-testid":"edit-label",defaultValue:F.current,type:"text",className:"ramp--markers-display__edit-marker",onChange:function(e){return U(e.target.value)},name:"label"})),u.default.createElement("td",null,u.default.createElement("input",{className:p.default("ramp--markers-display__edit-marker",h?"time-valid":"time-invalid"),id:"time","data-testid":"edit-timestamp",defaultValue:V.current,type:"text",onChange:function(e){return function(e){var t=At(e);g(t),H(e)}(e.target.value)},name:"time"})),u.default.createElement("td",null,u.default.createElement("div",{className:"marker-actions"},O&&u.default.createElement("p",{className:"ramp--markers-display__error-message"},D),u.default.createElement("button",{type:"submit",onClick:function(){var e={type:"Annotation",motivation:"highlighting",body:{type:"TextualBody",format:"text/html",value:F.current},id:n.id,target:"".concat(n.canvasId,"#t=").concat(St(V.current))},t={method:"PUT",credentials:"same-origin",headers:{Accept:"application/json"},body:JSON.stringify(e)};void 0!==s&&(t.headers["X-CSRF-Token"]=s),r=new AbortController,fetch(n.id,t,{signal:r.signal}).then((function(e){if(201!=e.status)throw new Error;a(F.current,V.current,n.id),G(),z()})).catch((function(e){console.error("MarkerRow -> handleEditSubmit -> failed to update annotation; ",e),q(!0),P("Marker update failed")}))},disabled:!h,className:"ramp--markers-display__edit-button","data-testid":"edit-save-button"},u.default.createElement(xn,null)," Save"),u.default.createElement("button",{className:"ramp--markers-display__edit-button-danger","data-testid":"edit-cancel-button",onClick:function(){H(x.time),U(x.label),k({}),G(),z()}},u.default.createElement(kn,null)," Cancel")))):S?u.default.createElement("tr",null,u.default.createElement("td",null,u.default.createElement("a",{href:"".concat(n.canvasId,"#t=").concat(B.current,","),onClick:function(e){return J(e)},"data-offset":B.current},F.current)),u.default.createElement("td",null,V.current),u.default.createElement("td",null,u.default.createElement("div",{className:"marker-actions"},u.default.createElement("p",null,"Are you sure?"),u.default.createElement("button",{type:"submit",className:"ramp--markers-display__edit-button-danger","data-testid":"delete-confirm-button",onClick:function(){var e={method:"DELETE",credentials:"same-origin",headers:{Accept:"application/json"}};void 0!==s&&(e.headers["X-CSRF-Token"]=s),r=new AbortController,fetch(n.id,e,{signal:r.signal}).then((function(e){if(200!=e.status)throw new Error;i(n.id),G(),z()})).catch((function(e){console.error("MarkerRow -> submitDelete() -> failed to delete annotation; ",e),z(),q(!0),P("Marker delete failed."),setTimeout((function(){G()}),1500)}))}},u.default.createElement(xn,null)," Yes"),u.default.createElement("button",{className:"ramp--markers-display__edit-button","data-testid":"delete-cancel-button",onClick:z},u.default.createElement(kn,null)," Cancel")))):u.default.createElement("tr",null,u.default.createElement("td",null,u.default.createElement("a",{href:"".concat(n.canvasId,"#t=").concat(B.current,","),onClick:function(e){return J(e)},"data-offset":B.current},F.current)),u.default.createElement("td",null,V.current),L&&u.default.createElement("td",null,u.default.createElement("div",{className:"marker-actions"},O&&u.default.createElement("p",{className:"ramp--markers-display__error-message"},D),u.default.createElement("button",{onClick:function(){k({time:V.current,label:F.current}),f(!0),o(!0)},className:"ramp--markers-display__edit-button","data-testid":"edit-button",disabled:_},u.default.createElement(yn,null)," Edit"),u.default.createElement("button",{className:"ramp--markers-display__edit-button-danger","data-testid":"delete-button",disabled:_,onClick:function(){C(!0),o(!0)}},u.default.createElement(bn,null)," Delete"))))};Ni.propTypes={marker:Tr.object.isRequired,handleSubmit:Tr.func.isRequired,handleDelete:Tr.func.isRequired,toggleIsEditing:Tr.func.isRequired,csrfToken:Tr.string};var Fi=function(t){var r,n=t.showHeading,i=void 0===n||n,o=t.headingText,s=void 0===o?"Markers":o,l=or(),c=l.allCanvases,d=l.canvasIndex,f=l.playlist,p=sr(),m=f.hasAnnotationService,v=f.annotationServiceId,h=f.markers,g=e.useState([]),y=T(g,2);y[0];var b=y[1],x=a.useErrorBoundary().showBoundary,k=e.useRef(),w=e.useRef([]),E=function(e){b.apply(void 0,D(e)),w.current=e},S=null===(r=document.getElementsByName("csrf-token")[0])||void 0===r?void 0:r.content;e.useEffect((function(){try{if((null==h?void 0:h.length)>0){var e=h.filter((function(e){return e.canvasIndex===d}))[0].canvasMarkers;E(e),null!=c&&(null==c?void 0:c.length)>0&&(k.current=c[d].canvasId)}}catch(e){x(e)}}),[d,h]);var C=e.useCallback((function(e,t,r){var n=w.current.map((function(n){return n.id===r&&(n.value=e,n.timeStr=t,n.time=St(t)),n}));E(n),p({updatedMarkers:n,type:"setPlaylistMarkers"})})),I=e.useCallback((function(e){var t=w.current.filter((function(t){return t.id!=e}));E(t),p({updatedMarkers:t,type:"setPlaylistMarkers"})})),R=e.useCallback((function(e){E([].concat(D(w.current),[e])),p({updatedMarkers:w.current,type:"setPlaylistMarkers"})})),O=e.useCallback((function(e){p({isEditing:e,type:"setIsEditing"})})),q=e.useMemo((function(){if(m)return u.default.createElement(_i,{newMarkerEndpoint:v,canvasId:k.current,handleCreate:R,csrfToken:S})}),[m,k.current,S]),j=e.useMemo((function(){if(w.current.length>0)return u.default.createElement("table",{className:"ramp--markers-display_table","data-testid":"markers-display-table"},u.default.createElement("thead",null,u.default.createElement("tr",null,u.default.createElement("th",null,"Name"),u.default.createElement("th",null,"Time"),m&&u.default.createElement("th",null,"Actions"))),u.default.createElement("tbody",null,w.current.map((function(e,t){return u.default.createElement(Ni,{key:t,marker:e,handleSubmit:C,handleDelete:I,toggleIsEditing:O,csrfToken:S})}))))}),[w.current]);return u.default.createElement("div",{className:"ramp--markers-display","data-testid":"markers-display"},i&&u.default.createElement("div",{className:"ramp--markers-display__title","data-testid":"markers-display-title"},u.default.createElement("h4",null,s)),q,j)};Fi.propTypes={showHeading:Tr.bool,headingText:Tr.string},exports.AutoAdvanceToggle=Li,exports.IIIFPlayer=Or,exports.MarkersDisplay=Fi,exports.MediaPlayer=ai,exports.MetadataDisplay=Ai,exports.StructuredNavigation=fi,exports.SupplementalFiles=function(t){var r=t.itemHeading,n=void 0===r?"Item files":r,i=t.sectionHeading,o=void 0===i?"Section files":i,s=t.showHeading,l=void 0===s||s,c=or().renderings,d=e.useState(),f=T(d,2),p=f[0],m=f[1],v=e.useState(),h=T(v,2),g=h[0],y=h[1],b=e.useState(!1),x=T(b,2),k=x[0],w=x[1],E=e.useState(!1),S=T(E,2),C=S[0],I=S[1],R=a.useErrorBoundary().showBoundary;e.useEffect((function(){try{var e;m(null==c?void 0:c.manifest);var t=null==c?void 0:c.canvas,r=0;t&&(y(t),r=t.reduce((function(e,t){return e+t.files.length}),0),w(r>0)),r>0||(null==c||null===(e=c.manifest)||void 0===e?void 0:e.length)>0?I(!0):I(!1)}catch(e){R(e)}}),[c]);var O=function(e,t){e.preventDefault(),Rt(t.id,t.filename,t.fileExt,t.isMachineGen)},q=e.useMemo((function(){return u.default.createElement(u.default.Fragment,null,C&&u.default.createElement("div",{className:"ramp--supplemental-files-display-content","data-testid":"supplemental-files-display-content"},Array.isArray(p)&&p.length>0&&u.default.createElement(u.default.Fragment,null,u.default.createElement("h4",null,n),u.default.createElement("dl",{key:"item-files"},p.map((function(t,r){return u.default.createElement(e.Fragment,{key:r},u.default.createElement("dd",{key:"item-file-".concat(r)},u.default.createElement("a",{href:t.id,key:r,onClick:function(e){return O(e,t)}},t.label)))})))),Array.isArray(g)&&k&&u.default.createElement(u.default.Fragment,null,u.default.createElement("h4",null,o),g.map((function(e,t){var r=e.files;return r.length>0&&u.default.createElement("dl",{key:"section-".concat(t,"-label")},u.default.createElement("dt",{key:e.label},e.label),r.map((function(e,r){return u.default.createElement("dd",{key:"section-".concat(t,"-file-").concat(r)},u.default.createElement("a",{href:e.id,key:r,onClick:function(t){return O(t,e)}},e.label))})))})))),!C&&u.default.createElement("div",{"data-testid":"supplemental-files-empty",className:"ramp--supplemental-files-empty"},u.default.createElement("p",null,"No Supplemental file(s) in Manifest")))}),[C,k]);return u.default.createElement("div",{"data-testid":"supplemental-files",className:"ramp--supplemental-files"},l&&u.default.createElement("div",{className:"ramp--supplemental-files-heading","data-testid":"supplemental-files-heading"},u.default.createElement("h4",null,"Files")),q)},exports.Transcript=Pi; diff --git a/dist/ramp.css b/dist/ramp.css index 97f41c31..896462f0 100644 --- a/dist/ramp.css +++ b/dist/ramp.css @@ -4,10 +4,12 @@ justify-content: space-between; padding: 1rem; background-color: rgba(224, 16, 26, 0.2); - border-radius: 0.25rem; } + border-radius: 0.25rem; +} .ramp--error-message__message { - padding: 12px 20px; } + padding: 12px 20px; +} .ramp--error-message__reset-button { background-color: #2a5459; @@ -16,113 +18,138 @@ border: none; border-radius: 4px; cursor: pointer; - font-size: medium; } - + font-size: medium; +} @import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;700&display=swap"); -[class*='ramp--'] { +[class*=ramp--] { font-family: "Open Sans", sans-serif; - color: #333333; } + color: #333333; +} /** Begin - Overrides for VideoJS related styling **/ .vjs-marker { /* to ignore/pass-through click-, tap-, scroll- and hover events https://stackoverflow.com/questions/3680429/click-through-div-to-underlying-elements?answertab=active#tab-top */ - pointer-events: none; } - .vjs-marker:hover { - transform: scale(1, 1) !important; } + pointer-events: none; +} +.vjs-marker:hover { + transform: scale(1, 1) !important; +} .vjs-menu li { - font-size: 1em; } - + font-size: 1em; +} .vjs-menu li.vjs-selected { - background-color: #80a590; } - .vjs-menu li.vjs-selected:hover { - background-color: #bbbbbb; } + background-color: #80a590; +} +.vjs-menu li.vjs-selected:hover { + background-color: #bbbbbb; +} .video-js .vjs-control-bar { /* Increase the control-bar icons/text size */ - font-size: 120%; } + font-size: 120%; +} /* Control bar styling for mobile/tablet devices */ .vjs-mobile-visible { opacity: 1 !important; display: inline; - z-index: 50; } + z-index: 50; +} /* Make VideoJS control bar buttons smaller */ .video-js .vjs-volume-panel .vjs-volume-panel-vertical, .video-js .vjs-control { - width: 2.5rem !important; } + width: 2.5rem !important; +} .video-js .vjs-control-bar { /* Ensures text scales with zoom */ - -webkit-text-size-adjust: 100%; } + -webkit-text-size-adjust: 100%; +} /* Center the CC button in mobile devices */ .vjs-subs-caps-button > button { - padding: 0 0 3em 0; } + padding: 0 0 3em 0; +} .video-js .vjs-progress-control:hover .vjs-play-progress:after { - display: none; } + display: none; +} /* Show poster image when playback ends */ .video-js.vjs-ended .vjs-poster { - display: block; } + display: block; +} .video-js .vjs-current-time { - display: block; } + display: block; +} /* Put playhead on top of markers */ .video-js .vjs-play-progress:before { - z-index: 101; } + z-index: 101; +} /* time-control elements */ .video-js .vjs-time-control, .video-js .vjs-time-control .vjs-duration { min-width: 0.5rem; padding: 0 0.25rem; - width: auto !important; } + width: auto !important; +} .vjs-time-divider { - display: block; } + display: block; +} .vjs-duration { - display: block !important; } + display: block !important; +} .vjs-playback-rate-value { line-height: 2.25 !important; - font-size: 1.25em !important; } + font-size: 1.25em !important; +} /* big-play button */ .video-js .vjs-big-play-button { border-radius: 50%; width: 50px; - scale: 2; } + scale: 2; +} .vjs-disabled { - pointer-events: none; } + pointer-events: none; +} /* Reduce media height to prevent media overlapping player boundaries */ .video-js .vjs-tech { - height: 99.75% !important; } + height: 99.75% !important; +} /* captions button selection */ .is-mobile .captions-on { - border-bottom: 0.3rem ridge #80a590 !important; } + border-bottom: 0.3rem ridge #80a590 !important; +} .captions-on { - border-bottom: 0.35rem ridge #80a590 !important; } + border-bottom: 0.35rem ridge #80a590 !important; +} /** End - Overrides for VideoJS related styling **/ ::-webkit-scrollbar { -webkit-appearance: none; - width: 8px; } + width: 8px; +} ::-webkit-scrollbar-thumb { border-radius: 5px; background-color: rgba(0, 0, 0, 0.5); - -webkit-box-shadow: 0 0 1px rgba(255, 255, 255, 0.5); } + -webkit-box-shadow: 0 0 1px rgba(255, 255, 255, 0.5); +} /* Loading spinner */ .lds-spinner { @@ -131,11 +158,13 @@ width: 80px; height: 30rem; left: 43%; - top: 45%; } + top: 45%; +} .lds-spinner div { transform-origin: 40px 40px; - animation: lds-spinner 1.2s linear infinite; } + animation: lds-spinner 1.2s linear infinite; +} .lds-spinner div:after { content: " "; @@ -146,62 +175,77 @@ width: 6px; height: 18px; border-radius: 20%; - background: #2a5459; } + background: #2a5459; +} .lds-spinner div:nth-child(1) { transform: rotate(0deg); - animation-delay: -1.1s; } + animation-delay: -1.1s; +} .lds-spinner div:nth-child(2) { transform: rotate(30deg); - animation-delay: -1s; } + animation-delay: -1s; +} .lds-spinner div:nth-child(3) { transform: rotate(60deg); - animation-delay: -0.9s; } + animation-delay: -0.9s; +} .lds-spinner div:nth-child(4) { transform: rotate(90deg); - animation-delay: -0.8s; } + animation-delay: -0.8s; +} .lds-spinner div:nth-child(5) { transform: rotate(120deg); - animation-delay: -0.7s; } + animation-delay: -0.7s; +} .lds-spinner div:nth-child(6) { transform: rotate(150deg); - animation-delay: -0.6s; } + animation-delay: -0.6s; +} .lds-spinner div:nth-child(7) { transform: rotate(180deg); - animation-delay: -0.5s; } + animation-delay: -0.5s; +} .lds-spinner div:nth-child(8) { transform: rotate(210deg); - animation-delay: -0.4s; } + animation-delay: -0.4s; +} .lds-spinner div:nth-child(9) { transform: rotate(240deg); - animation-delay: -0.3s; } + animation-delay: -0.3s; +} .lds-spinner div:nth-child(10) { transform: rotate(270deg); - animation-delay: -0.2s; } + animation-delay: -0.2s; +} .lds-spinner div:nth-child(11) { transform: rotate(300deg); - animation-delay: -0.1s; } + animation-delay: -0.1s; +} .lds-spinner div:nth-child(12) { transform: rotate(330deg); - animation-delay: 0s; } + animation-delay: 0s; +} @keyframes lds-spinner { 0% { - opacity: 1; } + opacity: 1; + } 100% { - opacity: 0; } } - + opacity: 0; + } +} .vjs-marker { position: absolute; left: 0; @@ -266,57 +310,66 @@ @import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;700&display=swap"); .ramp--media-player_inaccessible-message-content { width: 50%; - color: #f2f2f2; } - .ramp--media-player_inaccessible-message-content a { - color: #80a590; } + color: #f2f2f2; +} +.ramp--media-player_inaccessible-message-content a { + color: #80a590; +} .ramp--media-player_inaccessible-message-buttons { display: flex; - gap: 0.5em; } - .ramp--media-player_inaccessible-message-buttons button { - border: 1px solid; - color: white; - background-color: #2a5459; - padding: 0.5em; - border-radius: 0.3em; - cursor: pointer; - font-size: medium; - display: flex; - align-items: center; - gap: 0.25em; } + gap: 0.5em; +} +.ramp--media-player_inaccessible-message-buttons button { + border: 1px solid; + color: white; + background-color: #2a5459; + padding: 0.5em; + border-radius: 0.3em; + cursor: pointer; + font-size: medium; + display: flex; + align-items: center; + gap: 0.25em; +} .ramp--media-player_inaccessible-message-timer { color: inherit; - margin: 1em 0; } + margin: 1em 0; +} .ramp--media-player_inaccessible-message-timer.hidden { - visibility: hidden; } - + visibility: hidden; +} /* Adapted from: https://github.com/videojs/themes/blob/master/city/index.css */ /* Minimum height and width for audio player */ .vjs-theme-ramp.vjs-audio-only-mode { min-width: 490px; - min-height: 40px; } + min-height: 40px; +} /* Minimum height and width for video player */ .vjs-theme-ramp { min-width: 490px; - min-height: 270px; } + min-height: 270px; +} .vjs-theme-ramp .vjs-control-bar { height: 5em; padding-top: 1em; background: none; background-image: linear-gradient(to top, #000, rgba(0, 0, 0, 0)); - display: inline-block; } + display: inline-block; +} .vjs-theme-ramp .vjs-custom-progress-bar { position: absolute; width: 100% !important; - top: -0.5em; - margin: 0; } + top: 0.5em; + margin: 0; +} .vjs-theme-ramp .vjs-progress-control .vjs-progress-holder { position: absolute; @@ -324,23 +377,28 @@ right: 0; left: 0; width: 100%; - margin: 0; } + margin: 0; +} /* Time controls */ .vjs-theme-ramp .vjs-control-bar .vjs-time-control { line-height: 4em; - float: inline-start; } + float: inline-start; +} /* Remove padding around time-divider (/) */ .vjs-theme-ramp .vjs-time-divider { - padding: 0; } + padding: 0; +} .vjs-theme-ramp .vjs-duration { - padding-left: 0; } + padding-left: 0; +} /* Play action controls */ .vjs-theme-ramp .vjs-control-bar .vjs-play-control { - float: inline-start; } + float: inline-start; +} /* Rest of the controls */ .vjs-theme-ramp .vjs-mute-control, @@ -352,40 +410,49 @@ .vjs-theme-ramp .vjs-quality-selector, .vjs-theme-ramp .vjs-file-download, .vjs-theme-ramp .vjs-subs-caps-button { - float: inline-end; } + float: inline-end; +} /* Reduce height of full-screen toggle for easy scrubbing on progress */ .vjs-theme-ramp .vjs-control-bar .vjs-fullscreen-control, .vjs-theme-ramp .vjs-control-bar .vjs-track-scrubber { margin-top: 0.5em; - height: 70%; } + height: 70%; +} /* Mute control on mobile devices */ .vjs-theme-ramp.vjs-touch-enabled .vjs-mute-control { display: block !important; - float: inline-end; } + float: inline-end; +} /* Popup-menu button controls */ .vjs-theme-ramp .vjs-menu-button > .vjs-icon-placeholder { - margin-top: 0.5em; } + margin-top: 0.5em; +} .vjs-theme-ramp .vjs-menu-button-popup .vjs-menu { - margin-bottom: 0.75em; } + margin-bottom: 0.75em; +} /* Volume stuff */ .vjs-theme-ramp .vjs-volume-panel:hover .vjs-volume-control.vjs-volume-horizontal { - height: 100%; } + height: 100%; +} .vjs-theme-ramp .vjs-mute-control { - display: none; } + display: none; +} .vjs-theme-ramp div.vjs-menu-button { height: 60%; - margin-top: 0.5em; } + margin-top: 0.5em; +} .vjs-theme-ramp .vjs-volume-panel { margin-left: 0.5em; - margin-right: 0.5em; } + margin-right: 0.5em; +} .vjs-theme-ramp .vjs-volume-panel, .vjs-theme-ramp .vjs-volume-panel:hover, @@ -395,30 +462,36 @@ .vjs-theme-ramp .vjs-volume-panel:active .vjs-volume-control.vjs-volume-horizontal, .vjs-theme-ramp .vjs-volume-panel.vjs-volume-panel-horizontal:hover, .vjs-theme-ramp .vjs-volume-bar.vjs-slider-horizontal { - width: 3em; } + width: 3em; +} .vjs-theme-ramp .vjs-volume-level::before { - font-size: 1em; } + font-size: 1em; +} .vjs-theme-ramp .vjs-volume-panel .vjs-volume-control { opacity: 1; width: 100%; - height: 100%; } + height: 100%; +} .vjs-theme-ramp .vjs-volume-bar { background-color: transparent; - margin: 0; } + margin: 0; +} .vjs-theme-ramp .vjs-slider-horizontal .vjs-volume-level { - height: 100%; } + height: 100%; +} .vjs-theme-ramp .vjs-volume-bar.vjs-slider-horizontal { margin-top: 1em; margin-bottom: 0; - height: 100%; } + height: 100%; +} .vjs-theme-ramp .vjs-volume-bar::before { - content: ''; + content: ""; z-index: 0; width: 0; height: 0; @@ -427,16 +500,19 @@ left: 0; border-style: solid; border-width: 0 0 1.75em 3em; - border-color: transparent transparent rgba(255, 255, 255, 0.25) transparent; } + border-color: transparent transparent rgba(255, 255, 255, 0.25) transparent; +} .vjs-theme-ramp .vjs-volume-level { overflow: hidden; - background-color: transparent; } - .vjs-theme-ramp .vjs-volume-level .vjs-icon-placeholder { - display: none; } + background-color: transparent; +} +.vjs-theme-ramp .vjs-volume-level .vjs-icon-placeholder { + display: none; +} .vjs-theme-ramp .vjs-volume-level::before { - content: ''; + content: ""; z-index: 1; width: 0; height: 0; @@ -445,8 +521,8 @@ left: 0; border-style: solid; border-width: 0 0 1.75em 3em; - border-color: transparent transparent #fff transparent; } - + border-color: transparent transparent #fff transparent; +} @import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;700&display=swap"); .video-js .vjs-custom-progress-bar { cursor: pointer; @@ -454,79 +530,98 @@ align-items: center; min-width: 4em; touch-action: none; - height: 0.75em; } + height: 0.75em; +} .video-js.vjs-touch-enabled .vjs-custom-progress-bar { - z-index: 1001; } - .video-js.vjs-touch-enabled .vjs-custom-progress-bar .vjs-play-progress span svg { - display: none !important; } + z-index: 1001; +} +.video-js.vjs-touch-enabled .vjs-custom-progress-bar .vjs-play-progress span svg { + display: none !important; +} .vjs-custom-progress-bar .vjs-play-progress span svg { - height: 1.25em; } - + height: 1.25em; + margin-top: 0.1em; +} .vjs-custom-progress-bar .vjs-mouse-display .vjs-time-tooltip { background-color: white; - color: black; } - + color: black; +} .vjs-custom-progress-bar .vjs-marker.ramp--track-marker--playlist { - top: 0.75em; } - + top: 0.75em; +} .vjs-custom-progress-bar:hover { height: 1em !important; transition: height 0.15s ease-in-out; - -webkit-transition: height 0.15s ease-in-out; } - .vjs-custom-progress-bar:hover .vjs-play-progress span svg { - height: 1.5em; - transition: height 0.15s ease-in-out; - -webkit-transition: height 0.15s ease-in-out; } - .vjs-custom-progress-bar:hover .block-stripes { - height: 1em !important; - transition: height 0.15s ease-in-out; - -webkit-transition: height 0.15s ease-in-out; } - .vjs-custom-progress-bar:hover .vjs-marker.ramp--track-marker--playlist { - top: 1em; - transition: top 0.15s ease-in-out; - -webkit-transition: top 0.15s ease-in-out; } - .vjs-custom-progress-bar:hover .vjs-marker.ramp--track-marker--fragment { - top: -0.205em; - transition: top 0.15s ease-in-out; - -webkit-transition: top 0.15s ease-in-out; } - .vjs-custom-progress-bar:hover .vjs-marker.ramp--track-marker--search { - top: 0.35em; - transition: top 0.15s ease-in-out; - -webkit-transition: top 0.15s ease-in-out; } - .vjs-custom-progress-bar:hover .vjs-mouse-display .vjs-time-tooltip { - visibility: visible; - display: block; } + -webkit-transition: height 0.15s ease-in-out; +} +.vjs-custom-progress-bar:hover .vjs-play-progress span svg { + height: 1.5em; + transition: height 0.15s ease-in-out; + -webkit-transition: height 0.15s ease-in-out; +} +.vjs-custom-progress-bar:hover .block-stripes { + height: 1em !important; + transition: height 0.15s ease-in-out; + -webkit-transition: height 0.15s ease-in-out; +} +.vjs-custom-progress-bar:hover .vjs-marker.ramp--track-marker--playlist { + top: 1em; + transition: top 0.15s ease-in-out; + -webkit-transition: top 0.15s ease-in-out; +} +.vjs-custom-progress-bar:hover .vjs-marker.ramp--track-marker--fragment { + top: -0.205em; + transition: top 0.15s ease-in-out; + -webkit-transition: top 0.15s ease-in-out; +} +.vjs-custom-progress-bar:hover .vjs-marker.ramp--track-marker--search { + top: 0.35em; + transition: top 0.15s ease-in-out; + -webkit-transition: top 0.15s ease-in-out; +} +.vjs-custom-progress-bar:hover .vjs-mouse-display .vjs-time-tooltip { + visibility: visible; + display: block; +} .block-stripes { color: white; height: 0.75em; - background: repeating-linear-gradient(45deg, #333333, #333333 8px, #7e7e7e 8px, #7e7e7e 16px); } + background: repeating-linear-gradient(45deg, #333333, #333333 8px, #7e7e7e 8px, #7e7e7e 16px); +} #left-block { - z-index: 1; } + z-index: 1; +} #right-block { - position: relative; } + position: relative; +} .video-js .vjs-play-progress { - background-color: #2a5459; } + background-color: #2a5459; +} .video-js .vjs-play-progress > span > svg { - z-index: 2; } + z-index: 2; +} .video-js .vjs-progress-holder.played-range { - background: linear-gradient(90deg, #2a5459 var(--range-progress), #cfd8d3 var(--range-progress)); } - + background: linear-gradient(90deg, #2a5459 var(--range-progress), #cfd8d3 var(--range-progress)); +} @import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;700&display=swap"); .vjs-file-download { background-size: 1.25rem; - background-position: 0.75rem; } - .vjs-file-download .vjs-menu-title { - background-color: #7e7e7e; } - .vjs-file-download .vjs-menu-title:hover { - background-color: #7e7e7e; } + background-position: 0.75rem; +} +.vjs-file-download .vjs-menu-title { + background-color: #7e7e7e; +} +.vjs-file-download .vjs-menu-title:hover { + background-color: #7e7e7e; +} .vjs-menu-content.file-download-menu { position: absolute; @@ -535,49 +630,59 @@ box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); z-index: 1; right: -100%; - bottom: 100%; } - .vjs-menu-content.file-download-menu .menu-header { - background-color: #7e7e7e; } + bottom: 100%; +} +.vjs-menu-content.file-download-menu .menu-header { + background-color: #7e7e7e; +} .vjs-menu-content a { color: white; padding: 0 1.5rem 0 0.5rem; - text-decoration: none; } + text-decoration: none; +} .vjs-menu-item-text span, svg { vertical-align: top; - display: inline-block; } + display: inline-block; +} .vjs-menu-content li:hover { - background-color: rgba(115, 133, 159, 0.5); } - + background-color: rgba(115, 133, 159, 0.5); +} .vjs-previous-button, .vjs-next-button { cursor: pointer; - padding: 1.125em 0; } - + padding: 1.125em 0; +} .video-js .vjs-title-bar { - pointer-events: all; } - .video-js .vjs-title-bar .vjs-title-link { - color: white; - font-size: 150%; - text-decoration: none; } - .video-js .vjs-title-bar .vjs-title-link:hover { - text-decoration: underline; } + pointer-events: all; +} +.video-js .vjs-title-bar .vjs-title-link { + color: white; + font-size: 150%; + text-decoration: none; +} +.video-js .vjs-title-bar .vjs-title-link:hover { + text-decoration: underline; +} .video-js.vjs-playing .vjs-title-bar { - display: none; } - + display: none; +} @import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;700&display=swap"); .vjs-track-scrubber { - cursor: pointer; } - .vjs-track-scrubber .vjs-icon-zoom { - height: 1.25em; - width: 1.25em; - scale: 1.15; } - .vjs-track-scrubber .vjs-icon-zoom:hover { - filter: drop-shadow(0 0 0.25em #fff); } + cursor: pointer; +} +.vjs-track-scrubber .vjs-icon-zoom { + height: 1.25em; + width: 1.25em; + scale: 1.15; +} +.vjs-track-scrubber .vjs-icon-zoom:hover { + filter: drop-shadow(0 0 0.25em #fff); +} .vjs-track-scrubber-container { background: #696667; @@ -593,121 +698,148 @@ svg { width: 99.725%; display: flex; align-items: center; - justify-content: space-between; } - @media (max-width: 680px) { - .vjs-track-scrubber-container { - width: 99.55%; } } - @media (min-width: 1081px) { - .vjs-track-scrubber-container { - width: 99.825%; } } - .vjs-track-scrubber-container.hidden { - display: none; } - .vjs-track-scrubber-container .vjs-track-scrubber { - cursor: pointer; - height: 20px; - width: 100%; - background: linear-gradient(90deg, #80a590 var(--range-scrubber), #333333 var(--range-scrubber)); } - .vjs-track-scrubber-container .vjs-time { - color: #fff; - width: 4rem; - padding: 2px 5px; - text-align: center; - -moz-box-sizing: content-box; - -webkit-box-sizing: content-box; - box-sizing: content-box; - line-height: 1.5rem; - font-size: 0.75rem; } - .vjs-track-scrubber-container .vjs-time p { - color: #fff; - font-size: 11px; - line-height: 12px; - display: block; - margin: 6px 2px 0 0px; - width: auto; } - .vjs-track-scrubber-container .tooltiptext { - visibility: hidden; - width: 5em; - background-color: #7e7e7e; - color: #fff; - text-align: center; - border-radius: 6px; - padding: 5px 5px; - bottom: 2.5em; - position: relative; - z-index: 1000; - font-size: 0.75rem; } - .vjs-track-scrubber-container:hover .tooltiptext { - visibility: visible; } - .vjs-track-scrubber-container .tooltiptext::after { - content: ''; - position: absolute; - top: 100%; - left: 50%; - margin-left: -5px; - border-width: 5px; - border-style: solid; - border-color: #7e7e7e transparent transparent transparent; } + justify-content: space-between; +} +@media (max-width: 680px) { + .vjs-track-scrubber-container { + width: 99.55%; + } +} +@media (min-width: 1081px) { + .vjs-track-scrubber-container { + width: 99.825%; + } +} +.vjs-track-scrubber-container.hidden { + display: none; +} +.vjs-track-scrubber-container .vjs-track-scrubber { + cursor: pointer; + height: 20px; + width: 100%; + background: linear-gradient(90deg, #80a590 var(--range-scrubber), #333333 var(--range-scrubber)); +} +.vjs-track-scrubber-container .vjs-time { + color: #fff; + width: 4rem; + padding: 2px 5px; + text-align: center; + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; + box-sizing: content-box; + line-height: 1.5rem; + font-size: 0.75rem; +} +.vjs-track-scrubber-container .vjs-time p { + color: #fff; + font-size: 11px; + line-height: 12px; + display: block; + margin: 6px 2px 0 0px; + width: auto; +} +.vjs-track-scrubber-container .tooltiptext { + visibility: hidden; + width: 5em; + background-color: #7e7e7e; + color: #fff; + text-align: center; + border-radius: 6px; + padding: 5px 5px; + bottom: 2.5em; + position: relative; + z-index: 1000; + font-size: 0.75rem; +} +.vjs-track-scrubber-container:hover .tooltiptext { + visibility: visible; +} +.vjs-track-scrubber-container .tooltiptext::after { + content: ""; + position: absolute; + top: 100%; + left: 50%; + margin-left: -5px; + border-width: 5px; + border-style: solid; + border-color: #7e7e7e transparent transparent transparent; +} .vjs-marker.ramp--track-marker--fragment { opacity: 0.5; height: 16px; - top: -0.315em; } - .vjs-marker.ramp--track-marker--fragment[style] { - background-color: #80A590 !important; - border-radius: 0 !important; } + top: -0.315em; +} +.vjs-marker.ramp--track-marker--fragment[style] { + background-color: #80A590 !important; + border-radius: 0 !important; +} .vjs-marker.ramp--track-marker--playlist { height: 0.5em; transform: rotate(-45deg); top: 4px; - content: ''; + content: ""; border-color: #fff; border-style: solid; - border-width: 0.25em 0.25em 0 0; } - .vjs-marker.ramp--track-marker--playlist[style] { - background-color: transparent !important; - border-radius: 0 !important; - width: 0.5em !important; } + border-width: 0.25em 0.25em 0 0; +} +.vjs-marker.ramp--track-marker--playlist[style] { + background-color: transparent !important; + border-radius: 0 !important; + width: 0.5em !important; +} .vjs-marker.ramp--track-marker--search { - top: 0px; + top: 0.25em; height: 6px; opacity: 0.75; transition: opacity 200ms ease-out, transform 200ms ease-out, box-shadow 200ms ease-out; box-shadow: 0 0 0px 1px rgba(255, 255, 255, 0.95), 0 0 8px 0px rgba(0, 0, 0, 0.75); pointer-events: all; - transform: translate(-25%, -25%) rotate(45deg) scale(1) !important; } - .vjs-marker.ramp--track-marker--search[style] { - background-color: #2a5459 !important; - border-radius: 0 !important; - width: 6px !important; } - .vjs-marker.ramp--track-marker--search:hover { - opacity: 1; - transform: translate(-25%, -25%) rotate(45deg) scale(1.75) !important; - box-shadow: 0 0 0x 2px rgba(255, 255, 255, 0.95), 0 0 8px 1px rgba(0, 0, 0, 0.75); } - + transform: translate(-25%, -25%) rotate(45deg) scale(1) !important; +} +.vjs-marker.ramp--track-marker--search[style] { + background-color: #2a5459 !important; + border-radius: 0 !important; + width: 6px !important; +} +.vjs-marker.ramp--track-marker--search:hover { + opacity: 1; + transform: translate(-25%, -25%) rotate(45deg) scale(1.75) !important; + box-shadow: 0 0 0x 2px rgba(255, 255, 255, 0.95), 0 0 8px 1px rgba(0, 0, 0, 0.75); +} @import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;700&display=swap"); -.ramp--structured-nav { +.ramp--structured-nav__content { margin-top: 0; - overflow-y: auto; } - .ramp--structured-nav a { - color: #2a5459; - transition: 0.25s; - text-decoration: none; } - .ramp--structured-nav a:hover { - color: #333333; } - .ramp--structured-nav p { - padding-top: 1em; - color: #333333; } - -.ramp--structured-nav-with_root > ul.ramp--structured-nav__list > li > ul > li { - padding: 0 0 0.5rem 0; } - -.ramp--structured-nav-with_root > ul.ramp--structured-nav__list > li > ul > li:last-child { - padding: 0 0 0 0; } - -.ramp--structured-nav.playlist-items { - padding: 1em 2em; } + overflow-y: auto; +} +.ramp--structured-nav__content a { + color: #2a5459; + transition: 0.25s; + text-decoration: none; +} +.ramp--structured-nav__content a:hover { + color: #333333; +} +.ramp--structured-nav__content p { + padding-top: 1em; + color: #333333; +} + +.ramp--structured-nav__content-with_root > ul.ramp--structured-nav__list > li > ul > li { + padding: 0 0 0.5rem 0; +} +.ramp--structured-nav__content-with_root > ul.ramp--structured-nav__list > li > ul > li:last-child { + padding: 0 0 0 0; +} + +.ramp--structured-nav__content.playlist-items { + padding: 1em 1.5em; +} +.ramp--structured-nav__content.playlist-items ul.ramp--structured-nav__list > li:last-child { + padding: 0; +} .ramp--structured-nav__border { margin-top: 20px; @@ -717,10 +849,12 @@ svg { display: flex; flex-direction: column; max-height: 40vh; - position: relative; } + position: relative; +} .ramp--structured-nav__border > span { - display: none; } + display: none; +} .ramp--structured-nav__border > span.scrollable { background: #bbbbbb; @@ -735,79 +869,128 @@ svg { border: 1px solid #ddd; border-radius: 0.25rem 0.25rem 0 0; border-bottom: none; - padding: 0.25em; } - @media (min-width: 585px) and (max-width: 768px) { - .ramp--structured-nav__border > span.scrollable { - left: 25%; } } + padding: 0.25em; +} +@media (min-width: 585px) and (max-width: 768px) { + .ramp--structured-nav__border > span.scrollable { + left: 25%; + } +} ul.ramp--structured-nav__list { list-style: none; padding: 0 0 0 0; - margin: 0px; } - ul.ramp--structured-nav__list li:last-child { - padding: 0 0 0 0; } - ul.ramp--structured-nav__list li { - display: block; - padding: 0 0 0.5rem 0px; } - ul.ramp--structured-nav__list li .structure-item-locked { - vertical-align: middle; } - ul.ramp--structured-nav__list li ul > li { - padding: 0 0 0.5rem 1rem; } - ul.ramp--structured-nav__list li ul > li:last-child { - padding: 0 0 0 1rem; } - ul.ramp--structured-nav__list li.active > a { - color: #000; } - ul.ramp--structured-nav__list li.active { - font-weight: bold !important; } - ul.ramp--structured-nav__list li.active .tracker { - width: 0; - height: 0; - border-top: 3px solid transparent; - border-left: 7px solid #333333; - border-bottom: 3px solid transparent; - display: inline-block; - margin-left: -1rem; - margin-right: 0.5rem; - margin-top: -0.5rem; } - ul.ramp--structured-nav__list .ramp--structured-nav__section.active { - font-weight: bold; } - ul.ramp--structured-nav__list .ramp--structured-nav__section { - display: flex; - align-items: center; - background-color: #f2f2f2; - border-top: 1px solid #d3d3d3; - font-size: 1.25rem; - font-weight: 400; } - ul.ramp--structured-nav__list .ramp--structured-nav__section button { - border: none; - cursor: pointer; - text-align: left; - width: 100%; - padding: 1rem; - font-size: 1.25rem; - font-weight: inherit; - background: transparent; } - ul.ramp--structured-nav__list .ramp--structured-nav__section button:hover { - background-color: #cfd8d3; } - ul.ramp--structured-nav__list .ramp--structured-nav__section button span { - padding-left: 0; } - ul.ramp--structured-nav__list .ramp--structured-nav__section span { - padding: 1rem; } - ul.ramp--structured-nav__list .ramp--structured-nav__section span.ramp--structured-nav__section-duration { - border: 1px solid #7e7e7e; - border-radius: 999px; - color: #000; - font-size: 0.75rem; - letter-spacing: 0.02rem; - line-height: 1.6; - padding: 0 0.5rem; - margin-left: 0.5rem; } - ul.ramp--structured-nav__list svg.structure-item-locked { - margin-right: 0.5rem; } + margin: 0px; + font-size: medium; +} +ul.ramp--structured-nav__list li { + display: block; + padding: 0 0 0.5rem 1em; +} +ul.ramp--structured-nav__list li .structure-item-locked { + vertical-align: middle; +} +ul.ramp--structured-nav__list li ul { + padding-left: 0.5em; +} +ul.ramp--structured-nav__list li ul > li { + padding: 0 0 0.5rem 1rem; +} +ul.ramp--structured-nav__list li ul > li:last-child { + padding: 0 0 0 1rem; +} +ul.ramp--structured-nav__list li.active > a { + color: #000; +} +ul.ramp--structured-nav__list li.active { + font-weight: bold !important; +} +ul.ramp--structured-nav__list li.active .tracker { + width: 0; + height: 0; + border-top: 3px solid transparent; + border-left: 7px solid #333333; + border-bottom: 3px solid transparent; + display: inline-block; + margin-left: -1rem; + margin-right: 0.5rem; + margin-top: -0.5rem; +} +ul.ramp--structured-nav__list svg.structure-item-locked { + margin-right: 0.5rem; +} + +.ramp--structured-nav__section.active button { + font-weight: bold; +} +.ramp--structured-nav__section { + display: flex; + flex-direction: column; + background-color: transparent; + border-top: 1px solid #d3d3d3; + font-size: 1.25rem; + font-weight: 400; +} +.ramp--structured-nav__section .section-head-buttons { + display: grid; + grid-template-columns: 1fr auto; +} +.ramp--structured-nav__section .section-head-buttons span.ramp--structured-nav__section-title { + background: #f2f2f2; +} +.ramp--structured-nav__section .not-clickable { + pointer-events: none; +} +.ramp--structured-nav__section button { + border: none; + cursor: pointer; + text-align: left; + width: 100%; + padding: 1rem; + font-size: 1.25rem; + font-weight: inherit; + background: #f2f2f2; +} +.ramp--structured-nav__section button:hover { + background-color: #cfd8d3; +} +.ramp--structured-nav__section button span { + padding-left: 0; +} +.ramp--structured-nav__section button.collapse-expand-button .arrow { + border: solid black; + border-width: 0 0.1em 0.1em 0; + display: inline-block; + padding: 0.215em; +} +.ramp--structured-nav__section button.collapse-expand-button .up { + transform: rotate(-135deg); + -webkit-transform: rotate(-135deg); + transition: transform 0.35s ease-in-out; +} +.ramp--structured-nav__section button.collapse-expand-button .down { + transform: rotate(45deg); + -webkit-transform: rotate(45deg); + transition: transform 0.35s ease-in-out; +} +.ramp--structured-nav__section button.collapse-expand-button:hover { + background-color: #f2f2f2; +} +.ramp--structured-nav__section span.ramp--structured-nav__section-duration { + border: 1px solid #7e7e7e; + border-radius: 999px; + color: #000; + font-size: 0.75rem; + letter-spacing: 0.02rem; + line-height: 1.6; + padding: 0 0.5rem; + margin-left: 0.5rem; +} @import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;700&display=swap"); .ramp--transcript_nav { - container: transcript_nav / inline-size; } + container: transcript_nav/inline-size; +} .ramp--transcript_menu { position: sticky; @@ -820,16 +1003,19 @@ ul.ramp--structured-nav__list { border-radius: 3px; row-gap: 0.5em; display: flex; - flex-direction: column; } + flex-direction: column; +} .ramp--transcript_selector { display: flex; - gap: 0.5rem; } + gap: 0.5rem; +} .ramp--transcript_menu-info { display: flex; flex-direction: row; - flex-wrap: wrap; } + flex-wrap: wrap; +} .ramp--transcript_search_input input, .ramp--transcript_selector select { @@ -837,52 +1023,66 @@ ul.ramp--structured-nav__list { width: 100%; font-family: "Open Sans", sans-serif; max-height: 2rem; - min-height: 2rem; } + min-height: 2rem; +} .ramp--transcript_selector select { - padding: 0.25rem; } + padding: 0.25rem; +} .ramp--transcript_search_input { display: flex; flex-direction: row; - gap: 0.5rem; } - .ramp--transcript_search_input input { - padding: 0.25rem 0.4rem; - flex: 1 1 auto; } + gap: 0.5rem; +} +.ramp--transcript_search_input input { + padding: 0.25rem 0.4rem; + flex: 1 1 auto; +} .ramp--transcript_search_clear { - flex: 0 1 50%; } - .ramp--transcript_search_clear[disabled], .ramp--transcript_search_clear:disabled { - display: none; } - .ramp--transcript_search_clear span::after { - content: 'Clear'; } + flex: 0 1 50%; +} +.ramp--transcript_search_clear[disabled], .ramp--transcript_search_clear:disabled { + display: none; +} +.ramp--transcript_search_clear span::after { + content: "Clear"; +} .ramp--transcript_search_icon { - flex: 0 1 50%; } - .ramp--transcript_search_icon[disabled], .ramp--transcript_search_icon:disabled { - display: none; } + flex: 0 1 50%; +} +.ramp--transcript_search_icon[disabled], .ramp--transcript_search_icon:disabled { + display: none; +} .ramp--transcript_machine_generated { margin: 0; - line-height: 1.25em; } + line-height: 1.25em; +} .ramp--transcript_auto_scroll_check { display: flex; align-items: center; justify-content: flex-end; - line-height: 1.25em; } - .ramp--transcript_auto_scroll_check label { - margin-left: 0.25em; - line-height: 1.25em; } - .ramp--transcript_auto_scroll_check label { - margin-left: 0.25em; - line-height: 1.25em; } - .ramp--transcript_auto_scroll_check:has(input:disabled), - .ramp--transcript_auto_scroll_check:has(input:disabled) input { - cursor: not-allowed; } - .ramp--transcript_auto_scroll_check:has(input:disabled) label { - cursor: not-allowed; - color: #bbbbbb; } + line-height: 1.25em; +} +.ramp--transcript_auto_scroll_check label { + margin-left: 0.25em; + line-height: 1.25em; +} +.ramp--transcript_auto_scroll_check label { + margin-left: 0.25em; + line-height: 1.25em; +} +.ramp--transcript_auto_scroll_check:has(input:disabled), .ramp--transcript_auto_scroll_check:has(input:disabled) input { + cursor: not-allowed; +} +.ramp--transcript_auto_scroll_check:has(input:disabled) label { + cursor: not-allowed; + color: #bbbbbb; +} .ramp--transcript_menu_button { margin: 0; @@ -895,201 +1095,247 @@ ul.ramp--structured-nav__list { border: 1px solid #2a5459; background-color: #4d7b7b; cursor: pointer; - transition: background-color 0.2s ease-in; } - .ramp--transcript_menu_button span { - padding: 0.5rem 0.25rem; } - .ramp--transcript_menu_button:hover:not(:disabled):not([disabled]) { - background-color: #2a5459; - border: 1px solid #1a3a3f; } - .ramp--transcript_menu_button:active:not(:disabled):not([disabled]) { - background-color: #1a3a3f; } - .ramp--transcript_menu_button[disabled], .ramp--transcript_menu_button:disabled { - cursor: default; - opacity: 0.6; } + transition: background-color 0.2s ease-in; +} +.ramp--transcript_menu_button span { + padding: 0.5rem 0.25rem; +} +.ramp--transcript_menu_button:hover:not(:disabled):not([disabled]) { + background-color: #2a5459; + border: 1px solid #1a3a3f; +} +.ramp--transcript_menu_button:active:not(:disabled):not([disabled]) { + background-color: #1a3a3f; +} +.ramp--transcript_menu_button[disabled], .ramp--transcript_menu_button:disabled { + cursor: default; + opacity: 0.6; +} .ramp--transcript_search_prev, .ramp--transcript_search_next { - flex: none; } + flex: none; +} .ramp--transcript_search_count { white-space: nowrap; flex: 0 0 auto; - text-align: center; } + text-align: center; +} .ramp--transcript_search_navigator { display: flex; flex-direction: row; align-items: center; justify-content: space-between; - gap: 0.5em; } + gap: 0.5em; +} .ramp--transcript_downloader { margin: 0; - flex: 1 1 auto; } + flex: 1 1 auto; +} @container transcript_nav (max-width: 500px) { .ramp--transcript_menu { - grid-template-columns: 1fr minmax(max-content, 1fr); } } - + grid-template-columns: 1fr minmax(max-content, 1fr); + } +} @container transcript_nav (max-width: 480px) { .ramp--transcript_menu { - grid-template-columns: 1fr minmax(min-content, 1fr); } + grid-template-columns: 1fr minmax(min-content, 1fr); + } .ramp--transcript_search_input, .ramp--transcript_search_navigator { - grid-column: 1 / 3; } + grid-column: 1/3; + } .ramp--transcript_machine_generated, .ramp--transcript_auto_scroll_check { - grid-column: 1 / 3; } + grid-column: 1/3; + } .ramp--transcript_auto_scroll_check { - justify-content: flex-start; } } - + justify-content: flex-start; + } +} @container transcript_nav (max-width: 420px) { .ramp--transcript_selector, .ramp--transcript_downloader { - grid-column: 1 / 3; } } - + grid-column: 1/3; + } +} @import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;700&display=swap"); .ramp--transcript_nav { max-height: 30em; - padding: 10px; } - .ramp--transcript_nav div.transcript_content { - height: 19em; - overflow-y: auto; } - .ramp--transcript_nav div.transcript_content p { - font-size: small; - color: #333333; } - .ramp--transcript_nav div.transcript_content #no-transcript { - font-size: medium; - padding: 2em; } - .ramp--transcript_nav div.transcript_content.static { - overflow-y: unset; } - .ramp--transcript_nav iframe.transcript_viewer { - width: 100%; - width: -moz-available; - /* WebKit-based browsers will ignore this. */ - width: -webkit-fill-available; - /* Mozilla-based browsers will ignore this. */ - width: fill-available; - height: 25em; - /* Parent div's height - 5*/ } + padding: 10px; +} +.ramp--transcript_nav div.transcript_content { + height: 19em; + overflow-y: auto; +} +.ramp--transcript_nav div.transcript_content p { + font-size: small; + color: #333333; +} +.ramp--transcript_nav div.transcript_content #no-transcript { + font-size: medium; + padding: 2em; +} +.ramp--transcript_nav div.transcript_content.static { + overflow-y: unset; +} +.ramp--transcript_nav iframe.transcript_viewer { + width: 100%; + width: -moz-available; + /* WebKit-based browsers will ignore this. */ + width: -webkit-fill-available; + /* Mozilla-based browsers will ignore this. */ + width: fill-available; + height: 25em; + /* Parent div's height - 5*/ +} p.ramp--transcript_untimed_item { - margin: 0; } + margin: 0; +} a.ramp--transcript_item { display: flex; margin: 10px 10px 10px 10px; cursor: pointer; text-decoration: none; - transition: background-color 0.2s ease-in; } - a.ramp--transcript_item.active { - background-color: #d3d3d3; } - a.ramp--transcript_item:hover, a.ramp--transcript_item:focus { - background-color: #cfd8d3; } - a.ramp--transcript_item.disabled { - cursor: default; } - a.ramp--transcript_item.focused, a.ramp--transcript_item.focused:hover, a.ramp--transcript_item.focused:focus { - background-color: #d0dcdc; } - a.ramp--transcript_item.focused .ramp--transcript_highlight.current-hit { - border: 1px solid; - text-decoration: none; } - a.ramp--transcript_item .ramp--transcript_time { - margin-right: 15px; - color: #2a5459; } - a.ramp--transcript_item .ramp--transcript_text { - color: black; } - a.ramp--transcript_item > span::after { - display: block; - content: attr(title); - font-weight: bold; - height: 1px; - color: transparent; - overflow: hidden; - visibility: hidden; } + transition: background-color 0.2s ease-in; +} +a.ramp--transcript_item.active { + background-color: #d3d3d3; +} +a.ramp--transcript_item:hover, a.ramp--transcript_item:focus { + background-color: #cfd8d3; +} +a.ramp--transcript_item.disabled { + cursor: default; +} +a.ramp--transcript_item.focused, a.ramp--transcript_item.focused:hover, a.ramp--transcript_item.focused:focus { + background-color: #d0dcdc; +} +a.ramp--transcript_item.focused .ramp--transcript_highlight.current-hit { + border: 1px solid; + text-decoration: none; +} +a.ramp--transcript_item .ramp--transcript_time { + margin-right: 15px; + color: #2a5459; +} +a.ramp--transcript_item .ramp--transcript_text { + color: black; +} +a.ramp--transcript_item > span::after { + display: block; + content: attr(title); + font-weight: bold; + height: 1px; + color: transparent; + overflow: hidden; + visibility: hidden; +} .ramp--transcript_highlight { font-weight: bold; color: #3b5e5e; text-decoration: underline; - text-underline-offset: 3px; } - + text-underline-offset: 3px; +} @import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;700&display=swap"); .ramp--metadata-display { - min-width: inherit; } - .ramp--metadata-display p { - padding-left: 1rem; } - .ramp--metadata-display .ramp--metadata-display-title { - border: 0.05rem solid #d3d3d3; - border-radius: 0.25rem 0.25rem 0 0; - margin-bottom: 1rem; - background: #f2f2f2; } - .ramp--metadata-display .ramp--metadata-display-title h4 { - font-weight: normal; - padding: 0.5rem 1.5rem; - margin: 0; - color: #333333; } - .ramp--metadata-display .ramp--metadata-display-content { - padding: 0 1.5rem 1.5rem; - color: #333333; - max-height: 30rem; - overflow-y: auto; } - .ramp--metadata-display .ramp--metadata-display-content > span { - font-weight: bold; - font-style: italic; - padding: 0.5rem 0 0.5rem 1.5rem; - margin: 00 0 0.75rem; - color: #333333; - border-bottom: 0.1rem solid #7e7e7e; - display: block; - margin: 0 -1.5rem 0.5rem -1.5rem; } - .ramp--metadata-display .ramp--metadata-display-content .ramp--metadata-rights-heading { - border-bottom: 0.1rem solid #bbbbbb; - margin: 0; - padding: 0.5rem 0; } - .ramp--metadata-display .ramp--metadata-display-content dt { - font-weight: bold; } - .ramp--metadata-display .ramp--metadata-display-content dd { - padding-bottom: 1rem; - word-break: break-word; } - .ramp--metadata-display .ramp--metadata-display-content a { - color: #2a5459; } - + min-width: inherit; +} +.ramp--metadata-display p { + padding-left: 1rem; +} +.ramp--metadata-display .ramp--metadata-display-title { + border: 0.05rem solid #d3d3d3; + border-radius: 0.25rem 0.25rem 0 0; + margin-bottom: 1rem; + background: #f2f2f2; +} +.ramp--metadata-display .ramp--metadata-display-title h4 { + font-weight: normal; + padding: 0.5rem 1.5rem; + margin: 0; + color: #333333; +} +.ramp--metadata-display .ramp--metadata-display-content { + padding: 0 1.5rem 1.5rem; + color: #333333; + max-height: 30rem; + overflow-y: auto; +} +.ramp--metadata-display .ramp--metadata-display-content > span { + font-weight: bold; + font-style: italic; + padding: 0.5rem 0 0.5rem 1.5rem; + margin: 0 0 0.75rem; + color: #333333; + border-bottom: 0.1rem solid #7e7e7e; + display: block; + margin: 0 -1.5rem 0.5rem -1.5rem; +} +.ramp--metadata-display .ramp--metadata-display-content .ramp--metadata-rights-heading { + border-bottom: 0.1rem solid #bbbbbb; + margin: 0; + padding: 0.5rem 0; +} +.ramp--metadata-display .ramp--metadata-display-content dt { + font-weight: bold; +} +.ramp--metadata-display .ramp--metadata-display-content dd { + padding-bottom: 1rem; + word-break: break-word; +} +.ramp--metadata-display .ramp--metadata-display-content a { + color: #2a5459; +} @import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;700&display=swap"); .ramp--supplemental-files dd { - padding-bottom: 1rem; } - .ramp--supplemental-files dd a { - color: #2a5459; } - + padding-bottom: 1rem; +} +.ramp--supplemental-files dd a { + color: #2a5459; +} .ramp--supplemental-files .ramp--supplemental-files-heading { border: 0.05rem solid #d3d3d3; border-radius: 0.25rem 0.25rem 0 0; margin-bottom: 1rem; - background: #f2f2f2; } - .ramp--supplemental-files .ramp--supplemental-files-heading h4 { - font-weight: normal; - padding: 0.5rem 1.5rem; - margin: 0; - color: #333333; } - + background: #f2f2f2; +} +.ramp--supplemental-files .ramp--supplemental-files-heading h4 { + font-weight: normal; + padding: 0.5rem 1.5rem; + margin: 0; + color: #333333; +} .ramp--supplemental-files .ramp--supplemental-files-display-content { padding: 0 0 1.5rem 1.5rem; color: #333333; - max-height: 30rem; } - .ramp--supplemental-files .ramp--supplemental-files-display-content dt { - font-weight: bold; - padding-left: 1.5rem; } - .ramp--supplemental-files .ramp--supplemental-files-display-content dt ~ dd { - padding-left: 1.5rem; } - .ramp--supplemental-files .ramp--supplemental-files-display-content dd { - padding-bottom: 0; - margin-left: 1.5rem; } - .ramp--supplemental-files .ramp--supplemental-files-display-content a { - color: #2a5459; } - + max-height: 30rem; +} +.ramp--supplemental-files .ramp--supplemental-files-display-content dt { + font-weight: bold; + padding-left: 1.5rem; +} +.ramp--supplemental-files .ramp--supplemental-files-display-content dt ~ dd { + padding-left: 1.5rem; +} +.ramp--supplemental-files .ramp--supplemental-files-display-content dd { + padding-bottom: 0; + margin-left: 1.5rem; +} +.ramp--supplemental-files .ramp--supplemental-files-display-content a { + color: #2a5459; +} .ramp--supplemental-files .ramp--supplemental-files-empty { font-size: medium; - padding: 2em; } - + padding: 2em; +} @import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;700&display=swap"); .ramp--auto-advance { display: flex; @@ -1100,133 +1346,160 @@ a.ramp--transcript_item { width: fit-content; padding: 0.5rem 1.5rem; max-height: 2rem; - /* Reference: https://www.w3schools.com/howto/howto_css_switch.asp */ } - .ramp--auto-advance .ramp--auto-advance-label { - margin-bottom: 1rem; - font-weight: normal; - margin: 0; - color: #333333; - padding: 0.25rem; } - .ramp--auto-advance .ramp--auto-advance-toggle { - position: relative; - width: 60px; - height: 34px; - margin-left: 1em; } - .ramp--auto-advance .ramp--auto-advance-toggle input { - opacity: 0; - width: 0; - height: 0; } - .ramp--auto-advance .ramp--auto-advance-toggle .slider { - position: absolute; - cursor: pointer; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: #ccc; - -webkit-transition: .4s; - transition: .4s; - height: inherit; - width: inherit; } - .ramp--auto-advance .ramp--auto-advance-toggle .slider:before { - position: absolute; - content: ""; - height: 26px; - width: 26px; - left: 4px; - bottom: 4px; - background-color: white; - -webkit-transition: .4s; - transition: .4s; } - .ramp--auto-advance .ramp--auto-advance-toggle input:checked + .slider { - background-color: #2a5459; } - .ramp--auto-advance .ramp--auto-advance-toggle input:focus + .slider { - box-shadow: 0 0 1px #2a5459; } - .ramp--auto-advance .ramp--auto-advance-toggle input:checked + .slider:before { - -webkit-transform: translateX(26px); - -ms-transform: translateX(26px); - transform: translateX(26px); } - .ramp--auto-advance .ramp--auto-advance-toggle .slider.round { - border-radius: 34px; } - .ramp--auto-advance .ramp--auto-advance-toggle .slider.round:before { - border-radius: 50%; } - + /* Reference: https://www.w3schools.com/howto/howto_css_switch.asp */ +} +.ramp--auto-advance .ramp--auto-advance-label { + margin-bottom: 1rem; + font-weight: normal; + margin: 0; + color: #333333; + padding: 0.25rem; +} +.ramp--auto-advance .ramp--auto-advance-toggle { + position: relative; + width: 60px; + height: 34px; + margin-left: 1em; +} +.ramp--auto-advance .ramp--auto-advance-toggle input { + opacity: 0; + width: 0; + height: 0; +} +.ramp--auto-advance .ramp--auto-advance-toggle .slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: 0.4s; + transition: 0.4s; + height: inherit; + width: inherit; +} +.ramp--auto-advance .ramp--auto-advance-toggle .slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: 0.4s; + transition: 0.4s; +} +.ramp--auto-advance .ramp--auto-advance-toggle input:checked + .slider { + background-color: #2a5459; +} +.ramp--auto-advance .ramp--auto-advance-toggle input:focus + .slider { + box-shadow: 0 0 1px #2a5459; +} +.ramp--auto-advance .ramp--auto-advance-toggle input:checked + .slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} +.ramp--auto-advance .ramp--auto-advance-toggle .slider.round { + border-radius: 34px; +} +.ramp--auto-advance .ramp--auto-advance-toggle .slider.round:before { + border-radius: 50%; +} @import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;700&display=swap"); .ramp--markers-display { min-width: inherit; - padding: 1rem; } - .ramp--markers-display .ramp--markers-display__title { - border: 0.05rem solid #d3d3d3; - border-radius: 0.25rem 0.25rem 0 0; - margin-bottom: 1rem; - background: #f2f2f2; } - .ramp--markers-display .ramp--markers-display__title h4 { - font-weight: normal; - padding: 0.5rem 1.5rem; - margin: 0; - color: #333333; } - .ramp--markers-display table { - font-family: arial, sans-serif; - width: 100%; - border-collapse: collapse; } - .ramp--markers-display table *:disabled { - cursor: not-allowed; - opacity: 0.8; } - .ramp--markers-display table td:nth-child(3) { - width: 40%; } - .ramp--markers-display table th { - border: 1px solid #f2f2f2; - padding: 0.5rem; } - .ramp--markers-display table td { - border: 1px solid #f2f2f2; - text-align: left; - padding: 0.5rem; - font-weight: normal; } - .ramp--markers-display table input.ramp--markers-display__edit-marker { - width: 100%; - padding: 0.5rem 0.25rem; - display: inline-block; - border: 1px solid #ccc; - border-radius: 0.2rem; - box-sizing: border-box; - font-size: inherit; } - .ramp--markers-display .marker-actions { - display: flex; - justify-content: flex-end; } - .ramp--markers-display .marker-actions p { - margin: 0; - margin-top: 0.25rem; } - .ramp--markers-display .time-invalid { - outline: none; - border-color: #e0101a; - box-shadow: 0 0 10px #e0101a; } - .ramp--markers-display .ramp--markers-display__edit-button { - background-color: #2a5459; - color: white; - padding: 5px 10px; - border: none; - border-radius: 4px; - cursor: pointer; - margin-left: 0.5rem; } - .ramp--markers-display .ramp--markers-display__edit-button-danger { - background-color: #e0101a; - color: white; - padding: 5px 10px; - border: none; - border-radius: 4px; - cursor: pointer; - margin-left: 0.5rem; } - .ramp--markers-display .ramp--markers-display__error-message { - color: #e0101a; - font-size: small; - margin: auto; } + padding: 1rem; +} +.ramp--markers-display .ramp--markers-display__title { + border: 0.05rem solid #d3d3d3; + border-radius: 0.25rem 0.25rem 0 0; + margin-bottom: 1rem; + background: #f2f2f2; +} +.ramp--markers-display .ramp--markers-display__title h4 { + font-weight: normal; + padding: 0.5rem 1.5rem; + margin: 0; + color: #333333; +} +.ramp--markers-display table { + font-family: arial, sans-serif; + width: 100%; + border-collapse: collapse; +} +.ramp--markers-display table *:disabled { + cursor: not-allowed; + opacity: 0.8; +} +.ramp--markers-display table td:nth-child(3) { + width: 40%; +} +.ramp--markers-display table th { + border: 1px solid #f2f2f2; + padding: 0.5rem; +} +.ramp--markers-display table td { + border: 1px solid #f2f2f2; + text-align: left; + padding: 0.5rem; + font-weight: normal; +} +.ramp--markers-display table input.ramp--markers-display__edit-marker { + width: 100%; + padding: 0.5rem 0.25rem; + display: inline-block; + border: 1px solid #ccc; + border-radius: 0.2rem; + box-sizing: border-box; + font-size: inherit; +} +.ramp--markers-display .marker-actions { + display: flex; + justify-content: flex-end; +} +.ramp--markers-display .marker-actions p { + margin: 0; + margin-top: 0.25rem; +} +.ramp--markers-display .time-invalid { + outline: none; + border-color: #e0101a; + box-shadow: 0 0 10px #e0101a; +} +.ramp--markers-display .ramp--markers-display__edit-button { + background-color: #2a5459; + color: white; + padding: 5px 10px; + border: none; + border-radius: 4px; + cursor: pointer; + margin-left: 0.5rem; +} +.ramp--markers-display .ramp--markers-display__edit-button-danger { + background-color: #e0101a; + color: white; + padding: 5px 10px; + border: none; + border-radius: 4px; + cursor: pointer; + margin-left: 0.5rem; +} +.ramp--markers-display .ramp--markers-display__error-message { + color: #e0101a; + font-size: small; + margin: auto; +} .ramp--markers-display__markers-empty { font-size: medium; - padding: 2em; } + padding: 2em; +} .ramp-markers-display__new-marker { - margin-bottom: 1rem; } + margin-bottom: 1rem; +} .ramp--markers-display__new-marker-form { border: 1px solid #d3d3d3; @@ -1234,16 +1507,19 @@ a.ramp--transcript_item { border-radius: 0.25rem; margin: 1rem 0; font-size: 0.85rem; - font-weight: bold; } - .ramp--markers-display__new-marker-form table.create-marker-form-table { - border: none; } - .ramp--markers-display__new-marker-form input.ramp--markers-display__create-marker { - width: 80%; - vertical-align: middle; - padding: 0.5rem 0.25rem; - background-color: #fff; - border: 1px solid #ccc; - border-radius: 0.2rem; - box-sizing: border-box; - margin-left: 0.5rem; - font-size: inherit; } + font-weight: bold; +} +.ramp--markers-display__new-marker-form table.create-marker-form-table { + border: none; +} +.ramp--markers-display__new-marker-form input.ramp--markers-display__create-marker { + width: 80%; + vertical-align: middle; + padding: 0.5rem 0.25rem; + background-color: #fff; + border: 1px solid #ccc; + border-radius: 0.2rem; + box-sizing: border-box; + margin-left: 0.5rem; + font-size: inherit; +} \ No newline at end of file diff --git a/dist/ramp.esm.js b/dist/ramp.esm.js index fc704e74..a8573121 100644 --- a/dist/ramp.esm.js +++ b/dist/ramp.esm.js @@ -1,10 +1,10 @@ -import React, { useState, useEffect, useRef, useMemo, useContext, useCallback, Fragment } from 'react'; +import React, { useReducer, useContext, createContext, useState, useEffect, useMemo, useCallback, useRef, createRef, Fragment, memo } from 'react'; import { PropertyValue, parseManifest, Annotation } from 'manifesto.js'; import mimeDb from 'mime-db'; import sanitizeHtml from 'sanitize-html'; import { useErrorBoundary, ErrorBoundary } from 'react-error-boundary'; -import videojs from 'video.js'; import cx from 'classnames'; +import videojs from 'video.js'; import mammoth from 'mammoth'; var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; @@ -1272,8 +1272,8 @@ function isEmpty(value) { var isEmpty_1 = isEmpty; -function ownKeys$8(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } -function _objectSpread$8(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$8(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$8(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } +function ownKeys$9(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } +function _objectSpread$9(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$9(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$9(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } var S_ANNOTATION_TYPE = { transcript: 1, caption: 2, @@ -1678,7 +1678,7 @@ function parseResourceAnnotations(annotation, duration, motivation) { * there is a start defined at the manifest level */ if (!isPlaylist) { - target = _objectSpread$8(_objectSpread$8({}, target), {}, { + target = _objectSpread$9(_objectSpread$9({}, target), {}, { customStart: target.start, start: 0, altStart: 0 @@ -2019,8 +2019,8 @@ var groupBy = function groupBy(arry, key) { function _createForOfIteratorHelper$3(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$3(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray$3(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$3(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$3(o, minLen); } function _arrayLikeToArray$3(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } -function ownKeys$7(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } -function _objectSpread$7(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$7(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$7(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } +function ownKeys$8(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } +function _objectSpread$8(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$8(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$8(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } // HTML tags and attributes allowed in IIIF var HTML_SANITIZE_CONFIG = { @@ -2147,7 +2147,7 @@ function getMediaInfo(_ref) { // return empty object when canvasIndex is undefined if (canvasIndex === undefined || canvasIndex < 0) { - return _objectSpread$7(_objectSpread$7({}, info), {}, { + return _objectSpread$8(_objectSpread$8({}, info), {}, { error: 'Error fetching content' }); } @@ -2155,7 +2155,7 @@ function getMediaInfo(_ref) { // return an error when the given Manifest doesn't have any Canvas(es) var canvases = manifest.items; if ((canvases === null || canvases === void 0 ? void 0 : canvases.length) == 0) { - return _objectSpread$7(_objectSpread$7({}, info), {}, { + return _objectSpread$8(_objectSpread$8({}, info), {}, { poster: GENERIC_EMPTY_MANIFEST_MESSAGE }); } @@ -2193,14 +2193,14 @@ function getMediaInfo(_ref) { poster: poster }; if (mediaInfo.error) { - return _objectSpread$7({}, mediaInfo); + return _objectSpread$8({}, mediaInfo); } else { // Get media type var allTypes = mediaInfo.sources.map(function (q) { return q.kind; }); var mediaType = setMediaType(allTypes); - return _objectSpread$7(_objectSpread$7({}, mediaInfo), {}, { + return _objectSpread$8(_objectSpread$8({}, mediaInfo), {}, { error: null, mediaType: mediaType }); @@ -2529,7 +2529,7 @@ function parseMetadata(metadata, resourceType) { var _getLabelValue; // get value and replace \n characters with
    to display new lines in UI var value = (_getLabelValue = getLabelValue(md.value, true)) === null || _getLabelValue === void 0 ? void 0 : _getLabelValue.replace(/\n/g, "
    "); - var sanitizedValue = sanitizeHtml(value, _objectSpread$7({}, HTML_SANITIZE_CONFIG)); + var sanitizedValue = sanitizeHtml(value, _objectSpread$8({}, HTML_SANITIZE_CONFIG)); parsedMetadata.push({ label: getLabelValue(md.label), value: sanitizedValue @@ -2882,10 +2882,10 @@ function parseMarkerAnnotation(a) { } } -function ownKeys$6(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } -function _objectSpread$6(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$6(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$6(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } -var ManifestStateContext = /*#__PURE__*/React.createContext(); -var ManifestDispatchContext = /*#__PURE__*/React.createContext(); +function ownKeys$7(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } +function _objectSpread$7(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$7(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$7(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } +var ManifestStateContext = /*#__PURE__*/createContext(); +var ManifestDispatchContext = /*#__PURE__*/createContext(); /** * Definition of all state variables in this Context @@ -2943,11 +2943,11 @@ function manifestReducer() { var isPlaylist = getIsPlaylist(manifest.label); var annotationService = getAnnotationService(manifest.service); var playlistMarkers = parsePlaylistAnnotations(manifest); - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { manifest: manifest, allCanvases: canvases, autoAdvance: manifestBehavior, - playlist: _objectSpread$6(_objectSpread$6({}, state.playlist), {}, { + playlist: _objectSpread$7(_objectSpread$7({}, state.playlist), {}, { isPlaylist: isPlaylist, annotationServiceId: annotationService, hasAnnotationService: annotationService ? true : false, @@ -2957,56 +2957,56 @@ function manifestReducer() { } case 'switchCanvas': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { canvasIndex: action.canvasIndex, hasStructure: getHasStructure(state.canvasSegments, action.canvasIndex) }); } case 'switchItem': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { currentNavItem: action.item }); } case 'canvasDuration': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { canvasDuration: action.canvasDuration }); } case 'canvasLink': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { canvasLink: action.canvasLink }); } case 'canvasTargets': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { targets: action.canvasTargets }); } case 'hasMultipleItems': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { hasMultiItems: action.isMultiSource }); } case 'setSrcIndex': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { srcIndex: action.srcIndex }); } case 'setItemStartTime': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { startTime: action.startTime }); } case 'setAutoAdvance': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { autoAdvance: action.autoAdvance }); } @@ -3014,16 +3014,16 @@ function manifestReducer() { { // Set a new set of markers for the canvases in the Manifest if (action.markers) { - return _objectSpread$6(_objectSpread$6({}, state), {}, { - playlist: _objectSpread$6(_objectSpread$6({}, state.playlist), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { + playlist: _objectSpread$7(_objectSpread$7({}, state.playlist), {}, { markers: action.markers }) }); } // Update the existing markers for the current canvas on CRUD ops if (action.updatedMarkers) { - return _objectSpread$6(_objectSpread$6({}, state), {}, { - playlist: _objectSpread$6(_objectSpread$6({}, state.playlist), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { + playlist: _objectSpread$7(_objectSpread$7({}, state.playlist), {}, { markers: state.playlist.markers.map(function (m) { if (m.canvasIndex === state.canvasIndex) { m.canvasMarkers = action.updatedMarkers; @@ -3036,21 +3036,21 @@ function manifestReducer() { } case 'setIsEditing': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { - playlist: _objectSpread$6(_objectSpread$6({}, state.playlist), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { + playlist: _objectSpread$7(_objectSpread$7({}, state.playlist), {}, { isEditing: action.isEditing }) }); } case 'setCanvasIsEmpty': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { canvasIsEmpty: action.isEmpty }); } case 'setStructures': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { structures: action.structures }); } @@ -3060,7 +3060,7 @@ function manifestReducer() { var canvasStructures = action.timespans.filter(function (c) { return c.canvasIndex == state.canvasIndex + 1 && !c.isCanvas; }); - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { canvasSegments: action.timespans, hasStructure: canvasStructures.length > 0 }); @@ -3070,7 +3070,7 @@ function manifestReducer() { var _action$customStart = action.customStart, canvas = _action$customStart.canvas, time = _action$customStart.time; - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { customStart: { startIndex: canvas, startTime: time @@ -3081,8 +3081,8 @@ function manifestReducer() { } case 'setRenderingFiles': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { - renderings: _objectSpread$6({}, action.renderings) + return _objectSpread$7(_objectSpread$7({}, state), {}, { + renderings: _objectSpread$7({}, action.renderings) }); } default: @@ -3095,10 +3095,10 @@ function ManifestProvider(_ref) { var _ref$initialState = _ref.initialState, initialState = _ref$initialState === void 0 ? defaultState$1 : _ref$initialState, children = _ref.children; - var _React$useReducer = React.useReducer(manifestReducer, initialState), - _React$useReducer2 = _slicedToArray(_React$useReducer, 2), - state = _React$useReducer2[0], - dispatch = _React$useReducer2[1]; + var _useReducer = useReducer(manifestReducer, initialState), + _useReducer2 = _slicedToArray(_useReducer, 2), + state = _useReducer2[0], + dispatch = _useReducer2[1]; return /*#__PURE__*/React.createElement(ManifestStateContext.Provider, { value: state }, /*#__PURE__*/React.createElement(ManifestDispatchContext.Provider, { @@ -3106,24 +3106,24 @@ function ManifestProvider(_ref) { }, children)); } function useManifestState() { - var context = React.useContext(ManifestStateContext); + var context = useContext(ManifestStateContext); if (context === undefined) { throw new Error('useManifestState must be used within a ManifestProvider'); } return context; } function useManifestDispatch() { - var context = React.useContext(ManifestDispatchContext); + var context = useContext(ManifestDispatchContext); if (context === undefined) { throw new Error('useManifestDispatch must be used within a ManifestProvider'); } return context; } -function ownKeys$5(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } -function _objectSpread$5(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$5(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$5(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } -var PlayerStateContext = /*#__PURE__*/React.createContext(); -var PlayerDispatchContext = /*#__PURE__*/React.createContext(); +function ownKeys$6(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } +function _objectSpread$6(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$6(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$6(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } +var PlayerStateContext = /*#__PURE__*/createContext(); +var PlayerDispatchContext = /*#__PURE__*/createContext(); /** * Definition of all state variables in this Context @@ -3146,63 +3146,63 @@ function PlayerReducer() { switch (action.type) { case 'updatePlayer': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { player: action.player }); } case 'navClick': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { clickedUrl: action.clickedUrl, isClicked: true }); } case 'resetClick': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { isClicked: false }); } case 'setTimeFragment': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { startTime: action.startTime, endTime: action.endTime }); } case 'setSearchMarkers': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { searchMarkers: action.payload }); } case 'setPlayingStatus': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { isPlaying: action.isPlaying }); } case 'setCaptionStatus': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { captionOn: action.captionOn }); } case 'setIsEnded': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { isEnded: action.isEnded }); } case 'setCurrentTime': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { currentTime: action.currentTime }); } case 'setPlayerFocusElement': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { playerFocusElement: action.element ? action.element : '' }); } @@ -3216,10 +3216,10 @@ function PlayerProvider(_ref) { var _ref$initialState = _ref.initialState, initialState = _ref$initialState === void 0 ? defaultState : _ref$initialState, children = _ref.children; - var _React$useReducer = React.useReducer(PlayerReducer, initialState), - _React$useReducer2 = _slicedToArray(_React$useReducer, 2), - state = _React$useReducer2[0], - dispatch = _React$useReducer2[1]; + var _useReducer = useReducer(PlayerReducer, initialState), + _useReducer2 = _slicedToArray(_useReducer, 2), + state = _useReducer2[0], + dispatch = _useReducer2[1]; return /*#__PURE__*/React.createElement(PlayerStateContext.Provider, { value: state }, /*#__PURE__*/React.createElement(PlayerDispatchContext.Provider, { @@ -3227,14 +3227,14 @@ function PlayerProvider(_ref) { }, children)); } function usePlayerState() { - var context = React.useContext(PlayerStateContext); + var context = useContext(PlayerStateContext); if (context === undefined) { throw new Error("usePlayerState must be used within the PlayerProvider"); } return context; } function usePlayerDispatch() { - var context = React.useContext(PlayerDispatchContext); + var context = useContext(PlayerDispatchContext); if (context === undefined) { throw new Error("usePlayerDispatch must be used within the PlayerProvider"); } @@ -3698,10 +3698,10 @@ function IIIFPlayerWrapper(_ref) { startCanvasTime = _ref.startCanvasTime, children = _ref.children, manifestValue = _ref.manifest; - var _React$useState = React.useState(manifestValue), - _React$useState2 = _slicedToArray(_React$useState, 2), - manifest = _React$useState2[0], - setManifest = _React$useState2[1]; + var _useState = useState(manifestValue), + _useState2 = _slicedToArray(_useState, 2), + manifest = _useState2[0], + setManifest = _useState2[1]; var manifestDispatch = useManifestDispatch(); var playerDispatch = usePlayerDispatch(); var _useErrorBoundary = useErrorBoundary(), @@ -3717,7 +3717,7 @@ function IIIFPlayerWrapper(_ref) { case 0: controller = new AbortController(); requestOptions = { - // NOTE: try thin in Avalon + // NOTE: try this in Avalon //credentials: 'include', // headers: { 'Avalon-Api-Key': '' }, }; @@ -3762,7 +3762,7 @@ function IIIFPlayerWrapper(_ref) { return _ref2.apply(this, arguments); }; }(); - React.useEffect(function () { + useEffect(function () { setAppErrorMessage(customErrorMessage); setAppEmptyManifestMessage(emptyManifestMessage); if (!manifest && manifestUrl) { @@ -3774,7 +3774,7 @@ function IIIFPlayerWrapper(_ref) { if (controller) controller.abort(); }; }, []); - React.useEffect(function () { + useEffect(function () { if (manifest) { // Set customStart and rendering files in state before setting Manifest var renderingFiles = getRenderingFiles(manifest); @@ -3846,6 +3846,17 @@ ErrorMessage.propTypes = { children: PropTypes.object }; +/** + * Component with wrapped in React Contexts to provide access + * to global state across its children + * @param {Object} props + * @param {String} props.manifestUrl + * @param {Object} props.manifest + * @param {String} props.customErrorMessage + * @param {String} props.emptyManifestMessage + * @param {String} props.startCanvasId + * @param {String} props.startCanvasTime + */ function IIIFPlayer(_ref) { var manifestUrl = _ref.manifestUrl, manifest = _ref.manifest, @@ -5239,1956 +5250,1862 @@ var FileDownloadIcon = function FileDownloadIcon() { }))); }; -var classCallCheck = createCommonjsModule(function (module) { -function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } -} -module.exports = _classCallCheck, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); - -var _classCallCheck = /*@__PURE__*/getDefaultExportFromCjs(classCallCheck); - -var createClass = createCommonjsModule(function (module) { -function _defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, toPropertyKey(descriptor.key), descriptor); - } -} -function _createClass(Constructor, protoProps, staticProps) { - if (protoProps) _defineProperties(Constructor.prototype, protoProps); - if (staticProps) _defineProperties(Constructor, staticProps); - Object.defineProperty(Constructor, "prototype", { - writable: false - }); - return Constructor; -} -module.exports = _createClass, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); - -var _createClass = /*@__PURE__*/getDefaultExportFromCjs(createClass); - -var assertThisInitialized = createCommonjsModule(function (module) { -function _assertThisInitialized(self) { - if (self === void 0) { - throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); - } - return self; -} -module.exports = _assertThisInitialized, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); - -var _assertThisInitialized = /*@__PURE__*/getDefaultExportFromCjs(assertThisInitialized); - -var getPrototypeOf = createCommonjsModule(function (module) { -function _getPrototypeOf(o) { - module.exports = _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { - return o.__proto__ || Object.getPrototypeOf(o); - }, module.exports.__esModule = true, module.exports["default"] = module.exports; - return _getPrototypeOf(o); -} -module.exports = _getPrototypeOf, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); - -var _getPrototypeOf = /*@__PURE__*/getDefaultExportFromCjs(getPrototypeOf); - -var superPropBase = createCommonjsModule(function (module) { -function _superPropBase(object, property) { - while (!Object.prototype.hasOwnProperty.call(object, property)) { - object = getPrototypeOf(object); - if (object === null) break; - } - return object; -} -module.exports = _superPropBase, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); - -var get = createCommonjsModule(function (module) { -function _get() { - if (typeof Reflect !== "undefined" && Reflect.get) { - module.exports = _get = Reflect.get.bind(), module.exports.__esModule = true, module.exports["default"] = module.exports; - } else { - module.exports = _get = function _get(target, property, receiver) { - var base = superPropBase(target, property); - if (!base) return; - var desc = Object.getOwnPropertyDescriptor(base, property); - if (desc.get) { - return desc.get.call(arguments.length < 3 ? target : receiver); - } - return desc.value; - }, module.exports.__esModule = true, module.exports["default"] = module.exports; +var taggedTemplateLiteral = createCommonjsModule(function (module) { +function _taggedTemplateLiteral(strings, raw) { + if (!raw) { + raw = strings.slice(0); } - return _get.apply(this, arguments); + return Object.freeze(Object.defineProperties(strings, { + raw: { + value: Object.freeze(raw) + } + })); } -module.exports = _get, module.exports.__esModule = true, module.exports["default"] = module.exports; +module.exports = _taggedTemplateLiteral, module.exports.__esModule = true, module.exports["default"] = module.exports; }); -var _get = /*@__PURE__*/getDefaultExportFromCjs(get); +var _taggedTemplateLiteral = /*@__PURE__*/getDefaultExportFromCjs(taggedTemplateLiteral); -var setPrototypeOf = createCommonjsModule(function (module) { -function _setPrototypeOf(o, p) { - module.exports = _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { - o.__proto__ = p; - return o; - }, module.exports.__esModule = true, module.exports["default"] = module.exports; - return _setPrototypeOf(o, p); -} -module.exports = _setPrototypeOf, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); +var _templateObject$1, _templateObject2, _templateObject3, _templateObject4; +function _createForOfIteratorHelper$2(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$2(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } +function _unsupportedIterableToArray$2(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$2(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$2(o, minLen); } +function _arrayLikeToArray$2(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } -var inherits = createCommonjsModule(function (module) { -function _inherits(subClass, superClass) { - if (typeof superClass !== "function" && superClass !== null) { - throw new TypeError("Super expression must either be null or a function"); - } - subClass.prototype = Object.create(superClass && superClass.prototype, { - constructor: { - value: subClass, - writable: true, - configurable: true - } - }); - Object.defineProperty(subClass, "prototype", { - writable: false - }); - if (superClass) setPrototypeOf(subClass, superClass); -} -module.exports = _inherits, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); +// ENum for supported transcript MIME types +var TRANSCRIPT_MIME_TYPES = { + webvtt: ['text/vtt'], + srt: ['application/x-subrip', 'text/srt'], + text: ['text/plain'], + json: ['application/json'], + docx: ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'] +}; +var VTT_TIMESTAMP_REGEX = /^(?:\d{2}:)?\d{2}:\d{2}(?:\.\d+)/g; +// SRT allows using comma for milliseconds while WebVTT does not +var SRT_TIMESTAMP_REGEX = /^(?:\d{2}:)?\d{2}:\d{2}(?:[.,]\d+)/g; +var TRANSCRIPT_MIME_EXTENSIONS = [{ + type: TRANSCRIPT_MIME_TYPES.json, + ext: 'json' +}, { + type: TRANSCRIPT_MIME_TYPES.webvtt, + ext: 'vtt' +}, { + type: TRANSCRIPT_MIME_TYPES.text, + ext: 'txt' +}, { + type: TRANSCRIPT_MIME_TYPES.docx, + ext: 'docx' +}, { + type: TRANSCRIPT_MIME_TYPES.srt, + ext: 'srt' +}]; -var _inherits = /*@__PURE__*/getDefaultExportFromCjs(inherits); +// ENum for describing transcript types include invalid and no transcript info +var TRANSCRIPT_TYPES = { + invalidTimestamp: -4, + invalidVTT: -3, + noSupport: -2, + invalid: -1, + noTranscript: 0, + timedText: 1, + plainText: 2, + docx: 3 +}; -var possibleConstructorReturn = createCommonjsModule(function (module) { -var _typeof = _typeof_1["default"]; +// ENum for types transcript text lines in a time-synced transcript +var TRANSCRIPT_CUE_TYPES = { + note: 'NOTE', + timedCue: 'TIMED_CUE', + nonTimedLine: 'NON_TIMED_LINE' +}; -function _possibleConstructorReturn(self, call) { - if (call && (_typeof(call) === "object" || typeof call === "function")) { - return call; - } else if (call !== void 0) { - throw new TypeError("Derived constructors may only return object or undefined"); - } - return assertThisInitialized(self); +/** + * Parse the transcript information in the Manifest presented as supplementing annotations + * @param {String} manifestURL IIIF Presentation 3.0 manifest URL + * @param {String} title optional title given in the transcripts list in props + * @returns {Array} array of supplementing annotations for transcripts for all + * canvases in the Manifest + */ +function readSupplementingAnnotations(_x) { + return _readSupplementingAnnotations.apply(this, arguments); } -module.exports = _possibleConstructorReturn, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); - -var _possibleConstructorReturn = /*@__PURE__*/getDefaultExportFromCjs(possibleConstructorReturn); -function _createSuper$6(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$6(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } -function _isNativeReflectConstruct$6() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } -var SeekBar = videojs.getComponent('SeekBar'); -var VideoJSProgress = /*#__PURE__*/function (_SeekBar) { - _inherits(VideoJSProgress, _SeekBar); - var _super = _createSuper$6(VideoJSProgress); - function VideoJSProgress(player, options) { - var _this; - _classCallCheck(this, VideoJSProgress); - _this = _super.call(this, player, options); - /** - * Set start values for progress bar - * @param {Number} start canvas start time - */ - _defineProperty(_assertThisInitialized(_this), "initializeProgress", function (start) { - _this.setProgress(start); - _this.setInitTime(start); - _this.player.currentTime(start); - }); - _this.addClass('vjs-custom-progress-bar'); - _this.setAttribute('data-testid', 'videojs-custom-progressbar'); - _this.setAttribute('tabindex', 0); - _this.player = player; - _this.options = options; - _this.selectSource = _this.options.nextItemClicked; - _this.playerEventListener; - _this.initTimeRef = /*#__PURE__*/React.createRef(); - _this.progressRef = /*#__PURE__*/React.createRef(); - _this.canvasTargetsRef = /*#__PURE__*/React.createRef(); - _this.srcIndexRef = /*#__PURE__*/React.createRef(); - _this.isMultiSourceRef = /*#__PURE__*/React.createRef(); - _this.currentTimeRef = /*#__PURE__*/React.createRef(); - _this.pointerDragged = false; - _this.totalDuration; - _this.playProgress = _this.getChild('PlayProgressBar'); - _this.loadProgress = _this.getChild('LoadProgressBar'); - _this.player.on('ready', function () { - _this.initializeEl(); - _this.updateComponent(); - }); - _this.player.on('loadstart', function () { - _this.updateComponent(); - _this.buildProgressBar(); - }); - - // Update our progress bar after the user leaves full screen - _this.player.on('fullscreenchange', function (e) { - if (!_this.player.isFullscreen()) { - _this.setProgress(_this.player.currentTime()); - } - }); - _this.player.on('dispose', function () { - clearInterval(_this.playerEventListener); - }); - return _this; - } - _createClass(VideoJSProgress, [{ - key: "setInitTime", - value: function setInitTime(t) { - this.initTimeRef.current = t; - } - }, { - key: "setSrcIndex", - value: function setSrcIndex(i) { - this.srcIndexRef.current = i; - } - }, { - key: "setProgress", - value: function setProgress(p) { - this.progressRef.current = p; - } - }, { - key: "setCanvasTargets", - value: function setCanvasTargets(t) { - this.canvasTargetsRef.current = t; - this.totalDuration = t.reduce(function (acc, c) { - return acc + c.duration; - }, 0); - } - }, { - key: "setIsMultiSource", - value: function setIsMultiSource(m) { - this.isMultiSourceRef.current = m; - } - }, { - key: "setCurrentTime", - value: function setCurrentTime(t) { - this.currentTimeRef.current = t; - } - }, { - key: "updateComponent", - value: function updateComponent() { - var _this2 = this; - var _this$player = this.player, - srcIndex = _this$player.srcIndex, - targets = _this$player.targets; - this.setSrcIndex(srcIndex); - this.setCanvasTargets(targets); - var cTimes = targets[srcIndex]; - if (cTimes.customStart > cTimes.start) { - this.initializeProgress(cTimes.customStart); - } else { - this.initializeProgress(cTimes.start); +/** + * Refine and sanitize the user provided transcripts list in the props. If there are manifests + * in the given array process them to find supplementing annotations in the manifest and + * them to the transcripts array to be displayed in the component. + * @param {Array} transcripts list of transcripts from Transcript component's props + * @returns {Array} a refined transcripts array for each canvas with the following json + * structure; + * { canvasId: , items: [{ title, filename, url, isMachineGen, id }]} + */ +function _readSupplementingAnnotations() { + _readSupplementingAnnotations = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee(manifestURL) { + var title, + data, + _args = arguments; + return regenerator.wrap(function _callee$(_context) { + while (1) switch (_context.prev = _context.next) { + case 0: + title = _args.length > 1 && _args[1] !== undefined ? _args[1] : ''; + _context.next = 3; + return fetch(manifestURL).then(function (response) { + var fileType = response.headers.get('Content-Type'); + if (fileType.includes('application/json')) { + var jsonData = response.json(); + return jsonData; + } else { + // Avoid throwing an error when fetched file is not a JSON + return {}; + } + }).then(function (manifest) { + var canvases = manifest.items; + var newTranscriptsList = []; + if ((canvases === null || canvases === void 0 ? void 0 : canvases.length) > 0) { + canvases.map(function (canvas, index) { + var annotations = getAnnotations(canvas.annotations, 'supplementing'); + var canvasTranscripts = []; + if (annotations.length > 0) { + var annotBody = annotations[0].body; + if (annotBody.type === 'TextualBody') { + var label = title.length > 0 ? title : annotBody.label ? getLabelValue(annotBody.label) : "Canvas-".concat(index); + var _identifyMachineGen = identifyMachineGen(label), + isMachineGen = _identifyMachineGen.isMachineGen, + labelText = _identifyMachineGen.labelText; + canvasTranscripts.push({ + url: annotBody.id === undefined ? manifestURL : annotBody.id, + title: labelText, + isMachineGen: isMachineGen, + id: "".concat(labelText, "-").concat(index), + format: '' + }); + } else { + annotations.forEach(function (annotation, i) { + var annotBody = annotation.body; + var label = ''; + var filename = ''; + if (annotBody.label && Object.keys(annotBody.label).length > 0) { + var languages = Object.keys(annotBody.label); + if ((languages === null || languages === void 0 ? void 0 : languages.length) > 1) { + // If there are multiple labels for an annotation assume the first + // is the one intended for default display. + label = getLabelValue(annotBody.label); + // Assume that an unassigned language is meant to be the downloadable filename + filename = annotBody.label.hasOwnProperty('none') ? getLabelValue(annotBody.label.none[0]) : label; + } else { + // If there is a single label, use for both label and downloadable filename + label = getLabelValue(annotBody.label); + } + } else { + label = "".concat(i); + } + var id = annotBody.id; + var sType = identifySupplementingAnnotation(id); + var _identifyMachineGen2 = identifyMachineGen(label), + isMachineGen = _identifyMachineGen2.isMachineGen, + labelText = _identifyMachineGen2.labelText; + if (filename === '') { + filename = labelText; + } + if (sType === 1 || sType === 3) { + canvasTranscripts.push({ + title: labelText, + filename: filename, + url: id, + isMachineGen: isMachineGen, + id: "".concat(labelText, "-").concat(index, "-").concat(i), + format: annotBody.format || '' + }); + } + }); + } + } + newTranscriptsList.push({ + canvasId: index, + items: canvasTranscripts + }); + }); + } + return newTranscriptsList; + })["catch"](function (error) { + console.error('transcript-parser -> readSupplementingAnnotations() -> error fetching transcript resource at, ', manifestURL); + return []; + }); + case 3: + data = _context.sent; + return _context.abrupt("return", data); + case 5: + case "end": + return _context.stop(); } - this.setIsMultiSource((targets === null || targets === void 0 ? void 0 : targets.length) > 1 ? true : false); - if (!this.playerEventListener) { - /** - * Using a time interval instead of 'timeupdate event in VideoJS, because Safari - * and other browsers in MacOS stops firing the 'timeupdate' event consistently - * after a while - */ - this.playerEventListener = setInterval(function () { - /** - * Abortable inerval for Safari desktop browsers, for a smoother scrubbing - * experience. - * Mobile devices are excluded since they use native iOS player. - */ - if (IS_SAFARI && !IS_IPHONE) { - _this2.abortableTimeupdateHandler(); - } else { - _this2.timeUpdateHandler(); + }, _callee); + })); + return _readSupplementingAnnotations.apply(this, arguments); +} +function sanitizeTranscripts(_x2) { + return _sanitizeTranscripts.apply(this, arguments); +} + +/** + * Group a nested JSON object array by a given property name + * @param {Array} objectArray nested array to reduced + * @param {String} indexKey property name to be used to group elements in the array + * @param {String} selectKey property to be selected from the objects to accumulated + * @returns {Array} + */ +function _sanitizeTranscripts() { + _sanitizeTranscripts = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee4(transcripts) { + var allTranscripts, sanitizedTrs, newTranscripts; + return regenerator.wrap(function _callee4$(_context4) { + while (1) switch (_context4.prev = _context4.next) { + case 0: + if (!(!transcripts || transcripts == undefined || transcripts.length == 0)) { + _context4.next = 5; + break; } - }, 100); - } - } - }, { - key: "update", - value: function update() { - // Need this to make the other updates work - _get(_getPrototypeOf(VideoJSProgress.prototype), "update", this).call(this); - // Explicitly played range variable on update for touch devices - if (IS_TOUCH_ONLY && this.player.currentTime() === 0) { - this.removeClass('played-range'); - document.documentElement.style.setProperty('--range-progress', "calc(".concat(0, "%)")); - } - if (IS_MOBILE && IS_SAFARI && this.player.paused()) { - var _this$player$structSt; - var structStart = (_this$player$structSt = this.player.structStart) !== null && _this$player$structSt !== void 0 ? _this$player$structSt : 0; - if (structStart != 0 && this.player.currentTime() === 0) { - this.player.currentTime(structStart); - var played = Math.min(100, Math.max(0, 100 * (structStart / this.totalDuration))); - this.addClass('played-range'); - document.documentElement.style.setProperty('--range-progress', "calc(".concat(played, "%)")); - this.player.structStart = 0; - } - } else { - return; - } - } - }, { - key: "initializeEl", - value: function initializeEl() { - var _this3 = this; - var leftBlock = videojs.dom.createEl('div', { - className: 'block-stripes', - role: 'presentation', - id: 'left-block' - }); - var rightBlock = videojs.dom.createEl('div', { - className: 'block-stripes', - role: 'presentation', - id: 'right-block' - }); - this.el().appendChild(leftBlock); - this.el().appendChild(rightBlock); + console.error('No transcripts given as input'); + return _context4.abrupt("return", []); + case 5: + allTranscripts = []; // Build an empty list for each canvasId from the given transcripts prop + transcripts.map(function (trs) { + return allTranscripts.push({ + canvasId: trs.canvasId, + items: [] + }); + }); - /** - * Add eventlisteners to handle time tool-tip display and progress updates. - * Using pointerup, pointermove, pointerdown events instead of mouseup, - * mousemove, mousedown events to make it work with both mouse pointer - * and touch events. - */ - this.el().addEventListener('mouseenter', function (e) { - _this3.handleMouseMove(e); - }); - this.el().addEventListener('pointerup', function (e) { - if (_this3.pointerDragged) { - _this3.handleMouseUp(e); - } - }); - this.el().addEventListener('pointermove', function (e) { - _this3.handleMouseMove(e); - _this3.pointerDragged = true; - }); - this.el().addEventListener('pointerdown', function (e) { - _this3.handleMouseDown(e); - _this3.pointerDragged = false; - }); - } - }, { - key: "handleMouseMove", - value: function handleMouseMove(e) { - var _this$convertToTime = this.convertToTime(e), - currentTime = _this$convertToTime.currentTime, - offsetx = _this$convertToTime.offsetx; - if (currentTime != undefined) this.setCurrentTime(currentTime); - var mouseTimeDisplay = this.getChild('MouseTimeDisplay'); - if (mouseTimeDisplay) { - var timeTooltip = mouseTimeDisplay.getChild('TimeTooltip'); - var toolTipEl = timeTooltip.el_; - if (currentTime) { - toolTipEl.innerHTML = timeToHHmmss(currentTime); - } - var pullTooltip = toolTipEl.clientWidth / 2; - toolTipEl.style.left = "".concat(offsetx - pullTooltip, "px"); - } - } - }, { - key: "handleMouseDown", - value: function handleMouseDown(e) { - // Do nothing when right-click is pressed - if (!IS_TOUCH_ONLY && e.buttons === 2) return; - var _this$convertToTime2 = this.convertToTime(e), - currentTime = _this$convertToTime2.currentTime; - _this$convertToTime2._; - var clickedSrc; - if (this.isMultiSourceRef.current) { - clickedSrc = this.canvasTargetsRef.current.find(function (t) { - var virtualEnd = t.altStart + t.duration; - if (currentTime >= t.altStart && currentTime <= virtualEnd) { - return t; - } - }); - } - if (clickedSrc) { - var _clickedSrc$sIndex, _clickedSrc; - var clickedIndex = (_clickedSrc$sIndex = (_clickedSrc = clickedSrc) === null || _clickedSrc === void 0 ? void 0 : _clickedSrc.sIndex) !== null && _clickedSrc$sIndex !== void 0 ? _clickedSrc$sIndex : 0; - if (clickedIndex != this.srcIndexRef.current) { - this.selectSource(clickedSrc.sIndex, currentTime - clickedSrc.altStart); - this.setSrcIndex(clickedIndex); - } else { - this.player.currentTime(currentTime - clickedSrc.altStart); - } - } else { - this.player.currentTime(currentTime); - } + // Process the async function to resolve manifest URLs in the given transcripts array + // parallely to extract supplementing annotations in the manifests + _context4.next = 9; + return Promise.all(transcripts.map( /*#__PURE__*/function () { + var _ref5 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee3(transcript) { + var canvasId, items, sanitizedItems; + return regenerator.wrap(function _callee3$(_context3) { + while (1) switch (_context3.prev = _context3.next) { + case 0: + canvasId = transcript.canvasId, items = transcript.items; + _context3.next = 3; + return Promise.all(items.map( /*#__PURE__*/function () { + var _ref6 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee2(item, index) { + var title, url, manifestTranscripts, _identifyMachineGen3, isMachineGen, labelText, manifestItems, groupedTrs; + return regenerator.wrap(function _callee2$(_context2) { + while (1) switch (_context2.prev = _context2.next) { + case 0: + title = item.title, url = item.url; // For each item in the list check if it is a manifest and parse + // the it to identify any supplementing annotations in the + // manifest for each canvas + _context2.next = 3; + return readSupplementingAnnotations(url, title); + case 3: + manifestTranscripts = _context2.sent; + _identifyMachineGen3 = identifyMachineGen(title), isMachineGen = _identifyMachineGen3.isMachineGen, labelText = _identifyMachineGen3.labelText; + manifestItems = []; + if ((manifestTranscripts === null || manifestTranscripts === void 0 ? void 0 : manifestTranscripts.length) > 0) { + manifestItems = manifestTranscripts.map(function (mt) { + return mt.items; + }).flat(); - /** - * For touch devices, player.currentTime() update doesn't show the - * played range, even though the player's currentTime is properly set. - * Therefore, update the CSS here explicitly. - */ - if (IS_TOUCH_ONLY) { - var played = Math.min(100, Math.max(0, 100 * (currentTime / this.totalDuration))); - this.player.currentTime(currentTime); - this.addClass('played-range'); - document.documentElement.style.setProperty('--range-progress', "calc(".concat(played, "%)")); - } - } - }, { - key: "handleMouseUp", - value: function handleMouseUp(e) { - this.handleMouseDown(e); - } - }, { - key: "buildProgressBar", - value: function buildProgressBar() { - var _canvasTargetsRef$cur; - // Reset progress-bar for played range - this.removeClass('played-range'); - var canvasTargetsRef = this.canvasTargetsRef, - isMultiSourceRef = this.isMultiSourceRef, - player = this.player, - srcIndexRef = this.srcIndexRef, - totalDuration = this.totalDuration; - if (((_canvasTargetsRef$cur = canvasTargetsRef.current) === null || _canvasTargetsRef$cur === void 0 ? void 0 : _canvasTargetsRef$cur.length) > 0) { - var _canvasTargetsRef$cur2 = canvasTargetsRef.current[srcIndexRef.current], - altStart = _canvasTargetsRef$cur2.altStart, - start = _canvasTargetsRef$cur2.start, - end = _canvasTargetsRef$cur2.end, - duration = _canvasTargetsRef$cur2.duration; - var leftBlockEl = document.getElementById('left-block'); - var rightBlockEl = document.getElementById('right-block'); - if (!isMultiSourceRef.current) { - var leftBlock = start * 100 / duration; - var rightBlock = (duration - end) * 100 / duration; + // Concat the existing transcripts list and transcripts from the manifest and + // group them by canvasId + groupedTrs = groupByIndex(allTranscripts.concat(manifestTranscripts), 'canvasId', 'items'); + allTranscripts = groupedTrs; + } - // Set player.isClipped to use in the ended event to decide to advance to next - rightBlock > 0 ? player.isClipped = true : player.isClipped = false; - if (leftBlockEl) leftBlockEl.style.width = "".concat(leftBlock, "%"); - if (rightBlockEl) { - rightBlockEl.style.width = rightBlock + '%'; - rightBlockEl.style.left = "".concat(100 - rightBlock - leftBlock, "%"); - } - } else { - // Calculate offset of the duration of the current source - var leftOffset = Math.min(100, Math.max(0, 100 * (altStart / totalDuration))); - this.playProgress.el_.style.left = "".concat(leftOffset, "%"); - this.loadProgress.el_.style.left = "".concat(leftOffset, "%"); - // Add CSS class to mark the range from zero as played - this.addClass('played-range'); - document.documentElement.style.setProperty('--range-progress', "calc(".concat(leftOffset, "%)")); - } + // if manifest doesn't have canvases or + // supplementing annotations add original transcript from props + if (!(manifestTranscripts.length === 0 || manifestItems.length === 0)) { + _context2.next = 11; + break; + } + return _context2.abrupt("return", { + title: labelText, + filename: labelText, + url: url, + isMachineGen: isMachineGen, + id: "".concat(labelText, "-").concat(canvasId, "-").concat(index), + format: '' + }); + case 11: + return _context2.abrupt("return", null); + case 12: + case "end": + return _context2.stop(); + } + }, _callee2); + })); + return function (_x9, _x10) { + return _ref6.apply(this, arguments); + }; + }())); + case 3: + sanitizedItems = _context3.sent; + return _context3.abrupt("return", { + canvasId: canvasId, + items: sanitizedItems.filter(function (i) { + return i != null; + }) + }); + case 5: + case "end": + return _context3.stop(); + } + }, _callee3); + })); + return function (_x8) { + return _ref5.apply(this, arguments); + }; + }())); + case 9: + sanitizedTrs = _context4.sent; + // Group all the transcripts by canvasId one last time to eliminate duplicate canvasIds + newTranscripts = groupByIndex(allTranscripts.concat(sanitizedTrs), 'canvasId', 'items'); + return _context4.abrupt("return", newTranscripts); + case 12: + case "end": + return _context4.stop(); } + }, _callee4); + })); + return _sanitizeTranscripts.apply(this, arguments); +} +function groupByIndex(objectArray, indexKey, selectKey) { + return objectArray.reduce(function (acc, obj) { + var existing = acc.filter(function (a) { + return a[indexKey] == obj[indexKey]; + }); + if ((existing === null || existing === void 0 ? void 0 : existing.length) > 0) { + var current = existing[0]; + current[selectKey] = current[selectKey].concat(obj[selectKey]); + } else { + acc.push(obj); } - }, { - key: "convertToTime", - value: function convertToTime(e) { - var _e$nativeEvent$target, _this$totalDuration; - var eSrcElement = e.srcElement; - // When clicked on blocked time point - if (eSrcElement.classList.contains('block-stripes')) { - var _this$canvasTargetsRe = this.canvasTargetsRef.current[0], - altStart = _this$canvasTargetsRe.altStart, - end = _this$canvasTargetsRe.end, - _duration = _this$canvasTargetsRe.duration; - if (eSrcElement.id === 'right-block') { - // For right-block: place time tool-tip at the end of playable range - return { - currentTime: end, - offsetx: end / _duration * this.el().clientWidth - }; - } else { - // For left-block: place time tool-tip at the start of playable range - return { - currentTime: altStart, - offsetx: altStart / _duration * this.el().clientWidth - }; - } - } - var targetX = e.target.getBoundingClientRect().x; - var offsetx = e.nativeEvent != undefined ? e.nativeEvent.offsetX != undefined ? e.nativeEvent.offsetX // iOS and desktop events - : ((_e$nativeEvent$target = e.nativeEvent.targetTouches[0]) === null || _e$nativeEvent$target === void 0 ? void 0 : _e$nativeEvent$target.clientX) - targetX // Android event - : e.offsetX; // fallback in desktop browsers when nativeEvent is undefined - var currentTime; - var duration = (_this$totalDuration = this.totalDuration) !== null && _this$totalDuration !== void 0 ? _this$totalDuration : this.player.duration(); - if (offsetx && offsetx != undefined) { - if (this.isMultiSourceRef.current) { - /** - * Check if the mouse event occurred on the same src range. - * If so, adjust the offset to support altStart for the current src. - */ - var leftOffset = parseFloat(this.playProgress.el_.style.left) / 100 * this.el().clientWidth; - var elClassList = eSrcElement.classList; - var sameSrc = (elClassList === null || elClassList === void 0 ? void 0 : elClassList.length) > 0 ? elClassList.contains('vjs-play-progress') || elClassList.contains('vjs-load-progress') : true; - if (leftOffset > offsetx && sameSrc) { - offsetx = offsetx + leftOffset; - } - } - currentTime = offsetx / this.el().clientWidth * duration; - } - /** - * Parts of LoadProgress element is broken into segments as media loads, and displayed - * as separate div elements with `data-start` and `data-end` attributes respectively. - * When mouse event occurs on top of such element, add the segment start time to calculated - * current time from event. - */ - if (e.target.hasAttribute('data-start')) { - var _e$target$dataset = e.target.dataset, - start = _e$target$dataset.start; - _e$target$dataset._; - currentTime = currentTime + parseFloat(start); - offsetx = currentTime * this.el().clientWidth / this.totalDuration; - } - return { - currentTime: currentTime, - offsetx: offsetx - }; - } - }, { - key: "abortableTimeupdateHandler", - value: - /** - * A wrapper function around the time update interval, to cancel - * intermediate updates via the time interval when player is - * waiting to fetch stream - */ - function abortableTimeupdateHandler() { - var _this4 = this; - var player = this.player, - progressRef = this.progressRef; - player.on('waiting', function () { - if (IS_SAFARI && !IS_MOBILE) { - player.currentTime(progressRef.current); - } - cancelInterval(); - }); - var cancelInterval = function cancelInterval() { - if (internalInterval) { - clearInterval(internalInterval); - } - }; - var internalInterval = setInterval(function () { - _this4.timeUpdateHandler(); - }, 100); - } - }, { - key: "timeUpdateHandler", - value: - // Update progress bar with timeupdate in the player - function timeUpdateHandler() { - var _this5 = this; - var initTimeRef = this.initTimeRef, - player = this.player; - if (player.isDisposed() || player.ended() || player == null) { - return; - } - var curTime; - // Initially update progress from the prop passed from Ramp, - // this accounts for structured navigation when switching canvases - if (initTimeRef.current > 0 && player.currentTime() == 0) { - curTime = initTimeRef.current; - player.currentTime(initTimeRef.current); - } else { - curTime = player.currentTime(); - } - // Use debounced updates since, Safari desktop browsers need the extra - // update on 'seeked' event to timely update the progress bar. - if (IS_SAFARI && !IS_MOBILE && player.paused()) { - debounce_1(function () { - _this5.onTimeUpdate(curTime); - }); - } else { - this.onTimeUpdate(curTime); - } - this.setInitTime(0); - } - }, { - key: "onTimeUpdate", - value: function onTimeUpdate(curTime) { - // This state update caused weird lagging behaviors when using the iOS native - // video player. iOS player handles its own progress bar, so we can skip the - // update here only for video. - var iOS = this.player.hasClass("vjs-ios-native-fs"); - if (!(iOS && !this.player.audioOnlyMode_)) { - this.setProgress(curTime); - } - this.handleTimeUpdate(curTime); - } - }, { - key: "handleTimeUpdate", - value: - /** - * Update CSS for the input range's track while the media - * is playing - * @param {Number} curTime current time of the player - */ - function handleTimeUpdate(curTime) { - var _srcIndexRef$current; - var player = this.player, - el_ = this.el_, - canvasTargetsRef = this.canvasTargetsRef, - srcIndexRef = this.srcIndexRef; - - // Avoid null player instance when Video.js is getting initialized - if (!el_ || !player || !canvasTargetsRef.current) { - return; - } - var _canvasTargetsRef$cur3 = canvasTargetsRef.current[(_srcIndexRef$current = srcIndexRef.current) !== null && _srcIndexRef$current !== void 0 ? _srcIndexRef$current : 0], - start = _canvasTargetsRef$cur3.start, - end = _canvasTargetsRef$cur3.end, - duration = _canvasTargetsRef$cur3.duration; - - // Restrict access to the intended range in the media file - if (curTime < start) { - player.currentTime(start); - } - if (curTime >= end && !player.paused() && !player.isDisposed()) { - // Trigger ended event when playable range < duration of the - // full media. e.g. clipped playlist items - if (end < duration) { - player.trigger('ended'); - } - - // On the next play event set the time to start or a seeked time - // in between the 'ended' event and 'play' event - // Reference: https://github.com/videojs/video.js/blob/main/src/js/control-bar/play-toggle.js#L128 - player.one('play', function () { - var time = player.currentTime(); - if (time < end) { - player.currentTime(time); - } else { - player.currentTime(start); - } - }); - } - } - }]); - return VideoJSProgress; -}(SeekBar); -videojs.registerComponent('VideoJSProgress', VideoJSProgress); - -function _createSuper$5(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$5(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } -function _isNativeReflectConstruct$5() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } -var TimeDisplay = videojs.getComponent('TimeDisplay'); + return acc; + }, []); +} /** - * Custom component to display the current time of the player - * @param {Object} props - * @param {Object} props.player VideoJS player instance - * @param {Object} props.options options passed into component - * options: { srcIndex, targets } + * Parse a given transcript file into a format the Transcript component + * can render on the UI. E.g.: text file -> returns null, so that the Google + * doc viewer is rendered, IIIF manifest -> extract and parse transcript data + * within the manifest. + * @param {String} url URL of the transcript file selected + * @param {Number} canvasIndex Current canvas rendered in the player + * @param {String} format transcript file format read from Annotation + * @returns {Object} Array of trancript data objects with download URL */ -var VideoJSCurrentTime = /*#__PURE__*/function (_TimeDisplay) { - _inherits(VideoJSCurrentTime, _TimeDisplay); - var _super = _createSuper$5(VideoJSCurrentTime); - function VideoJSCurrentTime(player, options) { - var _this; - _classCallCheck(this, VideoJSCurrentTime); - _this = _super.call(this, player, options); - _this.addClass('vjs-time-control vjs-current-time-display'); - _this.setAttribute('role', 'presentation'); - _this.player = player; - _this.options = options; - _this.initTimeRef = /*#__PURE__*/React.createRef(); - _this.initTimeRef.current = options.currentTime; - _this.playerInterval; - _this.player.on('loadstart', function () { - _this.playerInterval = setInterval(function () { - _this.handleTimeUpdate(); - }, 100); - }); - _this.player.on('seeked', function () { - if (IS_SAFARI && !IS_MOBILE) { - _this.updateTextNode_(player.currentTime()); - } - }); +function parseTranscriptData(_x3, _x4, _x5) { + return _parseTranscriptData.apply(this, arguments); +} - // Update our timer after the user leaves full screen - _this.player.on('fullscreenchange', function () { - if (!player.isFullscreen()) { - _this.updateTextNode_(player.currentTime()); - } - }); - _this.player.on('dispose', function () { - clearInterval(_this.playerInterval); - }); - return _this; - } - _createClass(VideoJSCurrentTime, [{ - key: "buildCSSClass", - value: function buildCSSClass() { - return 'current-time'; - } - }, { - key: "setInitTime", - value: function setInitTime(t) { - this.initTimeRef.current = t; - } - }, { - key: "handleTimeUpdate", - value: function handleTimeUpdate() { - var player = this.player, - initTimeRef = this.initTimeRef; - var targets = player.targets, - srcIndex = player.srcIndex; - if (!player || player.isDisposed() || !targets) { - return; - } - var iOS = player.hasClass('vjs-ios-native-fs'); - var time; - // Update time from the given initial time if it is not zero - if (initTimeRef.current > 0 && player.currentTime() == 0) { - time = initTimeRef.current; - } else { - time = player.currentTime(); - } - var _targets = targets[srcIndex !== null && srcIndex !== void 0 ? srcIndex : 0], - start = _targets.start, - altStart = _targets.altStart; - if (altStart != start && srcIndex > 0) { - time = time + altStart; - } - // This state update caused weird lagging behaviors when using the iOS native - // video player. iOS player handles its own time, so we can skip the update here - // video items. - if (!(iOS && !player.audioOnlyMode_)) { - this.updateTextNode_(time); - } - this.setInitTime(0); - } - }]); - return VideoJSCurrentTime; -}(TimeDisplay); -videojs.registerComponent('VideoJSCurrentTime', VideoJSCurrentTime); +/** + * Parse MS word documents into HTML markdown using mammoth.js + * https://www.npmjs.com/package/mammoth + * @param {Object} response response from the fetch request + * @returns {Array} html markdown for the word document contents + */ +function _parseTranscriptData() { + _parseTranscriptData = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee5(url, canvasIndex, format) { + var tData, tUrl, contentType, fileData, fromContentType, fromAnnotFormat, fileType, urlExt, filteredExt, textData, textLines, jsonData, json, parsedText, _parseTimedText, _tData, tType; + return regenerator.wrap(function _callee5$(_context5) { + while (1) switch (_context5.prev = _context5.next) { + case 0: + tData = []; + tUrl = url; // Validate given URL + if (!(url === undefined)) { + _context5.next = 4; + break; + } + return _context5.abrupt("return", { + tData: tData, + tUrl: tUrl, + tType: TRANSCRIPT_TYPES.invalid + }); + case 4: + contentType = null; + fileData = null; // get file type + _context5.next = 8; + return fetch(url).then(handleFetchErrors).then(function (response) { + contentType = response.headers.get('Content-Type'); + fileData = response; + })["catch"](function (error) { + console.error('transcript-parser -> parseTranscriptData() -> fetching transcript -> ', error); + }); + case 8: + if (!(contentType == null)) { + _context5.next = 10; + break; + } + return _context5.abrupt("return", { + tData: [], + tUrl: tUrl, + tType: TRANSCRIPT_TYPES.invalid + }); + case 10: + /* + Use the Annotation format in the IIIF Manifest, file extension, and the + Content-Type in headers of the fetch request to determine the file type. + These are checked with priority descending in the order of Annotation format, + Content-Type in headers, and file extension in the resource URI. + */ + fromContentType = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { + return tm.type.includes(contentType.split(';')[0]); + }); + fromAnnotFormat = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { + return tm.type.includes(format); + }); + fileType = ''; + if ((fromAnnotFormat === null || fromAnnotFormat === void 0 ? void 0 : fromAnnotFormat.length) > 0) { + fileType = fromAnnotFormat[0].ext; + } else if (fromContentType.length > 0) { + fileType = fromContentType[0].ext; + } else { + urlExt = url.split('.').reverse()[0]; // Only use this if it exists in the supported list of file types for the component + filteredExt = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { + return tm.ext === urlExt; + }); + fileType = filteredExt.length > 0 ? urlExt : ''; + } -function _createSuper$4(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$4(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } -function _isNativeReflectConstruct$4() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } -var MenuButton = videojs.getComponent('MenuButton'); -var MenuItem = videojs.getComponent('MenuItem'); -var VideoJSFileDownload = /*#__PURE__*/function (_MenuButton) { - _inherits(VideoJSFileDownload, _MenuButton); - var _super = _createSuper$4(VideoJSFileDownload); - function VideoJSFileDownload(player, options) { - var _this; - _classCallCheck(this, VideoJSFileDownload); - _this = _super.call(this, player, options); - // Add SVG icon through CSS class - _this.addClass("vjs-file-download"); - _this.setAttribute('data-testid', 'videojs-file-download'); - // Use Video.js' stock SVG instead of setting it using CSS - _this.setIcon('file-download'); - return _this; - } - _createClass(VideoJSFileDownload, [{ - key: "createItems", - value: function createItems() { - var options_ = this.options_, - player_ = this.player_; - var files = options_.files; - if ((files === null || files === void 0 ? void 0 : files.length) > 0) { - return files.map(function (file) { - var item = new MenuItem(player_, { - label: file.label + // Return empty array to display an error message + if (!(canvasIndex === undefined)) { + _context5.next = 16; + break; + } + return _context5.abrupt("return", { + tData: tData, + tUrl: tUrl, + tType: TRANSCRIPT_TYPES.noTranscript }); - item.handleClick = function () { - fileDownload(file.id, file.filename, file.fileExt); - }; - return item; - }); - } else { - return []; + case 16: + _context5.t0 = fileType; + _context5.next = _context5.t0 === 'json' ? 19 : _context5.t0 === 'txt' ? 28 : _context5.t0 === 'srt' ? 39 : _context5.t0 === 'vtt' ? 39 : _context5.t0 === 'docx' ? 49 : 53; + break; + case 19: + _context5.next = 21; + return fileData.json(); + case 21: + jsonData = _context5.sent; + if (!((jsonData === null || jsonData === void 0 ? void 0 : jsonData.type) === 'Manifest')) { + _context5.next = 26; + break; + } + return _context5.abrupt("return", parseManifestTranscript(jsonData, url, canvasIndex)); + case 26: + json = parseJSONData(jsonData); + return _context5.abrupt("return", { + tData: json.tData, + tUrl: tUrl, + tType: json.tType, + tFileExt: fileType + }); + case 28: + _context5.next = 30; + return fileData.text(); + case 30: + textData = _context5.sent; + textLines = textData.split('\n'); + if (!(textLines.length == 0)) { + _context5.next = 36; + break; + } + return _context5.abrupt("return", { + tData: [], + tUrl: url, + tType: TRANSCRIPT_TYPES.noTranscript + }); + case 36: + parsedText = buildNonTimedText(textLines); + return _context5.abrupt("return", { + tData: parsedText, + tUrl: url, + tType: TRANSCRIPT_TYPES.plainText, + tFileExt: fileType + }); + case 38: + case 39: + _context5.next = 41; + return fileData.text(); + case 41: + textData = _context5.sent; + textLines = textData.split('\n'); + if (!(textLines.length == 0)) { + _context5.next = 47; + break; + } + return _context5.abrupt("return", { + tData: [], + tUrl: url, + tType: TRANSCRIPT_TYPES.noTranscript + }); + case 47: + _parseTimedText = parseTimedText(textData, fileType === 'srt'), _tData = _parseTimedText.tData, tType = _parseTimedText.tType; + return _context5.abrupt("return", { + tData: _tData, + tUrl: url, + tType: tType, + tFileExt: fileType + }); + case 49: + _context5.next = 51; + return parseWordFile(fileData); + case 51: + tData = _context5.sent; + return _context5.abrupt("return", { + tData: splitIntoElements(tData), + tUrl: url, + tType: TRANSCRIPT_TYPES.docx, + tFileExt: fileType + }); + case 53: + return _context5.abrupt("return", { + tData: [], + tUrl: url, + tType: TRANSCRIPT_TYPES.noSupport + }); + case 54: + case "end": + return _context5.stop(); } - } - }]); - return VideoJSFileDownload; -}(MenuButton); -videojs.registerComponent('VideoJSFileDownload', VideoJSFileDownload); - -function _createSuper$3(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$3(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } -function _isNativeReflectConstruct$3() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } -var Button$2 = videojs.getComponent('Button'); -var VideoJSNextButton = /*#__PURE__*/function (_Button) { - _inherits(VideoJSNextButton, _Button); - var _super = _createSuper$3(VideoJSNextButton); - function VideoJSNextButton(player, options) { - var _this; - _classCallCheck(this, VideoJSNextButton); - _this = _super.call(this, player, options); - // Use Video.js' stock SVG instead of setting it using CSS - _this.setIcon('next-item'); - _this.addClass('vjs-play-control vjs-control'); - _this.setAttribute('data-testid', 'videojs-next-button'); - _this.controlText('Next'); - _this.options = options; - _this.player = player; - _this.cIndex = options.canvasIndex; - - // Handle player reload or source change events - _this.player.on('loadstart', function () { - _this.updateComponent(); - }); - return _this; + }, _callee5); + })); + return _parseTranscriptData.apply(this, arguments); +} +function parseWordFile(_x6) { + return _parseWordFile.apply(this, arguments); +} +/** + * Parse json data into Transcript component friendly + * format + * @param {Object} jsonData array of JSON objects + * @returns {Object} + */ +function _parseWordFile() { + _parseWordFile = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee6(response) { + var tData, data, arrayBuffer; + return regenerator.wrap(function _callee6$(_context6) { + while (1) switch (_context6.prev = _context6.next) { + case 0: + tData = null; + _context6.next = 3; + return response.blob(); + case 3: + data = _context6.sent; + arrayBuffer = new File([data], name, { + type: response.headers.get('content-type') + }); + _context6.next = 7; + return mammoth.convertToHtml({ + arrayBuffer: arrayBuffer + }).then(function (result) { + tData = result.value; + })["catch"](function (err) { + console.error(err); + }); + case 7: + return _context6.abrupt("return", tData); + case 8: + case "end": + return _context6.stop(); + } + }, _callee6); + })); + return _parseWordFile.apply(this, arguments); +} +function parseJSONData(jsonData) { + if (jsonData.length == 0) { + return { + tData: [], + tType: TRANSCRIPT_TYPES.noTranscript + }; } - _createClass(VideoJSNextButton, [{ - key: "updateComponent", - value: function updateComponent() { - var player = this.player; - if (player && player != undefined) { - var _player$children; - // When canvasIndex property is not set in the player instance use dataset. - // This happens rarely, but when it does previous button cannot be used. - if (player.canvasIndex === undefined && ((_player$children = player.children()) === null || _player$children === void 0 ? void 0 : _player$children.length) > 0) { - this.cIndex = Number(player.children()[0].dataset.canvasindex); - } else { - this.cIndex = player.canvasIndex; + var tData = []; + var _iterator = _createForOfIteratorHelper$2(jsonData), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var jd = _step.value; + if (jd.speaker) { + var speaker = jd.speaker, + spans = jd.spans; + var _iterator2 = _createForOfIteratorHelper$2(spans), + _step2; + try { + for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { + var span = _step2.value; + span.speaker = speaker; + tData.push(span); + } + } catch (err) { + _iterator2.e(err); + } finally { + _iterator2.f(); + } + } else { + var _iterator3 = _createForOfIteratorHelper$2(jd.spans), + _step3; + try { + for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { + var _span = _step3.value; + _span.format = 'text/plain'; + _span.tag = TRANSCRIPT_CUE_TYPES.timedCue; + tData.push(_span); + } + } catch (err) { + _iterator3.e(err); + } finally { + _iterator3.f(); } - } - if (this.options.playerFocusElement === 'nextBtn') { - this.el().focus(); - } - } - }, { - key: "handleClick", - value: function handleClick() { - this.handleNextClick(false); - } - }, { - key: "handleKeyDown", - value: function handleKeyDown(e) { - if (e.which === 32 || e.which === 13) { - e.stopPropagation(); - this.handleNextClick(true); - } - } - }, { - key: "handleNextClick", - value: function handleNextClick(isKeyDown) { - if (this.cIndex != this.options.lastCanvasIndex) { - this.options.switchPlayer(this.cIndex + 1, true, isKeyDown ? 'nextBtn' : ''); } } - }]); - return VideoJSNextButton; -}(Button$2); -videojs.registerComponent('VideoJSNextButton', VideoJSNextButton); - -function _createSuper$2(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$2(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } -function _isNativeReflectConstruct$2() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } -var Button$1 = videojs.getComponent('Button'); -var VideoJSPreviousButton = /*#__PURE__*/function (_Button) { - _inherits(VideoJSPreviousButton, _Button); - var _super = _createSuper$2(VideoJSPreviousButton); - function VideoJSPreviousButton(player, options) { - var _this; - _classCallCheck(this, VideoJSPreviousButton); - _this = _super.call(this, player, options); - // Use Video.js' stock SVG instead of setting it using CSS - _this.setIcon('previous-item'); - _this.addClass('vjs-play-control vjs-control'); - _this.setAttribute('data-testid', 'videojs-previous-button'); - _this.options = options; - _this.player = player; - _this.cIndex = options.canvasIndex; + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + return { + tData: tData, + tType: TRANSCRIPT_TYPES.timedText + }; +} - // Handle player reload or source change events - _this.player.on('loadstart', function () { - _this.updateComponent(); - }); - return _this; +/* Parsing annotations when transcript data is fed from a IIIF manifest */ +/** + * Parse a IIIF manifest and extracts the transcript data. + * IIIF manifests can present transcript data in a couple of different ways. + * 1. Using 'rendering' prop to link to an external file + * a. when the external file contains only text + * b. when the external file contains annotations + * 2. Using IIIF 'annotations' within the manifest + * @param {Object} manifest IIIF manifest data + * @param {String} manifestURL IIIF manifest URL + * @param {Number} canvasIndex Current canvas index + * @returns {Object} object with the structure; + * { tData: transcript data, tUrl: file url } + */ +function parseManifestTranscript(manifest, manifestURL, canvasIndex) { + var _manifest$items; + var tData = []; + var tUrl = manifestURL; + var isExternalAnnotation = false; + var annotations = []; + if (manifest.annotations) { + annotations = getAnnotations(manifest.annotations, 'supplementing'); + } else if (((_manifest$items = manifest.items) === null || _manifest$items === void 0 ? void 0 : _manifest$items.length) > 0) { + var _manifest$items$canva; + annotations = getAnnotations((_manifest$items$canva = manifest.items[canvasIndex]) === null || _manifest$items$canva === void 0 ? void 0 : _manifest$items$canva.annotations, 'supplementing'); } - _createClass(VideoJSPreviousButton, [{ - key: "updateComponent", - value: function updateComponent() { - var player = this.player; - if (player && player != undefined) { - var _player$children; - // When canvasIndex property is not set in the player instance use dataset. - // This happens rarely, but when it does previous button cannot be used. - if (player.canvasIndex === undefined && ((_player$children = player.children()) === null || _player$children === void 0 ? void 0 : _player$children.length) > 0) { - this.cIndex = Number(player.children()[0].dataset.canvasindex); - } else { - this.cIndex = player.canvasIndex; - } - } - this.controlText(this.cIndex == 0 ? 'Replay' : 'Previous'); - if (this.options.playerFocusElement === 'previousBtn') { - this.el().focus(); - } - } - }, { - key: "handleClick", - value: function handleClick() { - this.handlePreviousClick(false); - } - }, { - key: "handleKeyDown", - value: function handleKeyDown(e) { - if (e.which === 32 || e.which === 13) { - e.stopPropagation(); - this.handlePreviousClick(true); - } - } - }, { - key: "handlePreviousClick", - value: function handlePreviousClick(isKeyDown) { - if (this.cIndex > -1 && this.cIndex != 0) { - this.options.switchPlayer(this.cIndex - 1, true, isKeyDown ? 'previousBtn' : ''); - } else if (this.cIndex == 0) { - this.player.currentTime(0); - } + + // determine whether annotations point to an external resource or + // a list of transcript fragments + if (annotations.length > 0) { + var annotation = annotations[0]; + var tType = annotation.body.type; + if (tType == 'TextualBody') { + isExternalAnnotation = false; + } else { + isExternalAnnotation = true; } - }]); - return VideoJSPreviousButton; -}(Button$1); -videojs.registerComponent('VideoJSPreviousButton', VideoJSPreviousButton); + } else { + return { + tData: [], + tUrl: tUrl, + tType: TRANSCRIPT_TYPES.noTranscript + }; + } + if (isExternalAnnotation) { + var _annotation = annotations[0]; + return parseExternalAnnotations(_annotation); + } else { + tData = createTData(annotations); + return { + tData: tData, + tUrl: tUrl, + tType: TRANSCRIPT_TYPES.timedText, + tFileExt: 'json' + }; + } +} -function _createSuper$1(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$1(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } -function _isNativeReflectConstruct$1() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } -var vjsComponent = videojs.getComponent('Component'); -var VideoJSTitleLink = /*#__PURE__*/function (_vjsComponent) { - _inherits(VideoJSTitleLink, _vjsComponent); - var _super = _createSuper$1(VideoJSTitleLink); - function VideoJSTitleLink(player, options) { - var _this; - _classCallCheck(this, VideoJSTitleLink); - _this = _super.call(this, player, options); - _this.setAttribute('data-testid', 'videojs-title-link'); - _this.addClass('vjs-title-bar'); - _this.options = options; - _this.player = player; - - // Handle player reload or source change events - _this.player.on('loadstart', function () { - _this.updateComponent(); - }); - return _this; - } - _createClass(VideoJSTitleLink, [{ - key: "updateComponent", - value: function updateComponent() { - var player = this.player; - if (player && player != undefined && player.canvasLink) { - var _player$canvasLink = player.canvasLink, - label = _player$canvasLink.label, - id = _player$canvasLink.id; - var title = label; - var href = null; - /** - * Avalon canvas ids are of the form 'http://host.edu/media_objects/#mo_id/manifest/canvas/#section_id`. - * Accessible url is 'http://host.edu/media_objects/#mo_id/section/#section_id' so we convert the canvas - * id for avalon manifest, but must assume other implementers will have the id as an actionable link. - */ - if (id.includes('manifest/canvas')) { - href = id.replace('manifest/canvas', 'section'); - } else { - href = id; - } - var link = videojs.dom.createEl('a', { - className: 'vjs-title-link', - href: href, - target: '_blank', - rel: 'noreferrer noopener', - innerHTML: title - }); - if (this.el().hasChildNodes()) { - this.el().replaceChildren(link); - } else { - this.el().appendChild(link); - } +/** + * Parse annotation linking to external resources like WebVTT, SRT, Text, and + * AnnotationPage .json files + * @param {Annotation} annotation Annotation from the manifest + * @returns {Object} object with the structure { tData: [], tUrl: '', tType: '' } + */ +function parseExternalAnnotations(_x7) { + return _parseExternalAnnotations.apply(this, arguments); +} +/** + * Converts Annotation to the common format that the + * transcripts component expects + * @param {Array} annotations array of Annotations + * @returns {Array} array of JSON objects + * Structure of the JSON object is as follows; + * { + * begin: 0, + * end: 60, + * text: 'Transcript text', + * format: 'text/plain', + * } + */ +function _parseExternalAnnotations() { + _parseExternalAnnotations = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee7(annotation) { + var tData, type, tBody, tUrl, tType, tFormat, tFileExt; + return regenerator.wrap(function _callee7$(_context7) { + while (1) switch (_context7.prev = _context7.next) { + case 0: + tData = []; + type = ''; + tBody = annotation.body; + tUrl = tBody.id; + tType = tBody.type; + tFormat = tBody.format; + tFileExt = ''; + /** When external file contains text data */ + if (!(tType === 'Text')) { + _context7.next = 12; + break; + } + _context7.next = 10; + return fetch(tUrl).then(handleFetchErrors).then(function (response) { + return response.text(); + }).then(function (data) { + if (TRANSCRIPT_MIME_TYPES.webvtt.includes(tFormat) || TRANSCRIPT_MIME_TYPES.srt.includes(tFormat)) { + var parsed = parseTimedText(data, TRANSCRIPT_MIME_TYPES.srt.includes(tFormat)); + tData = parsed.tData; + type = parsed.tType; + tFileExt = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { + return tm.type.includes(tFormat); + })[0].ext; + } else { + var textLines = data.split('\n'); + tData = buildNonTimedText(textLines); + type = TRANSCRIPT_TYPES.plainText; + tFileExt = 'txt'; + } + })["catch"](function (error) { + console.error('transcript-parser -> parseExternalAnnotations() -> fetching external transcript -> ', error); + throw error; + }); + case 10: + _context7.next = 15; + break; + case 12: + if (!(tType === 'AnnotationPage')) { + _context7.next = 15; + break; + } + _context7.next = 15; + return fetch(tUrl).then(handleFetchErrors).then(function (response) { + return response.json(); + }).then(function (data) { + var annotations = getAnnotations([data], 'supplementing'); + tData = createTData(annotations); + type = TRANSCRIPT_TYPES.timedText; + tFileExt = 'json'; + })["catch"](function (error) { + console.error('transcript-parser -> parseExternalAnnotations() -> fetching annotations -> ', error); + throw error; + }); + case 15: + return _context7.abrupt("return", { + tData: tData, + tUrl: tUrl, + tType: type, + tFileExt: tFileExt + }); + case 16: + case "end": + return _context7.stop(); } + }, _callee7); + })); + return _parseExternalAnnotations.apply(this, arguments); +} +function createTData(annotations) { + var tData = []; + annotations.map(function (a) { + if (a.id != null) { + var tBody = a.body; + var _getMediaFragment = getMediaFragment(a.target), + start = _getMediaFragment.start, + end = _getMediaFragment.end; + tData.push({ + text: tBody.value, + format: tBody.format, + begin: parseFloat(start), + end: parseFloat(end), + tag: TRANSCRIPT_CUE_TYPES.timedCue + }); } - }]); - return VideoJSTitleLink; -}(vjsComponent); -vjsComponent.registerComponent('VideoJSTitleLink', VideoJSTitleLink); - -function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } -function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - -// SVG icons for zoom-in and zoom-out icons as strings -var zoomOutIconSVG = "\n\n \n \n \n \n \n \n"; -var zoomInIconSVG = "\n\n \n \n \n \n \n \n"; - -// Function to inject SVGs into the DOM -function injectSVGIcons() { - var svgContainer = document.createElement('div'); - svgContainer.style.display = 'none'; - svgContainer.innerHTML = "".concat(zoomOutIconSVG).concat(zoomInIconSVG, ""); - document.body.appendChild(svgContainer); + }); + return tData; } -// Call the function to inject SVG icons -injectSVGIcons(); -var Button = videojs.getComponent('Button'); - /** - * Custom VideoJS component for displaying track view when - * there are tracks/structure timespans in the current Canvas - * @param {Object} options - * @param {Number} options.trackScrubberRef React ref to track scrubber element - * @param {Number} options.timeToolRef React ref to time tooltip element - * @param {Boolean} options.isPlaylist flag to indicate a playlist Manifest or not + * Parsing transcript data from a given file with timed text + * @param {Object} fileData content in the transcript file + * @param {Boolean} isSRT given transcript file is an SRT + * @returns {Array} array of JSON objects of the following + * structure; + * { + * begin: '00:00:00.000', + * end: '00:01:00.000', + * text: 'Transcript text sample' + * tag: NOTE || TIMED_CUE + * } */ -var VideoJSTrackScrubber = /*#__PURE__*/function (_Button) { - _inherits(VideoJSTrackScrubber, _Button); - var _super = _createSuper(VideoJSTrackScrubber); - function VideoJSTrackScrubber(player, options) { - var _this; - _classCallCheck(this, VideoJSTrackScrubber); - _this = _super.call(this, player, options); - _this.setAttribute('data-testid', 'videojs-track-scrubber-button'); - _this.addClass('vjs-button vjs-track-scrubber'); - _this.controlText('Toggle track scrubber'); - _this.el().innerHTML = "\n \n \n "; - _this.options = options; - _this.player = player; - _this.playerInterval; - _this.zoomedOutRef = /*#__PURE__*/React.createRef(); - _this.currentTrackRef = /*#__PURE__*/React.createRef(); +function parseTimedText(fileData) { + var isSRT = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + var tData = []; + var noteLines = []; - // Attach interval on first load for time updates - _this.player.on('ready', function () { - if (_this.options.trackScrubberRef.current) { - _this.playerInterval = setInterval(function () { - _this.handleTimeUpdate(); - }, 100); - _this.attachListeners(); - } - }); + // split file content into lines + var lines = fileData.split('\n'); - /* - When player is fully built and the trackScrubber element is initialized, - call method to mount React component. - */ - _this.player.on('loadstart', function () { - if (_this.options.trackScrubberRef.current) { - _this.updateComponent(); - if (!_this.playerInterval) { - _this.playerInterval = setInterval(function () { - _this.handleTimeUpdate(); - }, 100); - } - } - }); + // For SRT files all of the file content is considered as cues + var cueLines = lines; + if (!isSRT) { + var _validateWebVTT = validateWebVTT(lines), + valid = _validateWebVTT.valid, + cue_lines = _validateWebVTT.cue_lines, + notes = _validateWebVTT.notes; + if (!valid) { + console.error('Invalid WebVTT file'); + return { + tData: [], + tType: TRANSCRIPT_TYPES.invalidVTT + }; + } + cueLines = cue_lines; + noteLines = notes; + } + var groups = groupTimedTextLines(cueLines); - // Hide track scrubber if it is displayed when player is going fullscreen - _this.player.on('fullscreenchange', function () { - if (_this.player.isFullscreen() && !_this.zoomedOutRef.current) { - var tempZoom = _this.zoomedOutRef.current; - _this.setZoomedOut(!tempZoom); - } - }); + // Add back the NOTE(s) in the header block + groups.unshift.apply(groups, _toConsumableArray(noteLines)); + var hasInvalidTimestamp = false; + for (var i = 0; i < groups.length;) { + var line = parseTimedTextLine(groups[i], isSRT); + if (!line) { + hasInvalidTimestamp || (hasInvalidTimestamp = true); + break; + } else { + tData.push(line); + i++; + } + } + return { + tData: hasInvalidTimestamp ? null : tData, + tType: hasInvalidTimestamp ? TRANSCRIPT_TYPES.invalidTimestamp : TRANSCRIPT_TYPES.timedText + }; +} - // Clean up interval when player is disposed - _this.player.on('dispose', function () { - clearInterval(_this.playerInterval); - }); - return _this; +/** + * Validate WebVTT file with its header content + * @param {Array} lines WebVTT file content split into lines + * @returns {Boolean} + */ +function validateWebVTT(lines) { + var firstLine = lines.shift().trim(); + if ((firstLine === null || firstLine === void 0 ? void 0 : firstLine.length) == 6 && firstLine === 'WEBVTT') { + var _validateWebVTTHeader = validateWebVTTHeaders(lines), + valid = _validateWebVTTHeader.valid, + cue_lines = _validateWebVTTHeader.cue_lines, + notes = _validateWebVTTHeader.notes; + return { + valid: valid, + cue_lines: cue_lines, + notes: notes + }; + } else { + return { + valid: false, + cue_lines: [], + notes: [] + }; } - _createClass(VideoJSTrackScrubber, [{ - key: "setCurrentTrack", - value: function setCurrentTrack(t) { - this.currentTrackRef.current = t; - } - }, { - key: "setZoomedOut", - value: function setZoomedOut(z) { - this.zoomedOutRef.current = z; - if (z) { - this.options.trackScrubberRef.current.classList.add('hidden'); - this.el().innerHTML = "\n \n \n "; - } else { - this.options.trackScrubberRef.current.classList.remove('hidden'); - this.el().innerHTML = "\n \n \n "; +} + +/** + * Validate the text between 'WEBVTT' at the start and start of + * VTT cues. It looks for REGION and STYLE blocks and skips over these + * blocks. This doesn't validate the content within these blocks. + * When there's text in the header not followed by the keywords REGION and + * STYLE the WebVTT file is marked invalid. + * @param {Array} lines WebVTT file content split into lines + * @returns + */ +function validateWebVTTHeaders(lines) { + var endOfHeadersIndex = 0; + var firstCueIndex = 0; + var hasTextBeforeCues = false; + var notesInHeader = []; + + // Remove line numbers for vtt cues + lines = lines.filter(function (l) { + return Number(l) ? false : true; + }); + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + // Skip REGION and STYLE blocks as these are related to displaying cues as overlays + if (/^REGION$/.test(line.toUpperCase()) || /^STYLE$/.test(line.toUpperCase())) { + // Increment until an empty line is encountered within the header block + i++; + while (i < lines.length && (!lines[i] == '\r' || !lines[i] == '\n' || !lines[i] == '\r\n')) { + i++; } + endOfHeadersIndex = i; } - }, { - key: "attachListeners", - value: function attachListeners() { - var _this2 = this; - var trackScrubberRef = this.options.trackScrubberRef; - this.updateComponent(); - if (trackScrubberRef.current) { - // Initialize the track scrubber's current time and duration - this.populateTrackScrubber(); - this.updateTrackScrubberProgressBar(); - var pointerDragged = false; - // Attach mouse pointer events to track scrubber progress bar - var _trackScrubberRef$cur = _slicedToArray(trackScrubberRef.current.children, 3); - _trackScrubberRef$cur[0]; - var progressBar = _trackScrubberRef$cur[1]; - _trackScrubberRef$cur[2]; - progressBar.addEventListener('mouseenter', function (e) { - _this2.handleMouseMove(e); - }); - /* - Using pointerup, pointermove, pointerdown events instead of - mouseup, mousemove, mousedown events to make it work with both - mouse pointer and touch events - */ - progressBar.addEventListener('pointerup', function (e) { - if (pointerDragged) { - _this2.handleSetProgress(e); - } - }); - progressBar.addEventListener('pointermove', function (e) { - _this2.handleMouseMove(e); - pointerDragged = true; - }); - progressBar.addEventListener('pointerdown', function (e) { - // Only handle left click event - if (e.which === 1) { - _this2.handleSetProgress(e); - pointerDragged = false; - } - }); + // Gather comments presented as NOTE(s) in the header block to be displayed as transcript + else if (/^NOTE$/.test(line.toUpperCase())) { + var noteText = line; + i++; + // Increment until an empty line is encountered within the NOTE block + while (i < lines.length && (!lines[i] == '\r' || !lines[i] == '\n' || !lines[i] == '\r\n')) { + noteText = "".concat(noteText, "
    ").concat(lines[i].trim()); + i++; } + notesInHeader.push({ + times: '', + line: noteText, + tag: TRANSCRIPT_CUE_TYPES.note + }); } - }, { - key: "updateComponent", - value: function updateComponent() { - // Reset refs to initial value - this.zoomedOutRef.current = true; - this.currentTrackRef.current = {}; - } - - /** - * Keydown event handler for the track button on the player controls, - * when using keyboard navigation - * @param {Event} e keydown event - */ - }, { - key: "handleKeyDown", - value: function handleKeyDown(e) { - if (e.which === 32 || e.which === 13) { - e.preventDefault(); - this.handleTrackScrubberClick(); - e.stopPropagation(); - } + // Terminate validation once the first cue is reached + else if (line.includes('-->')) { + // Break the loop when it reaches the first vtt cue + firstCueIndex = i; + break; } - }, { - key: "handleClick", - value: function handleClick() { - this.handleTrackScrubberClick(); + // Flag to check for invalid text before cue lines + else if (typeof line === 'string' && line.trim().length != 0) { + hasTextBeforeCues = true; } + } - /** - * Click event handler for the track button on the player controls - */ - }, { - key: "handleTrackScrubberClick", - value: function handleTrackScrubberClick() { - var currentTrackRef = this.currentTrackRef, - player = this.player, - options = this.options; - // When player is not fully loaded on the page don't show the track scrubber - if (!options.trackScrubberRef.current || !currentTrackRef.current) return; + // Return the cues and comments in the header block when the given WebVTT is valid + if (firstCueIndex > endOfHeadersIndex && !hasTextBeforeCues) { + return { + valid: true, + cue_lines: lines.slice(firstCueIndex), + notes: notesInHeader + }; + } else { + return { + valid: false + }; + } +} - // If player is fullscreen exit before displaying track scrubber - if (player.isFullscreen()) { - player.exitFullscreen(); +/** + * Group multi line transcript text values alongside the relevant + * timestamp values. E.g. converts, + * [ + * "00:00:00.000 --> 00:01:00.000", "Transcript", " from multiple lines", + * "00:03:00.000 --> 00:04:00.000", "Next transcript text", + * "NOTE This is a comment" + * ] + * into + * [ + * { times: "00:00:00.000 --> 00:01:00.000", line: "Transcript from multiple lines", tag: "TIMED_CUE" }, + * { times: "00:03:00.000 --> 00:04:00.000", line: "Next transcript text", tag: "TIMED_CUE" }, + * { times: "", line: "NOTE This is a comment", tag: "NOTE" } + * ] + * @param {Array} lines array of lines in the WebVTT file + * @returns {Array} + */ +function groupTimedTextLines(lines) { + var groups = []; + var i; + for (i = 0; i < lines.length; i++) { + var line = lines[i]; + var t = {}; + if (line.includes('-->') || /^NOTE/.test(line)) { + var isNote = /^NOTE/.test(line); + t.times = isNote ? "" : line; + t.tag = isNote ? TRANSCRIPT_CUE_TYPES.note : TRANSCRIPT_CUE_TYPES.timedCue; + // Make sure there is a single space separating NOTE from the comment for single or multi-line comments + t.line = isNote ? line.replace(/^NOTE\s*/, 'NOTE ') : ''; + i++; + + // Increment until an empty line is encountered marking the end of the block + while (i < lines.length && !(lines[i] == '\r' || lines[i] == '\n' || lines[i] == '\r\n' || lines[i] == '')) { + t.line += lines[i].endsWith('-') ? lines[i] : lines[i].replace(/\s*$/, ' '); + i++; } - var tempZoom = this.zoomedOutRef.current; - this.setZoomedOut(!tempZoom); + t.line = t.line.trimEnd(); + groups.push(t); } + } + return groups; +} - /** - * Event handler for VideoJS player instance's 'timeupdate' event, which - * updates the track scrubber from player state. - */ - }, { - key: "handleTimeUpdate", - value: function handleTimeUpdate() { - var _player$markers$getMa; - var player = this.player, - options = this.options, - zoomedOutRef = this.zoomedOutRef; - // Hide track-scrubber for inaccessible item if it is open - if (player.canvasIsEmpty && !zoomedOutRef.current) { - this.setZoomedOut(true); - } - if (player.isDisposed() || player.ended()) return; - /* - Get the current track from the player.markers created from the structure timespans. - In playlists, markers are timepoint information representing highlighting annotations, - therefore omit reading markers information for track scrubber in playlist contexts. - */ - var playerCurrentTime = player.currentTime(); - if (player.markers && typeof player.markers !== 'function' && typeof player.markers.getMarkers === 'function' && ((_player$markers$getMa = player.markers.getMarkers()) === null || _player$markers$getMa === void 0 ? void 0 : _player$markers$getMa.length) > 0 && !options.isPlaylist) { - this.readPlayerMarkers(); - } else { - var _player$playableDurat, _player$altStart; - this.setCurrentTrack({ - duration: (_player$playableDurat = player.playableDuration) !== null && _player$playableDurat !== void 0 ? _player$playableDurat : player.duration(), - time: (_player$altStart = player.altStart) !== null && _player$altStart !== void 0 ? _player$altStart : 0, - key: '', - text: 'Complete media file' - }); - playerCurrentTime = player.srcIndex && player.srcIndex > 0 ? playerCurrentTime + player.altStart : playerCurrentTime; - } - this.updateTrackScrubberProgressBar(playerCurrentTime); - } - /** - * Calculate the progress and current time within the track and - * update them accordingly when the player's 'timeupdate' event fires. - * @param {Number} currentTime player's current time - */ - }, { - key: "updateTrackScrubberProgressBar", - value: function updateTrackScrubberProgressBar() { - var currentTime = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; - var player = this.player, - currentTrackRef = this.currentTrackRef; - // Handle Safari which emits the timeupdate event really quickly - if (!currentTrackRef.current) { - if (player.markers && typeof player.markers.getMarkers === 'function') { - this.readPlayerMarkers(); - } - } - var altStart = player.altStart, - srcIndex = player.srcIndex; - // Calculate corresponding time and played percentage values within track - var trackoffset = srcIndex > 0 ? currentTime - currentTrackRef.current.time + altStart : currentTime - currentTrackRef.current.time; - var trackpercent = Math.min(100, Math.max(0, 100 * trackoffset / currentTrackRef.current.duration)); - this.populateTrackScrubber(trackoffset, trackpercent); - } - }, { - key: "populateTrackScrubber", - value: - /** - * Update the track scrubber's current time, duration and played percentage - * when it is visible in UI. - * @param {Number} currentTime current time corresponding to the track - * @param {Number} playedPercentage elapsed time percentage of the track duration - */ - function populateTrackScrubber() { - var currentTime = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; - var playedPercentage = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; - var trackScrubberRef = this.options.trackScrubberRef; - if (!trackScrubberRef.current) { - return; +/** + * Create a JSON object from the transcript data + * @param {Object} obj + * @param {String} obj.times string with time information + * @param {String} obj.line string with transcript text + * @returns {Object} of the format; + * { + * begin: 0, + * end: 60, + * text: 'Transcript text sample', + * tag: NOTE || TIMED_CUE + * } + */ +function parseTimedTextLine(_ref, isSRT) { + var times = _ref.times, + line = _ref.line, + tag = _ref.tag; + var timestampRegex; + if (isSRT) { + // SRT allows using comma for milliseconds while WebVTT does not + timestampRegex = SRT_TIMESTAMP_REGEX; + } else { + timestampRegex = VTT_TIMESTAMP_REGEX; + } + switch (tag) { + case TRANSCRIPT_CUE_TYPES.note: + return { + begin: 0, + end: 0, + text: line, + tag: tag + }; + case TRANSCRIPT_CUE_TYPES.timedCue: + var _times$split = times.split(' --> '), + _times$split2 = _slicedToArray(_times$split, 2), + start = _times$split2[0], + end = _times$split2[1]; + // FIXME:: remove any styles for now, refine this + end = end.split(' ')[0]; + if (!start.match(timestampRegex) || !end.match(timestampRegex)) { + console.error('Invalid timestamp in line with text; ', line); + return null; } - var _trackScrubberRef$cur2 = _slicedToArray(trackScrubberRef.current.children, 3), - currentTimeDisplay = _trackScrubberRef$cur2[0]; - _trackScrubberRef$cur2[1]; - var durationDisplay = _trackScrubberRef$cur2[2]; - - // Set the elapsed time percentage in the progress bar of track scrubber - document.documentElement.style.setProperty('--range-scrubber', "calc(".concat(playedPercentage, "%)")); + return { + begin: timeToS(start), + end: timeToS(end), + text: line, + tag: tag + }; + default: + return null; + } +} - // Update the track duration - durationDisplay.innerHTML = timeToHHmmss(this.currentTrackRef.current.duration); - // Update current time elapsed within the current track - var cleanTime = !isNaN(currentTime) && currentTime > 0 ? currentTime : 0; - currentTimeDisplay.innerHTML = timeToHHmmss(cleanTime); - } - }, { - key: "readPlayerMarkers", - value: function readPlayerMarkers() { - var tracks = this.player.markers.getMarkers().filter(function (m) { - return m["class"] == 'ramp--track-marker--fragment'; +/** + * Parse the content search response from the search service, and then use it to calculate + * number of search hits for each transcripts, and create a list of matched transcript + * lines for the search in the current transcript + * @param {Object} response JSON response from content search API + * @param {String} query search query from transcript search + * @param {Array} trancripts content of the displayed transcript with ids + * @param {String} selectedTranscript url of the selected transcript + * @returns a list of matched transcript lines for the current search + */ +var parseContentSearchResponse = function parseContentSearchResponse(response, query, trancripts, selectedTranscript) { + var _response$items; + if (!response || response === undefined) return []; + var hitCounts = []; + var searchHits = []; + if (((_response$items = response.items) === null || _response$items === void 0 ? void 0 : _response$items.length) > 0) { + var items = response.items; + items.map(function (item) { + var anno = new Annotation(item); + // Exclude annotations without supplementing motivation + if (anno.getMotivation() != 'supplementing') return; + var target = anno.getTarget(); + var targetURI = getCanvasId(target); + var value = anno.getBody()[0].getProperty('value'); + var hitCount = getHitCountForCue(value, query, true); + searchHits.push({ + target: target, + targetURI: targetURI, + value: value, + hitCount: hitCount }); - if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0) { - this.setCurrentTrack(tracks[0]); - } - } - }, { - key: "handleMouseMove", - value: - /** - * Event handler for mouseenter and mousemove pointer events on the - * the track scrubber. This sets the time tooltip value and its offset - * position in the UI. - * @param {Event} e pointer event for user interaction - */ - function handleMouseMove(e) { - var timeToolRef = this.options.timeToolRef; - if (!timeToolRef.current) { - return; - } - var time = this.getTrackTime(e); - - // When hovering over the border of the track scrubber, convertTime() returns infinity, - // since e.target.clientWidth is zero. Use this value to not show the tooltip when this - // occurs. - if (isFinite(time)) { - // Calculate the horizontal position of the time tooltip using the event's offsetX property - var offset = e.offsetX - timeToolRef.current.offsetWidth / 2; // deduct 0.5 x width of tooltip element - timeToolRef.current.style.left = offset + 'px'; + }); + } + // Group search responses by transcript + var allSearchHits = groupBy(searchHits, 'targetURI'); - // Set text in the tooltip as the time relevant to the pointer event's position - timeToolRef.current.innerHTML = timeToHHmmss(time); - } - } - }, { - key: "handleSetProgress", - value: - /** - * Event handler for mousedown event on the track scrubber. This sets the - * progress percentage within track scrubber and update the player's current time - * when user clicks on a point within the track scrubber. - * @param {Event} e pointer event for user interaction - */ - function handleSetProgress(e) { - var currentTrackRef = this.currentTrackRef, - player = this.player; - if (!currentTrackRef.current) { - return; - } - var trackoffset = this.getTrackTime(e); - if (trackoffset != undefined) { - // Calculate percentage of the progress based on the pointer position's - // time and duration of the track - var trackpercent = Math.min(100, Math.max(0, 100 * (trackoffset / currentTrackRef.current.duration))); + // Calculate search hit count for each transcript in the Canvas + for (var _i = 0, _Object$entries = Object.entries(allSearchHits); _i < _Object$entries.length; _i++) { + var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), + key = _Object$entries$_i[0], + value = _Object$entries$_i[1]; + hitCounts.push({ + transcriptURL: key, + numberOfHits: value.reduce(function (acc, a) { + return acc + a.hitCount; + }, 0) + }); + } - // Set the elapsed time in the scrubber progress bar - document.documentElement.style.setProperty('--range-scrubber', "calc(".concat(trackpercent, "%)")); + // Get all the matching transcript lines with the query in the current transcript + var matchedTranscriptLines = getMatchedTranscriptLines(allSearchHits[selectedTranscript], query, trancripts); + return { + matchedTranscriptLines: matchedTranscriptLines, + hitCounts: hitCounts, + allSearchHits: allSearchHits + }; +}; - // Set player's current time with respective to the altStart for clipped items - var playerCurrentTime = player.isClipped ? trackoffset + currentTrackRef.current.time : trackoffset; - player.currentTime(playerCurrentTime); - } - } - }, { - key: "getTrackTime", - value: - /** - * Convert pointer position on track scrubber to a time value - * @param {Event} e pointer event for user interaction - * @returns {Number} time corresponding to the pointer position - */ - function getTrackTime(e) { - var currentTrackRef = this.currentTrackRef; - if (!currentTrackRef.current) { - return; +/** + * Create a list matched transcript lines for the current search for the displayed transcript + * @param {Array} searchHits a list of matched transcript lines with ids from the current transcript + * @param {String} query search query + * @param {Array} transcripts list of all the transcript lines from the current transcript + * @returns a list of matched transcrip lines in the current transcript + */ +var getMatchedTranscriptLines = function getMatchedTranscriptLines(searchHits, query, transcripts) { + var qStr = query.trim().toLocaleLowerCase(); + var transcriptLines = []; + if (searchHits === undefined) return; + var traversedIds = []; + searchHits.map(function (item, index) { + var target = item.target, + value = item.value; + // Read time offsets and text of the search hit + var timeRange = getMediaFragment(target); + + // Replace all HTML tags + var mappedText = value.replace(/<\/?[^>]+>/gi, ''); + var start = 0, + end = 0; + var transcriptId = undefined; + if (timeRange != undefined) { + // For timed-text + start = timeRange.start; + end = timeRange.end; + transcriptId = transcripts.findIndex(function (t) { + return t.begin == start && t.end == end; + }); + var queryText = qStr.match(/[a-zA-Z]+/gi) ? qStr.match(/[a-zA-Z]+/gi)[0] : qStr; + var matchOffset = mappedText.toLocaleLowerCase().indexOf(queryText); + if (matchOffset !== -1 && transcriptId != undefined) { + var match = markMatchedParts(value, qStr, item.hitCount, true); + transcriptLines.push({ + tag: TRANSCRIPT_CUE_TYPES.timedCue, + begin: start, + end: end, + id: transcriptId, + match: match, + matchCount: item.hitCount, + text: value + }); } - var offsetx = e.offsetX; - if (offsetx && offsetx != undefined) { - var time = offsetx / e.target.clientWidth * currentTrackRef.current.duration; - return time; + } else { + /** + * For non timed text, there's no unique id to match the search response to the transcript + * lines in the UI. So use filter() method instead of findIndex() method to get all matching + * transcript lines in the display. + * Use traversedIds array to remember the ids of already processed transcript lines in the list + * to avoid duplication in the matches. + */ + var hitsInfo = matchPartsInUntimedText(transcripts, mappedText, qStr, traversedIds); + traversedIds = hitsInfo.traversedIds; + transcriptLines = [].concat(_toConsumableArray(transcriptLines), _toConsumableArray(hitsInfo.hits)); + + /** + * When backend has a single block of text which is chuncked in the UI this helps to + * traverse all transcript cues. + */ + while (index === searchHits.length - 1 && ((_traversedIds = traversedIds) === null || _traversedIds === void 0 ? void 0 : _traversedIds.length) < transcripts.length) { + var _traversedIds; + var _hitsInfo = matchPartsInUntimedText(transcripts, mappedText, qStr, traversedIds); + traversedIds = _hitsInfo.traversedIds; + transcriptLines = [].concat(_toConsumableArray(transcriptLines), _toConsumableArray(_hitsInfo.hits)); } } - }]); - return VideoJSTrackScrubber; -}(Button); -videojs.registerComponent('VideoJSTrackScrubber', VideoJSTrackScrubber); + }); + return transcriptLines; +}; -function _createForOfIteratorHelper$2(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$2(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } -function _unsupportedIterableToArray$2(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$2(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$2(o, minLen); } -function _arrayLikeToArray$2(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } -function ownKeys$4(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } -function _objectSpread$4(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$4(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$4(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } -require('@silvermine/videojs-quality-selector')(videojs); -// import vjsYo from './vjsYo'; +/** + * Build a list of matched indexed transcript lines from content search response. + * In Avalon, docx and plain text files are chunked by paragraphs seperated by 2 or + * more new line characters. So, depending on the way the file is formatted the search + * response could include chunks of the text or the full text. + * In the library (mammoth) used in Transcript component to display docx files; the text is chunked + * into paragraphs seperated by one or more new line characters. + * And the search response doesn't include any text styling in the docx files. Therefore the + * text with style information is reformatted to include text highlights from the search response. + * This function uses the search response to calculate the hit counts and mark them for each indexed transcript + * line in the front-end to get the correct counts. + * @param {Array} transcripts indexed transcript text in UI + * @param {String} mappedText matched text from content search + * @param {String} query search query entered by the user + * @param {Array} traversedIds already included transcript indices + * @returns a list of matched transcript lines + */ +var matchPartsInUntimedText = function matchPartsInUntimedText(transcripts, mappedText, query, traversedIds) { + var escapedQ = buildRegexReadyText(query, true, false); + // Get hit counts for the current text, ignore matches with query preceded by - or ' + var qRegex = new RegExp(String.raw(_templateObject$1 || (_templateObject$1 = _taggedTemplateLiteral(["\b", "\b"], ["\\b", "\\b"])), escapedQ), 'gi'); + var matched = []; + // Start from the next cue after the last traveresed cue in the transcript + var lastTraversedId = traversedIds[traversedIds.length - 1] + 1 || 0; -function VideoJSPlayer(_ref) { - var isVideo = _ref.isVideo; - _ref.hasMultipleCanvases; - var isPlaylist = _ref.isPlaylist, - trackScrubberRef = _ref.trackScrubberRef, - scrubberTooltipRef = _ref.scrubberTooltipRef, - tracks = _ref.tracks, - placeholderText = _ref.placeholderText, - renderingFiles = _ref.renderingFiles, - enableFileDownload = _ref.enableFileDownload, - loadPrevOrNext = _ref.loadPrevOrNext, - lastCanvasIndex = _ref.lastCanvasIndex, - enableTitleLink = _ref.enableTitleLink, - videoJSLangMap = _ref.videoJSLangMap, - options = _ref.options; - var playerState = usePlayerState(); - var playerDispatch = usePlayerDispatch(); - var manifestState = useManifestState(); - var manifestDispatch = useManifestDispatch(); - var canvasDuration = manifestState.canvasDuration, - canvasIndex = manifestState.canvasIndex, - canvasLink = manifestState.canvasLink, - currentNavItem = manifestState.currentNavItem, - hasMultiItems = manifestState.hasMultiItems, - srcIndex = manifestState.srcIndex, - targets = manifestState.targets, - autoAdvance = manifestState.autoAdvance, - playlist = manifestState.playlist, - structures = manifestState.structures, - canvasSegments = manifestState.canvasSegments, - hasStructure = manifestState.hasStructure, - canvasIsEmpty = manifestState.canvasIsEmpty; - var isClicked = playerState.isClicked, - isEnded = playerState.isEnded, - isPlaying = playerState.isPlaying, - player = playerState.player, - searchMarkers = playerState.searchMarkers, - currentTime = playerState.currentTime; - var _React$useState = React.useState(canvasIndex), - _React$useState2 = _slicedToArray(_React$useState, 2); - _React$useState2[0]; - var _setCIndex = _React$useState2[1]; - var _React$useState3 = React.useState(false), - _React$useState4 = _slicedToArray(_React$useState3, 2), - isReady = _React$useState4[0], - _setIsReady = _React$useState4[1]; - var _React$useState5 = React.useState(''), - _React$useState6 = _slicedToArray(_React$useState5, 2), - activeId = _React$useState6[0], - _setActiveId = _React$useState6[1]; - var _useLocalStorage = useLocalStorage('startVolume', 1), - _useLocalStorage2 = _slicedToArray(_useLocalStorage, 2), - startVolume = _useLocalStorage2[0], - setStartVolume = _useLocalStorage2[1]; - var _useLocalStorage3 = useLocalStorage('startQuality', null), - _useLocalStorage4 = _slicedToArray(_useLocalStorage3, 2), - startQuality = _useLocalStorage4[0], - setStartQuality = _useLocalStorage4[1]; - var _useLocalStorage5 = useLocalStorage('startMuted', false), - _useLocalStorage6 = _slicedToArray(_useLocalStorage5, 2), - startMuted = _useLocalStorage6[0], - setStartMuted = _useLocalStorage6[1]; - var _useLocalStorage7 = useLocalStorage('startCaptioned', true), - _useLocalStorage8 = _slicedToArray(_useLocalStorage7, 2), - startCaptioned = _useLocalStorage8[0], - setStartCaptioned = _useLocalStorage8[1]; - var _React$useState7 = React.useState(null), - _React$useState8 = _slicedToArray(_React$useState7, 2), - fragmentMarker = _React$useState8[0], - setFragmentMarker = _React$useState8[1]; - var _React$useState9 = React.useState(CANVAS_MESSAGE_TIMEOUT / 1000), - _React$useState10 = _slicedToArray(_React$useState9, 2), - messageTime = _React$useState10[0], - setMessageTime = _React$useState10[1]; - var videoJSRef = React.useRef(null); - var playerRef = React.useRef(null); - var autoAdvanceRef = React.useRef(); - autoAdvanceRef.current = autoAdvance; - var srcIndexRef = React.useRef(); - srcIndexRef.current = srcIndex; - var activeIdRef = React.useRef(); - activeIdRef.current = activeId; - var setActiveId = function setActiveId(id) { - _setActiveId(id); - activeIdRef.current = id; - }; - var currentTimeRef = React.useRef(); - currentTimeRef.current = currentTime; - var isReadyRef = React.useRef(); - isReadyRef.current = isReady; - var setIsReady = function setIsReady(r) { - _setIsReady(r); - isReadyRef.current = r; - }; - var currentNavItemRef = React.useRef(); - currentNavItemRef.current = currentNavItem; - var canvasIsEmptyRef = React.useRef(); - canvasIsEmptyRef.current = canvasIsEmpty; - var canvasDurationRef = React.useRef(); - canvasDurationRef.current = canvasDuration; - var canvasLinkRef = React.useRef(); - canvasLinkRef.current = canvasLink; - var isPlayingRef = React.useRef(); - isPlayingRef.current = isPlaying; - var isEndedRef = React.useRef(); - isEndedRef.current = isEnded; - var cIndexRef = React.useRef(); - cIndexRef.current = canvasIndex; - var setCIndex = function setCIndex(i) { - _setCIndex(i); - cIndexRef.current = i; + /** + * For untimed text the search response text could be either, + * - mapped one to one with the cue text in Transcript component + * - include a part of the cue text in Transcript component + * When none of these work check if the cue text contains the search query + */ + for (var i = lastTraversedId; i < transcripts.length; i++) { + var t = transcripts[i]; + var cleanedText = t.text.replace(/<\/?[^>]+>/gi, '').trim(); + var matches = _toConsumableArray(cleanedText.matchAll(qRegex)); + var mappedTextCleaned = mappedText.trim(); + if (mappedTextCleaned == cleanedText || mappedTextCleaned.includes(cleanedText) && (matches === null || matches === void 0 ? void 0 : matches.length) > 0) { + t.matchCount = matches === null || matches === void 0 ? void 0 : matches.length; + matched.push(t); + traversedIds.push(t.id); + break; + } else if ((matches === null || matches === void 0 ? void 0 : matches.length) > 0) { + var _ref2; + t.matchCount = (_ref2 = _toConsumableArray(mappedTextCleaned.matchAll(qRegex))) === null || _ref2 === void 0 ? void 0 : _ref2.length; + matched.push(t); + traversedIds.push(t.id); + break; + } else { + traversedIds.push(t.id); + } + } + var hits = []; + matched.map(function (m) { + var value = addStyledHighlights(m.textDisplayed, query); + var match = markMatchedParts(value, query, m.matchCount, true); + hits.push({ + tag: TRANSCRIPT_CUE_TYPES.nonTimedLine, + begin: undefined, + end: undefined, + id: m.id, + match: match, + matchCount: m.matchCount, + text: value + }); + }); + return { + hits: hits, + traversedIds: traversedIds }; - var captionsOnRef = React.useRef(); - var activeTrackRef = React.useRef(); - var canvasSegmentsRef = React.useRef(); - canvasSegmentsRef.current = canvasSegments; - var structuresRef = React.useRef(); - structuresRef.current = structures; - var messageIntervalRef = React.useRef(null); - - // Dispose Video.js instance when VideoJSPlayer component is removed - React.useEffect(function () { - return function () { - if (playerRef.current != null) { - playerRef.current.dispose(); - document.removeEventListener('keydown', playerHotKeys); - setIsReady(false); - } - }; - }, []); +}; +/** + * Generic function to mark the matched transcript text in the cue where the output has + * surrounding the matched parts + * within the cue. + * @param {String} text matched transcript text/cue + * @param {String} query current search query + * @param {Numner} hitCount number of hits returned in the search response + * @param {Boolean} hasHighlight boolean flag to indicate text has tags + * @returns matched cue with HTML tags added for marking the hightlight + */ +var markMatchedParts = function markMatchedParts(text, query, hitCount) { + var hasHighlight = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; + if (text === undefined || !text) return; + var count = 0; + var replacerFn = function replacerFn(match) { + var cleanedMatch = match.replace(/<\/?[^>]+>/gi, ''); + // Only add highlights to search hits in the search response + if (count < hitCount) { + count++; + return "".concat(cleanedMatch, ""); + } else { + return cleanedMatch; + } + }; + var queryFormatted = query; /** - * Initialize Video.js when for the first page load or update - * src and other properties of the existing Video.js instance - * on Canvas change + * Content search response for a phrase search like 'Mr. Bungle' gives the response + * with highlights in the matched text as Mr. Bungle. + * So reconstruct the search query in the UI to match this phrase in the response. */ - React.useEffect(function () { - var _options$sources, _options$sources2; - setCIndex(canvasIndex); + if (hasHighlight) { + queryFormatted = buildRegexReadyText(query); + } - // Set selected quality from localStorage in Video.js options - setSelectedQuality(options.sources); + /** + * Content search API returns cues including "Mr. Bungle" as matches for both search queries + * "mr bungle" and "mr. bungle". + * When "mr bungle" is searched this function handles highlighting since the regex fails to + * identify the matches in the cues. + */ + var altReplace = function altReplace() { + var matches = _toConsumableArray(text.matchAll(/<\/?[^>]+>/gi)); + if ((matches === null || matches === void 0 ? void 0 : matches.length) === 0) return; + var startIndex = 0; + var newStr = ''; + for (var j = 0; j < matches.length && count < hitCount;) { + // Set offset to count matches based on the # of words in the phrase search query + var splitQ = query.split(/[\s-,\?]/); + var offset = (splitQ === null || splitQ === void 0 ? void 0 : splitQ.length) > 0 ? (splitQ === null || splitQ === void 0 ? void 0 : splitQ.length) * 2 - 1 : 1; + if (matches[j] === undefined && matches[j + offset] === undefined) return; - // Video.js player is only initialized on initial page load - if (!playerRef.current && ((_options$sources = options.sources) === null || _options$sources === void 0 ? void 0 : _options$sources.length) > 0) { - videojs.addLanguage(options.language, JSON.parse(videoJSLangMap)); - buildTracksHTML(); - - // Turn Video.js logging off and handle errors in this code, to avoid - // cluttering the console when loading inaccessible items. - videojs.log.level('off'); - var _player = playerRef.current = videojs(videoJSRef.current, options, function () { - playerInitSetup(playerRef.current); - }); - - /* Another way to add a component to the controlBar */ - // player.getChild('controlBar').addChild('vjsYo', {}); - - playerDispatch({ - player: _player, - type: 'updatePlayer' - }); - - // Update player status in state only when pause is initiate by the user - _player.controlBar.getChild('PlayToggle').on('pointerdown', function () { - handlePause(); - }); - _player.on('pointerdown', function (e) { - var elementTag = e.target.nodeName.toLowerCase(); - if (elementTag == 'video') { - handlePause(); - } - }); - } else if (playerRef.current && ((_options$sources2 = options.sources) === null || _options$sources2 === void 0 ? void 0 : _options$sources2.length) > 0) { - var _player2$markers; - // Update the existing Video.js player on consecutive Canvas changes - var _player2 = playerRef.current; - - // Reset markers - if (activeIdRef.current) (_player2$markers = _player2.markers) === null || _player2$markers === void 0 ? void 0 : _player2$markers.removeAll(); - setActiveId(null); - - // Block player while metadata is loaded when canvas is not empty - if (!canvasIsEmptyRef.current) { - _player2.addClass('vjs-disabled'); - setIsReady(false); - updatePlayer(_player2); - playerLoadedMetadata(_player2); - playerDispatch({ - player: _player2, - type: 'updatePlayer' - }); - } else { - // Mark as ready to for inaccessible canvas (empty) - setIsReady(true); + // Indices of start and end of the highlighted text including tags + var firstIndex = matches[j].index; + var lastIndex = matches[j + offset].index + matches[j + offset][0].length; + var prefix = text.slice(startIndex, firstIndex); + var cleanedMatch = text.slice(firstIndex, lastIndex).replace(/<\/?[^>]+>/gi, ''); + newStr = "".concat(newStr).concat(prefix, "").concat(cleanedMatch, ""); + startIndex = lastIndex; + j = +(offset + 1); + count++; + if (j == matches.length) { + newStr = "".concat(newStr).concat(text.slice(startIndex)); } } - }, [options.sources, videoJSRef]); - React.useEffect(function () { - // Clear existing interval for inaccessible message display - clearDisplayTimeInterval(); - if (playerRef.current) { - // Show/hide control bar for valid/inaccessible items respectively - if (canvasIsEmptyRef.current) { - var _currentNavItemRef$cu; - // Set the player's aspect ratio to video - playerRef.current.audioOnlyMode(false); - playerRef.current.canvasIsEmpty = true; - playerRef.current.aspectRatio('16:9'); - playerRef.current.controlBar.addClass('vjs-hidden'); - playerRef.current.removeClass('vjs-disabled'); - playerRef.current.pause(); - /** - * Update the activeId to update the active item in the structured navigation. - * For playable items this is updated in the timeupdate handler. - */ - setActiveId((_currentNavItemRef$cu = currentNavItemRef.current) === null || _currentNavItemRef$cu === void 0 ? void 0 : _currentNavItemRef$cu.id); - } else { - // Reveal control bar; needed when loading a Canvas after an inaccessible item - playerRef.current.controlBar.removeClass('vjs-hidden'); - } + return newStr; + }; + try { + var _ref3; + var queryRegex = new RegExp(String.raw(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["", ""])), queryFormatted), 'gi'); + if (((_ref3 = _toConsumableArray(text.matchAll(queryRegex))) === null || _ref3 === void 0 ? void 0 : _ref3.length) === 0) { + var highlighted = altReplace(); + return highlighted; + } else { + return text.replace(queryRegex, replacerFn); } + } catch (e) { + console.log('Error building RegExp for query: ', query); + } +}; - // Start interval for inaccessible message display - if (canvasIsEmptyRef.current && !messageIntervalRef.current) { - setMessageTime(CANVAS_MESSAGE_TIMEOUT / 1000); - createDisplayTimeInterval(); - } - }, [cIndexRef.current, canvasIsEmptyRef.current, currentNavItemRef.current]); +/** + * For docx files the content search response text doesn't have the formatted + * styles in the Word document (e.g. bold text wrapped in tags). So, + * use the styled text formatted with mammoth in the UI to add highlights from + * the content search response. + * @param {String} text string to be formatted + * @param {String} query string to find and replace with tags + * @returns a string formatted with highlights + */ +var addStyledHighlights = function addStyledHighlights(text, query) { + if (text === undefined || !text) return; + var replacerFn = function replacerFn(match) { + var cleanedMatch = buildRegexReadyText(match, false, true); + return cleanedMatch; + }; - /** - * Clear/create display timer interval when auto-advance is turned - * off/on respectively - */ - React.useEffect(function () { - if (!autoAdvance) { - clearDisplayTimeInterval(); - } else if (autoAdvance && !messageIntervalRef.current && canvasIsEmpty) { - setMessageTime(CANVAS_MESSAGE_TIMEOUT / 1000); - createDisplayTimeInterval(); - } - }, [autoAdvance]); + // Regex to get matches in the text while ignoring matches with query preceded by - or ' + var queryregex = new RegExp(String.raw(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["\b", "\b"], ["\\b", "\\b"])), buildRegexReadyText(query, true, false)), 'gi'); + var styled = text.replace(queryregex, replacerFn); + return styled; +}; - // update markers in player - React.useEffect(function () { - if (playerRef.current && playerRef.current.markers && isReadyRef.current) { - var _playlist$markers, _playerRef$current$ma; - // markers plugin not yet initialized - if (typeof playerRef.current.markers === 'function') { - playerRef.current.markers({ - markerTip: { - display: false, - // true, - text: function text(marker) { - return marker.text; - } - }, - markerStyle: {}, - markers: [] - }); - } - var playlistMarkers = []; - if (playlist !== null && playlist !== void 0 && (_playlist$markers = playlist.markers) !== null && _playlist$markers !== void 0 && _playlist$markers.length) { - var canvasMarkers = playlist.markers.filter(function (m) { - return m.canvasIndex === canvasIndex; - })[0].canvasMarkers; - playlistMarkers = canvasMarkers.map(function (m) { - return { - time: parseFloat(m.time), - text: m.value, - "class": 'ramp--track-marker--playlist' - }; - }); - } - (_playerRef$current$ma = playerRef.current.markers) === null || _playerRef$current$ma === void 0 ? void 0 : _playerRef$current$ma.removeAll(); - playerRef.current.markers.add([].concat(_toConsumableArray(fragmentMarker ? [fragmentMarker] : []), _toConsumableArray(searchMarkers), _toConsumableArray(playlistMarkers))); - } - }, [fragmentMarker, searchMarkers, canvasDuration, canvasIndex, playerRef.current, isReadyRef.current]); +/** + * Format a given string by escaping punctuations characters and grouping + * punctuations and text, to make it feasible to be used to build a regular + * expression accurately. + * @param {String} text string to be formatted with hightlights + * @param {Boolean} regExpReady flag to indicate the usage of the output as a regular exp + * @param {Boolean} addHightlight flag to indicate to/not to add tags + * @returns string with tags + */ +var buildRegexReadyText = function buildRegexReadyText(text) { + var regExpReady = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + var addHightlight = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; + // Text matches in the string + var matches = _toConsumableArray(text.matchAll(/[a-zA-Z']+/gi)); + // Punctuation matches in the string + var punctuationMatches = _toConsumableArray(text.matchAll(/([.+?"^${}\-|[\]\\])/g)); /** - * Build track HTML for Video.js player on initial page load + * If no punctuations are found within the text return text with highlights + * For RegExp ready strings: ignore matches followed by - or ' + * e.g. omit matches as "Bungle's" when search query is "bungle" */ - var buildTracksHTML = function buildTracksHTML() { - if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0 && videoJSRef.current) { - tracks.map(function (t) { - var trackEl = document.createElement('track'); - trackEl.setAttribute('key', t.key); - trackEl.setAttribute('src', t.src); - trackEl.setAttribute('kind', t.kind); - trackEl.setAttribute('label', t.label); - trackEl.setAttribute('srclang', t.srclang); - videoJSRef.current.appendChild(trackEl); - }); + if ((punctuationMatches === null || punctuationMatches === void 0 ? void 0 : punctuationMatches.length) === 0) { + var textFormatted = addHightlight ? text.split(' ').map(function (t) { + return "".concat(t, ""); + }).join(' ') : text; + var textRegex = regExpReady ? "".concat(textFormatted, "(?!['w*])") : textFormatted; + return textRegex; + } + var highlighted = ''; + var startIndex = 0; + var i = 0; + while (i < matches.length) { + var match = matches[i]; + var textMatch = addHightlight ? "".concat(match[0], "") : match[0]; + /** + * When build RegExp ready string with punctuation blocks in the given string; + * - use * quantifier for blocks either at the start/end of the string to match zero or more times + * - use + quantifier for blocks in the middle of the string to match one or more times + * This pattern is build according the response from the content search API results. + */ + var punctMatch = startIndex === 0 ? "(".concat(text.slice(startIndex, match.index), ")*") : "(".concat(text.slice(startIndex, match.index), ")+"); + highlighted = regExpReady ? "".concat(highlighted).concat(punctMatch, "(").concat(textMatch, ")") : "".concat(highlighted).concat(text.slice(startIndex, match.index)).concat(textMatch); + startIndex = match.index + match[0].length; + if (i === (matches === null || matches === void 0 ? void 0 : matches.length) - 1) { + highlighted = regExpReady ? "".concat(highlighted, "(").concat(text.slice(startIndex), ")*") : "".concat(highlighted).concat(text.slice(startIndex)); } + i++; + } + + // Escape punctuation characters in string for RegExp ready strings + var escapePunctuation = function escapePunctuation(str) { + var punctuationRegex = /([.?^${}|[\]\\])/g; + return str.replace(punctuationRegex, '\\$1'); }; - var updatePlayer = function updatePlayer(player) { - player.duration(canvasDurationRef.current); - player.src(options.sources); - player.poster(options.poster); - player.canvasIndex = cIndexRef.current; - player.canvasIsEmpty = canvasIsEmptyRef.current; - player.srcIndex = srcIndex; - player.targets = targets; - if (enableTitleLink) { - player.canvasLink = canvasLinkRef.current; - } + return regExpReady ? escapePunctuation(highlighted) : highlighted; +}; - // Update textTracks in the player - var oldTracks = player.remoteTextTracks(); - var i = oldTracks.length; - while (i--) { - player.removeRemoteTextTrack(oldTracks[i]); - } - if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0 && isVideo) { - tracks.forEach(function (track) { - player.addRemoteTextTrack(track, false); - }); - } +/** + * Calculate hit counts for each matched transcript cue + * @param {String} text matched transcript cue text + * @param {String} query search query from UI + * @param {Boolean} hasHighlight flag indicating has tags or not + * @returns + */ +var getHitCountForCue = function getHitCountForCue(text, query) { + var _ref4; + var hasHighlight = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + /* + Content search API highlights each word in the given phrase in the response. + Threfore, use first word in the query seperated by a white space to get the hit + counts for each cue. + Use regex with any punctuation followed by a white space to split the query. + e.g. query: Mr. bungle => search response: Mr. Bungle + */ + var partialQ = query.split(/[\s.,!?;:]/)[0]; + var cleanedPartialQ = partialQ.replace(/[\[\]\-]/gi, ''); + var hitTerm = hasHighlight ? buildRegexReadyText(partialQ) : cleanedPartialQ; + var highlightedTerm = new RegExp(String.raw(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral(["", ""])), hitTerm), 'gi'); + var hitCount = (_ref4 = _toConsumableArray(text.matchAll(highlightedTerm))) === null || _ref4 === void 0 ? void 0 : _ref4.length; + return hitCount; +}; - /* - Update player control bar for; - - track scrubber button - - appearance of the player: big play button and aspect ratio of the player - based on media type - - volume panel based on media type - - file download menu - */ - if (player.getChild('controlBar') != null && !canvasIsEmpty) { - var controlBar = player.getChild('controlBar'); - // Index of the full-screen toggle in the player's control bar - var fullscreenIndex = controlBar.children().findIndex(function (c) { - return c.name_ == 'FullscreenToggle'; - }); - /* - Track-scrubber button: remove if the Manifest is not a playlist manifest - or the current Canvas doesn't have structure items. Or add back in if it's - not present otherwise. - */ - if (!(hasStructure || playlist.isPlaylist)) { - controlBar.removeChild('videoJSTrackScrubber'); - } else if (!controlBar.getChild('videoJSTrackScrubber')) { - // Add track-scrubber button after duration display if it is not available - controlBar.addChild('videoJSTrackScrubber', { - trackScrubberRef: trackScrubberRef, - timeToolRef: scrubberTooltipRef - }); - } - if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0 && isVideo && !controlBar.getChild('subsCapsButton')) { - var captionIndex = IS_MOBILE ? controlBar.children().findIndex(function (c) { - return c.name_ == 'MuteToggle'; - }) : controlBar.children().findIndex(function (c) { - return c.name_ == 'VolumePanel'; - }); - var subsCapBtn = controlBar.addChild('subsCapsButton', {}, captionIndex + 1); - // Add CSS to mark captions-on - subsCapBtn.children_[0].addClass('captions-on'); - } +// TODO:: Could be used for marking search hits in Word Doc transcripts? +var splitIntoElements = function splitIntoElements(htmlContent) { + // Create a temporary DOM element to parse the HTML + var tempDiv = document.createElement('div'); + tempDiv.innerHTML = htmlContent; + + // Convert child nodes into an array + var elements = buildNonTimedText(Array.from(tempDiv.childNodes), true); + return elements; +}; + +/** + * Build non-timed transcript text content chunks into a JSON array + * with relevant information for display. These are then used by + * search module to convert the transcript content into an index. + * @param {Array} cues a list of trascript cues + * @param {Boolean} isHTML flag to detect inlined HTML in cues + * @returns a list of JSON objects for each cue + */ +var buildNonTimedText = function buildNonTimedText(cues) { + var isHTML = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + var indexedCues = []; + cues.map(function (c) { + indexedCues.push({ + text: isHTML ? c.innerText : c, + tag: TRANSCRIPT_CUE_TYPES.nonTimedLine, + textDisplayed: isHTML ? lib.decode(c.innerHTML) : c + }); + }); + return indexedCues; +}; +function ownKeys$5(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } +function _objectSpread$5(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$5(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$5(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } + +/** + * Disable each marker when one of the markers in the table + * is being edited reading isEditing value from global + * state and read presence of annotation service in the Manifest. + * @returns { + * isDisabled: Boolean, + * hasAnnotationService: Boolean + * } + */ +var useMarkers = function useMarkers() { + var manifestState = useContext(ManifestStateContext); + var _manifestState$playli = manifestState.playlist, + isEditing = _manifestState$playli.isEditing, + hasAnnotationService = _manifestState$playli.hasAnnotationService; + var isDisabled = useMemo(function () { + return isEditing; + }, [isEditing]); + return { + isDisabled: isDisabled, + hasAnnotationService: hasAnnotationService + }; +}; + +/** + * Read player and related updates as player is changed in + * global state + * @returns { + * canvasIndex: number, + * canvasIsEmpty: bool, + * isMultiCanvased: bool, + * lastCanvasIndex: number, + * player: object + * getCurrentTime: func, + * } + */ +var useMediaPlayer = function useMediaPlayer() { + var manifestState = useContext(ManifestStateContext); + var playerState = useContext(PlayerStateContext); + var player = playerState.player; + var allCanvases = manifestState.allCanvases, + canvasIndex = manifestState.canvasIndex, + canvasIsEmpty = manifestState.canvasIsEmpty; + + // Deduct 1 from length to compare against canvasIndex, which starts from 0 + var lastCanvasIndex = useMemo(function () { + var _ref; + return (_ref = (allCanvases === null || allCanvases === void 0 ? void 0 : allCanvases.length) - 1) !== null && _ref !== void 0 ? _ref : 0; + }, [allCanvases]); + var isMultiCanvased = useMemo(function () { + return (allCanvases === null || allCanvases === void 0 ? void 0 : allCanvases.length) - 1 > 0 ? true : false; + }, [allCanvases]); + + // Wrapper function to get player's time for creating a new playlist marker + var getCurrentTime = useCallback(function () { + if (player) { + return player.currentTime(); + } else { + return 0; + } + }, [player]); + return { + canvasIndex: canvasIndex, + canvasIsEmpty: canvasIsEmpty, + isMultiCanvased: isMultiCanvased, + lastCanvasIndex: lastCanvasIndex, + player: player, + getCurrentTime: getCurrentTime + }; +}; + +/** + * Read Canvas information and update state to reload player on + * Canvas changes + * @param {Object} obj + * @param {Boolean} obj.enableFileDownload + * @param {Boolean} obj.withCredentials + * @param {Number} obj.lastCanvasIndex + * @returns { + * isMultiSourced: bool, + * isPlaylist: bool, + * isVideo: bool, + * nextItemClicked: func, + * playerConfig: obj, + * ready: bool, + * renderingFiles: array, + * srcIndex: number, + * switchPlayer: func + * } + */ +var useSetupPlayer = function useSetupPlayer(_ref2) { + var _ref2$enableFileDownl = _ref2.enableFileDownload, + enableFileDownload = _ref2$enableFileDownl === void 0 ? false : _ref2$enableFileDownl, + _ref2$withCredentials = _ref2.withCredentials, + withCredentials = _ref2$withCredentials === void 0 ? false : _ref2$withCredentials, + lastCanvasIndex = _ref2.lastCanvasIndex; + var manifestDispatch = useContext(ManifestDispatchContext); + var playerDispatch = useContext(PlayerDispatchContext); + var manifestState = useContext(ManifestStateContext); + var allCanvases = manifestState.allCanvases, + autoAdvance = manifestState.autoAdvance, + canvasIndex = manifestState.canvasIndex, + customStart = manifestState.customStart, + manifest = manifestState.manifest, + playlist = manifestState.playlist, + renderings = manifestState.renderings, + srcIndex = manifestState.srcIndex; + var isPlaylist = playlist.isPlaylist; + var _useShowInaccessibleM = useShowInaccessibleMessage({ + lastCanvasIndex: lastCanvasIndex + }), + clearDisplayTimeInterval = _useShowInaccessibleM.clearDisplayTimeInterval, + createDisplayTimeInterval = _useShowInaccessibleM.createDisplayTimeInterval; + var _useState = useState(), + _useState2 = _slicedToArray(_useState, 2), + isVideo = _useState2[0], + setIsVideo = _useState2[1]; + var _useState3 = useState({ + error: '', + sources: [], + tracks: [], + poster: null, + targets: [] + }), + _useState4 = _slicedToArray(_useState3, 2), + playerConfig = _useState4[0], + setPlayerConfig = _useState4[1]; + var _useState5 = useState(), + _useState6 = _slicedToArray(_useState5, 2), + isMultiSourced = _useState6[0], + setIsMultiSourced = _useState6[1]; + var _useState7 = useState(true), + _useState8 = _slicedToArray(_useState7, 2), + firstLoad = _useState8[0], + setFirstLoad = _useState8[1]; + var _useState9 = useState(false), + _useState10 = _slicedToArray(_useState9, 2), + ready = _useState10[0], + setReady = _useState10[1]; + var renderingFiles = useMemo(function () { + if (enableFileDownload && renderings != {}) { + var _renderings$manifest, _renderings$canvas$ca; + return renderings === null || renderings === void 0 ? void 0 : (_renderings$manifest = renderings.manifest) === null || _renderings$manifest === void 0 ? void 0 : _renderings$manifest.concat(renderings === null || renderings === void 0 ? void 0 : (_renderings$canvas$ca = renderings.canvas[canvasIndex]) === null || _renderings$canvas$ca === void 0 ? void 0 : _renderings$canvas$ca.files); + } else { + return []; + } + }, [renderings, canvasIndex]); + useEffect(function () { + if (manifest) { /* - Change player's appearance when switching between audio and video canvases. - For audio: player height is reduced and big play button is removed - For video: player aspect ratio is set to 16:9 and has the centered big play button + Always start from the start time relevant to the Canvas only in playlist contexts, + because canvases related to playlist items always start from the given start. + With regular manifests, the start time could be different when using structured + navigation to switch between canvases. */ - if (!isVideo) { - player.audioOnlyMode(true); - player.addClass('vjs-audio'); - player.height(player.controlBar.height()); - player.removeChild('bigPlayButton'); - } else { - player.audioOnlyMode(false); - player.removeClass('vjs-audio'); - player.aspectRatio('16:9'); - player.addChild('bigPlayButton'); + if (canvasIndex == undefined || canvasIndex < 0) { + throw new Error('Invalid canvas index. Please check your Manifest.'); } + initCanvas(canvasIndex, isPlaylist); + } + return function () { + setReady(false); + playerDispatch({ + player: null, + type: 'updatePlayer' + }); + }; + }, [manifest, canvasIndex]); - /* - Re-add volumePanel/muteToggle icon: ensures the correct order of controls - on player reload. - On mobile device browsers, the volume panel is replaced by muteToggle - for both audio and video. - */ - if (!IS_MOBILE) { - controlBar.removeChild('VolumePanel'); - controlBar.addChild('VolumePanel'); - /* - Trigger ready event to reset the volume slider in the refreshed - volume panel. This is needed on player reload, since volume slider - is set on either 'ready' or 'volumechange' events. - */ - player.trigger('volumechange'); + /** + * Initialize the next Canvas to be viewed in the player instance + * @param {Number} canvasId index of the Canvas to be loaded into the player + * @param {Boolean} fromStart flag to indicate how to start new player instance + */ + var initCanvas = function initCanvas(canvasId, fromStart) { + clearDisplayTimeInterval(); + var _getMediaInfo = getMediaInfo({ + manifest: manifest, + canvasIndex: canvasId, + startTime: canvasId === customStart.startIndex && firstLoad ? customStart.startTime : 0, + srcIndex: srcIndex, + isPlaylist: isPlaylist + }), + isMultiSource = _getMediaInfo.isMultiSource, + sources = _getMediaInfo.sources, + tracks = _getMediaInfo.tracks, + canvasTargets = _getMediaInfo.canvasTargets, + mediaType = _getMediaInfo.mediaType, + error = _getMediaInfo.error, + poster = _getMediaInfo.poster; + if (withCredentials) { + sources.map(function (source) { + return source.withCredentials = true; + }); + } + setIsVideo(mediaType === 'video'); + manifestDispatch({ + canvasTargets: canvasTargets, + type: 'canvasTargets' + }); + manifestDispatch({ + isMultiSource: isMultiSource, + type: 'hasMultipleItems' + }); + + // Set the current time in player from the canvas details + if (fromStart) { + if ((canvasTargets === null || canvasTargets === void 0 ? void 0 : canvasTargets.length) > 0) { + playerDispatch({ + currentTime: canvasTargets[0].altStart, + type: 'setCurrentTime' + }); } else { - controlBar.removeChild('MuteToggle'); - controlBar.addChild('MuteToggle'); + playerDispatch({ + currentTime: 0, + type: 'setCurrentTime' + }); } - if (enableFileDownload) { - var fileDownloadIndex = controlBar.children().findIndex(function (c) { - return c.name_ == 'VideoJSFileDownload'; - }) || fullscreenIndex + 1; - controlBar.removeChild('videoJSFileDownload'); - if ((renderingFiles === null || renderingFiles === void 0 ? void 0 : renderingFiles.length) > 0) { - var fileOptions = { - title: 'Download Files', - controlText: 'Alternate resource download', - files: renderingFiles - }; - controlBar.addChild('videoJSFileDownload', _objectSpread$4({}, fileOptions), fileDownloadIndex); - } + } + setPlayerConfig(_objectSpread$5(_objectSpread$5({}, playerConfig), {}, { + error: error, + sources: sources, + tracks: tracks, + poster: poster, + targets: canvasTargets + })); + var currentCanvas = allCanvases.find(function (c) { + return c.canvasIndex === canvasId; + }); + if (!currentCanvas.isEmpty) { + // Manifest is taken from manifest state, and is a basic object at this point + // lacking the getLabel() function so we manually retrieve the first label. + var manifestLabel = manifest.label ? Object.values(manifest.label)[0][0] : ''; + // Filter out falsy items in case canvas.label is null or an empty string + var titleText = [manifestLabel, currentCanvas.label].filter(Boolean).join(' - '); + manifestDispatch({ + canvasDuration: currentCanvas.duration, + type: 'canvasDuration' + }); + manifestDispatch({ + canvasLink: { + label: titleText, + id: currentCanvas.canvasId + }, + type: 'canvasLink' + }); + manifestDispatch({ + type: 'setCanvasIsEmpty', + isEmpty: false + }); + } else { + playerDispatch({ + type: 'updatePlayer' + }); + manifestDispatch({ + type: 'setCanvasIsEmpty', + isEmpty: true + }); + // Set poster as playerConfig.error to be used for empty Canvas message in VideoJSPlayer + setPlayerConfig(_objectSpread$5(_objectSpread$5({}, playerConfig), {}, { + error: poster + })); + // Create timer to display the message when autoadvance is ON + if (autoAdvance) { + createDisplayTimeInterval(); } } + setIsMultiSourced(isMultiSource || false); + error ? setReady(false) : setReady(true); + // Reset firstLoad flag after customStart is used on initial load + setFirstLoad(false); }; /** - * Setup on loadedmetadata event is broken out of initial setup function, - * since this needs to be called when reloading the player on Canvas change - * @param {Object} player Video.js player instance + * Switch player when navigating across canvases + * @param {Number} index canvas index to be loaded into the player + * @param {Boolean} fromStart flag to indicate set player start time to zero or not + * @param {String} focusElement element to be focused within the player when using + * next or previous buttons with keyboard */ - var playerLoadedMetadata = function playerLoadedMetadata(player) { - player.one('loadedmetadata', function () { - console.log('Player loadedmetadata'); - player.duration(canvasDurationRef.current); - isEndedRef.current ? player.currentTime(0) : player.currentTime(currentTimeRef.current); - if (isEndedRef.current || isPlayingRef.current) { - /* - iOS devices lockdown the ability for unmuted audio and video media to autoplay. - They accomplish this by capturing any programmatic play events and returning - a rejected Promise. In certain versions of iOS, this rejected promise would - cause a runtime error within Ramp. This error would cause the error boundary - handling to trigger, forcing a user to reload the player/page. By silently - catching the rejected Promise we are able to provide a more seamless user - experience, where the user can manually play the media or change to a different - section. - */ - var promise = player.play(); - if (promise !== undefined) { - promise.then(function (_) { - // Autoplay - })["catch"](function (error) { - // Prevent error from triggering error boundary - }); - } - } - if (isVideo) { - setUpCaptions(player); - } - - /* - Set playable duration within the given media file and alternate start time as - player properties. These values are read by track-scrubber component to build - and update the track-scrubber progress and time in the UI. - */ - var mediaRange = getMediaFragment(player.src(), canvasDurationRef.current); - if (mediaRange != undefined) { - player.playableDuration = mediaRange.end - mediaRange.start; - player.altStart = mediaRange.start; - } else { - player.playableDuration = canvasDurationRef.current; - player.altStart = targets[srcIndex].altStart; - } - player.canvasIndex = cIndexRef.current; - setIsReady(true); - - /** - * Update currentNavItem on loadedmetadata event in Safari, as it doesn't - * trigger the 'timeupdate' event intermittently on load. - */ - if (IS_SAFARI) { - handleTimeUpdate(); - } - - /** - * When either player/browser tab is muted Safari and Chrome in iOS doesn't seem to - * load enough data related to audio-only media for the Video.js instance to play - * on page load. - * Since, it is not possible to detect muted tabs in JS the condition avoids - * checking for muted state altogether. - * Without this, Safari will not reach player.readyState() = 4, the state - * which indicates the player that enough data is available on the media - * for playback. - */ - if (!isVideo && (IS_SAFARI || IS_IOS) && player.readyState() != 4) { - player.load(); - } - - // Reveal player if not revealed on 'progress' event, allowing user to - // interact with the player since enough data is available for playback - if (player.hasClass('vjs-disabled')) { - player.removeClass('vjs-disabled'); - } - }); + var switchPlayer = function switchPlayer(index, fromStart) { + var focusElement = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; + if (index != undefined && index > -1 && index <= lastCanvasIndex) { + manifestDispatch({ + canvasIndex: index, + type: 'switchCanvas' + }); + initCanvas(index, fromStart); + playerDispatch({ + element: focusElement, + type: 'setPlayerFocusElement' + }); + } }; /** - * Setup player with player-related information parsed from the IIIF - * Manifest Canvas. This gets called on both initial page load and each - * Canvas switch to setup and update player respectively. - * @param {Object} player current player instance from Video.js + * Switch src in the player when seeked to a time range within a + * different item in the same canvas + * @param {Number} srcindex new srcIndex + * @param {Number} value current time of the player */ - var playerInitSetup = function playerInitSetup(player) { - player.on('ready', function () { - console.log('Player ready'); - - // Add this class in mobile/tablet devices to always show the control bar, - // since the inactivityTimeout is flaky in some browsers - if (IS_MOBILE || IS_IPAD) { - player.controlBar.addClass('vjs-mobile-visible'); - } - player.muted(startMuted); - player.volume(startVolume); - player.canvasIndex = cIndexRef.current; - player.duration(canvasDurationRef.current); - player.srcIndex = srcIndex; - player.targets = targets; - if (enableTitleLink) { - player.canvasLink = canvasLinkRef.current; - } - // Need to set this once experimentalSvgIcons option in Video.js options was enabled - player.getChild('controlBar').qualitySelector.setIcon('cog'); - }); - playerLoadedMetadata(player); - player.on('progress', function () { - // Reveal player if not revealed on 'loadedmetadata' event, allowing user to - // interact with the player since enough data is available for playback - if (player.hasClass('vjs-disabled')) { - player.removeClass('vjs-disabled'); - } - }); - player.on('canplay', function () { - // Reset isEnded flag - playerDispatch({ - isEnded: false, - type: 'setIsEnded' - }); - }); - player.on('play', function () { - playerDispatch({ - isPlaying: true, - type: 'setPlayingStatus' - }); - }); - player.on('timeupdate', function () { - handleTimeUpdate(); - }); - player.on('ended', function () { - /** - * Checking against isReadyRef stops from delayed events being executed - * when transitioning from a Canvas to the next. - * Checking against isPlayingRef.current to distinguish whether this event - * triggered intentionally, because Video.js seem to trigger this event when - * switching to a media file with a shorter duration in Safari browsers. - */ - setTimeout(function () { - if (isReadyRef.current && isPlayingRef.current) { - playerDispatch({ - isEnded: true, - type: 'setIsEnded' - }); - player.pause(); - if (!canvasIsEmptyRef.current) handleEnded(); - } - }, 100); - }); - player.on('volumechange', function () { - setStartMuted(player.muted()); - setStartVolume(player.volume()); - }); - player.on('qualityRequested', function (e, quality) { - setStartQuality(quality.label); + var nextItemClicked = function nextItemClicked(srcindex, value) { + playerDispatch({ + currentTime: value, + type: 'setCurrentTime' }); - // Use error event listener for inaccessible item display - player.on('error', function (e) { - var error = player.error(); - var errorMessage = 'Something went wrong. Please try again later or contact support for help.'; - // Handle different error codes - switch (error.code) { - case 1: - console.error('MEDIA_ERR_ABORTED: The fetching process for the media resource was aborted by the user agent\ - at the user’s request.'); - break; - case 2: - errorMessage = 'The media could not be loaded due to a network error. Please try again later.'; - console.error('MEDIA_ERR_NETWORK: A network error caused the user agent to stop fetching the media resource,\ - after the resource was established to be usable.'); - break; - case 3: - errorMessage = 'Media is corrupt or has features not supported by the browser. \ - Please try a different media or contact support for help.'; - console.error('MEDIA_ERR_DECODE: An error occurred while decoding the media resource, after\ - the resource was established to be usable.'); - break; - case 4: - errorMessage = 'Media could not be loaded. Network error or media format is not supported.'; - console.error('MEDIA_ERR_SRC_NOT_SUPPORTED: The media resource indicated by the src attribute was not suitable.'); - break; - default: - console.error('An unknown error occurred.'); - break; - } - // Show dismissable error display modal from Video.js - var errorDisplay = player.getChild('ErrorDisplay'); - if (errorDisplay) { - errorDisplay.contentEl().innerText = errorMessage; - errorDisplay.removeClass('vjs-hidden'); - player.removeClass('vjs-error'); - player.removeClass('vjs-disabled'); - } - e.stopPropagation(); + manifestDispatch({ + srcIndex: srcindex, + type: 'setSrcIndex' }); + }; + return { + isMultiSourced: isMultiSourced, + isPlaylist: isPlaylist, + isVideo: isVideo, + nextItemClicked: nextItemClicked, + playerConfig: playerConfig, + ready: ready, + renderingFiles: renderingFiles, + srcIndex: srcIndex, + switchPlayer: switchPlayer + }; +}; + +/** + * Initialize and update VideoJS instance on global state changes when + * Canvas changes + * @param {Object} obj + * @param {Object} obj.options VideoJS options + * @param {Function} obj.playerInitSetup VideoJS initialize setup func + * @param {String} obj.startQuality selected quality stored in local storage + * @param {Array} obj.tracks text tracks for the selected Canvas + * @param {Function} obj.updatePlayer VideoJS update func on Canvas change + * @param {Object} obj.videoJSRef React ref for video tag on page + * @param {String} obj.videoJSLangMap VideoJS language for set language + * @returns { + * activeId: string, + * fragmentMarker: obj, + * isReadyRef: obj, + * playerRef: obj, + * setActiveId: func, + * setFragmentMarker: func, + * setIsReady: func, + * } + */ +var useVideoJSPlayer = function useVideoJSPlayer(_ref3) { + var options = _ref3.options, + playerInitSetup = _ref3.playerInitSetup, + startQuality = _ref3.startQuality, + tracks = _ref3.tracks, + updatePlayer = _ref3.updatePlayer, + videoJSRef = _ref3.videoJSRef, + videoJSLangMap = _ref3.videoJSLangMap; + var manifestState = useContext(ManifestStateContext); + var playerState = useContext(PlayerStateContext); + var playerDispatch = useContext(PlayerDispatchContext); + var canvasDuration = manifestState.canvasDuration, + canvasIndex = manifestState.canvasIndex, + canvasIsEmpty = manifestState.canvasIsEmpty, + currentNavItem = manifestState.currentNavItem, + playlist = manifestState.playlist; + var currentTime = playerState.currentTime, + isClicked = playerState.isClicked, + isPlaying = playerState.isPlaying, + player = playerState.player, + searchMarkers = playerState.searchMarkers; + var _useState11 = useState(''), + _useState12 = _slicedToArray(_useState11, 2), + activeId = _useState12[0], + setActiveId = _useState12[1]; + var _useState13 = useState(null), + _useState14 = _slicedToArray(_useState13, 2), + fragmentMarker = _useState14[0], + setFragmentMarker = _useState14[1]; + // Needs to maintain this in a state variable for useEffect for marker updates + var _useState15 = useState(false), + _useState16 = _slicedToArray(_useState15, 2), + isReady = _useState16[0], + _setIsReady = _useState16[1]; + var isReadyRef = useRef(isReady); + var setIsReady = function setIsReady(r) { + _setIsReady(r); + isReadyRef.current = r; + }; + var playerRef = useRef(null); + useEffect(function () { /* This event handler helps to execute hotkeys functions related to 'keydown' events before any user interactions with the player or when focused on other non-input elements on the page */ document.addEventListener('keydown', function (event) { - var result = playerHotKeys(event, player, canvasIsEmptyRef.current); + var result = playerHotKeys(event, playerRef.current, canvasIsEmpty); // Update player status in global state switch (result) { case HOTKEY_ACTION_OUTPUT.pause: @@ -7196,94 +7113,157 @@ function VideoJSPlayer(_ref) { break; } }); - }; - - /** - * Setup captions for the player based on context - * @param {Object} player Video.js player instance - */ - var setUpCaptions = function setUpCaptions(player) { - var _textTracks$tracks_; - var textTracks = player.textTracks(); - /* - Filter the text track Video.js adds with an empty label and language - when nativeTextTracks are enabled for iPhones and iPads. - Related links, Video.js => https://github.com/videojs/video.js/issues/2808 and - in Apple => https://developer.apple.com/library/archive/qa/qa1801/_index.html - */ - if (IS_MOBILE && !IS_ANDROID) { - textTracks.on('addtrack', function () { - for (var i = 0; i < textTracks.length; i++) { - if (textTracks[i].language === '' && textTracks[i].label === '') { - player.textTracks().removeTrack(textTracks[i]); - } - /** - * This enables the caption in the native iOS player first playback. - * Only enable caption when captions are turned on. - * First caption is already turned on in the code block below, so read it - * from activeTrackRef - */ - if (startCaptioned && activeTrackRef.current) { - textTracks.tracks_.filter(function (t) { - return t.label === activeTrackRef.current.label && t.language === activeTrackRef.current.language; - })[0].mode = 'showing'; - } - } - }); - } - // Turn first caption/subtitle ON and turn captions ON indicator via CSS on first load - if (((_textTracks$tracks_ = textTracks.tracks_) === null || _textTracks$tracks_ === void 0 ? void 0 : _textTracks$tracks_.length) > 0) { - var firstSubCap = null; - // Flag to identify first valid caption for resource - var onFirstCap = false; - // Disable all text tracks to avoid multiple selections and pick the first one as default - for (var i = 0; i < textTracks.tracks_.length; i++) { - var t = textTracks.tracks_[i]; - if ((t.kind === 'subtitles' || t.kind === 'captions') && t.language != '' && t.label != '') { - t.mode = 'disabled'; - if (!onFirstCap) firstSubCap = t; - onFirstCap = true; - } + // Dispose Video.js instance when VideoJSPlayer component is removed + return function () { + if (player) { + player.dispose(); + document.removeEventListener('keydown', playerHotKeys); + setIsReady(false); } + }; + }, []); - // Enable the first caption when captions are enabled in the session - if (firstSubCap && startCaptioned) { - firstSubCap.mode = 'showing'; - activeTrackRef.current = firstSubCap; - handleCaptionChange(true); - } - } + // Update VideoJS instance on Canvas change + useEffect(function () { + var _options$sources, _options$sources2; + // Set selected quality from localStorage in Video.js options + setSelectedQuality(options.sources); - // Add/remove CSS to indicate captions/subtitles is turned on - textTracks.on('change', function () { - var trackModes = []; - for (var _i = 0; _i < textTracks.tracks_.length; _i++) { - var _textTracks$_i = textTracks[_i], - mode = _textTracks$_i.mode, - label = _textTracks$_i.label, - kind = _textTracks$_i.kind; - trackModes.push(textTracks[_i].mode); - if (mode === 'showing' && label != '' && (kind === 'subtitles' || kind === 'captions')) { - activeTrackRef.current = textTracks[_i]; + // Video.js player is only initialized on initial page load + if (!playerRef.current && ((_options$sources = options.sources) === null || _options$sources === void 0 ? void 0 : _options$sources.length) > 0) { + videojs.addLanguage(options.language, JSON.parse(videoJSLangMap)); + buildTracksHTML(); + + // Turn Video.js logging off and handle errors in this code, to avoid + // cluttering the console when loading inaccessible items. + videojs.log.level('off'); + var _player = playerRef.current = videojs(videoJSRef.current, options, function () { + playerInitSetup(playerRef.current); + }); + + /* Another way to add a component to the controlBar */ + // player.getChild('controlBar').addChild('vjsYo', {}); + + playerDispatch({ + player: _player, + type: 'updatePlayer' + }); + + // Update player status in state only when pause is initiate by the user + _player.controlBar.getChild('PlayToggle').on('pointerdown', function () { + handlePause(); + }); + _player.on('pointerdown', function (e) { + var elementTag = e.target.nodeName.toLowerCase(); + if (elementTag == 'video') { + handlePause(); } + }); + } else if (playerRef.current && ((_options$sources2 = options.sources) === null || _options$sources2 === void 0 ? void 0 : _options$sources2.length) > 0) { + var _player2$markers; + // Update the existing Video.js player on consecutive Canvas changes + var _player2 = playerRef.current; + + // Reset markers + if (activeId) (_player2$markers = _player2.markers) === null || _player2$markers === void 0 ? void 0 : _player2$markers.removeAll(); + setActiveId(null); + + // Block player while metadata is loaded when canvas is not empty + if (!canvasIsEmpty) { + _player2.addClass('vjs-disabled'); + setIsReady(false); + updatePlayer(_player2); + playerDispatch({ + player: _player2, + type: 'updatePlayer' + }); + } else { + // Mark as ready to for inaccessible canvas (empty) + setIsReady(true); } - var subsOn = trackModes.includes('showing') ? true : false; - handleCaptionChange(subsOn); - setStartCaptioned(subsOn); - }); - }; + } + }, [options.sources, videoJSRef]); + useEffect(function () { + if (player) { + // Show/hide control bar for valid/inaccessible items respectively + if (canvasIsEmpty) { + // Set the player's aspect ratio to video + player.audioOnlyMode(false); + player.canvasIsEmpty = true; + player.aspectRatio('16:9'); + player.controlBar.addClass('vjs-hidden'); + player.removeClass('vjs-disabled'); + player.pause(); + /** + * Update the activeId to update the active item in the structured navigation. + * For playable items this is updated in the timeupdate handler. + */ + setActiveId(currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.id); + } else { + // Reveal control bar; needed when loading a Canvas after an inaccessible item + player.controlBar.removeClass('vjs-hidden'); + } + } + }, [canvasIndex, canvasIsEmpty, currentNavItem]); - /** - * Setting the current time of the player when using structure navigation - */ - React.useEffect(function () { - if (playerRef.current !== null && isReadyRef.current) { - playerRef.current.currentTime(currentTimeRef.current, playerDispatch({ + // Setting the current time of the player when using structure navigation + useEffect(function () { + if (playerRef.current) { + playerRef.current.currentTime(currentTime, playerDispatch({ type: 'resetClick' })); } - }, [isClicked, isReady]); + }, [isClicked]); + + // Update VideoJS player's markers for search hits/playlist markers/structure navigation + useEffect(function () { + if (playerRef.current && playerRef.current.markers && isReady) { + var _playlist$markers, _playerRef$current$ma; + // markers plugin not yet initialized + if (typeof playerRef.current.markers === 'function') { + playerRef.current.markers({ + markerTip: { + display: false, + // true, + text: function text(marker) { + return marker.text; + } + }, + markerStyle: {}, + markers: [] + }); + } + var playlistMarkers = []; + if (playlist !== null && playlist !== void 0 && (_playlist$markers = playlist.markers) !== null && _playlist$markers !== void 0 && _playlist$markers.length) { + var canvasMarkers = playlist.markers.filter(function (m) { + return m.canvasIndex === canvasIndex; + })[0].canvasMarkers; + playlistMarkers = canvasMarkers.map(function (m) { + return { + time: parseFloat(m.time), + text: m.value, + "class": 'ramp--track-marker--playlist' + }; + }); + } + (_playerRef$current$ma = playerRef.current.markers) === null || _playerRef$current$ma === void 0 ? void 0 : _playerRef$current$ma.removeAll(); + playerRef.current.markers.add([].concat(_toConsumableArray(fragmentMarker ? [fragmentMarker] : []), _toConsumableArray(searchMarkers), _toConsumableArray(playlistMarkers))); + } + }, [fragmentMarker, searchMarkers, canvasDuration, canvasIndex, playerRef.current, isReady]); + + /** + * Update global state only when a user pause the player by using the + * player interface or keyboard shortcuts + */ + var handlePause = function handlePause() { + if (isPlaying) { + playerDispatch({ + isPlaying: false, + type: 'setPlayingStatus' + }); + } + }; var setSelectedQuality = function setSelectedQuality(sources) { //iterate through sources and find source that matches startQuality and source currently marked selected //if found set selected attribute on matching source then remove from currently marked one @@ -7300,3040 +7280,3808 @@ function VideoJSPlayer(_ref) { }; /** - * Add CSS class to icon to indicate captions are on/off in player control bar - * @param {Boolean} subsOn flag to indicate captions are on/off + * Build track HTML for Video.js player on initial page load */ - var handleCaptionChange = function handleCaptionChange(subsOn) { - var player = playerRef.current; - /** - * When subsCapsButton is not setup on Video.js initialization step, and is - * later added in updatePlayer() function player.controlBar.getChild() method - * needs to be used to access it. - */ - var subsCapsBtn = player.controlBar.getChild('subsCapsButton'); - /* - For audio instances Video.js is setup to not to build the CC button - in Ramp's player control bar. - */ - if (subsCapsBtn == undefined || !subsCapsBtn || !(subsCapsBtn !== null && subsCapsBtn !== void 0 && subsCapsBtn.children_)) { - return; - } - if (subsOn) { - subsCapsBtn.children_[0].addClass('captions-on'); - captionsOnRef.current = true; - } else { - subsCapsBtn.children_[0].removeClass('captions-on'); - captionsOnRef.current = false; + var buildTracksHTML = function buildTracksHTML() { + if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0 && videoJSRef.current) { + tracks.map(function (t) { + var trackEl = document.createElement('track'); + trackEl.setAttribute('key', t.key); + trackEl.setAttribute('src', t.src); + trackEl.setAttribute('kind', t.kind); + trackEl.setAttribute('label', t.label); + trackEl.setAttribute('srclang', t.srclang); + videoJSRef.current.appendChild(trackEl); + }); } }; + return { + activeId: activeId, + fragmentMarker: fragmentMarker, + isReadyRef: isReadyRef, + playerRef: playerRef, + setActiveId: setActiveId, + setFragmentMarker: setFragmentMarker, + setIsReady: setIsReady + }; +}; + +/** + * Handle display of inaccessible message timer and interval for + * countdown + * @param {Object} obj + * @param {Number} obj.lastCanvasIndex + * @returns { + * messageTime: number, + * clearCanvasMessageTimer: func, + * createCanvasMessageTimer: func + * } + */ +var useShowInaccessibleMessage = function useShowInaccessibleMessage(_ref4) { + var lastCanvasIndex = _ref4.lastCanvasIndex; + var manifestDispatch = useContext(ManifestDispatchContext); + var manifestState = useContext(ManifestStateContext); + var autoAdvance = manifestState.autoAdvance, + canvasIndex = manifestState.canvasIndex, + canvasIsEmpty = manifestState.canvasIsEmpty; + var _useState17 = useState(CANVAS_MESSAGE_TIMEOUT / 1000), + _useState18 = _slicedToArray(_useState17, 2), + messageTime = _useState18[0], + setMessageTime = _useState18[1]; + var canvasIndexRef = useRef(); + canvasIndexRef.current = useMemo(function () { + return canvasIndex; + }, [canvasIndex]); + var messageIntervalRef = useRef(null); + useEffect(function () { + // Clear existing interval for inaccessible message display + clearDisplayTimeInterval(); + if (canvasIsEmpty && !messageIntervalRef.current && autoAdvance) { + setMessageTime(CANVAS_MESSAGE_TIMEOUT / 1000); + createDisplayTimeInterval(); + } + }, [canvasIndex, autoAdvance, canvasIsEmpty]); /** - * Handle the 'ended' event fired by the player when a section comes to - * an end. If there are sections ahead move onto the next canvas and - * change the player and the state accordingly. - * Throttle helps to cancel the delayed function call triggered by ended event and - * load the correct item into the player, when the user clicks on a different item - * (not the next item in list) when the current item is coming to its end. + * Create an interval to run every second to update display for the timer + * for inaccessible canvas message display. Using useCallback to cache the + * function as this doesn't need to change with component re-renders */ - var handleEnded = React.useMemo(function () { - return throttle_1(function () { - var isLastCanvas = cIndexRef.current === lastCanvasIndex; - /** - * Do nothing if Canvas is not multi-sourced AND autoAdvance is turned off - * OR current Canvas is the last Canvas in the Manifest - */ - if ((!autoAdvanceRef.current || isLastCanvas) && !hasMultiItems) { - return; + var createDisplayTimeInterval = useCallback(function () { + var createTime = new Date().getTime(); + messageIntervalRef.current = setInterval(function () { + var now = new Date().getTime(); + var timeRemaining = (CANVAS_MESSAGE_TIMEOUT - (now - createTime)) / 1000; + if (timeRemaining > 0) { + setMessageTime(Math.ceil(timeRemaining)); } else { - var _structuresRef$curren; - // Remove all the existing structure related markers in the player - if (playerRef.current && playerRef.current.markers) { - playerRef.current.pause(); - setFragmentMarker(null); - playerRef.current.markers.removeAll(); + // Advance to next Canvas when timer ends + if (canvasIndexRef.current < lastCanvasIndex && autoAdvance) { + manifestDispatch({ + canvasIndex: canvasIndexRef.current + 1, + type: 'switchCanvas' + }); } - if (hasMultiItems) { - // When there are multiple sources in a single canvas - // advance to next source - if (srcIndex + 1 < targets.length) { - manifestDispatch({ - srcIndex: srcIndex + 1, - type: 'setSrcIndex' - }); - playerDispatch({ - currentTime: 0, - type: 'setCurrentTime' - }); - playerRef.current.play(); - } else { - return; - } - } else if (((_structuresRef$curren = structuresRef.current) === null || _structuresRef$curren === void 0 ? void 0 : _structuresRef$curren.length) > 0) { - var nextItem = structuresRef.current[cIndexRef.current + 1]; - if (nextItem) { - manifestDispatch({ - canvasIndex: cIndexRef.current + 1, - type: 'switchCanvas' - }); - - // Reset startTime and currentTime to zero - playerDispatch({ - startTime: 0, - type: 'setTimeFragment' - }); - playerDispatch({ - currentTime: 0, - type: 'setCurrentTime' - }); + clearDisplayTimeInterval(); + } + }, 1000); + }); - // Get first timespan in the next canvas - var firstTimespanInNextCanvas = canvasSegmentsRef.current.filter(function (t) { - return t.canvasIndex === nextItem.canvasIndex && t.itemIndex === 1; - }); - // If the nextItem doesn't have an ID (a Canvas media fragment) pick the first timespan - // in the next Canvas - var nextFirstItem = nextItem.id != undefined ? nextItem : firstTimespanInNextCanvas[0]; - var start = 0; - if (nextFirstItem != undefined && nextFirstItem.id != undefined) { - start = getMediaFragment(nextFirstItem.id, canvasDurationRef.current).start; - } + // Cleanup interval created for timer display for inaccessible message + var clearDisplayTimeInterval = useCallback(function () { + clearInterval(messageIntervalRef.current); + messageIntervalRef.current = null; + }); + return { + messageTime: messageTime, + clearDisplayTimeInterval: clearDisplayTimeInterval, + createDisplayTimeInterval: createDisplayTimeInterval + }; +}; - // If there's a timespan item at the start of the next canvas - // mark it as the currentNavItem. Otherwise empty out the currentNavItem. - if (start === 0) { - manifestDispatch({ - item: nextFirstItem, - type: 'switchItem' - }); - } else if (nextFirstItem.isEmpty) { - // Switch the currentNavItem and clear isEnded flag - manifestDispatch({ - item: nextFirstItem, - type: 'switchItem' - }); - playerRef.current.currentTime(start); - // Only play if the next item is not an inaccessible item - if (!nextItem.isEmpty) playerRef.current.play(); - } - } - } - } +/** + * Handle global state updates and local state updates for structured + * navigation related components based on the user interactions and + * player status updates + * @param {Object} obj + * @param {Number} obj.itemIndex + * @param {Boolean} obj.isRoot + * @param {String} obj.itemId URL of the struct item + * @param {Object} obj.liRef React ref for li element for struct item + * @param {Object} obj.sectionRef React ref for collapsible ul element + * @param {Boolean} obj.isCanvas + * @param {Number} obj.canvasDuration + * @param {Function} obj.setIsOpen + * @returns + */ +var useActiveStructure = function useActiveStructure(_ref5) { + var itemIndex = _ref5.itemIndex, + isRoot = _ref5.isRoot, + itemId = _ref5.itemId, + liRef = _ref5.liRef, + sectionRef = _ref5.sectionRef, + isCanvas = _ref5.isCanvas, + canvasDuration = _ref5.canvasDuration, + setIsOpen = _ref5.setIsOpen; + var playerDispatch = useContext(PlayerDispatchContext); + var manifestState = useContext(ManifestStateContext); + var canvasIndex = manifestState.canvasIndex, + currentNavItem = manifestState.currentNavItem, + playlist = manifestState.playlist; + var isPlaylist = playlist.isPlaylist; + var isActiveLi = useMemo(function () { + return itemId != undefined && (currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.id) === itemId && (isPlaylist || !isCanvas) && (currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.canvasIndex) === canvasIndex + 1 ? true : false; + }, [currentNavItem, canvasIndex]); + var isActiveSection = useMemo(function () { + var isCurrentSection = canvasIndex + 1 === itemIndex; + // Do not mark root range as active + if (isCurrentSection && !isRoot) { + // Collapse the section in structured navigation + setIsOpen(true); + return true; + } else { + return false; + } + }, [canvasIndex]); + var handleClick = useCallback(function (e) { + e.preventDefault(); + e.stopPropagation(); + var _getMediaFragment = getMediaFragment(itemId, canvasDuration), + start = _getMediaFragment.start, + end = _getMediaFragment.end; + var inRange = checkSrcRange({ + start: start, + end: end + }, { + end: canvasDuration }); - }, [cIndexRef.current]); - - /** - * Handle the 'timeUpdate' event emitted by VideoJS player. - * The current time of the playhead used to show structure in the player's - * time rail as the playhead arrives at a start time of an existing structure - * item. When the current time is inside an item, that time fragment is highlighted - * in the player's time rail. - * Using throttle helps for smooth updates by cancelling and cleaning up intermediate - * delayed function calls. - */ - var handleTimeUpdate = React.useMemo(function () { - return throttle_1(function () { - var player = playerRef.current; - if (player !== null && isReadyRef.current) { - var _player$currentTime; - var playerTime = (_player$currentTime = player.currentTime()) !== null && _player$currentTime !== void 0 ? _player$currentTime : currentTimeRef.current; - if (hasMultiItems && srcIndexRef.current > 0) { - playerTime = playerTime + targets[srcIndexRef.current].altStart; - } - var activeSegment = getActiveSegment(playerTime); - // the active segment has changed - if (activeIdRef.current !== (activeSegment === null || activeSegment === void 0 ? void 0 : activeSegment.id)) { - if (activeSegment === null) { - /** - * Clear currentNavItem and other related state variables to update the tracker - * in structure navigation and highlights within the player. - */ - manifestDispatch({ - item: null, - type: 'switchItem' - }); - setActiveId(null); - setFragmentMarker(null); - } else { - // Set the active segment in state - manifestDispatch({ - item: activeSegment, - type: 'switchItem' - }); - setActiveId(activeSegment.id); - if (!isPlaylist && player.markers) { - var _getMediaFragment = getMediaFragment(activeSegment.id, activeSegment.canvasDuration), - start = _getMediaFragment.start, - end = _getMediaFragment.end; - playerDispatch({ - endTime: end, - startTime: start, - type: 'setTimeFragment' - }); - if (start !== end) { - // don't let marker extend past the end of the canvas - var markerEnd = end > activeSegment.canvasDuration ? activeSegment.canvasDuration : end; - setFragmentMarker({ - time: start, - duration: markerEnd - start, - text: start, - "class": 'ramp--track-marker--fragment' - }); - } else { - // to prevent zero duration fragments I suppose - setFragmentMarker(null); - } - } else if (fragmentMarker !== null) { - setFragmentMarker(null); - } - } - } - } - }, 10); - }, []); - - /** - * Update global state only when a user pause the player by using the - * player interface or keyboard shortcuts - */ - var handlePause = function handlePause() { - if (isPlayingRef.current) { + /* + Only continue the click action if not both start and end times of + the timespan are not outside Canvas' duration + */ + if (inRange) { playerDispatch({ - isPlaying: false, - type: 'setPlayingStatus' + clickedUrl: itemId, + type: 'navClick' }); - } - }; - - /** - * Toggle play/pause on video touch for mobile browsers - * @param {Object} e onTouchEnd event - */ - var mobilePlayToggle = function mobilePlayToggle(e) { - if (e.changedTouches[0].clientX == touchX && e.changedTouches[0].clientY == touchY) { - if (player.paused()) { - player.play(); - } else { - player.pause(); + liRef.current.isClicked = true; + if (sectionRef.current) { + sectionRef.current.isClicked = true; } } + }); + return { + isActiveSection: isActiveSection, + isActiveLi: isActiveLi, + handleClick: handleClick, + canvasIndex: canvasIndex, + currentNavItem: currentNavItem, + isPlaylist: isPlaylist }; - - /** - * Save coordinates of touch start for comparison to touch end to prevent play/pause - * when user is scrolling. - * @param {Object} e onTouchStart event - */ - var touchX = null; - var touchY = null; - var saveTouchStartCoords = function saveTouchStartCoords(e) { - touchX = e.touches[0].clientX; - touchY = e.touches[0].clientY; +}; +var useTranscripts = function useTranscripts(_ref6) { + var manifestUrl = _ref6.manifestUrl, + playerID = _ref6.playerID, + setCurrentTime = _ref6.setCurrentTime, + transcripts = _ref6.transcripts; + var manifestState = useContext(ManifestStateContext); + var playerState = useContext(PlayerStateContext); + var NO_TRANSCRIPTS_MSG = 'No valid Transcript(s) found, please check again.'; + var INVALID_URL_MSG = 'Invalid URL for transcript, please check again.'; + var INVALID_VTT = 'Invalid WebVTT file, please check again.'; + var INVALID_TIMESTAMP = 'Invalid timestamp format in cue(s), please check again.'; + var NO_SUPPORT_MSG = 'Transcript format is not supported, please check again.'; + var abortController = new AbortController(); + var canvasIndexRef = useRef(); + var setCanvasIndex = function setCanvasIndex(c) { + abortController.abort(); + canvasIndexRef.current = c; }; + var playerRef = useRef(null); + var playerIntervalRef = useRef(null); + var _useState19 = useState(true), + _useState20 = _slicedToArray(_useState19, 2), + isEmpty = _useState20[0], + setIsEmpty = _useState20[1]; + var _useState21 = useState(true), + _useState22 = _slicedToArray(_useState21, 2), + isLoading = _useState22[0], + setIsLoading = _useState22[1]; + var _useState23 = useState([]), + _useState24 = _slicedToArray(_useState23, 2), + transcript = _useState24[0], + setTranscript = _useState24[1]; + var _useState25 = useState([]), + _useState26 = _slicedToArray(_useState25, 2), + transcriptsList = _useState26[0], + setTranscriptsList = _useState26[1]; + var _useState27 = useState({ + title: null, + filename: null, + id: null, + tUrl: null, + tType: null, + tFileExt: null, + isMachineGen: false, + tError: null + }), + _useState28 = _slicedToArray(_useState27, 2), + transcriptInfo = _useState28[0], + setTranscriptInfo = _useState28[1]; + var _useState29 = useState([]), + _useState30 = _slicedToArray(_useState29, 2), + canvasTranscripts = _useState30[0], + setCanvasTranscripts = _useState30[1]; + // Store transcript data in state to avoid re-requesting file contents + var _useState31 = useState([]), + _useState32 = _slicedToArray(_useState31, 2), + cachedTranscripts = _useState32[0], + setCachedTranscripts = _useState32[1]; + var _useState33 = useState(), + _useState34 = _slicedToArray(_useState33, 2), + selectedTranscript = _useState34[0], + setSelectedTranscript = _useState34[1]; /** - * Get the segment, which encapsulates the current time of the playhead, - * from a list of media fragments in the current canvas. - * @param {Number} time playhead's current time + * Start an interval at the start of the component to poll the + * canvasindex attribute changes in the player on the page */ - var getActiveSegment = function getActiveSegment(time) { - // Adjust time for multi-item canvases - var currentTime = time; - if (hasMultiItems) { - currentTime = currentTime + targets[srcIndex].altStart; - } - if (playlist.isPlaylist) { - // For playlists timespans and canvasIdex are mapped one-to-one - return canvasSegmentsRef.current[cIndexRef.current]; + useEffect(function () { + if (manifestState && playerState) { + canvasIndexRef.current = manifestState.canvasIndex; + playerRef.current = playerState.player; } else { - // Find the relevant media segment from the structure - var _iterator = _createForOfIteratorHelper$2(canvasSegmentsRef.current), - _step; - try { - for (_iterator.s(); !(_step = _iterator.n()).done;) { - var segment = _step.value; - var id = segment.id, - isCanvas = segment.isCanvas, - _canvasIndex = segment.canvasIndex; - if (_canvasIndex == cIndexRef.current + 1) { - // Canvases without structure has the Canvas information - // in Canvas-level item as a navigable link - if (isCanvas) { - return segment; - } - var segmentRange = getMediaFragment(id, canvasDuration); - var isInRange = checkSrcRange(segmentRange, canvasDuration); - var isInSegment = currentTime >= segmentRange.start && currentTime < segmentRange.end; - if (isInSegment && isInRange) { - return segment; - } + playerIntervalRef.current = setInterval(function () { + var domPlayer = document.getElementById(playerID); + if (!domPlayer) { + console.warn("Cannot find player, ".concat(playerID, " on page. Transcript synchronization is disabled")); + // Inaccessible canvas => stop loading spinner + setIsLoading(false); + } else { + if (domPlayer.player) playerRef.current = domPlayer.player;else playerRef.current = domPlayer; + } + if (playerRef.current) { + var cIndex = parseInt(playerRef.current.canvasIndex); + if (Number.isNaN(cIndex)) cIndex = 0; + if (cIndex !== canvasIndexRef.current) { + // Clear the transcript text in the component + setTranscript([]); + setCanvasIndex(cIndex); + setCurrentTime(playerRef.current.currentTime()); } } - } catch (err) { - _iterator.e(err); - } finally { - _iterator.f(); - } - return null; + }, 500); + } + if (playerRef.current) { + playerRef.current.on('timeupdate', function () { + setCurrentTime(playerRef.current.currentTime()); + }); + } + }, [manifestState]); + useEffect(function () { + if ((transcripts === null || transcripts === void 0 ? void 0 : transcripts.length) === 0 && !manifestUrl) { + // When both required props are invalid + setIsLoading(false); + setTranscript([]); + setTranscriptInfo({ + tType: TRANSCRIPT_TYPES.noTranscript, + id: '', + tError: NO_TRANSCRIPTS_MSG + }); + } else { + loadTranscripts(transcripts); } - }; - /** - * Create an interval to run every second to update display for the timer - * for inaccessible canvas message display. Using useCallback to cache the - * function as this doesn't need to change with component re-renders - */ - var createDisplayTimeInterval = React.useCallback(function () { - if (!autoAdvanceRef.current) return; - var createTime = new Date().getTime(); - messageIntervalRef.current = setInterval(function () { - var now = new Date().getTime(); - var timeRemaining = (CANVAS_MESSAGE_TIMEOUT - (now - createTime)) / 1000; - if (timeRemaining > 0) { - setMessageTime(Math.ceil(timeRemaining)); - } else { - clearDisplayTimeInterval(); - } - }, 1000); + // Clean up state when the component unmounts + return function () { + clearInterval(playerIntervalRef.current); + }; }, []); /** - * Cleanup interval created for timer display for inaccessible message + * If a list of transcripts is given in the props, then sanitize them + * to match the expected format in the component. + * If not fallback to reading transcripts from a given manifest URL. + * @param {Array} transcripts list of transcripts from props */ - var clearDisplayTimeInterval = React.useCallback(function () { - clearInterval(messageIntervalRef.current); - messageIntervalRef.current = null; - }); - return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", { - "data-vjs-player": true, - "data-canvasindex": cIndexRef.current - }, canvasIsEmptyRef.current && /*#__PURE__*/React.createElement("div", { - "data-testid": "inaccessible-message-display" - // These styles needs to be inline for the poster to display within the Video boundaries - , - style: { - position: !playerRef.current ? 'relative' : 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - fontSize: 'medium', - color: '#fff', - backgroundColor: 'black', - zIndex: 101, - aspectRatio: !playerRef.current ? '16/9' : '', - textAlign: 'center' - } - }, /*#__PURE__*/React.createElement("p", { - className: "ramp--media-player_inaccessible-message-content", - "data-testid": "inaccessible-message-content", - dangerouslySetInnerHTML: { - __html: placeholderText - } - }), /*#__PURE__*/React.createElement("div", { - className: "ramp--media-player_inaccessible-message-buttons" - }, canvasIndex >= 1 && /*#__PURE__*/React.createElement("button", { - "aria-label": "Go back to previous item", - onClick: function onClick() { - return loadPrevOrNext(canvasIndex - 1, true); - }, - "data-testid": "inaccessible-previous-button" - }, /*#__PURE__*/React.createElement(SectionButtonIcon, { - flip: true - }), " Previous"), canvasIndex != lastCanvasIndex && /*#__PURE__*/React.createElement("button", { - "aria-label": "Go to next item", - onClick: function onClick() { - return loadPrevOrNext(canvasIndex + 1, true); - }, - "data-testid": "inaccessible-next-button" - }, "Next ", /*#__PURE__*/React.createElement(SectionButtonIcon, null))), canvasIndex != lastCanvasIndex && /*#__PURE__*/React.createElement("p", { - "data-testid": "inaccessible-message-timer", - className: "ramp--media-player_inaccessible-message-timer ".concat(autoAdvanceRef.current ? '' : 'hidden') - }, "Next item in ".concat(messageTime, " second").concat(messageTime === 1 ? '' : 's'))), /*#__PURE__*/React.createElement("video", { - "data-testid": "videojs-".concat(isVideo ? 'video' : 'audio', "-element"), - "data-canvasindex": cIndexRef.current, - ref: videoJSRef, - className: "video-js vjs-big-play-centered vjs-theme-ramp vjs-disabled ".concat(IS_ANDROID ? 'is-mobile' : ''), - onTouchStart: saveTouchStartCoords, - onTouchEnd: mobilePlayToggle, - style: { - display: "".concat(canvasIsEmptyRef.current ? 'none' : '') + var loadTranscripts = /*#__PURE__*/function () { + var _ref7 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee(transcripts) { + var allTranscripts; + return regenerator.wrap(function _callee$(_context) { + while (1) switch (_context.prev = _context.next) { + case 0: + if (!((transcripts === null || transcripts === void 0 ? void 0 : transcripts.length) > 0 + // transcripts prop is processed first if given + )) { + _context.next = 6; + break; + } + _context.next = 3; + return sanitizeTranscripts(transcripts); + case 3: + _context.t0 = _context.sent; + _context.next = 9; + break; + case 6: + _context.next = 8; + return readSupplementingAnnotations(manifestUrl); + case 8: + _context.t0 = _context.sent; + case 9: + allTranscripts = _context.t0; + setTranscriptsList(allTranscripts !== null && allTranscripts !== void 0 ? allTranscripts : []); + initTranscriptData(allTranscripts !== null && allTranscripts !== void 0 ? allTranscripts : []); + case 12: + case "end": + return _context.stop(); + } + }, _callee); + })); + return function loadTranscripts(_x) { + return _ref7.apply(this, arguments); + }; + }(); + var initTranscriptData = function initTranscriptData(allTranscripts) { + var _getCanvasT, _getTItems; + // When canvasIndex updates -> return + if (abortController.signal.aborted) return; + var getCanvasT = function getCanvasT(tr) { + return tr.filter(function (t) { + return t.canvasId == canvasIndexRef.current; + }); + }; + var getTItems = function getTItems(tr) { + return getCanvasT(tr)[0].items; + }; + /** + * When transcripts prop is empty + * OR the respective canvas doesn't have transcript data + * OR canvas' transcript items list is empty + */ + if (!(allTranscripts !== null && allTranscripts !== void 0 && allTranscripts.length) > 0 || !((_getCanvasT = getCanvasT(allTranscripts)) !== null && _getCanvasT !== void 0 && _getCanvasT.length) > 0 || !((_getTItems = getTItems(allTranscripts)) !== null && _getTItems !== void 0 && _getTItems.length) > 0) { + setIsEmpty(true); + setTranscript([]); + setStateVar(undefined); + } else { + setIsEmpty(false); + var cTranscripts = getCanvasT(allTranscripts)[0]; + setCanvasTranscripts(cTranscripts.items); + setStateVar(cTranscripts.items[0]); } - })), (hasStructure || playlist.isPlaylist) && /*#__PURE__*/React.createElement("div", { - className: "vjs-track-scrubber-container hidden", - ref: trackScrubberRef, - id: "track_scrubber" - }, /*#__PURE__*/React.createElement("p", { - className: "vjs-time track-currenttime", - role: "presentation" - }), /*#__PURE__*/React.createElement("span", { - type: "range", - "aria-label": "Track scrubber", - role: "slider", - tabIndex: 0, - className: "vjs-track-scrubber", - style: { - width: '100%' + }; + useEffect(function () { + if ((transcriptsList === null || transcriptsList === void 0 ? void 0 : transcriptsList.length) > 0 && canvasIndexRef.current != undefined) { + var cTranscripts = transcriptsList.filter(function (tr) { + return tr.canvasId == canvasIndexRef.current; + })[0]; + setCanvasTranscripts(cTranscripts.items); + setStateVar(cTranscripts.items[0]); } - }, !IS_TOUCH_ONLY && /*#__PURE__*/React.createElement("span", { - className: "tooltiptext", - ref: scrubberTooltipRef, - "aria-hidden": true, - role: "presentation" - })), /*#__PURE__*/React.createElement("p", { - className: "vjs-time track-duration", - role: "presentation" - }))); -} -VideoJSPlayer.propTypes = { - isVideo: PropTypes.bool, - hasMultipleCanvases: PropTypes.bool, - isPlaylist: PropTypes.bool, - trackScrubberRef: PropTypes.object, - scrubberTooltipRef: PropTypes.object, - tracks: PropTypes.array, - placeholderText: PropTypes.string, - renderingFiles: PropTypes.array, - enableFileDownload: PropTypes.bool, - cancelAutoAdvance: PropTypes.func, - loadPrevOrNext: PropTypes.func, - lastCanvasIndex: PropTypes.number, - videoJSOptions: PropTypes.object -}; + }, [canvasIndexRef.current]); // helps to load initial transcript with async req -var Play = "Play"; -var Pause = "Pause"; -var Replay = "Replay"; -var Duration = "Duration"; -var LIVE = "LIVE"; -var Loaded = "Loaded"; -var Progress = "Progress"; -var Fullscreen = "Fullscreen"; -var Mute = "Mute"; -var Unmute = "Unmute"; -var Subtitles = "Subtitles"; -var Captions = "Captions"; -var Chapters = "Chapters"; -var Descriptions = "Descriptions"; -var Close = "Close"; -var Text = "Text"; -var White = "White"; -var Black = "Black"; -var Red = "Red"; -var Green = "Green"; -var Blue = "Blue"; -var Yellow = "Yellow"; -var Magenta = "Magenta"; -var Cyan = "Cyan"; -var Background = "Background"; -var Window = "Window"; -var Transparent = "Transparent"; -var Opaque = "Opaque"; -var None = "None"; -var Raised = "Raised"; -var Depressed = "Depressed"; -var Uniform = "Uniform"; -var Casual = "Casual"; -var Script = "Script"; -var Reset = "Reset"; -var Done = "Done"; -var Color = "Color"; -var Opacity = "Opacity"; -var en = { - "Audio Player": "Audio Player", - "Video Player": "Video Player", - Play: Play, - Pause: Pause, - Replay: Replay, - "Current Time": "Current Time", - Duration: Duration, - "Remaining Time": "Remaining Time", - "Stream Type": "Stream Type", - LIVE: LIVE, - "Seek to live, currently behind live": "Seek to live, currently behind live", - "Seek to live, currently playing live": "Seek to live, currently playing live", - Loaded: Loaded, - Progress: Progress, - "Progress Bar": "Progress Bar", - "progress bar timing: currentTime={1} duration={2}": "{1} of {2}", - Fullscreen: Fullscreen, - "Exit Fullscreen": "Exit Fullscreen", - Mute: Mute, - Unmute: Unmute, - "Playback Rate": "Playback Rate", - Subtitles: Subtitles, - "subtitles off": "subtitles off", - Captions: Captions, - "captions off": "captions off", - Chapters: Chapters, - Descriptions: Descriptions, - "descriptions off": "descriptions off", - "Audio Track": "Audio Track", - "Volume Level": "Volume Level", - "You aborted the media playback": "You aborted the media playback", - "A network error caused the media download to fail part-way.": "A network error caused the media download to fail part-way.", - "The media could not be loaded, either because the server or network failed or because the format is not supported.": "The media could not be loaded, either because the server or network failed or because the format is not supported.", - "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.", - "No compatible source was found for this media.": "No compatible source was found for this media.", - "The media is encrypted and we do not have the keys to decrypt it.": "The media is encrypted and we do not have the keys to decrypt it.", - "Play Video": "Play Video", - Close: Close, - "Close Modal Dialog": "Close Modal Dialog", - "Modal Window": "Modal Window", - "This is a modal window": "This is a modal window", - "This modal can be closed by pressing the Escape key or activating the close button.": "This modal can be closed by pressing the Escape key or activating the close button.", - ", opens captions settings dialog": ", opens captions settings dialog", - ", opens subtitles settings dialog": ", opens subtitles settings dialog", - ", opens descriptions settings dialog": ", opens descriptions settings dialog", - ", selected": ", selected", - "captions settings": "captions settings", - "subtitles settings": "subtitles settings", - "descriptions settings": "descriptions settings", - Text: Text, - White: White, - Black: Black, - Red: Red, - Green: Green, - Blue: Blue, - Yellow: Yellow, - Magenta: Magenta, - Cyan: Cyan, - Background: Background, - Window: Window, - Transparent: Transparent, - "Semi-Transparent": "Semi-Transparent", - Opaque: Opaque, - "Font Size": "Font Size", - "Text Edge Style": "Text Edge Style", - None: None, - Raised: Raised, - Depressed: Depressed, - Uniform: Uniform, - "Drop shadow": "Drop shadow", - "Font Family": "Font Family", - "Proportional Sans-Serif": "Proportional Sans-Serif", - "Monospace Sans-Serif": "Monospace Sans-Serif", - "Proportional Serif": "Proportional Serif", - "Monospace Serif": "Monospace Serif", - Casual: Casual, - Script: Script, - "Small Caps": "Small Caps", - Reset: Reset, - "restore all settings to the default values": "restore all settings to the default values", - Done: Done, - "Caption Settings Dialog": "Caption Settings Dialog", - "Beginning of dialog window. Escape will cancel and close the window.": "Beginning of dialog window. Escape will cancel and close the window.", - "End of dialog window.": "End of dialog window.", - "{1} is loading.": "{1} is loading.", - "Exit Picture-in-Picture": "Exit Picture-in-Picture", - "Picture-in-Picture": "Picture-in-Picture", - "No content": "No content", - Color: Color, - Opacity: Opacity, - "Text Background": "Text Background", - "Caption Area Background": "Caption Area Background", - "Playing in Picture-in-Picture": "Playing in Picture-in-Picture", - "Skip backward {1} seconds": "Skip backward {1} seconds", - "Skip forward {1} seconds": "Skip forward {1} seconds" -}; - -function ownKeys$3(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } -function _objectSpread$3(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$3(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$3(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } -var PLAYER_ID = "iiif-media-player"; -var MediaPlayer = function MediaPlayer(_ref) { - var _ref$enableFileDownlo = _ref.enableFileDownload, - enableFileDownload = _ref$enableFileDownlo === void 0 ? false : _ref$enableFileDownlo, - _ref$enablePIP = _ref.enablePIP, - enablePIP = _ref$enablePIP === void 0 ? false : _ref$enablePIP, - _ref$enablePlaybackRa = _ref.enablePlaybackRate, - enablePlaybackRate = _ref$enablePlaybackRa === void 0 ? false : _ref$enablePlaybackRa, - _ref$enableTitleLink = _ref.enableTitleLink, - enableTitleLink = _ref$enableTitleLink === void 0 ? false : _ref$enableTitleLink, - _ref$withCredentials = _ref.withCredentials, - withCredentials = _ref$withCredentials === void 0 ? false : _ref$withCredentials, - _ref$language = _ref.language, - language = _ref$language === void 0 ? 'en' : _ref$language; - var manifestState = useManifestState(); - var playerState = usePlayerState(); - var playerDispatch = usePlayerDispatch(); - var manifestDispatch = useManifestDispatch(); - var _useErrorBoundary = useErrorBoundary(), - showBoundary = _useErrorBoundary.showBoundary; - var _React$useState = React.useState({ - error: '', - sources: [], - tracks: [], - poster: null - }), - _React$useState2 = _slicedToArray(_React$useState, 2), - playerConfig = _React$useState2[0], - setPlayerConfig = _React$useState2[1]; - var _React$useState3 = React.useState(true), - _React$useState4 = _slicedToArray(_React$useState3, 2), - firstLoad = _React$useState4[0], - setFirstLoad = _React$useState4[1]; - var _React$useState5 = React.useState(false), - _React$useState6 = _slicedToArray(_React$useState5, 2), - ready = _React$useState6[0], - setReady = _React$useState6[1]; - var _React$useState7 = React.useState(canvasIndex), - _React$useState8 = _slicedToArray(_React$useState7, 2), - cIndex = _React$useState8[0], - setCIndex = _React$useState8[1]; - var _React$useState9 = React.useState(), - _React$useState10 = _slicedToArray(_React$useState9, 2), - isMultiSourced = _React$useState10[0], - setIsMultiSourced = _React$useState10[1]; - var _React$useState11 = React.useState(false), - _React$useState12 = _slicedToArray(_React$useState11, 2), - isMultiCanvased = _React$useState12[0], - setIsMultiCanvased = _React$useState12[1]; - var _React$useState13 = React.useState(0), - _React$useState14 = _slicedToArray(_React$useState13, 2), - lastCanvasIndex = _React$useState14[0], - setLastCanvasIndex = _React$useState14[1]; - var _React$useState15 = React.useState(), - _React$useState16 = _slicedToArray(_React$useState15, 2), - isVideo = _React$useState16[0], - setIsVideo = _React$useState16[1]; - var _React$useState17 = React.useState(), - _React$useState18 = _slicedToArray(_React$useState17, 2), - options = _React$useState18[0], - setOptions = _React$useState18[1]; - var _React$useState19 = React.useState(), - _React$useState20 = _slicedToArray(_React$useState19, 2), - renderingFiles = _React$useState20[0], - setRenderingFiles = _React$useState20[1]; - var canvasIndex = manifestState.canvasIndex, - allCanvases = manifestState.allCanvases, - manifest = manifestState.manifest, - canvasIsEmpty = manifestState.canvasIsEmpty, - srcIndex = manifestState.srcIndex, - targets = manifestState.targets, - playlist = manifestState.playlist, - autoAdvance = manifestState.autoAdvance, - hasStructure = manifestState.hasStructure, - customStart = manifestState.customStart, - renderings = manifestState.renderings; - var playerFocusElement = playerState.playerFocusElement, - currentTime = playerState.currentTime; - var currentTimeRef = React.useRef(); - currentTimeRef.current = currentTime; - var canvasIndexRef = React.useRef(); - canvasIndexRef.current = canvasIndex; - var autoAdvanceRef = React.useRef(); - autoAdvanceRef.current = autoAdvance; - var lastCanvasIndexRef = React.useRef(); - lastCanvasIndexRef.current = lastCanvasIndex; - var trackScrubberRef = React.useRef(); - var timeToolRef = React.useRef(); - var videoJSLangMap = React.useRef('{}'); - var canvasMessageTimerRef = React.useRef(null); - - // FIXME:: Dynamic language imports break with rollup configuration when packaging - // Using dynamic imports to enforce code-splitting in webpack - // https://webpack.js.org/api/module-methods/#dynamic-expressions-in-import - var loadVideoJSLanguageMap = React.useMemo(function () { - return /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee() { - var resources; - return regenerator.wrap(function _callee$(_context) { - while (1) switch (_context.prev = _context.next) { + var setStateVar = /*#__PURE__*/function () { + var _ref8 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee2(transcript) { + var _transcript, id, title, filename, url, isMachineGen, format, cached, _cached$, tData, tFileExt, tType, tError; + return regenerator.wrap(function _callee2$(_context2) { + while (1) switch (_context2.prev = _context2.next) { case 0: - _context.prev = 0; - _context.next = 3; - return import("video.js/dist/lang/".concat(language, ".json")); - case 3: - resources = _context.sent; - videoJSLangMap.current = JSON.stringify(resources); - _context.next = 11; + if (!(!transcript || transcript == undefined)) { + _context2.next = 5; + break; + } + setIsEmpty(true); + setIsLoading(false); + setTranscriptInfo({ + tType: TRANSCRIPT_TYPES.noTranscript, + id: '', + tError: NO_TRANSCRIPTS_MSG + }); + return _context2.abrupt("return"); + case 5: + // set isEmpty flag to render transcripts UI + setIsEmpty(false); + _transcript = transcript, id = _transcript.id, title = _transcript.title, filename = _transcript.filename, url = _transcript.url, isMachineGen = _transcript.isMachineGen, format = _transcript.format; // Check cached transcript data + cached = cachedTranscripts.filter(function (ct) { + return ct.id == id && ct.canvasId == canvasIndexRef.current; + }); + if (!((cached === null || cached === void 0 ? void 0 : cached.length) > 0)) { + _context2.next = 15; + break; + } + // Load cached transcript data into the component + _cached$ = cached[0], tData = _cached$.tData, tFileExt = _cached$.tFileExt, tType = _cached$.tType, tError = _cached$.tError; + setTranscript(tData); + setTranscriptInfo({ + title: title, + filename: filename, + id: id, + isMachineGen: isMachineGen, + tType: tType, + tUrl: url, + tFileExt: tFileExt, + tError: tError + }); + setSelectedTranscript(url); + _context2.next = 17; break; - case 7: - _context.prev = 7; - _context.t0 = _context["catch"](0); - console.warn("".concat(language, " is not available, defaulting to English")); - videoJSLangMap.current = JSON.stringify(en); - case 11: + case 15: + _context2.next = 17; + return Promise.resolve(parseTranscriptData(url, canvasIndexRef.current, format)).then(function (value) { + if (value != null) { + var _tData = value.tData, + tUrl = value.tUrl, + _tType = value.tType, + _tFileExt = value.tFileExt; + var newError = ''; + switch (_tType) { + case TRANSCRIPT_TYPES.invalid: + newError = INVALID_URL_MSG; + break; + case TRANSCRIPT_TYPES.noTranscript: + newError = NO_TRANSCRIPTS_MSG; + break; + case TRANSCRIPT_TYPES.noSupport: + newError = NO_SUPPORT_MSG; + break; + case TRANSCRIPT_TYPES.invalidVTT: + newError = INVALID_VTT; + break; + case TRANSCRIPT_TYPES.invalidTimestamp: + newError = INVALID_TIMESTAMP; + break; + } + setTranscript(_tData); + setTranscriptInfo({ + title: title, + filename: filename, + id: id, + isMachineGen: isMachineGen, + tType: _tType, + tUrl: tUrl, + tFileExt: _tFileExt, + tError: newError + }); + setSelectedTranscript(tUrl); + transcript = _objectSpread$5(_objectSpread$5({}, transcript), {}, { + tType: _tType, + tData: _tData, + tFileExt: _tFileExt, + canvasId: canvasIndexRef.current, + tError: newError + }); + // Cache the transcript info + setCachedTranscripts([].concat(_toConsumableArray(cachedTranscripts), [transcript])); + } + }); + case 17: + setIsLoading(false); + case 18: case "end": - return _context.stop(); + return _context2.stop(); + } + }, _callee2); + })); + return function setStateVar(_x2) { + return _ref8.apply(this, arguments); + }; + }(); + var selectTranscript = useCallback(function (selectedId) { + var selectedTranscript = canvasTranscripts.filter(function (tr) { + return tr.id === selectedId; + }); + setStateVar(selectedTranscript[0]); + }, [canvasTranscripts]); + return { + canvasIndexRef: canvasIndexRef, + canvasTranscripts: canvasTranscripts, + isEmpty: isEmpty, + isLoading: isLoading, + NO_SUPPORT_MSG: NO_SUPPORT_MSG, + playerRef: playerRef, + selectedTranscript: selectedTranscript, + selectTranscript: selectTranscript, + transcript: transcript, + transcriptInfo: transcriptInfo + }; +}; + +var classCallCheck = createCommonjsModule(function (module) { +function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } +} +module.exports = _classCallCheck, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var _classCallCheck = /*@__PURE__*/getDefaultExportFromCjs(classCallCheck); + +var createClass = createCommonjsModule(function (module) { +function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, toPropertyKey(descriptor.key), descriptor); + } +} +function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + Object.defineProperty(Constructor, "prototype", { + writable: false + }); + return Constructor; +} +module.exports = _createClass, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var _createClass = /*@__PURE__*/getDefaultExportFromCjs(createClass); + +var assertThisInitialized = createCommonjsModule(function (module) { +function _assertThisInitialized(self) { + if (self === void 0) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + return self; +} +module.exports = _assertThisInitialized, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var _assertThisInitialized = /*@__PURE__*/getDefaultExportFromCjs(assertThisInitialized); + +var getPrototypeOf = createCommonjsModule(function (module) { +function _getPrototypeOf(o) { + module.exports = _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { + return o.__proto__ || Object.getPrototypeOf(o); + }, module.exports.__esModule = true, module.exports["default"] = module.exports; + return _getPrototypeOf(o); +} +module.exports = _getPrototypeOf, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var _getPrototypeOf = /*@__PURE__*/getDefaultExportFromCjs(getPrototypeOf); + +var superPropBase = createCommonjsModule(function (module) { +function _superPropBase(object, property) { + while (!Object.prototype.hasOwnProperty.call(object, property)) { + object = getPrototypeOf(object); + if (object === null) break; + } + return object; +} +module.exports = _superPropBase, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var get = createCommonjsModule(function (module) { +function _get() { + if (typeof Reflect !== "undefined" && Reflect.get) { + module.exports = _get = Reflect.get.bind(), module.exports.__esModule = true, module.exports["default"] = module.exports; + } else { + module.exports = _get = function _get(target, property, receiver) { + var base = superPropBase(target, property); + if (!base) return; + var desc = Object.getOwnPropertyDescriptor(base, property); + if (desc.get) { + return desc.get.call(arguments.length < 3 ? target : receiver); + } + return desc.value; + }, module.exports.__esModule = true, module.exports["default"] = module.exports; + } + return _get.apply(this, arguments); +} +module.exports = _get, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var _get = /*@__PURE__*/getDefaultExportFromCjs(get); + +var setPrototypeOf = createCommonjsModule(function (module) { +function _setPrototypeOf(o, p) { + module.exports = _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { + o.__proto__ = p; + return o; + }, module.exports.__esModule = true, module.exports["default"] = module.exports; + return _setPrototypeOf(o, p); +} +module.exports = _setPrototypeOf, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var inherits = createCommonjsModule(function (module) { +function _inherits(subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function"); + } + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + writable: true, + configurable: true + } + }); + Object.defineProperty(subClass, "prototype", { + writable: false + }); + if (superClass) setPrototypeOf(subClass, superClass); +} +module.exports = _inherits, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var _inherits = /*@__PURE__*/getDefaultExportFromCjs(inherits); + +var possibleConstructorReturn = createCommonjsModule(function (module) { +var _typeof = _typeof_1["default"]; + +function _possibleConstructorReturn(self, call) { + if (call && (_typeof(call) === "object" || typeof call === "function")) { + return call; + } else if (call !== void 0) { + throw new TypeError("Derived constructors may only return object or undefined"); + } + return assertThisInitialized(self); +} +module.exports = _possibleConstructorReturn, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var _possibleConstructorReturn = /*@__PURE__*/getDefaultExportFromCjs(possibleConstructorReturn); + +function _createSuper$6(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$6(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } +function _isNativeReflectConstruct$6() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +var SeekBar = videojs.getComponent('SeekBar'); + +/** + * Custom component to show progress of playback built on top of + * Video.js' SeekBar component. This customization allows to display + * multiple-sources in a single Canvas as a contiguous time-block for + * the sum of durations of each source and clipped playlist items with + * blocked ranges. + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} props.options + * @param {Number} props.options.nextItemClicked callback func to switch current source + * when displaying multiple sources in a single instance + */ +var VideoJSProgress = /*#__PURE__*/function (_SeekBar) { + _inherits(VideoJSProgress, _SeekBar); + var _super = _createSuper$6(VideoJSProgress); + function VideoJSProgress(player, options) { + var _this; + _classCallCheck(this, VideoJSProgress); + _this = _super.call(this, player, options); + /** + * Set start values for progress bar + * @param {Number} start canvas start time + */ + _defineProperty(_assertThisInitialized(_this), "initializeProgress", function (start) { + _this.setProgress(start); + _this.setInitTime(start); + _this.player.currentTime(start); + }); + _this.addClass('vjs-custom-progress-bar'); + _this.setAttribute('data-testid', 'videojs-custom-progressbar'); + _this.setAttribute('tabindex', 0); + _this.player = player; + _this.options = options; + _this.selectSource = _this.options.nextItemClicked; + _this.playerEventListener; + _this.initTimeRef = /*#__PURE__*/createRef(); + _this.progressRef = /*#__PURE__*/createRef(); + _this.canvasTargetsRef = /*#__PURE__*/createRef(); + _this.srcIndexRef = /*#__PURE__*/createRef(); + _this.isMultiSourceRef = /*#__PURE__*/createRef(); + _this.currentTimeRef = /*#__PURE__*/createRef(); + _this.pointerDragged = false; + _this.totalDuration; + + // Retreive child elements in SeekBar to use for custom updates + _this.playProgress = _this.getChild('PlayProgressBar'); + _this.loadProgress = _this.getChild('LoadProgressBar'); + _this.player.on('ready', function () { + _this.initializeEl(); + _this.updateComponent(); + }); + _this.player.on('loadstart', function () { + _this.updateComponent(); + _this.buildProgressBar(); + }); + _this.player.on('loadeddata', function () { + _this.setInitTime(_this.player.currentTime()); + }); + + // Update our progress bar after the user leaves full screen + _this.player.on('fullscreenchange', function (e) { + if (!_this.player.isFullscreen()) { + _this.setProgress(_this.player.currentTime()); + } + }); + + // Clear interval upon player disposal + _this.player.on('dispose', function () { + clearInterval(_this.playerEventListener); + }); + return _this; + } + _createClass(VideoJSProgress, [{ + key: "setInitTime", + value: function setInitTime(t) { + this.initTimeRef.current = t; + } + }, { + key: "setSrcIndex", + value: function setSrcIndex(i) { + this.srcIndexRef.current = i; + } + }, { + key: "setProgress", + value: function setProgress(p) { + this.progressRef.current = p; + } + }, { + key: "setCanvasTargets", + value: function setCanvasTargets(t) { + this.canvasTargetsRef.current = t; + this.totalDuration = t.reduce(function (acc, c) { + return acc + c.duration; + }, 0); + } + }, { + key: "setIsMultiSource", + value: function setIsMultiSource(m) { + this.isMultiSourceRef.current = m; + } + }, { + key: "setCurrentTime", + value: function setCurrentTime(t) { + this.currentTimeRef.current = t; + } + }, { + key: "updateComponent", + value: + // Update component's variables on Canvas changes + function updateComponent() { + var _this2 = this; + var _this$player = this.player, + srcIndex = _this$player.srcIndex, + targets = _this$player.targets; + this.setSrcIndex(srcIndex); + this.setCanvasTargets(targets); + var cTimes = targets[srcIndex]; + if (cTimes.customStart > cTimes.start) { + this.initializeProgress(cTimes.customStart); + } else { + this.initializeProgress(cTimes.start); + } + this.setIsMultiSource((targets === null || targets === void 0 ? void 0 : targets.length) > 1 ? true : false); + if (!this.playerEventListener) { + /** + * Using a time interval instead of 'timeupdate event in VideoJS, because Safari + * and other browsers in MacOS stops firing the 'timeupdate' event consistently + * after a while + */ + this.playerEventListener = setInterval(function () { + /** + * Abortable inerval for Safari desktop browsers, for a smoother scrubbing + * experience. + * Mobile devices are excluded since they use native iOS player. + */ + if (IS_SAFARI && !IS_IPHONE) { + _this2.abortableTimeupdateHandler(); + } else { + _this2.timeUpdateHandler(); + } + }, 100); + } + } + + /** + * Use Video.js' update function to update time in mobile devices + * when changing Canvases. + * TODO:: this can probably removed by customizing PlayProgressBar and + * LoadProgressBar components? + */ + }, { + key: "update", + value: function update() { + // Need this to make the other updates work + _get(_getPrototypeOf(VideoJSProgress.prototype), "update", this).call(this); + // Explicitly update played range variable on reload for touch devices + if (IS_TOUCH_ONLY && this.player.currentTime() === 0) { + this.removeClass('played-range'); + document.documentElement.style.setProperty('--range-progress', "calc(".concat(0, "%)")); + } + if (IS_MOBILE && IS_SAFARI && this.player.paused()) { + var _this$player$structSt; + var structStart = (_this$player$structSt = this.player.structStart) !== null && _this$player$structSt !== void 0 ? _this$player$structSt : 0; + if (structStart != 0 && this.player.currentTime() === 0) { + this.player.currentTime(structStart); + var played = Math.min(100, Math.max(0, 100 * (structStart / this.totalDuration))); + this.addClass('played-range'); + document.documentElement.style.setProperty('--range-progress', "calc(".concat(played, "%)")); + this.player.structStart = 0; + } + } else { + return; + } + } + }, { + key: "initializeEl", + value: + // Create progress bar using Video.js' SeekBar component + function initializeEl() { + var _this3 = this; + /** + * Build and append placeholder elements to show blocked ranges, + * especially used in playlist context to present clipped items. + */ + var leftBlock = videojs.dom.createEl('div', { + className: 'block-stripes', + role: 'presentation', + id: 'left-block' + }); + var rightBlock = videojs.dom.createEl('div', { + className: 'block-stripes', + role: 'presentation', + id: 'right-block' + }); + this.el().appendChild(leftBlock); + this.el().appendChild(rightBlock); + + /** + * Add eventlisteners to handle time tool-tip display and progress updates. + * Using pointerup, pointermove, pointerdown events instead of mouseup, + * mousemove, mousedown events to make it work with both mouse pointer + * and touch events. + */ + this.el().addEventListener('mouseenter', function (e) { + _this3.handleMouseMove(e); + }); + this.el().addEventListener('pointerup', function (e) { + if (_this3.pointerDragged) { + _this3.handleMouseUp(e); + } + }); + this.el().addEventListener('pointermove', function (e) { + _this3.handleMouseMove(e); + _this3.pointerDragged = true; + }); + this.el().addEventListener('pointerdown', function (e) { + _this3.handleMouseDown(e); + _this3.pointerDragged = false; + }); + } + }, { + key: "handleMouseMove", + value: function handleMouseMove(e) { + var _this$convertToTime = this.convertToTime(e), + currentTime = _this$convertToTime.currentTime, + offsetx = _this$convertToTime.offsetx; + if (currentTime != undefined) this.setCurrentTime(currentTime); + var mouseTimeDisplay = this.getChild('MouseTimeDisplay'); + if (mouseTimeDisplay) { + var timeTooltip = mouseTimeDisplay.getChild('TimeTooltip'); + var toolTipEl = timeTooltip.el_; + if (currentTime) { + toolTipEl.innerHTML = timeToHHmmss(currentTime); } - }, _callee, null, [[0, 7]]); - })); - }, [language]); - React.useEffect(function () { - if (manifest) { - try { - loadVideoJSLanguageMap(); - /* - Always start from the start time relevant to the Canvas only in playlist contexts, - because canvases related to playlist items always start from the given start. - With regular manifests, the start time could be different when using structured - navigation to switch between canvases. - */ - if (canvasIndex == undefined || canvasIndex < 0) { - throw new Error('Invalid canvas index. Please check your Manifest.'); + var pullTooltip = toolTipEl.clientWidth / 2; + toolTipEl.style.left = "".concat(offsetx - pullTooltip, "px"); + } + } + }, { + key: "handleMouseDown", + value: function handleMouseDown(e) { + // Do nothing when right-click is pressed + if (!IS_TOUCH_ONLY && e.buttons === 2) return; + var _this$convertToTime2 = this.convertToTime(e), + currentTime = _this$convertToTime2.currentTime; + _this$convertToTime2._; + var clickedSrc; + if (this.isMultiSourceRef.current) { + clickedSrc = this.canvasTargetsRef.current.find(function (t) { + var virtualEnd = t.altStart + t.duration; + if (currentTime >= t.altStart && currentTime <= virtualEnd) { + return t; + } + }); + } + if (clickedSrc) { + var _clickedSrc$sIndex, _clickedSrc; + var clickedIndex = (_clickedSrc$sIndex = (_clickedSrc = clickedSrc) === null || _clickedSrc === void 0 ? void 0 : _clickedSrc.sIndex) !== null && _clickedSrc$sIndex !== void 0 ? _clickedSrc$sIndex : 0; + if (clickedIndex != this.srcIndexRef.current) { + this.selectSource(clickedSrc.sIndex, currentTime - clickedSrc.altStart); + this.setSrcIndex(clickedIndex); + } else { + this.player.currentTime(currentTime - clickedSrc.altStart); } - initCanvas(canvasIndex, playlist.isPlaylist); + } else { + this.player.currentTime(currentTime); + } - // Deduct 1 from length to compare against canvasIndex, which starts from 0 - var lastIndex = (allCanvases === null || allCanvases === void 0 ? void 0 : allCanvases.length) - 1; - setIsMultiCanvased(lastIndex > 0); - setLastCanvasIndex(lastIndex || 0); - } catch (e) { - showBoundary(e); + /** + * For touch devices, player.currentTime() update doesn't show the + * played range, even though the player's currentTime is properly set. + * Therefore, update the CSS here explicitly. + */ + if (IS_TOUCH_ONLY) { + var played = Math.min(100, Math.max(0, 100 * (currentTime / this.totalDuration))); + this.player.currentTime(currentTime); + this.addClass('played-range'); + document.documentElement.style.setProperty('--range-progress', "calc(".concat(played, "%)")); } } - return function () { - setReady(false); - setCIndex(0); - playerDispatch({ - player: null, - type: 'updatePlayer' - }); - }; - }, [manifest, canvasIndex, srcIndex]); + }, { + key: "handleMouseUp", + value: function handleMouseUp(e) { + this.handleMouseDown(e); + } + }, { + key: "buildProgressBar", + value: function buildProgressBar() { + var _canvasTargetsRef$cur; + // Reset progress-bar for played range on player reload + this.removeClass('played-range'); + var canvasTargetsRef = this.canvasTargetsRef, + isMultiSourceRef = this.isMultiSourceRef, + player = this.player, + srcIndexRef = this.srcIndexRef, + totalDuration = this.totalDuration; + if (((_canvasTargetsRef$cur = canvasTargetsRef.current) === null || _canvasTargetsRef$cur === void 0 ? void 0 : _canvasTargetsRef$cur.length) > 0) { + var _canvasTargetsRef$cur2 = canvasTargetsRef.current[srcIndexRef.current], + altStart = _canvasTargetsRef$cur2.altStart, + start = _canvasTargetsRef$cur2.start, + end = _canvasTargetsRef$cur2.end, + duration = _canvasTargetsRef$cur2.duration; + var leftBlockEl = document.getElementById('left-block'); + var rightBlockEl = document.getElementById('right-block'); + if (!isMultiSourceRef.current) { + var leftBlock = start * 100 / duration; + var rightBlock = (duration - end) * 100 / duration; - /** - * Handle the display timer for the inaccessbile message when autoplay is turned - * on/off while the current item is a restricted item - */ - React.useEffect(function () { - if (canvasIsEmpty) { - // Clear the existing timer when the autoplay is turned off when displaying - // inaccessible message - if (!autoAdvance && canvasMessageTimerRef.current) { - clearCanvasMessageTimer(); - } else { - // Create a timer to advance to the next Canvas when autoplay is turned - // on when inaccessible message is been displayed - createCanvasMessageTimer(); + // player.isClipped is used in VideoJSTrackScrbber to display accurate + // times for clipped items + rightBlock > 0 ? player.isClipped = true : player.isClipped = false; + if (leftBlockEl) leftBlockEl.style.width = "".concat(leftBlock, "%"); + if (rightBlockEl) { + rightBlockEl.style.width = rightBlock + '%'; + rightBlockEl.style.left = "".concat(100 - rightBlock - leftBlock, "%"); + } + } else { + // Offset of the duration of the current source for multi-source canvases + var leftOffset = Math.min(100, Math.max(0, 100 * (altStart / totalDuration))); + this.playProgress.el_.style.left = "".concat(leftOffset, "%"); + this.loadProgress.el_.style.left = "".concat(leftOffset, "%"); + // Add CSS class to mark the range from zero as played + this.addClass('played-range'); + document.documentElement.style.setProperty('--range-progress', "calc(".concat(leftOffset, "%)")); + } } } - }, [autoAdvanceRef.current]); - - /** - * Initialize the next Canvas to be viewed in the player instance - * @param {Number} canvasId index of the Canvas to be loaded into the player - * @param {Boolean} fromStart flag to indicate how to start new player instance - */ - var initCanvas = function initCanvas(canvasId, fromStart) { - clearCanvasMessageTimer(); - try { - var _getMediaInfo = getMediaInfo({ - manifest: manifest, - canvasIndex: canvasId, - startTime: canvasId === customStart.startIndex && firstLoad ? customStart.startTime : 0, - srcIndex: srcIndex, - isPlaylist: playlist.isPlaylist - }), - isMultiSource = _getMediaInfo.isMultiSource, - sources = _getMediaInfo.sources, - tracks = _getMediaInfo.tracks, - canvasTargets = _getMediaInfo.canvasTargets, - mediaType = _getMediaInfo.mediaType, - error = _getMediaInfo.error, - poster = _getMediaInfo.poster; - setIsVideo(mediaType === 'video'); - manifestDispatch({ - canvasTargets: canvasTargets, - type: 'canvasTargets' - }); - manifestDispatch({ - isMultiSource: isMultiSource, - type: 'hasMultipleItems' - }); - // Set the current time in player from the canvas details - if (fromStart) { - if ((canvasTargets === null || canvasTargets === void 0 ? void 0 : canvasTargets.length) > 0) { - playerDispatch({ - currentTime: canvasTargets[0].altStart, - type: 'setCurrentTime' - }); + /** + * Convert mouse event's offset to timepoint value in the progressbar, + * taking into account blocked ranges, and multi-source canvases. + * @param {Event} e mouse event + * @returns {currentTime: Number, offsetx: Number} + */ + }, { + key: "convertToTime", + value: function convertToTime(e) { + var _e$nativeEvent$target, _this$totalDuration; + var eSrcElement = e.srcElement; + // When clicked on blocked time point + if (eSrcElement.classList.contains('block-stripes')) { + var _this$canvasTargetsRe = this.canvasTargetsRef.current[0], + altStart = _this$canvasTargetsRe.altStart, + end = _this$canvasTargetsRe.end, + _duration = _this$canvasTargetsRe.duration; + if (eSrcElement.id === 'right-block') { + // For right-block: place time tool-tip at the end of playable range + return { + currentTime: end, + offsetx: end / _duration * this.el().clientWidth + }; } else { - playerDispatch({ - currentTime: 0, - type: 'setCurrentTime' - }); + // For left-block: place time tool-tip at the start of playable range + return { + currentTime: altStart, + offsetx: altStart / _duration * this.el().clientWidth + }; } } - setPlayerConfig(_objectSpread$3(_objectSpread$3({}, playerConfig), {}, { - error: error, - sources: sources, - tracks: tracks, - poster: poster - })); - var currentCanvas = allCanvases.find(function (c) { - return c.canvasIndex === canvasId; - }); - if (!currentCanvas.isEmpty) { - // Manifest is taken from manifest state, and is a basic object at this point - // lacking the getLabel() function so we manually retrieve the first label. - var manifestLabel = manifest.label ? Object.values(manifest.label)[0][0] : ''; - // Filter out falsy items in case canvas.label is null or an empty string - var titleText = [manifestLabel, currentCanvas.label].filter(Boolean).join(' - '); - manifestDispatch({ - canvasDuration: currentCanvas.duration, - type: 'canvasDuration' - }); - manifestDispatch({ - canvasLink: { - label: titleText, - id: currentCanvas.canvasId - }, - type: 'canvasLink' - }); - manifestDispatch({ - type: 'setCanvasIsEmpty', - isEmpty: false - }); - } else { - playerDispatch({ - type: 'updatePlayer' - }); - manifestDispatch({ - type: 'setCanvasIsEmpty', - isEmpty: true - }); - // Set poster as playerConfig.error to be used for empty Canvas message in VideoJSPlayer - setPlayerConfig(_objectSpread$3(_objectSpread$3({}, playerConfig), {}, { - error: poster - })); - // Create timer to display the message when autoadvance is ON - if (autoAdvanceRef.current) { - createCanvasMessageTimer(); + var targetX = e.target.getBoundingClientRect().x; + var offsetx = e.nativeEvent != undefined ? e.nativeEvent.offsetX != undefined ? e.nativeEvent.offsetX // iOS and desktop events + : ((_e$nativeEvent$target = e.nativeEvent.targetTouches[0]) === null || _e$nativeEvent$target === void 0 ? void 0 : _e$nativeEvent$target.clientX) - targetX // Android event + : e.offsetX; // fallback in desktop browsers when nativeEvent is undefined + var currentTime; + var duration = (_this$totalDuration = this.totalDuration) !== null && _this$totalDuration !== void 0 ? _this$totalDuration : this.player.duration(); + if (offsetx && offsetx != undefined) { + if (this.isMultiSourceRef.current) { + /** + * Check if the mouse event occurred on the same src range. + * If so, adjust the offset to support altStart for the current src. + */ + var leftOffset = parseFloat(this.playProgress.el_.style.left) / 100 * this.el().clientWidth; + var elClassList = eSrcElement.classList; + var sameSrc = (elClassList === null || elClassList === void 0 ? void 0 : elClassList.length) > 0 ? elClassList.contains('vjs-play-progress') || elClassList.contains('vjs-load-progress') : true; + if (leftOffset > offsetx && sameSrc) { + offsetx = offsetx + leftOffset; + } } + currentTime = offsetx / this.el().clientWidth * duration; } - setIsMultiSourced(isMultiSource || false); - setCIndex(canvasId); - if (enableFileDownload && renderings != {}) { - var _renderings$canvas$ca; - setRenderingFiles(renderings.manifest.concat((_renderings$canvas$ca = renderings.canvas[canvasId]) === null || _renderings$canvas$ca === void 0 ? void 0 : _renderings$canvas$ca.files)); - } - error ? setReady(false) : setReady(true); - } catch (e) { - showBoundary(e); - } - }; - - /** - * Switch src in the player when seeked to a time range within a - * different item in the same canvas - * @param {Number} srcindex new srcIndex - * @param {Number} value current time of the player - */ - var nextItemClicked = function nextItemClicked(srcindex, value) { - playerDispatch({ - currentTime: value, - type: 'setCurrentTime' - }); - manifestDispatch({ - srcIndex: srcindex, - type: 'setSrcIndex' - }); - }; - - /** - * Create timer to display the inaccessible Canvas message - */ - var createCanvasMessageTimer = function createCanvasMessageTimer() { - canvasMessageTimerRef.current = setTimeout(function () { - if (canvasIndexRef.current < lastCanvasIndexRef.current && autoAdvanceRef.current) { - manifestDispatch({ - canvasIndex: canvasIndexRef.current + 1, - type: 'switchCanvas' - }); + /** + * Parts of LoadProgress element is broken into segments as media loads, and displayed + * as separate div elements with `data-start` and `data-end` attributes respectively. + * When mouse event occurs on top of such element, add the segment start time to calculated + * current time from event. + */ + if (e.target.hasAttribute('data-start')) { + var _e$target$dataset = e.target.dataset, + start = _e$target$dataset.start; + _e$target$dataset._; + currentTime = currentTime + parseFloat(start); + offsetx = currentTime * this.el().clientWidth / this.totalDuration; } - }, CANVAS_MESSAGE_TIMEOUT); - }; - - /** - * Clear existing timer to display the inaccessible Canvas message - */ - var clearCanvasMessageTimer = function clearCanvasMessageTimer() { - if (canvasMessageTimerRef.current) { - clearTimeout(canvasMessageTimerRef.current); - canvasMessageTimerRef.current = null; + return { + currentTime: currentTime, + offsetx: offsetx + }; } - }; - - /** - * Switch player when navigating across canvases - * @param {Number} index canvas index to be loaded into the player - * @param {Boolean} fromStart flag to indicate set player start time to zero or not - * @param {String} focusElement element to be focused within the player when using - * next or previous buttons with keyboard - */ - var switchPlayer = function switchPlayer(index, fromStart) { - var focusElement = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; - if (index != undefined && index > -1 && canvasIndexRef.current != index && index <= lastCanvasIndexRef.current) { - manifestDispatch({ - canvasIndex: index, - type: 'switchCanvas' - }); - initCanvas(index, fromStart); - playerDispatch({ - element: focusElement, - type: 'setPlayerFocusElement' + }, { + key: "abortableTimeupdateHandler", + value: + /** + * A wrapper function around the time update interval, to cancel + * intermediate updates via the time interval when player is + * waiting to fetch stream + */ + function abortableTimeupdateHandler() { + var _this4 = this; + var player = this.player, + progressRef = this.progressRef; + player.on('waiting', function () { + if (IS_SAFARI && !IS_MOBILE) { + player.currentTime(progressRef.current); + } + cancelInterval(); }); + var cancelInterval = function cancelInterval() { + if (internalInterval) { + clearInterval(internalInterval); + } + }; + var internalInterval = setInterval(function () { + _this4.timeUpdateHandler(); + }, 100); } - }; - React.useEffect(function () { - var hlsOptions = withCredentials ? { - hls: { - withCredentials: true - } - } : {}; - var videoJsOptions; - // Only build the full set of option for the first playable Canvas since - // these options are only used on the initia Video.js instance creation - if (firstLoad && ready && !canvasIsEmpty) { - // Configuration options for Video.js instantiation - videoJsOptions = !canvasIsEmpty ? { - aspectRatio: isVideo ? '16:9' : '1:0', - audioOnlyMode: !isVideo, - autoplay: false, - bigPlayButton: isVideo, - id: PLAYER_ID, - playbackRates: enablePlaybackRate ? [0.5, 0.75, 1, 1.5, 2] : [], - experimentalSvgIcons: true, - // Setting inactivity timeout to zero in mobile and tablet devices translates to - // user is always active. And the control bar is not hidden when user is active. - // With this user can always use the controls when the media is playing. - inactivityTimeout: IS_MOBILE || IS_TOUCH_ONLY ? 0 : 2000, - poster: isVideo ? playerConfig.poster : null, - controls: true, - fluid: true, - language: language, - controlBar: { - // Define and order control bar controls - // See https://docs.videojs.com/tutorial-components.html for options of what - // seem to be supported controls - children: [isMultiCanvased ? 'videoJSPreviousButton' : '', 'playToggle', isMultiCanvased ? 'videoJSNextButton' : '', 'videoJSProgress', 'videoJSCurrentTime', 'timeDivider', 'durationDisplay', - // These icons are in reverse order to support `float: inline-end` in CSS - 'fullscreenToggle', enableFileDownload ? 'videoJSFileDownload' : '', enablePIP ? 'pictureInPictureToggle' : '', enablePlaybackRate ? 'playbackRateMenuButton' : '', 'qualitySelector', hasStructure || playlist.isPlaylist ? 'videoJSTrackScrubber' : '', playerConfig.tracks.length > 0 && isVideo ? 'subsCapsButton' : '', IS_MOBILE ? 'muteToggle' : 'volumePanel' - // 'vjsYo', custom component - ], - - videoJSProgress: { - srcIndex: srcIndex, - targets: targets, - currentTime: currentTime || 0, - nextItemClicked: nextItemClicked - }, - videoJSCurrentTime: { - srcIndex: srcIndex, - targets: targets, - currentTime: currentTime || 0 - } - }, - sources: isMultiSourced ? [playerConfig.sources[srcIndex]] : playerConfig.sources, - // Enable native text track functionality in iPhones and iPads - html5: _objectSpread$3(_objectSpread$3({}, hlsOptions), {}, { - nativeTextTracks: IS_MOBILE && !IS_ANDROID - }), - // Make error display modal dismissable - errorDisplay: { - uncloseable: false - }, - /* - Setting this option helps to override VideoJS's default 'keydown' event handler, whenever - the focus is on a native VideoJS control icon (e.g. play toggle). - E.g. click event on 'playtoggle' sets the focus on the play/pause button, - which has VideoJS's 'handleKeydown' event handler attached to it. Therefore, as long as the - focus is on the play/pause button the 'keydown' event will pass through VideoJS's default - 'keydown' event handler, without ever reaching the 'keydown' handler setup on the document - in Ramp code. - When this option is setup VideoJS's 'handleKeydown' event handler passes the event to the - function setup under the 'hotkeys' option when the native player controls are focused. - In Safari, this works without using 'hotkeys' option, therefore only set this in other browsers. - */ - userActions: { - hotkeys: !IS_SAFARI ? function (e) { - playerHotKeys(e, this); - } : undefined - }, - videoJSTitleLink: enableTitleLink - } : { - sources: [] - }; // Empty configurations for empty canvases - - // Add file download to toolbar when it is enabled via props - if (enableFileDownload && !canvasIsEmpty) { - videoJsOptions.controlBar.videoJSFileDownload = { - title: 'Download Files', - controlText: 'Alternate resource download', - files: renderingFiles - }; + }, { + key: "timeUpdateHandler", + value: + // Update progress bar with timeupdate in the player + function timeUpdateHandler() { + var _this5 = this; + var initTimeRef = this.initTimeRef, + player = this.player; + if (player.isDisposed() || player.ended() || player == null) { + return; } - if (isMultiCanvased && !canvasIsEmpty) { - videoJsOptions.controlBar.videoJSPreviousButton = { - canvasIndex: canvasIndex, - switchPlayer: switchPlayer, - playerFocusElement: playerFocusElement - }; - videoJsOptions.controlBar.videoJSNextButton = { - canvasIndex: canvasIndex, - lastCanvasIndex: lastCanvasIndexRef.current, - switchPlayer: switchPlayer, - playerFocusElement: playerFocusElement - }; + var curTime; + // Initially update progress from the prop passed from Ramp, + // this accounts for structured navigation when switching canvases + if (initTimeRef.current > 0 && player.currentTime() == 0) { + curTime = initTimeRef.current; + player.currentTime(initTimeRef.current); + } else { + curTime = player.currentTime(); } - // Iniitialize track scrubber button when the current Canvas has - // structure timespans or the given Manifest is a playlist Manifest - if ((hasStructure || playlist.isPlaylist) && !canvasIsEmpty) { - videoJsOptions.controlBar.videoJSTrackScrubber = { - trackScrubberRef: trackScrubberRef, - timeToolRef: timeToolRef, - isPlaylist: playlist.isPlaylist - }; + // Use debounced updates since, Safari desktop browsers need the extra + // update on 'seeked' event to timely update the progress bar. + if (IS_SAFARI && !IS_MOBILE && player.paused()) { + debounce_1(function () { + _this5.onTimeUpdate(curTime); + }); + } else { + this.onTimeUpdate(curTime); } - setFirstLoad(false); - } else { - videoJsOptions = { - sources: isMultiSourced ? [playerConfig.sources[srcIndex]] : playerConfig.sources, - poster: isVideo ? playerConfig.poster : null - }; + this.setInitTime(0); } - setOptions(videoJsOptions); - }, [ready, cIndex, srcIndex, canvasIsEmpty, currentTime]); - if (ready && options != undefined || canvasIsEmpty) { - return /*#__PURE__*/React.createElement("div", { - "data-testid": "media-player", - className: "ramp--media_player", - role: "presentation" - }, /*#__PURE__*/React.createElement(VideoJSPlayer, { - isVideo: isVideo, - hasMultipleCanvases: isMultiCanvased, - isPlaylist: playlist.isPlaylist, - trackScrubberRef: trackScrubberRef, - scrubberTooltipRef: timeToolRef, - tracks: playerConfig.tracks, - placeholderText: playerConfig.error, - renderingFiles: renderingFiles, - enableFileDownload: enableFileDownload, - loadPrevOrNext: switchPlayer, - lastCanvasIndex: lastCanvasIndex, - enableTitleLink: enableTitleLink, - videoJSLangMap: videoJSLangMap.current, - options: options - })); - } else { - return null; - } -}; -MediaPlayer.propTypes = { - enableFileDownload: PropTypes.bool, - enablePIP: PropTypes.bool, - enablePlaybackRate: PropTypes.bool, - enableTitleLink: PropTypes.bool, - withCredentials: PropTypes.bool, - language: PropTypes.string -}; - -var _extends_1 = createCommonjsModule(function (module) { -function _extends() { - module.exports = _extends = Object.assign ? Object.assign.bind() : function (target) { - for (var i = 1; i < arguments.length; i++) { - var source = arguments[i]; - for (var key in source) { - if (Object.prototype.hasOwnProperty.call(source, key)) { - target[key] = source[key]; - } + }, { + key: "onTimeUpdate", + value: function onTimeUpdate(curTime) { + // This state update caused weird lagging behaviors when using the iOS native + // video player. iOS player handles its own progress bar, so we can skip the + // update here only for video. + var iOS = this.player.hasClass("vjs-ios-native-fs"); + if (!(iOS && !this.player.audioOnlyMode_)) { + this.setProgress(curTime); } + this.handleTimeUpdate(curTime); } - return target; - }, module.exports.__esModule = true, module.exports["default"] = module.exports; - return _extends.apply(this, arguments); -} -module.exports = _extends, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); + }, { + key: "handleTimeUpdate", + value: + /** + * Update CSS for the input range's track while the media + * is playing + * @param {Number} curTime current time of the player + */ + function handleTimeUpdate(curTime) { + var _srcIndexRef$current; + var player = this.player, + el_ = this.el_, + canvasTargetsRef = this.canvasTargetsRef, + srcIndexRef = this.srcIndexRef; -var _extends = /*@__PURE__*/getDefaultExportFromCjs(_extends_1); + // Avoid null player instance when Video.js is getting initialized + if (!el_ || !player || !canvasTargetsRef.current) { + return; + } + var _canvasTargetsRef$cur3 = canvasTargetsRef.current[(_srcIndexRef$current = srcIndexRef.current) !== null && _srcIndexRef$current !== void 0 ? _srcIndexRef$current : 0], + start = _canvasTargetsRef$cur3.start, + end = _canvasTargetsRef$cur3.end, + duration = _canvasTargetsRef$cur3.duration; -var SectionHeading = function SectionHeading(_ref) { - var duration = _ref.duration, - label = _ref.label, - itemIndex = _ref.itemIndex, - canvasIndex = _ref.canvasIndex, - sectionRef = _ref.sectionRef, - itemId = _ref.itemId, - isRoot = _ref.isRoot, - handleClick = _ref.handleClick, - structureContainerRef = _ref.structureContainerRef; - var itemLabelRef = React.useRef(); - itemLabelRef.current = label; + // Restrict access to the intended range in the media file + if (curTime < start) { + player.currentTime(start); + } + if (curTime >= end && !player.paused() && !player.isDisposed()) { + // Trigger ended event when playable range < duration of the + // full media. e.g. clipped playlist items + if (end < duration) { + player.trigger('ended'); + } - /* - Auto-scroll active section into view only when user is not - actively interacting with structured navigation - */ - React.useEffect(function () { - if (canvasIndex + 1 === itemIndex && sectionRef.current && sectionRef.current.isClicked != undefined && !sectionRef.current.isClicked && structureContainerRef.current.isScrolling != undefined && !structureContainerRef.current.isScrolling) { - autoScroll(sectionRef.current, structureContainerRef); + // On the next play event set the time to start or a seeked time + // in between the 'ended' event and 'play' event + // Reference: https://github.com/videojs/video.js/blob/main/src/js/control-bar/play-toggle.js#L128 + player.one('play', function () { + var time = player.currentTime(); + if (time < end) { + player.currentTime(time); + } else { + player.currentTime(start); + } + }); + } } - sectionRef.current.isClicked = false; - }, [canvasIndex]); - var sectionClassName = "ramp--structured-nav__section".concat(canvasIndex + 1 === itemIndex ? ' active' : ''); - if (itemId != undefined) { - return /*#__PURE__*/React.createElement("div", { - className: sectionClassName, - role: "listitem", - "data-testid": "listitem-section", - ref: sectionRef, - "data-mediafrag": itemId, - "data-label": itemLabelRef.current - }, /*#__PURE__*/React.createElement("button", { - "data-testid": "listitem-section-button", - ref: sectionRef, - onClick: handleClick - }, /*#__PURE__*/React.createElement("span", { - className: "ramp--structured-nav__title", - "aria-label": itemLabelRef.current - }, "".concat(itemIndex, ". "), itemLabelRef.current, duration != '' && /*#__PURE__*/React.createElement("span", { - className: "ramp--structured-nav__section-duration" - }, duration)))); - } else { - return /*#__PURE__*/React.createElement("div", { - className: sectionClassName, - "data-testid": "listitem-section", - ref: sectionRef, - "data-label": itemLabelRef.current - }, /*#__PURE__*/React.createElement("span", { - className: "ramp--structured-nav__section-title", - role: "listitem", - "data-testid": "listitem-section-span", - "aria-label": itemLabelRef.current - }, isRoot ? '' : "".concat(itemIndex, ". "), itemLabelRef.current, duration != '' && /*#__PURE__*/React.createElement("span", { - className: "ramp--structured-nav__section-duration" - }, duration))); - } -}; -SectionHeading.propTypes = { - itemIndex: PropTypes.number.isRequired, - canvasIndex: PropTypes.number, - duration: PropTypes.string.isRequired, - label: PropTypes.string.isRequired, - sectionRef: PropTypes.object.isRequired, - itemId: PropTypes.string, - isRoot: PropTypes.bool, - handleClick: PropTypes.func.isRequired, - structureContainerRef: PropTypes.object.isRequired -}; + }]); + return VideoJSProgress; +}(SeekBar); +videojs.registerComponent('VideoJSProgress', VideoJSProgress); -var ListItem = function ListItem(_ref) { - var duration = _ref.duration, - id = _ref.id, - isTitle = _ref.isTitle, - isCanvas = _ref.isCanvas, - isClickable = _ref.isClickable, - isEmpty = _ref.isEmpty, - label = _ref.label, - summary = _ref.summary, - homepage = _ref.homepage, - isRoot = _ref.isRoot, - items = _ref.items, - itemIndex = _ref.itemIndex, - rangeId = _ref.rangeId, - canvasDuration = _ref.canvasDuration, - sectionRef = _ref.sectionRef, - structureContainerRef = _ref.structureContainerRef; - var playerDispatch = usePlayerDispatch(); - var _useManifestState = useManifestState(), - canvasIndex = _useManifestState.canvasIndex, - currentNavItem = _useManifestState.currentNavItem, - playlist = _useManifestState.playlist; - var isPlaylist = playlist.isPlaylist; - var itemIdRef = React.useRef(); - itemIdRef.current = id; - var itemLabelRef = React.useRef(); - itemLabelRef.current = label; - var itemSummaryRef = React.useRef(); - itemSummaryRef.current = summary; - var subMenu = items && items.length > 0 ? /*#__PURE__*/React.createElement(List, { - items: items, - sectionRef: sectionRef, - structureContainerRef: structureContainerRef - }) : null; - var liRef = React.useRef(null); - var handleClick = React.useCallback(function (e) { - e.preventDefault(); - e.stopPropagation(); - var _getMediaFragment = getMediaFragment(itemIdRef.current, canvasDuration), - start = _getMediaFragment.start, - end = _getMediaFragment.end; - var inRange = checkSrcRange({ - start: start, - end: end - }, { - end: canvasDuration +function _createSuper$5(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$5(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } +function _isNativeReflectConstruct$5() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +var TimeDisplay = videojs.getComponent('TimeDisplay'); + +/** + * Custom component to display the current time of the player + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} options + * @param {Number} options.currentTime + */ +var VideoJSCurrentTime = /*#__PURE__*/function (_TimeDisplay) { + _inherits(VideoJSCurrentTime, _TimeDisplay); + var _super = _createSuper$5(VideoJSCurrentTime); + function VideoJSCurrentTime(player, options) { + var _this; + _classCallCheck(this, VideoJSCurrentTime); + _this = _super.call(this, player, options); + _this.addClass('vjs-time-control vjs-current-time-display'); + _this.setAttribute('role', 'presentation'); + _this.player = player; + _this.options = options; + _this.initTimeRef = /*#__PURE__*/createRef(); + _this.initTimeRef.current = options.currentTime; + _this.playerInterval; + _this.player.on('loadstart', function () { + _this.playerInterval = setInterval(function () { + _this.handleTimeUpdate(); + }, 100); }); - /* - Only continue the click action if not both start and end times of - the timespan are not outside Canvas' duration - */ - if (inRange) { - playerDispatch({ - clickedUrl: itemIdRef.current, - type: 'navClick' - }); - liRef.current.isClicked = true; - if (sectionRef.current) { - sectionRef.current.isClicked = true; + _this.player.on('seeked', function () { + if (IS_SAFARI && !IS_MOBILE) { + _this.updateTextNode_(player.currentTime()); } + }); + + // Update our timer after the user leaves full screen + _this.player.on('fullscreenchange', function () { + if (!player.isFullscreen()) { + _this.updateTextNode_(player.currentTime()); + } + }); + + // Clean interval upon player dispose + _this.player.on('dispose', function () { + clearInterval(_this.playerInterval); + }); + return _this; + } + _createClass(VideoJSCurrentTime, [{ + key: "buildCSSClass", + value: function buildCSSClass() { + return 'current-time'; } - }); - React.useEffect(function () { - /* - Auto-scroll active structure item into view only when user is not actively - interacting with structured navigation - */ - if (liRef.current && (currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.id) == itemIdRef.current && liRef.current.isClicked != undefined && !liRef.current.isClicked && structureContainerRef.current.isScrolling != undefined && !structureContainerRef.current.isScrolling) { - autoScroll(liRef.current, structureContainerRef); + }, { + key: "setInitTime", + value: function setInitTime(t) { + this.initTimeRef.current = t; } - // Reset isClicked if active structure item is set - if (liRef.current) { - liRef.current.isClicked = false; + }, { + key: "handleTimeUpdate", + value: function handleTimeUpdate() { + var player = this.player, + initTimeRef = this.initTimeRef; + var targets = player.targets, + srcIndex = player.srcIndex; + if (!player || player.isDisposed() || !targets) { + return; + } + var iOS = player.hasClass('vjs-ios-native-fs'); + var time; + // Update time from the given initial time if it is not zero + if (initTimeRef.current > 0 && player.currentTime() == 0) { + time = initTimeRef.current; + } else { + time = player.currentTime(); + } + var _targets = targets[srcIndex !== null && srcIndex !== void 0 ? srcIndex : 0], + start = _targets.start, + altStart = _targets.altStart; + if (altStart != start && srcIndex > 0) { + time = time + altStart; + } + // This state update caused weird lagging behaviors when using the iOS native + // video player. iOS player handles its own time, so we can skip the update here + // video items. + if (!(iOS && !player.audioOnlyMode_)) { + this.updateTextNode_(time); + } + this.setInitTime(0); } - }, [currentNavItem]); - var renderListItem = function renderListItem() { - return /*#__PURE__*/React.createElement(React.Fragment, { - key: rangeId - }, isCanvas && !isPlaylist ? /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(SectionHeading, { - itemIndex: itemIndex, - canvasIndex: canvasIndex, - duration: duration, - label: label, - sectionRef: sectionRef, - itemId: itemIdRef.current, - isRoot: isRoot, - handleClick: handleClick, - structureContainerRef: structureContainerRef - })) : /*#__PURE__*/React.createElement(React.Fragment, null, isTitle ? /*#__PURE__*/React.createElement("span", { - className: "ramp--structured-nav__item-title", - role: "listitem", - "aria-label": itemLabelRef.current - }, itemLabelRef.current) : /*#__PURE__*/React.createElement(React.Fragment, { - key: id - }, /*#__PURE__*/React.createElement("div", { - className: "tracker" - }), isClickable ? /*#__PURE__*/React.createElement(React.Fragment, null, isEmpty && /*#__PURE__*/React.createElement(LockedSVGIcon, null), /*#__PURE__*/React.createElement("a", { - role: "listitem", - href: homepage && homepage != '' ? homepage : itemIdRef.current, - onClick: handleClick - }, "".concat(itemIndex, ". "), itemLabelRef.current, " ", duration.length > 0 ? " (".concat(duration, ")") : '')) : /*#__PURE__*/React.createElement("span", { - role: "listitem", - "aria-label": itemLabelRef.current - }, itemLabelRef.current)))); - }; - if (label != '') { - return /*#__PURE__*/React.createElement("li", { - "data-testid": "list-item", - ref: liRef, - className: 'ramp--structured-nav__list-item' + "".concat(itemIdRef.current != undefined && (currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.id) === itemIdRef.current && (isPlaylist || !isCanvas) && (currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.canvasIndex) === canvasIndex + 1 ? ' active' : ''), - "data-label": itemLabelRef.current, - "data-summary": itemSummaryRef.current - }, renderListItem(), subMenu); - } else { - return null; - } -}; -ListItem.propTypes = { - duration: PropTypes.string.isRequired, - id: PropTypes.string, - isTitle: PropTypes.bool.isRequired, - isCanvas: PropTypes.bool.isRequired, - isClickable: PropTypes.bool.isRequired, - isEmpty: PropTypes.bool.isRequired, - label: PropTypes.string.isRequired, - summary: PropTypes.string, - homepage: PropTypes.string, - isRoot: PropTypes.bool, - items: PropTypes.array.isRequired, - itemIndex: PropTypes.number, - rangeId: PropTypes.string.isRequired, - canvasDuration: PropTypes.number.isRequired, - sectionRef: PropTypes.object.isRequired, - structureContainerRef: PropTypes.object.isRequired -}; + }]); + return VideoJSCurrentTime; +}(TimeDisplay); +videojs.registerComponent('VideoJSCurrentTime', VideoJSCurrentTime); -var List = function List(_ref) { - var items = _ref.items, - sectionRef = _ref.sectionRef, - structureContainerRef = _ref.structureContainerRef; - var collapsibleContent = /*#__PURE__*/React.createElement("ul", { - "data-testid": "list", - className: "ramp--structured-nav__list", - role: "presentation" - }, items.map(function (item, index) { - if (item) { - return /*#__PURE__*/React.createElement(ListItem, _extends({}, item, { - sectionRef: sectionRef, - key: index, - structureContainerRef: structureContainerRef - })); +function _createSuper$4(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$4(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } +function _isNativeReflectConstruct$4() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +var MenuButton = videojs.getComponent('MenuButton'); +var MenuItem = videojs.getComponent('MenuItem'); + +/** + * Custom component to display rendering files as downloadable + * associated with the current Canvas. This control is enabled + * in the player's control-bar via 'enableFileDownload' prop in + * MediaPlayer component. + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} props.options + * @param {Number} props.options.files list of rendering files + */ +var VideoJSFileDownload = /*#__PURE__*/function (_MenuButton) { + _inherits(VideoJSFileDownload, _MenuButton); + var _super = _createSuper$4(VideoJSFileDownload); + function VideoJSFileDownload(player, options) { + var _this; + _classCallCheck(this, VideoJSFileDownload); + _this = _super.call(this, player, options); + // Add SVG icon through CSS class + _this.addClass("vjs-file-download"); + _this.setAttribute('data-testid', 'videojs-file-download'); + // Use Video.js' stock SVG instead of setting it using CSS + _this.setIcon('file-download'); + return _this; + } + _createClass(VideoJSFileDownload, [{ + key: "createItems", + value: function createItems() { + var options_ = this.options_, + player_ = this.player_; + var files = options_.files; + if ((files === null || files === void 0 ? void 0 : files.length) > 0) { + return files.map(function (file) { + var item = new MenuItem(player_, { + label: file.label + }); + item.handleClick = function () { + fileDownload(file.id, file.filename, file.fileExt); + }; + return item; + }); + } else { + return []; + } } - })); - return /*#__PURE__*/React.createElement(React.Fragment, null, collapsibleContent); -}; -List.propTypes = { - items: PropTypes.array.isRequired, - sectionRef: PropTypes.object.isRequired, - structureContainerRef: PropTypes.object.isRequired -}; + }]); + return VideoJSFileDownload; +}(MenuButton); +videojs.registerComponent('VideoJSFileDownload', VideoJSFileDownload); -function _createForOfIteratorHelper$1(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$1(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } -function _unsupportedIterableToArray$1(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$1(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$1(o, minLen); } -function _arrayLikeToArray$1(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } -var StructuredNavigation = function StructuredNavigation() { - var _structureItemsRef$cu; - var manifestDispatch = useManifestDispatch(); - var playerDispatch = usePlayerDispatch(); - var _usePlayerState = usePlayerState(), - clickedUrl = _usePlayerState.clickedUrl, - isClicked = _usePlayerState.isClicked, - isPlaying = _usePlayerState.isPlaying, - player = _usePlayerState.player; - var _useManifestState = useManifestState(), - allCanvases = _useManifestState.allCanvases, - canvasDuration = _useManifestState.canvasDuration, - canvasIndex = _useManifestState.canvasIndex, - hasMultiItems = _useManifestState.hasMultiItems, - targets = _useManifestState.targets, - manifest = _useManifestState.manifest, - playlist = _useManifestState.playlist, - canvasIsEmpty = _useManifestState.canvasIsEmpty, - canvasSegments = _useManifestState.canvasSegments; - var _useErrorBoundary = useErrorBoundary(), - showBoundary = _useErrorBoundary.showBoundary; - var canvasStructRef = React.useRef(); - var structureItemsRef = React.useRef(); - var canvasIsEmptyRef = React.useRef(canvasIsEmpty); - var hasRootRangeRef = React.useRef(false); - var structureContainerRef = React.useRef(); - var scrollableStructure = React.useRef(); - React.useEffect(function () { - // Update currentTime and canvasIndex in state if a - // custom start time and(or) canvas is given in manifest - if (manifest) { - try { - var _getStructureRanges = getStructureRanges(manifest, allCanvases, playlist.isPlaylist), - structures = _getStructureRanges.structures, - timespans = _getStructureRanges.timespans, - markRoot = _getStructureRanges.markRoot; - structureItemsRef.current = structures; - canvasStructRef.current = structures; - hasRootRangeRef.current = markRoot; - // Remove root-level structure item from navigation calculations - if ((structures === null || structures === void 0 ? void 0 : structures.length) > 0 && structures[0].isRoot) { - canvasStructRef.current = structures[0].items; +function _createSuper$3(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$3(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } +function _isNativeReflectConstruct$3() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +var Button$2 = videojs.getComponent('Button'); + +/** + * Custom VideoJS button component for navigating to the next Canvas when there + * are multiple canvases in a given Manifest + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} props.options + * @param {Number} props.options.canvasIndex current Canvas index + * @param {Number} props.options.lastCanvasIndex index of the last Canvas in the Manifest + * @param {Function} props.options.switchPlayer callback func to update Canvas change in state + */ +var VideoJSNextButton = /*#__PURE__*/function (_Button) { + _inherits(VideoJSNextButton, _Button); + var _super = _createSuper$3(VideoJSNextButton); + function VideoJSNextButton(player, options) { + var _this; + _classCallCheck(this, VideoJSNextButton); + _this = _super.call(this, player, options); + // Use Video.js' stock SVG instead of setting it using CSS + _this.setIcon('next-item'); + _this.addClass('vjs-play-control vjs-control'); + _this.setAttribute('data-testid', 'videojs-next-button'); + _this.controlText('Next'); + _this.options = options; + _this.player = player; + _this.cIndex = options.canvasIndex; + + // Handle player reload or source change events + _this.player.on('loadstart', function () { + _this.updateComponent(); + }); + return _this; + } + _createClass(VideoJSNextButton, [{ + key: "updateComponent", + value: function updateComponent() { + var player = this.player; + if (player && player != undefined) { + var _player$children; + // When canvasIndex property is not set in the player instance use dataset. + // This happens rarely, but when it does previous button cannot be used. + if (player.canvasIndex === undefined && ((_player$children = player.children()) === null || _player$children === void 0 ? void 0 : _player$children.length) > 0) { + this.cIndex = Number(player.children()[0].dataset.canvasindex); + } else { + this.cIndex = player.canvasIndex; } - manifestDispatch({ - structures: canvasStructRef.current, - type: 'setStructures' - }); - manifestDispatch({ - timespans: timespans, - type: 'setCanvasSegments' - }); - structureContainerRef.current.isScrolling = false; - } catch (error) { - showBoundary(error); } } - }, [manifest]); - - // Set currentNavItem when current Canvas is an inaccessible/empty item - React.useEffect(function () { - if (canvasIsEmpty && playlist.isPlaylist) { - manifestDispatch({ - item: canvasSegments[canvasIndex], - type: 'switchItem' - }); + }, { + key: "handleClick", + value: function handleClick() { + this.handleNextClick(false); } - }, [canvasIsEmpty, canvasIndex]); - React.useEffect(function () { - if (isClicked) { - var clickedItem = canvasSegments.filter(function (c) { - return c.id === clickedUrl; - }); - if ((clickedItem === null || clickedItem === void 0 ? void 0 : clickedItem.length) > 0) { - // Only update the current nav item for timespans - // Eliminate Canvas level items unless the structure is empty - var _clickedItem$ = clickedItem[0], - isCanvas = _clickedItem$.isCanvas, - items = _clickedItem$.items; - if (!isCanvas || items.length == 0 && isCanvas) { - manifestDispatch({ - item: clickedItem[0], - type: 'switchItem' - }); - } - } - var currentCanvasIndex = allCanvases.findIndex(function (c) { - return c.canvasURL === getCanvasId(clickedUrl); - }); - var timeFragment = getMediaFragment(clickedUrl, canvasDuration); - - // Invalid time fragment - if (!timeFragment || timeFragment == undefined) { - console.error('StructuredNavigation -> invalid media fragment in structure item -> ', timeFragment); - return; - } - var timeFragmentStart = timeFragment.start; - if (hasMultiItems) { - var _getCanvasTarget = getCanvasTarget(targets, timeFragment, canvasDuration), - srcIndex = _getCanvasTarget.srcIndex, - fragmentStart = _getCanvasTarget.fragmentStart; - timeFragmentStart = fragmentStart; - manifestDispatch({ - srcIndex: srcIndex, - type: 'setSrcIndex' - }); - } else { - // When clicked structure item is not in the current canvas - if (canvasIndex != currentCanvasIndex && currentCanvasIndex > -1) { - manifestDispatch({ - canvasIndex: currentCanvasIndex, - type: 'switchCanvas' - }); - canvasIsEmptyRef.current = canvasStructRef.current[currentCanvasIndex].isEmpty; - } + }, { + key: "handleKeyDown", + value: function handleKeyDown(e) { + if (e.which === 32 || e.which === 13) { + e.stopPropagation(); + this.handleNextClick(true); } - if (player && !canvasIsEmptyRef.current) { - player.currentTime(timeFragmentStart); - playerDispatch({ - startTime: timeFragment.start, - endTime: timeFragment.end, - type: 'setTimeFragment' - }); - - // Use this value in iOS to set the initial progress - // in the custom progress bar - player.structStart = timeFragmentStart; - playerDispatch({ - currentTime: timeFragmentStart, - type: 'setCurrentTime' - }); - // Setting userActive to true shows timerail breifly, helps - // to visualize the structure in player while playing - if (isPlaying) player.userActive(true); - } else if (canvasIsEmptyRef.current) { - // Reset isClicked in state for - // inaccessible items (empty canvases) - playerDispatch({ - type: 'resetClick' - }); + } + }, { + key: "handleNextClick", + value: function handleNextClick(isKeyDown) { + if (this.cIndex != this.options.lastCanvasIndex) { + this.options.switchPlayer(this.cIndex + 1, true, isKeyDown ? 'nextBtn' : ''); } } - }, [isClicked, player]); + }]); + return VideoJSNextButton; +}(Button$2); +videojs.registerComponent('VideoJSNextButton', VideoJSNextButton); - // Structured nav is populated by the time the player hook fires so we listen for - // that to run the check on whether the structured nav is scrollable. - React.useEffect(function () { - if (structureContainerRef.current) { - var elem = structureContainerRef.current; - var structureBorder = structureContainerRef.current.parentElement; - var structureEnd = Math.abs(elem.scrollHeight - (elem.scrollTop + elem.clientHeight)) <= 1; - scrollableStructure.current = !structureEnd; - if (structureBorder) { - resizeObserver.observe(structureBorder); +function _createSuper$2(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$2(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } +function _isNativeReflectConstruct$2() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +var Button$1 = videojs.getComponent('Button'); + +/** + * Custom VideoJS button component for navigating to the previous Canvas when there + * are multiple canvases in a given Manifest + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} props.options + * @param {Number} props.options.canvasIndex current Canvas index + * @param {Function} props.options.switchPlayer callback func to update Canvas change in state + */ +var VideoJSPreviousButton = /*#__PURE__*/function (_Button) { + _inherits(VideoJSPreviousButton, _Button); + var _super = _createSuper$2(VideoJSPreviousButton); + function VideoJSPreviousButton(player, options) { + var _this; + _classCallCheck(this, VideoJSPreviousButton); + _this = _super.call(this, player, options); + // Use Video.js' stock SVG instead of setting it using CSS + _this.setIcon('previous-item'); + _this.addClass('vjs-play-control vjs-control'); + _this.setAttribute('data-testid', 'videojs-previous-button'); + _this.options = options; + _this.player = player; + _this.cIndex = options.canvasIndex; + + // Handle player reload or source change events + _this.player.on('loadstart', function () { + _this.updateComponent(); + }); + return _this; + } + _createClass(VideoJSPreviousButton, [{ + key: "updateComponent", + value: function updateComponent() { + var player = this.player; + if (player && player != undefined) { + var _player$children; + // When canvasIndex property is not set in the player instance use dataset. + // This happens rarely, but when it does previous button cannot be used. + if (player.canvasIndex === undefined && ((_player$children = player.children()) === null || _player$children === void 0 ? void 0 : _player$children.length) > 0) { + this.cIndex = Number(player.children()[0].dataset.canvasindex); + } else { + this.cIndex = player.canvasIndex; + } } + this.controlText(this.cIndex == 0 ? 'Replay' : 'Previous'); } - }, [player]); - - // Update scrolling indicators when end of scrolling has been reached - var handleScrollable = function handleScrollable(e) { - var elem = e.target; - if (elem.classList.contains('ramp--structured-nav__border')) { - elem = elem.firstChild; + }, { + key: "handleClick", + value: function handleClick() { + this.handlePreviousClick(false); } - var scrollMsg = elem.nextSibling; - var structureEnd = Math.abs(elem.scrollHeight - (elem.scrollTop + elem.clientHeight)) <= 1; - if (elem && structureEnd && elem.classList.contains('scrollable')) { - elem.classList.remove('scrollable'); - } else if (elem && !structureEnd && !elem.classList.contains('scrollable')) { - elem.classList.add('scrollable'); + }, { + key: "handleKeyDown", + value: function handleKeyDown(e) { + if (e.which === 32 || e.which === 13) { + e.stopPropagation(); + this.handlePreviousClick(true); + } } - if (scrollMsg && structureEnd && scrollMsg.classList.contains('scrollable')) { - scrollMsg.classList.remove('scrollable'); - } else if (scrollMsg && !structureEnd && !scrollMsg.classList.contains('scrollable')) { - scrollMsg.classList.add('scrollable'); + }, { + key: "handlePreviousClick", + value: function handlePreviousClick(isKeyDown) { + if (this.cIndex > -1 && this.cIndex != 0) { + this.options.switchPlayer(this.cIndex - 1, true, isKeyDown ? 'previousBtn' : ''); + } else if (this.cIndex == 0) { + this.player.currentTime(0); + } } - }; + }]); + return VideoJSPreviousButton; +}(Button$1); +videojs.registerComponent('VideoJSPreviousButton', VideoJSPreviousButton); - // Update scrolling indicators when structured nav is resized - var resizeObserver = new ResizeObserver(function (entries) { - var _iterator = _createForOfIteratorHelper$1(entries), - _step; - try { - for (_iterator.s(); !(_step = _iterator.n()).done;) { - var entry = _step.value; - handleScrollable(entry); +function _createSuper$1(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$1(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } +function _isNativeReflectConstruct$1() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } +var vjsComponent = videojs.getComponent('Component'); + +/** + * Custom component to display title of the current item in the player + * in an overlay. + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} props.options + */ +var VideoJSTitleLink = /*#__PURE__*/function (_vjsComponent) { + _inherits(VideoJSTitleLink, _vjsComponent); + var _super = _createSuper$1(VideoJSTitleLink); + function VideoJSTitleLink(player, options) { + var _this; + _classCallCheck(this, VideoJSTitleLink); + _this = _super.call(this, player, options); + _this.setAttribute('data-testid', 'videojs-title-link'); + _this.addClass('vjs-title-bar'); + _this.options = options; + _this.player = player; + + // Handle player reload or source change events + _this.player.on('loadstart', function () { + _this.updateComponent(); + }); + return _this; + } + _createClass(VideoJSTitleLink, [{ + key: "updateComponent", + value: function updateComponent() { + var player = this.player; + if (player && player != undefined && player.canvasLink) { + var _player$canvasLink = player.canvasLink, + label = _player$canvasLink.label, + id = _player$canvasLink.id; + var title = label; + var href = null; + /** + * Avalon canvas ids are of the form 'http://host.edu/media_objects/#mo_id/manifest/canvas/#section_id`. + * Accessible url is 'http://host.edu/media_objects/#mo_id/section/#section_id' so we convert the canvas + * id for avalon manifest, but must assume other implementers will have the id as an actionable link. + */ + if (id.includes('manifest/canvas')) { + href = id.replace('manifest/canvas', 'section'); + } else { + href = id; + } + var link = videojs.dom.createEl('a', { + className: 'vjs-title-link', + href: href, + target: '_blank', + rel: 'noreferrer noopener', + innerHTML: title + }); + if (this.el().hasChildNodes()) { + this.el().replaceChildren(link); + } else { + this.el().appendChild(link); + } } - } catch (err) { - _iterator.e(err); - } finally { - _iterator.f(); } - }); - if (!manifest) { - return /*#__PURE__*/React.createElement("p", null, "No manifest - Please provide a valid manifest."); - } + }]); + return VideoJSTitleLink; +}(vjsComponent); +vjsComponent.registerComponent('VideoJSTitleLink', VideoJSTitleLink); - // Check for scrolling on initial render and build appropriate element class - var divClass = ''; - var spanClass = ''; - if (scrollableStructure.current) { - divClass = "ramp--structured-nav scrollable"; - spanClass = "scrollable"; - } else { - divClass = "ramp--structured-nav"; - } - if (playlist !== null && playlist !== void 0 && playlist.isPlaylist) { - divClass += " playlist-items"; - } - divClass += hasRootRangeRef.current ? " ramp--structured-nav-with_root" : ""; +function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - /** - * Update isScrolling flag within structure container ref, which is - * used by ListItem and SectionHeading components to decide to/not to - * auto scroll the content - * @param {Boolean} state - */ - var handleMouseOver = function handleMouseOver(state) { - structureContainerRef.current.isScrolling = state; - }; - return /*#__PURE__*/React.createElement("div", { - className: "ramp--structured-nav__border" - }, /*#__PURE__*/React.createElement("div", { - "data-testid": "structured-nav", - className: divClass, - ref: structureContainerRef, - role: "list", - "aria-label": "Structural content", - onScroll: handleScrollable, - onMouseLeave: function onMouseLeave() { - return handleMouseOver(false); - }, - onMouseOver: function onMouseOver() { - return handleMouseOver(true); - } - }, ((_structureItemsRef$cu = structureItemsRef.current) === null || _structureItemsRef$cu === void 0 ? void 0 : _structureItemsRef$cu.length) > 0 ? structureItemsRef.current.map(function (item, index) { - return /*#__PURE__*/React.createElement(List, { - items: [item], - sectionRef: /*#__PURE__*/React.createRef(), - key: index, - structureContainerRef: structureContainerRef - }); - }) : /*#__PURE__*/React.createElement("p", { - className: "ramp--no-structure" - }, "There are no structures in the manifest")), /*#__PURE__*/React.createElement("span", { - className: spanClass - }, "Scroll to see more")); -}; -StructuredNavigation.propTypes = {}; +// SVG icons for zoom-in and zoom-out icons as strings +var zoomOutIconSVG = "\n\n \n \n \n \n \n \n"; +var zoomInIconSVG = "\n\n \n \n \n \n \n \n"; -var objectWithoutPropertiesLoose = createCommonjsModule(function (module) { -function _objectWithoutPropertiesLoose(source, excluded) { - if (source == null) return {}; - var target = {}; - var sourceKeys = Object.keys(source); - var key, i; - for (i = 0; i < sourceKeys.length; i++) { - key = sourceKeys[i]; - if (excluded.indexOf(key) >= 0) continue; - target[key] = source[key]; - } - return target; +// Function to inject SVGs into the DOM +function injectSVGIcons() { + var svgContainer = document.createElement('div'); + svgContainer.style.display = 'none'; + svgContainer.innerHTML = "".concat(zoomOutIconSVG).concat(zoomInIconSVG, ""); + document.body.appendChild(svgContainer); } -module.exports = _objectWithoutPropertiesLoose, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); -var objectWithoutProperties = createCommonjsModule(function (module) { -function _objectWithoutProperties(source, excluded) { - if (source == null) return {}; - var target = objectWithoutPropertiesLoose(source, excluded); - var key, i; - if (Object.getOwnPropertySymbols) { - var sourceSymbolKeys = Object.getOwnPropertySymbols(source); - for (i = 0; i < sourceSymbolKeys.length; i++) { - key = sourceSymbolKeys[i]; - if (excluded.indexOf(key) >= 0) continue; - if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; - target[key] = source[key]; - } - } - return target; -} -module.exports = _objectWithoutProperties, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); +// Call the function to inject SVG icons +injectSVGIcons(); +var Button = videojs.getComponent('Button'); -var _objectWithoutProperties = /*@__PURE__*/getDefaultExportFromCjs(objectWithoutProperties); +/** + * Custom VideoJS component for displaying track view when there are + * tracks/structure timespans in the current Canvas. + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} props.options + * @param {Number} props.options.trackScrubberRef React ref to track scrubber element + * @param {Number} props.options.timeToolRef React ref to time tooltip element + * @param {Boolean} props.options.isPlaylist flag to indicate a playlist Manifest or not + */ +var VideoJSTrackScrubber = /*#__PURE__*/function (_Button) { + _inherits(VideoJSTrackScrubber, _Button); + var _super = _createSuper(VideoJSTrackScrubber); + function VideoJSTrackScrubber(player, options) { + var _this; + _classCallCheck(this, VideoJSTrackScrubber); + _this = _super.call(this, player, options); + _this.setAttribute('data-testid', 'videojs-track-scrubber-button'); + _this.addClass('vjs-button vjs-track-scrubber'); + _this.controlText('Toggle track scrubber'); + _this.el().innerHTML = "\n \n \n "; + _this.options = options; + _this.player = player; + _this.playerInterval; + _this.zoomedOutRef = /*#__PURE__*/createRef(); + _this.currentTrackRef = /*#__PURE__*/createRef(); -var taggedTemplateLiteral = createCommonjsModule(function (module) { -function _taggedTemplateLiteral(strings, raw) { - if (!raw) { - raw = strings.slice(0); - } - return Object.freeze(Object.defineProperties(strings, { - raw: { - value: Object.freeze(raw) - } - })); -} -module.exports = _taggedTemplateLiteral, module.exports.__esModule = true, module.exports["default"] = module.exports; -}); + // Attach interval on first load for time updates + _this.player.on('ready', function () { + if (_this.options.trackScrubberRef.current) { + _this.playerInterval = setInterval(function () { + _this.handleTimeUpdate(); + }, 100); + } + }); -var _taggedTemplateLiteral = /*@__PURE__*/getDefaultExportFromCjs(taggedTemplateLiteral); + /* + When player is fully built and the trackScrubber element is initialized, + call method to mount React component. + */ + _this.player.on('loadstart', function () { + if (_this.options.trackScrubberRef.current) { + _this.updateComponent(); + if (!_this.playerInterval) { + _this.playerInterval = setInterval(function () { + _this.handleTimeUpdate(); + }, 100); + } + } + }); -var _templateObject$1, _templateObject2, _templateObject3, _templateObject4; -function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } -function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } -function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } + // Hide track scrubber if it is displayed when player is going fullscreen + _this.player.on('fullscreenchange', function () { + if (_this.player.isFullscreen() && !_this.zoomedOutRef.current) { + var tempZoom = _this.zoomedOutRef.current; + _this.setZoomedOut(!tempZoom); + } + }); -// ENum for supported transcript MIME types -var TRANSCRIPT_MIME_TYPES = { - webvtt: ['text/vtt'], - srt: ['application/x-subrip', 'text/srt'], - text: ['text/plain'], - json: ['application/json'], - docx: ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'] -}; -var VTT_TIMESTAMP_REGEX = /^(?:\d{2}:)?\d{2}:\d{2}(?:\.\d+)/g; -// SRT allows using comma for milliseconds while WebVTT does not -var SRT_TIMESTAMP_REGEX = /^(?:\d{2}:)?\d{2}:\d{2}(?:[.,]\d+)/g; -var TRANSCRIPT_MIME_EXTENSIONS = [{ - type: TRANSCRIPT_MIME_TYPES.json, - ext: 'json' -}, { - type: TRANSCRIPT_MIME_TYPES.webvtt, - ext: 'vtt' -}, { - type: TRANSCRIPT_MIME_TYPES.text, - ext: 'txt' -}, { - type: TRANSCRIPT_MIME_TYPES.docx, - ext: 'docx' -}, { - type: TRANSCRIPT_MIME_TYPES.srt, - ext: 'srt' -}]; + // Clean up interval when player is disposed + _this.player.on('dispose', function () { + clearInterval(_this.playerInterval); + }); + return _this; + } + _createClass(VideoJSTrackScrubber, [{ + key: "setCurrentTrack", + value: function setCurrentTrack(t) { + this.currentTrackRef.current = t; + } + }, { + key: "setZoomedOut", + value: function setZoomedOut(z) { + this.zoomedOutRef.current = z; + if (z) { + this.options.trackScrubberRef.current.classList.add('hidden'); + this.el().innerHTML = "\n \n \n "; + } else { + this.options.trackScrubberRef.current.classList.remove('hidden'); + this.el().innerHTML = "\n \n \n "; + } + } + }, { + key: "attachListeners", + value: function attachListeners() { + var _this2 = this; + var trackScrubberRef = this.options.trackScrubberRef; + if (trackScrubberRef.current) { + // Initialize the track scrubber's current time and duration + this.populateTrackScrubber(); + this.updateTrackScrubberProgressBar(); + var pointerDragged = false; + // Attach mouse pointer events to track scrubber progress bar + var _trackScrubberRef$cur = _slicedToArray(trackScrubberRef.current.children, 3); + _trackScrubberRef$cur[0]; + var progressBar = _trackScrubberRef$cur[1]; + _trackScrubberRef$cur[2]; + progressBar.addEventListener('mouseenter', function (e) { + _this2.handleMouseMove(e); + }); + /* + Using pointerup, pointermove, pointerdown events instead of + mouseup, mousemove, mousedown events to make it work with both + mouse pointer and touch events + */ + progressBar.addEventListener('pointerup', function (e) { + if (pointerDragged) { + _this2.handleSetProgress(e); + } + }); + progressBar.addEventListener('pointermove', function (e) { + _this2.handleMouseMove(e); + pointerDragged = true; + }); + progressBar.addEventListener('pointerdown', function (e) { + // Only handle left click event + if (e.which === 1) { + _this2.handleSetProgress(e); + pointerDragged = false; + } + }); + } + } + }, { + key: "updateComponent", + value: function updateComponent() { + // Reset refs to initial value + this.zoomedOutRef.current = true; + this.currentTrackRef.current = {}; + this.attachListeners(); + } -// ENum for describing transcript types include invalid and no transcript info -var TRANSCRIPT_TYPES = { - invalidTimestamp: -4, - invalidVTT: -3, - noSupport: -2, - invalid: -1, - noTranscript: 0, - timedText: 1, - plainText: 2, - docx: 3 -}; + /** + * Keydown event handler for the track button on the player controls, + * when using keyboard navigation + * @param {Event} e keydown event + */ + }, { + key: "handleKeyDown", + value: function handleKeyDown(e) { + if (e.which === 32 || e.which === 13) { + e.preventDefault(); + this.handleTrackScrubberClick(); + e.stopPropagation(); + } + } + }, { + key: "handleClick", + value: function handleClick() { + this.handleTrackScrubberClick(); + } -// ENum for types transcript text lines in a time-synced transcript -var TRANSCRIPT_CUE_TYPES = { - note: 'NOTE', - timedCue: 'TIMED_CUE', - nonTimedLine: 'NON_TIMED_LINE' -}; + /** + * Click event handler for the track button on the player controls + */ + }, { + key: "handleTrackScrubberClick", + value: function handleTrackScrubberClick() { + var currentTrackRef = this.currentTrackRef, + player = this.player, + options = this.options; + // When player is not fully loaded on the page don't show the track scrubber + if (!options.trackScrubberRef.current || !currentTrackRef.current) return; -/** - * Parse the transcript information in the Manifest presented as supplementing annotations - * @param {String} manifestURL IIIF Presentation 3.0 manifest URL - * @param {String} title optional title given in the transcripts list in props - * @returns {Array} array of supplementing annotations for transcripts for all - * canvases in the Manifest - */ -function readSupplementingAnnotations(_x) { - return _readSupplementingAnnotations.apply(this, arguments); -} + // If player is fullscreen exit before displaying track scrubber + if (player.isFullscreen()) { + player.exitFullscreen(); + } + var tempZoom = this.zoomedOutRef.current; + this.setZoomedOut(!tempZoom); + } -/** - * Refine and sanitize the user provided transcripts list in the props. If there are manifests - * in the given array process them to find supplementing annotations in the manifest and - * them to the transcripts array to be displayed in the component. - * @param {Array} transcripts list of transcripts from Transcript component's props - * @returns {Array} a refined transcripts array for each canvas with the following json - * structure; - * { canvasId: , items: [{ title, filename, url, isMachineGen, id }]} - */ -function _readSupplementingAnnotations() { - _readSupplementingAnnotations = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee(manifestURL) { - var title, - data, - _args = arguments; - return regenerator.wrap(function _callee$(_context) { - while (1) switch (_context.prev = _context.next) { - case 0: - title = _args.length > 1 && _args[1] !== undefined ? _args[1] : ''; - _context.next = 3; - return fetch(manifestURL).then(function (response) { - var fileType = response.headers.get('Content-Type'); - if (fileType.includes('application/json')) { - var jsonData = response.json(); - return jsonData; - } else { - // Avoid throwing an error when fetched file is not a JSON - return {}; - } - }).then(function (manifest) { - var canvases = manifest.items; - var newTranscriptsList = []; - if ((canvases === null || canvases === void 0 ? void 0 : canvases.length) > 0) { - canvases.map(function (canvas, index) { - var annotations = getAnnotations(canvas.annotations, 'supplementing'); - var canvasTranscripts = []; - if (annotations.length > 0) { - var annotBody = annotations[0].body; - if (annotBody.type === 'TextualBody') { - var label = title.length > 0 ? title : annotBody.label ? getLabelValue(annotBody.label) : "Canvas-".concat(index); - var _identifyMachineGen = identifyMachineGen(label), - isMachineGen = _identifyMachineGen.isMachineGen, - labelText = _identifyMachineGen.labelText; - canvasTranscripts.push({ - url: annotBody.id === undefined ? manifestURL : annotBody.id, - title: labelText, - isMachineGen: isMachineGen, - id: "".concat(labelText, "-").concat(index), - format: '' - }); - } else { - annotations.forEach(function (annotation, i) { - var annotBody = annotation.body; - var label = ''; - var filename = ''; - if (annotBody.label && Object.keys(annotBody.label).length > 0) { - var languages = Object.keys(annotBody.label); - if ((languages === null || languages === void 0 ? void 0 : languages.length) > 1) { - // If there are multiple labels for an annotation assume the first - // is the one intended for default display. - label = getLabelValue(annotBody.label); - // Assume that an unassigned language is meant to be the downloadable filename - filename = annotBody.label.hasOwnProperty('none') ? getLabelValue(annotBody.label.none[0]) : label; - } else { - // If there is a single label, use for both label and downloadable filename - label = getLabelValue(annotBody.label); - } - } else { - label = "".concat(i); - } - var id = annotBody.id; - var sType = identifySupplementingAnnotation(id); - var _identifyMachineGen2 = identifyMachineGen(label), - isMachineGen = _identifyMachineGen2.isMachineGen, - labelText = _identifyMachineGen2.labelText; - if (filename === '') { - filename = labelText; - } - if (sType === 1 || sType === 3) { - canvasTranscripts.push({ - title: labelText, - filename: filename, - url: id, - isMachineGen: isMachineGen, - id: "".concat(labelText, "-").concat(index, "-").concat(i), - format: annotBody.format || '' - }); - } - }); - } - } - newTranscriptsList.push({ - canvasId: index, - items: canvasTranscripts - }); - }); - } - return newTranscriptsList; - })["catch"](function (error) { - console.error('transcript-parser -> readSupplementingAnnotations() -> error fetching transcript resource at, ', manifestURL); - return []; - }); - case 3: - data = _context.sent; - return _context.abrupt("return", data); - case 5: - case "end": - return _context.stop(); + /** + * Event handler for VideoJS player instance's 'timeupdate' event, which + * updates the track scrubber from player state. + */ + }, { + key: "handleTimeUpdate", + value: function handleTimeUpdate() { + var _player$markers$getMa; + var player = this.player, + options = this.options, + zoomedOutRef = this.zoomedOutRef; + // Hide track-scrubber for inaccessible item if it is open + if (player.canvasIsEmpty && !zoomedOutRef.current) { + this.setZoomedOut(true); + } + if (player.isDisposed() || player.ended()) return; + /* + Get the current track from the player.markers created from the structure timespans. + In playlists, markers are timepoint information representing highlighting annotations, + therefore omit reading markers information for track scrubber in playlist contexts. + */ + var playerCurrentTime = player.currentTime(); + if (player.markers && typeof player.markers !== 'function' && typeof player.markers.getMarkers === 'function' && ((_player$markers$getMa = player.markers.getMarkers()) === null || _player$markers$getMa === void 0 ? void 0 : _player$markers$getMa.length) > 0 && !options.isPlaylist) { + this.readPlayerMarkers(); + } else { + var _player$playableDurat, _player$altStart; + this.setCurrentTrack({ + duration: (_player$playableDurat = player.playableDuration) !== null && _player$playableDurat !== void 0 ? _player$playableDurat : player.duration(), + time: (_player$altStart = player.altStart) !== null && _player$altStart !== void 0 ? _player$altStart : 0, + key: '', + text: 'Complete media file' + }); + playerCurrentTime = player.srcIndex && player.srcIndex > 0 ? playerCurrentTime + player.altStart : playerCurrentTime; + } + this.updateTrackScrubberProgressBar(playerCurrentTime); + } + /** + * Calculate the progress and current time within the track and + * update them accordingly when the player's 'timeupdate' event fires. + * @param {Number} currentTime player's current time + */ + }, { + key: "updateTrackScrubberProgressBar", + value: function updateTrackScrubberProgressBar() { + var currentTime = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var player = this.player, + currentTrackRef = this.currentTrackRef; + // Handle Safari which emits the timeupdate event really quickly + if (!currentTrackRef.current) { + if (player.markers && typeof player.markers.getMarkers === 'function') { + this.readPlayerMarkers(); + } + } + var altStart = player.altStart, + srcIndex = player.srcIndex; + // Calculate corresponding time and played percentage values within track + var trackoffset = srcIndex > 0 ? currentTime - currentTrackRef.current.time + altStart : currentTime - currentTrackRef.current.time; + var trackpercent = Math.min(100, Math.max(0, 100 * trackoffset / currentTrackRef.current.duration)); + this.populateTrackScrubber(trackoffset, trackpercent); + } + }, { + key: "populateTrackScrubber", + value: + /** + * Update the track scrubber's current time, duration and played percentage + * when it is visible in UI. + * @param {Number} currentTime current time corresponding to the track + * @param {Number} playedPercentage elapsed time percentage of the track duration + */ + function populateTrackScrubber() { + var currentTime = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var playedPercentage = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + var trackScrubberRef = this.options.trackScrubberRef; + if (!trackScrubberRef.current) { + return; } - }, _callee); - })); - return _readSupplementingAnnotations.apply(this, arguments); -} -function sanitizeTranscripts(_x2) { - return _sanitizeTranscripts.apply(this, arguments); -} + var _trackScrubberRef$cur2 = _slicedToArray(trackScrubberRef.current.children, 3), + currentTimeDisplay = _trackScrubberRef$cur2[0]; + _trackScrubberRef$cur2[1]; + var durationDisplay = _trackScrubberRef$cur2[2]; -/** - * Group a nested JSON object array by a given property name - * @param {Array} objectArray nested array to reduced - * @param {String} indexKey property name to be used to group elements in the array - * @param {String} selectKey property to be selected from the objects to accumulated - * @returns {Array} - */ -function _sanitizeTranscripts() { - _sanitizeTranscripts = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee4(transcripts) { - var allTranscripts, sanitizedTrs, newTranscripts; - return regenerator.wrap(function _callee4$(_context4) { - while (1) switch (_context4.prev = _context4.next) { - case 0: - if (!(!transcripts || transcripts == undefined || transcripts.length == 0)) { - _context4.next = 5; - break; - } - console.error('No transcripts given as input'); - return _context4.abrupt("return", []); - case 5: - allTranscripts = []; // Build an empty list for each canvasId from the given transcripts prop - transcripts.map(function (trs) { - return allTranscripts.push({ - canvasId: trs.canvasId, - items: [] - }); - }); + // Set the elapsed time percentage in the progress bar of track scrubber + document.documentElement.style.setProperty('--range-scrubber', "calc(".concat(playedPercentage, "%)")); - // Process the async function to resolve manifest URLs in the given transcripts array - // parallely to extract supplementing annotations in the manifests - _context4.next = 9; - return Promise.all(transcripts.map( /*#__PURE__*/function () { - var _ref5 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee3(transcript) { - var canvasId, items, sanitizedItems; - return regenerator.wrap(function _callee3$(_context3) { - while (1) switch (_context3.prev = _context3.next) { - case 0: - canvasId = transcript.canvasId, items = transcript.items; - _context3.next = 3; - return Promise.all(items.map( /*#__PURE__*/function () { - var _ref6 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee2(item, index) { - var title, url, manifestTranscripts, _identifyMachineGen3, isMachineGen, labelText, manifestItems, groupedTrs; - return regenerator.wrap(function _callee2$(_context2) { - while (1) switch (_context2.prev = _context2.next) { - case 0: - title = item.title, url = item.url; // For each item in the list check if it is a manifest and parse - // the it to identify any supplementing annotations in the - // manifest for each canvas - _context2.next = 3; - return readSupplementingAnnotations(url, title); - case 3: - manifestTranscripts = _context2.sent; - _identifyMachineGen3 = identifyMachineGen(title), isMachineGen = _identifyMachineGen3.isMachineGen, labelText = _identifyMachineGen3.labelText; - manifestItems = []; - if ((manifestTranscripts === null || manifestTranscripts === void 0 ? void 0 : manifestTranscripts.length) > 0) { - manifestItems = manifestTranscripts.map(function (mt) { - return mt.items; - }).flat(); + // Update the track duration + durationDisplay.innerHTML = timeToHHmmss(this.currentTrackRef.current.duration); + // Update current time elapsed within the current track + var cleanTime = !isNaN(currentTime) && currentTime > 0 ? currentTime : 0; + currentTimeDisplay.innerHTML = timeToHHmmss(cleanTime); + } + }, { + key: "readPlayerMarkers", + value: function readPlayerMarkers() { + var tracks = this.player.markers.getMarkers().filter(function (m) { + return m["class"] == 'ramp--track-marker--fragment'; + }); + if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0) { + this.setCurrentTrack(tracks[0]); + } + } + }, { + key: "handleMouseMove", + value: + /** + * Event handler for mouseenter and mousemove pointer events on the + * the track scrubber. This sets the time tooltip value and its offset + * position in the UI. + * @param {Event} e pointer event for user interaction + */ + function handleMouseMove(e) { + var timeToolRef = this.options.timeToolRef; + if (!timeToolRef.current) { + return; + } + var time = this.getTrackTime(e); - // Concat the existing transcripts list and transcripts from the manifest and - // group them by canvasId - groupedTrs = groupByIndex(allTranscripts.concat(manifestTranscripts), 'canvasId', 'items'); - allTranscripts = groupedTrs; - } + // When hovering over the border of the track scrubber, convertTime() returns infinity, + // since e.target.clientWidth is zero. Use this value to not show the tooltip when this + // occurs. + if (isFinite(time)) { + // Calculate the horizontal position of the time tooltip using the event's offsetX property + var offset = e.offsetX - timeToolRef.current.offsetWidth / 2; // deduct 0.5 x width of tooltip element + timeToolRef.current.style.left = offset + 'px'; - // if manifest doesn't have canvases or - // supplementing annotations add original transcript from props - if (!(manifestTranscripts.length === 0 || manifestItems.length === 0)) { - _context2.next = 11; - break; - } - return _context2.abrupt("return", { - title: labelText, - filename: labelText, - url: url, - isMachineGen: isMachineGen, - id: "".concat(labelText, "-").concat(canvasId, "-").concat(index), - format: '' - }); - case 11: - return _context2.abrupt("return", null); - case 12: - case "end": - return _context2.stop(); - } - }, _callee2); - })); - return function (_x9, _x10) { - return _ref6.apply(this, arguments); - }; - }())); - case 3: - sanitizedItems = _context3.sent; - return _context3.abrupt("return", { - canvasId: canvasId, - items: sanitizedItems.filter(function (i) { - return i != null; - }) - }); - case 5: - case "end": - return _context3.stop(); - } - }, _callee3); - })); - return function (_x8) { - return _ref5.apply(this, arguments); - }; - }())); - case 9: - sanitizedTrs = _context4.sent; - // Group all the transcripts by canvasId one last time to eliminate duplicate canvasIds - newTranscripts = groupByIndex(allTranscripts.concat(sanitizedTrs), 'canvasId', 'items'); - return _context4.abrupt("return", newTranscripts); - case 12: - case "end": - return _context4.stop(); + // Set text in the tooltip as the time relevant to the pointer event's position + timeToolRef.current.innerHTML = timeToHHmmss(time); } - }, _callee4); - })); - return _sanitizeTranscripts.apply(this, arguments); -} -function groupByIndex(objectArray, indexKey, selectKey) { - return objectArray.reduce(function (acc, obj) { - var existing = acc.filter(function (a) { - return a[indexKey] == obj[indexKey]; - }); - if ((existing === null || existing === void 0 ? void 0 : existing.length) > 0) { - var current = existing[0]; - current[selectKey] = current[selectKey].concat(obj[selectKey]); - } else { - acc.push(obj); } - return acc; - }, []); -} + }, { + key: "handleSetProgress", + value: + /** + * Event handler for mousedown event on the track scrubber. This sets the + * progress percentage within track scrubber and update the player's current time + * when user clicks on a point within the track scrubber. + * @param {Event} e pointer event for user interaction + */ + function handleSetProgress(e) { + var currentTrackRef = this.currentTrackRef, + player = this.player; + if (!currentTrackRef.current) { + return; + } + var trackoffset = this.getTrackTime(e); + if (trackoffset != undefined) { + // Calculate percentage of the progress based on the pointer position's + // time and duration of the track + var trackpercent = Math.min(100, Math.max(0, 100 * (trackoffset / currentTrackRef.current.duration))); + + // Set the elapsed time in the scrubber progress bar + document.documentElement.style.setProperty('--range-scrubber', "calc(".concat(trackpercent, "%)")); + + // Set player's current time with respective to the altStart for clipped items + var playerCurrentTime = player.isClipped ? trackoffset + currentTrackRef.current.time : trackoffset; + player.currentTime(playerCurrentTime); + } + } + }, { + key: "getTrackTime", + value: + /** + * Convert pointer position on track scrubber to a time value + * @param {Event} e pointer event for user interaction + * @returns {Number} time corresponding to the pointer position + */ + function getTrackTime(e) { + var currentTrackRef = this.currentTrackRef; + if (!currentTrackRef.current) { + return; + } + var offsetx = e.offsetX; + if (offsetx && offsetx != undefined) { + var time = offsetx / e.target.clientWidth * currentTrackRef.current.duration; + return time; + } + } + }]); + return VideoJSTrackScrubber; +}(Button); +videojs.registerComponent('VideoJSTrackScrubber', VideoJSTrackScrubber); + +function _createForOfIteratorHelper$1(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$1(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } +function _unsupportedIterableToArray$1(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$1(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$1(o, minLen); } +function _arrayLikeToArray$1(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } +function ownKeys$4(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } +function _objectSpread$4(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$4(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$4(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } +require('@silvermine/videojs-quality-selector')(videojs); +// import vjsYo from './vjsYo'; /** - * Parse a given transcript file into a format the Transcript component - * can render on the UI. E.g.: text file -> returns null, so that the Google - * doc viewer is rendered, IIIF manifest -> extract and parse transcript data - * within the manifest. - * @param {String} url URL of the transcript file selected - * @param {Number} canvasIndex Current canvas rendered in the player - * @param {String} format transcript file format read from Annotation - * @returns {Object} Array of trancript data objects with download URL + * Module to setup VideoJS instance on initial page load and update + * on successive player reloads on Canvas changes. + * @param {Object} props + * @param {Boolean} props.isVideo + * @param {Boolean} props.isPlaylist + * @param {Object} props.trackScrubberRef + * @param {Object} props.scrubberTooltipRef + * @param {Array} props.tracks + * @param {String} props.placeholderText + * @param {Array} props.renderingFiles + * @param {Boolean} props.enableFileDownload + * @param {Function} props.loadPrevOrNext + * @param {Number} props.lastCanvasIndex + * @param {Boolean} props.enableTitleLink + * @param {String} props.videoJSLangMap + * @param {Object} props.options */ -function parseTranscriptData(_x3, _x4, _x5) { - return _parseTranscriptData.apply(this, arguments); -} +function VideoJSPlayer(_ref) { + var enableFileDownload = _ref.enableFileDownload, + enableTitleLink = _ref.enableTitleLink, + isVideo = _ref.isVideo, + options = _ref.options, + placeholderText = _ref.placeholderText, + scrubberTooltipRef = _ref.scrubberTooltipRef, + tracks = _ref.tracks, + trackScrubberRef = _ref.trackScrubberRef, + videoJSLangMap = _ref.videoJSLangMap, + withCredentials = _ref.withCredentials; + var playerState = usePlayerState(); + var playerDispatch = usePlayerDispatch(); + var manifestState = useManifestState(); + var manifestDispatch = useManifestDispatch(); + var canvasDuration = manifestState.canvasDuration, + canvasLink = manifestState.canvasLink, + hasMultiItems = manifestState.hasMultiItems, + targets = manifestState.targets, + autoAdvance = manifestState.autoAdvance, + structures = manifestState.structures, + canvasSegments = manifestState.canvasSegments, + hasStructure = manifestState.hasStructure; + var isEnded = playerState.isEnded, + isPlaying = playerState.isPlaying, + currentTime = playerState.currentTime; + var _useLocalStorage = useLocalStorage('startVolume', 1), + _useLocalStorage2 = _slicedToArray(_useLocalStorage, 2), + startVolume = _useLocalStorage2[0], + setStartVolume = _useLocalStorage2[1]; + var _useLocalStorage3 = useLocalStorage('startMuted', false), + _useLocalStorage4 = _slicedToArray(_useLocalStorage3, 2), + startMuted = _useLocalStorage4[0], + setStartMuted = _useLocalStorage4[1]; + var _useLocalStorage5 = useLocalStorage('startCaptioned', true), + _useLocalStorage6 = _slicedToArray(_useLocalStorage5, 2), + startCaptioned = _useLocalStorage6[0], + setStartCaptioned = _useLocalStorage6[1]; + var _useLocalStorage7 = useLocalStorage('startQuality', null), + _useLocalStorage8 = _slicedToArray(_useLocalStorage7, 2), + startQuality = _useLocalStorage8[0], + setStartQuality = _useLocalStorage8[1]; + var videoJSRef = useRef(null); + var captionsOnRef = useRef(); + var activeTrackRef = useRef(); + var _useMediaPlayer = useMediaPlayer(), + canvasIndex = _useMediaPlayer.canvasIndex, + canvasIsEmpty = _useMediaPlayer.canvasIsEmpty, + lastCanvasIndex = _useMediaPlayer.lastCanvasIndex; + var _useSetupPlayer = useSetupPlayer({ + enableFileDownload: enableFileDownload, + withCredentials: withCredentials, + lastCanvasIndex: lastCanvasIndex + }), + isPlaylist = _useSetupPlayer.isPlaylist, + renderingFiles = _useSetupPlayer.renderingFiles, + srcIndex = _useSetupPlayer.srcIndex, + switchPlayer = _useSetupPlayer.switchPlayer; + var _useShowInaccessibleM = useShowInaccessibleMessage({ + lastCanvasIndex: lastCanvasIndex + }), + messageTime = _useShowInaccessibleM.messageTime; + var canvasIsEmptyRef = useRef(); + canvasIsEmptyRef.current = useMemo(function () { + return canvasIsEmpty; + }, [canvasIsEmpty]); + var isEndedRef = useRef(); + isEndedRef.current = useMemo(function () { + return isEnded; + }, [isEnded]); + var isPlayingRef = useRef(); + isPlayingRef.current = useMemo(function () { + return isPlaying; + }, [isPlaying]); + var autoAdvanceRef = useRef(); + autoAdvanceRef.current = useMemo(function () { + return autoAdvance; + }, [autoAdvance]); + var srcIndexRef = useRef(); + srcIndexRef.current = useMemo(function () { + return srcIndex; + }, [srcIndex]); + var currentTimeRef = useRef(); + currentTimeRef.current = useMemo(function () { + return currentTime; + }, [currentTime]); -/** - * Parse MS word documents into HTML markdown using mammoth.js - * https://www.npmjs.com/package/mammoth - * @param {Object} response response from the fetch request - * @returns {Array} html markdown for the word document contents - */ -function _parseTranscriptData() { - _parseTranscriptData = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee5(url, canvasIndex, format) { - var tData, tUrl, contentType, fileData, fromContentType, fromAnnotFormat, fileType, urlExt, filteredExt, textData, textLines, jsonData, json, parsedText, _parseTimedText, _tData, tType; - return regenerator.wrap(function _callee5$(_context5) { - while (1) switch (_context5.prev = _context5.next) { - case 0: - tData = []; - tUrl = url; // Validate given URL - if (!(url === undefined)) { - _context5.next = 4; - break; - } - return _context5.abrupt("return", { - tData: tData, - tUrl: tUrl, - tType: TRANSCRIPT_TYPES.invalid - }); - case 4: - contentType = null; - fileData = null; // get file type - _context5.next = 8; - return fetch(url).then(handleFetchErrors).then(function (response) { - contentType = response.headers.get('Content-Type'); - fileData = response; - })["catch"](function (error) { - console.error('transcript-parser -> parseTranscriptData() -> fetching transcript -> ', error); - }); - case 8: - if (!(contentType == null)) { - _context5.next = 10; - break; - } - return _context5.abrupt("return", { - tData: [], - tUrl: tUrl, - tType: TRANSCRIPT_TYPES.invalid - }); - case 10: - /* - Use the Annotation format in the IIIF Manifest, file extension, and the - Content-Type in headers of the fetch request to determine the file type. - These are checked with priority descending in the order of Annotation format, - Content-Type in headers, and file extension in the resource URI. - */ - fromContentType = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { - return tm.type.includes(contentType.split(';')[0]); - }); - fromAnnotFormat = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { - return tm.type.includes(format); - }); - fileType = ''; - if ((fromAnnotFormat === null || fromAnnotFormat === void 0 ? void 0 : fromAnnotFormat.length) > 0) { - fileType = fromAnnotFormat[0].ext; - } else if (fromContentType.length > 0) { - fileType = fromContentType[0].ext; - } else { - urlExt = url.split('.').reverse()[0]; // Only use this if it exists in the supported list of file types for the component - filteredExt = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { - return tm.ext === urlExt; - }); - fileType = filteredExt.length > 0 ? urlExt : ''; - } + /** + * Setup player with player-related information parsed from the IIIF + * Manifest Canvas. This gets called on both initial page load and each + * Canvas switch to setup and update player respectively. + * @param {Object} player current player instance from Video.js + */ + var playerInitSetup = function playerInitSetup(player) { + player.on('ready', function () { + console.log('Player ready'); - // Return empty array to display an error message - if (!(canvasIndex === undefined)) { - _context5.next = 16; - break; - } - return _context5.abrupt("return", { - tData: tData, - tUrl: tUrl, - tType: TRANSCRIPT_TYPES.noTranscript - }); - case 16: - _context5.t0 = fileType; - _context5.next = _context5.t0 === 'json' ? 19 : _context5.t0 === 'txt' ? 28 : _context5.t0 === 'srt' ? 39 : _context5.t0 === 'vtt' ? 39 : _context5.t0 === 'docx' ? 49 : 53; - break; - case 19: - _context5.next = 21; - return fileData.json(); - case 21: - jsonData = _context5.sent; - if (!((jsonData === null || jsonData === void 0 ? void 0 : jsonData.type) === 'Manifest')) { - _context5.next = 26; - break; - } - return _context5.abrupt("return", parseManifestTranscript(jsonData, url, canvasIndex)); - case 26: - json = parseJSONData(jsonData); - return _context5.abrupt("return", { - tData: json.tData, - tUrl: tUrl, - tType: json.tType, - tFileExt: fileType - }); - case 28: - _context5.next = 30; - return fileData.text(); - case 30: - textData = _context5.sent; - textLines = textData.split('\n'); - if (!(textLines.length == 0)) { - _context5.next = 36; - break; - } - return _context5.abrupt("return", { - tData: [], - tUrl: url, - tType: TRANSCRIPT_TYPES.noTranscript - }); - case 36: - parsedText = buildNonTimedText(textLines); - return _context5.abrupt("return", { - tData: parsedText, - tUrl: url, - tType: TRANSCRIPT_TYPES.plainText, - tFileExt: fileType - }); - case 38: - case 39: - _context5.next = 41; - return fileData.text(); - case 41: - textData = _context5.sent; - textLines = textData.split('\n'); - if (!(textLines.length == 0)) { - _context5.next = 47; - break; - } - return _context5.abrupt("return", { - tData: [], - tUrl: url, - tType: TRANSCRIPT_TYPES.noTranscript - }); - case 47: - _parseTimedText = parseTimedText(textData, fileType === 'srt'), _tData = _parseTimedText.tData, tType = _parseTimedText.tType; - return _context5.abrupt("return", { - tData: _tData, - tUrl: url, - tType: tType, - tFileExt: fileType - }); - case 49: - _context5.next = 51; - return parseWordFile(fileData); - case 51: - tData = _context5.sent; - return _context5.abrupt("return", { - tData: splitIntoElements(tData), - tUrl: url, - tType: TRANSCRIPT_TYPES.docx, - tFileExt: fileType - }); - case 53: - return _context5.abrupt("return", { - tData: [], - tUrl: url, - tType: TRANSCRIPT_TYPES.noSupport + // Add this class in mobile/tablet devices to always show the control bar, + // since the inactivityTimeout is flaky in some browsers + if (IS_MOBILE || IS_IPAD) { + player.controlBar.addClass('vjs-mobile-visible'); + } + player.muted(startMuted); + player.volume(startVolume); + player.canvasIndex = cIndexRef.current; + player.duration(canvasDuration); + player.srcIndex = srcIndex; + player.targets = targets; + if (enableTitleLink) player.canvasLink = canvasLink; + + // Need to set this once experimentalSvgIcons option in Video.js options was enabled + player.getChild('controlBar').qualitySelector.setIcon('cog'); + }); + player.on('progress', function () { + // Reveal player if not revealed on 'loadedmetadata' event, allowing user to + // interact with the player since enough data is available for playback + if (player.hasClass('vjs-disabled')) { + player.removeClass('vjs-disabled'); + } + }); + player.on('canplay', function () { + // Reset isEnded flag + playerDispatch({ + isEnded: false, + type: 'setIsEnded' + }); + }); + player.on('play', function () { + playerDispatch({ + isPlaying: true, + type: 'setPlayingStatus' + }); + }); + player.on('timeupdate', function () { + handleTimeUpdate(); + }); + player.on('ended', function () { + /** + * Checking against isReadyRef.current stops from delayed events being executed + * when transitioning from a Canvas to the next. + * Checking against isPlayingRef.current to distinguish whether this event + * triggered intentionally, because Video.js seem to trigger this event when + * switching to a media file with a shorter duration in Safari browsers. + */ + setTimeout(function () { + if (isReadyRef.current && isPlayingRef.current) { + playerDispatch({ + isEnded: true, + type: 'setIsEnded' }); - case 54: - case "end": - return _context5.stop(); - } - }, _callee5); - })); - return _parseTranscriptData.apply(this, arguments); -} -function parseWordFile(_x6) { - return _parseWordFile.apply(this, arguments); -} -/** - * Parse json data into Transcript component friendly - * format - * @param {Object} jsonData array of JSON objects - * @returns {Object} - */ -function _parseWordFile() { - _parseWordFile = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee6(response) { - var tData, data, arrayBuffer; - return regenerator.wrap(function _callee6$(_context6) { - while (1) switch (_context6.prev = _context6.next) { - case 0: - tData = null; - _context6.next = 3; - return response.blob(); + player.pause(); + if (!canvasIsEmptyRef.current) handleEnded(); + } + }, 100); + }); + player.on('volumechange', function () { + setStartMuted(player.muted()); + setStartVolume(player.volume()); + }); + player.on('qualityRequested', function (e, quality) { + setStartQuality(quality.label); + }); + // Use error event listener for inaccessible item display + player.on('error', function (e) { + var error = player.error(); + var errorMessage = 'Something went wrong. Please try again later or contact support for help.'; + // Handle different error codes + switch (error.code) { + case 1: + console.error('MEDIA_ERR_ABORTED: The fetching process for the media resource was aborted by the user agent\ + at the user’s request.'); + break; + case 2: + errorMessage = 'The media could not be loaded due to a network error. Please try again later.'; + console.error('MEDIA_ERR_NETWORK: A network error caused the user agent to stop fetching the media resource,\ + after the resource was established to be usable.'); + break; case 3: - data = _context6.sent; - arrayBuffer = new File([data], name, { - type: response.headers.get('content-type') - }); - _context6.next = 7; - return mammoth.convertToHtml({ - arrayBuffer: arrayBuffer - }).then(function (result) { - tData = result.value; - })["catch"](function (err) { - console.error(err); - }); - case 7: - return _context6.abrupt("return", tData); - case 8: - case "end": - return _context6.stop(); + errorMessage = 'Media is corrupt or has features not supported by the browser. \ + Please try a different media or contact support for help.'; + console.error('MEDIA_ERR_DECODE: An error occurred while decoding the media resource, after\ + the resource was established to be usable.'); + break; + case 4: + errorMessage = 'Media could not be loaded. Network error or media format is not supported.'; + console.error('MEDIA_ERR_SRC_NOT_SUPPORTED: The media resource indicated by the src attribute was not suitable.'); + break; + default: + console.error('An unknown error occurred.'); + break; } - }, _callee6); - })); - return _parseWordFile.apply(this, arguments); -} -function parseJSONData(jsonData) { - if (jsonData.length == 0) { - return { - tData: [], - tType: TRANSCRIPT_TYPES.noTranscript - }; - } - var tData = []; - var _iterator = _createForOfIteratorHelper(jsonData), - _step; - try { - for (_iterator.s(); !(_step = _iterator.n()).done;) { - var jd = _step.value; - if (jd.speaker) { - var speaker = jd.speaker, - spans = jd.spans; - var _iterator2 = _createForOfIteratorHelper(spans), - _step2; - try { - for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { - var span = _step2.value; - span.speaker = speaker; - tData.push(span); - } - } catch (err) { - _iterator2.e(err); - } finally { - _iterator2.f(); - } + // Show dismissable error display modal from Video.js + var errorDisplay = player.getChild('ErrorDisplay'); + if (errorDisplay) { + errorDisplay.contentEl().innerText = errorMessage; + errorDisplay.removeClass('vjs-hidden'); + player.removeClass('vjs-error'); + player.removeClass('vjs-disabled'); + } + e.stopPropagation(); + }); + playerLoadedMetadata(player); + }; + + /** + * Update player properties and data when player is reloaded with + * source change, i.e. Canvas change + * @param {Object} player + */ + var updatePlayer = function updatePlayer(player) { + player.duration(canvasDuration); + player.src(options.sources); + player.poster(options.poster); + player.canvasIndex = cIndexRef.current; + player.canvasIsEmpty = canvasIsEmptyRef.current; + player.srcIndex = srcIndex; + player.targets = targets; + if (enableTitleLink) player.canvasLink = canvasLink; + + // Update textTracks in the player + var oldTracks = player.remoteTextTracks(); + var i = oldTracks.length; + while (i--) { + player.removeRemoteTextTrack(oldTracks[i]); + } + if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0 && isVideo) { + tracks.forEach(function (track) { + player.addRemoteTextTrack(track, false); + }); + } + + /* + Update player control bar for; + - track scrubber button + - appearance of the player: big play button and aspect ratio of the player + based on media type + - volume panel based on media type + - file download menu + */ + if (player.getChild('controlBar') != null && !canvasIsEmpty) { + var controlBar = player.getChild('controlBar'); + // Index of the full-screen toggle in the player's control bar + var fullscreenIndex = controlBar.children().findIndex(function (c) { + return c.name_ == 'FullscreenToggle'; + }); + /* + Track-scrubber button: remove if the Manifest is not a playlist manifest + or the current Canvas doesn't have structure items. Or add back in if it's + not present otherwise. + */ + if (!(hasStructure || isPlaylist)) { + controlBar.removeChild('videoJSTrackScrubber'); + } else if (!controlBar.getChild('videoJSTrackScrubber')) { + // Add track-scrubber button after duration display if it is not available + controlBar.addChild('videoJSTrackScrubber', { + trackScrubberRef: trackScrubberRef, + timeToolRef: scrubberTooltipRef + }); + } + if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0 && isVideo && !controlBar.getChild('subsCapsButton')) { + var captionIndex = IS_MOBILE ? controlBar.children().findIndex(function (c) { + return c.name_ == 'MuteToggle'; + }) : controlBar.children().findIndex(function (c) { + return c.name_ == 'VolumePanel'; + }); + var subsCapBtn = controlBar.addChild('subsCapsButton', {}, captionIndex + 1); + // Add CSS to mark captions-on + subsCapBtn.children_[0].addClass('captions-on'); + } + + /* + Change player's appearance when switching between audio and video canvases. + For audio: player height is reduced and big play button is removed + For video: player aspect ratio is set to 16:9 and has the centered big play button + */ + if (!isVideo) { + player.audioOnlyMode(true); + player.addClass('vjs-audio'); + player.height(player.controlBar.height()); + player.removeChild('bigPlayButton'); } else { - var _iterator3 = _createForOfIteratorHelper(jd.spans), - _step3; - try { - for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { - var _span = _step3.value; - _span.format = 'text/plain'; - _span.tag = TRANSCRIPT_CUE_TYPES.timedCue; - tData.push(_span); - } - } catch (err) { - _iterator3.e(err); - } finally { - _iterator3.f(); + player.audioOnlyMode(false); + player.removeClass('vjs-audio'); + player.aspectRatio('16:9'); + player.addChild('bigPlayButton'); + } + + /* + Re-add volumePanel/muteToggle icon: ensures the correct order of controls + on player reload. + On mobile device browsers, the volume panel is replaced by muteToggle + for both audio and video. + */ + if (!IS_MOBILE) { + controlBar.removeChild('VolumePanel'); + controlBar.addChild('VolumePanel'); + /* + Trigger ready event to reset the volume slider in the refreshed + volume panel. This is needed on player reload, since volume slider + is set on either 'ready' or 'volumechange' events. + */ + player.trigger('volumechange'); + } else { + controlBar.removeChild('MuteToggle'); + controlBar.addChild('MuteToggle'); + } + if (enableFileDownload) { + var fileDownloadIndex = controlBar.children().findIndex(function (c) { + return c.name_ == 'VideoJSFileDownload'; + }) || fullscreenIndex + 1; + controlBar.removeChild('videoJSFileDownload'); + if ((renderingFiles === null || renderingFiles === void 0 ? void 0 : renderingFiles.length) > 0) { + var fileOptions = { + title: 'Download Files', + controlText: 'Alternate resource download', + files: renderingFiles + }; + controlBar.addChild('videoJSFileDownload', _objectSpread$4({}, fileOptions), fileDownloadIndex); } } } - } catch (err) { - _iterator.e(err); - } finally { - _iterator.f(); - } - return { - tData: tData, - tType: TRANSCRIPT_TYPES.timedText + playerLoadedMetadata(player); }; -} - -/* Parsing annotations when transcript data is fed from a IIIF manifest */ -/** - * Parse a IIIF manifest and extracts the transcript data. - * IIIF manifests can present transcript data in a couple of different ways. - * 1. Using 'rendering' prop to link to an external file - * a. when the external file contains only text - * b. when the external file contains annotations - * 2. Using IIIF 'annotations' within the manifest - * @param {Object} manifest IIIF manifest data - * @param {String} manifestURL IIIF manifest URL - * @param {Number} canvasIndex Current canvas index - * @returns {Object} object with the structure; - * { tData: transcript data, tUrl: file url } - */ -function parseManifestTranscript(manifest, manifestURL, canvasIndex) { - var _manifest$items; - var tData = []; - var tUrl = manifestURL; - var isExternalAnnotation = false; - var annotations = []; - if (manifest.annotations) { - annotations = getAnnotations(manifest.annotations, 'supplementing'); - } else if (((_manifest$items = manifest.items) === null || _manifest$items === void 0 ? void 0 : _manifest$items.length) > 0) { - var _manifest$items$canva; - annotations = getAnnotations((_manifest$items$canva = manifest.items[canvasIndex]) === null || _manifest$items$canva === void 0 ? void 0 : _manifest$items$canva.annotations, 'supplementing'); - } - - // determine whether annotations point to an external resource or - // a list of transcript fragments - if (annotations.length > 0) { - var annotation = annotations[0]; - var tType = annotation.body.type; - if (tType == 'TextualBody') { - isExternalAnnotation = false; - } else { - isExternalAnnotation = true; - } - } else { - return { - tData: [], - tUrl: tUrl, - tType: TRANSCRIPT_TYPES.noTranscript - }; - } - if (isExternalAnnotation) { - var _annotation = annotations[0]; - return parseExternalAnnotations(_annotation); - } else { - tData = createTData(annotations); - return { - tData: tData, - tUrl: tUrl, - tType: TRANSCRIPT_TYPES.timedText, - tFileExt: 'json' - }; - } -} -/** - * Parse annotation linking to external resources like WebVTT, SRT, Text, and - * AnnotationPage .json files - * @param {Annotation} annotation Annotation from the manifest - * @returns {Object} object with the structure { tData: [], tUrl: '', tType: '' } - */ -function parseExternalAnnotations(_x7) { - return _parseExternalAnnotations.apply(this, arguments); -} -/** - * Converts Annotation to the common format that the - * transcripts component expects - * @param {Array} annotations array of Annotations - * @returns {Array} array of JSON objects - * Structure of the JSON object is as follows; - * { - * begin: 0, - * end: 60, - * text: 'Transcript text', - * format: 'text/plain', - * } - */ -function _parseExternalAnnotations() { - _parseExternalAnnotations = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee7(annotation) { - var tData, type, tBody, tUrl, tType, tFormat, tFileExt; - return regenerator.wrap(function _callee7$(_context7) { - while (1) switch (_context7.prev = _context7.next) { - case 0: - tData = []; - type = ''; - tBody = annotation.body; - tUrl = tBody.id; - tType = tBody.type; - tFormat = tBody.format; - tFileExt = ''; - /** When external file contains text data */ - if (!(tType === 'Text')) { - _context7.next = 12; - break; - } - _context7.next = 10; - return fetch(tUrl).then(handleFetchErrors).then(function (response) { - return response.text(); - }).then(function (data) { - if (TRANSCRIPT_MIME_TYPES.webvtt.includes(tFormat) || TRANSCRIPT_MIME_TYPES.srt.includes(tFormat)) { - var parsed = parseTimedText(data, TRANSCRIPT_MIME_TYPES.srt.includes(tFormat)); - tData = parsed.tData; - type = parsed.tType; - tFileExt = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { - return tm.type.includes(tFormat); - })[0].ext; - } else { - var textLines = data.split('\n'); - tData = buildNonTimedText(textLines); - type = TRANSCRIPT_TYPES.plainText; - tFileExt = 'txt'; - } - })["catch"](function (error) { - console.error('transcript-parser -> parseExternalAnnotations() -> fetching external transcript -> ', error); - throw error; - }); - case 10: - _context7.next = 15; - break; - case 12: - if (!(tType === 'AnnotationPage')) { - _context7.next = 15; - break; - } - _context7.next = 15; - return fetch(tUrl).then(handleFetchErrors).then(function (response) { - return response.json(); - }).then(function (data) { - var annotations = getAnnotations([data], 'supplementing'); - tData = createTData(annotations); - type = TRANSCRIPT_TYPES.timedText; - tFileExt = 'json'; + /** + * Setup on loadedmetadata event is broken out of initial setup function, + * since this needs to be called when reloading the player on Canvas change + * @param {Object} player Video.js player instance + */ + var playerLoadedMetadata = function playerLoadedMetadata(player) { + player.one('loadedmetadata', function () { + console.log('Player loadedmetadata'); + player.duration(canvasDuration); + isEndedRef.current ? player.currentTime(0) : player.currentTime(currentTimeRef.current); + if (isEndedRef.current || isPlayingRef.current) { + /* + iOS devices lockdown the ability for unmuted audio and video media to autoplay. + They accomplish this by capturing any programmatic play events and returning + a rejected Promise. In certain versions of iOS, this rejected promise would + cause a runtime error within Ramp. This error would cause the error boundary + handling to trigger, forcing a user to reload the player/page. By silently + catching the rejected Promise we are able to provide a more seamless user + experience, where the user can manually play the media or change to a different + section. + */ + var promise = player.play(); + if (promise !== undefined) { + promise.then(function (_) { + // Autoplay })["catch"](function (error) { - console.error('transcript-parser -> parseExternalAnnotations() -> fetching annotations -> ', error); - throw error; - }); - case 15: - return _context7.abrupt("return", { - tData: tData, - tUrl: tUrl, - tType: type, - tFileExt: tFileExt + // Prevent error from triggering error boundary }); - case 16: - case "end": - return _context7.stop(); + } + } + if (isVideo) { + setUpCaptions(player); } - }, _callee7); - })); - return _parseExternalAnnotations.apply(this, arguments); -} -function createTData(annotations) { - var tData = []; - annotations.map(function (a) { - if (a.id != null) { - var tBody = a.body; - var _getMediaFragment = getMediaFragment(a.target), - start = _getMediaFragment.start, - end = _getMediaFragment.end; - tData.push({ - text: tBody.value, - format: tBody.format, - begin: parseFloat(start), - end: parseFloat(end), - tag: TRANSCRIPT_CUE_TYPES.timedCue - }); - } - }); - return tData; -} - -/** - * Parsing transcript data from a given file with timed text - * @param {Object} fileData content in the transcript file - * @param {Boolean} isSRT given transcript file is an SRT - * @returns {Array} array of JSON objects of the following - * structure; - * { - * begin: '00:00:00.000', - * end: '00:01:00.000', - * text: 'Transcript text sample' - * tag: NOTE || TIMED_CUE - * } - */ -function parseTimedText(fileData) { - var isSRT = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - var tData = []; - var noteLines = []; - - // split file content into lines - var lines = fileData.split('\n'); - // For SRT files all of the file content is considered as cues - var cueLines = lines; - if (!isSRT) { - var _validateWebVTT = validateWebVTT(lines), - valid = _validateWebVTT.valid, - cue_lines = _validateWebVTT.cue_lines, - notes = _validateWebVTT.notes; - if (!valid) { - console.error('Invalid WebVTT file'); - return { - tData: [], - tType: TRANSCRIPT_TYPES.invalidVTT - }; - } - cueLines = cue_lines; - noteLines = notes; - } - var groups = groupTimedTextLines(cueLines); + /* + Set playable duration within the given media file and alternate start time as + player properties. These values are read by track-scrubber component to build + and update the track-scrubber progress and time in the UI. + */ + var mediaRange = getMediaFragment(player.src(), canvasDuration); + if (mediaRange != undefined) { + player.playableDuration = mediaRange.end - mediaRange.start; + player.altStart = mediaRange.start; + } else { + player.playableDuration = canvasDuration; + player.altStart = targets[srcIndex].altStart; + } + player.canvasIndex = cIndexRef.current; + setIsReady(true); - // Add back the NOTE(s) in the header block - groups.unshift.apply(groups, _toConsumableArray(noteLines)); - var hasInvalidTimestamp = false; - for (var i = 0; i < groups.length;) { - var line = parseTimedTextLine(groups[i], isSRT); - if (!line) { - hasInvalidTimestamp || (hasInvalidTimestamp = true); - break; - } else { - tData.push(line); - i++; - } - } - return { - tData: hasInvalidTimestamp ? null : tData, - tType: hasInvalidTimestamp ? TRANSCRIPT_TYPES.invalidTimestamp : TRANSCRIPT_TYPES.timedText + /** + * Update currentNavItem on loadedmetadata event in Safari, as it doesn't + * trigger the 'timeupdate' event intermittently on load. + */ + if (IS_SAFARI) { + handleTimeUpdate(); + } + + /** + * When either player/browser tab is muted Safari and Chrome in iOS doesn't seem to + * load enough data related to audio-only media for the Video.js instance to play + * on page load. + * Since, it is not possible to detect muted tabs in JS the condition avoids + * checking for muted state altogether. + * Without this, Safari will not reach player.readyState() = 4, the state + * which indicates the player that enough data is available on the media + * for playback. + */ + if (!isVideo && (IS_SAFARI || IS_IOS) && player.readyState() != 4) { + player.load(); + } + + // Reveal player if not revealed on 'progress' event, allowing user to + // interact with the player since enough data is available for playback + if (player.hasClass('vjs-disabled')) { + player.removeClass('vjs-disabled'); + } + }); }; -} + var _useVideoJSPlayer = useVideoJSPlayer({ + options: options, + playerInitSetup: playerInitSetup, + updatePlayer: updatePlayer, + startQuality: startQuality, + tracks: tracks, + videoJSRef: videoJSRef, + videoJSLangMap: videoJSLangMap + }), + activeId = _useVideoJSPlayer.activeId, + fragmentMarker = _useVideoJSPlayer.fragmentMarker, + isReadyRef = _useVideoJSPlayer.isReadyRef, + playerRef = _useVideoJSPlayer.playerRef, + setActiveId = _useVideoJSPlayer.setActiveId, + setFragmentMarker = _useVideoJSPlayer.setFragmentMarker, + setIsReady = _useVideoJSPlayer.setIsReady; + var cIndexRef = useRef(); + cIndexRef.current = useMemo(function () { + return canvasIndex; + }, [canvasIndex]); + var activeIdRef = useRef(); + activeIdRef.current = useMemo(function () { + return activeId; + }, [activeId]); -/** - * Validate WebVTT file with its header content - * @param {Array} lines WebVTT file content split into lines - * @returns {Boolean} - */ -function validateWebVTT(lines) { - var firstLine = lines.shift().trim(); - if ((firstLine === null || firstLine === void 0 ? void 0 : firstLine.length) == 6 && firstLine === 'WEBVTT') { - var _validateWebVTTHeader = validateWebVTTHeaders(lines), - valid = _validateWebVTTHeader.valid, - cue_lines = _validateWebVTTHeader.cue_lines, - notes = _validateWebVTTHeader.notes; - return { - valid: valid, - cue_lines: cue_lines, - notes: notes - }; - } else { - return { - valid: false, - cue_lines: [], - notes: [] - }; - } -} + /** + * Setup captions for the player based on context + * @param {Object} player Video.js player instance + */ + var setUpCaptions = function setUpCaptions(player) { + var _textTracks$tracks_; + var textTracks = player.textTracks(); + /* + Filter the text track Video.js adds with an empty label and language + when nativeTextTracks are enabled for iPhones and iPads. + Related links, Video.js => https://github.com/videojs/video.js/issues/2808 and + in Apple => https://developer.apple.com/library/archive/qa/qa1801/_index.html + */ + if (IS_MOBILE && !IS_ANDROID) { + textTracks.on('addtrack', function () { + for (var i = 0; i < textTracks.length; i++) { + if (textTracks[i].language === '' && textTracks[i].label === '') { + player.textTracks().removeTrack(textTracks[i]); + } + /** + * This enables the caption in the native iOS player first playback. + * Only enable caption when captions are turned on. + * First caption is already turned on in the code block below, so read it + * from activeTrackRef + */ + if (startCaptioned && activeTrackRef.current) { + textTracks.tracks_.filter(function (t) { + return t.label === activeTrackRef.current.label && t.language === activeTrackRef.current.language; + })[0].mode = 'showing'; + } + } + }); + } -/** - * Validate the text between 'WEBVTT' at the start and start of - * VTT cues. It looks for REGION and STYLE blocks and skips over these - * blocks. This doesn't validate the content within these blocks. - * When there's text in the header not followed by the keywords REGION and - * STYLE the WebVTT file is marked invalid. - * @param {Array} lines WebVTT file content split into lines - * @returns - */ -function validateWebVTTHeaders(lines) { - var endOfHeadersIndex = 0; - var firstCueIndex = 0; - var hasTextBeforeCues = false; - var notesInHeader = []; + // Turn first caption/subtitle ON and turn captions ON indicator via CSS on first load + if (((_textTracks$tracks_ = textTracks.tracks_) === null || _textTracks$tracks_ === void 0 ? void 0 : _textTracks$tracks_.length) > 0) { + var firstSubCap = null; + // Flag to identify first valid caption for resource + var onFirstCap = false; + // Disable all text tracks to avoid multiple selections and pick the first one as default + for (var i = 0; i < textTracks.tracks_.length; i++) { + var t = textTracks.tracks_[i]; + if ((t.kind === 'subtitles' || t.kind === 'captions') && t.language != '' && t.label != '') { + t.mode = 'disabled'; + if (!onFirstCap) firstSubCap = t; + onFirstCap = true; + } + } - // Remove line numbers for vtt cues - lines = lines.filter(function (l) { - return Number(l) ? false : true; - }); - for (var i = 0; i < lines.length; i++) { - var line = lines[i]; - // Skip REGION and STYLE blocks as these are related to displaying cues as overlays - if (/^REGION$/.test(line.toUpperCase()) || /^STYLE$/.test(line.toUpperCase())) { - // Increment until an empty line is encountered within the header block - i++; - while (i < lines.length && (!lines[i] == '\r' || !lines[i] == '\n' || !lines[i] == '\r\n')) { - i++; + // Enable the first caption when captions are enabled in the session + if (firstSubCap && startCaptioned) { + firstSubCap.mode = 'showing'; + activeTrackRef.current = firstSubCap; + handleCaptionChange(true); } - endOfHeadersIndex = i; } - // Gather comments presented as NOTE(s) in the header block to be displayed as transcript - else if (/^NOTE$/.test(line.toUpperCase())) { - var noteText = line; - i++; - // Increment until an empty line is encountered within the NOTE block - while (i < lines.length && (!lines[i] == '\r' || !lines[i] == '\n' || !lines[i] == '\r\n')) { - noteText = "".concat(noteText, "
    ").concat(lines[i].trim()); - i++; + + // Add/remove CSS to indicate captions/subtitles is turned on + textTracks.on('change', function () { + var trackModes = []; + for (var _i = 0; _i < textTracks.tracks_.length; _i++) { + var _textTracks$_i = textTracks[_i], + mode = _textTracks$_i.mode, + label = _textTracks$_i.label, + kind = _textTracks$_i.kind; + trackModes.push(textTracks[_i].mode); + if (mode === 'showing' && label != '' && (kind === 'subtitles' || kind === 'captions')) { + activeTrackRef.current = textTracks[_i]; + } } - notesInHeader.push({ - times: '', - line: noteText, - tag: TRANSCRIPT_CUE_TYPES.note - }); - } - // Terminate validation once the first cue is reached - else if (line.includes('-->')) { - // Break the loop when it reaches the first vtt cue - firstCueIndex = i; - break; + var subsOn = trackModes.includes('showing') ? true : false; + handleCaptionChange(subsOn); + setStartCaptioned(subsOn); + }); + }; + + /** + * Add CSS class to icon to indicate captions are on/off in player control bar + * @param {Boolean} subsOn flag to indicate captions are on/off + */ + var handleCaptionChange = function handleCaptionChange(subsOn) { + var player = playerRef.current; + /** + * When subsCapsButton is not setup on Video.js initialization step, and is + * later added in updatePlayer() function player.controlBar.getChild() method + * needs to be used to access it. + */ + var subsCapsBtn = player.controlBar.getChild('subsCapsButton'); + /* + For audio instances Video.js is setup to not to build the CC button + in Ramp's player control bar. + */ + if (subsCapsBtn == undefined || !subsCapsBtn || !(subsCapsBtn !== null && subsCapsBtn !== void 0 && subsCapsBtn.children_)) { + return; } - // Flag to check for invalid text before cue lines - else if (typeof line === 'string' && line.trim().length != 0) { - hasTextBeforeCues = true; + if (subsOn) { + subsCapsBtn.children_[0].addClass('captions-on'); + captionsOnRef.current = true; + } else { + subsCapsBtn.children_[0].removeClass('captions-on'); + captionsOnRef.current = false; } - } + }; + + /** + * Handle the 'ended' event fired by the player when a section comes to + * an end. If there are sections ahead move onto the next canvas and + * change the player and the state accordingly. + * Throttle helps to cancel the delayed function call triggered by ended event and + * load the correct item into the player, when the user clicks on a different item + * (not the next item in list) when the current item is coming to its end. + */ + var handleEnded = useMemo(function () { + return throttle_1(function () { + var isLastCanvas = cIndexRef.current === lastCanvasIndex; + /** + * Do nothing if Canvas is not multi-sourced AND autoAdvance is turned off + * OR current Canvas is the last Canvas in the Manifest + */ + if ((!autoAdvanceRef.current || isLastCanvas) && !hasMultiItems) { + return; + } else { + // Remove all the existing structure related markers in the player + if (playerRef.current && playerRef.current.markers) { + playerRef.current.pause(); + setFragmentMarker(null); + playerRef.current.markers.removeAll(); + } + if (hasMultiItems) { + // When there are multiple sources in a single canvas + // advance to next source + if (srcIndex + 1 < targets.length) { + manifestDispatch({ + srcIndex: srcIndex + 1, + type: 'setSrcIndex' + }); + playerDispatch({ + currentTime: 0, + type: 'setCurrentTime' + }); + playerRef.current.play(); + } else { + return; + } + } else if ((structures === null || structures === void 0 ? void 0 : structures.length) > 0) { + var nextItem = structures[cIndexRef.current + 1]; + if (nextItem) { + manifestDispatch({ + canvasIndex: cIndexRef.current + 1, + type: 'switchCanvas' + }); - // Return the cues and comments in the header block when the given WebVTT is valid - if (firstCueIndex > endOfHeadersIndex && !hasTextBeforeCues) { - return { - valid: true, - cue_lines: lines.slice(firstCueIndex), - notes: notesInHeader - }; - } else { - return { - valid: false - }; - } -} + // Reset startTime and currentTime to zero + playerDispatch({ + startTime: 0, + type: 'setTimeFragment' + }); + playerDispatch({ + currentTime: 0, + type: 'setCurrentTime' + }); -/** - * Group multi line transcript text values alongside the relevant - * timestamp values. E.g. converts, - * [ - * "00:00:00.000 --> 00:01:00.000", "Transcript", " from multiple lines", - * "00:03:00.000 --> 00:04:00.000", "Next transcript text", - * "NOTE This is a comment" - * ] - * into - * [ - * { times: "00:00:00.000 --> 00:01:00.000", line: "Transcript from multiple lines", tag: "TIMED_CUE" }, - * { times: "00:03:00.000 --> 00:04:00.000", line: "Next transcript text", tag: "TIMED_CUE" }, - * { times: "", line: "NOTE This is a comment", tag: "NOTE" } - * ] - * @param {Array} lines array of lines in the WebVTT file - * @returns {Array} - */ -function groupTimedTextLines(lines) { - var groups = []; - var i; - for (i = 0; i < lines.length; i++) { - var line = lines[i]; - var t = {}; - if (line.includes('-->') || /^NOTE/.test(line)) { - var isNote = /^NOTE/.test(line); - t.times = isNote ? "" : line; - t.tag = isNote ? TRANSCRIPT_CUE_TYPES.note : TRANSCRIPT_CUE_TYPES.timedCue; - // Make sure there is a single space separating NOTE from the comment for single or multi-line comments - t.line = isNote ? line.replace(/^NOTE\s*/, 'NOTE ') : ''; - i++; + // Get first timespan in the next canvas + var firstTimespanInNextCanvas = canvasSegments.filter(function (t) { + return t.canvasIndex === nextItem.canvasIndex && t.itemIndex === 1; + }); + // If the nextItem doesn't have an ID (a Canvas media fragment) pick the first timespan + // in the next Canvas + var nextFirstItem = nextItem.id != undefined ? nextItem : firstTimespanInNextCanvas[0]; + var start = 0; + if (nextFirstItem != undefined && nextFirstItem.id != undefined) { + start = getMediaFragment(nextFirstItem.id, canvasDuration).start; + } - // Increment until an empty line is encountered marking the end of the block - while (i < lines.length && !(lines[i] == '\r' || lines[i] == '\n' || lines[i] == '\r\n' || lines[i] == '')) { - t.line += lines[i].endsWith('-') ? lines[i] : lines[i].replace(/\s*$/, ' '); - i++; + // If there's a timespan item at the start of the next canvas + // mark it as the currentNavItem. Otherwise empty out the currentNavItem. + if (start === 0) { + manifestDispatch({ + item: nextFirstItem, + type: 'switchItem' + }); + } else if (nextFirstItem.isEmpty) { + // Switch the currentNavItem and clear isEnded flag + manifestDispatch({ + item: nextFirstItem, + type: 'switchItem' + }); + playerRef.current.currentTime(start); + // Only play if the next item is not an inaccessible item + if (!nextItem.isEmpty) playerRef.current.play(); + } + } + } + } + }); + }, [cIndexRef.current]); + + /** + * Handle the 'timeUpdate' event emitted by VideoJS player. + * The current time of the playhead used to show structure in the player's + * time rail as the playhead arrives at a start time of an existing structure + * item. When the current time is inside an item, that time fragment is highlighted + * in the player's time rail. + * Using throttle helps for smooth updates by cancelling and cleaning up intermediate + * delayed function calls. + */ + var handleTimeUpdate = useMemo(function () { + return throttle_1(function () { + var player = playerRef.current; + if (player && isReadyRef.current) { + var _player$currentTime; + var playerTime = (_player$currentTime = player.currentTime()) !== null && _player$currentTime !== void 0 ? _player$currentTime : currentTimeRef.current; + if (hasMultiItems && srcIndexRef.current > 0) { + playerTime = playerTime + targets[srcIndexRef.current].altStart; + } + var activeSegment = getActiveSegment(playerTime); + // the active segment has changed + if (activeIdRef.current !== (activeSegment === null || activeSegment === void 0 ? void 0 : activeSegment.id)) { + if (!activeSegment) { + /** + * Clear currentNavItem and other related state variables to update the tracker + * in structure navigation and highlights within the player. + */ + manifestDispatch({ + item: null, + type: 'switchItem' + }); + setActiveId(null); + setFragmentMarker(null); + } else { + // Set the active segment in state + manifestDispatch({ + item: activeSegment, + type: 'switchItem' + }); + setActiveId(activeSegment.id); + if (!isPlaylist && player.markers) { + var _getMediaFragment = getMediaFragment(activeSegment.id, activeSegment.canvasDuration), + start = _getMediaFragment.start, + end = _getMediaFragment.end; + playerDispatch({ + endTime: end, + startTime: start, + type: 'setTimeFragment' + }); + if (start !== end) { + // don't let marker extend past the end of the canvas + var markerEnd = end > activeSegment.canvasDuration ? activeSegment.canvasDuration : end; + setFragmentMarker({ + time: start, + duration: markerEnd - start, + text: start, + "class": 'ramp--track-marker--fragment' + }); + } else { + // to prevent zero duration fragments I suppose + setFragmentMarker(null); + } + } else if (fragmentMarker !== null) { + setFragmentMarker(null); + } + } + } + } + }, 10); + }, []); + + /** + * Toggle play/pause on video touch for mobile browsers + * @param {Object} e onTouchEnd event + */ + var mobilePlayToggle = function mobilePlayToggle(e) { + var player = playerRef.current; + if (e.changedTouches[0].clientX == touchX && e.changedTouches[0].clientY == touchY) { + if (player.paused()) { + player.play(); + } else { + player.pause(); } - t.line = t.line.trimEnd(); - groups.push(t); } - } - return groups; -} + }; -/** - * Create a JSON object from the transcript data - * @param {Object} obj - * @param {String} obj.times string with time information - * @param {String} obj.line string with transcript text - * @returns {Object} of the format; - * { - * begin: 0, - * end: 60, - * text: 'Transcript text sample', - * tag: NOTE || TIMED_CUE - * } - */ -function parseTimedTextLine(_ref, isSRT) { - var times = _ref.times, - line = _ref.line, - tag = _ref.tag; - var timestampRegex; - if (isSRT) { - // SRT allows using comma for milliseconds while WebVTT does not - timestampRegex = SRT_TIMESTAMP_REGEX; - } else { - timestampRegex = VTT_TIMESTAMP_REGEX; - } - switch (tag) { - case TRANSCRIPT_CUE_TYPES.note: - return { - begin: 0, - end: 0, - text: line, - tag: tag - }; - case TRANSCRIPT_CUE_TYPES.timedCue: - var _times$split = times.split(' --> '), - _times$split2 = _slicedToArray(_times$split, 2), - start = _times$split2[0], - end = _times$split2[1]; - // FIXME:: remove any styles for now, refine this - end = end.split(' ')[0]; - if (!start.match(timestampRegex) || !end.match(timestampRegex)) { - console.error('Invalid timestamp in line with text; ', line); - return null; + /** + * Save coordinates of touch start for comparison to touch end to prevent play/pause + * when user is scrolling. + * @param {Object} e onTouchStart event + */ + var touchX = null; + var touchY = null; + var saveTouchStartCoords = function saveTouchStartCoords(e) { + touchX = e.touches[0].clientX; + touchY = e.touches[0].clientY; + }; + + /** + * Get the segment, which encapsulates the current time of the playhead, + * from a list of media fragments in the current canvas. + * @param {Number} time playhead's current time + */ + var getActiveSegment = function getActiveSegment(time) { + if (isPlaylist) { + // For playlists timespans and canvasIdex are mapped one-to-one + return canvasSegments[cIndexRef.current]; + } else { + // Find the relevant media segment from the structure + var _iterator = _createForOfIteratorHelper$1(canvasSegments), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var segment = _step.value; + var id = segment.id, + isCanvas = segment.isCanvas, + _canvasIndex = segment.canvasIndex; + if (_canvasIndex == cIndexRef.current + 1) { + // Canvases without structure has the Canvas information + // in Canvas-level item as a navigable link + if (isCanvas) { + return segment; + } + var segmentRange = getMediaFragment(id, canvasDuration); + var isInRange = checkSrcRange(segmentRange, canvasDuration); + var isInSegment = time >= segmentRange.start && time < segmentRange.end; + if (isInSegment && isInRange) { + return segment; + } + } + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); } - return { - begin: timeToS(start), - end: timeToS(end), - text: line, - tag: tag - }; - default: return null; - } + } + }; + + /** + * Click event handler for previous/next buttons in inaccessible + * message display + * @param {Number} c updated Canvas index upon event trigger + */ + var handlePrevNextClick = function handlePrevNextClick(c) { + switchPlayer(c, true); + }; + + /** + * Keydown event handler for previou/next buttons in inaccessible + * message display. + * IMPORTANT: btnName param should be either 'nextBtn' or 'previousBtn' + * @param {Event} e keydown event + * @param {Number} c update Canvas index upon event trigger + * @param {String} btnName name of the pressed button + */ + var handlePrevNextKeydown = function handlePrevNextKeydown(e, c, btnName) { + if (e.which === 32 || e.which === 13) { + switchPlayer(c, true, btnName); + } + }; + return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", { + "data-vjs-player": true, + "data-canvasindex": cIndexRef.current + }, canvasIsEmptyRef.current && /*#__PURE__*/React.createElement("div", { + "data-testid": "inaccessible-message-display" + // These styles needs to be inline for the poster to display within the Video boundaries + , + style: { + position: !playerRef.current ? 'relative' : 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + fontSize: 'medium', + textAlign: 'center', + color: '#fff', + backgroundColor: 'black', + zIndex: 101, + aspectRatio: !playerRef.current ? '16/9' : '' + } + }, /*#__PURE__*/React.createElement("p", { + className: "ramp--media-player_inaccessible-message-content", + "data-testid": "inaccessible-message-content", + dangerouslySetInnerHTML: { + __html: placeholderText + } + }), /*#__PURE__*/React.createElement("div", { + className: "ramp--media-player_inaccessible-message-buttons" + }, canvasIndex >= 1 && /*#__PURE__*/React.createElement("button", { + "aria-label": "Go back to previous item", + onClick: function onClick() { + return handlePrevNextClick(canvasIndex - 1); + }, + onKeyDown: function onKeyDown(e) { + return handlePrevNextKeydown(e, canvasIndex - 1, 'previousBtn'); + }, + "data-testid": "inaccessible-previous-button" + }, /*#__PURE__*/React.createElement(SectionButtonIcon, { + flip: true + }), " Previous"), canvasIndex != lastCanvasIndex && /*#__PURE__*/React.createElement("button", { + "aria-label": "Go to next item", + onClick: function onClick() { + return handlePrevNextClick(canvasIndex + 1); + }, + onKeyDown: function onKeyDown(e) { + return handlePrevNextKeydown(e, canvasIndex + 1, 'nextBtn'); + }, + "data-testid": "inaccessible-next-button" + }, "Next ", /*#__PURE__*/React.createElement(SectionButtonIcon, null))), canvasIndex != lastCanvasIndex && /*#__PURE__*/React.createElement("p", { + "data-testid": "inaccessible-message-timer", + className: cx('ramp--media-player_inaccessible-message-timer', autoAdvanceRef.current ? '' : 'hidden') + }, "Next item in ".concat(messageTime, " second").concat(messageTime === 1 ? '' : 's'))), /*#__PURE__*/React.createElement("video", { + "data-testid": "videojs-".concat(isVideo ? 'video' : 'audio', "-element"), + "data-canvasindex": cIndexRef.current, + ref: videoJSRef, + className: cx('video-js vjs-big-play-centered vjs-theme-ramp vjs-disabled', IS_ANDROID ? 'is-mobile' : ''), + onTouchStart: saveTouchStartCoords, + onTouchEnd: mobilePlayToggle, + style: { + display: "".concat(canvasIsEmptyRef.current ? 'none' : '') + } + })), (hasStructure || isPlaylist) && /*#__PURE__*/React.createElement("div", { + className: "vjs-track-scrubber-container hidden", + ref: trackScrubberRef, + id: "track_scrubber" + }, /*#__PURE__*/React.createElement("p", { + className: "vjs-time track-currenttime", + role: "presentation" + }), /*#__PURE__*/React.createElement("span", { + type: "range", + "aria-label": "Track scrubber", + role: "slider", + tabIndex: 0, + className: "vjs-track-scrubber", + style: { + width: '100%' + } + }, !IS_TOUCH_ONLY && /*#__PURE__*/React.createElement("span", { + className: "tooltiptext", + ref: scrubberTooltipRef, + "aria-hidden": true, + role: "presentation" + })), /*#__PURE__*/React.createElement("p", { + className: "vjs-time track-duration", + role: "presentation" + }))); } +VideoJSPlayer.propTypes = { + enableFileDownload: PropTypes.bool, + enableTitleLink: PropTypes.bool, + isVideo: PropTypes.bool, + options: PropTypes.object, + placeholderText: PropTypes.string, + scrubberTooltipRef: PropTypes.object, + tracks: PropTypes.array, + trackScrubberRef: PropTypes.object, + videoJSLangMap: PropTypes.string, + withCredentials: PropTypes.bool +}; + +var Play = "Play"; +var Pause = "Pause"; +var Replay = "Replay"; +var Duration = "Duration"; +var LIVE = "LIVE"; +var Loaded = "Loaded"; +var Progress = "Progress"; +var Fullscreen = "Fullscreen"; +var Mute = "Mute"; +var Unmute = "Unmute"; +var Subtitles = "Subtitles"; +var Captions = "Captions"; +var Chapters = "Chapters"; +var Descriptions = "Descriptions"; +var Close = "Close"; +var Text = "Text"; +var White = "White"; +var Black = "Black"; +var Red = "Red"; +var Green = "Green"; +var Blue = "Blue"; +var Yellow = "Yellow"; +var Magenta = "Magenta"; +var Cyan = "Cyan"; +var Background = "Background"; +var Window = "Window"; +var Transparent = "Transparent"; +var Opaque = "Opaque"; +var None = "None"; +var Raised = "Raised"; +var Depressed = "Depressed"; +var Uniform = "Uniform"; +var Casual = "Casual"; +var Script = "Script"; +var Reset = "Reset"; +var Done = "Done"; +var Color = "Color"; +var Opacity = "Opacity"; +var en = { + "Audio Player": "Audio Player", + "Video Player": "Video Player", + Play: Play, + Pause: Pause, + Replay: Replay, + "Current Time": "Current Time", + Duration: Duration, + "Remaining Time": "Remaining Time", + "Stream Type": "Stream Type", + LIVE: LIVE, + "Seek to live, currently behind live": "Seek to live, currently behind live", + "Seek to live, currently playing live": "Seek to live, currently playing live", + Loaded: Loaded, + Progress: Progress, + "Progress Bar": "Progress Bar", + "progress bar timing: currentTime={1} duration={2}": "{1} of {2}", + Fullscreen: Fullscreen, + "Exit Fullscreen": "Exit Fullscreen", + Mute: Mute, + Unmute: Unmute, + "Playback Rate": "Playback Rate", + Subtitles: Subtitles, + "subtitles off": "subtitles off", + Captions: Captions, + "captions off": "captions off", + Chapters: Chapters, + Descriptions: Descriptions, + "descriptions off": "descriptions off", + "Audio Track": "Audio Track", + "Volume Level": "Volume Level", + "You aborted the media playback": "You aborted the media playback", + "A network error caused the media download to fail part-way.": "A network error caused the media download to fail part-way.", + "The media could not be loaded, either because the server or network failed or because the format is not supported.": "The media could not be loaded, either because the server or network failed or because the format is not supported.", + "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.", + "No compatible source was found for this media.": "No compatible source was found for this media.", + "The media is encrypted and we do not have the keys to decrypt it.": "The media is encrypted and we do not have the keys to decrypt it.", + "Play Video": "Play Video", + Close: Close, + "Close Modal Dialog": "Close Modal Dialog", + "Modal Window": "Modal Window", + "This is a modal window": "This is a modal window", + "This modal can be closed by pressing the Escape key or activating the close button.": "This modal can be closed by pressing the Escape key or activating the close button.", + ", opens captions settings dialog": ", opens captions settings dialog", + ", opens subtitles settings dialog": ", opens subtitles settings dialog", + ", opens descriptions settings dialog": ", opens descriptions settings dialog", + ", selected": ", selected", + "captions settings": "captions settings", + "subtitles settings": "subtitles settings", + "descriptions settings": "descriptions settings", + Text: Text, + White: White, + Black: Black, + Red: Red, + Green: Green, + Blue: Blue, + Yellow: Yellow, + Magenta: Magenta, + Cyan: Cyan, + Background: Background, + Window: Window, + Transparent: Transparent, + "Semi-Transparent": "Semi-Transparent", + Opaque: Opaque, + "Font Size": "Font Size", + "Text Edge Style": "Text Edge Style", + None: None, + Raised: Raised, + Depressed: Depressed, + Uniform: Uniform, + "Drop shadow": "Drop shadow", + "Font Family": "Font Family", + "Proportional Sans-Serif": "Proportional Sans-Serif", + "Monospace Sans-Serif": "Monospace Sans-Serif", + "Proportional Serif": "Proportional Serif", + "Monospace Serif": "Monospace Serif", + Casual: Casual, + Script: Script, + "Small Caps": "Small Caps", + Reset: Reset, + "restore all settings to the default values": "restore all settings to the default values", + Done: Done, + "Caption Settings Dialog": "Caption Settings Dialog", + "Beginning of dialog window. Escape will cancel and close the window.": "Beginning of dialog window. Escape will cancel and close the window.", + "End of dialog window.": "End of dialog window.", + "{1} is loading.": "{1} is loading.", + "Exit Picture-in-Picture": "Exit Picture-in-Picture", + "Picture-in-Picture": "Picture-in-Picture", + "No content": "No content", + Color: Color, + Opacity: Opacity, + "Text Background": "Text Background", + "Caption Area Background": "Caption Area Background", + "Playing in Picture-in-Picture": "Playing in Picture-in-Picture", + "Skip backward {1} seconds": "Skip backward {1} seconds", + "Skip forward {1} seconds": "Skip forward {1} seconds" +}; + +function ownKeys$3(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } +function _objectSpread$3(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$3(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$3(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } +var PLAYER_ID = "iiif-media-player"; /** - * Parse the content search response from the search service, and then use it to calculate - * number of search hits for each transcripts, and create a list of matched transcript - * lines for the search in the current transcript - * @param {Object} response JSON response from content search API - * @param {String} query search query from transcript search - * @param {Array} trancripts content of the displayed transcript with ids - * @param {String} selectedTranscript url of the selected transcript - * @returns a list of matched transcript lines for the current search + * Parse resource related information form the current canvas in manifest, + * and build an options object for Video.js using that information. + * @param {Object} props + * @param {Boolean} props.enableFileDownload + * @param {Boolean} props.enablePIP + * @param {Boolean} props.enablePlaybackRate + * @param {Boolean} props.enableTitleLink + * @param {Boolean} props.withCredentials + * @param {String} props.language */ -var parseContentSearchResponse = function parseContentSearchResponse(response, query, trancripts, selectedTranscript) { - var _response$items; - if (!response || response === undefined) return []; - var hitCounts = []; - var searchHits = []; - if (((_response$items = response.items) === null || _response$items === void 0 ? void 0 : _response$items.length) > 0) { - var items = response.items; - items.map(function (item) { - var anno = new Annotation(item); - // Exclude annotations without supplementing motivation - if (anno.getMotivation() != 'supplementing') return; - var target = anno.getTarget(); - var targetURI = getCanvasId(target); - var value = anno.getBody()[0].getProperty('value'); - var hitCount = getHitCountForCue(value, query, true); - searchHits.push({ - target: target, - targetURI: targetURI, - value: value, - hitCount: hitCount - }); - }); - } - // Group search responses by transcript - var allSearchHits = groupBy(searchHits, 'targetURI'); +var MediaPlayer = function MediaPlayer(_ref) { + var _ref$enableFileDownlo = _ref.enableFileDownload, + enableFileDownload = _ref$enableFileDownlo === void 0 ? false : _ref$enableFileDownlo, + _ref$enablePIP = _ref.enablePIP, + enablePIP = _ref$enablePIP === void 0 ? false : _ref$enablePIP, + _ref$enablePlaybackRa = _ref.enablePlaybackRate, + enablePlaybackRate = _ref$enablePlaybackRa === void 0 ? false : _ref$enablePlaybackRa, + _ref$enableTitleLink = _ref.enableTitleLink, + enableTitleLink = _ref$enableTitleLink === void 0 ? false : _ref$enableTitleLink, + _ref$withCredentials = _ref.withCredentials, + withCredentials = _ref$withCredentials === void 0 ? false : _ref$withCredentials, + _ref$language = _ref.language, + language = _ref$language === void 0 ? 'en' : _ref$language; + var manifestState = useManifestState(); + var playerState = usePlayerState(); + var _useErrorBoundary = useErrorBoundary(), + showBoundary = _useErrorBoundary.showBoundary; + var srcIndex = manifestState.srcIndex, + hasStructure = manifestState.hasStructure, + playlist = manifestState.playlist; + var isPlaylist = playlist.isPlaylist; + playerState.playerFocusElement; + var currentTime = playerState.currentTime; + var trackScrubberRef = useRef(); + var timeToolRef = useRef(); + var videoJSLangMap = useRef('{}'); + var _useMediaPlayer = useMediaPlayer(), + canvasIsEmpty = _useMediaPlayer.canvasIsEmpty, + canvasIndex = _useMediaPlayer.canvasIndex, + isMultiCanvased = _useMediaPlayer.isMultiCanvased, + lastCanvasIndex = _useMediaPlayer.lastCanvasIndex; + var _useSetupPlayer = useSetupPlayer({ + enableFileDownload: enableFileDownload, + withCredentials: withCredentials, + lastCanvasIndex: lastCanvasIndex + }), + isMultiSourced = _useSetupPlayer.isMultiSourced, + isVideo = _useSetupPlayer.isVideo, + playerConfig = _useSetupPlayer.playerConfig, + ready = _useSetupPlayer.ready, + renderingFiles = _useSetupPlayer.renderingFiles, + nextItemClicked = _useSetupPlayer.nextItemClicked, + switchPlayer = _useSetupPlayer.switchPlayer; + var error = playerConfig.error, + poster = playerConfig.poster, + sources = playerConfig.sources, + targets = playerConfig.targets, + tracks = playerConfig.tracks; - // Calculate search hit count for each transcript in the Canvas - for (var _i = 0, _Object$entries = Object.entries(allSearchHits); _i < _Object$entries.length; _i++) { - var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), - key = _Object$entries$_i[0], - value = _Object$entries$_i[1]; - hitCounts.push({ - transcriptURL: key, - numberOfHits: value.reduce(function (acc, a) { - return acc + a.hitCount; - }, 0) + // Using dynamic imports to enforce code-splitting in webpack + // https://webpack.js.org/api/module-methods/#dynamic-expressions-in-import + var loadVideoJSLanguageMap = useMemo(function () { + return /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee() { + var resources; + return regenerator.wrap(function _callee$(_context) { + while (1) switch (_context.prev = _context.next) { + case 0: + _context.prev = 0; + _context.next = 3; + return import("video.js/dist/lang/".concat(language, ".json")); + case 3: + resources = _context.sent; + videoJSLangMap.current = JSON.stringify(resources); + _context.next = 11; + break; + case 7: + _context.prev = 7; + _context.t0 = _context["catch"](0); + console.warn("".concat(language, " is not available, defaulting to English")); + videoJSLangMap.current = JSON.stringify(en); + case 11: + case "end": + return _context.stop(); + } + }, _callee, null, [[0, 7]]); + })); + }, [language]); + useEffect(function () { + try { + loadVideoJSLanguageMap(); + } catch (e) { + showBoundary(e); + } + }, []); + + // Default VideoJS options not updated with the Canvas data + var defaultOptions = useMemo(function () { + return { + autoplay: false, + id: PLAYER_ID, + playbackRates: enablePlaybackRate ? [0.5, 0.75, 1, 1.5, 2] : [], + experimentalSvgIcons: true, + controls: true, + fluid: true, + language: language, + // Setting inactivity timeout to zero in mobile and tablet devices translates to + // user is always active. And the control bar is not hidden when user is active. + // With this user can always use the controls when the media is playing. + inactivityTimeout: IS_MOBILE || IS_TOUCH_ONLY ? 0 : 2000, + // Enable native text track functionality in iPhones and iPads + html5: { + nativeTextTracks: IS_MOBILE && !IS_ANDROID + }, + // Make error display modal dismissable + errorDisplay: { + uncloseable: false + }, + /* + Setting this option helps to override VideoJS's default 'keydown' event handler, whenever + the focus is on a native VideoJS control icon (e.g. play toggle). + E.g. click event on 'playtoggle' sets the focus on the play/pause button, + which has VideoJS's 'handleKeydown' event handler attached to it. Therefore, as long as the + focus is on the play/pause button the 'keydown' event will pass through VideoJS's default + 'keydown' event handler, without ever reaching the 'keydown' handler setup on the document + in Ramp code. + When this option is setup VideoJS's 'handleKeydown' event handler passes the event to the + function setup under the 'hotkeys' option when the native player controls are focused. + In Safari, this works without using 'hotkeys' option, therefore only set this in other browsers. + */ + userActions: { + hotkeys: !IS_SAFARI ? function (e) { + playerHotKeys(e, this); + } : undefined + }, + videoJSTitleLink: enableTitleLink + }; + }, [language, enablePlaybackRate, enableTitleLink]); + + // Build VideoJS options for the current Canvas from defaultOptions + var videoJSOptions = useMemo(function () { + return !canvasIsEmpty ? _objectSpread$3(_objectSpread$3({}, defaultOptions), {}, { + aspectRatio: isVideo ? '16:9' : '1:0', + audioOnlyMode: !isVideo, + bigPlayButton: isVideo, + poster: isVideo ? poster : null, + controlBar: { + // Define and order control bar controls + // See https://docs.videojs.com/tutorial-components.html for options of what + // seem to be supported controls + children: [isMultiCanvased ? 'videoJSPreviousButton' : '', 'playToggle', isMultiCanvased ? 'videoJSNextButton' : '', 'videoJSProgress', 'videoJSCurrentTime', 'timeDivider', 'durationDisplay', + // These icons are in reverse order to support `float: inline-end` in CSS + 'fullscreenToggle', enableFileDownload ? 'videoJSFileDownload' : '', enablePIP ? 'pictureInPictureToggle' : '', enablePlaybackRate ? 'playbackRateMenuButton' : '', 'qualitySelector', hasStructure || isPlaylist ? 'videoJSTrackScrubber' : '', tracks.length > 0 && isVideo ? 'subsCapsButton' : '', IS_MOBILE ? 'muteToggle' : 'volumePanel' + // 'vjsYo', custom component + ], + + videoJSProgress: { + srcIndex: srcIndex, + targets: targets, + currentTime: currentTime !== null && currentTime !== void 0 ? currentTime : 0, + nextItemClicked: nextItemClicked + }, + videoJSCurrentTime: { + srcIndex: srcIndex, + targets: targets, + currentTime: currentTime || 0 + }, + videoJSFileDownload: enableFileDownload && { + title: 'Download Files', + controlText: 'Alternate resource download', + files: renderingFiles + }, + videoJSPreviousButton: isMultiCanvased && { + canvasIndex: canvasIndex, + switchPlayer: switchPlayer + }, + videoJSNextButton: isMultiCanvased && { + canvasIndex: canvasIndex, + lastCanvasIndex: lastCanvasIndex, + switchPlayer: switchPlayer + }, + videoJSTrackScrubber: (hasStructure || isPlaylist) && { + trackScrubberRef: trackScrubberRef, + timeToolRef: timeToolRef, + isPlaylist: isPlaylist + } + }, + sources: isMultiSourced ? [sources[srcIndex]] : sources + }) : _objectSpread$3(_objectSpread$3({}, defaultOptions), {}, { + sources: [] }); + }, [isVideo, playerConfig, srcIndex]); + if (ready && videoJSOptions != undefined || canvasIsEmpty) { + return /*#__PURE__*/React.createElement("div", { + "data-testid": "media-player", + className: "ramp--media_player", + role: "presentation" + }, /*#__PURE__*/React.createElement(VideoJSPlayer, { + enableFileDownload: enableFileDownload, + enableTitleLink: enableTitleLink, + isVideo: isVideo, + options: videoJSOptions, + placeholderText: error, + scrubberTooltipRef: timeToolRef, + tracks: tracks, + trackScrubberRef: trackScrubberRef, + videoJSLangMap: videoJSLangMap.current, + withCredentials: withCredentials + })); + } else { + return null; } - - // Get all the matching transcript lines with the query in the current transcript - var matchedTranscriptLines = getMatchedTranscriptLines(allSearchHits[selectedTranscript], query, trancripts); - return { - matchedTranscriptLines: matchedTranscriptLines, - hitCounts: hitCounts, - allSearchHits: allSearchHits - }; }; +MediaPlayer.propTypes = { + enableFileDownload: PropTypes.bool, + enablePIP: PropTypes.bool, + enablePlaybackRate: PropTypes.bool, + enableTitleLink: PropTypes.bool, + withCredentials: PropTypes.bool, + language: PropTypes.string +}; + +var _extends_1 = createCommonjsModule(function (module) { +function _extends() { + module.exports = _extends = Object.assign ? Object.assign.bind() : function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + return target; + }, module.exports.__esModule = true, module.exports["default"] = module.exports; + return _extends.apply(this, arguments); +} +module.exports = _extends, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); + +var _extends = /*@__PURE__*/getDefaultExportFromCjs(_extends_1); /** - * Create a list matched transcript lines for the current search for the displayed transcript - * @param {Array} searchHits a list of matched transcript lines with ids from the current transcript - * @param {String} query search query - * @param {Array} transcripts list of all the transcript lines from the current transcript - * @returns a list of matched transcrip lines in the current transcript + * Build leaf-level nodes in the structures in Manifest. These nodes can be + * either timespans (with media fragment) or titles (w/o media fragment). + * @param {Object} props + * @param {Number} props.duration duration of the item + * @param {String} props.id media fragemnt of the item + * @param {Boolean} props.isTitle flag to indicate item w/o mediafragment + * @param {Boolean} props.isCanvas flag to indicate item is at Canvas-level + * @param {Boolean} props.isClickable flag to indicate item is within resource duration + * @param {Boolean} props.isEmpty flag to indicate Canvas associated with item is inaccessible + * @param {String} props.label text label of the item + * @param {String} props.summary summary associated with the item (in playlist context) + * @param {String} props.homepage homepage associated with the item (in playlist context) + * @param {Array} props.items list of children for the item + * @param {Number} props.itemIndex index of the item within the section/canvas + * @param {String} props.rangeId unique id of the item + * @param {Number} props.canvasDuration duration of the Canvas associated with the item + * @param {Object} props.sectionRef React ref of the section element associated with the item + * @param {Object} props.structureContainerRef React ref of the structure container */ -var getMatchedTranscriptLines = function getMatchedTranscriptLines(searchHits, query, transcripts) { - var qStr = query.trim().toLocaleLowerCase(); - var transcriptLines = []; - if (searchHits === undefined) return; - var traversedIds = []; - searchHits.map(function (item, index) { - var target = item.target, - value = item.value; - // Read time offsets and text of the search hit - var timeRange = getMediaFragment(target); - - // Replace all HTML tags - var mappedText = value.replace(/<\/?[^>]+>/gi, ''); - var start = 0, - end = 0; - var transcriptId = undefined; - if (timeRange != undefined) { - // For timed-text - start = timeRange.start; - end = timeRange.end; - transcriptId = transcripts.findIndex(function (t) { - return t.begin == start && t.end == end; - }); - var queryText = qStr.match(/[a-zA-Z]+/gi) ? qStr.match(/[a-zA-Z]+/gi)[0] : qStr; - var matchOffset = mappedText.toLocaleLowerCase().indexOf(queryText); - if (matchOffset !== -1 && transcriptId != undefined) { - var match = markMatchedParts(value, qStr, item.hitCount, true); - transcriptLines.push({ - tag: TRANSCRIPT_CUE_TYPES.timedCue, - begin: start, - end: end, - id: transcriptId, - match: match, - matchCount: item.hitCount, - text: value - }); - } - } else { - /** - * For non timed text, there's no unique id to match the search response to the transcript - * lines in the UI. So use filter() method instead of findIndex() method to get all matching - * transcript lines in the display. - * Use traversedIds array to remember the ids of already processed transcript lines in the list - * to avoid duplication in the matches. - */ - var hitsInfo = matchPartsInUntimedText(transcripts, mappedText, qStr, traversedIds); - traversedIds = hitsInfo.traversedIds; - transcriptLines = [].concat(_toConsumableArray(transcriptLines), _toConsumableArray(hitsInfo.hits)); +var ListItem = function ListItem(_ref) { + var duration = _ref.duration, + id = _ref.id, + isTitle = _ref.isTitle, + isCanvas = _ref.isCanvas, + isClickable = _ref.isClickable, + isEmpty = _ref.isEmpty, + label = _ref.label, + summary = _ref.summary, + homepage = _ref.homepage, + items = _ref.items, + itemIndex = _ref.itemIndex, + rangeId = _ref.rangeId, + canvasDuration = _ref.canvasDuration, + sectionRef = _ref.sectionRef, + structureContainerRef = _ref.structureContainerRef; + var liRef = useRef(null); + var _useActiveStructure = useActiveStructure({ + itemId: id, + liRef: liRef, + sectionRef: sectionRef, + isCanvas: isCanvas, + canvasDuration: canvasDuration + }), + handleClick = _useActiveStructure.handleClick, + isActiveLi = _useActiveStructure.isActiveLi, + currentNavItem = _useActiveStructure.currentNavItem, + isPlaylist = _useActiveStructure.isPlaylist; + var subMenu = items && items.length > 0 ? /*#__PURE__*/React.createElement(List, { + items: items, + sectionRef: sectionRef, + structureContainerRef: structureContainerRef, + isPlaylist: isPlaylist + }) : null; - /** - * When backend has a single block of text which is chuncked in the UI this helps to - * traverse all transcript cues. - */ - while (index === searchHits.length - 1 && ((_traversedIds = traversedIds) === null || _traversedIds === void 0 ? void 0 : _traversedIds.length) < transcripts.length) { - var _traversedIds; - var _hitsInfo = matchPartsInUntimedText(transcripts, mappedText, qStr, traversedIds); - traversedIds = _hitsInfo.traversedIds; - transcriptLines = [].concat(_toConsumableArray(transcriptLines), _toConsumableArray(_hitsInfo.hits)); - } + /* + Auto-scroll active structure item into view only when user is not actively + interacting with structured navigation + */ + useEffect(function () { + if (liRef.current && (currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.id) == id && liRef.current.isClicked != undefined && !liRef.current.isClicked && structureContainerRef.current.isScrolling != undefined && !structureContainerRef.current.isScrolling) { + autoScroll(liRef.current, structureContainerRef); } - }); - return transcriptLines; + // Reset isClicked if active structure item is set + if (liRef.current) { + liRef.current.isClicked = false; + } + }, [currentNavItem]); + var renderListItem = function renderListItem() { + return /*#__PURE__*/React.createElement(Fragment, { + key: rangeId + }, /*#__PURE__*/React.createElement(Fragment, null, isTitle ? /*#__PURE__*/React.createElement("span", { + className: "ramp--structured-nav__item-title", + role: "listitem", + "aria-label": label + }, label) : /*#__PURE__*/React.createElement(Fragment, { + key: id + }, /*#__PURE__*/React.createElement("div", { + className: "tracker" + }), isClickable ? /*#__PURE__*/React.createElement(Fragment, null, isEmpty && /*#__PURE__*/React.createElement(LockedSVGIcon, null), /*#__PURE__*/React.createElement("a", { + role: "listitem", + href: homepage && homepage != '' ? homepage : id, + onClick: handleClick + }, "".concat(itemIndex, ". "), label, " ", duration.length > 0 ? " (".concat(duration, ")") : '')) : /*#__PURE__*/React.createElement("span", { + role: "listitem", + "aria-label": label + }, label)))); + }; + if (label != '') { + return /*#__PURE__*/React.createElement("li", { + "data-testid": "list-item", + ref: liRef, + className: cx('ramp--structured-nav__list-item', isActiveLi ? 'active' : ''), + "data-label": label, + "data-summary": summary + }, renderListItem(), subMenu); + } else { + return null; + } +}; +ListItem.propTypes = { + duration: PropTypes.string.isRequired, + id: PropTypes.string, + isTitle: PropTypes.bool.isRequired, + isCanvas: PropTypes.bool.isRequired, + isClickable: PropTypes.bool.isRequired, + isEmpty: PropTypes.bool.isRequired, + label: PropTypes.string.isRequired, + summary: PropTypes.string, + homepage: PropTypes.string, + items: PropTypes.array.isRequired, + itemIndex: PropTypes.number, + rangeId: PropTypes.string.isRequired, + canvasDuration: PropTypes.number.isRequired, + sectionRef: PropTypes.object.isRequired, + structureContainerRef: PropTypes.object.isRequired }; /** - * Build a list of matched indexed transcript lines from content search response. - * In Avalon, docx and plain text files are chunked by paragraphs seperated by 2 or - * more new line characters. So, depending on the way the file is formatted the search - * response could include chunks of the text or the full text. - * In the library (mammoth) used in Transcript component to display docx files; the text is chunked - * into paragraphs seperated by one or more new line characters. - * And the search response doesn't include any text styling in the docx files. Therefore the - * text with style information is reformatted to include text highlights from the search response. - * This function uses the search response to calculate the hit counts and mark them for each indexed transcript - * line in the front-end to get the correct counts. - * @param {Array} transcripts indexed transcript text in UI - * @param {String} mappedText matched text from content search - * @param {String} query search query entered by the user - * @param {Array} traversedIds already included transcript indices - * @returns a list of matched transcript lines + * Build Canvas level range items. When the range has child elements nested make it + * collapsible. + * @param {Object} props + * @param {Number} props.duration range duration + * @param {Boolean} props.hasChildren flag to indicate presence of child structure in range + * @param {String} props.itemId media fragment if associated with the range + * @param {Number} props.itemIndex index of the canvas in structures + * @param {Array} props.items list of children structure items in range + * @param {Boolean} props.isRoot flag to indicate root range on top of structures + * @param {String} props.label text label to be displayed + * @param {Object} props.sectionRef React ref of the section element associated with the item + * @param {Object} props.structureContainerRef React ref of the structure container */ -var matchPartsInUntimedText = function matchPartsInUntimedText(transcripts, mappedText, query, traversedIds) { - var escapedQ = buildRegexReadyText(query, true, false); - // Get hit counts for the current text, ignore matches with query preceded by - or ' - var qRegex = new RegExp(String.raw(_templateObject$1 || (_templateObject$1 = _taggedTemplateLiteral(["\b", "\b"], ["\\b", "\\b"])), escapedQ), 'gi'); - var matched = []; - // Start from the next cue after the last traveresed cue in the transcript - var lastTraversedId = traversedIds[traversedIds.length - 1] + 1 || 0; +var SectionHeading = function SectionHeading(_ref) { + var duration = _ref.duration, + _ref$hasChildren = _ref.hasChildren, + hasChildren = _ref$hasChildren === void 0 ? false : _ref$hasChildren, + itemId = _ref.itemId, + itemIndex = _ref.itemIndex, + items = _ref.items, + _ref$isRoot = _ref.isRoot, + isRoot = _ref$isRoot === void 0 ? false : _ref$isRoot, + label = _ref.label, + sectionRef = _ref.sectionRef, + structureContainerRef = _ref.structureContainerRef; + var _useState = useState(false), + _useState2 = _slicedToArray(_useState, 2), + isOpen = _useState2[0], + setIsOpen = _useState2[1]; + var toggleOpen = function toggleOpen(e) { + setIsOpen(!isOpen); + sectionRef.current.isOpen = true; + }; + var _useActiveStructure = useActiveStructure({ + itemIndex: itemIndex, + isRoot: isRoot, + itemId: itemId, + liRef: sectionRef, + sectionRef: sectionRef, + isCanvas: true, + canvasDuration: duration, + setIsOpen: setIsOpen + }), + isActiveSection = _useActiveStructure.isActiveSection, + canvasIndex = _useActiveStructure.canvasIndex, + handleClick = _useActiveStructure.handleClick, + isPlaylist = _useActiveStructure.isPlaylist; - /** - * For untimed text the search response text could be either, - * - mapped one to one with the cue text in Transcript component - * - include a part of the cue text in Transcript component - * When none of these work check if the cue text contains the search query - */ - for (var i = lastTraversedId; i < transcripts.length; i++) { - var t = transcripts[i]; - var cleanedText = t.text.replace(/<\/?[^>]+>/gi, '').trim(); - var matches = _toConsumableArray(cleanedText.matchAll(qRegex)); - var mappedTextCleaned = mappedText.trim(); - if (mappedTextCleaned == cleanedText || mappedTextCleaned.includes(cleanedText) && (matches === null || matches === void 0 ? void 0 : matches.length) > 0) { - t.matchCount = matches === null || matches === void 0 ? void 0 : matches.length; - matched.push(t); - traversedIds.push(t.id); - break; - } else if ((matches === null || matches === void 0 ? void 0 : matches.length) > 0) { - var _ref2; - t.matchCount = (_ref2 = _toConsumableArray(mappedTextCleaned.matchAll(qRegex))) === null || _ref2 === void 0 ? void 0 : _ref2.length; - matched.push(t); - traversedIds.push(t.id); - break; - } else { - traversedIds.push(t.id); + /* + Auto-scroll active section into view only when user is not + actively interacting with structured navigation + */ + useEffect(function () { + if (canvasIndex + 1 === itemIndex && sectionRef.current && sectionRef.current.isClicked != undefined && !sectionRef.current.isClicked && structureContainerRef.current.isScrolling != undefined && !structureContainerRef.current.isScrolling) { + autoScroll(sectionRef.current, structureContainerRef); } - } - var hits = []; - matched.map(function (m) { - var value = addStyledHighlights(m.textDisplayed, query); - var match = markMatchedParts(value, query, m.matchCount, true); - hits.push({ - tag: TRANSCRIPT_CUE_TYPES.nonTimedLine, - begin: undefined, - end: undefined, - id: m.id, - match: match, - matchCount: m.matchCount, - text: value - }); - }); - return { - hits: hits, - traversedIds: traversedIds + sectionRef.current.isClicked = false; + }, [canvasIndex]); + var collapsibleButton = function collapsibleButton() { + return /*#__PURE__*/React.createElement("button", { + className: "collapse-expand-button", + "data-testid": "section-collapse-icon", + onClick: toggleOpen + }, /*#__PURE__*/React.createElement("i", { + className: cx('arrow', isOpen ? 'up' : 'down') + })); }; + return /*#__PURE__*/React.createElement("div", { + className: cx('ramp--structured-nav__section', isActiveSection ? 'active' : ''), + role: "listitem", + "data-testid": "listitem-section", + ref: sectionRef, + "data-label": label, + "data-mediafrag": itemId !== null && itemId !== void 0 ? itemId : '' + }, /*#__PURE__*/React.createElement("div", { + className: "section-head-buttons" + }, /*#__PURE__*/React.createElement("button", { + "data-testid": itemId == undefined ? "listitem-section-span" : "listitem-section-button", + ref: sectionRef, + onClick: handleClick, + className: cx('ramp--structured-nav__section-title', !itemId && 'not-clickable') + }, /*#__PURE__*/React.createElement("span", { + className: "ramp--structured-nav__title", + "aria-label": label, + role: "listitem" + }, isRoot ? '' : "".concat(itemIndex, ". "), label, duration != '' && /*#__PURE__*/React.createElement("span", { + className: "ramp--structured-nav__section-duration" + }, duration))), hasChildren && collapsibleButton()), isOpen && hasChildren && /*#__PURE__*/React.createElement(List, { + items: items, + sectionRef: sectionRef, + key: itemId, + structureContainerRef: structureContainerRef, + isPlaylist: isPlaylist + })); +}; +SectionHeading.propTypes = { + itemIndex: PropTypes.number.isRequired, + canvasIndex: PropTypes.number, + duration: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + sectionRef: PropTypes.object.isRequired, + itemId: PropTypes.string, + isRoot: PropTypes.bool, + structureContainerRef: PropTypes.object.isRequired, + hasChildren: PropTypes.bool, + items: PropTypes.array }; /** - * Generic function to mark the matched transcript text in the cue where the output has - * surrounding the matched parts - * within the cue. - * @param {String} text matched transcript text/cue - * @param {String} query current search query - * @param {Numner} hitCount number of hits returned in the search response - * @param {Boolean} hasHighlight boolean flag to indicate text has tags - * @returns matched cue with HTML tags added for marking the hightlight + * Build a section of structure using a
      HTML element, this can represent + * a title Range with children or canvas Range with children. + * This element helps build the nested ranges in structures, as it is being + * called recuresively in ListItem and SectionHeading components. + * @param {Object} props + * @param {Array} props.items a list of structure items under a title/section + * @param {Object} props.sectionRef React ref of the current section/canvas + * @param {Object} props.structureContainerRef React ref of the parent container + * @param {Boolean} props.isPlaylist */ -var markMatchedParts = function markMatchedParts(text, query, hitCount) { - var hasHighlight = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; - if (text === undefined || !text) return; - var count = 0; - var replacerFn = function replacerFn(match) { - var cleanedMatch = match.replace(/<\/?[^>]+>/gi, ''); - // Only add highlights to search hits in the search response - if (count < hitCount) { - count++; - return "".concat(cleanedMatch, ""); +var List = function List(_ref) { + var items = _ref.items, + sectionRef = _ref.sectionRef, + structureContainerRef = _ref.structureContainerRef, + isPlaylist = _ref.isPlaylist; + var collapsibleContent = /*#__PURE__*/React.createElement("ul", { + "data-testid": "list", + className: "ramp--structured-nav__list", + role: "presentation" + }, items.map(function (item, index) { + // Render canvas items as SectionHeadings in non-playlist contexts + if (item.isCanvas && !isPlaylist) { + var _item$items; + return /*#__PURE__*/React.createElement(SectionHeading, { + key: "".concat(item.label, "-").concat(index), + itemIndex: index + 1, + duration: item.duration, + label: item.label, + sectionRef: sectionRef, + itemId: item.id, + isRoot: item.isRoot, + structureContainerRef: structureContainerRef, + hasChildren: ((_item$items = item.items) === null || _item$items === void 0 ? void 0 : _item$items.length) > 0, + items: item.items + }); } else { - return cleanedMatch; + return /*#__PURE__*/React.createElement(ListItem, _extends({}, item, { + sectionRef: sectionRef, + key: index, + structureContainerRef: structureContainerRef + })); + } + })); + return /*#__PURE__*/React.createElement(React.Fragment, null, collapsibleContent); +}; +List.propTypes = { + items: PropTypes.array.isRequired, + sectionRef: PropTypes.object.isRequired, + structureContainerRef: PropTypes.object.isRequired, + isPlaylist: PropTypes.bool.isRequired +}; + +function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } +function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } +function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } + +/** + * Parse structures property in the Manifest, and build UI as needed. + * For playlists: structures is displayed as a list of items. + * For all the other manifests: each Canvas Range is highlighted as a section in the + * display and their child elements are displayed in collapsible UI elements + * respectively. + */ +var StructuredNavigation = function StructuredNavigation() { + var _structureItemsRef$cu; + var manifestDispatch = useManifestDispatch(); + var playerDispatch = usePlayerDispatch(); + var _usePlayerState = usePlayerState(), + clickedUrl = _usePlayerState.clickedUrl, + isClicked = _usePlayerState.isClicked, + isPlaying = _usePlayerState.isPlaying, + player = _usePlayerState.player; + var _useManifestState = useManifestState(), + allCanvases = _useManifestState.allCanvases, + canvasDuration = _useManifestState.canvasDuration, + canvasIndex = _useManifestState.canvasIndex, + hasMultiItems = _useManifestState.hasMultiItems, + targets = _useManifestState.targets, + manifest = _useManifestState.manifest, + playlist = _useManifestState.playlist, + canvasIsEmpty = _useManifestState.canvasIsEmpty, + canvasSegments = _useManifestState.canvasSegments; + var _useErrorBoundary = useErrorBoundary(), + showBoundary = _useErrorBoundary.showBoundary; + var canvasStructRef = useRef(); + var structureItemsRef = useRef(); + var canvasIsEmptyRef = useRef(canvasIsEmpty); + var hasRootRangeRef = useRef(false); + var structureContainerRef = useRef(); + var scrollableStructure = useRef(); + useEffect(function () { + // Update currentTime and canvasIndex in state if a + // custom start time and(or) canvas is given in manifest + if (manifest) { + try { + var _getStructureRanges = getStructureRanges(manifest, allCanvases, playlist.isPlaylist), + structures = _getStructureRanges.structures, + timespans = _getStructureRanges.timespans, + markRoot = _getStructureRanges.markRoot; + structureItemsRef.current = structures; + canvasStructRef.current = structures; + hasRootRangeRef.current = markRoot; + // Remove root-level structure item from navigation calculations + if ((structures === null || structures === void 0 ? void 0 : structures.length) > 0 && structures[0].isRoot) { + canvasStructRef.current = structures[0].items; + } + manifestDispatch({ + structures: canvasStructRef.current, + type: 'setStructures' + }); + manifestDispatch({ + timespans: timespans, + type: 'setCanvasSegments' + }); + structureContainerRef.current.isScrolling = false; + } catch (error) { + showBoundary(error); + } } - }; - var queryFormatted = query; - /** - * Content search response for a phrase search like 'Mr. Bungle' gives the response - * with highlights in the matched text as Mr. Bungle. - * So reconstruct the search query in the UI to match this phrase in the response. - */ - if (hasHighlight) { - queryFormatted = buildRegexReadyText(query); - } + }, [manifest]); - /** - * Content search API returns cues including "Mr. Bungle" as matches for both search queries - * "mr bungle" and "mr. bungle". - * When "mr bungle" is searched this function handles highlighting since the regex fails to - * identify the matches in the cues. - */ - var altReplace = function altReplace() { - var matches = _toConsumableArray(text.matchAll(/<\/?[^>]+>/gi)); - if ((matches === null || matches === void 0 ? void 0 : matches.length) === 0) return; - var startIndex = 0; - var newStr = ''; - for (var j = 0; j < matches.length && count < hitCount;) { - // Set offset to count matches based on the # of words in the phrase search query - var splitQ = query.split(/[\s-,\?]/); - var offset = (splitQ === null || splitQ === void 0 ? void 0 : splitQ.length) > 0 ? (splitQ === null || splitQ === void 0 ? void 0 : splitQ.length) * 2 - 1 : 1; - if (matches[j] === undefined && matches[j + offset] === undefined) return; + // Set currentNavItem when current Canvas is an inaccessible/empty item + useEffect(function () { + if (canvasIsEmpty && playlist.isPlaylist) { + manifestDispatch({ + item: canvasSegments[canvasIndex], + type: 'switchItem' + }); + } + }, [canvasIsEmpty, canvasIndex]); + useEffect(function () { + if (isClicked) { + var clickedItem = canvasSegments.filter(function (c) { + return c.id === clickedUrl; + }); + if ((clickedItem === null || clickedItem === void 0 ? void 0 : clickedItem.length) > 0) { + // Only update the current nav item for timespans + // Eliminate Canvas level items unless the structure is empty + var _clickedItem$ = clickedItem[0], + isCanvas = _clickedItem$.isCanvas, + items = _clickedItem$.items; + if (!isCanvas || items.length == 0 && isCanvas) { + manifestDispatch({ + item: clickedItem[0], + type: 'switchItem' + }); + } + } + var currentCanvasIndex = allCanvases.findIndex(function (c) { + return c.canvasURL === getCanvasId(clickedUrl); + }); + var timeFragment = getMediaFragment(clickedUrl, canvasDuration); - // Indices of start and end of the highlighted text including tags - var firstIndex = matches[j].index; - var lastIndex = matches[j + offset].index + matches[j + offset][0].length; - var prefix = text.slice(startIndex, firstIndex); - var cleanedMatch = text.slice(firstIndex, lastIndex).replace(/<\/?[^>]+>/gi, ''); - newStr = "".concat(newStr).concat(prefix, "").concat(cleanedMatch, ""); - startIndex = lastIndex; - j = +(offset + 1); - count++; - if (j == matches.length) { - newStr = "".concat(newStr).concat(text.slice(startIndex)); + // Invalid time fragment + if (!timeFragment || timeFragment == undefined) { + console.error('StructuredNavigation -> invalid media fragment in structure item -> ', timeFragment); + return; + } + var timeFragmentStart = timeFragment.start; + if (hasMultiItems) { + var _getCanvasTarget = getCanvasTarget(targets, timeFragment, canvasDuration), + srcIndex = _getCanvasTarget.srcIndex, + fragmentStart = _getCanvasTarget.fragmentStart; + timeFragmentStart = fragmentStart; + manifestDispatch({ + srcIndex: srcIndex, + type: 'setSrcIndex' + }); + } else { + // When clicked structure item is not in the current canvas + if (canvasIndex != currentCanvasIndex && currentCanvasIndex > -1) { + manifestDispatch({ + canvasIndex: currentCanvasIndex, + type: 'switchCanvas' + }); + canvasIsEmptyRef.current = canvasStructRef.current[currentCanvasIndex].isEmpty; + } + } + if (player && !canvasIsEmptyRef.current) { + player.currentTime(timeFragmentStart); + playerDispatch({ + startTime: timeFragment.start, + endTime: timeFragment.end, + type: 'setTimeFragment' + }); + + // Use this value in iOS to set the initial progress + // in the custom progress bar + player.structStart = timeFragmentStart; + playerDispatch({ + currentTime: timeFragmentStart, + type: 'setCurrentTime' + }); + // Setting userActive to true shows timerail breifly, helps + // to visualize the structure in player while playing + if (isPlaying) player.userActive(true); + } else if (canvasIsEmptyRef.current) { + // Reset isClicked in state for + // inaccessible items (empty canvases) + playerDispatch({ + type: 'resetClick' + }); } } - return newStr; - }; - try { - var _ref3; - var queryRegex = new RegExp(String.raw(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["", ""])), queryFormatted), 'gi'); - if (((_ref3 = _toConsumableArray(text.matchAll(queryRegex))) === null || _ref3 === void 0 ? void 0 : _ref3.length) === 0) { - var highlighted = altReplace(); - return highlighted; - } else { - return text.replace(queryRegex, replacerFn); + }, [isClicked, player]); + + // Structured nav is populated by the time the player hook fires so we listen for + // that to run the check on whether the structured nav is scrollable. + useEffect(function () { + if (structureContainerRef.current) { + var elem = structureContainerRef.current; + var structureBorder = structureContainerRef.current.parentElement; + var structureEnd = Math.abs(elem.scrollHeight - (elem.scrollTop + elem.clientHeight)) <= 1; + scrollableStructure.current = !structureEnd; + if (structureBorder) { + resizeObserver.observe(structureBorder); + } } - } catch (e) { - console.log('Error building RegExp for query: ', query); - } -}; + }, [player]); -/** - * For docx files the content search response text doesn't have the formatted - * styles in the Word document (e.g. bold text wrapped in tags). So, - * use the styled text formatted with mammoth in the UI to add highlights from - * the content search response. - * @param {String} text string to be formatted - * @param {String} query string to find and replace with tags - * @returns a string formatted with highlights - */ -var addStyledHighlights = function addStyledHighlights(text, query) { - if (text === undefined || !text) return; - var replacerFn = function replacerFn(match) { - var cleanedMatch = buildRegexReadyText(match, false, true); - return cleanedMatch; + // Update scrolling indicators when end of scrolling has been reached + var handleScrollable = function handleScrollable(e) { + var elem = e.target; + if (elem.classList.contains('ramp--structured-nav__border')) { + elem = elem.firstChild; + } + var scrollMsg = elem.nextSibling; + var structureEnd = Math.abs(elem.scrollHeight - (elem.scrollTop + elem.clientHeight)) <= 1; + if (elem && structureEnd && elem.classList.contains('scrollable')) { + elem.classList.remove('scrollable'); + } else if (elem && !structureEnd && !elem.classList.contains('scrollable')) { + elem.classList.add('scrollable'); + } + if (scrollMsg && structureEnd && scrollMsg.classList.contains('scrollable')) { + scrollMsg.classList.remove('scrollable'); + } else if (scrollMsg && !structureEnd && !scrollMsg.classList.contains('scrollable')) { + scrollMsg.classList.add('scrollable'); + } }; - // Regex to get matches in the text while ignoring matches with query preceded by - or ' - var queryregex = new RegExp(String.raw(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["\b", "\b"], ["\\b", "\\b"])), buildRegexReadyText(query, true, false)), 'gi'); - var styled = text.replace(queryregex, replacerFn); - return styled; -}; - -/** - * Format a given string by escaping punctuations characters and grouping - * punctuations and text, to make it feasible to be used to build a regular - * expression accurately. - * @param {String} text string to be formatted with hightlights - * @param {Boolean} regExpReady flag to indicate the usage of the output as a regular exp - * @param {Boolean} addHightlight flag to indicate to/not to add tags - * @returns string with tags - */ -var buildRegexReadyText = function buildRegexReadyText(text) { - var regExpReady = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; - var addHightlight = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; - // Text matches in the string - var matches = _toConsumableArray(text.matchAll(/[a-zA-Z']+/gi)); - // Punctuation matches in the string - var punctuationMatches = _toConsumableArray(text.matchAll(/([.+?"^${}\-|[\]\\])/g)); - - /** - * If no punctuations are found within the text return text with highlights - * For RegExp ready strings: ignore matches followed by - or ' - * e.g. omit matches as "Bungle's" when search query is "bungle" - */ - if ((punctuationMatches === null || punctuationMatches === void 0 ? void 0 : punctuationMatches.length) === 0) { - var textFormatted = addHightlight ? text.split(' ').map(function (t) { - return "".concat(t, ""); - }).join(' ') : text; - var textRegex = regExpReady ? "".concat(textFormatted, "(?!['w*])") : textFormatted; - return textRegex; - } - var highlighted = ''; - var startIndex = 0; - var i = 0; - while (i < matches.length) { - var match = matches[i]; - var textMatch = addHightlight ? "".concat(match[0], "") : match[0]; - /** - * When build RegExp ready string with punctuation blocks in the given string; - * - use * quantifier for blocks either at the start/end of the string to match zero or more times - * - use + quantifier for blocks in the middle of the string to match one or more times - * This pattern is build according the response from the content search API results. - */ - var punctMatch = startIndex === 0 ? "(".concat(text.slice(startIndex, match.index), ")*") : "(".concat(text.slice(startIndex, match.index), ")+"); - highlighted = regExpReady ? "".concat(highlighted).concat(punctMatch, "(").concat(textMatch, ")") : "".concat(highlighted).concat(text.slice(startIndex, match.index)).concat(textMatch); - startIndex = match.index + match[0].length; - if (i === (matches === null || matches === void 0 ? void 0 : matches.length) - 1) { - highlighted = regExpReady ? "".concat(highlighted, "(").concat(text.slice(startIndex), ")*") : "".concat(highlighted).concat(text.slice(startIndex)); + // Update scrolling indicators when structured nav is resized + var resizeObserver = new ResizeObserver(function (entries) { + var _iterator = _createForOfIteratorHelper(entries), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var entry = _step.value; + handleScrollable(entry); + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); } - i++; + }); + if (!manifest) { + return /*#__PURE__*/React.createElement("p", null, "No manifest - Please provide a valid manifest."); } - // Escape punctuation characters in string for RegExp ready strings - var escapePunctuation = function escapePunctuation(str) { - var punctuationRegex = /([.?^${}|[\]\\])/g; - return str.replace(punctuationRegex, '\\$1'); + /** + * Update isScrolling flag within structure container ref, which is + * used by ListItem and SectionHeading components to decide to/not to + * auto scroll the content + * @param {Boolean} state + */ + var handleMouseOver = function handleMouseOver(state) { + structureContainerRef.current.isScrolling = state; }; - return regExpReady ? escapePunctuation(highlighted) : highlighted; + return /*#__PURE__*/React.createElement("div", { + className: "ramp--structured-nav" + }, /*#__PURE__*/React.createElement("div", { + className: "ramp--structured-nav__border" + }, /*#__PURE__*/React.createElement("div", { + "data-testid": "structured-nav", + className: cx('ramp--structured-nav__content', scrollableStructure.current && 'scrollable', (playlist === null || playlist === void 0 ? void 0 : playlist.isPlaylist) && 'playlist-items', hasRootRangeRef.current && 'ramp--structured-nav__content-with_root'), + ref: structureContainerRef, + role: "list", + "aria-label": "Structural content", + onScroll: handleScrollable, + onMouseLeave: function onMouseLeave() { + return handleMouseOver(false); + }, + onMouseOver: function onMouseOver() { + return handleMouseOver(true); + } + }, ((_structureItemsRef$cu = structureItemsRef.current) === null || _structureItemsRef$cu === void 0 ? void 0 : _structureItemsRef$cu.length) > 0 ? structureItemsRef.current.map(function (item, index) { + var _item$items; + return ( + /* For playlist views omit the accordion style display of + structure for canvas-level items */ + item.isCanvas && !playlist.isPlaylist ? /*#__PURE__*/React.createElement(SectionHeading, { + key: "".concat(item.label, "-").concat(index), + itemIndex: index + 1, + duration: item.duration, + label: item.label, + sectionRef: /*#__PURE__*/createRef(), + itemId: item.id, + isRoot: item.isRoot, + structureContainerRef: structureContainerRef, + hasChildren: ((_item$items = item.items) === null || _item$items === void 0 ? void 0 : _item$items.length) > 0, + items: item.items + }) : /*#__PURE__*/React.createElement(List, { + items: [item], + sectionRef: /*#__PURE__*/createRef(), + key: "".concat(item.label, "-").concat(index), + structureContainerRef: structureContainerRef, + isPlaylist: playlist.isPlaylist + }) + ); + }) : /*#__PURE__*/React.createElement("p", { + className: "ramp--no-structure" + }, "There are no structures in the manifest")), /*#__PURE__*/React.createElement("span", { + className: cx(scrollableStructure.current && 'scrollable') + }, "Scroll to see more"))); }; +StructuredNavigation.propTypes = {}; -/** - * Calculate hit counts for each matched transcript cue - * @param {String} text matched transcript cue text - * @param {String} query search query from UI - * @param {Boolean} hasHighlight flag indicating has tags or not - * @returns - */ -var getHitCountForCue = function getHitCountForCue(text, query) { - var _ref4; - var hasHighlight = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; - /* - Content search API highlights each word in the given phrase in the response. - Threfore, use first word in the query seperated by a white space to get the hit - counts for each cue. - Use regex with any punctuation followed by a white space to split the query. - e.g. query: Mr. bungle => search response: Mr. Bungle - */ - var partialQ = query.split(/[\s.,!?;:]/)[0]; - var cleanedPartialQ = partialQ.replace(/[\[\]\-]/gi, ''); - var hitTerm = hasHighlight ? buildRegexReadyText(partialQ) : cleanedPartialQ; - var highlightedTerm = new RegExp(String.raw(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral(["", ""])), hitTerm), 'gi'); - var hitCount = (_ref4 = _toConsumableArray(text.matchAll(highlightedTerm))) === null || _ref4 === void 0 ? void 0 : _ref4.length; - return hitCount; -}; +var objectWithoutPropertiesLoose = createCommonjsModule(function (module) { +function _objectWithoutPropertiesLoose(source, excluded) { + if (source == null) return {}; + var target = {}; + var sourceKeys = Object.keys(source); + var key, i; + for (i = 0; i < sourceKeys.length; i++) { + key = sourceKeys[i]; + if (excluded.indexOf(key) >= 0) continue; + target[key] = source[key]; + } + return target; +} +module.exports = _objectWithoutPropertiesLoose, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); -// TODO:: Could be used for marking search hits in Word Doc transcripts? -var splitIntoElements = function splitIntoElements(htmlContent) { - // Create a temporary DOM element to parse the HTML - var tempDiv = document.createElement('div'); - tempDiv.innerHTML = htmlContent; +var objectWithoutProperties = createCommonjsModule(function (module) { +function _objectWithoutProperties(source, excluded) { + if (source == null) return {}; + var target = objectWithoutPropertiesLoose(source, excluded); + var key, i; + if (Object.getOwnPropertySymbols) { + var sourceSymbolKeys = Object.getOwnPropertySymbols(source); + for (i = 0; i < sourceSymbolKeys.length; i++) { + key = sourceSymbolKeys[i]; + if (excluded.indexOf(key) >= 0) continue; + if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; + target[key] = source[key]; + } + } + return target; +} +module.exports = _objectWithoutProperties, module.exports.__esModule = true, module.exports["default"] = module.exports; +}); - // Convert child nodes into an array - var elements = buildNonTimedText(Array.from(tempDiv.childNodes), true); - return elements; -}; +var _objectWithoutProperties = /*@__PURE__*/getDefaultExportFromCjs(objectWithoutProperties); /** - * Build non-timed transcript text content chunks into a JSON array - * with relevant information for display. These are then used by - * search module to convert the transcript content into an index. - * @param {Array} cues a list of trascript cues - * @param {Boolean} isHTML flag to detect inlined HTML in cues - * @returns a list of JSON objects for each cue + * Build the file download button for the displayed transcript file + * in the transcript viewer. + * @param {Object} props + * @param {String} fileUrl downloadable link to the file in server + * @param {String} fileName + * @param {Boolean} machineGenerated set to true for machine generated files + * @param {String} fileExt extension of the file */ -var buildNonTimedText = function buildNonTimedText(cues) { - var isHTML = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - var indexedCues = []; - cues.map(function (c) { - indexedCues.push({ - text: isHTML ? c.innerText : c, - tag: TRANSCRIPT_CUE_TYPES.nonTimedLine, - textDisplayed: isHTML ? lib.decode(c.innerHTML) : c - }); - }); - return indexedCues; -}; - var TranscriptDownloader = function TranscriptDownloader(_ref) { var fileUrl = _ref.fileUrl, fileName = _ref.fileName, @@ -10358,6 +11106,14 @@ TranscriptDownloader.propTypes = { fileExt: PropTypes.string }; +/** + * Build seletor and downloader for transcripts in the current Canvas + * @param {Object} props + * @param {Function} props.selectTranscript callback func to update transcript selection + * @param {Array} props.transcriptData list of the information for each transcirpt in the Canvas + * @param {Object} props.transcriptInfo information of the selected transcript + * @param {Boolean} props.noTranscript flag to indicate unsupported transcript selection + */ var TranscriptSelector = function TranscriptSelector(_ref) { var selectTranscript = _ref.selectTranscript, transcriptData = _ref.transcriptData, @@ -10414,8 +11170,17 @@ TranscriptSelector.propTypes = { }).isRequired, noTranscript: PropTypes.bool.isRequired }; -var TranscriptSelector$1 = /*#__PURE__*/React.memo(TranscriptSelector); +var TranscriptSelector$1 = /*#__PURE__*/memo(TranscriptSelector); +/** + * Build search within UI in the transcript search and handle user queries + * @param {Object} props + * @param {Object} props.searchResults result set from the current search + * @param {String} props.searchQuery search query entered by the user + * @param {Number} props.focusedMatchIndex index of the focused the search hit + * @param {Function} props.setFocusedMatchIndex callback func to update focused match in search hits + * @param {Function} props.setSearchQuery callback func to set search query + */ var TranscriptSearch = function TranscriptSearch(_ref) { var searchResults = _ref.searchResults, _ref$searchQuery = _ref.searchQuery, @@ -10520,6 +11285,21 @@ var _excluded$1 = ["showSearch", "setAutoScrollEnabled", "autoScrollEnabled", "s function ownKeys$2(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread$2(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$2(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$2(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } var MACHINE_GEN_MESSAGE = 'Machine-generated transcript may contain errors.'; + +/** + * Build menu for the displaying transcript search, search navigation, + * and transcript selector + * @param {Object} props + * @param {Boolean} props.showSearch show/hide search UI + * @param {Function} props.setAutoScrollEnabled callback func to change auto-scroll preference + * @param {Boolean} props.autoScrollEnabled flag to indicate auto-scroll transcript check + * @param {String} props.searchQuery user entered search query + * @param {Function} props.setSearchQuery callback func to update search query + * @param {Object} props.searchResults result set from the current search + * @param {Number} props.focusedMatchIndex index of the focused search hit + * @param {Function} props.setFocusedMatchIndex callback func to update focused search hit with + * search navigation + */ var TranscriptMenu = function TranscriptMenu(_ref) { var showSearch = _ref.showSearch, setAutoScrollEnabled = _ref.setAutoScrollEnabled, @@ -11007,11 +11787,6 @@ var useFocusedMatch = function useFocusedMatch(_ref6) { var _excluded = ["initialSearchQuery"]; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } -var NO_TRANSCRIPTS_MSG = 'No valid Transcript(s) found, please check again.'; -var INVALID_URL_MSG = 'Invalid URL for transcript, please check again.'; -var INVALID_VTT = 'Invalid WebVTT file, please check again.'; -var INVALID_TIMESTAMP = 'Invalid timestamp format in cue(s), please check again.'; -var NO_SUPPORT = 'Transcript format is not supported, please check again.'; var buildSpeakerText = function buildSpeakerText(item) { var isDocx = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; var text = isDocx ? item.textDisplayed : item.text; @@ -11024,7 +11799,7 @@ var buildSpeakerText = function buildSpeakerText(item) { return text; } }; -var TranscriptLine = /*#__PURE__*/React.memo(function (_ref) { +var TranscriptLine = /*#__PURE__*/memo(function (_ref) { var item = _ref.item, goToItem = _ref.goToItem, isActive = _ref.isActive, @@ -11035,17 +11810,17 @@ var TranscriptLine = /*#__PURE__*/React.memo(function (_ref) { transcriptContainerRef = _ref.transcriptContainerRef, isNonTimedText = _ref.isNonTimedText, focusedMatchIndex = _ref.focusedMatchIndex; - var itemRef = React.useRef(null); + var itemRef = useRef(null); var isFocused = item.id === focusedMatchId; - var wasFocusedRef = React.useRef(isFocused); - var wasActiveRef = React.useRef(isActive); + var wasFocusedRef = useRef(isFocused); + var wasActiveRef = useRef(isActive); // React ref to store previous focusedMatchIndex - var prevFocusedIndexRef = React.useRef(-1); + var prevFocusedIndexRef = useRef(-1); // React ref to store previous focusedMatchId - var prevFocusedIdRef = React.useRef(-1); + var prevFocusedIdRef = useRef(-1); // React ref to iterate through multiple hits within a focused cue - var activeRelativeCountRef = React.useRef(0); - React.useEffect(function () { + var activeRelativeCountRef = useRef(0); + useEffect(function () { var doScroll = false; var prevFocused = prevFocusedIdRef.current; if (isActive && !wasActiveRef.current) { @@ -11079,7 +11854,7 @@ var TranscriptLine = /*#__PURE__*/React.memo(function (_ref) { * Add a border highlight to the current focused search hit when using search * result navigation, when there are multiple hits within a focused cue */ - React.useEffect(function () { + useEffect(function () { if (itemRef.current && isFocused) { // Find all highlights within the focused cue var highlights = itemRef.current.querySelectorAll('.ramp--transcript_highlight'); @@ -11163,7 +11938,7 @@ var TranscriptLine = /*#__PURE__*/React.memo(function (_ref) { return null; } }); -var TranscriptList = /*#__PURE__*/React.memo(function (_ref2) { +var TranscriptList = /*#__PURE__*/memo(function (_ref2) { var seekPlayer = _ref2.seekPlayer, currentTime = _ref2.currentTime, searchResults = _ref2.searchResults, @@ -11174,11 +11949,11 @@ var TranscriptList = /*#__PURE__*/React.memo(function (_ref2) { showNotes = _ref2.showNotes, transcriptContainerRef = _ref2.transcriptContainerRef, focusedMatchIndex = _ref2.focusedMatchIndex; - var _React$useState = React.useState(null), - _React$useState2 = _slicedToArray(_React$useState, 2), - manuallyActivatedItemId = _React$useState2[0], - setManuallyActivatedItem = _React$useState2[1]; - var goToItem = React.useCallback(function (item) { + var _useState = useState(null), + _useState2 = _slicedToArray(_useState, 2), + manuallyActivatedItemId = _useState2[0], + setManuallyActivatedItem = _useState2[1]; + var goToItem = useCallback(function (item) { if (typeof item.begin === 'number') { seekPlayer(item.begin); setManuallyActivatedItem(null); @@ -11231,11 +12006,14 @@ var TranscriptList = /*#__PURE__*/React.memo(function (_ref2) { }); /** - * - * @param {String} param0 ID of the HTML element for the player on page - * @param {String} param1 manifest URL to read transcripts from - * @param {Object} param2 transcripts resource - * @returns + * Parse and display transcript content for the current Canvas. + * @param {Object} props + * @param {String} props.playerID + * @param {String} props.manifestUrl + * @param {Boolean} props.showNotes + * @param {Object} props.showNotes + * @param {Object} props.search + * @param {Array} props.transcripts */ var Transcript = function Transcript(_ref3) { var playerID = _ref3.playerID, @@ -11246,361 +12024,79 @@ var Transcript = function Transcript(_ref3) { search = _ref3$search === void 0 ? {} : _ref3$search, _ref3$transcripts = _ref3.transcripts, transcripts = _ref3$transcripts === void 0 ? [] : _ref3$transcripts; - var _React$useState3 = React.useState([]), - _React$useState4 = _slicedToArray(_React$useState3, 2), - transcriptsList = _React$useState4[0], - setTranscriptsList = _React$useState4[1]; - var _React$useState5 = React.useState([]), - _React$useState6 = _slicedToArray(_React$useState5, 2), - canvasTranscripts = _React$useState6[0], - setCanvasTranscripts = _React$useState6[1]; - var _React$useState7 = React.useState([]), - _React$useState8 = _slicedToArray(_React$useState7, 2), - transcript = _React$useState8[0], - setTranscript = _React$useState8[1]; - var _React$useState9 = React.useState({ - title: null, - filename: null, - id: null, - tUrl: null, - tType: null, - tFileExt: null, - isMachineGen: false, - tError: null - }), - _React$useState10 = _slicedToArray(_React$useState9, 2), - transcriptInfo = _React$useState10[0], - setTranscriptInfo = _React$useState10[1]; - var _React$useState11 = React.useState(), - _React$useState12 = _slicedToArray(_React$useState11, 2), - selectedTranscript = _React$useState12[0], - setSelectedTranscript = _React$useState12[1]; - var _React$useState13 = React.useState(true), - _React$useState14 = _slicedToArray(_React$useState13, 2), - isLoading = _React$useState14[0], - setIsLoading = _React$useState14[1]; - // Store transcript data in state to avoid re-requesting file contents - var _React$useState15 = React.useState([]), - _React$useState16 = _slicedToArray(_React$useState15, 2), - cachedTranscripts = _React$useState16[0], - setCachedTranscripts = _React$useState16[1]; - - /* - Enable search only for timed text as it is only working for these transcripts - TODO:: remove 'isSearchable' if/when search is supported for other formats - */ - var _useSearchOpts = useSearchOpts(_objectSpread(_objectSpread({}, search), {}, { - isSearchable: transcriptInfo.tType === TRANSCRIPT_TYPES.timedText || transcriptInfo.tType === TRANSCRIPT_TYPES.docx || transcriptInfo.tType === TRANSCRIPT_TYPES.plainText, - showMarkers: transcriptInfo.tType === TRANSCRIPT_TYPES.timedText - })), - initialSearchQuery = _useSearchOpts.initialSearchQuery, - searchOpts = _objectWithoutProperties(_useSearchOpts, _excluded); - var _React$useState17 = React.useState(initialSearchQuery), - _React$useState18 = _slicedToArray(_React$useState17, 2), - searchQuery = _React$useState18[0], - setSearchQuery = _React$useState18[1]; - var _React$useState19 = React.useState(-1), - _React$useState20 = _slicedToArray(_React$useState19, 2), - _canvasIndex = _React$useState20[0], - _setCanvasIndex = _React$useState20[1]; - var canvasIndexRef = React.useRef(_canvasIndex); - var setCanvasIndex = function setCanvasIndex(c) { - abortController.abort(); - canvasIndexRef.current = c; - _setCanvasIndex(c); // force re-render - }; - - var searchResults = useFilteredTranscripts(_objectSpread(_objectSpread({}, searchOpts), {}, { - query: searchQuery, - transcripts: transcript, - canvasIndex: canvasIndexRef.current, - selectedTranscript: selectedTranscript - })); - var _useFocusedMatch = useFocusedMatch({ - searchResults: searchResults - }), - focusedMatchId = _useFocusedMatch.focusedMatchId, - setFocusedMatchId = _useFocusedMatch.setFocusedMatchId, - focusedMatchIndex = _useFocusedMatch.focusedMatchIndex, - setFocusedMatchIndex = _useFocusedMatch.setFocusedMatchIndex; - var tanscriptHitCounts = useSearchCounts({ - searchResults: searchResults, - canvasTranscripts: canvasTranscripts, - searchQuery: searchQuery - }); - var _React$useState21 = React.useState(true), - _React$useState22 = _slicedToArray(_React$useState21, 2), - isEmpty = _React$useState22[0], - setIsEmpty = _React$useState22[1]; - var _React$useState23 = React.useState(true), - _React$useState24 = _slicedToArray(_React$useState23, 2), - _autoScrollEnabled = _React$useState24[0], - _setAutoScrollEnabled = _React$useState24[1]; - var autoScrollEnabledRef = React.useRef(_autoScrollEnabled); - var setAutoScrollEnabled = function setAutoScrollEnabled(a) { - autoScrollEnabledRef.current = a; - _setAutoScrollEnabled(a); // force re-render - }; - - var abortController = new AbortController(); - var playerIntervalRef = React.useRef(null); - var playerRef = React.useRef(null); - var transcriptContainerRef = React.useRef(); - var _React$useState25 = React.useState(-1), - _React$useState26 = _slicedToArray(_React$useState25, 2), - currentTime = _React$useState26[0], - _setCurrentTime = _React$useState26[1]; - var setCurrentTime = React.useMemo(function () { + var _useState3 = useState(-1), + _useState4 = _slicedToArray(_useState3, 2), + currentTime = _useState4[0], + _setCurrentTime = _useState4[1]; + var setCurrentTime = useMemo(function () { return throttle_1(_setCurrentTime, 50); - }, []); - var seekPlayer = React.useCallback(function (time) { - setCurrentTime(time); // so selecting an item works in tests - if (playerRef.current) playerRef.current.currentTime = time; - }, []); - - /** - * Start an interval at the start of the component to poll the - * canvasindex attribute changes in the player on the page - */ - React.useEffect(function () { - playerIntervalRef.current = setInterval(function () { - var domPlayer = document.getElementById(playerID); - if (!domPlayer) { - console.warn("Cannot find player, ".concat(playerID, " on page. Transcript synchronization is disabled")); - // Inaccessible canvas => stop loading spinner - setIsLoading(false); - } else { - if (domPlayer.children[0]) playerRef.current = domPlayer.children[0];else playerRef.current = domPlayer; - } - if (playerRef.current) { - var cIndex = parseInt(playerRef.current.dataset['canvasindex']); - if (Number.isNaN(cIndex)) cIndex = 0; - if (cIndex !== canvasIndexRef.current) { - // Clear the transcript text in the component - setTranscript([]); - setCanvasIndex(cIndex); - setCurrentTime(playerRef.current.currentTime); - playerRef.current.addEventListener('timeupdate', function () { - setCurrentTime(playerRef.current.currentTime); - }); - } - } - }, 500); - }, []); - React.useEffect(function () { - // Clean up state when the component unmounts - return function () { - clearInterval(playerIntervalRef.current); - }; - }, []); - - /** - * If a list of transcripts is given in the props, then sanitize them - * to match the expected format in the component. - * If not fallback to reading transcripts from a given manifest URL. - * @param {Array} transcripts list of transcripts from props - */ - var loadTranscripts = /*#__PURE__*/function () { - var _ref4 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee(transcripts) { - var allTranscripts; - return regenerator.wrap(function _callee$(_context) { - while (1) switch (_context.prev = _context.next) { - case 0: - if (!((transcripts === null || transcripts === void 0 ? void 0 : transcripts.length) > 0 - // transcripts prop is processed first if given - )) { - _context.next = 6; - break; - } - _context.next = 3; - return sanitizeTranscripts(transcripts); - case 3: - _context.t0 = _context.sent; - _context.next = 9; - break; - case 6: - _context.next = 8; - return readSupplementingAnnotations(manifestUrl); - case 8: - _context.t0 = _context.sent; - case 9: - allTranscripts = _context.t0; - setTranscriptsList(allTranscripts !== null && allTranscripts !== void 0 ? allTranscripts : []); - initTranscriptData(allTranscripts !== null && allTranscripts !== void 0 ? allTranscripts : []); - case 12: - case "end": - return _context.stop(); - } - }, _callee); - })); - return function loadTranscripts(_x) { - return _ref4.apply(this, arguments); - }; - }(); - React.useEffect(function () { - if ((transcripts === null || transcripts === void 0 ? void 0 : transcripts.length) === 0 && !manifestUrl) { - // When both required props are invalid - setIsLoading(false); - setTranscript([]); - setTranscriptInfo({ - tType: TRANSCRIPT_TYPES.noTranscript, - id: '', - tError: NO_TRANSCRIPTS_MSG - }); - } else { - loadTranscripts(transcripts); - } - }, [canvasIndexRef.current]); // helps to load initial transcript with async req + }, []); - React.useEffect(function () { - if ((transcriptsList === null || transcriptsList === void 0 ? void 0 : transcriptsList.length) > 0 && canvasIndexRef.current != undefined) { - var cTranscripts = transcriptsList.filter(function (tr) { - return tr.canvasId == canvasIndexRef.current; - })[0]; - setCanvasTranscripts(cTranscripts.items); - setStateVar(cTranscripts.items[0]); - } - }, [canvasIndexRef.current]); - var initTranscriptData = function initTranscriptData(allTranscripts) { - var _getCanvasT, _getTItems; - // When canvasIndex updates -> return - if (abortController.signal.aborted) return; - var getCanvasT = function getCanvasT(tr) { - return tr.filter(function (t) { - return t.canvasId == _canvasIndex; - }); - }; - var getTItems = function getTItems(tr) { - return getCanvasT(tr)[0].items; - }; - /** - * When transcripts prop is empty - * OR the respective canvas doesn't have transcript data - * OR canvas' transcript items list is empty - */ - if (!(allTranscripts !== null && allTranscripts !== void 0 && allTranscripts.length) > 0 || !((_getCanvasT = getCanvasT(allTranscripts)) !== null && _getCanvasT !== void 0 && _getCanvasT.length) > 0 || !((_getTItems = getTItems(allTranscripts)) !== null && _getTItems !== void 0 && _getTItems.length) > 0) { - setIsEmpty(true); - setTranscript([]); - setStateVar(undefined); - } else { - setIsEmpty(false); - var cTranscripts = getCanvasT(allTranscripts)[0]; - setCanvasTranscripts(cTranscripts.items); - setStateVar(cTranscripts.items[0]); - } + // Read and parse transcript(s) as state changes + var _useTranscripts = useTranscripts({ + manifestUrl: manifestUrl, + playerID: playerID, + setCurrentTime: setCurrentTime, + transcripts: transcripts + }), + canvasIndexRef = _useTranscripts.canvasIndexRef, + canvasTranscripts = _useTranscripts.canvasTranscripts, + isEmpty = _useTranscripts.isEmpty, + isLoading = _useTranscripts.isLoading, + NO_SUPPORT_MSG = _useTranscripts.NO_SUPPORT_MSG, + playerRef = _useTranscripts.playerRef, + selectedTranscript = _useTranscripts.selectedTranscript, + selectTranscript = _useTranscripts.selectTranscript, + transcript = _useTranscripts.transcript, + transcriptInfo = _useTranscripts.transcriptInfo; + + /* + Enable search only for timed text as it is only working for these transcripts + TODO:: remove 'isSearchable' if/when search is supported for other formats + */ + var _useSearchOpts = useSearchOpts(_objectSpread(_objectSpread({}, search), {}, { + isSearchable: transcriptInfo.tType === TRANSCRIPT_TYPES.timedText || transcriptInfo.tType === TRANSCRIPT_TYPES.docx || transcriptInfo.tType === TRANSCRIPT_TYPES.plainText, + showMarkers: transcriptInfo.tType === TRANSCRIPT_TYPES.timedText + })), + initialSearchQuery = _useSearchOpts.initialSearchQuery, + searchOpts = _objectWithoutProperties(_useSearchOpts, _excluded); + var _useState5 = useState(initialSearchQuery), + _useState6 = _slicedToArray(_useState5, 2), + searchQuery = _useState6[0], + setSearchQuery = _useState6[1]; + var searchResults = useFilteredTranscripts(_objectSpread(_objectSpread({}, searchOpts), {}, { + query: searchQuery, + transcripts: transcript, + canvasIndex: canvasIndexRef.current, + selectedTranscript: selectedTranscript + })); + var _useFocusedMatch = useFocusedMatch({ + searchResults: searchResults + }), + focusedMatchId = _useFocusedMatch.focusedMatchId, + setFocusedMatchId = _useFocusedMatch.setFocusedMatchId, + focusedMatchIndex = _useFocusedMatch.focusedMatchIndex, + setFocusedMatchIndex = _useFocusedMatch.setFocusedMatchIndex; + var tanscriptHitCounts = useSearchCounts({ + searchResults: searchResults, + canvasTranscripts: canvasTranscripts, + searchQuery: searchQuery + }); + var _useState7 = useState(true), + _useState8 = _slicedToArray(_useState7, 2), + _autoScrollEnabled = _useState8[0], + _setAutoScrollEnabled = _useState8[1]; + var autoScrollEnabledRef = useRef(_autoScrollEnabled); + var setAutoScrollEnabled = function setAutoScrollEnabled(a) { + autoScrollEnabledRef.current = a; + _setAutoScrollEnabled(a); // force re-render }; - var selectTranscript = React.useCallback(function (selectedId) { - var selectedTranscript = canvasTranscripts.filter(function (tr) { - return tr.id === selectedId; - }); - setStateVar(selectedTranscript[0]); - }, [canvasTranscripts]); - var setStateVar = /*#__PURE__*/function () { - var _ref5 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee2(transcript) { - var _transcript, id, title, filename, url, isMachineGen, format, cached, _cached$, tData, tFileExt, tType, tError; - return regenerator.wrap(function _callee2$(_context2) { - while (1) switch (_context2.prev = _context2.next) { - case 0: - if (!(!transcript || transcript == undefined)) { - _context2.next = 5; - break; - } - setIsEmpty(true); - setIsLoading(false); - setTranscriptInfo({ - tType: TRANSCRIPT_TYPES.noTranscript, - id: '', - tError: NO_TRANSCRIPTS_MSG - }); - return _context2.abrupt("return"); - case 5: - // set isEmpty flag to render transcripts UI - setIsEmpty(false); - _transcript = transcript, id = _transcript.id, title = _transcript.title, filename = _transcript.filename, url = _transcript.url, isMachineGen = _transcript.isMachineGen, format = _transcript.format; // Check cached transcript data - cached = cachedTranscripts.filter(function (ct) { - return ct.id == id && ct.canvasId == canvasIndexRef.current; - }); - if (!((cached === null || cached === void 0 ? void 0 : cached.length) > 0)) { - _context2.next = 15; - break; - } - // Load cached transcript data into the component - _cached$ = cached[0], tData = _cached$.tData, tFileExt = _cached$.tFileExt, tType = _cached$.tType, tError = _cached$.tError; - setTranscript(tData); - setTranscriptInfo({ - title: title, - filename: filename, - id: id, - isMachineGen: isMachineGen, - tType: tType, - tUrl: url, - tFileExt: tFileExt, - tError: tError - }); - setSelectedTranscript(url); - _context2.next = 17; - break; - case 15: - _context2.next = 17; - return Promise.resolve(parseTranscriptData(url, canvasIndexRef.current, format)).then(function (value) { - if (value != null) { - var _tData = value.tData, - tUrl = value.tUrl, - _tType = value.tType, - _tFileExt = value.tFileExt; - var newError = ''; - switch (_tType) { - case TRANSCRIPT_TYPES.invalid: - newError = INVALID_URL_MSG; - break; - case TRANSCRIPT_TYPES.noTranscript: - newError = NO_TRANSCRIPTS_MSG; - break; - case TRANSCRIPT_TYPES.noSupport: - newError = NO_SUPPORT; - break; - case TRANSCRIPT_TYPES.invalidVTT: - newError = INVALID_VTT; - break; - case TRANSCRIPT_TYPES.invalidTimestamp: - newError = INVALID_TIMESTAMP; - break; - } - setTranscript(_tData); - setTranscriptInfo({ - title: title, - filename: filename, - id: id, - isMachineGen: isMachineGen, - tType: _tType, - tUrl: tUrl, - tFileExt: _tFileExt, - tError: newError - }); - setSelectedTranscript(tUrl); - transcript = _objectSpread(_objectSpread({}, transcript), {}, { - tType: _tType, - tData: _tData, - tFileExt: _tFileExt, - canvasId: canvasIndexRef.current, - tError: newError - }); - // Cache the transcript info - setCachedTranscripts([].concat(_toConsumableArray(cachedTranscripts), [transcript])); - } - }); - case 17: - setIsLoading(false); - case 18: - case "end": - return _context2.stop(); - } - }, _callee2); - })); - return function setStateVar(_x2) { - return _ref5.apply(this, arguments); - }; - }(); + + var transcriptContainerRef = useRef(); + var seekPlayer = useCallback(function (time) { + setCurrentTime(time); // so selecting an item works in tests + if (playerRef.current) playerRef.current.currentTime(time); + }, []); if (!isLoading) { var _transcriptInfo$tErro; return /*#__PURE__*/React.createElement("div", { @@ -11612,7 +12108,7 @@ var Transcript = function Transcript(_ref3) { selectTranscript: selectTranscript, transcriptData: tanscriptHitCounts, transcriptInfo: transcriptInfo, - noTranscript: ((_transcriptInfo$tErro = transcriptInfo.tError) === null || _transcriptInfo$tErro === void 0 ? void 0 : _transcriptInfo$tErro.length) > 0 && transcriptInfo.tError != NO_SUPPORT, + noTranscript: ((_transcriptInfo$tErro = transcriptInfo.tError) === null || _transcriptInfo$tErro === void 0 ? void 0 : _transcriptInfo$tErro.length) > 0 && transcriptInfo.tError != NO_SUPPORT_MSG, setAutoScrollEnabled: setAutoScrollEnabled, setFocusedMatchIndex: setFocusedMatchIndex, focusedMatchIndex: focusedMatchIndex, @@ -11621,7 +12117,7 @@ var Transcript = function Transcript(_ref3) { searchQuery: searchQuery, setSearchQuery: setSearchQuery }), /*#__PURE__*/React.createElement("div", { - className: "transcript_content ".concat(transcript ? '' : 'static'), + className: cx('transcript_content', transcript ? '' : 'static'), "data-testid": "transcript_content_".concat(transcriptInfo.tType), role: "list", "aria-label": "Attached Transcript content", @@ -11669,11 +12165,16 @@ Transcript.propTypes = { }; /** - * @param {Boolean} param0 display only Canvas metadata when set to true with other props are default - * @param {Boolean} param1 display both Manifest and Canvas metadata when set to true - * @param {Boolean} param2 hide the title in the metadata when set to false, defaults to true - * @param {Boolean} param3 hide the heading UI component when set to false, defaults to true - * @returns + * Parse and display metadata, rights, and requiredStatement information + * related to the current resource. The display of the scope of this information + * can be customized using props as needed. + * @param {Object} props + * @param {Boolean} props.displayOnlyCanvasMetadata + * @param {Boolean} props.displayAllMetadata + * @param {Boolean} props.displayTitle + * @param {Boolean} props.showHeading + * @param {String} props.itemHeading + * @param {String} props.sectionHeaading */ var MetadataDisplay = function MetadataDisplay(_ref) { var _ref$displayOnlyCanva = _ref.displayOnlyCanvasMetadata, @@ -11691,38 +12192,42 @@ var MetadataDisplay = function MetadataDisplay(_ref) { var _useManifestState = useManifestState(), manifest = _useManifestState.manifest, canvasIndex = _useManifestState.canvasIndex; - var _React$useState = React.useState(), - _React$useState2 = _slicedToArray(_React$useState, 2), - manifestMetadata = _React$useState2[0], - setManifestMetadata = _React$useState2[1]; + var _useState = useState(), + _useState2 = _slicedToArray(_useState, 2), + manifestMetadata = _useState2[0], + setManifestMetadata = _useState2[1]; // Metadata for all Canavases in state - var _React$useState3 = React.useState(), - _React$useState4 = _slicedToArray(_React$useState3, 2); - _React$useState4[0]; - var _setCanvasesMetadata = _React$useState4[1]; + var _useState3 = useState(), + _useState4 = _slicedToArray(_useState3, 2); + _useState4[0]; + var _setCanvasesMetadata = _useState4[1]; // Current Canvas metadata in state - var _React$useState5 = React.useState(), - _React$useState6 = _slicedToArray(_React$useState5, 2), - canvasMetadata = _React$useState6[0], - setCanvasMetadata = _React$useState6[1]; + var _useState5 = useState(), + _useState6 = _slicedToArray(_useState5, 2), + canvasMetadata = _useState6[0], + setCanvasMetadata = _useState6[1]; // Boolean flags set according to user props to hide/show metadata - var _React$useState7 = React.useState(), - _React$useState8 = _slicedToArray(_React$useState7, 2), - showManifestMetadata = _React$useState8[0], - setShowManifestMetadata = _React$useState8[1]; - var _React$useState9 = React.useState(), - _React$useState10 = _slicedToArray(_React$useState9, 2), - showCanvasMetadata = _React$useState10[0], - setShowCanvasMetadata = _React$useState10[1]; - var _React$useState11 = React.useState(), - _React$useState12 = _slicedToArray(_React$useState11, 2), - manifestRights = _React$useState12[0], - setManifestRights = _React$useState12[1]; - var _React$useState13 = React.useState(), - _React$useState14 = _slicedToArray(_React$useState13, 2), - canvasRights = _React$useState14[0], - setCanvasRights = _React$useState14[1]; - var canvasesMetadataRef = React.useRef(); + var _useState7 = useState(), + _useState8 = _slicedToArray(_useState7, 2), + showManifestMetadata = _useState8[0], + setShowManifestMetadata = _useState8[1]; + var _useState9 = useState(), + _useState10 = _slicedToArray(_useState9, 2), + showCanvasMetadata = _useState10[0], + setShowCanvasMetadata = _useState10[1]; + var _useState11 = useState(), + _useState12 = _slicedToArray(_useState11, 2), + manifestRights = _useState12[0], + setManifestRights = _useState12[1]; + var _useState13 = useState(), + _useState14 = _slicedToArray(_useState13, 2), + canvasRights = _useState14[0], + setCanvasRights = _useState14[1]; + var _useState15 = useState(false), + _useState16 = _slicedToArray(_useState15, 2), + hasMetadata = _useState16[0], + setHasMetadata = _useState16[1]; + var canvasesMetadataRef = useRef(); var setCanvasesMetadata = function setCanvasesMetadata(m) { _setCanvasesMetadata(m); canvasesMetadataRef.current = m; @@ -11732,7 +12237,7 @@ var MetadataDisplay = function MetadataDisplay(_ref) { * and/or Canvases based on the input props and set the initial set(s) of * metadata in the component's state */ - React.useEffect(function () { + useEffect(function () { if (manifest) { var _parsedMetadata$right; // Display Canvas metadata only when specified in the props @@ -11750,6 +12255,7 @@ var MetadataDisplay = function MetadataDisplay(_ref) { setCanvasMetadataInState(); } if (showManifest) { + var _manifestMeta; var manifestMeta = parsedMetadata.manifestMetadata; if (!displayTitle) { manifestMeta = manifestMeta.filter(function (md) { @@ -11757,6 +12263,7 @@ var MetadataDisplay = function MetadataDisplay(_ref) { }); } setManifestMetadata(manifestMeta); + setHasMetadata(((_manifestMeta = manifestMeta) === null || _manifestMeta === void 0 ? void 0 : _manifestMeta.length) > 0); } if (((_parsedMetadata$right = parsedMetadata.rights) === null || _parsedMetadata$right === void 0 ? void 0 : _parsedMetadata$right.length) > 0) { setManifestRights(parsedMetadata.rights); @@ -11769,7 +12276,7 @@ var MetadataDisplay = function MetadataDisplay(_ref) { * in the component's state listening to the canvasIndex changes in the central * state */ - React.useEffect(function () { + useEffect(function () { if (canvasIndex >= 0 && showCanvasMetadata) { setCanvasMetadataInState(); } @@ -11783,6 +12290,7 @@ var MetadataDisplay = function MetadataDisplay(_ref) { return m.canvasindex === canvasIndex; })[0]; if (canvasData != undefined) { + var _metadata; var metadata = canvasData.metadata, rights = canvasData.rights; if (!displayTitle && metadata != undefined) { @@ -11791,23 +12299,17 @@ var MetadataDisplay = function MetadataDisplay(_ref) { }); } setCanvasMetadata(metadata); + setHasMetadata(((_metadata = metadata) === null || _metadata === void 0 ? void 0 : _metadata.length) > 0); if (rights != undefined && (rights === null || rights === void 0 ? void 0 : rights.length) > 0) { setCanvasRights(rights); } } }; - /** - * Distinguish whether there is any metadata to be displayed - * @returns {Boolean} - */ - var hasMetadata = function hasMetadata() { - return (canvasMetadata === null || canvasMetadata === void 0 ? void 0 : canvasMetadata.length) > 0 || (manifestMetadata === null || manifestMetadata === void 0 ? void 0 : manifestMetadata.length) > 0; - }; var buildMetadata = function buildMetadata(metadata) { var metadataPairs = []; if ((metadata === null || metadata === void 0 ? void 0 : metadata.length) > 0) { metadata.map(function (md, index) { - metadataPairs.push( /*#__PURE__*/React.createElement(React.Fragment, { + metadataPairs.push( /*#__PURE__*/React.createElement(Fragment, { key: index }, /*#__PURE__*/React.createElement("dt", null, md.label), /*#__PURE__*/React.createElement("dd", { dangerouslySetInnerHTML: { @@ -11818,21 +12320,31 @@ var MetadataDisplay = function MetadataDisplay(_ref) { } return /*#__PURE__*/React.createElement("dl", null, metadataPairs); }; + var manifestMetadataBlock = useMemo(function () { + if (showManifestMetadata && (manifestMetadata === null || manifestMetadata === void 0 ? void 0 : manifestMetadata.length) > 0) { + return /*#__PURE__*/React.createElement(React.Fragment, null, displayAllMetadata && /*#__PURE__*/React.createElement("span", null, itemHeading), buildMetadata(manifestMetadata), (manifestRights === null || manifestRights === void 0 ? void 0 : manifestRights.length) > 0 && /*#__PURE__*/React.createElement("span", { + className: "ramp--metadata-rights-heading", + "data-testid": "manifest-rights" + }, "Rights"), buildMetadata(manifestRights)); + } + }, [manifestMetadata]); + var canvasMetadataBlock = useMemo(function () { + if (showCanvasMetadata && (canvasMetadata === null || canvasMetadata === void 0 ? void 0 : canvasMetadata.length) > 0) { + return /*#__PURE__*/React.createElement(React.Fragment, null, displayAllMetadata && /*#__PURE__*/React.createElement("span", null, sectionHeaading), buildMetadata(canvasMetadata), (canvasRights === null || canvasRights === void 0 ? void 0 : canvasRights.length) > 0 && /*#__PURE__*/React.createElement("span", { + className: "ramp--metadata-rights-heading", + "data-testid": "canvas-rights" + }, "Rights"), buildMetadata(canvasRights)); + } + }, [canvasMetadata]); return /*#__PURE__*/React.createElement("div", { "data-testid": "metadata-display", className: "ramp--metadata-display" }, showHeading && /*#__PURE__*/React.createElement("div", { className: "ramp--metadata-display-title", "data-testid": "metadata-display-title" - }, /*#__PURE__*/React.createElement("h4", null, "Details")), hasMetadata() && /*#__PURE__*/React.createElement("div", { + }, /*#__PURE__*/React.createElement("h4", null, "Details")), hasMetadata ? /*#__PURE__*/React.createElement("div", { className: "ramp--metadata-display-content" - }, showManifestMetadata && (manifestMetadata === null || manifestMetadata === void 0 ? void 0 : manifestMetadata.length) > 0 && /*#__PURE__*/React.createElement(React.Fragment, null, displayAllMetadata && /*#__PURE__*/React.createElement("span", null, itemHeading), buildMetadata(manifestMetadata), (manifestRights === null || manifestRights === void 0 ? void 0 : manifestRights.length) > 0 && /*#__PURE__*/React.createElement("span", { - className: "ramp--metadata-rights-heading", - "data-testid": "manifest-rights" - }, "Rights"), buildMetadata(manifestRights)), showCanvasMetadata && (canvasMetadata === null || canvasMetadata === void 0 ? void 0 : canvasMetadata.length) > 0 && /*#__PURE__*/React.createElement(React.Fragment, null, displayAllMetadata && /*#__PURE__*/React.createElement("span", null, sectionHeaading), buildMetadata(canvasMetadata), (canvasRights === null || canvasRights === void 0 ? void 0 : canvasRights.length) > 0 && /*#__PURE__*/React.createElement("span", { - className: "ramp--metadata-rights-heading", - "data-testid": "canvas-rights" - }, "Rights"), buildMetadata(canvasRights))), !hasMetadata() && /*#__PURE__*/React.createElement("div", { + }, manifestMetadataBlock, canvasMetadataBlock) : /*#__PURE__*/React.createElement("div", { "data-testid": "metadata-display-message", className: "ramp--metadata-display-message" }, /*#__PURE__*/React.createElement("p", null, "No valid Metadata is in the Manifest/Canvas(es)"))); @@ -11846,6 +12358,14 @@ MetadataDisplay.propTypes = { sectionHeaading: PropTypes.string }; +/** + * Display supplemental files as downloadable links, referenced in both + * manifest and at each canvas as rendering files. + * @param {Object} props + * @param {String} props.itemHeading + * @param {String} props.sectionHeaading + * @param {Boolean} props.showHeading + */ var SupplementalFiles = function SupplementalFiles(_ref) { var _ref$itemHeading = _ref.itemHeading, itemHeading = _ref$itemHeading === void 0 ? "Item files" : _ref$itemHeading, @@ -11901,17 +12421,11 @@ var SupplementalFiles = function SupplementalFiles(_ref) { event.preventDefault(); fileDownload(file.id, file.filename, file.fileExt, file.isMachineGen); }; - return useMemo(function () { - return /*#__PURE__*/React.createElement("div", { - "data-testid": "supplemental-files", - className: "ramp--supplemental-files" - }, showHeading && /*#__PURE__*/React.createElement("div", { - className: "ramp--supplemental-files-heading", - "data-testid": "supplemental-files-heading" - }, /*#__PURE__*/React.createElement("h4", null, "Files")), hasFiles && /*#__PURE__*/React.createElement("div", { + var filesDisplay = useMemo(function () { + return /*#__PURE__*/React.createElement(React.Fragment, null, hasFiles && /*#__PURE__*/React.createElement("div", { className: "ramp--supplemental-files-display-content", "data-testid": "supplemental-files-display-content" - }, Array.isArray(manifestSupplementalFiles) && manifestSupplementalFiles.length > 0 && /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement("h4", null, itemHeading), /*#__PURE__*/React.createElement("dl", { + }, Array.isArray(manifestSupplementalFiles) && manifestSupplementalFiles.length > 0 && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("h4", null, itemHeading), /*#__PURE__*/React.createElement("dl", { key: "item-files" }, manifestSupplementalFiles.map(function (file, index) { return /*#__PURE__*/React.createElement(Fragment, { @@ -11925,7 +12439,7 @@ var SupplementalFiles = function SupplementalFiles(_ref) { return handleDownload(e, file); } }, file.label))); - }))), Array.isArray(canvasSupplementalFiles) && hasSectionFiles && /*#__PURE__*/React.createElement(Fragment, null, /*#__PURE__*/React.createElement("h4", null, sectionHeading), canvasSupplementalFiles.map(function (canvasFiles, idx) { + }))), Array.isArray(canvasSupplementalFiles) && hasSectionFiles && /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("h4", null, sectionHeading), canvasSupplementalFiles.map(function (canvasFiles, idx) { var files = canvasFiles.files; return files.length > 0 && /*#__PURE__*/React.createElement("dl", { key: "section-".concat(idx, "-label") @@ -11947,8 +12461,22 @@ var SupplementalFiles = function SupplementalFiles(_ref) { className: "ramp--supplemental-files-empty" }, /*#__PURE__*/React.createElement("p", null, "No Supplemental file(s) in Manifest"))); }, [hasFiles, hasSectionFiles]); + return /*#__PURE__*/React.createElement("div", { + "data-testid": "supplemental-files", + className: "ramp--supplemental-files" + }, showHeading && /*#__PURE__*/React.createElement("div", { + className: "ramp--supplemental-files-heading", + "data-testid": "supplemental-files-heading" + }, /*#__PURE__*/React.createElement("h4", null, "Files")), filesDisplay); }; +/** + * A toggle button to enable/disable auto-play across multiple + * canvases + * @param {Object} props + * @param {String} props.label + * @param {Boolean} props.showLabel + */ var AutoAdvanceToggle = function AutoAdvanceToggle(_ref) { var _ref$label = _ref.label, label = _ref$label === void 0 ? "Autoplay" : _ref$label, @@ -11963,97 +12491,74 @@ var AutoAdvanceToggle = function AutoAdvanceToggle(_ref) { type: "setAutoAdvance" }); }; - return React.useMemo(function () { - return /*#__PURE__*/React.createElement("div", { - "data-testid": "auto-advance", - className: "ramp--auto-advance" - }, showLabel && /*#__PURE__*/React.createElement("span", { - className: "ramp--auto-advance-label", - "data-testid": "auto-advance-label", - htmlFor: "auto-advance-toggle", - id: "auto-advance-toggle-label" - }, label), /*#__PURE__*/React.createElement("label", { - className: "ramp--auto-advance-toggle", - "aria-labelledby": "auto-advance-toggle-label" - }, /*#__PURE__*/React.createElement("input", { + var toggleButton = useMemo(function () { + return /*#__PURE__*/React.createElement("input", { "data-testid": "auto-advance-toggle", name: "auto-advance-toggle", type: "checkbox", checked: autoAdvance, "aria-label": label, onChange: handleChange - }), /*#__PURE__*/React.createElement("span", { - className: "slider round" - }))); + }); }, [autoAdvance]); + return /*#__PURE__*/React.createElement("div", { + "data-testid": "auto-advance", + className: "ramp--auto-advance" + }, showLabel && /*#__PURE__*/React.createElement("span", { + className: "ramp--auto-advance-label", + "data-testid": "auto-advance-label", + htmlFor: "auto-advance-toggle", + id: "auto-advance-toggle-label" + }, label), /*#__PURE__*/React.createElement("label", { + className: "ramp--auto-advance-toggle", + "aria-labelledby": "auto-advance-toggle-label" + }, toggleButton, /*#__PURE__*/React.createElement("span", { + className: "slider round" + }))); }; AutoAdvanceToggle.propTypes = { label: PropTypes.string, showLabel: PropTypes.bool }; -var useMarkers = function useMarkers() { - var manifestState = useContext(ManifestStateContext); - var isEditing = manifestState.playlist.isEditing; - var isDisabled = useMemo(function () { - return isEditing; - }, [isEditing]); - return { - isDisabled: isDisabled - }; -}; -var usePlayer = function usePlayer() { - var playerState = useContext(PlayerStateContext); - var player; - if (playerState) { - player = playerState.player; - } - var playerRef = useRef(); - playerRef.current = useMemo(function () { - return player; - }, [player]); - var getCurrentTime = useCallback(function () { - if (playerRef.current) { - return playerRef.current.currentTime(); - } else { - return 0; - } - }, [playerRef.current]); - return { - getCurrentTime: getCurrentTime, - player: playerRef.current - }; -}; - +/** + * Build and handle creation of new markers for playlists. This component is rendered + * on page when the user has permissions to create new markers in a given playlist Manifest. + * @param {Object} props + * @param {String} props.newMarkerEndpoint annotationService to POST create markers request + * @param {Number} props.canvasId URI of the current Canvas + * @param {Function} props.handleCreate callback function to update global state + * @param {String} props.csrfToken token to authenticate POST request + */ var CreateMarker = function CreateMarker(_ref) { var newMarkerEndpoint = _ref.newMarkerEndpoint, canvasId = _ref.canvasId, handleCreate = _ref.handleCreate, csrfToken = _ref.csrfToken; - var _React$useState = React.useState(false), - _React$useState2 = _slicedToArray(_React$useState, 2), - isOpen = _React$useState2[0], - setIsOpen = _React$useState2[1]; - var _React$useState3 = React.useState(false), - _React$useState4 = _slicedToArray(_React$useState3, 2), - isValid = _React$useState4[0], - setIsValid = _React$useState4[1]; - var _React$useState5 = React.useState(false), - _React$useState6 = _slicedToArray(_React$useState5, 2), - saveError = _React$useState6[0], - setSaveError = _React$useState6[1]; - var _React$useState7 = React.useState(''), - _React$useState8 = _slicedToArray(_React$useState7, 2), - errorMessage = _React$useState8[0], - setErrorMessage = _React$useState8[1]; - var _React$useState9 = React.useState(), - _React$useState10 = _slicedToArray(_React$useState9, 2), - markerTime = _React$useState10[0], - setMarkerTime = _React$useState10[1]; + var _useState = useState(false), + _useState2 = _slicedToArray(_useState, 2), + isOpen = _useState2[0], + setIsOpen = _useState2[1]; + var _useState3 = useState(false), + _useState4 = _slicedToArray(_useState3, 2), + isValid = _useState4[0], + setIsValid = _useState4[1]; + var _useState5 = useState(false), + _useState6 = _slicedToArray(_useState5, 2), + saveError = _useState6[0], + setSaveError = _useState6[1]; + var _useState7 = useState(''), + _useState8 = _slicedToArray(_useState7, 2), + errorMessage = _useState8[0], + setErrorMessage = _useState8[1]; + var _useState9 = useState(), + _useState10 = _slicedToArray(_useState9, 2), + markerTime = _useState10[0], + setMarkerTime = _useState10[1]; var controller; - var _usePlayer = usePlayer(), - getCurrentTime = _usePlayer.getCurrentTime; - React.useEffect(function () { + var _useMediaPlayer = useMediaPlayer(), + getCurrentTime = _useMediaPlayer.getCurrentTime; + useEffect(function () { // Close new marker form on Canvas change setIsOpen(false); @@ -12068,7 +12573,7 @@ var CreateMarker = function CreateMarker(_ref) { validateTime(currentTime); setIsOpen(true); }; - var handleCreateSubmit = React.useCallback(function (e) { + var handleCreateSubmit = useCallback(function (e) { e.preventDefault(); var form = e.target; var formData = new FormData(form); @@ -12120,7 +12625,7 @@ var CreateMarker = function CreateMarker(_ref) { setErrorMessage('Marker creation failed.'); }); }, [canvasId]); - var handleCreateCancel = React.useCallback(function () { + var handleCreateCancel = useCallback(function () { setIsOpen(false); setIsValid(false); setErrorMessage(''); @@ -12161,7 +12666,7 @@ var CreateMarker = function CreateMarker(_ref) { id: "new-marker-time", "data-testid": "create-marker-timestamp", type: "text", - className: "ramp--markers-display__create-marker ".concat(isValid ? 'time-valid' : 'time-invalid'), + className: cx('ramp--markers-display__create-marker', isValid ? 'time-valid' : 'time-invalid'), name: "time", value: markerTime, onChange: validateTime @@ -12183,63 +12688,75 @@ var CreateMarker = function CreateMarker(_ref) { CreateMarker.propTypes = { newMarkerEndpoint: PropTypes.string.isRequired, canvasId: PropTypes.string, - handleCreate: PropTypes.func.isRequired + handleCreate: PropTypes.func.isRequired, + csrfToken: PropTypes.string }; +/** + * Build a table row for each 'highlighting; annotation in the current Canvas in the Manifest. + * These are timepoint annotations. When user has permissions to edit annotations, an actions + * column is populated for each annotation with edit and delete actions. + * @param {Object} props + * @param {Object} props.marker each marker parsed from annotations + * @param {Function} props.handleSubmit callback func to update state on marker edit action + * @param {Function} props.handleDelete callback func to update state on marker delete action + * @param {Function} props.toggleIsEditing callback function to update global state + * @param {String} props.csrfToken token to authenticate POST request + */ var MarkerRow = function MarkerRow(_ref) { var marker = _ref.marker, handleSubmit = _ref.handleSubmit, handleDelete = _ref.handleDelete, - hasAnnotationService = _ref.hasAnnotationService, toggleIsEditing = _ref.toggleIsEditing, csrfToken = _ref.csrfToken; - var _React$useState = React.useState(false), - _React$useState2 = _slicedToArray(_React$useState, 2), - editing = _React$useState2[0], - setEditing = _React$useState2[1]; - var _React$useState3 = React.useState(true), - _React$useState4 = _slicedToArray(_React$useState3, 2), - isValid = _React$useState4[0], - setIsValid = _React$useState4[1]; - var _React$useState5 = React.useState(), - _React$useState6 = _slicedToArray(_React$useState5, 2), - tempMarker = _React$useState6[0], - setTempMarker = _React$useState6[1]; - var _React$useState7 = React.useState(false), - _React$useState8 = _slicedToArray(_React$useState7, 2), - deleting = _React$useState8[0], - setDeleting = _React$useState8[1]; - var _React$useState9 = React.useState(false), - _React$useState10 = _slicedToArray(_React$useState9, 2), - saveError = _React$useState10[0], - setSaveError = _React$useState10[1]; - var _React$useState11 = React.useState(''), - _React$useState12 = _slicedToArray(_React$useState11, 2), - errorMessage = _React$useState12[0], - setErrorMessage = _React$useState12[1]; + var _useState = useState(false), + _useState2 = _slicedToArray(_useState, 2), + editing = _useState2[0], + setEditing = _useState2[1]; + var _useState3 = useState(true), + _useState4 = _slicedToArray(_useState3, 2), + isValid = _useState4[0], + setIsValid = _useState4[1]; + var _useState5 = useState(), + _useState6 = _slicedToArray(_useState5, 2), + tempMarker = _useState6[0], + setTempMarker = _useState6[1]; + var _useState7 = useState(false), + _useState8 = _slicedToArray(_useState7, 2), + deleting = _useState8[0], + setDeleting = _useState8[1]; + var _useState9 = useState(false), + _useState10 = _slicedToArray(_useState9, 2), + saveError = _useState10[0], + setSaveError = _useState10[1]; + var _useState11 = useState(''), + _useState12 = _slicedToArray(_useState11, 2), + errorMessage = _useState12[0], + setErrorMessage = _useState12[1]; var controller; var _useMarkers = useMarkers(), + hasAnnotationService = _useMarkers.hasAnnotationService, isDisabled = _useMarkers.isDisabled; - var _usePlayer = usePlayer(), - player = _usePlayer.player; + var _useMediaPlayer = useMediaPlayer(), + player = _useMediaPlayer.player; // Remove all fetch requests on unmount - React.useEffect(function () { + useEffect(function () { return function () { var _controller; (_controller = controller) === null || _controller === void 0 ? void 0 : _controller.abort(); }; }, []); - React.useEffect(function () { + useEffect(function () { setMarkerLabel(marker.value); setMarkerTime(marker.timeStr); }, [marker]); - var markerLabelRef = React.useRef(marker.value); + var markerLabelRef = useRef(marker.value); var setMarkerLabel = function setMarkerLabel(label) { markerLabelRef.current = label; }; - var markerOffsetRef = React.useRef(timeToS(marker.timeStr)); - var markerTimeRef = React.useRef(marker.timeStr); + var markerOffsetRef = useRef(timeToS(marker.timeStr)); + var markerTimeRef = useRef(marker.timeStr); var setMarkerTime = function setMarkerTime(time) { markerTimeRef.current = time; markerOffsetRef.current = timeToS(time); @@ -12368,7 +12885,7 @@ var MarkerRow = function MarkerRow(_ref) { setEditing(false); toggleIsEditing(false); }; - var handleMarkerClick = React.useCallback(function (e) { + var handleMarkerClick = useCallback(function (e) { e.preventDefault(); var currentTime = parseFloat(e.target.dataset['offset']); if (player) { @@ -12387,7 +12904,7 @@ var MarkerRow = function MarkerRow(_ref) { }, name: "label" })), /*#__PURE__*/React.createElement("td", null, /*#__PURE__*/React.createElement("input", { - className: "ramp--markers-display__edit-marker ".concat(isValid ? 'time-valid' : 'time-invalid'), + className: cx('ramp--markers-display__edit-marker', isValid ? 'time-valid' : 'time-invalid'), id: "time", "data-testid": "edit-timestamp", defaultValue: markerTimeRef.current, @@ -12458,10 +12975,17 @@ MarkerRow.propTypes = { marker: PropTypes.object.isRequired, handleSubmit: PropTypes.func.isRequired, handleDelete: PropTypes.func.isRequired, - hasAnnotationService: PropTypes.bool.isRequired, - toggleIsEditing: PropTypes.func.isRequired + toggleIsEditing: PropTypes.func.isRequired, + csrfToken: PropTypes.string }; +/** + * Display timepoint annotations associated with the current Canvas + * in a tabular format. + * @param {Object} props + * @param {Boolean} props.showHeading + * @param {String} props.headingText + */ var MarkersDisplay = function MarkersDisplay(_ref) { var _document$getElements; var _ref$showHeading = _ref.showHeading, @@ -12483,6 +13007,8 @@ var MarkersDisplay = function MarkersDisplay(_ref) { var _useErrorBoundary = useErrorBoundary(), showBoundary = _useErrorBoundary.showBoundary; var canvasIdRef = useRef(); + + // Using a ref updates markers table immediately after marker edit/creation var canvasPlaylistsMarkersRef = useRef([]); var setCanvasMarkers = function setCanvasMarkers(list) { setCanvasPlaylistsMarkers.apply(void 0, _toConsumableArray(list)); @@ -12546,33 +13072,40 @@ var MarkersDisplay = function MarkersDisplay(_ref) { type: 'setIsEditing' }); }); - return useMemo(function () { - return /*#__PURE__*/React.createElement("div", { - className: "ramp--markers-display", - "data-testid": "markers-display" - }, showHeading && /*#__PURE__*/React.createElement("div", { - className: "ramp--markers-display__title", - "data-testid": "markers-display-title" - }, /*#__PURE__*/React.createElement("h4", null, headingText)), hasAnnotationService && /*#__PURE__*/React.createElement(CreateMarker, { - newMarkerEndpoint: annotationServiceId, - canvasId: canvasIdRef.current, - handleCreate: handleCreate, - csrfToken: csrfToken - }), canvasPlaylistsMarkersRef.current.length > 0 && /*#__PURE__*/React.createElement("table", { - className: "ramp--markers-display_table", - "data-testid": "markers-display-table" - }, /*#__PURE__*/React.createElement("thead", null, /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("th", null, "Name"), /*#__PURE__*/React.createElement("th", null, "Time"), hasAnnotationService && /*#__PURE__*/React.createElement("th", null, "Actions"))), /*#__PURE__*/React.createElement("tbody", null, canvasPlaylistsMarkersRef.current.map(function (marker, index) { - return /*#__PURE__*/React.createElement(MarkerRow, { - key: index, - marker: marker, - handleSubmit: handleSubmit, - handleDelete: handleDelete, - hasAnnotationService: hasAnnotationService, - toggleIsEditing: toggleIsEditing, + var createMarker = useMemo(function () { + if (hasAnnotationService) { + return /*#__PURE__*/React.createElement(CreateMarker, { + newMarkerEndpoint: annotationServiceId, + canvasId: canvasIdRef.current, + handleCreate: handleCreate, csrfToken: csrfToken }); - })))); - }, [canvasPlaylistsMarkersRef.current, csrfToken]); + } + }, [hasAnnotationService, canvasIdRef.current, csrfToken]); + var markersTable = useMemo(function () { + if (canvasPlaylistsMarkersRef.current.length > 0) { + return /*#__PURE__*/React.createElement("table", { + className: "ramp--markers-display_table", + "data-testid": "markers-display-table" + }, /*#__PURE__*/React.createElement("thead", null, /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("th", null, "Name"), /*#__PURE__*/React.createElement("th", null, "Time"), hasAnnotationService && /*#__PURE__*/React.createElement("th", null, "Actions"))), /*#__PURE__*/React.createElement("tbody", null, canvasPlaylistsMarkersRef.current.map(function (marker, index) { + return /*#__PURE__*/React.createElement(MarkerRow, { + key: index, + marker: marker, + handleSubmit: handleSubmit, + handleDelete: handleDelete, + toggleIsEditing: toggleIsEditing, + csrfToken: csrfToken + }); + }))); + } + }, [canvasPlaylistsMarkersRef.current]); + return /*#__PURE__*/React.createElement("div", { + className: "ramp--markers-display", + "data-testid": "markers-display" + }, showHeading && /*#__PURE__*/React.createElement("div", { + className: "ramp--markers-display__title", + "data-testid": "markers-display-title" + }, /*#__PURE__*/React.createElement("h4", null, headingText)), createMarker, markersTable); }; MarkersDisplay.propTypes = { showHeading: PropTypes.bool, diff --git a/dist/ramp.umd.js b/dist/ramp.umd.js index 7312b290..89f856fa 100644 --- a/dist/ramp.umd.js +++ b/dist/ramp.umd.js @@ -1,16 +1,16 @@ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('manifesto.js'), require('mime-db'), require('sanitize-html'), require('react-error-boundary'), require('video.js'), require('classnames'), require('mammoth')) : - typeof define === 'function' && define.amd ? define(['exports', 'react', 'manifesto.js', 'mime-db', 'sanitize-html', 'react-error-boundary', 'video.js', 'classnames', 'mammoth'], factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.nulibAdminUIComponents = {}, global.React, global.manifesto, global.mimeDb, global.sanitizeHtml, global.reactErrorBoundary, global.videojs, global.cx, global.mammoth)); -})(this, (function (exports, React, manifesto_js, mimeDb, sanitizeHtml, reactErrorBoundary, videojs, cx, mammoth) { 'use strict'; + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('manifesto.js'), require('mime-db'), require('sanitize-html'), require('react-error-boundary'), require('classnames'), require('video.js'), require('mammoth')) : + typeof define === 'function' && define.amd ? define(['exports', 'react', 'manifesto.js', 'mime-db', 'sanitize-html', 'react-error-boundary', 'classnames', 'video.js', 'mammoth'], factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.nulibAdminUIComponents = {}, global.React, global.manifesto, global.mimeDb, global.sanitizeHtml, global.reactErrorBoundary, global.cx, global.videojs, global.mammoth)); +})(this, (function (exports, React, manifesto_js, mimeDb, sanitizeHtml, reactErrorBoundary, cx, videojs, mammoth) { 'use strict'; function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var React__default = /*#__PURE__*/_interopDefaultLegacy(React); var mimeDb__default = /*#__PURE__*/_interopDefaultLegacy(mimeDb); var sanitizeHtml__default = /*#__PURE__*/_interopDefaultLegacy(sanitizeHtml); - var videojs__default = /*#__PURE__*/_interopDefaultLegacy(videojs); var cx__default = /*#__PURE__*/_interopDefaultLegacy(cx); + var videojs__default = /*#__PURE__*/_interopDefaultLegacy(videojs); var mammoth__default = /*#__PURE__*/_interopDefaultLegacy(mammoth); var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; @@ -1278,8 +1278,8 @@ var isEmpty_1 = isEmpty; - function ownKeys$8(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } - function _objectSpread$8(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$8(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$8(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } + function ownKeys$9(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } + function _objectSpread$9(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$9(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$9(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } var S_ANNOTATION_TYPE = { transcript: 1, caption: 2, @@ -1684,7 +1684,7 @@ * there is a start defined at the manifest level */ if (!isPlaylist) { - target = _objectSpread$8(_objectSpread$8({}, target), {}, { + target = _objectSpread$9(_objectSpread$9({}, target), {}, { customStart: target.start, start: 0, altStart: 0 @@ -2025,8 +2025,8 @@ function _createForOfIteratorHelper$3(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$3(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray$3(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$3(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$3(o, minLen); } function _arrayLikeToArray$3(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } - function ownKeys$7(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } - function _objectSpread$7(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$7(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$7(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } + function ownKeys$8(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } + function _objectSpread$8(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$8(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$8(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } // HTML tags and attributes allowed in IIIF var HTML_SANITIZE_CONFIG = { @@ -2153,7 +2153,7 @@ // return empty object when canvasIndex is undefined if (canvasIndex === undefined || canvasIndex < 0) { - return _objectSpread$7(_objectSpread$7({}, info), {}, { + return _objectSpread$8(_objectSpread$8({}, info), {}, { error: 'Error fetching content' }); } @@ -2161,7 +2161,7 @@ // return an error when the given Manifest doesn't have any Canvas(es) var canvases = manifest.items; if ((canvases === null || canvases === void 0 ? void 0 : canvases.length) == 0) { - return _objectSpread$7(_objectSpread$7({}, info), {}, { + return _objectSpread$8(_objectSpread$8({}, info), {}, { poster: GENERIC_EMPTY_MANIFEST_MESSAGE }); } @@ -2199,14 +2199,14 @@ poster: poster }; if (mediaInfo.error) { - return _objectSpread$7({}, mediaInfo); + return _objectSpread$8({}, mediaInfo); } else { // Get media type var allTypes = mediaInfo.sources.map(function (q) { return q.kind; }); var mediaType = setMediaType(allTypes); - return _objectSpread$7(_objectSpread$7({}, mediaInfo), {}, { + return _objectSpread$8(_objectSpread$8({}, mediaInfo), {}, { error: null, mediaType: mediaType }); @@ -2535,7 +2535,7 @@ var _getLabelValue; // get value and replace \n characters with
      to display new lines in UI var value = (_getLabelValue = getLabelValue(md.value, true)) === null || _getLabelValue === void 0 ? void 0 : _getLabelValue.replace(/\n/g, "
      "); - var sanitizedValue = sanitizeHtml__default["default"](value, _objectSpread$7({}, HTML_SANITIZE_CONFIG)); + var sanitizedValue = sanitizeHtml__default["default"](value, _objectSpread$8({}, HTML_SANITIZE_CONFIG)); parsedMetadata.push({ label: getLabelValue(md.label), value: sanitizedValue @@ -2888,10 +2888,10 @@ } } - function ownKeys$6(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } - function _objectSpread$6(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$6(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$6(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } - var ManifestStateContext = /*#__PURE__*/React__default["default"].createContext(); - var ManifestDispatchContext = /*#__PURE__*/React__default["default"].createContext(); + function ownKeys$7(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } + function _objectSpread$7(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$7(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$7(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } + var ManifestStateContext = /*#__PURE__*/React.createContext(); + var ManifestDispatchContext = /*#__PURE__*/React.createContext(); /** * Definition of all state variables in this Context @@ -2949,11 +2949,11 @@ var isPlaylist = getIsPlaylist(manifest.label); var annotationService = getAnnotationService(manifest.service); var playlistMarkers = parsePlaylistAnnotations(manifest); - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { manifest: manifest, allCanvases: canvases, autoAdvance: manifestBehavior, - playlist: _objectSpread$6(_objectSpread$6({}, state.playlist), {}, { + playlist: _objectSpread$7(_objectSpread$7({}, state.playlist), {}, { isPlaylist: isPlaylist, annotationServiceId: annotationService, hasAnnotationService: annotationService ? true : false, @@ -2963,56 +2963,56 @@ } case 'switchCanvas': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { canvasIndex: action.canvasIndex, hasStructure: getHasStructure(state.canvasSegments, action.canvasIndex) }); } case 'switchItem': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { currentNavItem: action.item }); } case 'canvasDuration': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { canvasDuration: action.canvasDuration }); } case 'canvasLink': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { canvasLink: action.canvasLink }); } case 'canvasTargets': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { targets: action.canvasTargets }); } case 'hasMultipleItems': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { hasMultiItems: action.isMultiSource }); } case 'setSrcIndex': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { srcIndex: action.srcIndex }); } case 'setItemStartTime': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { startTime: action.startTime }); } case 'setAutoAdvance': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { autoAdvance: action.autoAdvance }); } @@ -3020,16 +3020,16 @@ { // Set a new set of markers for the canvases in the Manifest if (action.markers) { - return _objectSpread$6(_objectSpread$6({}, state), {}, { - playlist: _objectSpread$6(_objectSpread$6({}, state.playlist), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { + playlist: _objectSpread$7(_objectSpread$7({}, state.playlist), {}, { markers: action.markers }) }); } // Update the existing markers for the current canvas on CRUD ops if (action.updatedMarkers) { - return _objectSpread$6(_objectSpread$6({}, state), {}, { - playlist: _objectSpread$6(_objectSpread$6({}, state.playlist), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { + playlist: _objectSpread$7(_objectSpread$7({}, state.playlist), {}, { markers: state.playlist.markers.map(function (m) { if (m.canvasIndex === state.canvasIndex) { m.canvasMarkers = action.updatedMarkers; @@ -3042,21 +3042,21 @@ } case 'setIsEditing': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { - playlist: _objectSpread$6(_objectSpread$6({}, state.playlist), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { + playlist: _objectSpread$7(_objectSpread$7({}, state.playlist), {}, { isEditing: action.isEditing }) }); } case 'setCanvasIsEmpty': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { canvasIsEmpty: action.isEmpty }); } case 'setStructures': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { structures: action.structures }); } @@ -3066,7 +3066,7 @@ var canvasStructures = action.timespans.filter(function (c) { return c.canvasIndex == state.canvasIndex + 1 && !c.isCanvas; }); - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { canvasSegments: action.timespans, hasStructure: canvasStructures.length > 0 }); @@ -3076,7 +3076,7 @@ var _action$customStart = action.customStart, canvas = _action$customStart.canvas, time = _action$customStart.time; - return _objectSpread$6(_objectSpread$6({}, state), {}, { + return _objectSpread$7(_objectSpread$7({}, state), {}, { customStart: { startIndex: canvas, startTime: time @@ -3087,8 +3087,8 @@ } case 'setRenderingFiles': { - return _objectSpread$6(_objectSpread$6({}, state), {}, { - renderings: _objectSpread$6({}, action.renderings) + return _objectSpread$7(_objectSpread$7({}, state), {}, { + renderings: _objectSpread$7({}, action.renderings) }); } default: @@ -3101,10 +3101,10 @@ var _ref$initialState = _ref.initialState, initialState = _ref$initialState === void 0 ? defaultState$1 : _ref$initialState, children = _ref.children; - var _React$useReducer = React__default["default"].useReducer(manifestReducer, initialState), - _React$useReducer2 = _slicedToArray(_React$useReducer, 2), - state = _React$useReducer2[0], - dispatch = _React$useReducer2[1]; + var _useReducer = React.useReducer(manifestReducer, initialState), + _useReducer2 = _slicedToArray(_useReducer, 2), + state = _useReducer2[0], + dispatch = _useReducer2[1]; return /*#__PURE__*/React__default["default"].createElement(ManifestStateContext.Provider, { value: state }, /*#__PURE__*/React__default["default"].createElement(ManifestDispatchContext.Provider, { @@ -3112,24 +3112,24 @@ }, children)); } function useManifestState() { - var context = React__default["default"].useContext(ManifestStateContext); + var context = React.useContext(ManifestStateContext); if (context === undefined) { throw new Error('useManifestState must be used within a ManifestProvider'); } return context; } function useManifestDispatch() { - var context = React__default["default"].useContext(ManifestDispatchContext); + var context = React.useContext(ManifestDispatchContext); if (context === undefined) { throw new Error('useManifestDispatch must be used within a ManifestProvider'); } return context; } - function ownKeys$5(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } - function _objectSpread$5(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$5(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$5(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } - var PlayerStateContext = /*#__PURE__*/React__default["default"].createContext(); - var PlayerDispatchContext = /*#__PURE__*/React__default["default"].createContext(); + function ownKeys$6(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } + function _objectSpread$6(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$6(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$6(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } + var PlayerStateContext = /*#__PURE__*/React.createContext(); + var PlayerDispatchContext = /*#__PURE__*/React.createContext(); /** * Definition of all state variables in this Context @@ -3152,63 +3152,63 @@ switch (action.type) { case 'updatePlayer': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { player: action.player }); } case 'navClick': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { clickedUrl: action.clickedUrl, isClicked: true }); } case 'resetClick': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { isClicked: false }); } case 'setTimeFragment': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { startTime: action.startTime, endTime: action.endTime }); } case 'setSearchMarkers': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { searchMarkers: action.payload }); } case 'setPlayingStatus': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { isPlaying: action.isPlaying }); } case 'setCaptionStatus': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { captionOn: action.captionOn }); } case 'setIsEnded': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { isEnded: action.isEnded }); } case 'setCurrentTime': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { currentTime: action.currentTime }); } case 'setPlayerFocusElement': { - return _objectSpread$5(_objectSpread$5({}, state), {}, { + return _objectSpread$6(_objectSpread$6({}, state), {}, { playerFocusElement: action.element ? action.element : '' }); } @@ -3222,10 +3222,10 @@ var _ref$initialState = _ref.initialState, initialState = _ref$initialState === void 0 ? defaultState : _ref$initialState, children = _ref.children; - var _React$useReducer = React__default["default"].useReducer(PlayerReducer, initialState), - _React$useReducer2 = _slicedToArray(_React$useReducer, 2), - state = _React$useReducer2[0], - dispatch = _React$useReducer2[1]; + var _useReducer = React.useReducer(PlayerReducer, initialState), + _useReducer2 = _slicedToArray(_useReducer, 2), + state = _useReducer2[0], + dispatch = _useReducer2[1]; return /*#__PURE__*/React__default["default"].createElement(PlayerStateContext.Provider, { value: state }, /*#__PURE__*/React__default["default"].createElement(PlayerDispatchContext.Provider, { @@ -3233,14 +3233,14 @@ }, children)); } function usePlayerState() { - var context = React__default["default"].useContext(PlayerStateContext); + var context = React.useContext(PlayerStateContext); if (context === undefined) { throw new Error("usePlayerState must be used within the PlayerProvider"); } return context; } function usePlayerDispatch() { - var context = React__default["default"].useContext(PlayerDispatchContext); + var context = React.useContext(PlayerDispatchContext); if (context === undefined) { throw new Error("usePlayerDispatch must be used within the PlayerProvider"); } @@ -3704,10 +3704,10 @@ startCanvasTime = _ref.startCanvasTime, children = _ref.children, manifestValue = _ref.manifest; - var _React$useState = React__default["default"].useState(manifestValue), - _React$useState2 = _slicedToArray(_React$useState, 2), - manifest = _React$useState2[0], - setManifest = _React$useState2[1]; + var _useState = React.useState(manifestValue), + _useState2 = _slicedToArray(_useState, 2), + manifest = _useState2[0], + setManifest = _useState2[1]; var manifestDispatch = useManifestDispatch(); var playerDispatch = usePlayerDispatch(); var _useErrorBoundary = reactErrorBoundary.useErrorBoundary(), @@ -3723,7 +3723,7 @@ case 0: controller = new AbortController(); requestOptions = { - // NOTE: try thin in Avalon + // NOTE: try this in Avalon //credentials: 'include', // headers: { 'Avalon-Api-Key': '' }, }; @@ -3768,7 +3768,7 @@ return _ref2.apply(this, arguments); }; }(); - React__default["default"].useEffect(function () { + React.useEffect(function () { setAppErrorMessage(customErrorMessage); setAppEmptyManifestMessage(emptyManifestMessage); if (!manifest && manifestUrl) { @@ -3780,7 +3780,7 @@ if (controller) controller.abort(); }; }, []); - React__default["default"].useEffect(function () { + React.useEffect(function () { if (manifest) { // Set customStart and rendering files in state before setting Manifest var renderingFiles = getRenderingFiles(manifest); @@ -3852,6 +3852,17 @@ children: PropTypes.object }; + /** + * Component with wrapped in React Contexts to provide access + * to global state across its children + * @param {Object} props + * @param {String} props.manifestUrl + * @param {Object} props.manifest + * @param {String} props.customErrorMessage + * @param {String} props.emptyManifestMessage + * @param {String} props.startCanvasId + * @param {String} props.startCanvasTime + */ function IIIFPlayer(_ref) { var manifestUrl = _ref.manifestUrl, manifest = _ref.manifest, @@ -5245,1956 +5256,1862 @@ }))); }; - var classCallCheck = createCommonjsModule(function (module) { - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - } - module.exports = _classCallCheck, module.exports.__esModule = true, module.exports["default"] = module.exports; - }); - - var _classCallCheck = /*@__PURE__*/getDefaultExportFromCjs(classCallCheck); - - var createClass = createCommonjsModule(function (module) { - function _defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, toPropertyKey(descriptor.key), descriptor); - } - } - function _createClass(Constructor, protoProps, staticProps) { - if (protoProps) _defineProperties(Constructor.prototype, protoProps); - if (staticProps) _defineProperties(Constructor, staticProps); - Object.defineProperty(Constructor, "prototype", { - writable: false - }); - return Constructor; - } - module.exports = _createClass, module.exports.__esModule = true, module.exports["default"] = module.exports; - }); - - var _createClass = /*@__PURE__*/getDefaultExportFromCjs(createClass); - - var assertThisInitialized = createCommonjsModule(function (module) { - function _assertThisInitialized(self) { - if (self === void 0) { - throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); - } - return self; - } - module.exports = _assertThisInitialized, module.exports.__esModule = true, module.exports["default"] = module.exports; - }); - - var _assertThisInitialized = /*@__PURE__*/getDefaultExportFromCjs(assertThisInitialized); - - var getPrototypeOf = createCommonjsModule(function (module) { - function _getPrototypeOf(o) { - module.exports = _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { - return o.__proto__ || Object.getPrototypeOf(o); - }, module.exports.__esModule = true, module.exports["default"] = module.exports; - return _getPrototypeOf(o); - } - module.exports = _getPrototypeOf, module.exports.__esModule = true, module.exports["default"] = module.exports; - }); - - var _getPrototypeOf = /*@__PURE__*/getDefaultExportFromCjs(getPrototypeOf); - - var superPropBase = createCommonjsModule(function (module) { - function _superPropBase(object, property) { - while (!Object.prototype.hasOwnProperty.call(object, property)) { - object = getPrototypeOf(object); - if (object === null) break; - } - return object; - } - module.exports = _superPropBase, module.exports.__esModule = true, module.exports["default"] = module.exports; - }); - - var get = createCommonjsModule(function (module) { - function _get() { - if (typeof Reflect !== "undefined" && Reflect.get) { - module.exports = _get = Reflect.get.bind(), module.exports.__esModule = true, module.exports["default"] = module.exports; - } else { - module.exports = _get = function _get(target, property, receiver) { - var base = superPropBase(target, property); - if (!base) return; - var desc = Object.getOwnPropertyDescriptor(base, property); - if (desc.get) { - return desc.get.call(arguments.length < 3 ? target : receiver); - } - return desc.value; - }, module.exports.__esModule = true, module.exports["default"] = module.exports; + var taggedTemplateLiteral = createCommonjsModule(function (module) { + function _taggedTemplateLiteral(strings, raw) { + if (!raw) { + raw = strings.slice(0); } - return _get.apply(this, arguments); + return Object.freeze(Object.defineProperties(strings, { + raw: { + value: Object.freeze(raw) + } + })); } - module.exports = _get, module.exports.__esModule = true, module.exports["default"] = module.exports; + module.exports = _taggedTemplateLiteral, module.exports.__esModule = true, module.exports["default"] = module.exports; }); - var _get = /*@__PURE__*/getDefaultExportFromCjs(get); + var _taggedTemplateLiteral = /*@__PURE__*/getDefaultExportFromCjs(taggedTemplateLiteral); - var setPrototypeOf = createCommonjsModule(function (module) { - function _setPrototypeOf(o, p) { - module.exports = _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { - o.__proto__ = p; - return o; - }, module.exports.__esModule = true, module.exports["default"] = module.exports; - return _setPrototypeOf(o, p); - } - module.exports = _setPrototypeOf, module.exports.__esModule = true, module.exports["default"] = module.exports; - }); + var _templateObject$1, _templateObject2, _templateObject3, _templateObject4; + function _createForOfIteratorHelper$2(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$2(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } + function _unsupportedIterableToArray$2(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$2(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$2(o, minLen); } + function _arrayLikeToArray$2(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } - var inherits = createCommonjsModule(function (module) { - function _inherits(subClass, superClass) { - if (typeof superClass !== "function" && superClass !== null) { - throw new TypeError("Super expression must either be null or a function"); - } - subClass.prototype = Object.create(superClass && superClass.prototype, { - constructor: { - value: subClass, - writable: true, - configurable: true - } - }); - Object.defineProperty(subClass, "prototype", { - writable: false - }); - if (superClass) setPrototypeOf(subClass, superClass); - } - module.exports = _inherits, module.exports.__esModule = true, module.exports["default"] = module.exports; - }); + // ENum for supported transcript MIME types + var TRANSCRIPT_MIME_TYPES = { + webvtt: ['text/vtt'], + srt: ['application/x-subrip', 'text/srt'], + text: ['text/plain'], + json: ['application/json'], + docx: ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'] + }; + var VTT_TIMESTAMP_REGEX = /^(?:\d{2}:)?\d{2}:\d{2}(?:\.\d+)/g; + // SRT allows using comma for milliseconds while WebVTT does not + var SRT_TIMESTAMP_REGEX = /^(?:\d{2}:)?\d{2}:\d{2}(?:[.,]\d+)/g; + var TRANSCRIPT_MIME_EXTENSIONS = [{ + type: TRANSCRIPT_MIME_TYPES.json, + ext: 'json' + }, { + type: TRANSCRIPT_MIME_TYPES.webvtt, + ext: 'vtt' + }, { + type: TRANSCRIPT_MIME_TYPES.text, + ext: 'txt' + }, { + type: TRANSCRIPT_MIME_TYPES.docx, + ext: 'docx' + }, { + type: TRANSCRIPT_MIME_TYPES.srt, + ext: 'srt' + }]; - var _inherits = /*@__PURE__*/getDefaultExportFromCjs(inherits); + // ENum for describing transcript types include invalid and no transcript info + var TRANSCRIPT_TYPES = { + invalidTimestamp: -4, + invalidVTT: -3, + noSupport: -2, + invalid: -1, + noTranscript: 0, + timedText: 1, + plainText: 2, + docx: 3 + }; - var possibleConstructorReturn = createCommonjsModule(function (module) { - var _typeof = _typeof_1["default"]; + // ENum for types transcript text lines in a time-synced transcript + var TRANSCRIPT_CUE_TYPES = { + note: 'NOTE', + timedCue: 'TIMED_CUE', + nonTimedLine: 'NON_TIMED_LINE' + }; - function _possibleConstructorReturn(self, call) { - if (call && (_typeof(call) === "object" || typeof call === "function")) { - return call; - } else if (call !== void 0) { - throw new TypeError("Derived constructors may only return object or undefined"); - } - return assertThisInitialized(self); + /** + * Parse the transcript information in the Manifest presented as supplementing annotations + * @param {String} manifestURL IIIF Presentation 3.0 manifest URL + * @param {String} title optional title given in the transcripts list in props + * @returns {Array} array of supplementing annotations for transcripts for all + * canvases in the Manifest + */ + function readSupplementingAnnotations(_x) { + return _readSupplementingAnnotations.apply(this, arguments); } - module.exports = _possibleConstructorReturn, module.exports.__esModule = true, module.exports["default"] = module.exports; - }); - - var _possibleConstructorReturn = /*@__PURE__*/getDefaultExportFromCjs(possibleConstructorReturn); - function _createSuper$6(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$6(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } - function _isNativeReflectConstruct$6() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - var SeekBar = videojs__default["default"].getComponent('SeekBar'); - var VideoJSProgress = /*#__PURE__*/function (_SeekBar) { - _inherits(VideoJSProgress, _SeekBar); - var _super = _createSuper$6(VideoJSProgress); - function VideoJSProgress(player, options) { - var _this; - _classCallCheck(this, VideoJSProgress); - _this = _super.call(this, player, options); - /** - * Set start values for progress bar - * @param {Number} start canvas start time - */ - _defineProperty(_assertThisInitialized(_this), "initializeProgress", function (start) { - _this.setProgress(start); - _this.setInitTime(start); - _this.player.currentTime(start); - }); - _this.addClass('vjs-custom-progress-bar'); - _this.setAttribute('data-testid', 'videojs-custom-progressbar'); - _this.setAttribute('tabindex', 0); - _this.player = player; - _this.options = options; - _this.selectSource = _this.options.nextItemClicked; - _this.playerEventListener; - _this.initTimeRef = /*#__PURE__*/React__default["default"].createRef(); - _this.progressRef = /*#__PURE__*/React__default["default"].createRef(); - _this.canvasTargetsRef = /*#__PURE__*/React__default["default"].createRef(); - _this.srcIndexRef = /*#__PURE__*/React__default["default"].createRef(); - _this.isMultiSourceRef = /*#__PURE__*/React__default["default"].createRef(); - _this.currentTimeRef = /*#__PURE__*/React__default["default"].createRef(); - _this.pointerDragged = false; - _this.totalDuration; - _this.playProgress = _this.getChild('PlayProgressBar'); - _this.loadProgress = _this.getChild('LoadProgressBar'); - _this.player.on('ready', function () { - _this.initializeEl(); - _this.updateComponent(); - }); - _this.player.on('loadstart', function () { - _this.updateComponent(); - _this.buildProgressBar(); - }); - - // Update our progress bar after the user leaves full screen - _this.player.on('fullscreenchange', function (e) { - if (!_this.player.isFullscreen()) { - _this.setProgress(_this.player.currentTime()); - } - }); - _this.player.on('dispose', function () { - clearInterval(_this.playerEventListener); - }); - return _this; - } - _createClass(VideoJSProgress, [{ - key: "setInitTime", - value: function setInitTime(t) { - this.initTimeRef.current = t; - } - }, { - key: "setSrcIndex", - value: function setSrcIndex(i) { - this.srcIndexRef.current = i; - } - }, { - key: "setProgress", - value: function setProgress(p) { - this.progressRef.current = p; - } - }, { - key: "setCanvasTargets", - value: function setCanvasTargets(t) { - this.canvasTargetsRef.current = t; - this.totalDuration = t.reduce(function (acc, c) { - return acc + c.duration; - }, 0); - } - }, { - key: "setIsMultiSource", - value: function setIsMultiSource(m) { - this.isMultiSourceRef.current = m; - } - }, { - key: "setCurrentTime", - value: function setCurrentTime(t) { - this.currentTimeRef.current = t; - } - }, { - key: "updateComponent", - value: function updateComponent() { - var _this2 = this; - var _this$player = this.player, - srcIndex = _this$player.srcIndex, - targets = _this$player.targets; - this.setSrcIndex(srcIndex); - this.setCanvasTargets(targets); - var cTimes = targets[srcIndex]; - if (cTimes.customStart > cTimes.start) { - this.initializeProgress(cTimes.customStart); - } else { - this.initializeProgress(cTimes.start); + /** + * Refine and sanitize the user provided transcripts list in the props. If there are manifests + * in the given array process them to find supplementing annotations in the manifest and + * them to the transcripts array to be displayed in the component. + * @param {Array} transcripts list of transcripts from Transcript component's props + * @returns {Array} a refined transcripts array for each canvas with the following json + * structure; + * { canvasId: , items: [{ title, filename, url, isMachineGen, id }]} + */ + function _readSupplementingAnnotations() { + _readSupplementingAnnotations = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee(manifestURL) { + var title, + data, + _args = arguments; + return regenerator.wrap(function _callee$(_context) { + while (1) switch (_context.prev = _context.next) { + case 0: + title = _args.length > 1 && _args[1] !== undefined ? _args[1] : ''; + _context.next = 3; + return fetch(manifestURL).then(function (response) { + var fileType = response.headers.get('Content-Type'); + if (fileType.includes('application/json')) { + var jsonData = response.json(); + return jsonData; + } else { + // Avoid throwing an error when fetched file is not a JSON + return {}; + } + }).then(function (manifest) { + var canvases = manifest.items; + var newTranscriptsList = []; + if ((canvases === null || canvases === void 0 ? void 0 : canvases.length) > 0) { + canvases.map(function (canvas, index) { + var annotations = getAnnotations(canvas.annotations, 'supplementing'); + var canvasTranscripts = []; + if (annotations.length > 0) { + var annotBody = annotations[0].body; + if (annotBody.type === 'TextualBody') { + var label = title.length > 0 ? title : annotBody.label ? getLabelValue(annotBody.label) : "Canvas-".concat(index); + var _identifyMachineGen = identifyMachineGen(label), + isMachineGen = _identifyMachineGen.isMachineGen, + labelText = _identifyMachineGen.labelText; + canvasTranscripts.push({ + url: annotBody.id === undefined ? manifestURL : annotBody.id, + title: labelText, + isMachineGen: isMachineGen, + id: "".concat(labelText, "-").concat(index), + format: '' + }); + } else { + annotations.forEach(function (annotation, i) { + var annotBody = annotation.body; + var label = ''; + var filename = ''; + if (annotBody.label && Object.keys(annotBody.label).length > 0) { + var languages = Object.keys(annotBody.label); + if ((languages === null || languages === void 0 ? void 0 : languages.length) > 1) { + // If there are multiple labels for an annotation assume the first + // is the one intended for default display. + label = getLabelValue(annotBody.label); + // Assume that an unassigned language is meant to be the downloadable filename + filename = annotBody.label.hasOwnProperty('none') ? getLabelValue(annotBody.label.none[0]) : label; + } else { + // If there is a single label, use for both label and downloadable filename + label = getLabelValue(annotBody.label); + } + } else { + label = "".concat(i); + } + var id = annotBody.id; + var sType = identifySupplementingAnnotation(id); + var _identifyMachineGen2 = identifyMachineGen(label), + isMachineGen = _identifyMachineGen2.isMachineGen, + labelText = _identifyMachineGen2.labelText; + if (filename === '') { + filename = labelText; + } + if (sType === 1 || sType === 3) { + canvasTranscripts.push({ + title: labelText, + filename: filename, + url: id, + isMachineGen: isMachineGen, + id: "".concat(labelText, "-").concat(index, "-").concat(i), + format: annotBody.format || '' + }); + } + }); + } + } + newTranscriptsList.push({ + canvasId: index, + items: canvasTranscripts + }); + }); + } + return newTranscriptsList; + })["catch"](function (error) { + console.error('transcript-parser -> readSupplementingAnnotations() -> error fetching transcript resource at, ', manifestURL); + return []; + }); + case 3: + data = _context.sent; + return _context.abrupt("return", data); + case 5: + case "end": + return _context.stop(); } - this.setIsMultiSource((targets === null || targets === void 0 ? void 0 : targets.length) > 1 ? true : false); - if (!this.playerEventListener) { - /** - * Using a time interval instead of 'timeupdate event in VideoJS, because Safari - * and other browsers in MacOS stops firing the 'timeupdate' event consistently - * after a while - */ - this.playerEventListener = setInterval(function () { - /** - * Abortable inerval for Safari desktop browsers, for a smoother scrubbing - * experience. - * Mobile devices are excluded since they use native iOS player. - */ - if (IS_SAFARI && !IS_IPHONE) { - _this2.abortableTimeupdateHandler(); - } else { - _this2.timeUpdateHandler(); + }, _callee); + })); + return _readSupplementingAnnotations.apply(this, arguments); + } + function sanitizeTranscripts(_x2) { + return _sanitizeTranscripts.apply(this, arguments); + } + + /** + * Group a nested JSON object array by a given property name + * @param {Array} objectArray nested array to reduced + * @param {String} indexKey property name to be used to group elements in the array + * @param {String} selectKey property to be selected from the objects to accumulated + * @returns {Array} + */ + function _sanitizeTranscripts() { + _sanitizeTranscripts = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee4(transcripts) { + var allTranscripts, sanitizedTrs, newTranscripts; + return regenerator.wrap(function _callee4$(_context4) { + while (1) switch (_context4.prev = _context4.next) { + case 0: + if (!(!transcripts || transcripts == undefined || transcripts.length == 0)) { + _context4.next = 5; + break; } - }, 100); - } - } - }, { - key: "update", - value: function update() { - // Need this to make the other updates work - _get(_getPrototypeOf(VideoJSProgress.prototype), "update", this).call(this); - // Explicitly played range variable on update for touch devices - if (IS_TOUCH_ONLY && this.player.currentTime() === 0) { - this.removeClass('played-range'); - document.documentElement.style.setProperty('--range-progress', "calc(".concat(0, "%)")); - } - if (IS_MOBILE && IS_SAFARI && this.player.paused()) { - var _this$player$structSt; - var structStart = (_this$player$structSt = this.player.structStart) !== null && _this$player$structSt !== void 0 ? _this$player$structSt : 0; - if (structStart != 0 && this.player.currentTime() === 0) { - this.player.currentTime(structStart); - var played = Math.min(100, Math.max(0, 100 * (structStart / this.totalDuration))); - this.addClass('played-range'); - document.documentElement.style.setProperty('--range-progress', "calc(".concat(played, "%)")); - this.player.structStart = 0; - } - } else { - return; - } - } - }, { - key: "initializeEl", - value: function initializeEl() { - var _this3 = this; - var leftBlock = videojs__default["default"].dom.createEl('div', { - className: 'block-stripes', - role: 'presentation', - id: 'left-block' - }); - var rightBlock = videojs__default["default"].dom.createEl('div', { - className: 'block-stripes', - role: 'presentation', - id: 'right-block' - }); - this.el().appendChild(leftBlock); - this.el().appendChild(rightBlock); + console.error('No transcripts given as input'); + return _context4.abrupt("return", []); + case 5: + allTranscripts = []; // Build an empty list for each canvasId from the given transcripts prop + transcripts.map(function (trs) { + return allTranscripts.push({ + canvasId: trs.canvasId, + items: [] + }); + }); - /** - * Add eventlisteners to handle time tool-tip display and progress updates. - * Using pointerup, pointermove, pointerdown events instead of mouseup, - * mousemove, mousedown events to make it work with both mouse pointer - * and touch events. - */ - this.el().addEventListener('mouseenter', function (e) { - _this3.handleMouseMove(e); - }); - this.el().addEventListener('pointerup', function (e) { - if (_this3.pointerDragged) { - _this3.handleMouseUp(e); - } - }); - this.el().addEventListener('pointermove', function (e) { - _this3.handleMouseMove(e); - _this3.pointerDragged = true; - }); - this.el().addEventListener('pointerdown', function (e) { - _this3.handleMouseDown(e); - _this3.pointerDragged = false; - }); - } - }, { - key: "handleMouseMove", - value: function handleMouseMove(e) { - var _this$convertToTime = this.convertToTime(e), - currentTime = _this$convertToTime.currentTime, - offsetx = _this$convertToTime.offsetx; - if (currentTime != undefined) this.setCurrentTime(currentTime); - var mouseTimeDisplay = this.getChild('MouseTimeDisplay'); - if (mouseTimeDisplay) { - var timeTooltip = mouseTimeDisplay.getChild('TimeTooltip'); - var toolTipEl = timeTooltip.el_; - if (currentTime) { - toolTipEl.innerHTML = timeToHHmmss(currentTime); - } - var pullTooltip = toolTipEl.clientWidth / 2; - toolTipEl.style.left = "".concat(offsetx - pullTooltip, "px"); - } - } - }, { - key: "handleMouseDown", - value: function handleMouseDown(e) { - // Do nothing when right-click is pressed - if (!IS_TOUCH_ONLY && e.buttons === 2) return; - var _this$convertToTime2 = this.convertToTime(e), - currentTime = _this$convertToTime2.currentTime; - _this$convertToTime2._; - var clickedSrc; - if (this.isMultiSourceRef.current) { - clickedSrc = this.canvasTargetsRef.current.find(function (t) { - var virtualEnd = t.altStart + t.duration; - if (currentTime >= t.altStart && currentTime <= virtualEnd) { - return t; - } - }); - } - if (clickedSrc) { - var _clickedSrc$sIndex, _clickedSrc; - var clickedIndex = (_clickedSrc$sIndex = (_clickedSrc = clickedSrc) === null || _clickedSrc === void 0 ? void 0 : _clickedSrc.sIndex) !== null && _clickedSrc$sIndex !== void 0 ? _clickedSrc$sIndex : 0; - if (clickedIndex != this.srcIndexRef.current) { - this.selectSource(clickedSrc.sIndex, currentTime - clickedSrc.altStart); - this.setSrcIndex(clickedIndex); - } else { - this.player.currentTime(currentTime - clickedSrc.altStart); - } - } else { - this.player.currentTime(currentTime); - } + // Process the async function to resolve manifest URLs in the given transcripts array + // parallely to extract supplementing annotations in the manifests + _context4.next = 9; + return Promise.all(transcripts.map( /*#__PURE__*/function () { + var _ref5 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee3(transcript) { + var canvasId, items, sanitizedItems; + return regenerator.wrap(function _callee3$(_context3) { + while (1) switch (_context3.prev = _context3.next) { + case 0: + canvasId = transcript.canvasId, items = transcript.items; + _context3.next = 3; + return Promise.all(items.map( /*#__PURE__*/function () { + var _ref6 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee2(item, index) { + var title, url, manifestTranscripts, _identifyMachineGen3, isMachineGen, labelText, manifestItems, groupedTrs; + return regenerator.wrap(function _callee2$(_context2) { + while (1) switch (_context2.prev = _context2.next) { + case 0: + title = item.title, url = item.url; // For each item in the list check if it is a manifest and parse + // the it to identify any supplementing annotations in the + // manifest for each canvas + _context2.next = 3; + return readSupplementingAnnotations(url, title); + case 3: + manifestTranscripts = _context2.sent; + _identifyMachineGen3 = identifyMachineGen(title), isMachineGen = _identifyMachineGen3.isMachineGen, labelText = _identifyMachineGen3.labelText; + manifestItems = []; + if ((manifestTranscripts === null || manifestTranscripts === void 0 ? void 0 : manifestTranscripts.length) > 0) { + manifestItems = manifestTranscripts.map(function (mt) { + return mt.items; + }).flat(); - /** - * For touch devices, player.currentTime() update doesn't show the - * played range, even though the player's currentTime is properly set. - * Therefore, update the CSS here explicitly. - */ - if (IS_TOUCH_ONLY) { - var played = Math.min(100, Math.max(0, 100 * (currentTime / this.totalDuration))); - this.player.currentTime(currentTime); - this.addClass('played-range'); - document.documentElement.style.setProperty('--range-progress', "calc(".concat(played, "%)")); - } - } - }, { - key: "handleMouseUp", - value: function handleMouseUp(e) { - this.handleMouseDown(e); - } - }, { - key: "buildProgressBar", - value: function buildProgressBar() { - var _canvasTargetsRef$cur; - // Reset progress-bar for played range - this.removeClass('played-range'); - var canvasTargetsRef = this.canvasTargetsRef, - isMultiSourceRef = this.isMultiSourceRef, - player = this.player, - srcIndexRef = this.srcIndexRef, - totalDuration = this.totalDuration; - if (((_canvasTargetsRef$cur = canvasTargetsRef.current) === null || _canvasTargetsRef$cur === void 0 ? void 0 : _canvasTargetsRef$cur.length) > 0) { - var _canvasTargetsRef$cur2 = canvasTargetsRef.current[srcIndexRef.current], - altStart = _canvasTargetsRef$cur2.altStart, - start = _canvasTargetsRef$cur2.start, - end = _canvasTargetsRef$cur2.end, - duration = _canvasTargetsRef$cur2.duration; - var leftBlockEl = document.getElementById('left-block'); - var rightBlockEl = document.getElementById('right-block'); - if (!isMultiSourceRef.current) { - var leftBlock = start * 100 / duration; - var rightBlock = (duration - end) * 100 / duration; + // Concat the existing transcripts list and transcripts from the manifest and + // group them by canvasId + groupedTrs = groupByIndex(allTranscripts.concat(manifestTranscripts), 'canvasId', 'items'); + allTranscripts = groupedTrs; + } - // Set player.isClipped to use in the ended event to decide to advance to next - rightBlock > 0 ? player.isClipped = true : player.isClipped = false; - if (leftBlockEl) leftBlockEl.style.width = "".concat(leftBlock, "%"); - if (rightBlockEl) { - rightBlockEl.style.width = rightBlock + '%'; - rightBlockEl.style.left = "".concat(100 - rightBlock - leftBlock, "%"); - } - } else { - // Calculate offset of the duration of the current source - var leftOffset = Math.min(100, Math.max(0, 100 * (altStart / totalDuration))); - this.playProgress.el_.style.left = "".concat(leftOffset, "%"); - this.loadProgress.el_.style.left = "".concat(leftOffset, "%"); - // Add CSS class to mark the range from zero as played - this.addClass('played-range'); - document.documentElement.style.setProperty('--range-progress', "calc(".concat(leftOffset, "%)")); - } + // if manifest doesn't have canvases or + // supplementing annotations add original transcript from props + if (!(manifestTranscripts.length === 0 || manifestItems.length === 0)) { + _context2.next = 11; + break; + } + return _context2.abrupt("return", { + title: labelText, + filename: labelText, + url: url, + isMachineGen: isMachineGen, + id: "".concat(labelText, "-").concat(canvasId, "-").concat(index), + format: '' + }); + case 11: + return _context2.abrupt("return", null); + case 12: + case "end": + return _context2.stop(); + } + }, _callee2); + })); + return function (_x9, _x10) { + return _ref6.apply(this, arguments); + }; + }())); + case 3: + sanitizedItems = _context3.sent; + return _context3.abrupt("return", { + canvasId: canvasId, + items: sanitizedItems.filter(function (i) { + return i != null; + }) + }); + case 5: + case "end": + return _context3.stop(); + } + }, _callee3); + })); + return function (_x8) { + return _ref5.apply(this, arguments); + }; + }())); + case 9: + sanitizedTrs = _context4.sent; + // Group all the transcripts by canvasId one last time to eliminate duplicate canvasIds + newTranscripts = groupByIndex(allTranscripts.concat(sanitizedTrs), 'canvasId', 'items'); + return _context4.abrupt("return", newTranscripts); + case 12: + case "end": + return _context4.stop(); } + }, _callee4); + })); + return _sanitizeTranscripts.apply(this, arguments); + } + function groupByIndex(objectArray, indexKey, selectKey) { + return objectArray.reduce(function (acc, obj) { + var existing = acc.filter(function (a) { + return a[indexKey] == obj[indexKey]; + }); + if ((existing === null || existing === void 0 ? void 0 : existing.length) > 0) { + var current = existing[0]; + current[selectKey] = current[selectKey].concat(obj[selectKey]); + } else { + acc.push(obj); } - }, { - key: "convertToTime", - value: function convertToTime(e) { - var _e$nativeEvent$target, _this$totalDuration; - var eSrcElement = e.srcElement; - // When clicked on blocked time point - if (eSrcElement.classList.contains('block-stripes')) { - var _this$canvasTargetsRe = this.canvasTargetsRef.current[0], - altStart = _this$canvasTargetsRe.altStart, - end = _this$canvasTargetsRe.end, - _duration = _this$canvasTargetsRe.duration; - if (eSrcElement.id === 'right-block') { - // For right-block: place time tool-tip at the end of playable range - return { - currentTime: end, - offsetx: end / _duration * this.el().clientWidth - }; - } else { - // For left-block: place time tool-tip at the start of playable range - return { - currentTime: altStart, - offsetx: altStart / _duration * this.el().clientWidth - }; - } - } - var targetX = e.target.getBoundingClientRect().x; - var offsetx = e.nativeEvent != undefined ? e.nativeEvent.offsetX != undefined ? e.nativeEvent.offsetX // iOS and desktop events - : ((_e$nativeEvent$target = e.nativeEvent.targetTouches[0]) === null || _e$nativeEvent$target === void 0 ? void 0 : _e$nativeEvent$target.clientX) - targetX // Android event - : e.offsetX; // fallback in desktop browsers when nativeEvent is undefined - var currentTime; - var duration = (_this$totalDuration = this.totalDuration) !== null && _this$totalDuration !== void 0 ? _this$totalDuration : this.player.duration(); - if (offsetx && offsetx != undefined) { - if (this.isMultiSourceRef.current) { - /** - * Check if the mouse event occurred on the same src range. - * If so, adjust the offset to support altStart for the current src. - */ - var leftOffset = parseFloat(this.playProgress.el_.style.left) / 100 * this.el().clientWidth; - var elClassList = eSrcElement.classList; - var sameSrc = (elClassList === null || elClassList === void 0 ? void 0 : elClassList.length) > 0 ? elClassList.contains('vjs-play-progress') || elClassList.contains('vjs-load-progress') : true; - if (leftOffset > offsetx && sameSrc) { - offsetx = offsetx + leftOffset; - } - } - currentTime = offsetx / this.el().clientWidth * duration; - } - /** - * Parts of LoadProgress element is broken into segments as media loads, and displayed - * as separate div elements with `data-start` and `data-end` attributes respectively. - * When mouse event occurs on top of such element, add the segment start time to calculated - * current time from event. - */ - if (e.target.hasAttribute('data-start')) { - var _e$target$dataset = e.target.dataset, - start = _e$target$dataset.start; - _e$target$dataset._; - currentTime = currentTime + parseFloat(start); - offsetx = currentTime * this.el().clientWidth / this.totalDuration; - } - return { - currentTime: currentTime, - offsetx: offsetx - }; - } - }, { - key: "abortableTimeupdateHandler", - value: - /** - * A wrapper function around the time update interval, to cancel - * intermediate updates via the time interval when player is - * waiting to fetch stream - */ - function abortableTimeupdateHandler() { - var _this4 = this; - var player = this.player, - progressRef = this.progressRef; - player.on('waiting', function () { - if (IS_SAFARI && !IS_MOBILE) { - player.currentTime(progressRef.current); - } - cancelInterval(); - }); - var cancelInterval = function cancelInterval() { - if (internalInterval) { - clearInterval(internalInterval); - } - }; - var internalInterval = setInterval(function () { - _this4.timeUpdateHandler(); - }, 100); - } - }, { - key: "timeUpdateHandler", - value: - // Update progress bar with timeupdate in the player - function timeUpdateHandler() { - var _this5 = this; - var initTimeRef = this.initTimeRef, - player = this.player; - if (player.isDisposed() || player.ended() || player == null) { - return; - } - var curTime; - // Initially update progress from the prop passed from Ramp, - // this accounts for structured navigation when switching canvases - if (initTimeRef.current > 0 && player.currentTime() == 0) { - curTime = initTimeRef.current; - player.currentTime(initTimeRef.current); - } else { - curTime = player.currentTime(); - } - // Use debounced updates since, Safari desktop browsers need the extra - // update on 'seeked' event to timely update the progress bar. - if (IS_SAFARI && !IS_MOBILE && player.paused()) { - debounce_1(function () { - _this5.onTimeUpdate(curTime); - }); - } else { - this.onTimeUpdate(curTime); - } - this.setInitTime(0); - } - }, { - key: "onTimeUpdate", - value: function onTimeUpdate(curTime) { - // This state update caused weird lagging behaviors when using the iOS native - // video player. iOS player handles its own progress bar, so we can skip the - // update here only for video. - var iOS = this.player.hasClass("vjs-ios-native-fs"); - if (!(iOS && !this.player.audioOnlyMode_)) { - this.setProgress(curTime); - } - this.handleTimeUpdate(curTime); - } - }, { - key: "handleTimeUpdate", - value: - /** - * Update CSS for the input range's track while the media - * is playing - * @param {Number} curTime current time of the player - */ - function handleTimeUpdate(curTime) { - var _srcIndexRef$current; - var player = this.player, - el_ = this.el_, - canvasTargetsRef = this.canvasTargetsRef, - srcIndexRef = this.srcIndexRef; - - // Avoid null player instance when Video.js is getting initialized - if (!el_ || !player || !canvasTargetsRef.current) { - return; - } - var _canvasTargetsRef$cur3 = canvasTargetsRef.current[(_srcIndexRef$current = srcIndexRef.current) !== null && _srcIndexRef$current !== void 0 ? _srcIndexRef$current : 0], - start = _canvasTargetsRef$cur3.start, - end = _canvasTargetsRef$cur3.end, - duration = _canvasTargetsRef$cur3.duration; - - // Restrict access to the intended range in the media file - if (curTime < start) { - player.currentTime(start); - } - if (curTime >= end && !player.paused() && !player.isDisposed()) { - // Trigger ended event when playable range < duration of the - // full media. e.g. clipped playlist items - if (end < duration) { - player.trigger('ended'); - } - - // On the next play event set the time to start or a seeked time - // in between the 'ended' event and 'play' event - // Reference: https://github.com/videojs/video.js/blob/main/src/js/control-bar/play-toggle.js#L128 - player.one('play', function () { - var time = player.currentTime(); - if (time < end) { - player.currentTime(time); - } else { - player.currentTime(start); - } - }); - } - } - }]); - return VideoJSProgress; - }(SeekBar); - videojs__default["default"].registerComponent('VideoJSProgress', VideoJSProgress); - - function _createSuper$5(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$5(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } - function _isNativeReflectConstruct$5() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - var TimeDisplay = videojs__default["default"].getComponent('TimeDisplay'); + return acc; + }, []); + } /** - * Custom component to display the current time of the player - * @param {Object} props - * @param {Object} props.player VideoJS player instance - * @param {Object} props.options options passed into component - * options: { srcIndex, targets } + * Parse a given transcript file into a format the Transcript component + * can render on the UI. E.g.: text file -> returns null, so that the Google + * doc viewer is rendered, IIIF manifest -> extract and parse transcript data + * within the manifest. + * @param {String} url URL of the transcript file selected + * @param {Number} canvasIndex Current canvas rendered in the player + * @param {String} format transcript file format read from Annotation + * @returns {Object} Array of trancript data objects with download URL */ - var VideoJSCurrentTime = /*#__PURE__*/function (_TimeDisplay) { - _inherits(VideoJSCurrentTime, _TimeDisplay); - var _super = _createSuper$5(VideoJSCurrentTime); - function VideoJSCurrentTime(player, options) { - var _this; - _classCallCheck(this, VideoJSCurrentTime); - _this = _super.call(this, player, options); - _this.addClass('vjs-time-control vjs-current-time-display'); - _this.setAttribute('role', 'presentation'); - _this.player = player; - _this.options = options; - _this.initTimeRef = /*#__PURE__*/React__default["default"].createRef(); - _this.initTimeRef.current = options.currentTime; - _this.playerInterval; - _this.player.on('loadstart', function () { - _this.playerInterval = setInterval(function () { - _this.handleTimeUpdate(); - }, 100); - }); - _this.player.on('seeked', function () { - if (IS_SAFARI && !IS_MOBILE) { - _this.updateTextNode_(player.currentTime()); - } - }); + function parseTranscriptData(_x3, _x4, _x5) { + return _parseTranscriptData.apply(this, arguments); + } - // Update our timer after the user leaves full screen - _this.player.on('fullscreenchange', function () { - if (!player.isFullscreen()) { - _this.updateTextNode_(player.currentTime()); - } - }); - _this.player.on('dispose', function () { - clearInterval(_this.playerInterval); - }); - return _this; - } - _createClass(VideoJSCurrentTime, [{ - key: "buildCSSClass", - value: function buildCSSClass() { - return 'current-time'; - } - }, { - key: "setInitTime", - value: function setInitTime(t) { - this.initTimeRef.current = t; - } - }, { - key: "handleTimeUpdate", - value: function handleTimeUpdate() { - var player = this.player, - initTimeRef = this.initTimeRef; - var targets = player.targets, - srcIndex = player.srcIndex; - if (!player || player.isDisposed() || !targets) { - return; - } - var iOS = player.hasClass('vjs-ios-native-fs'); - var time; - // Update time from the given initial time if it is not zero - if (initTimeRef.current > 0 && player.currentTime() == 0) { - time = initTimeRef.current; - } else { - time = player.currentTime(); - } - var _targets = targets[srcIndex !== null && srcIndex !== void 0 ? srcIndex : 0], - start = _targets.start, - altStart = _targets.altStart; - if (altStart != start && srcIndex > 0) { - time = time + altStart; - } - // This state update caused weird lagging behaviors when using the iOS native - // video player. iOS player handles its own time, so we can skip the update here - // video items. - if (!(iOS && !player.audioOnlyMode_)) { - this.updateTextNode_(time); - } - this.setInitTime(0); - } - }]); - return VideoJSCurrentTime; - }(TimeDisplay); - videojs__default["default"].registerComponent('VideoJSCurrentTime', VideoJSCurrentTime); + /** + * Parse MS word documents into HTML markdown using mammoth.js + * https://www.npmjs.com/package/mammoth + * @param {Object} response response from the fetch request + * @returns {Array} html markdown for the word document contents + */ + function _parseTranscriptData() { + _parseTranscriptData = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee5(url, canvasIndex, format) { + var tData, tUrl, contentType, fileData, fromContentType, fromAnnotFormat, fileType, urlExt, filteredExt, textData, textLines, jsonData, json, parsedText, _parseTimedText, _tData, tType; + return regenerator.wrap(function _callee5$(_context5) { + while (1) switch (_context5.prev = _context5.next) { + case 0: + tData = []; + tUrl = url; // Validate given URL + if (!(url === undefined)) { + _context5.next = 4; + break; + } + return _context5.abrupt("return", { + tData: tData, + tUrl: tUrl, + tType: TRANSCRIPT_TYPES.invalid + }); + case 4: + contentType = null; + fileData = null; // get file type + _context5.next = 8; + return fetch(url).then(handleFetchErrors).then(function (response) { + contentType = response.headers.get('Content-Type'); + fileData = response; + })["catch"](function (error) { + console.error('transcript-parser -> parseTranscriptData() -> fetching transcript -> ', error); + }); + case 8: + if (!(contentType == null)) { + _context5.next = 10; + break; + } + return _context5.abrupt("return", { + tData: [], + tUrl: tUrl, + tType: TRANSCRIPT_TYPES.invalid + }); + case 10: + /* + Use the Annotation format in the IIIF Manifest, file extension, and the + Content-Type in headers of the fetch request to determine the file type. + These are checked with priority descending in the order of Annotation format, + Content-Type in headers, and file extension in the resource URI. + */ + fromContentType = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { + return tm.type.includes(contentType.split(';')[0]); + }); + fromAnnotFormat = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { + return tm.type.includes(format); + }); + fileType = ''; + if ((fromAnnotFormat === null || fromAnnotFormat === void 0 ? void 0 : fromAnnotFormat.length) > 0) { + fileType = fromAnnotFormat[0].ext; + } else if (fromContentType.length > 0) { + fileType = fromContentType[0].ext; + } else { + urlExt = url.split('.').reverse()[0]; // Only use this if it exists in the supported list of file types for the component + filteredExt = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { + return tm.ext === urlExt; + }); + fileType = filteredExt.length > 0 ? urlExt : ''; + } - function _createSuper$4(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$4(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } - function _isNativeReflectConstruct$4() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - var MenuButton = videojs__default["default"].getComponent('MenuButton'); - var MenuItem = videojs__default["default"].getComponent('MenuItem'); - var VideoJSFileDownload = /*#__PURE__*/function (_MenuButton) { - _inherits(VideoJSFileDownload, _MenuButton); - var _super = _createSuper$4(VideoJSFileDownload); - function VideoJSFileDownload(player, options) { - var _this; - _classCallCheck(this, VideoJSFileDownload); - _this = _super.call(this, player, options); - // Add SVG icon through CSS class - _this.addClass("vjs-file-download"); - _this.setAttribute('data-testid', 'videojs-file-download'); - // Use Video.js' stock SVG instead of setting it using CSS - _this.setIcon('file-download'); - return _this; - } - _createClass(VideoJSFileDownload, [{ - key: "createItems", - value: function createItems() { - var options_ = this.options_, - player_ = this.player_; - var files = options_.files; - if ((files === null || files === void 0 ? void 0 : files.length) > 0) { - return files.map(function (file) { - var item = new MenuItem(player_, { - label: file.label + // Return empty array to display an error message + if (!(canvasIndex === undefined)) { + _context5.next = 16; + break; + } + return _context5.abrupt("return", { + tData: tData, + tUrl: tUrl, + tType: TRANSCRIPT_TYPES.noTranscript }); - item.handleClick = function () { - fileDownload(file.id, file.filename, file.fileExt); - }; - return item; - }); - } else { - return []; + case 16: + _context5.t0 = fileType; + _context5.next = _context5.t0 === 'json' ? 19 : _context5.t0 === 'txt' ? 28 : _context5.t0 === 'srt' ? 39 : _context5.t0 === 'vtt' ? 39 : _context5.t0 === 'docx' ? 49 : 53; + break; + case 19: + _context5.next = 21; + return fileData.json(); + case 21: + jsonData = _context5.sent; + if (!((jsonData === null || jsonData === void 0 ? void 0 : jsonData.type) === 'Manifest')) { + _context5.next = 26; + break; + } + return _context5.abrupt("return", parseManifestTranscript(jsonData, url, canvasIndex)); + case 26: + json = parseJSONData(jsonData); + return _context5.abrupt("return", { + tData: json.tData, + tUrl: tUrl, + tType: json.tType, + tFileExt: fileType + }); + case 28: + _context5.next = 30; + return fileData.text(); + case 30: + textData = _context5.sent; + textLines = textData.split('\n'); + if (!(textLines.length == 0)) { + _context5.next = 36; + break; + } + return _context5.abrupt("return", { + tData: [], + tUrl: url, + tType: TRANSCRIPT_TYPES.noTranscript + }); + case 36: + parsedText = buildNonTimedText(textLines); + return _context5.abrupt("return", { + tData: parsedText, + tUrl: url, + tType: TRANSCRIPT_TYPES.plainText, + tFileExt: fileType + }); + case 38: + case 39: + _context5.next = 41; + return fileData.text(); + case 41: + textData = _context5.sent; + textLines = textData.split('\n'); + if (!(textLines.length == 0)) { + _context5.next = 47; + break; + } + return _context5.abrupt("return", { + tData: [], + tUrl: url, + tType: TRANSCRIPT_TYPES.noTranscript + }); + case 47: + _parseTimedText = parseTimedText(textData, fileType === 'srt'), _tData = _parseTimedText.tData, tType = _parseTimedText.tType; + return _context5.abrupt("return", { + tData: _tData, + tUrl: url, + tType: tType, + tFileExt: fileType + }); + case 49: + _context5.next = 51; + return parseWordFile(fileData); + case 51: + tData = _context5.sent; + return _context5.abrupt("return", { + tData: splitIntoElements(tData), + tUrl: url, + tType: TRANSCRIPT_TYPES.docx, + tFileExt: fileType + }); + case 53: + return _context5.abrupt("return", { + tData: [], + tUrl: url, + tType: TRANSCRIPT_TYPES.noSupport + }); + case 54: + case "end": + return _context5.stop(); } - } - }]); - return VideoJSFileDownload; - }(MenuButton); - videojs__default["default"].registerComponent('VideoJSFileDownload', VideoJSFileDownload); - - function _createSuper$3(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$3(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } - function _isNativeReflectConstruct$3() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - var Button$2 = videojs__default["default"].getComponent('Button'); - var VideoJSNextButton = /*#__PURE__*/function (_Button) { - _inherits(VideoJSNextButton, _Button); - var _super = _createSuper$3(VideoJSNextButton); - function VideoJSNextButton(player, options) { - var _this; - _classCallCheck(this, VideoJSNextButton); - _this = _super.call(this, player, options); - // Use Video.js' stock SVG instead of setting it using CSS - _this.setIcon('next-item'); - _this.addClass('vjs-play-control vjs-control'); - _this.setAttribute('data-testid', 'videojs-next-button'); - _this.controlText('Next'); - _this.options = options; - _this.player = player; - _this.cIndex = options.canvasIndex; - - // Handle player reload or source change events - _this.player.on('loadstart', function () { - _this.updateComponent(); - }); - return _this; + }, _callee5); + })); + return _parseTranscriptData.apply(this, arguments); + } + function parseWordFile(_x6) { + return _parseWordFile.apply(this, arguments); + } + /** + * Parse json data into Transcript component friendly + * format + * @param {Object} jsonData array of JSON objects + * @returns {Object} + */ + function _parseWordFile() { + _parseWordFile = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee6(response) { + var tData, data, arrayBuffer; + return regenerator.wrap(function _callee6$(_context6) { + while (1) switch (_context6.prev = _context6.next) { + case 0: + tData = null; + _context6.next = 3; + return response.blob(); + case 3: + data = _context6.sent; + arrayBuffer = new File([data], name, { + type: response.headers.get('content-type') + }); + _context6.next = 7; + return mammoth__default["default"].convertToHtml({ + arrayBuffer: arrayBuffer + }).then(function (result) { + tData = result.value; + })["catch"](function (err) { + console.error(err); + }); + case 7: + return _context6.abrupt("return", tData); + case 8: + case "end": + return _context6.stop(); + } + }, _callee6); + })); + return _parseWordFile.apply(this, arguments); + } + function parseJSONData(jsonData) { + if (jsonData.length == 0) { + return { + tData: [], + tType: TRANSCRIPT_TYPES.noTranscript + }; } - _createClass(VideoJSNextButton, [{ - key: "updateComponent", - value: function updateComponent() { - var player = this.player; - if (player && player != undefined) { - var _player$children; - // When canvasIndex property is not set in the player instance use dataset. - // This happens rarely, but when it does previous button cannot be used. - if (player.canvasIndex === undefined && ((_player$children = player.children()) === null || _player$children === void 0 ? void 0 : _player$children.length) > 0) { - this.cIndex = Number(player.children()[0].dataset.canvasindex); - } else { - this.cIndex = player.canvasIndex; + var tData = []; + var _iterator = _createForOfIteratorHelper$2(jsonData), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var jd = _step.value; + if (jd.speaker) { + var speaker = jd.speaker, + spans = jd.spans; + var _iterator2 = _createForOfIteratorHelper$2(spans), + _step2; + try { + for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { + var span = _step2.value; + span.speaker = speaker; + tData.push(span); + } + } catch (err) { + _iterator2.e(err); + } finally { + _iterator2.f(); + } + } else { + var _iterator3 = _createForOfIteratorHelper$2(jd.spans), + _step3; + try { + for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { + var _span = _step3.value; + _span.format = 'text/plain'; + _span.tag = TRANSCRIPT_CUE_TYPES.timedCue; + tData.push(_span); + } + } catch (err) { + _iterator3.e(err); + } finally { + _iterator3.f(); } - } - if (this.options.playerFocusElement === 'nextBtn') { - this.el().focus(); - } - } - }, { - key: "handleClick", - value: function handleClick() { - this.handleNextClick(false); - } - }, { - key: "handleKeyDown", - value: function handleKeyDown(e) { - if (e.which === 32 || e.which === 13) { - e.stopPropagation(); - this.handleNextClick(true); - } - } - }, { - key: "handleNextClick", - value: function handleNextClick(isKeyDown) { - if (this.cIndex != this.options.lastCanvasIndex) { - this.options.switchPlayer(this.cIndex + 1, true, isKeyDown ? 'nextBtn' : ''); } } - }]); - return VideoJSNextButton; - }(Button$2); - videojs__default["default"].registerComponent('VideoJSNextButton', VideoJSNextButton); - - function _createSuper$2(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$2(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } - function _isNativeReflectConstruct$2() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - var Button$1 = videojs__default["default"].getComponent('Button'); - var VideoJSPreviousButton = /*#__PURE__*/function (_Button) { - _inherits(VideoJSPreviousButton, _Button); - var _super = _createSuper$2(VideoJSPreviousButton); - function VideoJSPreviousButton(player, options) { - var _this; - _classCallCheck(this, VideoJSPreviousButton); - _this = _super.call(this, player, options); - // Use Video.js' stock SVG instead of setting it using CSS - _this.setIcon('previous-item'); - _this.addClass('vjs-play-control vjs-control'); - _this.setAttribute('data-testid', 'videojs-previous-button'); - _this.options = options; - _this.player = player; - _this.cIndex = options.canvasIndex; + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); + } + return { + tData: tData, + tType: TRANSCRIPT_TYPES.timedText + }; + } - // Handle player reload or source change events - _this.player.on('loadstart', function () { - _this.updateComponent(); - }); - return _this; + /* Parsing annotations when transcript data is fed from a IIIF manifest */ + /** + * Parse a IIIF manifest and extracts the transcript data. + * IIIF manifests can present transcript data in a couple of different ways. + * 1. Using 'rendering' prop to link to an external file + * a. when the external file contains only text + * b. when the external file contains annotations + * 2. Using IIIF 'annotations' within the manifest + * @param {Object} manifest IIIF manifest data + * @param {String} manifestURL IIIF manifest URL + * @param {Number} canvasIndex Current canvas index + * @returns {Object} object with the structure; + * { tData: transcript data, tUrl: file url } + */ + function parseManifestTranscript(manifest, manifestURL, canvasIndex) { + var _manifest$items; + var tData = []; + var tUrl = manifestURL; + var isExternalAnnotation = false; + var annotations = []; + if (manifest.annotations) { + annotations = getAnnotations(manifest.annotations, 'supplementing'); + } else if (((_manifest$items = manifest.items) === null || _manifest$items === void 0 ? void 0 : _manifest$items.length) > 0) { + var _manifest$items$canva; + annotations = getAnnotations((_manifest$items$canva = manifest.items[canvasIndex]) === null || _manifest$items$canva === void 0 ? void 0 : _manifest$items$canva.annotations, 'supplementing'); } - _createClass(VideoJSPreviousButton, [{ - key: "updateComponent", - value: function updateComponent() { - var player = this.player; - if (player && player != undefined) { - var _player$children; - // When canvasIndex property is not set in the player instance use dataset. - // This happens rarely, but when it does previous button cannot be used. - if (player.canvasIndex === undefined && ((_player$children = player.children()) === null || _player$children === void 0 ? void 0 : _player$children.length) > 0) { - this.cIndex = Number(player.children()[0].dataset.canvasindex); - } else { - this.cIndex = player.canvasIndex; - } - } - this.controlText(this.cIndex == 0 ? 'Replay' : 'Previous'); - if (this.options.playerFocusElement === 'previousBtn') { - this.el().focus(); - } - } - }, { - key: "handleClick", - value: function handleClick() { - this.handlePreviousClick(false); - } - }, { - key: "handleKeyDown", - value: function handleKeyDown(e) { - if (e.which === 32 || e.which === 13) { - e.stopPropagation(); - this.handlePreviousClick(true); - } - } - }, { - key: "handlePreviousClick", - value: function handlePreviousClick(isKeyDown) { - if (this.cIndex > -1 && this.cIndex != 0) { - this.options.switchPlayer(this.cIndex - 1, true, isKeyDown ? 'previousBtn' : ''); - } else if (this.cIndex == 0) { - this.player.currentTime(0); - } + + // determine whether annotations point to an external resource or + // a list of transcript fragments + if (annotations.length > 0) { + var annotation = annotations[0]; + var tType = annotation.body.type; + if (tType == 'TextualBody') { + isExternalAnnotation = false; + } else { + isExternalAnnotation = true; } - }]); - return VideoJSPreviousButton; - }(Button$1); - videojs__default["default"].registerComponent('VideoJSPreviousButton', VideoJSPreviousButton); + } else { + return { + tData: [], + tUrl: tUrl, + tType: TRANSCRIPT_TYPES.noTranscript + }; + } + if (isExternalAnnotation) { + var _annotation = annotations[0]; + return parseExternalAnnotations(_annotation); + } else { + tData = createTData(annotations); + return { + tData: tData, + tUrl: tUrl, + tType: TRANSCRIPT_TYPES.timedText, + tFileExt: 'json' + }; + } + } - function _createSuper$1(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$1(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } - function _isNativeReflectConstruct$1() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - var vjsComponent = videojs__default["default"].getComponent('Component'); - var VideoJSTitleLink = /*#__PURE__*/function (_vjsComponent) { - _inherits(VideoJSTitleLink, _vjsComponent); - var _super = _createSuper$1(VideoJSTitleLink); - function VideoJSTitleLink(player, options) { - var _this; - _classCallCheck(this, VideoJSTitleLink); - _this = _super.call(this, player, options); - _this.setAttribute('data-testid', 'videojs-title-link'); - _this.addClass('vjs-title-bar'); - _this.options = options; - _this.player = player; - - // Handle player reload or source change events - _this.player.on('loadstart', function () { - _this.updateComponent(); - }); - return _this; - } - _createClass(VideoJSTitleLink, [{ - key: "updateComponent", - value: function updateComponent() { - var player = this.player; - if (player && player != undefined && player.canvasLink) { - var _player$canvasLink = player.canvasLink, - label = _player$canvasLink.label, - id = _player$canvasLink.id; - var title = label; - var href = null; - /** - * Avalon canvas ids are of the form 'http://host.edu/media_objects/#mo_id/manifest/canvas/#section_id`. - * Accessible url is 'http://host.edu/media_objects/#mo_id/section/#section_id' so we convert the canvas - * id for avalon manifest, but must assume other implementers will have the id as an actionable link. - */ - if (id.includes('manifest/canvas')) { - href = id.replace('manifest/canvas', 'section'); - } else { - href = id; - } - var link = videojs__default["default"].dom.createEl('a', { - className: 'vjs-title-link', - href: href, - target: '_blank', - rel: 'noreferrer noopener', - innerHTML: title - }); - if (this.el().hasChildNodes()) { - this.el().replaceChildren(link); - } else { - this.el().appendChild(link); - } + /** + * Parse annotation linking to external resources like WebVTT, SRT, Text, and + * AnnotationPage .json files + * @param {Annotation} annotation Annotation from the manifest + * @returns {Object} object with the structure { tData: [], tUrl: '', tType: '' } + */ + function parseExternalAnnotations(_x7) { + return _parseExternalAnnotations.apply(this, arguments); + } + /** + * Converts Annotation to the common format that the + * transcripts component expects + * @param {Array} annotations array of Annotations + * @returns {Array} array of JSON objects + * Structure of the JSON object is as follows; + * { + * begin: 0, + * end: 60, + * text: 'Transcript text', + * format: 'text/plain', + * } + */ + function _parseExternalAnnotations() { + _parseExternalAnnotations = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee7(annotation) { + var tData, type, tBody, tUrl, tType, tFormat, tFileExt; + return regenerator.wrap(function _callee7$(_context7) { + while (1) switch (_context7.prev = _context7.next) { + case 0: + tData = []; + type = ''; + tBody = annotation.body; + tUrl = tBody.id; + tType = tBody.type; + tFormat = tBody.format; + tFileExt = ''; + /** When external file contains text data */ + if (!(tType === 'Text')) { + _context7.next = 12; + break; + } + _context7.next = 10; + return fetch(tUrl).then(handleFetchErrors).then(function (response) { + return response.text(); + }).then(function (data) { + if (TRANSCRIPT_MIME_TYPES.webvtt.includes(tFormat) || TRANSCRIPT_MIME_TYPES.srt.includes(tFormat)) { + var parsed = parseTimedText(data, TRANSCRIPT_MIME_TYPES.srt.includes(tFormat)); + tData = parsed.tData; + type = parsed.tType; + tFileExt = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { + return tm.type.includes(tFormat); + })[0].ext; + } else { + var textLines = data.split('\n'); + tData = buildNonTimedText(textLines); + type = TRANSCRIPT_TYPES.plainText; + tFileExt = 'txt'; + } + })["catch"](function (error) { + console.error('transcript-parser -> parseExternalAnnotations() -> fetching external transcript -> ', error); + throw error; + }); + case 10: + _context7.next = 15; + break; + case 12: + if (!(tType === 'AnnotationPage')) { + _context7.next = 15; + break; + } + _context7.next = 15; + return fetch(tUrl).then(handleFetchErrors).then(function (response) { + return response.json(); + }).then(function (data) { + var annotations = getAnnotations([data], 'supplementing'); + tData = createTData(annotations); + type = TRANSCRIPT_TYPES.timedText; + tFileExt = 'json'; + })["catch"](function (error) { + console.error('transcript-parser -> parseExternalAnnotations() -> fetching annotations -> ', error); + throw error; + }); + case 15: + return _context7.abrupt("return", { + tData: tData, + tUrl: tUrl, + tType: type, + tFileExt: tFileExt + }); + case 16: + case "end": + return _context7.stop(); } + }, _callee7); + })); + return _parseExternalAnnotations.apply(this, arguments); + } + function createTData(annotations) { + var tData = []; + annotations.map(function (a) { + if (a.id != null) { + var tBody = a.body; + var _getMediaFragment = getMediaFragment(a.target), + start = _getMediaFragment.start, + end = _getMediaFragment.end; + tData.push({ + text: tBody.value, + format: tBody.format, + begin: parseFloat(start), + end: parseFloat(end), + tag: TRANSCRIPT_CUE_TYPES.timedCue + }); } - }]); - return VideoJSTitleLink; - }(vjsComponent); - vjsComponent.registerComponent('VideoJSTitleLink', VideoJSTitleLink); - - function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } - function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - - // SVG icons for zoom-in and zoom-out icons as strings - var zoomOutIconSVG = "\n\n \n \n \n \n \n \n"; - var zoomInIconSVG = "\n\n \n \n \n \n \n \n"; - - // Function to inject SVGs into the DOM - function injectSVGIcons() { - var svgContainer = document.createElement('div'); - svgContainer.style.display = 'none'; - svgContainer.innerHTML = "".concat(zoomOutIconSVG).concat(zoomInIconSVG, ""); - document.body.appendChild(svgContainer); + }); + return tData; } - // Call the function to inject SVG icons - injectSVGIcons(); - var Button = videojs__default["default"].getComponent('Button'); - /** - * Custom VideoJS component for displaying track view when - * there are tracks/structure timespans in the current Canvas - * @param {Object} options - * @param {Number} options.trackScrubberRef React ref to track scrubber element - * @param {Number} options.timeToolRef React ref to time tooltip element - * @param {Boolean} options.isPlaylist flag to indicate a playlist Manifest or not + * Parsing transcript data from a given file with timed text + * @param {Object} fileData content in the transcript file + * @param {Boolean} isSRT given transcript file is an SRT + * @returns {Array} array of JSON objects of the following + * structure; + * { + * begin: '00:00:00.000', + * end: '00:01:00.000', + * text: 'Transcript text sample' + * tag: NOTE || TIMED_CUE + * } */ - var VideoJSTrackScrubber = /*#__PURE__*/function (_Button) { - _inherits(VideoJSTrackScrubber, _Button); - var _super = _createSuper(VideoJSTrackScrubber); - function VideoJSTrackScrubber(player, options) { - var _this; - _classCallCheck(this, VideoJSTrackScrubber); - _this = _super.call(this, player, options); - _this.setAttribute('data-testid', 'videojs-track-scrubber-button'); - _this.addClass('vjs-button vjs-track-scrubber'); - _this.controlText('Toggle track scrubber'); - _this.el().innerHTML = "\n \n \n "; - _this.options = options; - _this.player = player; - _this.playerInterval; - _this.zoomedOutRef = /*#__PURE__*/React__default["default"].createRef(); - _this.currentTrackRef = /*#__PURE__*/React__default["default"].createRef(); + function parseTimedText(fileData) { + var isSRT = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + var tData = []; + var noteLines = []; - // Attach interval on first load for time updates - _this.player.on('ready', function () { - if (_this.options.trackScrubberRef.current) { - _this.playerInterval = setInterval(function () { - _this.handleTimeUpdate(); - }, 100); - _this.attachListeners(); - } - }); + // split file content into lines + var lines = fileData.split('\n'); - /* - When player is fully built and the trackScrubber element is initialized, - call method to mount React component. - */ - _this.player.on('loadstart', function () { - if (_this.options.trackScrubberRef.current) { - _this.updateComponent(); - if (!_this.playerInterval) { - _this.playerInterval = setInterval(function () { - _this.handleTimeUpdate(); - }, 100); - } - } - }); + // For SRT files all of the file content is considered as cues + var cueLines = lines; + if (!isSRT) { + var _validateWebVTT = validateWebVTT(lines), + valid = _validateWebVTT.valid, + cue_lines = _validateWebVTT.cue_lines, + notes = _validateWebVTT.notes; + if (!valid) { + console.error('Invalid WebVTT file'); + return { + tData: [], + tType: TRANSCRIPT_TYPES.invalidVTT + }; + } + cueLines = cue_lines; + noteLines = notes; + } + var groups = groupTimedTextLines(cueLines); - // Hide track scrubber if it is displayed when player is going fullscreen - _this.player.on('fullscreenchange', function () { - if (_this.player.isFullscreen() && !_this.zoomedOutRef.current) { - var tempZoom = _this.zoomedOutRef.current; - _this.setZoomedOut(!tempZoom); - } - }); + // Add back the NOTE(s) in the header block + groups.unshift.apply(groups, _toConsumableArray(noteLines)); + var hasInvalidTimestamp = false; + for (var i = 0; i < groups.length;) { + var line = parseTimedTextLine(groups[i], isSRT); + if (!line) { + hasInvalidTimestamp || (hasInvalidTimestamp = true); + break; + } else { + tData.push(line); + i++; + } + } + return { + tData: hasInvalidTimestamp ? null : tData, + tType: hasInvalidTimestamp ? TRANSCRIPT_TYPES.invalidTimestamp : TRANSCRIPT_TYPES.timedText + }; + } - // Clean up interval when player is disposed - _this.player.on('dispose', function () { - clearInterval(_this.playerInterval); - }); - return _this; + /** + * Validate WebVTT file with its header content + * @param {Array} lines WebVTT file content split into lines + * @returns {Boolean} + */ + function validateWebVTT(lines) { + var firstLine = lines.shift().trim(); + if ((firstLine === null || firstLine === void 0 ? void 0 : firstLine.length) == 6 && firstLine === 'WEBVTT') { + var _validateWebVTTHeader = validateWebVTTHeaders(lines), + valid = _validateWebVTTHeader.valid, + cue_lines = _validateWebVTTHeader.cue_lines, + notes = _validateWebVTTHeader.notes; + return { + valid: valid, + cue_lines: cue_lines, + notes: notes + }; + } else { + return { + valid: false, + cue_lines: [], + notes: [] + }; } - _createClass(VideoJSTrackScrubber, [{ - key: "setCurrentTrack", - value: function setCurrentTrack(t) { - this.currentTrackRef.current = t; - } - }, { - key: "setZoomedOut", - value: function setZoomedOut(z) { - this.zoomedOutRef.current = z; - if (z) { - this.options.trackScrubberRef.current.classList.add('hidden'); - this.el().innerHTML = "\n \n \n "; - } else { - this.options.trackScrubberRef.current.classList.remove('hidden'); - this.el().innerHTML = "\n \n \n "; + } + + /** + * Validate the text between 'WEBVTT' at the start and start of + * VTT cues. It looks for REGION and STYLE blocks and skips over these + * blocks. This doesn't validate the content within these blocks. + * When there's text in the header not followed by the keywords REGION and + * STYLE the WebVTT file is marked invalid. + * @param {Array} lines WebVTT file content split into lines + * @returns + */ + function validateWebVTTHeaders(lines) { + var endOfHeadersIndex = 0; + var firstCueIndex = 0; + var hasTextBeforeCues = false; + var notesInHeader = []; + + // Remove line numbers for vtt cues + lines = lines.filter(function (l) { + return Number(l) ? false : true; + }); + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + // Skip REGION and STYLE blocks as these are related to displaying cues as overlays + if (/^REGION$/.test(line.toUpperCase()) || /^STYLE$/.test(line.toUpperCase())) { + // Increment until an empty line is encountered within the header block + i++; + while (i < lines.length && (!lines[i] == '\r' || !lines[i] == '\n' || !lines[i] == '\r\n')) { + i++; } + endOfHeadersIndex = i; } - }, { - key: "attachListeners", - value: function attachListeners() { - var _this2 = this; - var trackScrubberRef = this.options.trackScrubberRef; - this.updateComponent(); - if (trackScrubberRef.current) { - // Initialize the track scrubber's current time and duration - this.populateTrackScrubber(); - this.updateTrackScrubberProgressBar(); - var pointerDragged = false; - // Attach mouse pointer events to track scrubber progress bar - var _trackScrubberRef$cur = _slicedToArray(trackScrubberRef.current.children, 3); - _trackScrubberRef$cur[0]; - var progressBar = _trackScrubberRef$cur[1]; - _trackScrubberRef$cur[2]; - progressBar.addEventListener('mouseenter', function (e) { - _this2.handleMouseMove(e); - }); - /* - Using pointerup, pointermove, pointerdown events instead of - mouseup, mousemove, mousedown events to make it work with both - mouse pointer and touch events - */ - progressBar.addEventListener('pointerup', function (e) { - if (pointerDragged) { - _this2.handleSetProgress(e); - } - }); - progressBar.addEventListener('pointermove', function (e) { - _this2.handleMouseMove(e); - pointerDragged = true; - }); - progressBar.addEventListener('pointerdown', function (e) { - // Only handle left click event - if (e.which === 1) { - _this2.handleSetProgress(e); - pointerDragged = false; - } - }); + // Gather comments presented as NOTE(s) in the header block to be displayed as transcript + else if (/^NOTE$/.test(line.toUpperCase())) { + var noteText = line; + i++; + // Increment until an empty line is encountered within the NOTE block + while (i < lines.length && (!lines[i] == '\r' || !lines[i] == '\n' || !lines[i] == '\r\n')) { + noteText = "".concat(noteText, "
      ").concat(lines[i].trim()); + i++; } + notesInHeader.push({ + times: '', + line: noteText, + tag: TRANSCRIPT_CUE_TYPES.note + }); } - }, { - key: "updateComponent", - value: function updateComponent() { - // Reset refs to initial value - this.zoomedOutRef.current = true; - this.currentTrackRef.current = {}; - } - - /** - * Keydown event handler for the track button on the player controls, - * when using keyboard navigation - * @param {Event} e keydown event - */ - }, { - key: "handleKeyDown", - value: function handleKeyDown(e) { - if (e.which === 32 || e.which === 13) { - e.preventDefault(); - this.handleTrackScrubberClick(); - e.stopPropagation(); - } + // Terminate validation once the first cue is reached + else if (line.includes('-->')) { + // Break the loop when it reaches the first vtt cue + firstCueIndex = i; + break; } - }, { - key: "handleClick", - value: function handleClick() { - this.handleTrackScrubberClick(); + // Flag to check for invalid text before cue lines + else if (typeof line === 'string' && line.trim().length != 0) { + hasTextBeforeCues = true; } + } - /** - * Click event handler for the track button on the player controls - */ - }, { - key: "handleTrackScrubberClick", - value: function handleTrackScrubberClick() { - var currentTrackRef = this.currentTrackRef, - player = this.player, - options = this.options; - // When player is not fully loaded on the page don't show the track scrubber - if (!options.trackScrubberRef.current || !currentTrackRef.current) return; + // Return the cues and comments in the header block when the given WebVTT is valid + if (firstCueIndex > endOfHeadersIndex && !hasTextBeforeCues) { + return { + valid: true, + cue_lines: lines.slice(firstCueIndex), + notes: notesInHeader + }; + } else { + return { + valid: false + }; + } + } - // If player is fullscreen exit before displaying track scrubber - if (player.isFullscreen()) { - player.exitFullscreen(); + /** + * Group multi line transcript text values alongside the relevant + * timestamp values. E.g. converts, + * [ + * "00:00:00.000 --> 00:01:00.000", "Transcript", " from multiple lines", + * "00:03:00.000 --> 00:04:00.000", "Next transcript text", + * "NOTE This is a comment" + * ] + * into + * [ + * { times: "00:00:00.000 --> 00:01:00.000", line: "Transcript from multiple lines", tag: "TIMED_CUE" }, + * { times: "00:03:00.000 --> 00:04:00.000", line: "Next transcript text", tag: "TIMED_CUE" }, + * { times: "", line: "NOTE This is a comment", tag: "NOTE" } + * ] + * @param {Array} lines array of lines in the WebVTT file + * @returns {Array} + */ + function groupTimedTextLines(lines) { + var groups = []; + var i; + for (i = 0; i < lines.length; i++) { + var line = lines[i]; + var t = {}; + if (line.includes('-->') || /^NOTE/.test(line)) { + var isNote = /^NOTE/.test(line); + t.times = isNote ? "" : line; + t.tag = isNote ? TRANSCRIPT_CUE_TYPES.note : TRANSCRIPT_CUE_TYPES.timedCue; + // Make sure there is a single space separating NOTE from the comment for single or multi-line comments + t.line = isNote ? line.replace(/^NOTE\s*/, 'NOTE ') : ''; + i++; + + // Increment until an empty line is encountered marking the end of the block + while (i < lines.length && !(lines[i] == '\r' || lines[i] == '\n' || lines[i] == '\r\n' || lines[i] == '')) { + t.line += lines[i].endsWith('-') ? lines[i] : lines[i].replace(/\s*$/, ' '); + i++; } - var tempZoom = this.zoomedOutRef.current; - this.setZoomedOut(!tempZoom); + t.line = t.line.trimEnd(); + groups.push(t); } + } + return groups; + } - /** - * Event handler for VideoJS player instance's 'timeupdate' event, which - * updates the track scrubber from player state. - */ - }, { - key: "handleTimeUpdate", - value: function handleTimeUpdate() { - var _player$markers$getMa; - var player = this.player, - options = this.options, - zoomedOutRef = this.zoomedOutRef; - // Hide track-scrubber for inaccessible item if it is open - if (player.canvasIsEmpty && !zoomedOutRef.current) { - this.setZoomedOut(true); - } - if (player.isDisposed() || player.ended()) return; - /* - Get the current track from the player.markers created from the structure timespans. - In playlists, markers are timepoint information representing highlighting annotations, - therefore omit reading markers information for track scrubber in playlist contexts. - */ - var playerCurrentTime = player.currentTime(); - if (player.markers && typeof player.markers !== 'function' && typeof player.markers.getMarkers === 'function' && ((_player$markers$getMa = player.markers.getMarkers()) === null || _player$markers$getMa === void 0 ? void 0 : _player$markers$getMa.length) > 0 && !options.isPlaylist) { - this.readPlayerMarkers(); - } else { - var _player$playableDurat, _player$altStart; - this.setCurrentTrack({ - duration: (_player$playableDurat = player.playableDuration) !== null && _player$playableDurat !== void 0 ? _player$playableDurat : player.duration(), - time: (_player$altStart = player.altStart) !== null && _player$altStart !== void 0 ? _player$altStart : 0, - key: '', - text: 'Complete media file' - }); - playerCurrentTime = player.srcIndex && player.srcIndex > 0 ? playerCurrentTime + player.altStart : playerCurrentTime; - } - this.updateTrackScrubberProgressBar(playerCurrentTime); - } - /** - * Calculate the progress and current time within the track and - * update them accordingly when the player's 'timeupdate' event fires. - * @param {Number} currentTime player's current time - */ - }, { - key: "updateTrackScrubberProgressBar", - value: function updateTrackScrubberProgressBar() { - var currentTime = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; - var player = this.player, - currentTrackRef = this.currentTrackRef; - // Handle Safari which emits the timeupdate event really quickly - if (!currentTrackRef.current) { - if (player.markers && typeof player.markers.getMarkers === 'function') { - this.readPlayerMarkers(); - } - } - var altStart = player.altStart, - srcIndex = player.srcIndex; - // Calculate corresponding time and played percentage values within track - var trackoffset = srcIndex > 0 ? currentTime - currentTrackRef.current.time + altStart : currentTime - currentTrackRef.current.time; - var trackpercent = Math.min(100, Math.max(0, 100 * trackoffset / currentTrackRef.current.duration)); - this.populateTrackScrubber(trackoffset, trackpercent); - } - }, { - key: "populateTrackScrubber", - value: - /** - * Update the track scrubber's current time, duration and played percentage - * when it is visible in UI. - * @param {Number} currentTime current time corresponding to the track - * @param {Number} playedPercentage elapsed time percentage of the track duration - */ - function populateTrackScrubber() { - var currentTime = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; - var playedPercentage = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; - var trackScrubberRef = this.options.trackScrubberRef; - if (!trackScrubberRef.current) { - return; + /** + * Create a JSON object from the transcript data + * @param {Object} obj + * @param {String} obj.times string with time information + * @param {String} obj.line string with transcript text + * @returns {Object} of the format; + * { + * begin: 0, + * end: 60, + * text: 'Transcript text sample', + * tag: NOTE || TIMED_CUE + * } + */ + function parseTimedTextLine(_ref, isSRT) { + var times = _ref.times, + line = _ref.line, + tag = _ref.tag; + var timestampRegex; + if (isSRT) { + // SRT allows using comma for milliseconds while WebVTT does not + timestampRegex = SRT_TIMESTAMP_REGEX; + } else { + timestampRegex = VTT_TIMESTAMP_REGEX; + } + switch (tag) { + case TRANSCRIPT_CUE_TYPES.note: + return { + begin: 0, + end: 0, + text: line, + tag: tag + }; + case TRANSCRIPT_CUE_TYPES.timedCue: + var _times$split = times.split(' --> '), + _times$split2 = _slicedToArray(_times$split, 2), + start = _times$split2[0], + end = _times$split2[1]; + // FIXME:: remove any styles for now, refine this + end = end.split(' ')[0]; + if (!start.match(timestampRegex) || !end.match(timestampRegex)) { + console.error('Invalid timestamp in line with text; ', line); + return null; } - var _trackScrubberRef$cur2 = _slicedToArray(trackScrubberRef.current.children, 3), - currentTimeDisplay = _trackScrubberRef$cur2[0]; - _trackScrubberRef$cur2[1]; - var durationDisplay = _trackScrubberRef$cur2[2]; - - // Set the elapsed time percentage in the progress bar of track scrubber - document.documentElement.style.setProperty('--range-scrubber', "calc(".concat(playedPercentage, "%)")); + return { + begin: timeToS(start), + end: timeToS(end), + text: line, + tag: tag + }; + default: + return null; + } + } - // Update the track duration - durationDisplay.innerHTML = timeToHHmmss(this.currentTrackRef.current.duration); - // Update current time elapsed within the current track - var cleanTime = !isNaN(currentTime) && currentTime > 0 ? currentTime : 0; - currentTimeDisplay.innerHTML = timeToHHmmss(cleanTime); - } - }, { - key: "readPlayerMarkers", - value: function readPlayerMarkers() { - var tracks = this.player.markers.getMarkers().filter(function (m) { - return m["class"] == 'ramp--track-marker--fragment'; + /** + * Parse the content search response from the search service, and then use it to calculate + * number of search hits for each transcripts, and create a list of matched transcript + * lines for the search in the current transcript + * @param {Object} response JSON response from content search API + * @param {String} query search query from transcript search + * @param {Array} trancripts content of the displayed transcript with ids + * @param {String} selectedTranscript url of the selected transcript + * @returns a list of matched transcript lines for the current search + */ + var parseContentSearchResponse = function parseContentSearchResponse(response, query, trancripts, selectedTranscript) { + var _response$items; + if (!response || response === undefined) return []; + var hitCounts = []; + var searchHits = []; + if (((_response$items = response.items) === null || _response$items === void 0 ? void 0 : _response$items.length) > 0) { + var items = response.items; + items.map(function (item) { + var anno = new manifesto_js.Annotation(item); + // Exclude annotations without supplementing motivation + if (anno.getMotivation() != 'supplementing') return; + var target = anno.getTarget(); + var targetURI = getCanvasId(target); + var value = anno.getBody()[0].getProperty('value'); + var hitCount = getHitCountForCue(value, query, true); + searchHits.push({ + target: target, + targetURI: targetURI, + value: value, + hitCount: hitCount }); - if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0) { - this.setCurrentTrack(tracks[0]); - } - } - }, { - key: "handleMouseMove", - value: - /** - * Event handler for mouseenter and mousemove pointer events on the - * the track scrubber. This sets the time tooltip value and its offset - * position in the UI. - * @param {Event} e pointer event for user interaction - */ - function handleMouseMove(e) { - var timeToolRef = this.options.timeToolRef; - if (!timeToolRef.current) { - return; - } - var time = this.getTrackTime(e); - - // When hovering over the border of the track scrubber, convertTime() returns infinity, - // since e.target.clientWidth is zero. Use this value to not show the tooltip when this - // occurs. - if (isFinite(time)) { - // Calculate the horizontal position of the time tooltip using the event's offsetX property - var offset = e.offsetX - timeToolRef.current.offsetWidth / 2; // deduct 0.5 x width of tooltip element - timeToolRef.current.style.left = offset + 'px'; + }); + } + // Group search responses by transcript + var allSearchHits = groupBy(searchHits, 'targetURI'); - // Set text in the tooltip as the time relevant to the pointer event's position - timeToolRef.current.innerHTML = timeToHHmmss(time); - } - } - }, { - key: "handleSetProgress", - value: - /** - * Event handler for mousedown event on the track scrubber. This sets the - * progress percentage within track scrubber and update the player's current time - * when user clicks on a point within the track scrubber. - * @param {Event} e pointer event for user interaction - */ - function handleSetProgress(e) { - var currentTrackRef = this.currentTrackRef, - player = this.player; - if (!currentTrackRef.current) { - return; - } - var trackoffset = this.getTrackTime(e); - if (trackoffset != undefined) { - // Calculate percentage of the progress based on the pointer position's - // time and duration of the track - var trackpercent = Math.min(100, Math.max(0, 100 * (trackoffset / currentTrackRef.current.duration))); + // Calculate search hit count for each transcript in the Canvas + for (var _i = 0, _Object$entries = Object.entries(allSearchHits); _i < _Object$entries.length; _i++) { + var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), + key = _Object$entries$_i[0], + value = _Object$entries$_i[1]; + hitCounts.push({ + transcriptURL: key, + numberOfHits: value.reduce(function (acc, a) { + return acc + a.hitCount; + }, 0) + }); + } - // Set the elapsed time in the scrubber progress bar - document.documentElement.style.setProperty('--range-scrubber', "calc(".concat(trackpercent, "%)")); + // Get all the matching transcript lines with the query in the current transcript + var matchedTranscriptLines = getMatchedTranscriptLines(allSearchHits[selectedTranscript], query, trancripts); + return { + matchedTranscriptLines: matchedTranscriptLines, + hitCounts: hitCounts, + allSearchHits: allSearchHits + }; + }; - // Set player's current time with respective to the altStart for clipped items - var playerCurrentTime = player.isClipped ? trackoffset + currentTrackRef.current.time : trackoffset; - player.currentTime(playerCurrentTime); - } - } - }, { - key: "getTrackTime", - value: - /** - * Convert pointer position on track scrubber to a time value - * @param {Event} e pointer event for user interaction - * @returns {Number} time corresponding to the pointer position - */ - function getTrackTime(e) { - var currentTrackRef = this.currentTrackRef; - if (!currentTrackRef.current) { - return; + /** + * Create a list matched transcript lines for the current search for the displayed transcript + * @param {Array} searchHits a list of matched transcript lines with ids from the current transcript + * @param {String} query search query + * @param {Array} transcripts list of all the transcript lines from the current transcript + * @returns a list of matched transcrip lines in the current transcript + */ + var getMatchedTranscriptLines = function getMatchedTranscriptLines(searchHits, query, transcripts) { + var qStr = query.trim().toLocaleLowerCase(); + var transcriptLines = []; + if (searchHits === undefined) return; + var traversedIds = []; + searchHits.map(function (item, index) { + var target = item.target, + value = item.value; + // Read time offsets and text of the search hit + var timeRange = getMediaFragment(target); + + // Replace all HTML tags + var mappedText = value.replace(/<\/?[^>]+>/gi, ''); + var start = 0, + end = 0; + var transcriptId = undefined; + if (timeRange != undefined) { + // For timed-text + start = timeRange.start; + end = timeRange.end; + transcriptId = transcripts.findIndex(function (t) { + return t.begin == start && t.end == end; + }); + var queryText = qStr.match(/[a-zA-Z]+/gi) ? qStr.match(/[a-zA-Z]+/gi)[0] : qStr; + var matchOffset = mappedText.toLocaleLowerCase().indexOf(queryText); + if (matchOffset !== -1 && transcriptId != undefined) { + var match = markMatchedParts(value, qStr, item.hitCount, true); + transcriptLines.push({ + tag: TRANSCRIPT_CUE_TYPES.timedCue, + begin: start, + end: end, + id: transcriptId, + match: match, + matchCount: item.hitCount, + text: value + }); } - var offsetx = e.offsetX; - if (offsetx && offsetx != undefined) { - var time = offsetx / e.target.clientWidth * currentTrackRef.current.duration; - return time; + } else { + /** + * For non timed text, there's no unique id to match the search response to the transcript + * lines in the UI. So use filter() method instead of findIndex() method to get all matching + * transcript lines in the display. + * Use traversedIds array to remember the ids of already processed transcript lines in the list + * to avoid duplication in the matches. + */ + var hitsInfo = matchPartsInUntimedText(transcripts, mappedText, qStr, traversedIds); + traversedIds = hitsInfo.traversedIds; + transcriptLines = [].concat(_toConsumableArray(transcriptLines), _toConsumableArray(hitsInfo.hits)); + + /** + * When backend has a single block of text which is chuncked in the UI this helps to + * traverse all transcript cues. + */ + while (index === searchHits.length - 1 && ((_traversedIds = traversedIds) === null || _traversedIds === void 0 ? void 0 : _traversedIds.length) < transcripts.length) { + var _traversedIds; + var _hitsInfo = matchPartsInUntimedText(transcripts, mappedText, qStr, traversedIds); + traversedIds = _hitsInfo.traversedIds; + transcriptLines = [].concat(_toConsumableArray(transcriptLines), _toConsumableArray(_hitsInfo.hits)); } } - }]); - return VideoJSTrackScrubber; - }(Button); - videojs__default["default"].registerComponent('VideoJSTrackScrubber', VideoJSTrackScrubber); + }); + return transcriptLines; + }; - function _createForOfIteratorHelper$2(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$2(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } - function _unsupportedIterableToArray$2(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$2(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$2(o, minLen); } - function _arrayLikeToArray$2(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } - function ownKeys$4(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } - function _objectSpread$4(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$4(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$4(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } - require('@silvermine/videojs-quality-selector')(videojs__default["default"]); - // import vjsYo from './vjsYo'; + /** + * Build a list of matched indexed transcript lines from content search response. + * In Avalon, docx and plain text files are chunked by paragraphs seperated by 2 or + * more new line characters. So, depending on the way the file is formatted the search + * response could include chunks of the text or the full text. + * In the library (mammoth) used in Transcript component to display docx files; the text is chunked + * into paragraphs seperated by one or more new line characters. + * And the search response doesn't include any text styling in the docx files. Therefore the + * text with style information is reformatted to include text highlights from the search response. + * This function uses the search response to calculate the hit counts and mark them for each indexed transcript + * line in the front-end to get the correct counts. + * @param {Array} transcripts indexed transcript text in UI + * @param {String} mappedText matched text from content search + * @param {String} query search query entered by the user + * @param {Array} traversedIds already included transcript indices + * @returns a list of matched transcript lines + */ + var matchPartsInUntimedText = function matchPartsInUntimedText(transcripts, mappedText, query, traversedIds) { + var escapedQ = buildRegexReadyText(query, true, false); + // Get hit counts for the current text, ignore matches with query preceded by - or ' + var qRegex = new RegExp(String.raw(_templateObject$1 || (_templateObject$1 = _taggedTemplateLiteral(["\b", "\b"], ["\\b", "\\b"])), escapedQ), 'gi'); + var matched = []; + // Start from the next cue after the last traveresed cue in the transcript + var lastTraversedId = traversedIds[traversedIds.length - 1] + 1 || 0; - function VideoJSPlayer(_ref) { - var isVideo = _ref.isVideo; - _ref.hasMultipleCanvases; - var isPlaylist = _ref.isPlaylist, - trackScrubberRef = _ref.trackScrubberRef, - scrubberTooltipRef = _ref.scrubberTooltipRef, - tracks = _ref.tracks, - placeholderText = _ref.placeholderText, - renderingFiles = _ref.renderingFiles, - enableFileDownload = _ref.enableFileDownload, - loadPrevOrNext = _ref.loadPrevOrNext, - lastCanvasIndex = _ref.lastCanvasIndex, - enableTitleLink = _ref.enableTitleLink, - videoJSLangMap = _ref.videoJSLangMap, - options = _ref.options; - var playerState = usePlayerState(); - var playerDispatch = usePlayerDispatch(); - var manifestState = useManifestState(); - var manifestDispatch = useManifestDispatch(); - var canvasDuration = manifestState.canvasDuration, - canvasIndex = manifestState.canvasIndex, - canvasLink = manifestState.canvasLink, - currentNavItem = manifestState.currentNavItem, - hasMultiItems = manifestState.hasMultiItems, - srcIndex = manifestState.srcIndex, - targets = manifestState.targets, - autoAdvance = manifestState.autoAdvance, - playlist = manifestState.playlist, - structures = manifestState.structures, - canvasSegments = manifestState.canvasSegments, - hasStructure = manifestState.hasStructure, - canvasIsEmpty = manifestState.canvasIsEmpty; - var isClicked = playerState.isClicked, - isEnded = playerState.isEnded, - isPlaying = playerState.isPlaying, - player = playerState.player, - searchMarkers = playerState.searchMarkers, - currentTime = playerState.currentTime; - var _React$useState = React__default["default"].useState(canvasIndex), - _React$useState2 = _slicedToArray(_React$useState, 2); - _React$useState2[0]; - var _setCIndex = _React$useState2[1]; - var _React$useState3 = React__default["default"].useState(false), - _React$useState4 = _slicedToArray(_React$useState3, 2), - isReady = _React$useState4[0], - _setIsReady = _React$useState4[1]; - var _React$useState5 = React__default["default"].useState(''), - _React$useState6 = _slicedToArray(_React$useState5, 2), - activeId = _React$useState6[0], - _setActiveId = _React$useState6[1]; - var _useLocalStorage = useLocalStorage('startVolume', 1), - _useLocalStorage2 = _slicedToArray(_useLocalStorage, 2), - startVolume = _useLocalStorage2[0], - setStartVolume = _useLocalStorage2[1]; - var _useLocalStorage3 = useLocalStorage('startQuality', null), - _useLocalStorage4 = _slicedToArray(_useLocalStorage3, 2), - startQuality = _useLocalStorage4[0], - setStartQuality = _useLocalStorage4[1]; - var _useLocalStorage5 = useLocalStorage('startMuted', false), - _useLocalStorage6 = _slicedToArray(_useLocalStorage5, 2), - startMuted = _useLocalStorage6[0], - setStartMuted = _useLocalStorage6[1]; - var _useLocalStorage7 = useLocalStorage('startCaptioned', true), - _useLocalStorage8 = _slicedToArray(_useLocalStorage7, 2), - startCaptioned = _useLocalStorage8[0], - setStartCaptioned = _useLocalStorage8[1]; - var _React$useState7 = React__default["default"].useState(null), - _React$useState8 = _slicedToArray(_React$useState7, 2), - fragmentMarker = _React$useState8[0], - setFragmentMarker = _React$useState8[1]; - var _React$useState9 = React__default["default"].useState(CANVAS_MESSAGE_TIMEOUT / 1000), - _React$useState10 = _slicedToArray(_React$useState9, 2), - messageTime = _React$useState10[0], - setMessageTime = _React$useState10[1]; - var videoJSRef = React__default["default"].useRef(null); - var playerRef = React__default["default"].useRef(null); - var autoAdvanceRef = React__default["default"].useRef(); - autoAdvanceRef.current = autoAdvance; - var srcIndexRef = React__default["default"].useRef(); - srcIndexRef.current = srcIndex; - var activeIdRef = React__default["default"].useRef(); - activeIdRef.current = activeId; - var setActiveId = function setActiveId(id) { - _setActiveId(id); - activeIdRef.current = id; - }; - var currentTimeRef = React__default["default"].useRef(); - currentTimeRef.current = currentTime; - var isReadyRef = React__default["default"].useRef(); - isReadyRef.current = isReady; - var setIsReady = function setIsReady(r) { - _setIsReady(r); - isReadyRef.current = r; - }; - var currentNavItemRef = React__default["default"].useRef(); - currentNavItemRef.current = currentNavItem; - var canvasIsEmptyRef = React__default["default"].useRef(); - canvasIsEmptyRef.current = canvasIsEmpty; - var canvasDurationRef = React__default["default"].useRef(); - canvasDurationRef.current = canvasDuration; - var canvasLinkRef = React__default["default"].useRef(); - canvasLinkRef.current = canvasLink; - var isPlayingRef = React__default["default"].useRef(); - isPlayingRef.current = isPlaying; - var isEndedRef = React__default["default"].useRef(); - isEndedRef.current = isEnded; - var cIndexRef = React__default["default"].useRef(); - cIndexRef.current = canvasIndex; - var setCIndex = function setCIndex(i) { - _setCIndex(i); - cIndexRef.current = i; + /** + * For untimed text the search response text could be either, + * - mapped one to one with the cue text in Transcript component + * - include a part of the cue text in Transcript component + * When none of these work check if the cue text contains the search query + */ + for (var i = lastTraversedId; i < transcripts.length; i++) { + var t = transcripts[i]; + var cleanedText = t.text.replace(/<\/?[^>]+>/gi, '').trim(); + var matches = _toConsumableArray(cleanedText.matchAll(qRegex)); + var mappedTextCleaned = mappedText.trim(); + if (mappedTextCleaned == cleanedText || mappedTextCleaned.includes(cleanedText) && (matches === null || matches === void 0 ? void 0 : matches.length) > 0) { + t.matchCount = matches === null || matches === void 0 ? void 0 : matches.length; + matched.push(t); + traversedIds.push(t.id); + break; + } else if ((matches === null || matches === void 0 ? void 0 : matches.length) > 0) { + var _ref2; + t.matchCount = (_ref2 = _toConsumableArray(mappedTextCleaned.matchAll(qRegex))) === null || _ref2 === void 0 ? void 0 : _ref2.length; + matched.push(t); + traversedIds.push(t.id); + break; + } else { + traversedIds.push(t.id); + } + } + var hits = []; + matched.map(function (m) { + var value = addStyledHighlights(m.textDisplayed, query); + var match = markMatchedParts(value, query, m.matchCount, true); + hits.push({ + tag: TRANSCRIPT_CUE_TYPES.nonTimedLine, + begin: undefined, + end: undefined, + id: m.id, + match: match, + matchCount: m.matchCount, + text: value + }); + }); + return { + hits: hits, + traversedIds: traversedIds }; - var captionsOnRef = React__default["default"].useRef(); - var activeTrackRef = React__default["default"].useRef(); - var canvasSegmentsRef = React__default["default"].useRef(); - canvasSegmentsRef.current = canvasSegments; - var structuresRef = React__default["default"].useRef(); - structuresRef.current = structures; - var messageIntervalRef = React__default["default"].useRef(null); - - // Dispose Video.js instance when VideoJSPlayer component is removed - React__default["default"].useEffect(function () { - return function () { - if (playerRef.current != null) { - playerRef.current.dispose(); - document.removeEventListener('keydown', playerHotKeys); - setIsReady(false); - } - }; - }, []); + }; + /** + * Generic function to mark the matched transcript text in the cue where the output has + * surrounding the matched parts + * within the cue. + * @param {String} text matched transcript text/cue + * @param {String} query current search query + * @param {Numner} hitCount number of hits returned in the search response + * @param {Boolean} hasHighlight boolean flag to indicate text has tags + * @returns matched cue with HTML tags added for marking the hightlight + */ + var markMatchedParts = function markMatchedParts(text, query, hitCount) { + var hasHighlight = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; + if (text === undefined || !text) return; + var count = 0; + var replacerFn = function replacerFn(match) { + var cleanedMatch = match.replace(/<\/?[^>]+>/gi, ''); + // Only add highlights to search hits in the search response + if (count < hitCount) { + count++; + return "".concat(cleanedMatch, ""); + } else { + return cleanedMatch; + } + }; + var queryFormatted = query; /** - * Initialize Video.js when for the first page load or update - * src and other properties of the existing Video.js instance - * on Canvas change + * Content search response for a phrase search like 'Mr. Bungle' gives the response + * with highlights in the matched text as Mr. Bungle. + * So reconstruct the search query in the UI to match this phrase in the response. */ - React__default["default"].useEffect(function () { - var _options$sources, _options$sources2; - setCIndex(canvasIndex); + if (hasHighlight) { + queryFormatted = buildRegexReadyText(query); + } - // Set selected quality from localStorage in Video.js options - setSelectedQuality(options.sources); + /** + * Content search API returns cues including "Mr. Bungle" as matches for both search queries + * "mr bungle" and "mr. bungle". + * When "mr bungle" is searched this function handles highlighting since the regex fails to + * identify the matches in the cues. + */ + var altReplace = function altReplace() { + var matches = _toConsumableArray(text.matchAll(/<\/?[^>]+>/gi)); + if ((matches === null || matches === void 0 ? void 0 : matches.length) === 0) return; + var startIndex = 0; + var newStr = ''; + for (var j = 0; j < matches.length && count < hitCount;) { + // Set offset to count matches based on the # of words in the phrase search query + var splitQ = query.split(/[\s-,\?]/); + var offset = (splitQ === null || splitQ === void 0 ? void 0 : splitQ.length) > 0 ? (splitQ === null || splitQ === void 0 ? void 0 : splitQ.length) * 2 - 1 : 1; + if (matches[j] === undefined && matches[j + offset] === undefined) return; - // Video.js player is only initialized on initial page load - if (!playerRef.current && ((_options$sources = options.sources) === null || _options$sources === void 0 ? void 0 : _options$sources.length) > 0) { - videojs__default["default"].addLanguage(options.language, JSON.parse(videoJSLangMap)); - buildTracksHTML(); - - // Turn Video.js logging off and handle errors in this code, to avoid - // cluttering the console when loading inaccessible items. - videojs__default["default"].log.level('off'); - var _player = playerRef.current = videojs__default["default"](videoJSRef.current, options, function () { - playerInitSetup(playerRef.current); - }); - - /* Another way to add a component to the controlBar */ - // player.getChild('controlBar').addChild('vjsYo', {}); - - playerDispatch({ - player: _player, - type: 'updatePlayer' - }); - - // Update player status in state only when pause is initiate by the user - _player.controlBar.getChild('PlayToggle').on('pointerdown', function () { - handlePause(); - }); - _player.on('pointerdown', function (e) { - var elementTag = e.target.nodeName.toLowerCase(); - if (elementTag == 'video') { - handlePause(); - } - }); - } else if (playerRef.current && ((_options$sources2 = options.sources) === null || _options$sources2 === void 0 ? void 0 : _options$sources2.length) > 0) { - var _player2$markers; - // Update the existing Video.js player on consecutive Canvas changes - var _player2 = playerRef.current; - - // Reset markers - if (activeIdRef.current) (_player2$markers = _player2.markers) === null || _player2$markers === void 0 ? void 0 : _player2$markers.removeAll(); - setActiveId(null); - - // Block player while metadata is loaded when canvas is not empty - if (!canvasIsEmptyRef.current) { - _player2.addClass('vjs-disabled'); - setIsReady(false); - updatePlayer(_player2); - playerLoadedMetadata(_player2); - playerDispatch({ - player: _player2, - type: 'updatePlayer' - }); - } else { - // Mark as ready to for inaccessible canvas (empty) - setIsReady(true); + // Indices of start and end of the highlighted text including tags + var firstIndex = matches[j].index; + var lastIndex = matches[j + offset].index + matches[j + offset][0].length; + var prefix = text.slice(startIndex, firstIndex); + var cleanedMatch = text.slice(firstIndex, lastIndex).replace(/<\/?[^>]+>/gi, ''); + newStr = "".concat(newStr).concat(prefix, "").concat(cleanedMatch, ""); + startIndex = lastIndex; + j = +(offset + 1); + count++; + if (j == matches.length) { + newStr = "".concat(newStr).concat(text.slice(startIndex)); } } - }, [options.sources, videoJSRef]); - React__default["default"].useEffect(function () { - // Clear existing interval for inaccessible message display - clearDisplayTimeInterval(); - if (playerRef.current) { - // Show/hide control bar for valid/inaccessible items respectively - if (canvasIsEmptyRef.current) { - var _currentNavItemRef$cu; - // Set the player's aspect ratio to video - playerRef.current.audioOnlyMode(false); - playerRef.current.canvasIsEmpty = true; - playerRef.current.aspectRatio('16:9'); - playerRef.current.controlBar.addClass('vjs-hidden'); - playerRef.current.removeClass('vjs-disabled'); - playerRef.current.pause(); - /** - * Update the activeId to update the active item in the structured navigation. - * For playable items this is updated in the timeupdate handler. - */ - setActiveId((_currentNavItemRef$cu = currentNavItemRef.current) === null || _currentNavItemRef$cu === void 0 ? void 0 : _currentNavItemRef$cu.id); - } else { - // Reveal control bar; needed when loading a Canvas after an inaccessible item - playerRef.current.controlBar.removeClass('vjs-hidden'); - } + return newStr; + }; + try { + var _ref3; + var queryRegex = new RegExp(String.raw(_templateObject2 || (_templateObject2 = _taggedTemplateLiteral(["", ""])), queryFormatted), 'gi'); + if (((_ref3 = _toConsumableArray(text.matchAll(queryRegex))) === null || _ref3 === void 0 ? void 0 : _ref3.length) === 0) { + var highlighted = altReplace(); + return highlighted; + } else { + return text.replace(queryRegex, replacerFn); } + } catch (e) { + console.log('Error building RegExp for query: ', query); + } + }; - // Start interval for inaccessible message display - if (canvasIsEmptyRef.current && !messageIntervalRef.current) { - setMessageTime(CANVAS_MESSAGE_TIMEOUT / 1000); - createDisplayTimeInterval(); - } - }, [cIndexRef.current, canvasIsEmptyRef.current, currentNavItemRef.current]); + /** + * For docx files the content search response text doesn't have the formatted + * styles in the Word document (e.g. bold text wrapped in tags). So, + * use the styled text formatted with mammoth in the UI to add highlights from + * the content search response. + * @param {String} text string to be formatted + * @param {String} query string to find and replace with tags + * @returns a string formatted with highlights + */ + var addStyledHighlights = function addStyledHighlights(text, query) { + if (text === undefined || !text) return; + var replacerFn = function replacerFn(match) { + var cleanedMatch = buildRegexReadyText(match, false, true); + return cleanedMatch; + }; - /** - * Clear/create display timer interval when auto-advance is turned - * off/on respectively - */ - React__default["default"].useEffect(function () { - if (!autoAdvance) { - clearDisplayTimeInterval(); - } else if (autoAdvance && !messageIntervalRef.current && canvasIsEmpty) { - setMessageTime(CANVAS_MESSAGE_TIMEOUT / 1000); - createDisplayTimeInterval(); - } - }, [autoAdvance]); + // Regex to get matches in the text while ignoring matches with query preceded by - or ' + var queryregex = new RegExp(String.raw(_templateObject3 || (_templateObject3 = _taggedTemplateLiteral(["\b", "\b"], ["\\b", "\\b"])), buildRegexReadyText(query, true, false)), 'gi'); + var styled = text.replace(queryregex, replacerFn); + return styled; + }; - // update markers in player - React__default["default"].useEffect(function () { - if (playerRef.current && playerRef.current.markers && isReadyRef.current) { - var _playlist$markers, _playerRef$current$ma; - // markers plugin not yet initialized - if (typeof playerRef.current.markers === 'function') { - playerRef.current.markers({ - markerTip: { - display: false, - // true, - text: function text(marker) { - return marker.text; - } - }, - markerStyle: {}, - markers: [] - }); - } - var playlistMarkers = []; - if (playlist !== null && playlist !== void 0 && (_playlist$markers = playlist.markers) !== null && _playlist$markers !== void 0 && _playlist$markers.length) { - var canvasMarkers = playlist.markers.filter(function (m) { - return m.canvasIndex === canvasIndex; - })[0].canvasMarkers; - playlistMarkers = canvasMarkers.map(function (m) { - return { - time: parseFloat(m.time), - text: m.value, - "class": 'ramp--track-marker--playlist' - }; - }); - } - (_playerRef$current$ma = playerRef.current.markers) === null || _playerRef$current$ma === void 0 ? void 0 : _playerRef$current$ma.removeAll(); - playerRef.current.markers.add([].concat(_toConsumableArray(fragmentMarker ? [fragmentMarker] : []), _toConsumableArray(searchMarkers), _toConsumableArray(playlistMarkers))); - } - }, [fragmentMarker, searchMarkers, canvasDuration, canvasIndex, playerRef.current, isReadyRef.current]); + /** + * Format a given string by escaping punctuations characters and grouping + * punctuations and text, to make it feasible to be used to build a regular + * expression accurately. + * @param {String} text string to be formatted with hightlights + * @param {Boolean} regExpReady flag to indicate the usage of the output as a regular exp + * @param {Boolean} addHightlight flag to indicate to/not to add tags + * @returns string with tags + */ + var buildRegexReadyText = function buildRegexReadyText(text) { + var regExpReady = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true; + var addHightlight = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; + // Text matches in the string + var matches = _toConsumableArray(text.matchAll(/[a-zA-Z']+/gi)); + // Punctuation matches in the string + var punctuationMatches = _toConsumableArray(text.matchAll(/([.+?"^${}\-|[\]\\])/g)); /** - * Build track HTML for Video.js player on initial page load + * If no punctuations are found within the text return text with highlights + * For RegExp ready strings: ignore matches followed by - or ' + * e.g. omit matches as "Bungle's" when search query is "bungle" */ - var buildTracksHTML = function buildTracksHTML() { - if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0 && videoJSRef.current) { - tracks.map(function (t) { - var trackEl = document.createElement('track'); - trackEl.setAttribute('key', t.key); - trackEl.setAttribute('src', t.src); - trackEl.setAttribute('kind', t.kind); - trackEl.setAttribute('label', t.label); - trackEl.setAttribute('srclang', t.srclang); - videoJSRef.current.appendChild(trackEl); - }); + if ((punctuationMatches === null || punctuationMatches === void 0 ? void 0 : punctuationMatches.length) === 0) { + var textFormatted = addHightlight ? text.split(' ').map(function (t) { + return "".concat(t, ""); + }).join(' ') : text; + var textRegex = regExpReady ? "".concat(textFormatted, "(?!['w*])") : textFormatted; + return textRegex; + } + var highlighted = ''; + var startIndex = 0; + var i = 0; + while (i < matches.length) { + var match = matches[i]; + var textMatch = addHightlight ? "".concat(match[0], "") : match[0]; + /** + * When build RegExp ready string with punctuation blocks in the given string; + * - use * quantifier for blocks either at the start/end of the string to match zero or more times + * - use + quantifier for blocks in the middle of the string to match one or more times + * This pattern is build according the response from the content search API results. + */ + var punctMatch = startIndex === 0 ? "(".concat(text.slice(startIndex, match.index), ")*") : "(".concat(text.slice(startIndex, match.index), ")+"); + highlighted = regExpReady ? "".concat(highlighted).concat(punctMatch, "(").concat(textMatch, ")") : "".concat(highlighted).concat(text.slice(startIndex, match.index)).concat(textMatch); + startIndex = match.index + match[0].length; + if (i === (matches === null || matches === void 0 ? void 0 : matches.length) - 1) { + highlighted = regExpReady ? "".concat(highlighted, "(").concat(text.slice(startIndex), ")*") : "".concat(highlighted).concat(text.slice(startIndex)); } + i++; + } + + // Escape punctuation characters in string for RegExp ready strings + var escapePunctuation = function escapePunctuation(str) { + var punctuationRegex = /([.?^${}|[\]\\])/g; + return str.replace(punctuationRegex, '\\$1'); }; - var updatePlayer = function updatePlayer(player) { - player.duration(canvasDurationRef.current); - player.src(options.sources); - player.poster(options.poster); - player.canvasIndex = cIndexRef.current; - player.canvasIsEmpty = canvasIsEmptyRef.current; - player.srcIndex = srcIndex; - player.targets = targets; - if (enableTitleLink) { - player.canvasLink = canvasLinkRef.current; - } + return regExpReady ? escapePunctuation(highlighted) : highlighted; + }; - // Update textTracks in the player - var oldTracks = player.remoteTextTracks(); - var i = oldTracks.length; - while (i--) { - player.removeRemoteTextTrack(oldTracks[i]); - } - if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0 && isVideo) { - tracks.forEach(function (track) { - player.addRemoteTextTrack(track, false); - }); - } + /** + * Calculate hit counts for each matched transcript cue + * @param {String} text matched transcript cue text + * @param {String} query search query from UI + * @param {Boolean} hasHighlight flag indicating has tags or not + * @returns + */ + var getHitCountForCue = function getHitCountForCue(text, query) { + var _ref4; + var hasHighlight = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; + /* + Content search API highlights each word in the given phrase in the response. + Threfore, use first word in the query seperated by a white space to get the hit + counts for each cue. + Use regex with any punctuation followed by a white space to split the query. + e.g. query: Mr. bungle => search response: Mr. Bungle + */ + var partialQ = query.split(/[\s.,!?;:]/)[0]; + var cleanedPartialQ = partialQ.replace(/[\[\]\-]/gi, ''); + var hitTerm = hasHighlight ? buildRegexReadyText(partialQ) : cleanedPartialQ; + var highlightedTerm = new RegExp(String.raw(_templateObject4 || (_templateObject4 = _taggedTemplateLiteral(["", ""])), hitTerm), 'gi'); + var hitCount = (_ref4 = _toConsumableArray(text.matchAll(highlightedTerm))) === null || _ref4 === void 0 ? void 0 : _ref4.length; + return hitCount; + }; - /* - Update player control bar for; - - track scrubber button - - appearance of the player: big play button and aspect ratio of the player - based on media type - - volume panel based on media type - - file download menu - */ - if (player.getChild('controlBar') != null && !canvasIsEmpty) { - var controlBar = player.getChild('controlBar'); - // Index of the full-screen toggle in the player's control bar - var fullscreenIndex = controlBar.children().findIndex(function (c) { - return c.name_ == 'FullscreenToggle'; - }); - /* - Track-scrubber button: remove if the Manifest is not a playlist manifest - or the current Canvas doesn't have structure items. Or add back in if it's - not present otherwise. - */ - if (!(hasStructure || playlist.isPlaylist)) { - controlBar.removeChild('videoJSTrackScrubber'); - } else if (!controlBar.getChild('videoJSTrackScrubber')) { - // Add track-scrubber button after duration display if it is not available - controlBar.addChild('videoJSTrackScrubber', { - trackScrubberRef: trackScrubberRef, - timeToolRef: scrubberTooltipRef - }); - } - if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0 && isVideo && !controlBar.getChild('subsCapsButton')) { - var captionIndex = IS_MOBILE ? controlBar.children().findIndex(function (c) { - return c.name_ == 'MuteToggle'; - }) : controlBar.children().findIndex(function (c) { - return c.name_ == 'VolumePanel'; - }); - var subsCapBtn = controlBar.addChild('subsCapsButton', {}, captionIndex + 1); - // Add CSS to mark captions-on - subsCapBtn.children_[0].addClass('captions-on'); - } + // TODO:: Could be used for marking search hits in Word Doc transcripts? + var splitIntoElements = function splitIntoElements(htmlContent) { + // Create a temporary DOM element to parse the HTML + var tempDiv = document.createElement('div'); + tempDiv.innerHTML = htmlContent; + + // Convert child nodes into an array + var elements = buildNonTimedText(Array.from(tempDiv.childNodes), true); + return elements; + }; + + /** + * Build non-timed transcript text content chunks into a JSON array + * with relevant information for display. These are then used by + * search module to convert the transcript content into an index. + * @param {Array} cues a list of trascript cues + * @param {Boolean} isHTML flag to detect inlined HTML in cues + * @returns a list of JSON objects for each cue + */ + var buildNonTimedText = function buildNonTimedText(cues) { + var isHTML = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + var indexedCues = []; + cues.map(function (c) { + indexedCues.push({ + text: isHTML ? c.innerText : c, + tag: TRANSCRIPT_CUE_TYPES.nonTimedLine, + textDisplayed: isHTML ? lib.decode(c.innerHTML) : c + }); + }); + return indexedCues; + }; + function ownKeys$5(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } + function _objectSpread$5(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$5(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$5(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } + + /** + * Disable each marker when one of the markers in the table + * is being edited reading isEditing value from global + * state and read presence of annotation service in the Manifest. + * @returns { + * isDisabled: Boolean, + * hasAnnotationService: Boolean + * } + */ + var useMarkers = function useMarkers() { + var manifestState = React.useContext(ManifestStateContext); + var _manifestState$playli = manifestState.playlist, + isEditing = _manifestState$playli.isEditing, + hasAnnotationService = _manifestState$playli.hasAnnotationService; + var isDisabled = React.useMemo(function () { + return isEditing; + }, [isEditing]); + return { + isDisabled: isDisabled, + hasAnnotationService: hasAnnotationService + }; + }; + + /** + * Read player and related updates as player is changed in + * global state + * @returns { + * canvasIndex: number, + * canvasIsEmpty: bool, + * isMultiCanvased: bool, + * lastCanvasIndex: number, + * player: object + * getCurrentTime: func, + * } + */ + var useMediaPlayer = function useMediaPlayer() { + var manifestState = React.useContext(ManifestStateContext); + var playerState = React.useContext(PlayerStateContext); + var player = playerState.player; + var allCanvases = manifestState.allCanvases, + canvasIndex = manifestState.canvasIndex, + canvasIsEmpty = manifestState.canvasIsEmpty; + + // Deduct 1 from length to compare against canvasIndex, which starts from 0 + var lastCanvasIndex = React.useMemo(function () { + var _ref; + return (_ref = (allCanvases === null || allCanvases === void 0 ? void 0 : allCanvases.length) - 1) !== null && _ref !== void 0 ? _ref : 0; + }, [allCanvases]); + var isMultiCanvased = React.useMemo(function () { + return (allCanvases === null || allCanvases === void 0 ? void 0 : allCanvases.length) - 1 > 0 ? true : false; + }, [allCanvases]); + + // Wrapper function to get player's time for creating a new playlist marker + var getCurrentTime = React.useCallback(function () { + if (player) { + return player.currentTime(); + } else { + return 0; + } + }, [player]); + return { + canvasIndex: canvasIndex, + canvasIsEmpty: canvasIsEmpty, + isMultiCanvased: isMultiCanvased, + lastCanvasIndex: lastCanvasIndex, + player: player, + getCurrentTime: getCurrentTime + }; + }; + + /** + * Read Canvas information and update state to reload player on + * Canvas changes + * @param {Object} obj + * @param {Boolean} obj.enableFileDownload + * @param {Boolean} obj.withCredentials + * @param {Number} obj.lastCanvasIndex + * @returns { + * isMultiSourced: bool, + * isPlaylist: bool, + * isVideo: bool, + * nextItemClicked: func, + * playerConfig: obj, + * ready: bool, + * renderingFiles: array, + * srcIndex: number, + * switchPlayer: func + * } + */ + var useSetupPlayer = function useSetupPlayer(_ref2) { + var _ref2$enableFileDownl = _ref2.enableFileDownload, + enableFileDownload = _ref2$enableFileDownl === void 0 ? false : _ref2$enableFileDownl, + _ref2$withCredentials = _ref2.withCredentials, + withCredentials = _ref2$withCredentials === void 0 ? false : _ref2$withCredentials, + lastCanvasIndex = _ref2.lastCanvasIndex; + var manifestDispatch = React.useContext(ManifestDispatchContext); + var playerDispatch = React.useContext(PlayerDispatchContext); + var manifestState = React.useContext(ManifestStateContext); + var allCanvases = manifestState.allCanvases, + autoAdvance = manifestState.autoAdvance, + canvasIndex = manifestState.canvasIndex, + customStart = manifestState.customStart, + manifest = manifestState.manifest, + playlist = manifestState.playlist, + renderings = manifestState.renderings, + srcIndex = manifestState.srcIndex; + var isPlaylist = playlist.isPlaylist; + var _useShowInaccessibleM = useShowInaccessibleMessage({ + lastCanvasIndex: lastCanvasIndex + }), + clearDisplayTimeInterval = _useShowInaccessibleM.clearDisplayTimeInterval, + createDisplayTimeInterval = _useShowInaccessibleM.createDisplayTimeInterval; + var _useState = React.useState(), + _useState2 = _slicedToArray(_useState, 2), + isVideo = _useState2[0], + setIsVideo = _useState2[1]; + var _useState3 = React.useState({ + error: '', + sources: [], + tracks: [], + poster: null, + targets: [] + }), + _useState4 = _slicedToArray(_useState3, 2), + playerConfig = _useState4[0], + setPlayerConfig = _useState4[1]; + var _useState5 = React.useState(), + _useState6 = _slicedToArray(_useState5, 2), + isMultiSourced = _useState6[0], + setIsMultiSourced = _useState6[1]; + var _useState7 = React.useState(true), + _useState8 = _slicedToArray(_useState7, 2), + firstLoad = _useState8[0], + setFirstLoad = _useState8[1]; + var _useState9 = React.useState(false), + _useState10 = _slicedToArray(_useState9, 2), + ready = _useState10[0], + setReady = _useState10[1]; + var renderingFiles = React.useMemo(function () { + if (enableFileDownload && renderings != {}) { + var _renderings$manifest, _renderings$canvas$ca; + return renderings === null || renderings === void 0 ? void 0 : (_renderings$manifest = renderings.manifest) === null || _renderings$manifest === void 0 ? void 0 : _renderings$manifest.concat(renderings === null || renderings === void 0 ? void 0 : (_renderings$canvas$ca = renderings.canvas[canvasIndex]) === null || _renderings$canvas$ca === void 0 ? void 0 : _renderings$canvas$ca.files); + } else { + return []; + } + }, [renderings, canvasIndex]); + React.useEffect(function () { + if (manifest) { /* - Change player's appearance when switching between audio and video canvases. - For audio: player height is reduced and big play button is removed - For video: player aspect ratio is set to 16:9 and has the centered big play button + Always start from the start time relevant to the Canvas only in playlist contexts, + because canvases related to playlist items always start from the given start. + With regular manifests, the start time could be different when using structured + navigation to switch between canvases. */ - if (!isVideo) { - player.audioOnlyMode(true); - player.addClass('vjs-audio'); - player.height(player.controlBar.height()); - player.removeChild('bigPlayButton'); - } else { - player.audioOnlyMode(false); - player.removeClass('vjs-audio'); - player.aspectRatio('16:9'); - player.addChild('bigPlayButton'); + if (canvasIndex == undefined || canvasIndex < 0) { + throw new Error('Invalid canvas index. Please check your Manifest.'); } + initCanvas(canvasIndex, isPlaylist); + } + return function () { + setReady(false); + playerDispatch({ + player: null, + type: 'updatePlayer' + }); + }; + }, [manifest, canvasIndex]); - /* - Re-add volumePanel/muteToggle icon: ensures the correct order of controls - on player reload. - On mobile device browsers, the volume panel is replaced by muteToggle - for both audio and video. - */ - if (!IS_MOBILE) { - controlBar.removeChild('VolumePanel'); - controlBar.addChild('VolumePanel'); - /* - Trigger ready event to reset the volume slider in the refreshed - volume panel. This is needed on player reload, since volume slider - is set on either 'ready' or 'volumechange' events. - */ - player.trigger('volumechange'); + /** + * Initialize the next Canvas to be viewed in the player instance + * @param {Number} canvasId index of the Canvas to be loaded into the player + * @param {Boolean} fromStart flag to indicate how to start new player instance + */ + var initCanvas = function initCanvas(canvasId, fromStart) { + clearDisplayTimeInterval(); + var _getMediaInfo = getMediaInfo({ + manifest: manifest, + canvasIndex: canvasId, + startTime: canvasId === customStart.startIndex && firstLoad ? customStart.startTime : 0, + srcIndex: srcIndex, + isPlaylist: isPlaylist + }), + isMultiSource = _getMediaInfo.isMultiSource, + sources = _getMediaInfo.sources, + tracks = _getMediaInfo.tracks, + canvasTargets = _getMediaInfo.canvasTargets, + mediaType = _getMediaInfo.mediaType, + error = _getMediaInfo.error, + poster = _getMediaInfo.poster; + if (withCredentials) { + sources.map(function (source) { + return source.withCredentials = true; + }); + } + setIsVideo(mediaType === 'video'); + manifestDispatch({ + canvasTargets: canvasTargets, + type: 'canvasTargets' + }); + manifestDispatch({ + isMultiSource: isMultiSource, + type: 'hasMultipleItems' + }); + + // Set the current time in player from the canvas details + if (fromStart) { + if ((canvasTargets === null || canvasTargets === void 0 ? void 0 : canvasTargets.length) > 0) { + playerDispatch({ + currentTime: canvasTargets[0].altStart, + type: 'setCurrentTime' + }); } else { - controlBar.removeChild('MuteToggle'); - controlBar.addChild('MuteToggle'); + playerDispatch({ + currentTime: 0, + type: 'setCurrentTime' + }); } - if (enableFileDownload) { - var fileDownloadIndex = controlBar.children().findIndex(function (c) { - return c.name_ == 'VideoJSFileDownload'; - }) || fullscreenIndex + 1; - controlBar.removeChild('videoJSFileDownload'); - if ((renderingFiles === null || renderingFiles === void 0 ? void 0 : renderingFiles.length) > 0) { - var fileOptions = { - title: 'Download Files', - controlText: 'Alternate resource download', - files: renderingFiles - }; - controlBar.addChild('videoJSFileDownload', _objectSpread$4({}, fileOptions), fileDownloadIndex); - } + } + setPlayerConfig(_objectSpread$5(_objectSpread$5({}, playerConfig), {}, { + error: error, + sources: sources, + tracks: tracks, + poster: poster, + targets: canvasTargets + })); + var currentCanvas = allCanvases.find(function (c) { + return c.canvasIndex === canvasId; + }); + if (!currentCanvas.isEmpty) { + // Manifest is taken from manifest state, and is a basic object at this point + // lacking the getLabel() function so we manually retrieve the first label. + var manifestLabel = manifest.label ? Object.values(manifest.label)[0][0] : ''; + // Filter out falsy items in case canvas.label is null or an empty string + var titleText = [manifestLabel, currentCanvas.label].filter(Boolean).join(' - '); + manifestDispatch({ + canvasDuration: currentCanvas.duration, + type: 'canvasDuration' + }); + manifestDispatch({ + canvasLink: { + label: titleText, + id: currentCanvas.canvasId + }, + type: 'canvasLink' + }); + manifestDispatch({ + type: 'setCanvasIsEmpty', + isEmpty: false + }); + } else { + playerDispatch({ + type: 'updatePlayer' + }); + manifestDispatch({ + type: 'setCanvasIsEmpty', + isEmpty: true + }); + // Set poster as playerConfig.error to be used for empty Canvas message in VideoJSPlayer + setPlayerConfig(_objectSpread$5(_objectSpread$5({}, playerConfig), {}, { + error: poster + })); + // Create timer to display the message when autoadvance is ON + if (autoAdvance) { + createDisplayTimeInterval(); } } + setIsMultiSourced(isMultiSource || false); + error ? setReady(false) : setReady(true); + // Reset firstLoad flag after customStart is used on initial load + setFirstLoad(false); }; /** - * Setup on loadedmetadata event is broken out of initial setup function, - * since this needs to be called when reloading the player on Canvas change - * @param {Object} player Video.js player instance + * Switch player when navigating across canvases + * @param {Number} index canvas index to be loaded into the player + * @param {Boolean} fromStart flag to indicate set player start time to zero or not + * @param {String} focusElement element to be focused within the player when using + * next or previous buttons with keyboard */ - var playerLoadedMetadata = function playerLoadedMetadata(player) { - player.one('loadedmetadata', function () { - console.log('Player loadedmetadata'); - player.duration(canvasDurationRef.current); - isEndedRef.current ? player.currentTime(0) : player.currentTime(currentTimeRef.current); - if (isEndedRef.current || isPlayingRef.current) { - /* - iOS devices lockdown the ability for unmuted audio and video media to autoplay. - They accomplish this by capturing any programmatic play events and returning - a rejected Promise. In certain versions of iOS, this rejected promise would - cause a runtime error within Ramp. This error would cause the error boundary - handling to trigger, forcing a user to reload the player/page. By silently - catching the rejected Promise we are able to provide a more seamless user - experience, where the user can manually play the media or change to a different - section. - */ - var promise = player.play(); - if (promise !== undefined) { - promise.then(function (_) { - // Autoplay - })["catch"](function (error) { - // Prevent error from triggering error boundary - }); - } - } - if (isVideo) { - setUpCaptions(player); - } - - /* - Set playable duration within the given media file and alternate start time as - player properties. These values are read by track-scrubber component to build - and update the track-scrubber progress and time in the UI. - */ - var mediaRange = getMediaFragment(player.src(), canvasDurationRef.current); - if (mediaRange != undefined) { - player.playableDuration = mediaRange.end - mediaRange.start; - player.altStart = mediaRange.start; - } else { - player.playableDuration = canvasDurationRef.current; - player.altStart = targets[srcIndex].altStart; - } - player.canvasIndex = cIndexRef.current; - setIsReady(true); - - /** - * Update currentNavItem on loadedmetadata event in Safari, as it doesn't - * trigger the 'timeupdate' event intermittently on load. - */ - if (IS_SAFARI) { - handleTimeUpdate(); - } - - /** - * When either player/browser tab is muted Safari and Chrome in iOS doesn't seem to - * load enough data related to audio-only media for the Video.js instance to play - * on page load. - * Since, it is not possible to detect muted tabs in JS the condition avoids - * checking for muted state altogether. - * Without this, Safari will not reach player.readyState() = 4, the state - * which indicates the player that enough data is available on the media - * for playback. - */ - if (!isVideo && (IS_SAFARI || IS_IOS) && player.readyState() != 4) { - player.load(); - } - - // Reveal player if not revealed on 'progress' event, allowing user to - // interact with the player since enough data is available for playback - if (player.hasClass('vjs-disabled')) { - player.removeClass('vjs-disabled'); - } - }); + var switchPlayer = function switchPlayer(index, fromStart) { + var focusElement = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; + if (index != undefined && index > -1 && index <= lastCanvasIndex) { + manifestDispatch({ + canvasIndex: index, + type: 'switchCanvas' + }); + initCanvas(index, fromStart); + playerDispatch({ + element: focusElement, + type: 'setPlayerFocusElement' + }); + } }; /** - * Setup player with player-related information parsed from the IIIF - * Manifest Canvas. This gets called on both initial page load and each - * Canvas switch to setup and update player respectively. - * @param {Object} player current player instance from Video.js + * Switch src in the player when seeked to a time range within a + * different item in the same canvas + * @param {Number} srcindex new srcIndex + * @param {Number} value current time of the player */ - var playerInitSetup = function playerInitSetup(player) { - player.on('ready', function () { - console.log('Player ready'); - - // Add this class in mobile/tablet devices to always show the control bar, - // since the inactivityTimeout is flaky in some browsers - if (IS_MOBILE || IS_IPAD) { - player.controlBar.addClass('vjs-mobile-visible'); - } - player.muted(startMuted); - player.volume(startVolume); - player.canvasIndex = cIndexRef.current; - player.duration(canvasDurationRef.current); - player.srcIndex = srcIndex; - player.targets = targets; - if (enableTitleLink) { - player.canvasLink = canvasLinkRef.current; - } - // Need to set this once experimentalSvgIcons option in Video.js options was enabled - player.getChild('controlBar').qualitySelector.setIcon('cog'); - }); - playerLoadedMetadata(player); - player.on('progress', function () { - // Reveal player if not revealed on 'loadedmetadata' event, allowing user to - // interact with the player since enough data is available for playback - if (player.hasClass('vjs-disabled')) { - player.removeClass('vjs-disabled'); - } - }); - player.on('canplay', function () { - // Reset isEnded flag - playerDispatch({ - isEnded: false, - type: 'setIsEnded' - }); - }); - player.on('play', function () { - playerDispatch({ - isPlaying: true, - type: 'setPlayingStatus' - }); - }); - player.on('timeupdate', function () { - handleTimeUpdate(); - }); - player.on('ended', function () { - /** - * Checking against isReadyRef stops from delayed events being executed - * when transitioning from a Canvas to the next. - * Checking against isPlayingRef.current to distinguish whether this event - * triggered intentionally, because Video.js seem to trigger this event when - * switching to a media file with a shorter duration in Safari browsers. - */ - setTimeout(function () { - if (isReadyRef.current && isPlayingRef.current) { - playerDispatch({ - isEnded: true, - type: 'setIsEnded' - }); - player.pause(); - if (!canvasIsEmptyRef.current) handleEnded(); - } - }, 100); - }); - player.on('volumechange', function () { - setStartMuted(player.muted()); - setStartVolume(player.volume()); - }); - player.on('qualityRequested', function (e, quality) { - setStartQuality(quality.label); + var nextItemClicked = function nextItemClicked(srcindex, value) { + playerDispatch({ + currentTime: value, + type: 'setCurrentTime' }); - // Use error event listener for inaccessible item display - player.on('error', function (e) { - var error = player.error(); - var errorMessage = 'Something went wrong. Please try again later or contact support for help.'; - // Handle different error codes - switch (error.code) { - case 1: - console.error('MEDIA_ERR_ABORTED: The fetching process for the media resource was aborted by the user agent\ - at the user’s request.'); - break; - case 2: - errorMessage = 'The media could not be loaded due to a network error. Please try again later.'; - console.error('MEDIA_ERR_NETWORK: A network error caused the user agent to stop fetching the media resource,\ - after the resource was established to be usable.'); - break; - case 3: - errorMessage = 'Media is corrupt or has features not supported by the browser. \ - Please try a different media or contact support for help.'; - console.error('MEDIA_ERR_DECODE: An error occurred while decoding the media resource, after\ - the resource was established to be usable.'); - break; - case 4: - errorMessage = 'Media could not be loaded. Network error or media format is not supported.'; - console.error('MEDIA_ERR_SRC_NOT_SUPPORTED: The media resource indicated by the src attribute was not suitable.'); - break; - default: - console.error('An unknown error occurred.'); - break; - } - // Show dismissable error display modal from Video.js - var errorDisplay = player.getChild('ErrorDisplay'); - if (errorDisplay) { - errorDisplay.contentEl().innerText = errorMessage; - errorDisplay.removeClass('vjs-hidden'); - player.removeClass('vjs-error'); - player.removeClass('vjs-disabled'); - } - e.stopPropagation(); + manifestDispatch({ + srcIndex: srcindex, + type: 'setSrcIndex' }); + }; + return { + isMultiSourced: isMultiSourced, + isPlaylist: isPlaylist, + isVideo: isVideo, + nextItemClicked: nextItemClicked, + playerConfig: playerConfig, + ready: ready, + renderingFiles: renderingFiles, + srcIndex: srcIndex, + switchPlayer: switchPlayer + }; + }; + + /** + * Initialize and update VideoJS instance on global state changes when + * Canvas changes + * @param {Object} obj + * @param {Object} obj.options VideoJS options + * @param {Function} obj.playerInitSetup VideoJS initialize setup func + * @param {String} obj.startQuality selected quality stored in local storage + * @param {Array} obj.tracks text tracks for the selected Canvas + * @param {Function} obj.updatePlayer VideoJS update func on Canvas change + * @param {Object} obj.videoJSRef React ref for video tag on page + * @param {String} obj.videoJSLangMap VideoJS language for set language + * @returns { + * activeId: string, + * fragmentMarker: obj, + * isReadyRef: obj, + * playerRef: obj, + * setActiveId: func, + * setFragmentMarker: func, + * setIsReady: func, + * } + */ + var useVideoJSPlayer = function useVideoJSPlayer(_ref3) { + var options = _ref3.options, + playerInitSetup = _ref3.playerInitSetup, + startQuality = _ref3.startQuality, + tracks = _ref3.tracks, + updatePlayer = _ref3.updatePlayer, + videoJSRef = _ref3.videoJSRef, + videoJSLangMap = _ref3.videoJSLangMap; + var manifestState = React.useContext(ManifestStateContext); + var playerState = React.useContext(PlayerStateContext); + var playerDispatch = React.useContext(PlayerDispatchContext); + var canvasDuration = manifestState.canvasDuration, + canvasIndex = manifestState.canvasIndex, + canvasIsEmpty = manifestState.canvasIsEmpty, + currentNavItem = manifestState.currentNavItem, + playlist = manifestState.playlist; + var currentTime = playerState.currentTime, + isClicked = playerState.isClicked, + isPlaying = playerState.isPlaying, + player = playerState.player, + searchMarkers = playerState.searchMarkers; + var _useState11 = React.useState(''), + _useState12 = _slicedToArray(_useState11, 2), + activeId = _useState12[0], + setActiveId = _useState12[1]; + var _useState13 = React.useState(null), + _useState14 = _slicedToArray(_useState13, 2), + fragmentMarker = _useState14[0], + setFragmentMarker = _useState14[1]; + // Needs to maintain this in a state variable for useEffect for marker updates + var _useState15 = React.useState(false), + _useState16 = _slicedToArray(_useState15, 2), + isReady = _useState16[0], + _setIsReady = _useState16[1]; + var isReadyRef = React.useRef(isReady); + var setIsReady = function setIsReady(r) { + _setIsReady(r); + isReadyRef.current = r; + }; + var playerRef = React.useRef(null); + React.useEffect(function () { /* This event handler helps to execute hotkeys functions related to 'keydown' events before any user interactions with the player or when focused on other non-input elements on the page */ document.addEventListener('keydown', function (event) { - var result = playerHotKeys(event, player, canvasIsEmptyRef.current); + var result = playerHotKeys(event, playerRef.current, canvasIsEmpty); // Update player status in global state switch (result) { case HOTKEY_ACTION_OUTPUT.pause: @@ -7202,94 +7119,157 @@ break; } }); - }; - - /** - * Setup captions for the player based on context - * @param {Object} player Video.js player instance - */ - var setUpCaptions = function setUpCaptions(player) { - var _textTracks$tracks_; - var textTracks = player.textTracks(); - /* - Filter the text track Video.js adds with an empty label and language - when nativeTextTracks are enabled for iPhones and iPads. - Related links, Video.js => https://github.com/videojs/video.js/issues/2808 and - in Apple => https://developer.apple.com/library/archive/qa/qa1801/_index.html - */ - if (IS_MOBILE && !IS_ANDROID) { - textTracks.on('addtrack', function () { - for (var i = 0; i < textTracks.length; i++) { - if (textTracks[i].language === '' && textTracks[i].label === '') { - player.textTracks().removeTrack(textTracks[i]); - } - /** - * This enables the caption in the native iOS player first playback. - * Only enable caption when captions are turned on. - * First caption is already turned on in the code block below, so read it - * from activeTrackRef - */ - if (startCaptioned && activeTrackRef.current) { - textTracks.tracks_.filter(function (t) { - return t.label === activeTrackRef.current.label && t.language === activeTrackRef.current.language; - })[0].mode = 'showing'; - } - } - }); - } - // Turn first caption/subtitle ON and turn captions ON indicator via CSS on first load - if (((_textTracks$tracks_ = textTracks.tracks_) === null || _textTracks$tracks_ === void 0 ? void 0 : _textTracks$tracks_.length) > 0) { - var firstSubCap = null; - // Flag to identify first valid caption for resource - var onFirstCap = false; - // Disable all text tracks to avoid multiple selections and pick the first one as default - for (var i = 0; i < textTracks.tracks_.length; i++) { - var t = textTracks.tracks_[i]; - if ((t.kind === 'subtitles' || t.kind === 'captions') && t.language != '' && t.label != '') { - t.mode = 'disabled'; - if (!onFirstCap) firstSubCap = t; - onFirstCap = true; - } + // Dispose Video.js instance when VideoJSPlayer component is removed + return function () { + if (player) { + player.dispose(); + document.removeEventListener('keydown', playerHotKeys); + setIsReady(false); } + }; + }, []); - // Enable the first caption when captions are enabled in the session - if (firstSubCap && startCaptioned) { - firstSubCap.mode = 'showing'; - activeTrackRef.current = firstSubCap; - handleCaptionChange(true); - } - } + // Update VideoJS instance on Canvas change + React.useEffect(function () { + var _options$sources, _options$sources2; + // Set selected quality from localStorage in Video.js options + setSelectedQuality(options.sources); - // Add/remove CSS to indicate captions/subtitles is turned on - textTracks.on('change', function () { - var trackModes = []; - for (var _i = 0; _i < textTracks.tracks_.length; _i++) { - var _textTracks$_i = textTracks[_i], - mode = _textTracks$_i.mode, - label = _textTracks$_i.label, - kind = _textTracks$_i.kind; - trackModes.push(textTracks[_i].mode); - if (mode === 'showing' && label != '' && (kind === 'subtitles' || kind === 'captions')) { - activeTrackRef.current = textTracks[_i]; + // Video.js player is only initialized on initial page load + if (!playerRef.current && ((_options$sources = options.sources) === null || _options$sources === void 0 ? void 0 : _options$sources.length) > 0) { + videojs__default["default"].addLanguage(options.language, JSON.parse(videoJSLangMap)); + buildTracksHTML(); + + // Turn Video.js logging off and handle errors in this code, to avoid + // cluttering the console when loading inaccessible items. + videojs__default["default"].log.level('off'); + var _player = playerRef.current = videojs__default["default"](videoJSRef.current, options, function () { + playerInitSetup(playerRef.current); + }); + + /* Another way to add a component to the controlBar */ + // player.getChild('controlBar').addChild('vjsYo', {}); + + playerDispatch({ + player: _player, + type: 'updatePlayer' + }); + + // Update player status in state only when pause is initiate by the user + _player.controlBar.getChild('PlayToggle').on('pointerdown', function () { + handlePause(); + }); + _player.on('pointerdown', function (e) { + var elementTag = e.target.nodeName.toLowerCase(); + if (elementTag == 'video') { + handlePause(); } + }); + } else if (playerRef.current && ((_options$sources2 = options.sources) === null || _options$sources2 === void 0 ? void 0 : _options$sources2.length) > 0) { + var _player2$markers; + // Update the existing Video.js player on consecutive Canvas changes + var _player2 = playerRef.current; + + // Reset markers + if (activeId) (_player2$markers = _player2.markers) === null || _player2$markers === void 0 ? void 0 : _player2$markers.removeAll(); + setActiveId(null); + + // Block player while metadata is loaded when canvas is not empty + if (!canvasIsEmpty) { + _player2.addClass('vjs-disabled'); + setIsReady(false); + updatePlayer(_player2); + playerDispatch({ + player: _player2, + type: 'updatePlayer' + }); + } else { + // Mark as ready to for inaccessible canvas (empty) + setIsReady(true); } - var subsOn = trackModes.includes('showing') ? true : false; - handleCaptionChange(subsOn); - setStartCaptioned(subsOn); - }); - }; + } + }, [options.sources, videoJSRef]); + React.useEffect(function () { + if (player) { + // Show/hide control bar for valid/inaccessible items respectively + if (canvasIsEmpty) { + // Set the player's aspect ratio to video + player.audioOnlyMode(false); + player.canvasIsEmpty = true; + player.aspectRatio('16:9'); + player.controlBar.addClass('vjs-hidden'); + player.removeClass('vjs-disabled'); + player.pause(); + /** + * Update the activeId to update the active item in the structured navigation. + * For playable items this is updated in the timeupdate handler. + */ + setActiveId(currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.id); + } else { + // Reveal control bar; needed when loading a Canvas after an inaccessible item + player.controlBar.removeClass('vjs-hidden'); + } + } + }, [canvasIndex, canvasIsEmpty, currentNavItem]); - /** - * Setting the current time of the player when using structure navigation - */ - React__default["default"].useEffect(function () { - if (playerRef.current !== null && isReadyRef.current) { - playerRef.current.currentTime(currentTimeRef.current, playerDispatch({ + // Setting the current time of the player when using structure navigation + React.useEffect(function () { + if (playerRef.current) { + playerRef.current.currentTime(currentTime, playerDispatch({ type: 'resetClick' })); } - }, [isClicked, isReady]); + }, [isClicked]); + + // Update VideoJS player's markers for search hits/playlist markers/structure navigation + React.useEffect(function () { + if (playerRef.current && playerRef.current.markers && isReady) { + var _playlist$markers, _playerRef$current$ma; + // markers plugin not yet initialized + if (typeof playerRef.current.markers === 'function') { + playerRef.current.markers({ + markerTip: { + display: false, + // true, + text: function text(marker) { + return marker.text; + } + }, + markerStyle: {}, + markers: [] + }); + } + var playlistMarkers = []; + if (playlist !== null && playlist !== void 0 && (_playlist$markers = playlist.markers) !== null && _playlist$markers !== void 0 && _playlist$markers.length) { + var canvasMarkers = playlist.markers.filter(function (m) { + return m.canvasIndex === canvasIndex; + })[0].canvasMarkers; + playlistMarkers = canvasMarkers.map(function (m) { + return { + time: parseFloat(m.time), + text: m.value, + "class": 'ramp--track-marker--playlist' + }; + }); + } + (_playerRef$current$ma = playerRef.current.markers) === null || _playerRef$current$ma === void 0 ? void 0 : _playerRef$current$ma.removeAll(); + playerRef.current.markers.add([].concat(_toConsumableArray(fragmentMarker ? [fragmentMarker] : []), _toConsumableArray(searchMarkers), _toConsumableArray(playlistMarkers))); + } + }, [fragmentMarker, searchMarkers, canvasDuration, canvasIndex, playerRef.current, isReady]); + + /** + * Update global state only when a user pause the player by using the + * player interface or keyboard shortcuts + */ + var handlePause = function handlePause() { + if (isPlaying) { + playerDispatch({ + isPlaying: false, + type: 'setPlayingStatus' + }); + } + }; var setSelectedQuality = function setSelectedQuality(sources) { //iterate through sources and find source that matches startQuality and source currently marked selected //if found set selected attribute on matching source then remove from currently marked one @@ -7306,3040 +7286,3808 @@ }; /** - * Add CSS class to icon to indicate captions are on/off in player control bar - * @param {Boolean} subsOn flag to indicate captions are on/off + * Build track HTML for Video.js player on initial page load */ - var handleCaptionChange = function handleCaptionChange(subsOn) { - var player = playerRef.current; - /** - * When subsCapsButton is not setup on Video.js initialization step, and is - * later added in updatePlayer() function player.controlBar.getChild() method - * needs to be used to access it. - */ - var subsCapsBtn = player.controlBar.getChild('subsCapsButton'); - /* - For audio instances Video.js is setup to not to build the CC button - in Ramp's player control bar. - */ - if (subsCapsBtn == undefined || !subsCapsBtn || !(subsCapsBtn !== null && subsCapsBtn !== void 0 && subsCapsBtn.children_)) { - return; - } - if (subsOn) { - subsCapsBtn.children_[0].addClass('captions-on'); - captionsOnRef.current = true; - } else { - subsCapsBtn.children_[0].removeClass('captions-on'); - captionsOnRef.current = false; + var buildTracksHTML = function buildTracksHTML() { + if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0 && videoJSRef.current) { + tracks.map(function (t) { + var trackEl = document.createElement('track'); + trackEl.setAttribute('key', t.key); + trackEl.setAttribute('src', t.src); + trackEl.setAttribute('kind', t.kind); + trackEl.setAttribute('label', t.label); + trackEl.setAttribute('srclang', t.srclang); + videoJSRef.current.appendChild(trackEl); + }); } }; + return { + activeId: activeId, + fragmentMarker: fragmentMarker, + isReadyRef: isReadyRef, + playerRef: playerRef, + setActiveId: setActiveId, + setFragmentMarker: setFragmentMarker, + setIsReady: setIsReady + }; + }; + + /** + * Handle display of inaccessible message timer and interval for + * countdown + * @param {Object} obj + * @param {Number} obj.lastCanvasIndex + * @returns { + * messageTime: number, + * clearCanvasMessageTimer: func, + * createCanvasMessageTimer: func + * } + */ + var useShowInaccessibleMessage = function useShowInaccessibleMessage(_ref4) { + var lastCanvasIndex = _ref4.lastCanvasIndex; + var manifestDispatch = React.useContext(ManifestDispatchContext); + var manifestState = React.useContext(ManifestStateContext); + var autoAdvance = manifestState.autoAdvance, + canvasIndex = manifestState.canvasIndex, + canvasIsEmpty = manifestState.canvasIsEmpty; + var _useState17 = React.useState(CANVAS_MESSAGE_TIMEOUT / 1000), + _useState18 = _slicedToArray(_useState17, 2), + messageTime = _useState18[0], + setMessageTime = _useState18[1]; + var canvasIndexRef = React.useRef(); + canvasIndexRef.current = React.useMemo(function () { + return canvasIndex; + }, [canvasIndex]); + var messageIntervalRef = React.useRef(null); + React.useEffect(function () { + // Clear existing interval for inaccessible message display + clearDisplayTimeInterval(); + if (canvasIsEmpty && !messageIntervalRef.current && autoAdvance) { + setMessageTime(CANVAS_MESSAGE_TIMEOUT / 1000); + createDisplayTimeInterval(); + } + }, [canvasIndex, autoAdvance, canvasIsEmpty]); /** - * Handle the 'ended' event fired by the player when a section comes to - * an end. If there are sections ahead move onto the next canvas and - * change the player and the state accordingly. - * Throttle helps to cancel the delayed function call triggered by ended event and - * load the correct item into the player, when the user clicks on a different item - * (not the next item in list) when the current item is coming to its end. + * Create an interval to run every second to update display for the timer + * for inaccessible canvas message display. Using useCallback to cache the + * function as this doesn't need to change with component re-renders */ - var handleEnded = React__default["default"].useMemo(function () { - return throttle_1(function () { - var isLastCanvas = cIndexRef.current === lastCanvasIndex; - /** - * Do nothing if Canvas is not multi-sourced AND autoAdvance is turned off - * OR current Canvas is the last Canvas in the Manifest - */ - if ((!autoAdvanceRef.current || isLastCanvas) && !hasMultiItems) { - return; + var createDisplayTimeInterval = React.useCallback(function () { + var createTime = new Date().getTime(); + messageIntervalRef.current = setInterval(function () { + var now = new Date().getTime(); + var timeRemaining = (CANVAS_MESSAGE_TIMEOUT - (now - createTime)) / 1000; + if (timeRemaining > 0) { + setMessageTime(Math.ceil(timeRemaining)); } else { - var _structuresRef$curren; - // Remove all the existing structure related markers in the player - if (playerRef.current && playerRef.current.markers) { - playerRef.current.pause(); - setFragmentMarker(null); - playerRef.current.markers.removeAll(); + // Advance to next Canvas when timer ends + if (canvasIndexRef.current < lastCanvasIndex && autoAdvance) { + manifestDispatch({ + canvasIndex: canvasIndexRef.current + 1, + type: 'switchCanvas' + }); } - if (hasMultiItems) { - // When there are multiple sources in a single canvas - // advance to next source - if (srcIndex + 1 < targets.length) { - manifestDispatch({ - srcIndex: srcIndex + 1, - type: 'setSrcIndex' - }); - playerDispatch({ - currentTime: 0, - type: 'setCurrentTime' - }); - playerRef.current.play(); - } else { - return; - } - } else if (((_structuresRef$curren = structuresRef.current) === null || _structuresRef$curren === void 0 ? void 0 : _structuresRef$curren.length) > 0) { - var nextItem = structuresRef.current[cIndexRef.current + 1]; - if (nextItem) { - manifestDispatch({ - canvasIndex: cIndexRef.current + 1, - type: 'switchCanvas' - }); - - // Reset startTime and currentTime to zero - playerDispatch({ - startTime: 0, - type: 'setTimeFragment' - }); - playerDispatch({ - currentTime: 0, - type: 'setCurrentTime' - }); + clearDisplayTimeInterval(); + } + }, 1000); + }); - // Get first timespan in the next canvas - var firstTimespanInNextCanvas = canvasSegmentsRef.current.filter(function (t) { - return t.canvasIndex === nextItem.canvasIndex && t.itemIndex === 1; - }); - // If the nextItem doesn't have an ID (a Canvas media fragment) pick the first timespan - // in the next Canvas - var nextFirstItem = nextItem.id != undefined ? nextItem : firstTimespanInNextCanvas[0]; - var start = 0; - if (nextFirstItem != undefined && nextFirstItem.id != undefined) { - start = getMediaFragment(nextFirstItem.id, canvasDurationRef.current).start; - } + // Cleanup interval created for timer display for inaccessible message + var clearDisplayTimeInterval = React.useCallback(function () { + clearInterval(messageIntervalRef.current); + messageIntervalRef.current = null; + }); + return { + messageTime: messageTime, + clearDisplayTimeInterval: clearDisplayTimeInterval, + createDisplayTimeInterval: createDisplayTimeInterval + }; + }; - // If there's a timespan item at the start of the next canvas - // mark it as the currentNavItem. Otherwise empty out the currentNavItem. - if (start === 0) { - manifestDispatch({ - item: nextFirstItem, - type: 'switchItem' - }); - } else if (nextFirstItem.isEmpty) { - // Switch the currentNavItem and clear isEnded flag - manifestDispatch({ - item: nextFirstItem, - type: 'switchItem' - }); - playerRef.current.currentTime(start); - // Only play if the next item is not an inaccessible item - if (!nextItem.isEmpty) playerRef.current.play(); - } - } - } - } + /** + * Handle global state updates and local state updates for structured + * navigation related components based on the user interactions and + * player status updates + * @param {Object} obj + * @param {Number} obj.itemIndex + * @param {Boolean} obj.isRoot + * @param {String} obj.itemId URL of the struct item + * @param {Object} obj.liRef React ref for li element for struct item + * @param {Object} obj.sectionRef React ref for collapsible ul element + * @param {Boolean} obj.isCanvas + * @param {Number} obj.canvasDuration + * @param {Function} obj.setIsOpen + * @returns + */ + var useActiveStructure = function useActiveStructure(_ref5) { + var itemIndex = _ref5.itemIndex, + isRoot = _ref5.isRoot, + itemId = _ref5.itemId, + liRef = _ref5.liRef, + sectionRef = _ref5.sectionRef, + isCanvas = _ref5.isCanvas, + canvasDuration = _ref5.canvasDuration, + setIsOpen = _ref5.setIsOpen; + var playerDispatch = React.useContext(PlayerDispatchContext); + var manifestState = React.useContext(ManifestStateContext); + var canvasIndex = manifestState.canvasIndex, + currentNavItem = manifestState.currentNavItem, + playlist = manifestState.playlist; + var isPlaylist = playlist.isPlaylist; + var isActiveLi = React.useMemo(function () { + return itemId != undefined && (currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.id) === itemId && (isPlaylist || !isCanvas) && (currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.canvasIndex) === canvasIndex + 1 ? true : false; + }, [currentNavItem, canvasIndex]); + var isActiveSection = React.useMemo(function () { + var isCurrentSection = canvasIndex + 1 === itemIndex; + // Do not mark root range as active + if (isCurrentSection && !isRoot) { + // Collapse the section in structured navigation + setIsOpen(true); + return true; + } else { + return false; + } + }, [canvasIndex]); + var handleClick = React.useCallback(function (e) { + e.preventDefault(); + e.stopPropagation(); + var _getMediaFragment = getMediaFragment(itemId, canvasDuration), + start = _getMediaFragment.start, + end = _getMediaFragment.end; + var inRange = checkSrcRange({ + start: start, + end: end + }, { + end: canvasDuration }); - }, [cIndexRef.current]); - - /** - * Handle the 'timeUpdate' event emitted by VideoJS player. - * The current time of the playhead used to show structure in the player's - * time rail as the playhead arrives at a start time of an existing structure - * item. When the current time is inside an item, that time fragment is highlighted - * in the player's time rail. - * Using throttle helps for smooth updates by cancelling and cleaning up intermediate - * delayed function calls. - */ - var handleTimeUpdate = React__default["default"].useMemo(function () { - return throttle_1(function () { - var player = playerRef.current; - if (player !== null && isReadyRef.current) { - var _player$currentTime; - var playerTime = (_player$currentTime = player.currentTime()) !== null && _player$currentTime !== void 0 ? _player$currentTime : currentTimeRef.current; - if (hasMultiItems && srcIndexRef.current > 0) { - playerTime = playerTime + targets[srcIndexRef.current].altStart; - } - var activeSegment = getActiveSegment(playerTime); - // the active segment has changed - if (activeIdRef.current !== (activeSegment === null || activeSegment === void 0 ? void 0 : activeSegment.id)) { - if (activeSegment === null) { - /** - * Clear currentNavItem and other related state variables to update the tracker - * in structure navigation and highlights within the player. - */ - manifestDispatch({ - item: null, - type: 'switchItem' - }); - setActiveId(null); - setFragmentMarker(null); - } else { - // Set the active segment in state - manifestDispatch({ - item: activeSegment, - type: 'switchItem' - }); - setActiveId(activeSegment.id); - if (!isPlaylist && player.markers) { - var _getMediaFragment = getMediaFragment(activeSegment.id, activeSegment.canvasDuration), - start = _getMediaFragment.start, - end = _getMediaFragment.end; - playerDispatch({ - endTime: end, - startTime: start, - type: 'setTimeFragment' - }); - if (start !== end) { - // don't let marker extend past the end of the canvas - var markerEnd = end > activeSegment.canvasDuration ? activeSegment.canvasDuration : end; - setFragmentMarker({ - time: start, - duration: markerEnd - start, - text: start, - "class": 'ramp--track-marker--fragment' - }); - } else { - // to prevent zero duration fragments I suppose - setFragmentMarker(null); - } - } else if (fragmentMarker !== null) { - setFragmentMarker(null); - } - } - } - } - }, 10); - }, []); - - /** - * Update global state only when a user pause the player by using the - * player interface or keyboard shortcuts - */ - var handlePause = function handlePause() { - if (isPlayingRef.current) { + /* + Only continue the click action if not both start and end times of + the timespan are not outside Canvas' duration + */ + if (inRange) { playerDispatch({ - isPlaying: false, - type: 'setPlayingStatus' + clickedUrl: itemId, + type: 'navClick' }); - } - }; - - /** - * Toggle play/pause on video touch for mobile browsers - * @param {Object} e onTouchEnd event - */ - var mobilePlayToggle = function mobilePlayToggle(e) { - if (e.changedTouches[0].clientX == touchX && e.changedTouches[0].clientY == touchY) { - if (player.paused()) { - player.play(); - } else { - player.pause(); + liRef.current.isClicked = true; + if (sectionRef.current) { + sectionRef.current.isClicked = true; } } + }); + return { + isActiveSection: isActiveSection, + isActiveLi: isActiveLi, + handleClick: handleClick, + canvasIndex: canvasIndex, + currentNavItem: currentNavItem, + isPlaylist: isPlaylist }; - - /** - * Save coordinates of touch start for comparison to touch end to prevent play/pause - * when user is scrolling. - * @param {Object} e onTouchStart event - */ - var touchX = null; - var touchY = null; - var saveTouchStartCoords = function saveTouchStartCoords(e) { - touchX = e.touches[0].clientX; - touchY = e.touches[0].clientY; + }; + var useTranscripts = function useTranscripts(_ref6) { + var manifestUrl = _ref6.manifestUrl, + playerID = _ref6.playerID, + setCurrentTime = _ref6.setCurrentTime, + transcripts = _ref6.transcripts; + var manifestState = React.useContext(ManifestStateContext); + var playerState = React.useContext(PlayerStateContext); + var NO_TRANSCRIPTS_MSG = 'No valid Transcript(s) found, please check again.'; + var INVALID_URL_MSG = 'Invalid URL for transcript, please check again.'; + var INVALID_VTT = 'Invalid WebVTT file, please check again.'; + var INVALID_TIMESTAMP = 'Invalid timestamp format in cue(s), please check again.'; + var NO_SUPPORT_MSG = 'Transcript format is not supported, please check again.'; + var abortController = new AbortController(); + var canvasIndexRef = React.useRef(); + var setCanvasIndex = function setCanvasIndex(c) { + abortController.abort(); + canvasIndexRef.current = c; }; + var playerRef = React.useRef(null); + var playerIntervalRef = React.useRef(null); + var _useState19 = React.useState(true), + _useState20 = _slicedToArray(_useState19, 2), + isEmpty = _useState20[0], + setIsEmpty = _useState20[1]; + var _useState21 = React.useState(true), + _useState22 = _slicedToArray(_useState21, 2), + isLoading = _useState22[0], + setIsLoading = _useState22[1]; + var _useState23 = React.useState([]), + _useState24 = _slicedToArray(_useState23, 2), + transcript = _useState24[0], + setTranscript = _useState24[1]; + var _useState25 = React.useState([]), + _useState26 = _slicedToArray(_useState25, 2), + transcriptsList = _useState26[0], + setTranscriptsList = _useState26[1]; + var _useState27 = React.useState({ + title: null, + filename: null, + id: null, + tUrl: null, + tType: null, + tFileExt: null, + isMachineGen: false, + tError: null + }), + _useState28 = _slicedToArray(_useState27, 2), + transcriptInfo = _useState28[0], + setTranscriptInfo = _useState28[1]; + var _useState29 = React.useState([]), + _useState30 = _slicedToArray(_useState29, 2), + canvasTranscripts = _useState30[0], + setCanvasTranscripts = _useState30[1]; + // Store transcript data in state to avoid re-requesting file contents + var _useState31 = React.useState([]), + _useState32 = _slicedToArray(_useState31, 2), + cachedTranscripts = _useState32[0], + setCachedTranscripts = _useState32[1]; + var _useState33 = React.useState(), + _useState34 = _slicedToArray(_useState33, 2), + selectedTranscript = _useState34[0], + setSelectedTranscript = _useState34[1]; /** - * Get the segment, which encapsulates the current time of the playhead, - * from a list of media fragments in the current canvas. - * @param {Number} time playhead's current time + * Start an interval at the start of the component to poll the + * canvasindex attribute changes in the player on the page */ - var getActiveSegment = function getActiveSegment(time) { - // Adjust time for multi-item canvases - var currentTime = time; - if (hasMultiItems) { - currentTime = currentTime + targets[srcIndex].altStart; - } - if (playlist.isPlaylist) { - // For playlists timespans and canvasIdex are mapped one-to-one - return canvasSegmentsRef.current[cIndexRef.current]; + React.useEffect(function () { + if (manifestState && playerState) { + canvasIndexRef.current = manifestState.canvasIndex; + playerRef.current = playerState.player; } else { - // Find the relevant media segment from the structure - var _iterator = _createForOfIteratorHelper$2(canvasSegmentsRef.current), - _step; - try { - for (_iterator.s(); !(_step = _iterator.n()).done;) { - var segment = _step.value; - var id = segment.id, - isCanvas = segment.isCanvas, - _canvasIndex = segment.canvasIndex; - if (_canvasIndex == cIndexRef.current + 1) { - // Canvases without structure has the Canvas information - // in Canvas-level item as a navigable link - if (isCanvas) { - return segment; - } - var segmentRange = getMediaFragment(id, canvasDuration); - var isInRange = checkSrcRange(segmentRange, canvasDuration); - var isInSegment = currentTime >= segmentRange.start && currentTime < segmentRange.end; - if (isInSegment && isInRange) { - return segment; - } + playerIntervalRef.current = setInterval(function () { + var domPlayer = document.getElementById(playerID); + if (!domPlayer) { + console.warn("Cannot find player, ".concat(playerID, " on page. Transcript synchronization is disabled")); + // Inaccessible canvas => stop loading spinner + setIsLoading(false); + } else { + if (domPlayer.player) playerRef.current = domPlayer.player;else playerRef.current = domPlayer; + } + if (playerRef.current) { + var cIndex = parseInt(playerRef.current.canvasIndex); + if (Number.isNaN(cIndex)) cIndex = 0; + if (cIndex !== canvasIndexRef.current) { + // Clear the transcript text in the component + setTranscript([]); + setCanvasIndex(cIndex); + setCurrentTime(playerRef.current.currentTime()); } } - } catch (err) { - _iterator.e(err); - } finally { - _iterator.f(); - } - return null; + }, 500); + } + if (playerRef.current) { + playerRef.current.on('timeupdate', function () { + setCurrentTime(playerRef.current.currentTime()); + }); + } + }, [manifestState]); + React.useEffect(function () { + if ((transcripts === null || transcripts === void 0 ? void 0 : transcripts.length) === 0 && !manifestUrl) { + // When both required props are invalid + setIsLoading(false); + setTranscript([]); + setTranscriptInfo({ + tType: TRANSCRIPT_TYPES.noTranscript, + id: '', + tError: NO_TRANSCRIPTS_MSG + }); + } else { + loadTranscripts(transcripts); } - }; - /** - * Create an interval to run every second to update display for the timer - * for inaccessible canvas message display. Using useCallback to cache the - * function as this doesn't need to change with component re-renders - */ - var createDisplayTimeInterval = React__default["default"].useCallback(function () { - if (!autoAdvanceRef.current) return; - var createTime = new Date().getTime(); - messageIntervalRef.current = setInterval(function () { - var now = new Date().getTime(); - var timeRemaining = (CANVAS_MESSAGE_TIMEOUT - (now - createTime)) / 1000; - if (timeRemaining > 0) { - setMessageTime(Math.ceil(timeRemaining)); - } else { - clearDisplayTimeInterval(); - } - }, 1000); + // Clean up state when the component unmounts + return function () { + clearInterval(playerIntervalRef.current); + }; }, []); /** - * Cleanup interval created for timer display for inaccessible message + * If a list of transcripts is given in the props, then sanitize them + * to match the expected format in the component. + * If not fallback to reading transcripts from a given manifest URL. + * @param {Array} transcripts list of transcripts from props */ - var clearDisplayTimeInterval = React__default["default"].useCallback(function () { - clearInterval(messageIntervalRef.current); - messageIntervalRef.current = null; - }); - return /*#__PURE__*/React__default["default"].createElement("div", null, /*#__PURE__*/React__default["default"].createElement("div", { - "data-vjs-player": true, - "data-canvasindex": cIndexRef.current - }, canvasIsEmptyRef.current && /*#__PURE__*/React__default["default"].createElement("div", { - "data-testid": "inaccessible-message-display" - // These styles needs to be inline for the poster to display within the Video boundaries - , - style: { - position: !playerRef.current ? 'relative' : 'absolute', - top: 0, - left: 0, - right: 0, - bottom: 0, - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - fontSize: 'medium', - color: '#fff', - backgroundColor: 'black', - zIndex: 101, - aspectRatio: !playerRef.current ? '16/9' : '', - textAlign: 'center' - } - }, /*#__PURE__*/React__default["default"].createElement("p", { - className: "ramp--media-player_inaccessible-message-content", - "data-testid": "inaccessible-message-content", - dangerouslySetInnerHTML: { - __html: placeholderText - } - }), /*#__PURE__*/React__default["default"].createElement("div", { - className: "ramp--media-player_inaccessible-message-buttons" - }, canvasIndex >= 1 && /*#__PURE__*/React__default["default"].createElement("button", { - "aria-label": "Go back to previous item", - onClick: function onClick() { - return loadPrevOrNext(canvasIndex - 1, true); - }, - "data-testid": "inaccessible-previous-button" - }, /*#__PURE__*/React__default["default"].createElement(SectionButtonIcon, { - flip: true - }), " Previous"), canvasIndex != lastCanvasIndex && /*#__PURE__*/React__default["default"].createElement("button", { - "aria-label": "Go to next item", - onClick: function onClick() { - return loadPrevOrNext(canvasIndex + 1, true); - }, - "data-testid": "inaccessible-next-button" - }, "Next ", /*#__PURE__*/React__default["default"].createElement(SectionButtonIcon, null))), canvasIndex != lastCanvasIndex && /*#__PURE__*/React__default["default"].createElement("p", { - "data-testid": "inaccessible-message-timer", - className: "ramp--media-player_inaccessible-message-timer ".concat(autoAdvanceRef.current ? '' : 'hidden') - }, "Next item in ".concat(messageTime, " second").concat(messageTime === 1 ? '' : 's'))), /*#__PURE__*/React__default["default"].createElement("video", { - "data-testid": "videojs-".concat(isVideo ? 'video' : 'audio', "-element"), - "data-canvasindex": cIndexRef.current, - ref: videoJSRef, - className: "video-js vjs-big-play-centered vjs-theme-ramp vjs-disabled ".concat(IS_ANDROID ? 'is-mobile' : ''), - onTouchStart: saveTouchStartCoords, - onTouchEnd: mobilePlayToggle, - style: { - display: "".concat(canvasIsEmptyRef.current ? 'none' : '') + var loadTranscripts = /*#__PURE__*/function () { + var _ref7 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee(transcripts) { + var allTranscripts; + return regenerator.wrap(function _callee$(_context) { + while (1) switch (_context.prev = _context.next) { + case 0: + if (!((transcripts === null || transcripts === void 0 ? void 0 : transcripts.length) > 0 + // transcripts prop is processed first if given + )) { + _context.next = 6; + break; + } + _context.next = 3; + return sanitizeTranscripts(transcripts); + case 3: + _context.t0 = _context.sent; + _context.next = 9; + break; + case 6: + _context.next = 8; + return readSupplementingAnnotations(manifestUrl); + case 8: + _context.t0 = _context.sent; + case 9: + allTranscripts = _context.t0; + setTranscriptsList(allTranscripts !== null && allTranscripts !== void 0 ? allTranscripts : []); + initTranscriptData(allTranscripts !== null && allTranscripts !== void 0 ? allTranscripts : []); + case 12: + case "end": + return _context.stop(); + } + }, _callee); + })); + return function loadTranscripts(_x) { + return _ref7.apply(this, arguments); + }; + }(); + var initTranscriptData = function initTranscriptData(allTranscripts) { + var _getCanvasT, _getTItems; + // When canvasIndex updates -> return + if (abortController.signal.aborted) return; + var getCanvasT = function getCanvasT(tr) { + return tr.filter(function (t) { + return t.canvasId == canvasIndexRef.current; + }); + }; + var getTItems = function getTItems(tr) { + return getCanvasT(tr)[0].items; + }; + /** + * When transcripts prop is empty + * OR the respective canvas doesn't have transcript data + * OR canvas' transcript items list is empty + */ + if (!(allTranscripts !== null && allTranscripts !== void 0 && allTranscripts.length) > 0 || !((_getCanvasT = getCanvasT(allTranscripts)) !== null && _getCanvasT !== void 0 && _getCanvasT.length) > 0 || !((_getTItems = getTItems(allTranscripts)) !== null && _getTItems !== void 0 && _getTItems.length) > 0) { + setIsEmpty(true); + setTranscript([]); + setStateVar(undefined); + } else { + setIsEmpty(false); + var cTranscripts = getCanvasT(allTranscripts)[0]; + setCanvasTranscripts(cTranscripts.items); + setStateVar(cTranscripts.items[0]); } - })), (hasStructure || playlist.isPlaylist) && /*#__PURE__*/React__default["default"].createElement("div", { - className: "vjs-track-scrubber-container hidden", - ref: trackScrubberRef, - id: "track_scrubber" - }, /*#__PURE__*/React__default["default"].createElement("p", { - className: "vjs-time track-currenttime", - role: "presentation" - }), /*#__PURE__*/React__default["default"].createElement("span", { - type: "range", - "aria-label": "Track scrubber", - role: "slider", - tabIndex: 0, - className: "vjs-track-scrubber", - style: { - width: '100%' + }; + React.useEffect(function () { + if ((transcriptsList === null || transcriptsList === void 0 ? void 0 : transcriptsList.length) > 0 && canvasIndexRef.current != undefined) { + var cTranscripts = transcriptsList.filter(function (tr) { + return tr.canvasId == canvasIndexRef.current; + })[0]; + setCanvasTranscripts(cTranscripts.items); + setStateVar(cTranscripts.items[0]); } - }, !IS_TOUCH_ONLY && /*#__PURE__*/React__default["default"].createElement("span", { - className: "tooltiptext", - ref: scrubberTooltipRef, - "aria-hidden": true, - role: "presentation" - })), /*#__PURE__*/React__default["default"].createElement("p", { - className: "vjs-time track-duration", - role: "presentation" - }))); - } - VideoJSPlayer.propTypes = { - isVideo: PropTypes.bool, - hasMultipleCanvases: PropTypes.bool, - isPlaylist: PropTypes.bool, - trackScrubberRef: PropTypes.object, - scrubberTooltipRef: PropTypes.object, - tracks: PropTypes.array, - placeholderText: PropTypes.string, - renderingFiles: PropTypes.array, - enableFileDownload: PropTypes.bool, - cancelAutoAdvance: PropTypes.func, - loadPrevOrNext: PropTypes.func, - lastCanvasIndex: PropTypes.number, - videoJSOptions: PropTypes.object - }; + }, [canvasIndexRef.current]); // helps to load initial transcript with async req - var Play = "Play"; - var Pause = "Pause"; - var Replay = "Replay"; - var Duration = "Duration"; - var LIVE = "LIVE"; - var Loaded = "Loaded"; - var Progress = "Progress"; - var Fullscreen = "Fullscreen"; - var Mute = "Mute"; - var Unmute = "Unmute"; - var Subtitles = "Subtitles"; - var Captions = "Captions"; - var Chapters = "Chapters"; - var Descriptions = "Descriptions"; - var Close = "Close"; - var Text = "Text"; - var White = "White"; - var Black = "Black"; - var Red = "Red"; - var Green = "Green"; - var Blue = "Blue"; - var Yellow = "Yellow"; - var Magenta = "Magenta"; - var Cyan = "Cyan"; - var Background = "Background"; - var Window = "Window"; - var Transparent = "Transparent"; - var Opaque = "Opaque"; - var None = "None"; - var Raised = "Raised"; - var Depressed = "Depressed"; - var Uniform = "Uniform"; - var Casual = "Casual"; - var Script = "Script"; - var Reset = "Reset"; - var Done = "Done"; - var Color = "Color"; - var Opacity = "Opacity"; - var en = { - "Audio Player": "Audio Player", - "Video Player": "Video Player", - Play: Play, - Pause: Pause, - Replay: Replay, - "Current Time": "Current Time", - Duration: Duration, - "Remaining Time": "Remaining Time", - "Stream Type": "Stream Type", - LIVE: LIVE, - "Seek to live, currently behind live": "Seek to live, currently behind live", - "Seek to live, currently playing live": "Seek to live, currently playing live", - Loaded: Loaded, - Progress: Progress, - "Progress Bar": "Progress Bar", - "progress bar timing: currentTime={1} duration={2}": "{1} of {2}", - Fullscreen: Fullscreen, - "Exit Fullscreen": "Exit Fullscreen", - Mute: Mute, - Unmute: Unmute, - "Playback Rate": "Playback Rate", - Subtitles: Subtitles, - "subtitles off": "subtitles off", - Captions: Captions, - "captions off": "captions off", - Chapters: Chapters, - Descriptions: Descriptions, - "descriptions off": "descriptions off", - "Audio Track": "Audio Track", - "Volume Level": "Volume Level", - "You aborted the media playback": "You aborted the media playback", - "A network error caused the media download to fail part-way.": "A network error caused the media download to fail part-way.", - "The media could not be loaded, either because the server or network failed or because the format is not supported.": "The media could not be loaded, either because the server or network failed or because the format is not supported.", - "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.", - "No compatible source was found for this media.": "No compatible source was found for this media.", - "The media is encrypted and we do not have the keys to decrypt it.": "The media is encrypted and we do not have the keys to decrypt it.", - "Play Video": "Play Video", - Close: Close, - "Close Modal Dialog": "Close Modal Dialog", - "Modal Window": "Modal Window", - "This is a modal window": "This is a modal window", - "This modal can be closed by pressing the Escape key or activating the close button.": "This modal can be closed by pressing the Escape key or activating the close button.", - ", opens captions settings dialog": ", opens captions settings dialog", - ", opens subtitles settings dialog": ", opens subtitles settings dialog", - ", opens descriptions settings dialog": ", opens descriptions settings dialog", - ", selected": ", selected", - "captions settings": "captions settings", - "subtitles settings": "subtitles settings", - "descriptions settings": "descriptions settings", - Text: Text, - White: White, - Black: Black, - Red: Red, - Green: Green, - Blue: Blue, - Yellow: Yellow, - Magenta: Magenta, - Cyan: Cyan, - Background: Background, - Window: Window, - Transparent: Transparent, - "Semi-Transparent": "Semi-Transparent", - Opaque: Opaque, - "Font Size": "Font Size", - "Text Edge Style": "Text Edge Style", - None: None, - Raised: Raised, - Depressed: Depressed, - Uniform: Uniform, - "Drop shadow": "Drop shadow", - "Font Family": "Font Family", - "Proportional Sans-Serif": "Proportional Sans-Serif", - "Monospace Sans-Serif": "Monospace Sans-Serif", - "Proportional Serif": "Proportional Serif", - "Monospace Serif": "Monospace Serif", - Casual: Casual, - Script: Script, - "Small Caps": "Small Caps", - Reset: Reset, - "restore all settings to the default values": "restore all settings to the default values", - Done: Done, - "Caption Settings Dialog": "Caption Settings Dialog", - "Beginning of dialog window. Escape will cancel and close the window.": "Beginning of dialog window. Escape will cancel and close the window.", - "End of dialog window.": "End of dialog window.", - "{1} is loading.": "{1} is loading.", - "Exit Picture-in-Picture": "Exit Picture-in-Picture", - "Picture-in-Picture": "Picture-in-Picture", - "No content": "No content", - Color: Color, - Opacity: Opacity, - "Text Background": "Text Background", - "Caption Area Background": "Caption Area Background", - "Playing in Picture-in-Picture": "Playing in Picture-in-Picture", - "Skip backward {1} seconds": "Skip backward {1} seconds", - "Skip forward {1} seconds": "Skip forward {1} seconds" - }; - - function ownKeys$3(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } - function _objectSpread$3(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$3(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$3(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } - var PLAYER_ID = "iiif-media-player"; - var MediaPlayer = function MediaPlayer(_ref) { - var _ref$enableFileDownlo = _ref.enableFileDownload, - enableFileDownload = _ref$enableFileDownlo === void 0 ? false : _ref$enableFileDownlo, - _ref$enablePIP = _ref.enablePIP, - enablePIP = _ref$enablePIP === void 0 ? false : _ref$enablePIP, - _ref$enablePlaybackRa = _ref.enablePlaybackRate, - enablePlaybackRate = _ref$enablePlaybackRa === void 0 ? false : _ref$enablePlaybackRa, - _ref$enableTitleLink = _ref.enableTitleLink, - enableTitleLink = _ref$enableTitleLink === void 0 ? false : _ref$enableTitleLink, - _ref$withCredentials = _ref.withCredentials, - withCredentials = _ref$withCredentials === void 0 ? false : _ref$withCredentials, - _ref$language = _ref.language, - language = _ref$language === void 0 ? 'en' : _ref$language; - var manifestState = useManifestState(); - var playerState = usePlayerState(); - var playerDispatch = usePlayerDispatch(); - var manifestDispatch = useManifestDispatch(); - var _useErrorBoundary = reactErrorBoundary.useErrorBoundary(), - showBoundary = _useErrorBoundary.showBoundary; - var _React$useState = React__default["default"].useState({ - error: '', - sources: [], - tracks: [], - poster: null - }), - _React$useState2 = _slicedToArray(_React$useState, 2), - playerConfig = _React$useState2[0], - setPlayerConfig = _React$useState2[1]; - var _React$useState3 = React__default["default"].useState(true), - _React$useState4 = _slicedToArray(_React$useState3, 2), - firstLoad = _React$useState4[0], - setFirstLoad = _React$useState4[1]; - var _React$useState5 = React__default["default"].useState(false), - _React$useState6 = _slicedToArray(_React$useState5, 2), - ready = _React$useState6[0], - setReady = _React$useState6[1]; - var _React$useState7 = React__default["default"].useState(canvasIndex), - _React$useState8 = _slicedToArray(_React$useState7, 2), - cIndex = _React$useState8[0], - setCIndex = _React$useState8[1]; - var _React$useState9 = React__default["default"].useState(), - _React$useState10 = _slicedToArray(_React$useState9, 2), - isMultiSourced = _React$useState10[0], - setIsMultiSourced = _React$useState10[1]; - var _React$useState11 = React__default["default"].useState(false), - _React$useState12 = _slicedToArray(_React$useState11, 2), - isMultiCanvased = _React$useState12[0], - setIsMultiCanvased = _React$useState12[1]; - var _React$useState13 = React__default["default"].useState(0), - _React$useState14 = _slicedToArray(_React$useState13, 2), - lastCanvasIndex = _React$useState14[0], - setLastCanvasIndex = _React$useState14[1]; - var _React$useState15 = React__default["default"].useState(), - _React$useState16 = _slicedToArray(_React$useState15, 2), - isVideo = _React$useState16[0], - setIsVideo = _React$useState16[1]; - var _React$useState17 = React__default["default"].useState(), - _React$useState18 = _slicedToArray(_React$useState17, 2), - options = _React$useState18[0], - setOptions = _React$useState18[1]; - var _React$useState19 = React__default["default"].useState(), - _React$useState20 = _slicedToArray(_React$useState19, 2), - renderingFiles = _React$useState20[0], - setRenderingFiles = _React$useState20[1]; - var canvasIndex = manifestState.canvasIndex, - allCanvases = manifestState.allCanvases, - manifest = manifestState.manifest, - canvasIsEmpty = manifestState.canvasIsEmpty, - srcIndex = manifestState.srcIndex, - targets = manifestState.targets, - playlist = manifestState.playlist, - autoAdvance = manifestState.autoAdvance, - hasStructure = manifestState.hasStructure, - customStart = manifestState.customStart, - renderings = manifestState.renderings; - var playerFocusElement = playerState.playerFocusElement, - currentTime = playerState.currentTime; - var currentTimeRef = React__default["default"].useRef(); - currentTimeRef.current = currentTime; - var canvasIndexRef = React__default["default"].useRef(); - canvasIndexRef.current = canvasIndex; - var autoAdvanceRef = React__default["default"].useRef(); - autoAdvanceRef.current = autoAdvance; - var lastCanvasIndexRef = React__default["default"].useRef(); - lastCanvasIndexRef.current = lastCanvasIndex; - var trackScrubberRef = React__default["default"].useRef(); - var timeToolRef = React__default["default"].useRef(); - var videoJSLangMap = React__default["default"].useRef('{}'); - var canvasMessageTimerRef = React__default["default"].useRef(null); - - // FIXME:: Dynamic language imports break with rollup configuration when packaging - // Using dynamic imports to enforce code-splitting in webpack - // https://webpack.js.org/api/module-methods/#dynamic-expressions-in-import - var loadVideoJSLanguageMap = React__default["default"].useMemo(function () { - return /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee() { - var resources; - return regenerator.wrap(function _callee$(_context) { - while (1) switch (_context.prev = _context.next) { + var setStateVar = /*#__PURE__*/function () { + var _ref8 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee2(transcript) { + var _transcript, id, title, filename, url, isMachineGen, format, cached, _cached$, tData, tFileExt, tType, tError; + return regenerator.wrap(function _callee2$(_context2) { + while (1) switch (_context2.prev = _context2.next) { case 0: - _context.prev = 0; - _context.next = 3; - return import("video.js/dist/lang/".concat(language, ".json")); - case 3: - resources = _context.sent; - videoJSLangMap.current = JSON.stringify(resources); - _context.next = 11; + if (!(!transcript || transcript == undefined)) { + _context2.next = 5; + break; + } + setIsEmpty(true); + setIsLoading(false); + setTranscriptInfo({ + tType: TRANSCRIPT_TYPES.noTranscript, + id: '', + tError: NO_TRANSCRIPTS_MSG + }); + return _context2.abrupt("return"); + case 5: + // set isEmpty flag to render transcripts UI + setIsEmpty(false); + _transcript = transcript, id = _transcript.id, title = _transcript.title, filename = _transcript.filename, url = _transcript.url, isMachineGen = _transcript.isMachineGen, format = _transcript.format; // Check cached transcript data + cached = cachedTranscripts.filter(function (ct) { + return ct.id == id && ct.canvasId == canvasIndexRef.current; + }); + if (!((cached === null || cached === void 0 ? void 0 : cached.length) > 0)) { + _context2.next = 15; + break; + } + // Load cached transcript data into the component + _cached$ = cached[0], tData = _cached$.tData, tFileExt = _cached$.tFileExt, tType = _cached$.tType, tError = _cached$.tError; + setTranscript(tData); + setTranscriptInfo({ + title: title, + filename: filename, + id: id, + isMachineGen: isMachineGen, + tType: tType, + tUrl: url, + tFileExt: tFileExt, + tError: tError + }); + setSelectedTranscript(url); + _context2.next = 17; break; - case 7: - _context.prev = 7; - _context.t0 = _context["catch"](0); - console.warn("".concat(language, " is not available, defaulting to English")); - videoJSLangMap.current = JSON.stringify(en); - case 11: + case 15: + _context2.next = 17; + return Promise.resolve(parseTranscriptData(url, canvasIndexRef.current, format)).then(function (value) { + if (value != null) { + var _tData = value.tData, + tUrl = value.tUrl, + _tType = value.tType, + _tFileExt = value.tFileExt; + var newError = ''; + switch (_tType) { + case TRANSCRIPT_TYPES.invalid: + newError = INVALID_URL_MSG; + break; + case TRANSCRIPT_TYPES.noTranscript: + newError = NO_TRANSCRIPTS_MSG; + break; + case TRANSCRIPT_TYPES.noSupport: + newError = NO_SUPPORT_MSG; + break; + case TRANSCRIPT_TYPES.invalidVTT: + newError = INVALID_VTT; + break; + case TRANSCRIPT_TYPES.invalidTimestamp: + newError = INVALID_TIMESTAMP; + break; + } + setTranscript(_tData); + setTranscriptInfo({ + title: title, + filename: filename, + id: id, + isMachineGen: isMachineGen, + tType: _tType, + tUrl: tUrl, + tFileExt: _tFileExt, + tError: newError + }); + setSelectedTranscript(tUrl); + transcript = _objectSpread$5(_objectSpread$5({}, transcript), {}, { + tType: _tType, + tData: _tData, + tFileExt: _tFileExt, + canvasId: canvasIndexRef.current, + tError: newError + }); + // Cache the transcript info + setCachedTranscripts([].concat(_toConsumableArray(cachedTranscripts), [transcript])); + } + }); + case 17: + setIsLoading(false); + case 18: case "end": - return _context.stop(); + return _context2.stop(); + } + }, _callee2); + })); + return function setStateVar(_x2) { + return _ref8.apply(this, arguments); + }; + }(); + var selectTranscript = React.useCallback(function (selectedId) { + var selectedTranscript = canvasTranscripts.filter(function (tr) { + return tr.id === selectedId; + }); + setStateVar(selectedTranscript[0]); + }, [canvasTranscripts]); + return { + canvasIndexRef: canvasIndexRef, + canvasTranscripts: canvasTranscripts, + isEmpty: isEmpty, + isLoading: isLoading, + NO_SUPPORT_MSG: NO_SUPPORT_MSG, + playerRef: playerRef, + selectedTranscript: selectedTranscript, + selectTranscript: selectTranscript, + transcript: transcript, + transcriptInfo: transcriptInfo + }; + }; + + var classCallCheck = createCommonjsModule(function (module) { + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + module.exports = _classCallCheck, module.exports.__esModule = true, module.exports["default"] = module.exports; + }); + + var _classCallCheck = /*@__PURE__*/getDefaultExportFromCjs(classCallCheck); + + var createClass = createCommonjsModule(function (module) { + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, toPropertyKey(descriptor.key), descriptor); + } + } + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + Object.defineProperty(Constructor, "prototype", { + writable: false + }); + return Constructor; + } + module.exports = _createClass, module.exports.__esModule = true, module.exports["default"] = module.exports; + }); + + var _createClass = /*@__PURE__*/getDefaultExportFromCjs(createClass); + + var assertThisInitialized = createCommonjsModule(function (module) { + function _assertThisInitialized(self) { + if (self === void 0) { + throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); + } + return self; + } + module.exports = _assertThisInitialized, module.exports.__esModule = true, module.exports["default"] = module.exports; + }); + + var _assertThisInitialized = /*@__PURE__*/getDefaultExportFromCjs(assertThisInitialized); + + var getPrototypeOf = createCommonjsModule(function (module) { + function _getPrototypeOf(o) { + module.exports = _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function _getPrototypeOf(o) { + return o.__proto__ || Object.getPrototypeOf(o); + }, module.exports.__esModule = true, module.exports["default"] = module.exports; + return _getPrototypeOf(o); + } + module.exports = _getPrototypeOf, module.exports.__esModule = true, module.exports["default"] = module.exports; + }); + + var _getPrototypeOf = /*@__PURE__*/getDefaultExportFromCjs(getPrototypeOf); + + var superPropBase = createCommonjsModule(function (module) { + function _superPropBase(object, property) { + while (!Object.prototype.hasOwnProperty.call(object, property)) { + object = getPrototypeOf(object); + if (object === null) break; + } + return object; + } + module.exports = _superPropBase, module.exports.__esModule = true, module.exports["default"] = module.exports; + }); + + var get = createCommonjsModule(function (module) { + function _get() { + if (typeof Reflect !== "undefined" && Reflect.get) { + module.exports = _get = Reflect.get.bind(), module.exports.__esModule = true, module.exports["default"] = module.exports; + } else { + module.exports = _get = function _get(target, property, receiver) { + var base = superPropBase(target, property); + if (!base) return; + var desc = Object.getOwnPropertyDescriptor(base, property); + if (desc.get) { + return desc.get.call(arguments.length < 3 ? target : receiver); + } + return desc.value; + }, module.exports.__esModule = true, module.exports["default"] = module.exports; + } + return _get.apply(this, arguments); + } + module.exports = _get, module.exports.__esModule = true, module.exports["default"] = module.exports; + }); + + var _get = /*@__PURE__*/getDefaultExportFromCjs(get); + + var setPrototypeOf = createCommonjsModule(function (module) { + function _setPrototypeOf(o, p) { + module.exports = _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function _setPrototypeOf(o, p) { + o.__proto__ = p; + return o; + }, module.exports.__esModule = true, module.exports["default"] = module.exports; + return _setPrototypeOf(o, p); + } + module.exports = _setPrototypeOf, module.exports.__esModule = true, module.exports["default"] = module.exports; + }); + + var inherits = createCommonjsModule(function (module) { + function _inherits(subClass, superClass) { + if (typeof superClass !== "function" && superClass !== null) { + throw new TypeError("Super expression must either be null or a function"); + } + subClass.prototype = Object.create(superClass && superClass.prototype, { + constructor: { + value: subClass, + writable: true, + configurable: true + } + }); + Object.defineProperty(subClass, "prototype", { + writable: false + }); + if (superClass) setPrototypeOf(subClass, superClass); + } + module.exports = _inherits, module.exports.__esModule = true, module.exports["default"] = module.exports; + }); + + var _inherits = /*@__PURE__*/getDefaultExportFromCjs(inherits); + + var possibleConstructorReturn = createCommonjsModule(function (module) { + var _typeof = _typeof_1["default"]; + + function _possibleConstructorReturn(self, call) { + if (call && (_typeof(call) === "object" || typeof call === "function")) { + return call; + } else if (call !== void 0) { + throw new TypeError("Derived constructors may only return object or undefined"); + } + return assertThisInitialized(self); + } + module.exports = _possibleConstructorReturn, module.exports.__esModule = true, module.exports["default"] = module.exports; + }); + + var _possibleConstructorReturn = /*@__PURE__*/getDefaultExportFromCjs(possibleConstructorReturn); + + function _createSuper$6(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$6(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } + function _isNativeReflectConstruct$6() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } + var SeekBar = videojs__default["default"].getComponent('SeekBar'); + + /** + * Custom component to show progress of playback built on top of + * Video.js' SeekBar component. This customization allows to display + * multiple-sources in a single Canvas as a contiguous time-block for + * the sum of durations of each source and clipped playlist items with + * blocked ranges. + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} props.options + * @param {Number} props.options.nextItemClicked callback func to switch current source + * when displaying multiple sources in a single instance + */ + var VideoJSProgress = /*#__PURE__*/function (_SeekBar) { + _inherits(VideoJSProgress, _SeekBar); + var _super = _createSuper$6(VideoJSProgress); + function VideoJSProgress(player, options) { + var _this; + _classCallCheck(this, VideoJSProgress); + _this = _super.call(this, player, options); + /** + * Set start values for progress bar + * @param {Number} start canvas start time + */ + _defineProperty(_assertThisInitialized(_this), "initializeProgress", function (start) { + _this.setProgress(start); + _this.setInitTime(start); + _this.player.currentTime(start); + }); + _this.addClass('vjs-custom-progress-bar'); + _this.setAttribute('data-testid', 'videojs-custom-progressbar'); + _this.setAttribute('tabindex', 0); + _this.player = player; + _this.options = options; + _this.selectSource = _this.options.nextItemClicked; + _this.playerEventListener; + _this.initTimeRef = /*#__PURE__*/React.createRef(); + _this.progressRef = /*#__PURE__*/React.createRef(); + _this.canvasTargetsRef = /*#__PURE__*/React.createRef(); + _this.srcIndexRef = /*#__PURE__*/React.createRef(); + _this.isMultiSourceRef = /*#__PURE__*/React.createRef(); + _this.currentTimeRef = /*#__PURE__*/React.createRef(); + _this.pointerDragged = false; + _this.totalDuration; + + // Retreive child elements in SeekBar to use for custom updates + _this.playProgress = _this.getChild('PlayProgressBar'); + _this.loadProgress = _this.getChild('LoadProgressBar'); + _this.player.on('ready', function () { + _this.initializeEl(); + _this.updateComponent(); + }); + _this.player.on('loadstart', function () { + _this.updateComponent(); + _this.buildProgressBar(); + }); + _this.player.on('loadeddata', function () { + _this.setInitTime(_this.player.currentTime()); + }); + + // Update our progress bar after the user leaves full screen + _this.player.on('fullscreenchange', function (e) { + if (!_this.player.isFullscreen()) { + _this.setProgress(_this.player.currentTime()); + } + }); + + // Clear interval upon player disposal + _this.player.on('dispose', function () { + clearInterval(_this.playerEventListener); + }); + return _this; + } + _createClass(VideoJSProgress, [{ + key: "setInitTime", + value: function setInitTime(t) { + this.initTimeRef.current = t; + } + }, { + key: "setSrcIndex", + value: function setSrcIndex(i) { + this.srcIndexRef.current = i; + } + }, { + key: "setProgress", + value: function setProgress(p) { + this.progressRef.current = p; + } + }, { + key: "setCanvasTargets", + value: function setCanvasTargets(t) { + this.canvasTargetsRef.current = t; + this.totalDuration = t.reduce(function (acc, c) { + return acc + c.duration; + }, 0); + } + }, { + key: "setIsMultiSource", + value: function setIsMultiSource(m) { + this.isMultiSourceRef.current = m; + } + }, { + key: "setCurrentTime", + value: function setCurrentTime(t) { + this.currentTimeRef.current = t; + } + }, { + key: "updateComponent", + value: + // Update component's variables on Canvas changes + function updateComponent() { + var _this2 = this; + var _this$player = this.player, + srcIndex = _this$player.srcIndex, + targets = _this$player.targets; + this.setSrcIndex(srcIndex); + this.setCanvasTargets(targets); + var cTimes = targets[srcIndex]; + if (cTimes.customStart > cTimes.start) { + this.initializeProgress(cTimes.customStart); + } else { + this.initializeProgress(cTimes.start); + } + this.setIsMultiSource((targets === null || targets === void 0 ? void 0 : targets.length) > 1 ? true : false); + if (!this.playerEventListener) { + /** + * Using a time interval instead of 'timeupdate event in VideoJS, because Safari + * and other browsers in MacOS stops firing the 'timeupdate' event consistently + * after a while + */ + this.playerEventListener = setInterval(function () { + /** + * Abortable inerval for Safari desktop browsers, for a smoother scrubbing + * experience. + * Mobile devices are excluded since they use native iOS player. + */ + if (IS_SAFARI && !IS_IPHONE) { + _this2.abortableTimeupdateHandler(); + } else { + _this2.timeUpdateHandler(); + } + }, 100); + } + } + + /** + * Use Video.js' update function to update time in mobile devices + * when changing Canvases. + * TODO:: this can probably removed by customizing PlayProgressBar and + * LoadProgressBar components? + */ + }, { + key: "update", + value: function update() { + // Need this to make the other updates work + _get(_getPrototypeOf(VideoJSProgress.prototype), "update", this).call(this); + // Explicitly update played range variable on reload for touch devices + if (IS_TOUCH_ONLY && this.player.currentTime() === 0) { + this.removeClass('played-range'); + document.documentElement.style.setProperty('--range-progress', "calc(".concat(0, "%)")); + } + if (IS_MOBILE && IS_SAFARI && this.player.paused()) { + var _this$player$structSt; + var structStart = (_this$player$structSt = this.player.structStart) !== null && _this$player$structSt !== void 0 ? _this$player$structSt : 0; + if (structStart != 0 && this.player.currentTime() === 0) { + this.player.currentTime(structStart); + var played = Math.min(100, Math.max(0, 100 * (structStart / this.totalDuration))); + this.addClass('played-range'); + document.documentElement.style.setProperty('--range-progress', "calc(".concat(played, "%)")); + this.player.structStart = 0; + } + } else { + return; + } + } + }, { + key: "initializeEl", + value: + // Create progress bar using Video.js' SeekBar component + function initializeEl() { + var _this3 = this; + /** + * Build and append placeholder elements to show blocked ranges, + * especially used in playlist context to present clipped items. + */ + var leftBlock = videojs__default["default"].dom.createEl('div', { + className: 'block-stripes', + role: 'presentation', + id: 'left-block' + }); + var rightBlock = videojs__default["default"].dom.createEl('div', { + className: 'block-stripes', + role: 'presentation', + id: 'right-block' + }); + this.el().appendChild(leftBlock); + this.el().appendChild(rightBlock); + + /** + * Add eventlisteners to handle time tool-tip display and progress updates. + * Using pointerup, pointermove, pointerdown events instead of mouseup, + * mousemove, mousedown events to make it work with both mouse pointer + * and touch events. + */ + this.el().addEventListener('mouseenter', function (e) { + _this3.handleMouseMove(e); + }); + this.el().addEventListener('pointerup', function (e) { + if (_this3.pointerDragged) { + _this3.handleMouseUp(e); + } + }); + this.el().addEventListener('pointermove', function (e) { + _this3.handleMouseMove(e); + _this3.pointerDragged = true; + }); + this.el().addEventListener('pointerdown', function (e) { + _this3.handleMouseDown(e); + _this3.pointerDragged = false; + }); + } + }, { + key: "handleMouseMove", + value: function handleMouseMove(e) { + var _this$convertToTime = this.convertToTime(e), + currentTime = _this$convertToTime.currentTime, + offsetx = _this$convertToTime.offsetx; + if (currentTime != undefined) this.setCurrentTime(currentTime); + var mouseTimeDisplay = this.getChild('MouseTimeDisplay'); + if (mouseTimeDisplay) { + var timeTooltip = mouseTimeDisplay.getChild('TimeTooltip'); + var toolTipEl = timeTooltip.el_; + if (currentTime) { + toolTipEl.innerHTML = timeToHHmmss(currentTime); } - }, _callee, null, [[0, 7]]); - })); - }, [language]); - React__default["default"].useEffect(function () { - if (manifest) { - try { - loadVideoJSLanguageMap(); - /* - Always start from the start time relevant to the Canvas only in playlist contexts, - because canvases related to playlist items always start from the given start. - With regular manifests, the start time could be different when using structured - navigation to switch between canvases. - */ - if (canvasIndex == undefined || canvasIndex < 0) { - throw new Error('Invalid canvas index. Please check your Manifest.'); + var pullTooltip = toolTipEl.clientWidth / 2; + toolTipEl.style.left = "".concat(offsetx - pullTooltip, "px"); + } + } + }, { + key: "handleMouseDown", + value: function handleMouseDown(e) { + // Do nothing when right-click is pressed + if (!IS_TOUCH_ONLY && e.buttons === 2) return; + var _this$convertToTime2 = this.convertToTime(e), + currentTime = _this$convertToTime2.currentTime; + _this$convertToTime2._; + var clickedSrc; + if (this.isMultiSourceRef.current) { + clickedSrc = this.canvasTargetsRef.current.find(function (t) { + var virtualEnd = t.altStart + t.duration; + if (currentTime >= t.altStart && currentTime <= virtualEnd) { + return t; + } + }); + } + if (clickedSrc) { + var _clickedSrc$sIndex, _clickedSrc; + var clickedIndex = (_clickedSrc$sIndex = (_clickedSrc = clickedSrc) === null || _clickedSrc === void 0 ? void 0 : _clickedSrc.sIndex) !== null && _clickedSrc$sIndex !== void 0 ? _clickedSrc$sIndex : 0; + if (clickedIndex != this.srcIndexRef.current) { + this.selectSource(clickedSrc.sIndex, currentTime - clickedSrc.altStart); + this.setSrcIndex(clickedIndex); + } else { + this.player.currentTime(currentTime - clickedSrc.altStart); } - initCanvas(canvasIndex, playlist.isPlaylist); + } else { + this.player.currentTime(currentTime); + } - // Deduct 1 from length to compare against canvasIndex, which starts from 0 - var lastIndex = (allCanvases === null || allCanvases === void 0 ? void 0 : allCanvases.length) - 1; - setIsMultiCanvased(lastIndex > 0); - setLastCanvasIndex(lastIndex || 0); - } catch (e) { - showBoundary(e); + /** + * For touch devices, player.currentTime() update doesn't show the + * played range, even though the player's currentTime is properly set. + * Therefore, update the CSS here explicitly. + */ + if (IS_TOUCH_ONLY) { + var played = Math.min(100, Math.max(0, 100 * (currentTime / this.totalDuration))); + this.player.currentTime(currentTime); + this.addClass('played-range'); + document.documentElement.style.setProperty('--range-progress', "calc(".concat(played, "%)")); } } - return function () { - setReady(false); - setCIndex(0); - playerDispatch({ - player: null, - type: 'updatePlayer' - }); - }; - }, [manifest, canvasIndex, srcIndex]); + }, { + key: "handleMouseUp", + value: function handleMouseUp(e) { + this.handleMouseDown(e); + } + }, { + key: "buildProgressBar", + value: function buildProgressBar() { + var _canvasTargetsRef$cur; + // Reset progress-bar for played range on player reload + this.removeClass('played-range'); + var canvasTargetsRef = this.canvasTargetsRef, + isMultiSourceRef = this.isMultiSourceRef, + player = this.player, + srcIndexRef = this.srcIndexRef, + totalDuration = this.totalDuration; + if (((_canvasTargetsRef$cur = canvasTargetsRef.current) === null || _canvasTargetsRef$cur === void 0 ? void 0 : _canvasTargetsRef$cur.length) > 0) { + var _canvasTargetsRef$cur2 = canvasTargetsRef.current[srcIndexRef.current], + altStart = _canvasTargetsRef$cur2.altStart, + start = _canvasTargetsRef$cur2.start, + end = _canvasTargetsRef$cur2.end, + duration = _canvasTargetsRef$cur2.duration; + var leftBlockEl = document.getElementById('left-block'); + var rightBlockEl = document.getElementById('right-block'); + if (!isMultiSourceRef.current) { + var leftBlock = start * 100 / duration; + var rightBlock = (duration - end) * 100 / duration; - /** - * Handle the display timer for the inaccessbile message when autoplay is turned - * on/off while the current item is a restricted item - */ - React__default["default"].useEffect(function () { - if (canvasIsEmpty) { - // Clear the existing timer when the autoplay is turned off when displaying - // inaccessible message - if (!autoAdvance && canvasMessageTimerRef.current) { - clearCanvasMessageTimer(); - } else { - // Create a timer to advance to the next Canvas when autoplay is turned - // on when inaccessible message is been displayed - createCanvasMessageTimer(); + // player.isClipped is used in VideoJSTrackScrbber to display accurate + // times for clipped items + rightBlock > 0 ? player.isClipped = true : player.isClipped = false; + if (leftBlockEl) leftBlockEl.style.width = "".concat(leftBlock, "%"); + if (rightBlockEl) { + rightBlockEl.style.width = rightBlock + '%'; + rightBlockEl.style.left = "".concat(100 - rightBlock - leftBlock, "%"); + } + } else { + // Offset of the duration of the current source for multi-source canvases + var leftOffset = Math.min(100, Math.max(0, 100 * (altStart / totalDuration))); + this.playProgress.el_.style.left = "".concat(leftOffset, "%"); + this.loadProgress.el_.style.left = "".concat(leftOffset, "%"); + // Add CSS class to mark the range from zero as played + this.addClass('played-range'); + document.documentElement.style.setProperty('--range-progress', "calc(".concat(leftOffset, "%)")); + } } } - }, [autoAdvanceRef.current]); - - /** - * Initialize the next Canvas to be viewed in the player instance - * @param {Number} canvasId index of the Canvas to be loaded into the player - * @param {Boolean} fromStart flag to indicate how to start new player instance - */ - var initCanvas = function initCanvas(canvasId, fromStart) { - clearCanvasMessageTimer(); - try { - var _getMediaInfo = getMediaInfo({ - manifest: manifest, - canvasIndex: canvasId, - startTime: canvasId === customStart.startIndex && firstLoad ? customStart.startTime : 0, - srcIndex: srcIndex, - isPlaylist: playlist.isPlaylist - }), - isMultiSource = _getMediaInfo.isMultiSource, - sources = _getMediaInfo.sources, - tracks = _getMediaInfo.tracks, - canvasTargets = _getMediaInfo.canvasTargets, - mediaType = _getMediaInfo.mediaType, - error = _getMediaInfo.error, - poster = _getMediaInfo.poster; - setIsVideo(mediaType === 'video'); - manifestDispatch({ - canvasTargets: canvasTargets, - type: 'canvasTargets' - }); - manifestDispatch({ - isMultiSource: isMultiSource, - type: 'hasMultipleItems' - }); - // Set the current time in player from the canvas details - if (fromStart) { - if ((canvasTargets === null || canvasTargets === void 0 ? void 0 : canvasTargets.length) > 0) { - playerDispatch({ - currentTime: canvasTargets[0].altStart, - type: 'setCurrentTime' - }); + /** + * Convert mouse event's offset to timepoint value in the progressbar, + * taking into account blocked ranges, and multi-source canvases. + * @param {Event} e mouse event + * @returns {currentTime: Number, offsetx: Number} + */ + }, { + key: "convertToTime", + value: function convertToTime(e) { + var _e$nativeEvent$target, _this$totalDuration; + var eSrcElement = e.srcElement; + // When clicked on blocked time point + if (eSrcElement.classList.contains('block-stripes')) { + var _this$canvasTargetsRe = this.canvasTargetsRef.current[0], + altStart = _this$canvasTargetsRe.altStart, + end = _this$canvasTargetsRe.end, + _duration = _this$canvasTargetsRe.duration; + if (eSrcElement.id === 'right-block') { + // For right-block: place time tool-tip at the end of playable range + return { + currentTime: end, + offsetx: end / _duration * this.el().clientWidth + }; } else { - playerDispatch({ - currentTime: 0, - type: 'setCurrentTime' - }); + // For left-block: place time tool-tip at the start of playable range + return { + currentTime: altStart, + offsetx: altStart / _duration * this.el().clientWidth + }; } } - setPlayerConfig(_objectSpread$3(_objectSpread$3({}, playerConfig), {}, { - error: error, - sources: sources, - tracks: tracks, - poster: poster - })); - var currentCanvas = allCanvases.find(function (c) { - return c.canvasIndex === canvasId; - }); - if (!currentCanvas.isEmpty) { - // Manifest is taken from manifest state, and is a basic object at this point - // lacking the getLabel() function so we manually retrieve the first label. - var manifestLabel = manifest.label ? Object.values(manifest.label)[0][0] : ''; - // Filter out falsy items in case canvas.label is null or an empty string - var titleText = [manifestLabel, currentCanvas.label].filter(Boolean).join(' - '); - manifestDispatch({ - canvasDuration: currentCanvas.duration, - type: 'canvasDuration' - }); - manifestDispatch({ - canvasLink: { - label: titleText, - id: currentCanvas.canvasId - }, - type: 'canvasLink' - }); - manifestDispatch({ - type: 'setCanvasIsEmpty', - isEmpty: false - }); - } else { - playerDispatch({ - type: 'updatePlayer' - }); - manifestDispatch({ - type: 'setCanvasIsEmpty', - isEmpty: true - }); - // Set poster as playerConfig.error to be used for empty Canvas message in VideoJSPlayer - setPlayerConfig(_objectSpread$3(_objectSpread$3({}, playerConfig), {}, { - error: poster - })); - // Create timer to display the message when autoadvance is ON - if (autoAdvanceRef.current) { - createCanvasMessageTimer(); + var targetX = e.target.getBoundingClientRect().x; + var offsetx = e.nativeEvent != undefined ? e.nativeEvent.offsetX != undefined ? e.nativeEvent.offsetX // iOS and desktop events + : ((_e$nativeEvent$target = e.nativeEvent.targetTouches[0]) === null || _e$nativeEvent$target === void 0 ? void 0 : _e$nativeEvent$target.clientX) - targetX // Android event + : e.offsetX; // fallback in desktop browsers when nativeEvent is undefined + var currentTime; + var duration = (_this$totalDuration = this.totalDuration) !== null && _this$totalDuration !== void 0 ? _this$totalDuration : this.player.duration(); + if (offsetx && offsetx != undefined) { + if (this.isMultiSourceRef.current) { + /** + * Check if the mouse event occurred on the same src range. + * If so, adjust the offset to support altStart for the current src. + */ + var leftOffset = parseFloat(this.playProgress.el_.style.left) / 100 * this.el().clientWidth; + var elClassList = eSrcElement.classList; + var sameSrc = (elClassList === null || elClassList === void 0 ? void 0 : elClassList.length) > 0 ? elClassList.contains('vjs-play-progress') || elClassList.contains('vjs-load-progress') : true; + if (leftOffset > offsetx && sameSrc) { + offsetx = offsetx + leftOffset; + } } + currentTime = offsetx / this.el().clientWidth * duration; } - setIsMultiSourced(isMultiSource || false); - setCIndex(canvasId); - if (enableFileDownload && renderings != {}) { - var _renderings$canvas$ca; - setRenderingFiles(renderings.manifest.concat((_renderings$canvas$ca = renderings.canvas[canvasId]) === null || _renderings$canvas$ca === void 0 ? void 0 : _renderings$canvas$ca.files)); - } - error ? setReady(false) : setReady(true); - } catch (e) { - showBoundary(e); - } - }; - - /** - * Switch src in the player when seeked to a time range within a - * different item in the same canvas - * @param {Number} srcindex new srcIndex - * @param {Number} value current time of the player - */ - var nextItemClicked = function nextItemClicked(srcindex, value) { - playerDispatch({ - currentTime: value, - type: 'setCurrentTime' - }); - manifestDispatch({ - srcIndex: srcindex, - type: 'setSrcIndex' - }); - }; - - /** - * Create timer to display the inaccessible Canvas message - */ - var createCanvasMessageTimer = function createCanvasMessageTimer() { - canvasMessageTimerRef.current = setTimeout(function () { - if (canvasIndexRef.current < lastCanvasIndexRef.current && autoAdvanceRef.current) { - manifestDispatch({ - canvasIndex: canvasIndexRef.current + 1, - type: 'switchCanvas' - }); + /** + * Parts of LoadProgress element is broken into segments as media loads, and displayed + * as separate div elements with `data-start` and `data-end` attributes respectively. + * When mouse event occurs on top of such element, add the segment start time to calculated + * current time from event. + */ + if (e.target.hasAttribute('data-start')) { + var _e$target$dataset = e.target.dataset, + start = _e$target$dataset.start; + _e$target$dataset._; + currentTime = currentTime + parseFloat(start); + offsetx = currentTime * this.el().clientWidth / this.totalDuration; } - }, CANVAS_MESSAGE_TIMEOUT); - }; - - /** - * Clear existing timer to display the inaccessible Canvas message - */ - var clearCanvasMessageTimer = function clearCanvasMessageTimer() { - if (canvasMessageTimerRef.current) { - clearTimeout(canvasMessageTimerRef.current); - canvasMessageTimerRef.current = null; + return { + currentTime: currentTime, + offsetx: offsetx + }; } - }; - - /** - * Switch player when navigating across canvases - * @param {Number} index canvas index to be loaded into the player - * @param {Boolean} fromStart flag to indicate set player start time to zero or not - * @param {String} focusElement element to be focused within the player when using - * next or previous buttons with keyboard - */ - var switchPlayer = function switchPlayer(index, fromStart) { - var focusElement = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : ''; - if (index != undefined && index > -1 && canvasIndexRef.current != index && index <= lastCanvasIndexRef.current) { - manifestDispatch({ - canvasIndex: index, - type: 'switchCanvas' - }); - initCanvas(index, fromStart); - playerDispatch({ - element: focusElement, - type: 'setPlayerFocusElement' + }, { + key: "abortableTimeupdateHandler", + value: + /** + * A wrapper function around the time update interval, to cancel + * intermediate updates via the time interval when player is + * waiting to fetch stream + */ + function abortableTimeupdateHandler() { + var _this4 = this; + var player = this.player, + progressRef = this.progressRef; + player.on('waiting', function () { + if (IS_SAFARI && !IS_MOBILE) { + player.currentTime(progressRef.current); + } + cancelInterval(); }); + var cancelInterval = function cancelInterval() { + if (internalInterval) { + clearInterval(internalInterval); + } + }; + var internalInterval = setInterval(function () { + _this4.timeUpdateHandler(); + }, 100); } - }; - React__default["default"].useEffect(function () { - var hlsOptions = withCredentials ? { - hls: { - withCredentials: true - } - } : {}; - var videoJsOptions; - // Only build the full set of option for the first playable Canvas since - // these options are only used on the initia Video.js instance creation - if (firstLoad && ready && !canvasIsEmpty) { - // Configuration options for Video.js instantiation - videoJsOptions = !canvasIsEmpty ? { - aspectRatio: isVideo ? '16:9' : '1:0', - audioOnlyMode: !isVideo, - autoplay: false, - bigPlayButton: isVideo, - id: PLAYER_ID, - playbackRates: enablePlaybackRate ? [0.5, 0.75, 1, 1.5, 2] : [], - experimentalSvgIcons: true, - // Setting inactivity timeout to zero in mobile and tablet devices translates to - // user is always active. And the control bar is not hidden when user is active. - // With this user can always use the controls when the media is playing. - inactivityTimeout: IS_MOBILE || IS_TOUCH_ONLY ? 0 : 2000, - poster: isVideo ? playerConfig.poster : null, - controls: true, - fluid: true, - language: language, - controlBar: { - // Define and order control bar controls - // See https://docs.videojs.com/tutorial-components.html for options of what - // seem to be supported controls - children: [isMultiCanvased ? 'videoJSPreviousButton' : '', 'playToggle', isMultiCanvased ? 'videoJSNextButton' : '', 'videoJSProgress', 'videoJSCurrentTime', 'timeDivider', 'durationDisplay', - // These icons are in reverse order to support `float: inline-end` in CSS - 'fullscreenToggle', enableFileDownload ? 'videoJSFileDownload' : '', enablePIP ? 'pictureInPictureToggle' : '', enablePlaybackRate ? 'playbackRateMenuButton' : '', 'qualitySelector', hasStructure || playlist.isPlaylist ? 'videoJSTrackScrubber' : '', playerConfig.tracks.length > 0 && isVideo ? 'subsCapsButton' : '', IS_MOBILE ? 'muteToggle' : 'volumePanel' - // 'vjsYo', custom component - ], - - videoJSProgress: { - srcIndex: srcIndex, - targets: targets, - currentTime: currentTime || 0, - nextItemClicked: nextItemClicked - }, - videoJSCurrentTime: { - srcIndex: srcIndex, - targets: targets, - currentTime: currentTime || 0 - } - }, - sources: isMultiSourced ? [playerConfig.sources[srcIndex]] : playerConfig.sources, - // Enable native text track functionality in iPhones and iPads - html5: _objectSpread$3(_objectSpread$3({}, hlsOptions), {}, { - nativeTextTracks: IS_MOBILE && !IS_ANDROID - }), - // Make error display modal dismissable - errorDisplay: { - uncloseable: false - }, - /* - Setting this option helps to override VideoJS's default 'keydown' event handler, whenever - the focus is on a native VideoJS control icon (e.g. play toggle). - E.g. click event on 'playtoggle' sets the focus on the play/pause button, - which has VideoJS's 'handleKeydown' event handler attached to it. Therefore, as long as the - focus is on the play/pause button the 'keydown' event will pass through VideoJS's default - 'keydown' event handler, without ever reaching the 'keydown' handler setup on the document - in Ramp code. - When this option is setup VideoJS's 'handleKeydown' event handler passes the event to the - function setup under the 'hotkeys' option when the native player controls are focused. - In Safari, this works without using 'hotkeys' option, therefore only set this in other browsers. - */ - userActions: { - hotkeys: !IS_SAFARI ? function (e) { - playerHotKeys(e, this); - } : undefined - }, - videoJSTitleLink: enableTitleLink - } : { - sources: [] - }; // Empty configurations for empty canvases - - // Add file download to toolbar when it is enabled via props - if (enableFileDownload && !canvasIsEmpty) { - videoJsOptions.controlBar.videoJSFileDownload = { - title: 'Download Files', - controlText: 'Alternate resource download', - files: renderingFiles - }; + }, { + key: "timeUpdateHandler", + value: + // Update progress bar with timeupdate in the player + function timeUpdateHandler() { + var _this5 = this; + var initTimeRef = this.initTimeRef, + player = this.player; + if (player.isDisposed() || player.ended() || player == null) { + return; } - if (isMultiCanvased && !canvasIsEmpty) { - videoJsOptions.controlBar.videoJSPreviousButton = { - canvasIndex: canvasIndex, - switchPlayer: switchPlayer, - playerFocusElement: playerFocusElement - }; - videoJsOptions.controlBar.videoJSNextButton = { - canvasIndex: canvasIndex, - lastCanvasIndex: lastCanvasIndexRef.current, - switchPlayer: switchPlayer, - playerFocusElement: playerFocusElement - }; + var curTime; + // Initially update progress from the prop passed from Ramp, + // this accounts for structured navigation when switching canvases + if (initTimeRef.current > 0 && player.currentTime() == 0) { + curTime = initTimeRef.current; + player.currentTime(initTimeRef.current); + } else { + curTime = player.currentTime(); } - // Iniitialize track scrubber button when the current Canvas has - // structure timespans or the given Manifest is a playlist Manifest - if ((hasStructure || playlist.isPlaylist) && !canvasIsEmpty) { - videoJsOptions.controlBar.videoJSTrackScrubber = { - trackScrubberRef: trackScrubberRef, - timeToolRef: timeToolRef, - isPlaylist: playlist.isPlaylist - }; + // Use debounced updates since, Safari desktop browsers need the extra + // update on 'seeked' event to timely update the progress bar. + if (IS_SAFARI && !IS_MOBILE && player.paused()) { + debounce_1(function () { + _this5.onTimeUpdate(curTime); + }); + } else { + this.onTimeUpdate(curTime); } - setFirstLoad(false); - } else { - videoJsOptions = { - sources: isMultiSourced ? [playerConfig.sources[srcIndex]] : playerConfig.sources, - poster: isVideo ? playerConfig.poster : null - }; + this.setInitTime(0); } - setOptions(videoJsOptions); - }, [ready, cIndex, srcIndex, canvasIsEmpty, currentTime]); - if (ready && options != undefined || canvasIsEmpty) { - return /*#__PURE__*/React__default["default"].createElement("div", { - "data-testid": "media-player", - className: "ramp--media_player", - role: "presentation" - }, /*#__PURE__*/React__default["default"].createElement(VideoJSPlayer, { - isVideo: isVideo, - hasMultipleCanvases: isMultiCanvased, - isPlaylist: playlist.isPlaylist, - trackScrubberRef: trackScrubberRef, - scrubberTooltipRef: timeToolRef, - tracks: playerConfig.tracks, - placeholderText: playerConfig.error, - renderingFiles: renderingFiles, - enableFileDownload: enableFileDownload, - loadPrevOrNext: switchPlayer, - lastCanvasIndex: lastCanvasIndex, - enableTitleLink: enableTitleLink, - videoJSLangMap: videoJSLangMap.current, - options: options - })); - } else { - return null; - } - }; - MediaPlayer.propTypes = { - enableFileDownload: PropTypes.bool, - enablePIP: PropTypes.bool, - enablePlaybackRate: PropTypes.bool, - enableTitleLink: PropTypes.bool, - withCredentials: PropTypes.bool, - language: PropTypes.string - }; - - var _extends_1 = createCommonjsModule(function (module) { - function _extends() { - module.exports = _extends = Object.assign ? Object.assign.bind() : function (target) { - for (var i = 1; i < arguments.length; i++) { - var source = arguments[i]; - for (var key in source) { - if (Object.prototype.hasOwnProperty.call(source, key)) { - target[key] = source[key]; - } + }, { + key: "onTimeUpdate", + value: function onTimeUpdate(curTime) { + // This state update caused weird lagging behaviors when using the iOS native + // video player. iOS player handles its own progress bar, so we can skip the + // update here only for video. + var iOS = this.player.hasClass("vjs-ios-native-fs"); + if (!(iOS && !this.player.audioOnlyMode_)) { + this.setProgress(curTime); } + this.handleTimeUpdate(curTime); } - return target; - }, module.exports.__esModule = true, module.exports["default"] = module.exports; - return _extends.apply(this, arguments); - } - module.exports = _extends, module.exports.__esModule = true, module.exports["default"] = module.exports; - }); + }, { + key: "handleTimeUpdate", + value: + /** + * Update CSS for the input range's track while the media + * is playing + * @param {Number} curTime current time of the player + */ + function handleTimeUpdate(curTime) { + var _srcIndexRef$current; + var player = this.player, + el_ = this.el_, + canvasTargetsRef = this.canvasTargetsRef, + srcIndexRef = this.srcIndexRef; - var _extends = /*@__PURE__*/getDefaultExportFromCjs(_extends_1); + // Avoid null player instance when Video.js is getting initialized + if (!el_ || !player || !canvasTargetsRef.current) { + return; + } + var _canvasTargetsRef$cur3 = canvasTargetsRef.current[(_srcIndexRef$current = srcIndexRef.current) !== null && _srcIndexRef$current !== void 0 ? _srcIndexRef$current : 0], + start = _canvasTargetsRef$cur3.start, + end = _canvasTargetsRef$cur3.end, + duration = _canvasTargetsRef$cur3.duration; - var SectionHeading = function SectionHeading(_ref) { - var duration = _ref.duration, - label = _ref.label, - itemIndex = _ref.itemIndex, - canvasIndex = _ref.canvasIndex, - sectionRef = _ref.sectionRef, - itemId = _ref.itemId, - isRoot = _ref.isRoot, - handleClick = _ref.handleClick, - structureContainerRef = _ref.structureContainerRef; - var itemLabelRef = React__default["default"].useRef(); - itemLabelRef.current = label; + // Restrict access to the intended range in the media file + if (curTime < start) { + player.currentTime(start); + } + if (curTime >= end && !player.paused() && !player.isDisposed()) { + // Trigger ended event when playable range < duration of the + // full media. e.g. clipped playlist items + if (end < duration) { + player.trigger('ended'); + } - /* - Auto-scroll active section into view only when user is not - actively interacting with structured navigation - */ - React__default["default"].useEffect(function () { - if (canvasIndex + 1 === itemIndex && sectionRef.current && sectionRef.current.isClicked != undefined && !sectionRef.current.isClicked && structureContainerRef.current.isScrolling != undefined && !structureContainerRef.current.isScrolling) { - autoScroll(sectionRef.current, structureContainerRef); + // On the next play event set the time to start or a seeked time + // in between the 'ended' event and 'play' event + // Reference: https://github.com/videojs/video.js/blob/main/src/js/control-bar/play-toggle.js#L128 + player.one('play', function () { + var time = player.currentTime(); + if (time < end) { + player.currentTime(time); + } else { + player.currentTime(start); + } + }); + } } - sectionRef.current.isClicked = false; - }, [canvasIndex]); - var sectionClassName = "ramp--structured-nav__section".concat(canvasIndex + 1 === itemIndex ? ' active' : ''); - if (itemId != undefined) { - return /*#__PURE__*/React__default["default"].createElement("div", { - className: sectionClassName, - role: "listitem", - "data-testid": "listitem-section", - ref: sectionRef, - "data-mediafrag": itemId, - "data-label": itemLabelRef.current - }, /*#__PURE__*/React__default["default"].createElement("button", { - "data-testid": "listitem-section-button", - ref: sectionRef, - onClick: handleClick - }, /*#__PURE__*/React__default["default"].createElement("span", { - className: "ramp--structured-nav__title", - "aria-label": itemLabelRef.current - }, "".concat(itemIndex, ". "), itemLabelRef.current, duration != '' && /*#__PURE__*/React__default["default"].createElement("span", { - className: "ramp--structured-nav__section-duration" - }, duration)))); - } else { - return /*#__PURE__*/React__default["default"].createElement("div", { - className: sectionClassName, - "data-testid": "listitem-section", - ref: sectionRef, - "data-label": itemLabelRef.current - }, /*#__PURE__*/React__default["default"].createElement("span", { - className: "ramp--structured-nav__section-title", - role: "listitem", - "data-testid": "listitem-section-span", - "aria-label": itemLabelRef.current - }, isRoot ? '' : "".concat(itemIndex, ". "), itemLabelRef.current, duration != '' && /*#__PURE__*/React__default["default"].createElement("span", { - className: "ramp--structured-nav__section-duration" - }, duration))); - } - }; - SectionHeading.propTypes = { - itemIndex: PropTypes.number.isRequired, - canvasIndex: PropTypes.number, - duration: PropTypes.string.isRequired, - label: PropTypes.string.isRequired, - sectionRef: PropTypes.object.isRequired, - itemId: PropTypes.string, - isRoot: PropTypes.bool, - handleClick: PropTypes.func.isRequired, - structureContainerRef: PropTypes.object.isRequired - }; + }]); + return VideoJSProgress; + }(SeekBar); + videojs__default["default"].registerComponent('VideoJSProgress', VideoJSProgress); - var ListItem = function ListItem(_ref) { - var duration = _ref.duration, - id = _ref.id, - isTitle = _ref.isTitle, - isCanvas = _ref.isCanvas, - isClickable = _ref.isClickable, - isEmpty = _ref.isEmpty, - label = _ref.label, - summary = _ref.summary, - homepage = _ref.homepage, - isRoot = _ref.isRoot, - items = _ref.items, - itemIndex = _ref.itemIndex, - rangeId = _ref.rangeId, - canvasDuration = _ref.canvasDuration, - sectionRef = _ref.sectionRef, - structureContainerRef = _ref.structureContainerRef; - var playerDispatch = usePlayerDispatch(); - var _useManifestState = useManifestState(), - canvasIndex = _useManifestState.canvasIndex, - currentNavItem = _useManifestState.currentNavItem, - playlist = _useManifestState.playlist; - var isPlaylist = playlist.isPlaylist; - var itemIdRef = React__default["default"].useRef(); - itemIdRef.current = id; - var itemLabelRef = React__default["default"].useRef(); - itemLabelRef.current = label; - var itemSummaryRef = React__default["default"].useRef(); - itemSummaryRef.current = summary; - var subMenu = items && items.length > 0 ? /*#__PURE__*/React__default["default"].createElement(List, { - items: items, - sectionRef: sectionRef, - structureContainerRef: structureContainerRef - }) : null; - var liRef = React__default["default"].useRef(null); - var handleClick = React__default["default"].useCallback(function (e) { - e.preventDefault(); - e.stopPropagation(); - var _getMediaFragment = getMediaFragment(itemIdRef.current, canvasDuration), - start = _getMediaFragment.start, - end = _getMediaFragment.end; - var inRange = checkSrcRange({ - start: start, - end: end - }, { - end: canvasDuration + function _createSuper$5(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$5(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } + function _isNativeReflectConstruct$5() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } + var TimeDisplay = videojs__default["default"].getComponent('TimeDisplay'); + + /** + * Custom component to display the current time of the player + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} options + * @param {Number} options.currentTime + */ + var VideoJSCurrentTime = /*#__PURE__*/function (_TimeDisplay) { + _inherits(VideoJSCurrentTime, _TimeDisplay); + var _super = _createSuper$5(VideoJSCurrentTime); + function VideoJSCurrentTime(player, options) { + var _this; + _classCallCheck(this, VideoJSCurrentTime); + _this = _super.call(this, player, options); + _this.addClass('vjs-time-control vjs-current-time-display'); + _this.setAttribute('role', 'presentation'); + _this.player = player; + _this.options = options; + _this.initTimeRef = /*#__PURE__*/React.createRef(); + _this.initTimeRef.current = options.currentTime; + _this.playerInterval; + _this.player.on('loadstart', function () { + _this.playerInterval = setInterval(function () { + _this.handleTimeUpdate(); + }, 100); }); - /* - Only continue the click action if not both start and end times of - the timespan are not outside Canvas' duration - */ - if (inRange) { - playerDispatch({ - clickedUrl: itemIdRef.current, - type: 'navClick' - }); - liRef.current.isClicked = true; - if (sectionRef.current) { - sectionRef.current.isClicked = true; + _this.player.on('seeked', function () { + if (IS_SAFARI && !IS_MOBILE) { + _this.updateTextNode_(player.currentTime()); } + }); + + // Update our timer after the user leaves full screen + _this.player.on('fullscreenchange', function () { + if (!player.isFullscreen()) { + _this.updateTextNode_(player.currentTime()); + } + }); + + // Clean interval upon player dispose + _this.player.on('dispose', function () { + clearInterval(_this.playerInterval); + }); + return _this; + } + _createClass(VideoJSCurrentTime, [{ + key: "buildCSSClass", + value: function buildCSSClass() { + return 'current-time'; } - }); - React__default["default"].useEffect(function () { - /* - Auto-scroll active structure item into view only when user is not actively - interacting with structured navigation - */ - if (liRef.current && (currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.id) == itemIdRef.current && liRef.current.isClicked != undefined && !liRef.current.isClicked && structureContainerRef.current.isScrolling != undefined && !structureContainerRef.current.isScrolling) { - autoScroll(liRef.current, structureContainerRef); + }, { + key: "setInitTime", + value: function setInitTime(t) { + this.initTimeRef.current = t; } - // Reset isClicked if active structure item is set - if (liRef.current) { - liRef.current.isClicked = false; + }, { + key: "handleTimeUpdate", + value: function handleTimeUpdate() { + var player = this.player, + initTimeRef = this.initTimeRef; + var targets = player.targets, + srcIndex = player.srcIndex; + if (!player || player.isDisposed() || !targets) { + return; + } + var iOS = player.hasClass('vjs-ios-native-fs'); + var time; + // Update time from the given initial time if it is not zero + if (initTimeRef.current > 0 && player.currentTime() == 0) { + time = initTimeRef.current; + } else { + time = player.currentTime(); + } + var _targets = targets[srcIndex !== null && srcIndex !== void 0 ? srcIndex : 0], + start = _targets.start, + altStart = _targets.altStart; + if (altStart != start && srcIndex > 0) { + time = time + altStart; + } + // This state update caused weird lagging behaviors when using the iOS native + // video player. iOS player handles its own time, so we can skip the update here + // video items. + if (!(iOS && !player.audioOnlyMode_)) { + this.updateTextNode_(time); + } + this.setInitTime(0); } - }, [currentNavItem]); - var renderListItem = function renderListItem() { - return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, { - key: rangeId - }, isCanvas && !isPlaylist ? /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, /*#__PURE__*/React__default["default"].createElement(SectionHeading, { - itemIndex: itemIndex, - canvasIndex: canvasIndex, - duration: duration, - label: label, - sectionRef: sectionRef, - itemId: itemIdRef.current, - isRoot: isRoot, - handleClick: handleClick, - structureContainerRef: structureContainerRef - })) : /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, isTitle ? /*#__PURE__*/React__default["default"].createElement("span", { - className: "ramp--structured-nav__item-title", - role: "listitem", - "aria-label": itemLabelRef.current - }, itemLabelRef.current) : /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, { - key: id - }, /*#__PURE__*/React__default["default"].createElement("div", { - className: "tracker" - }), isClickable ? /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, isEmpty && /*#__PURE__*/React__default["default"].createElement(LockedSVGIcon, null), /*#__PURE__*/React__default["default"].createElement("a", { - role: "listitem", - href: homepage && homepage != '' ? homepage : itemIdRef.current, - onClick: handleClick - }, "".concat(itemIndex, ". "), itemLabelRef.current, " ", duration.length > 0 ? " (".concat(duration, ")") : '')) : /*#__PURE__*/React__default["default"].createElement("span", { - role: "listitem", - "aria-label": itemLabelRef.current - }, itemLabelRef.current)))); - }; - if (label != '') { - return /*#__PURE__*/React__default["default"].createElement("li", { - "data-testid": "list-item", - ref: liRef, - className: 'ramp--structured-nav__list-item' + "".concat(itemIdRef.current != undefined && (currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.id) === itemIdRef.current && (isPlaylist || !isCanvas) && (currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.canvasIndex) === canvasIndex + 1 ? ' active' : ''), - "data-label": itemLabelRef.current, - "data-summary": itemSummaryRef.current - }, renderListItem(), subMenu); - } else { - return null; - } - }; - ListItem.propTypes = { - duration: PropTypes.string.isRequired, - id: PropTypes.string, - isTitle: PropTypes.bool.isRequired, - isCanvas: PropTypes.bool.isRequired, - isClickable: PropTypes.bool.isRequired, - isEmpty: PropTypes.bool.isRequired, - label: PropTypes.string.isRequired, - summary: PropTypes.string, - homepage: PropTypes.string, - isRoot: PropTypes.bool, - items: PropTypes.array.isRequired, - itemIndex: PropTypes.number, - rangeId: PropTypes.string.isRequired, - canvasDuration: PropTypes.number.isRequired, - sectionRef: PropTypes.object.isRequired, - structureContainerRef: PropTypes.object.isRequired - }; + }]); + return VideoJSCurrentTime; + }(TimeDisplay); + videojs__default["default"].registerComponent('VideoJSCurrentTime', VideoJSCurrentTime); - var List = function List(_ref) { - var items = _ref.items, - sectionRef = _ref.sectionRef, - structureContainerRef = _ref.structureContainerRef; - var collapsibleContent = /*#__PURE__*/React__default["default"].createElement("ul", { - "data-testid": "list", - className: "ramp--structured-nav__list", - role: "presentation" - }, items.map(function (item, index) { - if (item) { - return /*#__PURE__*/React__default["default"].createElement(ListItem, _extends({}, item, { - sectionRef: sectionRef, - key: index, - structureContainerRef: structureContainerRef - })); + function _createSuper$4(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$4(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } + function _isNativeReflectConstruct$4() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } + var MenuButton = videojs__default["default"].getComponent('MenuButton'); + var MenuItem = videojs__default["default"].getComponent('MenuItem'); + + /** + * Custom component to display rendering files as downloadable + * associated with the current Canvas. This control is enabled + * in the player's control-bar via 'enableFileDownload' prop in + * MediaPlayer component. + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} props.options + * @param {Number} props.options.files list of rendering files + */ + var VideoJSFileDownload = /*#__PURE__*/function (_MenuButton) { + _inherits(VideoJSFileDownload, _MenuButton); + var _super = _createSuper$4(VideoJSFileDownload); + function VideoJSFileDownload(player, options) { + var _this; + _classCallCheck(this, VideoJSFileDownload); + _this = _super.call(this, player, options); + // Add SVG icon through CSS class + _this.addClass("vjs-file-download"); + _this.setAttribute('data-testid', 'videojs-file-download'); + // Use Video.js' stock SVG instead of setting it using CSS + _this.setIcon('file-download'); + return _this; + } + _createClass(VideoJSFileDownload, [{ + key: "createItems", + value: function createItems() { + var options_ = this.options_, + player_ = this.player_; + var files = options_.files; + if ((files === null || files === void 0 ? void 0 : files.length) > 0) { + return files.map(function (file) { + var item = new MenuItem(player_, { + label: file.label + }); + item.handleClick = function () { + fileDownload(file.id, file.filename, file.fileExt); + }; + return item; + }); + } else { + return []; + } } - })); - return /*#__PURE__*/React__default["default"].createElement(React__default["default"].Fragment, null, collapsibleContent); - }; - List.propTypes = { - items: PropTypes.array.isRequired, - sectionRef: PropTypes.object.isRequired, - structureContainerRef: PropTypes.object.isRequired - }; + }]); + return VideoJSFileDownload; + }(MenuButton); + videojs__default["default"].registerComponent('VideoJSFileDownload', VideoJSFileDownload); - function _createForOfIteratorHelper$1(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$1(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } - function _unsupportedIterableToArray$1(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$1(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$1(o, minLen); } - function _arrayLikeToArray$1(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } - var StructuredNavigation = function StructuredNavigation() { - var _structureItemsRef$cu; - var manifestDispatch = useManifestDispatch(); - var playerDispatch = usePlayerDispatch(); - var _usePlayerState = usePlayerState(), - clickedUrl = _usePlayerState.clickedUrl, - isClicked = _usePlayerState.isClicked, - isPlaying = _usePlayerState.isPlaying, - player = _usePlayerState.player; - var _useManifestState = useManifestState(), - allCanvases = _useManifestState.allCanvases, - canvasDuration = _useManifestState.canvasDuration, - canvasIndex = _useManifestState.canvasIndex, - hasMultiItems = _useManifestState.hasMultiItems, - targets = _useManifestState.targets, - manifest = _useManifestState.manifest, - playlist = _useManifestState.playlist, - canvasIsEmpty = _useManifestState.canvasIsEmpty, - canvasSegments = _useManifestState.canvasSegments; - var _useErrorBoundary = reactErrorBoundary.useErrorBoundary(), - showBoundary = _useErrorBoundary.showBoundary; - var canvasStructRef = React__default["default"].useRef(); - var structureItemsRef = React__default["default"].useRef(); - var canvasIsEmptyRef = React__default["default"].useRef(canvasIsEmpty); - var hasRootRangeRef = React__default["default"].useRef(false); - var structureContainerRef = React__default["default"].useRef(); - var scrollableStructure = React__default["default"].useRef(); - React__default["default"].useEffect(function () { - // Update currentTime and canvasIndex in state if a - // custom start time and(or) canvas is given in manifest - if (manifest) { - try { - var _getStructureRanges = getStructureRanges(manifest, allCanvases, playlist.isPlaylist), - structures = _getStructureRanges.structures, - timespans = _getStructureRanges.timespans, - markRoot = _getStructureRanges.markRoot; - structureItemsRef.current = structures; - canvasStructRef.current = structures; - hasRootRangeRef.current = markRoot; - // Remove root-level structure item from navigation calculations - if ((structures === null || structures === void 0 ? void 0 : structures.length) > 0 && structures[0].isRoot) { - canvasStructRef.current = structures[0].items; + function _createSuper$3(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$3(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } + function _isNativeReflectConstruct$3() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } + var Button$2 = videojs__default["default"].getComponent('Button'); + + /** + * Custom VideoJS button component for navigating to the next Canvas when there + * are multiple canvases in a given Manifest + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} props.options + * @param {Number} props.options.canvasIndex current Canvas index + * @param {Number} props.options.lastCanvasIndex index of the last Canvas in the Manifest + * @param {Function} props.options.switchPlayer callback func to update Canvas change in state + */ + var VideoJSNextButton = /*#__PURE__*/function (_Button) { + _inherits(VideoJSNextButton, _Button); + var _super = _createSuper$3(VideoJSNextButton); + function VideoJSNextButton(player, options) { + var _this; + _classCallCheck(this, VideoJSNextButton); + _this = _super.call(this, player, options); + // Use Video.js' stock SVG instead of setting it using CSS + _this.setIcon('next-item'); + _this.addClass('vjs-play-control vjs-control'); + _this.setAttribute('data-testid', 'videojs-next-button'); + _this.controlText('Next'); + _this.options = options; + _this.player = player; + _this.cIndex = options.canvasIndex; + + // Handle player reload or source change events + _this.player.on('loadstart', function () { + _this.updateComponent(); + }); + return _this; + } + _createClass(VideoJSNextButton, [{ + key: "updateComponent", + value: function updateComponent() { + var player = this.player; + if (player && player != undefined) { + var _player$children; + // When canvasIndex property is not set in the player instance use dataset. + // This happens rarely, but when it does previous button cannot be used. + if (player.canvasIndex === undefined && ((_player$children = player.children()) === null || _player$children === void 0 ? void 0 : _player$children.length) > 0) { + this.cIndex = Number(player.children()[0].dataset.canvasindex); + } else { + this.cIndex = player.canvasIndex; } - manifestDispatch({ - structures: canvasStructRef.current, - type: 'setStructures' - }); - manifestDispatch({ - timespans: timespans, - type: 'setCanvasSegments' - }); - structureContainerRef.current.isScrolling = false; - } catch (error) { - showBoundary(error); } } - }, [manifest]); - - // Set currentNavItem when current Canvas is an inaccessible/empty item - React__default["default"].useEffect(function () { - if (canvasIsEmpty && playlist.isPlaylist) { - manifestDispatch({ - item: canvasSegments[canvasIndex], - type: 'switchItem' - }); + }, { + key: "handleClick", + value: function handleClick() { + this.handleNextClick(false); } - }, [canvasIsEmpty, canvasIndex]); - React__default["default"].useEffect(function () { - if (isClicked) { - var clickedItem = canvasSegments.filter(function (c) { - return c.id === clickedUrl; - }); - if ((clickedItem === null || clickedItem === void 0 ? void 0 : clickedItem.length) > 0) { - // Only update the current nav item for timespans - // Eliminate Canvas level items unless the structure is empty - var _clickedItem$ = clickedItem[0], - isCanvas = _clickedItem$.isCanvas, - items = _clickedItem$.items; - if (!isCanvas || items.length == 0 && isCanvas) { - manifestDispatch({ - item: clickedItem[0], - type: 'switchItem' - }); - } - } - var currentCanvasIndex = allCanvases.findIndex(function (c) { - return c.canvasURL === getCanvasId(clickedUrl); - }); - var timeFragment = getMediaFragment(clickedUrl, canvasDuration); - - // Invalid time fragment - if (!timeFragment || timeFragment == undefined) { - console.error('StructuredNavigation -> invalid media fragment in structure item -> ', timeFragment); - return; - } - var timeFragmentStart = timeFragment.start; - if (hasMultiItems) { - var _getCanvasTarget = getCanvasTarget(targets, timeFragment, canvasDuration), - srcIndex = _getCanvasTarget.srcIndex, - fragmentStart = _getCanvasTarget.fragmentStart; - timeFragmentStart = fragmentStart; - manifestDispatch({ - srcIndex: srcIndex, - type: 'setSrcIndex' - }); - } else { - // When clicked structure item is not in the current canvas - if (canvasIndex != currentCanvasIndex && currentCanvasIndex > -1) { - manifestDispatch({ - canvasIndex: currentCanvasIndex, - type: 'switchCanvas' - }); - canvasIsEmptyRef.current = canvasStructRef.current[currentCanvasIndex].isEmpty; - } + }, { + key: "handleKeyDown", + value: function handleKeyDown(e) { + if (e.which === 32 || e.which === 13) { + e.stopPropagation(); + this.handleNextClick(true); } - if (player && !canvasIsEmptyRef.current) { - player.currentTime(timeFragmentStart); - playerDispatch({ - startTime: timeFragment.start, - endTime: timeFragment.end, - type: 'setTimeFragment' - }); - - // Use this value in iOS to set the initial progress - // in the custom progress bar - player.structStart = timeFragmentStart; - playerDispatch({ - currentTime: timeFragmentStart, - type: 'setCurrentTime' - }); - // Setting userActive to true shows timerail breifly, helps - // to visualize the structure in player while playing - if (isPlaying) player.userActive(true); - } else if (canvasIsEmptyRef.current) { - // Reset isClicked in state for - // inaccessible items (empty canvases) - playerDispatch({ - type: 'resetClick' - }); + } + }, { + key: "handleNextClick", + value: function handleNextClick(isKeyDown) { + if (this.cIndex != this.options.lastCanvasIndex) { + this.options.switchPlayer(this.cIndex + 1, true, isKeyDown ? 'nextBtn' : ''); } } - }, [isClicked, player]); + }]); + return VideoJSNextButton; + }(Button$2); + videojs__default["default"].registerComponent('VideoJSNextButton', VideoJSNextButton); - // Structured nav is populated by the time the player hook fires so we listen for - // that to run the check on whether the structured nav is scrollable. - React__default["default"].useEffect(function () { - if (structureContainerRef.current) { - var elem = structureContainerRef.current; - var structureBorder = structureContainerRef.current.parentElement; - var structureEnd = Math.abs(elem.scrollHeight - (elem.scrollTop + elem.clientHeight)) <= 1; - scrollableStructure.current = !structureEnd; - if (structureBorder) { - resizeObserver.observe(structureBorder); + function _createSuper$2(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$2(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } + function _isNativeReflectConstruct$2() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } + var Button$1 = videojs__default["default"].getComponent('Button'); + + /** + * Custom VideoJS button component for navigating to the previous Canvas when there + * are multiple canvases in a given Manifest + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} props.options + * @param {Number} props.options.canvasIndex current Canvas index + * @param {Function} props.options.switchPlayer callback func to update Canvas change in state + */ + var VideoJSPreviousButton = /*#__PURE__*/function (_Button) { + _inherits(VideoJSPreviousButton, _Button); + var _super = _createSuper$2(VideoJSPreviousButton); + function VideoJSPreviousButton(player, options) { + var _this; + _classCallCheck(this, VideoJSPreviousButton); + _this = _super.call(this, player, options); + // Use Video.js' stock SVG instead of setting it using CSS + _this.setIcon('previous-item'); + _this.addClass('vjs-play-control vjs-control'); + _this.setAttribute('data-testid', 'videojs-previous-button'); + _this.options = options; + _this.player = player; + _this.cIndex = options.canvasIndex; + + // Handle player reload or source change events + _this.player.on('loadstart', function () { + _this.updateComponent(); + }); + return _this; + } + _createClass(VideoJSPreviousButton, [{ + key: "updateComponent", + value: function updateComponent() { + var player = this.player; + if (player && player != undefined) { + var _player$children; + // When canvasIndex property is not set in the player instance use dataset. + // This happens rarely, but when it does previous button cannot be used. + if (player.canvasIndex === undefined && ((_player$children = player.children()) === null || _player$children === void 0 ? void 0 : _player$children.length) > 0) { + this.cIndex = Number(player.children()[0].dataset.canvasindex); + } else { + this.cIndex = player.canvasIndex; + } } + this.controlText(this.cIndex == 0 ? 'Replay' : 'Previous'); } - }, [player]); - - // Update scrolling indicators when end of scrolling has been reached - var handleScrollable = function handleScrollable(e) { - var elem = e.target; - if (elem.classList.contains('ramp--structured-nav__border')) { - elem = elem.firstChild; + }, { + key: "handleClick", + value: function handleClick() { + this.handlePreviousClick(false); } - var scrollMsg = elem.nextSibling; - var structureEnd = Math.abs(elem.scrollHeight - (elem.scrollTop + elem.clientHeight)) <= 1; - if (elem && structureEnd && elem.classList.contains('scrollable')) { - elem.classList.remove('scrollable'); - } else if (elem && !structureEnd && !elem.classList.contains('scrollable')) { - elem.classList.add('scrollable'); + }, { + key: "handleKeyDown", + value: function handleKeyDown(e) { + if (e.which === 32 || e.which === 13) { + e.stopPropagation(); + this.handlePreviousClick(true); + } } - if (scrollMsg && structureEnd && scrollMsg.classList.contains('scrollable')) { - scrollMsg.classList.remove('scrollable'); - } else if (scrollMsg && !structureEnd && !scrollMsg.classList.contains('scrollable')) { - scrollMsg.classList.add('scrollable'); + }, { + key: "handlePreviousClick", + value: function handlePreviousClick(isKeyDown) { + if (this.cIndex > -1 && this.cIndex != 0) { + this.options.switchPlayer(this.cIndex - 1, true, isKeyDown ? 'previousBtn' : ''); + } else if (this.cIndex == 0) { + this.player.currentTime(0); + } } - }; + }]); + return VideoJSPreviousButton; + }(Button$1); + videojs__default["default"].registerComponent('VideoJSPreviousButton', VideoJSPreviousButton); - // Update scrolling indicators when structured nav is resized - var resizeObserver = new ResizeObserver(function (entries) { - var _iterator = _createForOfIteratorHelper$1(entries), - _step; - try { - for (_iterator.s(); !(_step = _iterator.n()).done;) { - var entry = _step.value; - handleScrollable(entry); + function _createSuper$1(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct$1(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } + function _isNativeReflectConstruct$1() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } + var vjsComponent = videojs__default["default"].getComponent('Component'); + + /** + * Custom component to display title of the current item in the player + * in an overlay. + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} props.options + */ + var VideoJSTitleLink = /*#__PURE__*/function (_vjsComponent) { + _inherits(VideoJSTitleLink, _vjsComponent); + var _super = _createSuper$1(VideoJSTitleLink); + function VideoJSTitleLink(player, options) { + var _this; + _classCallCheck(this, VideoJSTitleLink); + _this = _super.call(this, player, options); + _this.setAttribute('data-testid', 'videojs-title-link'); + _this.addClass('vjs-title-bar'); + _this.options = options; + _this.player = player; + + // Handle player reload or source change events + _this.player.on('loadstart', function () { + _this.updateComponent(); + }); + return _this; + } + _createClass(VideoJSTitleLink, [{ + key: "updateComponent", + value: function updateComponent() { + var player = this.player; + if (player && player != undefined && player.canvasLink) { + var _player$canvasLink = player.canvasLink, + label = _player$canvasLink.label, + id = _player$canvasLink.id; + var title = label; + var href = null; + /** + * Avalon canvas ids are of the form 'http://host.edu/media_objects/#mo_id/manifest/canvas/#section_id`. + * Accessible url is 'http://host.edu/media_objects/#mo_id/section/#section_id' so we convert the canvas + * id for avalon manifest, but must assume other implementers will have the id as an actionable link. + */ + if (id.includes('manifest/canvas')) { + href = id.replace('manifest/canvas', 'section'); + } else { + href = id; + } + var link = videojs__default["default"].dom.createEl('a', { + className: 'vjs-title-link', + href: href, + target: '_blank', + rel: 'noreferrer noopener', + innerHTML: title + }); + if (this.el().hasChildNodes()) { + this.el().replaceChildren(link); + } else { + this.el().appendChild(link); + } } - } catch (err) { - _iterator.e(err); - } finally { - _iterator.f(); } - }); - if (!manifest) { - return /*#__PURE__*/React__default["default"].createElement("p", null, "No manifest - Please provide a valid manifest."); - } + }]); + return VideoJSTitleLink; + }(vjsComponent); + vjsComponent.registerComponent('VideoJSTitleLink', VideoJSTitleLink); - // Check for scrolling on initial render and build appropriate element class - var divClass = ''; - var spanClass = ''; - if (scrollableStructure.current) { - divClass = "ramp--structured-nav scrollable"; - spanClass = "scrollable"; - } else { - divClass = "ramp--structured-nav"; - } - if (playlist !== null && playlist !== void 0 && playlist.isPlaylist) { - divClass += " playlist-items"; - } - divClass += hasRootRangeRef.current ? " ramp--structured-nav-with_root" : ""; + function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; } + function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } - /** - * Update isScrolling flag within structure container ref, which is - * used by ListItem and SectionHeading components to decide to/not to - * auto scroll the content - * @param {Boolean} state - */ - var handleMouseOver = function handleMouseOver(state) { - structureContainerRef.current.isScrolling = state; - }; - return /*#__PURE__*/React__default["default"].createElement("div", { - className: "ramp--structured-nav__border" - }, /*#__PURE__*/React__default["default"].createElement("div", { - "data-testid": "structured-nav", - className: divClass, - ref: structureContainerRef, - role: "list", - "aria-label": "Structural content", - onScroll: handleScrollable, - onMouseLeave: function onMouseLeave() { - return handleMouseOver(false); - }, - onMouseOver: function onMouseOver() { - return handleMouseOver(true); - } - }, ((_structureItemsRef$cu = structureItemsRef.current) === null || _structureItemsRef$cu === void 0 ? void 0 : _structureItemsRef$cu.length) > 0 ? structureItemsRef.current.map(function (item, index) { - return /*#__PURE__*/React__default["default"].createElement(List, { - items: [item], - sectionRef: /*#__PURE__*/React__default["default"].createRef(), - key: index, - structureContainerRef: structureContainerRef - }); - }) : /*#__PURE__*/React__default["default"].createElement("p", { - className: "ramp--no-structure" - }, "There are no structures in the manifest")), /*#__PURE__*/React__default["default"].createElement("span", { - className: spanClass - }, "Scroll to see more")); - }; - StructuredNavigation.propTypes = {}; + // SVG icons for zoom-in and zoom-out icons as strings + var zoomOutIconSVG = "\n\n \n \n \n \n \n \n"; + var zoomInIconSVG = "\n\n \n \n \n \n \n \n"; - var objectWithoutPropertiesLoose = createCommonjsModule(function (module) { - function _objectWithoutPropertiesLoose(source, excluded) { - if (source == null) return {}; - var target = {}; - var sourceKeys = Object.keys(source); - var key, i; - for (i = 0; i < sourceKeys.length; i++) { - key = sourceKeys[i]; - if (excluded.indexOf(key) >= 0) continue; - target[key] = source[key]; - } - return target; + // Function to inject SVGs into the DOM + function injectSVGIcons() { + var svgContainer = document.createElement('div'); + svgContainer.style.display = 'none'; + svgContainer.innerHTML = "".concat(zoomOutIconSVG).concat(zoomInIconSVG, ""); + document.body.appendChild(svgContainer); } - module.exports = _objectWithoutPropertiesLoose, module.exports.__esModule = true, module.exports["default"] = module.exports; - }); - var objectWithoutProperties = createCommonjsModule(function (module) { - function _objectWithoutProperties(source, excluded) { - if (source == null) return {}; - var target = objectWithoutPropertiesLoose(source, excluded); - var key, i; - if (Object.getOwnPropertySymbols) { - var sourceSymbolKeys = Object.getOwnPropertySymbols(source); - for (i = 0; i < sourceSymbolKeys.length; i++) { - key = sourceSymbolKeys[i]; - if (excluded.indexOf(key) >= 0) continue; - if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; - target[key] = source[key]; - } - } - return target; - } - module.exports = _objectWithoutProperties, module.exports.__esModule = true, module.exports["default"] = module.exports; - }); + // Call the function to inject SVG icons + injectSVGIcons(); + var Button = videojs__default["default"].getComponent('Button'); - var _objectWithoutProperties = /*@__PURE__*/getDefaultExportFromCjs(objectWithoutProperties); + /** + * Custom VideoJS component for displaying track view when there are + * tracks/structure timespans in the current Canvas. + * @param {Object} props + * @param {Object} props.player VideoJS player instance + * @param {Object} props.options + * @param {Number} props.options.trackScrubberRef React ref to track scrubber element + * @param {Number} props.options.timeToolRef React ref to time tooltip element + * @param {Boolean} props.options.isPlaylist flag to indicate a playlist Manifest or not + */ + var VideoJSTrackScrubber = /*#__PURE__*/function (_Button) { + _inherits(VideoJSTrackScrubber, _Button); + var _super = _createSuper(VideoJSTrackScrubber); + function VideoJSTrackScrubber(player, options) { + var _this; + _classCallCheck(this, VideoJSTrackScrubber); + _this = _super.call(this, player, options); + _this.setAttribute('data-testid', 'videojs-track-scrubber-button'); + _this.addClass('vjs-button vjs-track-scrubber'); + _this.controlText('Toggle track scrubber'); + _this.el().innerHTML = "\n \n \n "; + _this.options = options; + _this.player = player; + _this.playerInterval; + _this.zoomedOutRef = /*#__PURE__*/React.createRef(); + _this.currentTrackRef = /*#__PURE__*/React.createRef(); - var taggedTemplateLiteral = createCommonjsModule(function (module) { - function _taggedTemplateLiteral(strings, raw) { - if (!raw) { - raw = strings.slice(0); - } - return Object.freeze(Object.defineProperties(strings, { - raw: { - value: Object.freeze(raw) - } - })); - } - module.exports = _taggedTemplateLiteral, module.exports.__esModule = true, module.exports["default"] = module.exports; - }); + // Attach interval on first load for time updates + _this.player.on('ready', function () { + if (_this.options.trackScrubberRef.current) { + _this.playerInterval = setInterval(function () { + _this.handleTimeUpdate(); + }, 100); + } + }); - var _taggedTemplateLiteral = /*@__PURE__*/getDefaultExportFromCjs(taggedTemplateLiteral); + /* + When player is fully built and the trackScrubber element is initialized, + call method to mount React component. + */ + _this.player.on('loadstart', function () { + if (_this.options.trackScrubberRef.current) { + _this.updateComponent(); + if (!_this.playerInterval) { + _this.playerInterval = setInterval(function () { + _this.handleTimeUpdate(); + }, 100); + } + } + }); - var _templateObject$1, _templateObject2, _templateObject3, _templateObject4; - function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } - function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } - function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } + // Hide track scrubber if it is displayed when player is going fullscreen + _this.player.on('fullscreenchange', function () { + if (_this.player.isFullscreen() && !_this.zoomedOutRef.current) { + var tempZoom = _this.zoomedOutRef.current; + _this.setZoomedOut(!tempZoom); + } + }); - // ENum for supported transcript MIME types - var TRANSCRIPT_MIME_TYPES = { - webvtt: ['text/vtt'], - srt: ['application/x-subrip', 'text/srt'], - text: ['text/plain'], - json: ['application/json'], - docx: ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'] - }; - var VTT_TIMESTAMP_REGEX = /^(?:\d{2}:)?\d{2}:\d{2}(?:\.\d+)/g; - // SRT allows using comma for milliseconds while WebVTT does not - var SRT_TIMESTAMP_REGEX = /^(?:\d{2}:)?\d{2}:\d{2}(?:[.,]\d+)/g; - var TRANSCRIPT_MIME_EXTENSIONS = [{ - type: TRANSCRIPT_MIME_TYPES.json, - ext: 'json' - }, { - type: TRANSCRIPT_MIME_TYPES.webvtt, - ext: 'vtt' - }, { - type: TRANSCRIPT_MIME_TYPES.text, - ext: 'txt' - }, { - type: TRANSCRIPT_MIME_TYPES.docx, - ext: 'docx' - }, { - type: TRANSCRIPT_MIME_TYPES.srt, - ext: 'srt' - }]; + // Clean up interval when player is disposed + _this.player.on('dispose', function () { + clearInterval(_this.playerInterval); + }); + return _this; + } + _createClass(VideoJSTrackScrubber, [{ + key: "setCurrentTrack", + value: function setCurrentTrack(t) { + this.currentTrackRef.current = t; + } + }, { + key: "setZoomedOut", + value: function setZoomedOut(z) { + this.zoomedOutRef.current = z; + if (z) { + this.options.trackScrubberRef.current.classList.add('hidden'); + this.el().innerHTML = "\n \n \n "; + } else { + this.options.trackScrubberRef.current.classList.remove('hidden'); + this.el().innerHTML = "\n \n \n "; + } + } + }, { + key: "attachListeners", + value: function attachListeners() { + var _this2 = this; + var trackScrubberRef = this.options.trackScrubberRef; + if (trackScrubberRef.current) { + // Initialize the track scrubber's current time and duration + this.populateTrackScrubber(); + this.updateTrackScrubberProgressBar(); + var pointerDragged = false; + // Attach mouse pointer events to track scrubber progress bar + var _trackScrubberRef$cur = _slicedToArray(trackScrubberRef.current.children, 3); + _trackScrubberRef$cur[0]; + var progressBar = _trackScrubberRef$cur[1]; + _trackScrubberRef$cur[2]; + progressBar.addEventListener('mouseenter', function (e) { + _this2.handleMouseMove(e); + }); + /* + Using pointerup, pointermove, pointerdown events instead of + mouseup, mousemove, mousedown events to make it work with both + mouse pointer and touch events + */ + progressBar.addEventListener('pointerup', function (e) { + if (pointerDragged) { + _this2.handleSetProgress(e); + } + }); + progressBar.addEventListener('pointermove', function (e) { + _this2.handleMouseMove(e); + pointerDragged = true; + }); + progressBar.addEventListener('pointerdown', function (e) { + // Only handle left click event + if (e.which === 1) { + _this2.handleSetProgress(e); + pointerDragged = false; + } + }); + } + } + }, { + key: "updateComponent", + value: function updateComponent() { + // Reset refs to initial value + this.zoomedOutRef.current = true; + this.currentTrackRef.current = {}; + this.attachListeners(); + } - // ENum for describing transcript types include invalid and no transcript info - var TRANSCRIPT_TYPES = { - invalidTimestamp: -4, - invalidVTT: -3, - noSupport: -2, - invalid: -1, - noTranscript: 0, - timedText: 1, - plainText: 2, - docx: 3 - }; + /** + * Keydown event handler for the track button on the player controls, + * when using keyboard navigation + * @param {Event} e keydown event + */ + }, { + key: "handleKeyDown", + value: function handleKeyDown(e) { + if (e.which === 32 || e.which === 13) { + e.preventDefault(); + this.handleTrackScrubberClick(); + e.stopPropagation(); + } + } + }, { + key: "handleClick", + value: function handleClick() { + this.handleTrackScrubberClick(); + } - // ENum for types transcript text lines in a time-synced transcript - var TRANSCRIPT_CUE_TYPES = { - note: 'NOTE', - timedCue: 'TIMED_CUE', - nonTimedLine: 'NON_TIMED_LINE' - }; + /** + * Click event handler for the track button on the player controls + */ + }, { + key: "handleTrackScrubberClick", + value: function handleTrackScrubberClick() { + var currentTrackRef = this.currentTrackRef, + player = this.player, + options = this.options; + // When player is not fully loaded on the page don't show the track scrubber + if (!options.trackScrubberRef.current || !currentTrackRef.current) return; - /** - * Parse the transcript information in the Manifest presented as supplementing annotations - * @param {String} manifestURL IIIF Presentation 3.0 manifest URL - * @param {String} title optional title given in the transcripts list in props - * @returns {Array} array of supplementing annotations for transcripts for all - * canvases in the Manifest - */ - function readSupplementingAnnotations(_x) { - return _readSupplementingAnnotations.apply(this, arguments); - } + // If player is fullscreen exit before displaying track scrubber + if (player.isFullscreen()) { + player.exitFullscreen(); + } + var tempZoom = this.zoomedOutRef.current; + this.setZoomedOut(!tempZoom); + } - /** - * Refine and sanitize the user provided transcripts list in the props. If there are manifests - * in the given array process them to find supplementing annotations in the manifest and - * them to the transcripts array to be displayed in the component. - * @param {Array} transcripts list of transcripts from Transcript component's props - * @returns {Array} a refined transcripts array for each canvas with the following json - * structure; - * { canvasId: , items: [{ title, filename, url, isMachineGen, id }]} - */ - function _readSupplementingAnnotations() { - _readSupplementingAnnotations = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee(manifestURL) { - var title, - data, - _args = arguments; - return regenerator.wrap(function _callee$(_context) { - while (1) switch (_context.prev = _context.next) { - case 0: - title = _args.length > 1 && _args[1] !== undefined ? _args[1] : ''; - _context.next = 3; - return fetch(manifestURL).then(function (response) { - var fileType = response.headers.get('Content-Type'); - if (fileType.includes('application/json')) { - var jsonData = response.json(); - return jsonData; - } else { - // Avoid throwing an error when fetched file is not a JSON - return {}; - } - }).then(function (manifest) { - var canvases = manifest.items; - var newTranscriptsList = []; - if ((canvases === null || canvases === void 0 ? void 0 : canvases.length) > 0) { - canvases.map(function (canvas, index) { - var annotations = getAnnotations(canvas.annotations, 'supplementing'); - var canvasTranscripts = []; - if (annotations.length > 0) { - var annotBody = annotations[0].body; - if (annotBody.type === 'TextualBody') { - var label = title.length > 0 ? title : annotBody.label ? getLabelValue(annotBody.label) : "Canvas-".concat(index); - var _identifyMachineGen = identifyMachineGen(label), - isMachineGen = _identifyMachineGen.isMachineGen, - labelText = _identifyMachineGen.labelText; - canvasTranscripts.push({ - url: annotBody.id === undefined ? manifestURL : annotBody.id, - title: labelText, - isMachineGen: isMachineGen, - id: "".concat(labelText, "-").concat(index), - format: '' - }); - } else { - annotations.forEach(function (annotation, i) { - var annotBody = annotation.body; - var label = ''; - var filename = ''; - if (annotBody.label && Object.keys(annotBody.label).length > 0) { - var languages = Object.keys(annotBody.label); - if ((languages === null || languages === void 0 ? void 0 : languages.length) > 1) { - // If there are multiple labels for an annotation assume the first - // is the one intended for default display. - label = getLabelValue(annotBody.label); - // Assume that an unassigned language is meant to be the downloadable filename - filename = annotBody.label.hasOwnProperty('none') ? getLabelValue(annotBody.label.none[0]) : label; - } else { - // If there is a single label, use for both label and downloadable filename - label = getLabelValue(annotBody.label); - } - } else { - label = "".concat(i); - } - var id = annotBody.id; - var sType = identifySupplementingAnnotation(id); - var _identifyMachineGen2 = identifyMachineGen(label), - isMachineGen = _identifyMachineGen2.isMachineGen, - labelText = _identifyMachineGen2.labelText; - if (filename === '') { - filename = labelText; - } - if (sType === 1 || sType === 3) { - canvasTranscripts.push({ - title: labelText, - filename: filename, - url: id, - isMachineGen: isMachineGen, - id: "".concat(labelText, "-").concat(index, "-").concat(i), - format: annotBody.format || '' - }); - } - }); - } - } - newTranscriptsList.push({ - canvasId: index, - items: canvasTranscripts - }); - }); - } - return newTranscriptsList; - })["catch"](function (error) { - console.error('transcript-parser -> readSupplementingAnnotations() -> error fetching transcript resource at, ', manifestURL); - return []; - }); - case 3: - data = _context.sent; - return _context.abrupt("return", data); - case 5: - case "end": - return _context.stop(); + /** + * Event handler for VideoJS player instance's 'timeupdate' event, which + * updates the track scrubber from player state. + */ + }, { + key: "handleTimeUpdate", + value: function handleTimeUpdate() { + var _player$markers$getMa; + var player = this.player, + options = this.options, + zoomedOutRef = this.zoomedOutRef; + // Hide track-scrubber for inaccessible item if it is open + if (player.canvasIsEmpty && !zoomedOutRef.current) { + this.setZoomedOut(true); + } + if (player.isDisposed() || player.ended()) return; + /* + Get the current track from the player.markers created from the structure timespans. + In playlists, markers are timepoint information representing highlighting annotations, + therefore omit reading markers information for track scrubber in playlist contexts. + */ + var playerCurrentTime = player.currentTime(); + if (player.markers && typeof player.markers !== 'function' && typeof player.markers.getMarkers === 'function' && ((_player$markers$getMa = player.markers.getMarkers()) === null || _player$markers$getMa === void 0 ? void 0 : _player$markers$getMa.length) > 0 && !options.isPlaylist) { + this.readPlayerMarkers(); + } else { + var _player$playableDurat, _player$altStart; + this.setCurrentTrack({ + duration: (_player$playableDurat = player.playableDuration) !== null && _player$playableDurat !== void 0 ? _player$playableDurat : player.duration(), + time: (_player$altStart = player.altStart) !== null && _player$altStart !== void 0 ? _player$altStart : 0, + key: '', + text: 'Complete media file' + }); + playerCurrentTime = player.srcIndex && player.srcIndex > 0 ? playerCurrentTime + player.altStart : playerCurrentTime; + } + this.updateTrackScrubberProgressBar(playerCurrentTime); + } + /** + * Calculate the progress and current time within the track and + * update them accordingly when the player's 'timeupdate' event fires. + * @param {Number} currentTime player's current time + */ + }, { + key: "updateTrackScrubberProgressBar", + value: function updateTrackScrubberProgressBar() { + var currentTime = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var player = this.player, + currentTrackRef = this.currentTrackRef; + // Handle Safari which emits the timeupdate event really quickly + if (!currentTrackRef.current) { + if (player.markers && typeof player.markers.getMarkers === 'function') { + this.readPlayerMarkers(); + } + } + var altStart = player.altStart, + srcIndex = player.srcIndex; + // Calculate corresponding time and played percentage values within track + var trackoffset = srcIndex > 0 ? currentTime - currentTrackRef.current.time + altStart : currentTime - currentTrackRef.current.time; + var trackpercent = Math.min(100, Math.max(0, 100 * trackoffset / currentTrackRef.current.duration)); + this.populateTrackScrubber(trackoffset, trackpercent); + } + }, { + key: "populateTrackScrubber", + value: + /** + * Update the track scrubber's current time, duration and played percentage + * when it is visible in UI. + * @param {Number} currentTime current time corresponding to the track + * @param {Number} playedPercentage elapsed time percentage of the track duration + */ + function populateTrackScrubber() { + var currentTime = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; + var playedPercentage = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; + var trackScrubberRef = this.options.trackScrubberRef; + if (!trackScrubberRef.current) { + return; } - }, _callee); - })); - return _readSupplementingAnnotations.apply(this, arguments); - } - function sanitizeTranscripts(_x2) { - return _sanitizeTranscripts.apply(this, arguments); - } + var _trackScrubberRef$cur2 = _slicedToArray(trackScrubberRef.current.children, 3), + currentTimeDisplay = _trackScrubberRef$cur2[0]; + _trackScrubberRef$cur2[1]; + var durationDisplay = _trackScrubberRef$cur2[2]; - /** - * Group a nested JSON object array by a given property name - * @param {Array} objectArray nested array to reduced - * @param {String} indexKey property name to be used to group elements in the array - * @param {String} selectKey property to be selected from the objects to accumulated - * @returns {Array} - */ - function _sanitizeTranscripts() { - _sanitizeTranscripts = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee4(transcripts) { - var allTranscripts, sanitizedTrs, newTranscripts; - return regenerator.wrap(function _callee4$(_context4) { - while (1) switch (_context4.prev = _context4.next) { - case 0: - if (!(!transcripts || transcripts == undefined || transcripts.length == 0)) { - _context4.next = 5; - break; - } - console.error('No transcripts given as input'); - return _context4.abrupt("return", []); - case 5: - allTranscripts = []; // Build an empty list for each canvasId from the given transcripts prop - transcripts.map(function (trs) { - return allTranscripts.push({ - canvasId: trs.canvasId, - items: [] - }); - }); + // Set the elapsed time percentage in the progress bar of track scrubber + document.documentElement.style.setProperty('--range-scrubber', "calc(".concat(playedPercentage, "%)")); - // Process the async function to resolve manifest URLs in the given transcripts array - // parallely to extract supplementing annotations in the manifests - _context4.next = 9; - return Promise.all(transcripts.map( /*#__PURE__*/function () { - var _ref5 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee3(transcript) { - var canvasId, items, sanitizedItems; - return regenerator.wrap(function _callee3$(_context3) { - while (1) switch (_context3.prev = _context3.next) { - case 0: - canvasId = transcript.canvasId, items = transcript.items; - _context3.next = 3; - return Promise.all(items.map( /*#__PURE__*/function () { - var _ref6 = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee2(item, index) { - var title, url, manifestTranscripts, _identifyMachineGen3, isMachineGen, labelText, manifestItems, groupedTrs; - return regenerator.wrap(function _callee2$(_context2) { - while (1) switch (_context2.prev = _context2.next) { - case 0: - title = item.title, url = item.url; // For each item in the list check if it is a manifest and parse - // the it to identify any supplementing annotations in the - // manifest for each canvas - _context2.next = 3; - return readSupplementingAnnotations(url, title); - case 3: - manifestTranscripts = _context2.sent; - _identifyMachineGen3 = identifyMachineGen(title), isMachineGen = _identifyMachineGen3.isMachineGen, labelText = _identifyMachineGen3.labelText; - manifestItems = []; - if ((manifestTranscripts === null || manifestTranscripts === void 0 ? void 0 : manifestTranscripts.length) > 0) { - manifestItems = manifestTranscripts.map(function (mt) { - return mt.items; - }).flat(); + // Update the track duration + durationDisplay.innerHTML = timeToHHmmss(this.currentTrackRef.current.duration); + // Update current time elapsed within the current track + var cleanTime = !isNaN(currentTime) && currentTime > 0 ? currentTime : 0; + currentTimeDisplay.innerHTML = timeToHHmmss(cleanTime); + } + }, { + key: "readPlayerMarkers", + value: function readPlayerMarkers() { + var tracks = this.player.markers.getMarkers().filter(function (m) { + return m["class"] == 'ramp--track-marker--fragment'; + }); + if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0) { + this.setCurrentTrack(tracks[0]); + } + } + }, { + key: "handleMouseMove", + value: + /** + * Event handler for mouseenter and mousemove pointer events on the + * the track scrubber. This sets the time tooltip value and its offset + * position in the UI. + * @param {Event} e pointer event for user interaction + */ + function handleMouseMove(e) { + var timeToolRef = this.options.timeToolRef; + if (!timeToolRef.current) { + return; + } + var time = this.getTrackTime(e); - // Concat the existing transcripts list and transcripts from the manifest and - // group them by canvasId - groupedTrs = groupByIndex(allTranscripts.concat(manifestTranscripts), 'canvasId', 'items'); - allTranscripts = groupedTrs; - } + // When hovering over the border of the track scrubber, convertTime() returns infinity, + // since e.target.clientWidth is zero. Use this value to not show the tooltip when this + // occurs. + if (isFinite(time)) { + // Calculate the horizontal position of the time tooltip using the event's offsetX property + var offset = e.offsetX - timeToolRef.current.offsetWidth / 2; // deduct 0.5 x width of tooltip element + timeToolRef.current.style.left = offset + 'px'; - // if manifest doesn't have canvases or - // supplementing annotations add original transcript from props - if (!(manifestTranscripts.length === 0 || manifestItems.length === 0)) { - _context2.next = 11; - break; - } - return _context2.abrupt("return", { - title: labelText, - filename: labelText, - url: url, - isMachineGen: isMachineGen, - id: "".concat(labelText, "-").concat(canvasId, "-").concat(index), - format: '' - }); - case 11: - return _context2.abrupt("return", null); - case 12: - case "end": - return _context2.stop(); - } - }, _callee2); - })); - return function (_x9, _x10) { - return _ref6.apply(this, arguments); - }; - }())); - case 3: - sanitizedItems = _context3.sent; - return _context3.abrupt("return", { - canvasId: canvasId, - items: sanitizedItems.filter(function (i) { - return i != null; - }) - }); - case 5: - case "end": - return _context3.stop(); - } - }, _callee3); - })); - return function (_x8) { - return _ref5.apply(this, arguments); - }; - }())); - case 9: - sanitizedTrs = _context4.sent; - // Group all the transcripts by canvasId one last time to eliminate duplicate canvasIds - newTranscripts = groupByIndex(allTranscripts.concat(sanitizedTrs), 'canvasId', 'items'); - return _context4.abrupt("return", newTranscripts); - case 12: - case "end": - return _context4.stop(); + // Set text in the tooltip as the time relevant to the pointer event's position + timeToolRef.current.innerHTML = timeToHHmmss(time); } - }, _callee4); - })); - return _sanitizeTranscripts.apply(this, arguments); - } - function groupByIndex(objectArray, indexKey, selectKey) { - return objectArray.reduce(function (acc, obj) { - var existing = acc.filter(function (a) { - return a[indexKey] == obj[indexKey]; - }); - if ((existing === null || existing === void 0 ? void 0 : existing.length) > 0) { - var current = existing[0]; - current[selectKey] = current[selectKey].concat(obj[selectKey]); - } else { - acc.push(obj); } - return acc; - }, []); - } + }, { + key: "handleSetProgress", + value: + /** + * Event handler for mousedown event on the track scrubber. This sets the + * progress percentage within track scrubber and update the player's current time + * when user clicks on a point within the track scrubber. + * @param {Event} e pointer event for user interaction + */ + function handleSetProgress(e) { + var currentTrackRef = this.currentTrackRef, + player = this.player; + if (!currentTrackRef.current) { + return; + } + var trackoffset = this.getTrackTime(e); + if (trackoffset != undefined) { + // Calculate percentage of the progress based on the pointer position's + // time and duration of the track + var trackpercent = Math.min(100, Math.max(0, 100 * (trackoffset / currentTrackRef.current.duration))); + + // Set the elapsed time in the scrubber progress bar + document.documentElement.style.setProperty('--range-scrubber', "calc(".concat(trackpercent, "%)")); + + // Set player's current time with respective to the altStart for clipped items + var playerCurrentTime = player.isClipped ? trackoffset + currentTrackRef.current.time : trackoffset; + player.currentTime(playerCurrentTime); + } + } + }, { + key: "getTrackTime", + value: + /** + * Convert pointer position on track scrubber to a time value + * @param {Event} e pointer event for user interaction + * @returns {Number} time corresponding to the pointer position + */ + function getTrackTime(e) { + var currentTrackRef = this.currentTrackRef; + if (!currentTrackRef.current) { + return; + } + var offsetx = e.offsetX; + if (offsetx && offsetx != undefined) { + var time = offsetx / e.target.clientWidth * currentTrackRef.current.duration; + return time; + } + } + }]); + return VideoJSTrackScrubber; + }(Button); + videojs__default["default"].registerComponent('VideoJSTrackScrubber', VideoJSTrackScrubber); + + function _createForOfIteratorHelper$1(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray$1(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } + function _unsupportedIterableToArray$1(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray$1(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray$1(o, minLen); } + function _arrayLikeToArray$1(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } + function ownKeys$4(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } + function _objectSpread$4(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$4(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$4(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } + require('@silvermine/videojs-quality-selector')(videojs__default["default"]); + // import vjsYo from './vjsYo'; /** - * Parse a given transcript file into a format the Transcript component - * can render on the UI. E.g.: text file -> returns null, so that the Google - * doc viewer is rendered, IIIF manifest -> extract and parse transcript data - * within the manifest. - * @param {String} url URL of the transcript file selected - * @param {Number} canvasIndex Current canvas rendered in the player - * @param {String} format transcript file format read from Annotation - * @returns {Object} Array of trancript data objects with download URL + * Module to setup VideoJS instance on initial page load and update + * on successive player reloads on Canvas changes. + * @param {Object} props + * @param {Boolean} props.isVideo + * @param {Boolean} props.isPlaylist + * @param {Object} props.trackScrubberRef + * @param {Object} props.scrubberTooltipRef + * @param {Array} props.tracks + * @param {String} props.placeholderText + * @param {Array} props.renderingFiles + * @param {Boolean} props.enableFileDownload + * @param {Function} props.loadPrevOrNext + * @param {Number} props.lastCanvasIndex + * @param {Boolean} props.enableTitleLink + * @param {String} props.videoJSLangMap + * @param {Object} props.options */ - function parseTranscriptData(_x3, _x4, _x5) { - return _parseTranscriptData.apply(this, arguments); - } + function VideoJSPlayer(_ref) { + var enableFileDownload = _ref.enableFileDownload, + enableTitleLink = _ref.enableTitleLink, + isVideo = _ref.isVideo, + options = _ref.options, + placeholderText = _ref.placeholderText, + scrubberTooltipRef = _ref.scrubberTooltipRef, + tracks = _ref.tracks, + trackScrubberRef = _ref.trackScrubberRef, + videoJSLangMap = _ref.videoJSLangMap, + withCredentials = _ref.withCredentials; + var playerState = usePlayerState(); + var playerDispatch = usePlayerDispatch(); + var manifestState = useManifestState(); + var manifestDispatch = useManifestDispatch(); + var canvasDuration = manifestState.canvasDuration, + canvasLink = manifestState.canvasLink, + hasMultiItems = manifestState.hasMultiItems, + targets = manifestState.targets, + autoAdvance = manifestState.autoAdvance, + structures = manifestState.structures, + canvasSegments = manifestState.canvasSegments, + hasStructure = manifestState.hasStructure; + var isEnded = playerState.isEnded, + isPlaying = playerState.isPlaying, + currentTime = playerState.currentTime; + var _useLocalStorage = useLocalStorage('startVolume', 1), + _useLocalStorage2 = _slicedToArray(_useLocalStorage, 2), + startVolume = _useLocalStorage2[0], + setStartVolume = _useLocalStorage2[1]; + var _useLocalStorage3 = useLocalStorage('startMuted', false), + _useLocalStorage4 = _slicedToArray(_useLocalStorage3, 2), + startMuted = _useLocalStorage4[0], + setStartMuted = _useLocalStorage4[1]; + var _useLocalStorage5 = useLocalStorage('startCaptioned', true), + _useLocalStorage6 = _slicedToArray(_useLocalStorage5, 2), + startCaptioned = _useLocalStorage6[0], + setStartCaptioned = _useLocalStorage6[1]; + var _useLocalStorage7 = useLocalStorage('startQuality', null), + _useLocalStorage8 = _slicedToArray(_useLocalStorage7, 2), + startQuality = _useLocalStorage8[0], + setStartQuality = _useLocalStorage8[1]; + var videoJSRef = React.useRef(null); + var captionsOnRef = React.useRef(); + var activeTrackRef = React.useRef(); + var _useMediaPlayer = useMediaPlayer(), + canvasIndex = _useMediaPlayer.canvasIndex, + canvasIsEmpty = _useMediaPlayer.canvasIsEmpty, + lastCanvasIndex = _useMediaPlayer.lastCanvasIndex; + var _useSetupPlayer = useSetupPlayer({ + enableFileDownload: enableFileDownload, + withCredentials: withCredentials, + lastCanvasIndex: lastCanvasIndex + }), + isPlaylist = _useSetupPlayer.isPlaylist, + renderingFiles = _useSetupPlayer.renderingFiles, + srcIndex = _useSetupPlayer.srcIndex, + switchPlayer = _useSetupPlayer.switchPlayer; + var _useShowInaccessibleM = useShowInaccessibleMessage({ + lastCanvasIndex: lastCanvasIndex + }), + messageTime = _useShowInaccessibleM.messageTime; + var canvasIsEmptyRef = React.useRef(); + canvasIsEmptyRef.current = React.useMemo(function () { + return canvasIsEmpty; + }, [canvasIsEmpty]); + var isEndedRef = React.useRef(); + isEndedRef.current = React.useMemo(function () { + return isEnded; + }, [isEnded]); + var isPlayingRef = React.useRef(); + isPlayingRef.current = React.useMemo(function () { + return isPlaying; + }, [isPlaying]); + var autoAdvanceRef = React.useRef(); + autoAdvanceRef.current = React.useMemo(function () { + return autoAdvance; + }, [autoAdvance]); + var srcIndexRef = React.useRef(); + srcIndexRef.current = React.useMemo(function () { + return srcIndex; + }, [srcIndex]); + var currentTimeRef = React.useRef(); + currentTimeRef.current = React.useMemo(function () { + return currentTime; + }, [currentTime]); - /** - * Parse MS word documents into HTML markdown using mammoth.js - * https://www.npmjs.com/package/mammoth - * @param {Object} response response from the fetch request - * @returns {Array} html markdown for the word document contents - */ - function _parseTranscriptData() { - _parseTranscriptData = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee5(url, canvasIndex, format) { - var tData, tUrl, contentType, fileData, fromContentType, fromAnnotFormat, fileType, urlExt, filteredExt, textData, textLines, jsonData, json, parsedText, _parseTimedText, _tData, tType; - return regenerator.wrap(function _callee5$(_context5) { - while (1) switch (_context5.prev = _context5.next) { - case 0: - tData = []; - tUrl = url; // Validate given URL - if (!(url === undefined)) { - _context5.next = 4; - break; - } - return _context5.abrupt("return", { - tData: tData, - tUrl: tUrl, - tType: TRANSCRIPT_TYPES.invalid - }); - case 4: - contentType = null; - fileData = null; // get file type - _context5.next = 8; - return fetch(url).then(handleFetchErrors).then(function (response) { - contentType = response.headers.get('Content-Type'); - fileData = response; - })["catch"](function (error) { - console.error('transcript-parser -> parseTranscriptData() -> fetching transcript -> ', error); - }); - case 8: - if (!(contentType == null)) { - _context5.next = 10; - break; - } - return _context5.abrupt("return", { - tData: [], - tUrl: tUrl, - tType: TRANSCRIPT_TYPES.invalid - }); - case 10: - /* - Use the Annotation format in the IIIF Manifest, file extension, and the - Content-Type in headers of the fetch request to determine the file type. - These are checked with priority descending in the order of Annotation format, - Content-Type in headers, and file extension in the resource URI. - */ - fromContentType = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { - return tm.type.includes(contentType.split(';')[0]); - }); - fromAnnotFormat = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { - return tm.type.includes(format); - }); - fileType = ''; - if ((fromAnnotFormat === null || fromAnnotFormat === void 0 ? void 0 : fromAnnotFormat.length) > 0) { - fileType = fromAnnotFormat[0].ext; - } else if (fromContentType.length > 0) { - fileType = fromContentType[0].ext; - } else { - urlExt = url.split('.').reverse()[0]; // Only use this if it exists in the supported list of file types for the component - filteredExt = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { - return tm.ext === urlExt; - }); - fileType = filteredExt.length > 0 ? urlExt : ''; - } + /** + * Setup player with player-related information parsed from the IIIF + * Manifest Canvas. This gets called on both initial page load and each + * Canvas switch to setup and update player respectively. + * @param {Object} player current player instance from Video.js + */ + var playerInitSetup = function playerInitSetup(player) { + player.on('ready', function () { + console.log('Player ready'); - // Return empty array to display an error message - if (!(canvasIndex === undefined)) { - _context5.next = 16; - break; - } - return _context5.abrupt("return", { - tData: tData, - tUrl: tUrl, - tType: TRANSCRIPT_TYPES.noTranscript - }); - case 16: - _context5.t0 = fileType; - _context5.next = _context5.t0 === 'json' ? 19 : _context5.t0 === 'txt' ? 28 : _context5.t0 === 'srt' ? 39 : _context5.t0 === 'vtt' ? 39 : _context5.t0 === 'docx' ? 49 : 53; - break; - case 19: - _context5.next = 21; - return fileData.json(); - case 21: - jsonData = _context5.sent; - if (!((jsonData === null || jsonData === void 0 ? void 0 : jsonData.type) === 'Manifest')) { - _context5.next = 26; - break; - } - return _context5.abrupt("return", parseManifestTranscript(jsonData, url, canvasIndex)); - case 26: - json = parseJSONData(jsonData); - return _context5.abrupt("return", { - tData: json.tData, - tUrl: tUrl, - tType: json.tType, - tFileExt: fileType - }); - case 28: - _context5.next = 30; - return fileData.text(); - case 30: - textData = _context5.sent; - textLines = textData.split('\n'); - if (!(textLines.length == 0)) { - _context5.next = 36; - break; - } - return _context5.abrupt("return", { - tData: [], - tUrl: url, - tType: TRANSCRIPT_TYPES.noTranscript - }); - case 36: - parsedText = buildNonTimedText(textLines); - return _context5.abrupt("return", { - tData: parsedText, - tUrl: url, - tType: TRANSCRIPT_TYPES.plainText, - tFileExt: fileType - }); - case 38: - case 39: - _context5.next = 41; - return fileData.text(); - case 41: - textData = _context5.sent; - textLines = textData.split('\n'); - if (!(textLines.length == 0)) { - _context5.next = 47; - break; - } - return _context5.abrupt("return", { - tData: [], - tUrl: url, - tType: TRANSCRIPT_TYPES.noTranscript - }); - case 47: - _parseTimedText = parseTimedText(textData, fileType === 'srt'), _tData = _parseTimedText.tData, tType = _parseTimedText.tType; - return _context5.abrupt("return", { - tData: _tData, - tUrl: url, - tType: tType, - tFileExt: fileType - }); - case 49: - _context5.next = 51; - return parseWordFile(fileData); - case 51: - tData = _context5.sent; - return _context5.abrupt("return", { - tData: splitIntoElements(tData), - tUrl: url, - tType: TRANSCRIPT_TYPES.docx, - tFileExt: fileType - }); - case 53: - return _context5.abrupt("return", { - tData: [], - tUrl: url, - tType: TRANSCRIPT_TYPES.noSupport + // Add this class in mobile/tablet devices to always show the control bar, + // since the inactivityTimeout is flaky in some browsers + if (IS_MOBILE || IS_IPAD) { + player.controlBar.addClass('vjs-mobile-visible'); + } + player.muted(startMuted); + player.volume(startVolume); + player.canvasIndex = cIndexRef.current; + player.duration(canvasDuration); + player.srcIndex = srcIndex; + player.targets = targets; + if (enableTitleLink) player.canvasLink = canvasLink; + + // Need to set this once experimentalSvgIcons option in Video.js options was enabled + player.getChild('controlBar').qualitySelector.setIcon('cog'); + }); + player.on('progress', function () { + // Reveal player if not revealed on 'loadedmetadata' event, allowing user to + // interact with the player since enough data is available for playback + if (player.hasClass('vjs-disabled')) { + player.removeClass('vjs-disabled'); + } + }); + player.on('canplay', function () { + // Reset isEnded flag + playerDispatch({ + isEnded: false, + type: 'setIsEnded' + }); + }); + player.on('play', function () { + playerDispatch({ + isPlaying: true, + type: 'setPlayingStatus' + }); + }); + player.on('timeupdate', function () { + handleTimeUpdate(); + }); + player.on('ended', function () { + /** + * Checking against isReadyRef.current stops from delayed events being executed + * when transitioning from a Canvas to the next. + * Checking against isPlayingRef.current to distinguish whether this event + * triggered intentionally, because Video.js seem to trigger this event when + * switching to a media file with a shorter duration in Safari browsers. + */ + setTimeout(function () { + if (isReadyRef.current && isPlayingRef.current) { + playerDispatch({ + isEnded: true, + type: 'setIsEnded' }); - case 54: - case "end": - return _context5.stop(); - } - }, _callee5); - })); - return _parseTranscriptData.apply(this, arguments); - } - function parseWordFile(_x6) { - return _parseWordFile.apply(this, arguments); - } - /** - * Parse json data into Transcript component friendly - * format - * @param {Object} jsonData array of JSON objects - * @returns {Object} - */ - function _parseWordFile() { - _parseWordFile = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee6(response) { - var tData, data, arrayBuffer; - return regenerator.wrap(function _callee6$(_context6) { - while (1) switch (_context6.prev = _context6.next) { - case 0: - tData = null; - _context6.next = 3; - return response.blob(); + player.pause(); + if (!canvasIsEmptyRef.current) handleEnded(); + } + }, 100); + }); + player.on('volumechange', function () { + setStartMuted(player.muted()); + setStartVolume(player.volume()); + }); + player.on('qualityRequested', function (e, quality) { + setStartQuality(quality.label); + }); + // Use error event listener for inaccessible item display + player.on('error', function (e) { + var error = player.error(); + var errorMessage = 'Something went wrong. Please try again later or contact support for help.'; + // Handle different error codes + switch (error.code) { + case 1: + console.error('MEDIA_ERR_ABORTED: The fetching process for the media resource was aborted by the user agent\ + at the user’s request.'); + break; + case 2: + errorMessage = 'The media could not be loaded due to a network error. Please try again later.'; + console.error('MEDIA_ERR_NETWORK: A network error caused the user agent to stop fetching the media resource,\ + after the resource was established to be usable.'); + break; case 3: - data = _context6.sent; - arrayBuffer = new File([data], name, { - type: response.headers.get('content-type') - }); - _context6.next = 7; - return mammoth__default["default"].convertToHtml({ - arrayBuffer: arrayBuffer - }).then(function (result) { - tData = result.value; - })["catch"](function (err) { - console.error(err); - }); - case 7: - return _context6.abrupt("return", tData); - case 8: - case "end": - return _context6.stop(); + errorMessage = 'Media is corrupt or has features not supported by the browser. \ + Please try a different media or contact support for help.'; + console.error('MEDIA_ERR_DECODE: An error occurred while decoding the media resource, after\ + the resource was established to be usable.'); + break; + case 4: + errorMessage = 'Media could not be loaded. Network error or media format is not supported.'; + console.error('MEDIA_ERR_SRC_NOT_SUPPORTED: The media resource indicated by the src attribute was not suitable.'); + break; + default: + console.error('An unknown error occurred.'); + break; } - }, _callee6); - })); - return _parseWordFile.apply(this, arguments); - } - function parseJSONData(jsonData) { - if (jsonData.length == 0) { - return { - tData: [], - tType: TRANSCRIPT_TYPES.noTranscript - }; - } - var tData = []; - var _iterator = _createForOfIteratorHelper(jsonData), - _step; - try { - for (_iterator.s(); !(_step = _iterator.n()).done;) { - var jd = _step.value; - if (jd.speaker) { - var speaker = jd.speaker, - spans = jd.spans; - var _iterator2 = _createForOfIteratorHelper(spans), - _step2; - try { - for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { - var span = _step2.value; - span.speaker = speaker; - tData.push(span); - } - } catch (err) { - _iterator2.e(err); - } finally { - _iterator2.f(); - } + // Show dismissable error display modal from Video.js + var errorDisplay = player.getChild('ErrorDisplay'); + if (errorDisplay) { + errorDisplay.contentEl().innerText = errorMessage; + errorDisplay.removeClass('vjs-hidden'); + player.removeClass('vjs-error'); + player.removeClass('vjs-disabled'); + } + e.stopPropagation(); + }); + playerLoadedMetadata(player); + }; + + /** + * Update player properties and data when player is reloaded with + * source change, i.e. Canvas change + * @param {Object} player + */ + var updatePlayer = function updatePlayer(player) { + player.duration(canvasDuration); + player.src(options.sources); + player.poster(options.poster); + player.canvasIndex = cIndexRef.current; + player.canvasIsEmpty = canvasIsEmptyRef.current; + player.srcIndex = srcIndex; + player.targets = targets; + if (enableTitleLink) player.canvasLink = canvasLink; + + // Update textTracks in the player + var oldTracks = player.remoteTextTracks(); + var i = oldTracks.length; + while (i--) { + player.removeRemoteTextTrack(oldTracks[i]); + } + if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0 && isVideo) { + tracks.forEach(function (track) { + player.addRemoteTextTrack(track, false); + }); + } + + /* + Update player control bar for; + - track scrubber button + - appearance of the player: big play button and aspect ratio of the player + based on media type + - volume panel based on media type + - file download menu + */ + if (player.getChild('controlBar') != null && !canvasIsEmpty) { + var controlBar = player.getChild('controlBar'); + // Index of the full-screen toggle in the player's control bar + var fullscreenIndex = controlBar.children().findIndex(function (c) { + return c.name_ == 'FullscreenToggle'; + }); + /* + Track-scrubber button: remove if the Manifest is not a playlist manifest + or the current Canvas doesn't have structure items. Or add back in if it's + not present otherwise. + */ + if (!(hasStructure || isPlaylist)) { + controlBar.removeChild('videoJSTrackScrubber'); + } else if (!controlBar.getChild('videoJSTrackScrubber')) { + // Add track-scrubber button after duration display if it is not available + controlBar.addChild('videoJSTrackScrubber', { + trackScrubberRef: trackScrubberRef, + timeToolRef: scrubberTooltipRef + }); + } + if ((tracks === null || tracks === void 0 ? void 0 : tracks.length) > 0 && isVideo && !controlBar.getChild('subsCapsButton')) { + var captionIndex = IS_MOBILE ? controlBar.children().findIndex(function (c) { + return c.name_ == 'MuteToggle'; + }) : controlBar.children().findIndex(function (c) { + return c.name_ == 'VolumePanel'; + }); + var subsCapBtn = controlBar.addChild('subsCapsButton', {}, captionIndex + 1); + // Add CSS to mark captions-on + subsCapBtn.children_[0].addClass('captions-on'); + } + + /* + Change player's appearance when switching between audio and video canvases. + For audio: player height is reduced and big play button is removed + For video: player aspect ratio is set to 16:9 and has the centered big play button + */ + if (!isVideo) { + player.audioOnlyMode(true); + player.addClass('vjs-audio'); + player.height(player.controlBar.height()); + player.removeChild('bigPlayButton'); } else { - var _iterator3 = _createForOfIteratorHelper(jd.spans), - _step3; - try { - for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { - var _span = _step3.value; - _span.format = 'text/plain'; - _span.tag = TRANSCRIPT_CUE_TYPES.timedCue; - tData.push(_span); - } - } catch (err) { - _iterator3.e(err); - } finally { - _iterator3.f(); + player.audioOnlyMode(false); + player.removeClass('vjs-audio'); + player.aspectRatio('16:9'); + player.addChild('bigPlayButton'); + } + + /* + Re-add volumePanel/muteToggle icon: ensures the correct order of controls + on player reload. + On mobile device browsers, the volume panel is replaced by muteToggle + for both audio and video. + */ + if (!IS_MOBILE) { + controlBar.removeChild('VolumePanel'); + controlBar.addChild('VolumePanel'); + /* + Trigger ready event to reset the volume slider in the refreshed + volume panel. This is needed on player reload, since volume slider + is set on either 'ready' or 'volumechange' events. + */ + player.trigger('volumechange'); + } else { + controlBar.removeChild('MuteToggle'); + controlBar.addChild('MuteToggle'); + } + if (enableFileDownload) { + var fileDownloadIndex = controlBar.children().findIndex(function (c) { + return c.name_ == 'VideoJSFileDownload'; + }) || fullscreenIndex + 1; + controlBar.removeChild('videoJSFileDownload'); + if ((renderingFiles === null || renderingFiles === void 0 ? void 0 : renderingFiles.length) > 0) { + var fileOptions = { + title: 'Download Files', + controlText: 'Alternate resource download', + files: renderingFiles + }; + controlBar.addChild('videoJSFileDownload', _objectSpread$4({}, fileOptions), fileDownloadIndex); } } } - } catch (err) { - _iterator.e(err); - } finally { - _iterator.f(); - } - return { - tData: tData, - tType: TRANSCRIPT_TYPES.timedText + playerLoadedMetadata(player); }; - } - - /* Parsing annotations when transcript data is fed from a IIIF manifest */ - /** - * Parse a IIIF manifest and extracts the transcript data. - * IIIF manifests can present transcript data in a couple of different ways. - * 1. Using 'rendering' prop to link to an external file - * a. when the external file contains only text - * b. when the external file contains annotations - * 2. Using IIIF 'annotations' within the manifest - * @param {Object} manifest IIIF manifest data - * @param {String} manifestURL IIIF manifest URL - * @param {Number} canvasIndex Current canvas index - * @returns {Object} object with the structure; - * { tData: transcript data, tUrl: file url } - */ - function parseManifestTranscript(manifest, manifestURL, canvasIndex) { - var _manifest$items; - var tData = []; - var tUrl = manifestURL; - var isExternalAnnotation = false; - var annotations = []; - if (manifest.annotations) { - annotations = getAnnotations(manifest.annotations, 'supplementing'); - } else if (((_manifest$items = manifest.items) === null || _manifest$items === void 0 ? void 0 : _manifest$items.length) > 0) { - var _manifest$items$canva; - annotations = getAnnotations((_manifest$items$canva = manifest.items[canvasIndex]) === null || _manifest$items$canva === void 0 ? void 0 : _manifest$items$canva.annotations, 'supplementing'); - } - - // determine whether annotations point to an external resource or - // a list of transcript fragments - if (annotations.length > 0) { - var annotation = annotations[0]; - var tType = annotation.body.type; - if (tType == 'TextualBody') { - isExternalAnnotation = false; - } else { - isExternalAnnotation = true; - } - } else { - return { - tData: [], - tUrl: tUrl, - tType: TRANSCRIPT_TYPES.noTranscript - }; - } - if (isExternalAnnotation) { - var _annotation = annotations[0]; - return parseExternalAnnotations(_annotation); - } else { - tData = createTData(annotations); - return { - tData: tData, - tUrl: tUrl, - tType: TRANSCRIPT_TYPES.timedText, - tFileExt: 'json' - }; - } - } - /** - * Parse annotation linking to external resources like WebVTT, SRT, Text, and - * AnnotationPage .json files - * @param {Annotation} annotation Annotation from the manifest - * @returns {Object} object with the structure { tData: [], tUrl: '', tType: '' } - */ - function parseExternalAnnotations(_x7) { - return _parseExternalAnnotations.apply(this, arguments); - } - /** - * Converts Annotation to the common format that the - * transcripts component expects - * @param {Array} annotations array of Annotations - * @returns {Array} array of JSON objects - * Structure of the JSON object is as follows; - * { - * begin: 0, - * end: 60, - * text: 'Transcript text', - * format: 'text/plain', - * } - */ - function _parseExternalAnnotations() { - _parseExternalAnnotations = _asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee7(annotation) { - var tData, type, tBody, tUrl, tType, tFormat, tFileExt; - return regenerator.wrap(function _callee7$(_context7) { - while (1) switch (_context7.prev = _context7.next) { - case 0: - tData = []; - type = ''; - tBody = annotation.body; - tUrl = tBody.id; - tType = tBody.type; - tFormat = tBody.format; - tFileExt = ''; - /** When external file contains text data */ - if (!(tType === 'Text')) { - _context7.next = 12; - break; - } - _context7.next = 10; - return fetch(tUrl).then(handleFetchErrors).then(function (response) { - return response.text(); - }).then(function (data) { - if (TRANSCRIPT_MIME_TYPES.webvtt.includes(tFormat) || TRANSCRIPT_MIME_TYPES.srt.includes(tFormat)) { - var parsed = parseTimedText(data, TRANSCRIPT_MIME_TYPES.srt.includes(tFormat)); - tData = parsed.tData; - type = parsed.tType; - tFileExt = TRANSCRIPT_MIME_EXTENSIONS.filter(function (tm) { - return tm.type.includes(tFormat); - })[0].ext; - } else { - var textLines = data.split('\n'); - tData = buildNonTimedText(textLines); - type = TRANSCRIPT_TYPES.plainText; - tFileExt = 'txt'; - } - })["catch"](function (error) { - console.error('transcript-parser -> parseExternalAnnotations() -> fetching external transcript -> ', error); - throw error; - }); - case 10: - _context7.next = 15; - break; - case 12: - if (!(tType === 'AnnotationPage')) { - _context7.next = 15; - break; - } - _context7.next = 15; - return fetch(tUrl).then(handleFetchErrors).then(function (response) { - return response.json(); - }).then(function (data) { - var annotations = getAnnotations([data], 'supplementing'); - tData = createTData(annotations); - type = TRANSCRIPT_TYPES.timedText; - tFileExt = 'json'; + /** + * Setup on loadedmetadata event is broken out of initial setup function, + * since this needs to be called when reloading the player on Canvas change + * @param {Object} player Video.js player instance + */ + var playerLoadedMetadata = function playerLoadedMetadata(player) { + player.one('loadedmetadata', function () { + console.log('Player loadedmetadata'); + player.duration(canvasDuration); + isEndedRef.current ? player.currentTime(0) : player.currentTime(currentTimeRef.current); + if (isEndedRef.current || isPlayingRef.current) { + /* + iOS devices lockdown the ability for unmuted audio and video media to autoplay. + They accomplish this by capturing any programmatic play events and returning + a rejected Promise. In certain versions of iOS, this rejected promise would + cause a runtime error within Ramp. This error would cause the error boundary + handling to trigger, forcing a user to reload the player/page. By silently + catching the rejected Promise we are able to provide a more seamless user + experience, where the user can manually play the media or change to a different + section. + */ + var promise = player.play(); + if (promise !== undefined) { + promise.then(function (_) { + // Autoplay })["catch"](function (error) { - console.error('transcript-parser -> parseExternalAnnotations() -> fetching annotations -> ', error); - throw error; - }); - case 15: - return _context7.abrupt("return", { - tData: tData, - tUrl: tUrl, - tType: type, - tFileExt: tFileExt + // Prevent error from triggering error boundary }); - case 16: - case "end": - return _context7.stop(); + } + } + if (isVideo) { + setUpCaptions(player); } - }, _callee7); - })); - return _parseExternalAnnotations.apply(this, arguments); - } - function createTData(annotations) { - var tData = []; - annotations.map(function (a) { - if (a.id != null) { - var tBody = a.body; - var _getMediaFragment = getMediaFragment(a.target), - start = _getMediaFragment.start, - end = _getMediaFragment.end; - tData.push({ - text: tBody.value, - format: tBody.format, - begin: parseFloat(start), - end: parseFloat(end), - tag: TRANSCRIPT_CUE_TYPES.timedCue - }); - } - }); - return tData; - } - - /** - * Parsing transcript data from a given file with timed text - * @param {Object} fileData content in the transcript file - * @param {Boolean} isSRT given transcript file is an SRT - * @returns {Array} array of JSON objects of the following - * structure; - * { - * begin: '00:00:00.000', - * end: '00:01:00.000', - * text: 'Transcript text sample' - * tag: NOTE || TIMED_CUE - * } - */ - function parseTimedText(fileData) { - var isSRT = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; - var tData = []; - var noteLines = []; - - // split file content into lines - var lines = fileData.split('\n'); - // For SRT files all of the file content is considered as cues - var cueLines = lines; - if (!isSRT) { - var _validateWebVTT = validateWebVTT(lines), - valid = _validateWebVTT.valid, - cue_lines = _validateWebVTT.cue_lines, - notes = _validateWebVTT.notes; - if (!valid) { - console.error('Invalid WebVTT file'); - return { - tData: [], - tType: TRANSCRIPT_TYPES.invalidVTT - }; - } - cueLines = cue_lines; - noteLines = notes; - } - var groups = groupTimedTextLines(cueLines); + /* + Set playable duration within the given media file and alternate start time as + player properties. These values are read by track-scrubber component to build + and update the track-scrubber progress and time in the UI. + */ + var mediaRange = getMediaFragment(player.src(), canvasDuration); + if (mediaRange != undefined) { + player.playableDuration = mediaRange.end - mediaRange.start; + player.altStart = mediaRange.start; + } else { + player.playableDuration = canvasDuration; + player.altStart = targets[srcIndex].altStart; + } + player.canvasIndex = cIndexRef.current; + setIsReady(true); - // Add back the NOTE(s) in the header block - groups.unshift.apply(groups, _toConsumableArray(noteLines)); - var hasInvalidTimestamp = false; - for (var i = 0; i < groups.length;) { - var line = parseTimedTextLine(groups[i], isSRT); - if (!line) { - hasInvalidTimestamp || (hasInvalidTimestamp = true); - break; - } else { - tData.push(line); - i++; - } - } - return { - tData: hasInvalidTimestamp ? null : tData, - tType: hasInvalidTimestamp ? TRANSCRIPT_TYPES.invalidTimestamp : TRANSCRIPT_TYPES.timedText + /** + * Update currentNavItem on loadedmetadata event in Safari, as it doesn't + * trigger the 'timeupdate' event intermittently on load. + */ + if (IS_SAFARI) { + handleTimeUpdate(); + } + + /** + * When either player/browser tab is muted Safari and Chrome in iOS doesn't seem to + * load enough data related to audio-only media for the Video.js instance to play + * on page load. + * Since, it is not possible to detect muted tabs in JS the condition avoids + * checking for muted state altogether. + * Without this, Safari will not reach player.readyState() = 4, the state + * which indicates the player that enough data is available on the media + * for playback. + */ + if (!isVideo && (IS_SAFARI || IS_IOS) && player.readyState() != 4) { + player.load(); + } + + // Reveal player if not revealed on 'progress' event, allowing user to + // interact with the player since enough data is available for playback + if (player.hasClass('vjs-disabled')) { + player.removeClass('vjs-disabled'); + } + }); }; - } + var _useVideoJSPlayer = useVideoJSPlayer({ + options: options, + playerInitSetup: playerInitSetup, + updatePlayer: updatePlayer, + startQuality: startQuality, + tracks: tracks, + videoJSRef: videoJSRef, + videoJSLangMap: videoJSLangMap + }), + activeId = _useVideoJSPlayer.activeId, + fragmentMarker = _useVideoJSPlayer.fragmentMarker, + isReadyRef = _useVideoJSPlayer.isReadyRef, + playerRef = _useVideoJSPlayer.playerRef, + setActiveId = _useVideoJSPlayer.setActiveId, + setFragmentMarker = _useVideoJSPlayer.setFragmentMarker, + setIsReady = _useVideoJSPlayer.setIsReady; + var cIndexRef = React.useRef(); + cIndexRef.current = React.useMemo(function () { + return canvasIndex; + }, [canvasIndex]); + var activeIdRef = React.useRef(); + activeIdRef.current = React.useMemo(function () { + return activeId; + }, [activeId]); - /** - * Validate WebVTT file with its header content - * @param {Array} lines WebVTT file content split into lines - * @returns {Boolean} - */ - function validateWebVTT(lines) { - var firstLine = lines.shift().trim(); - if ((firstLine === null || firstLine === void 0 ? void 0 : firstLine.length) == 6 && firstLine === 'WEBVTT') { - var _validateWebVTTHeader = validateWebVTTHeaders(lines), - valid = _validateWebVTTHeader.valid, - cue_lines = _validateWebVTTHeader.cue_lines, - notes = _validateWebVTTHeader.notes; - return { - valid: valid, - cue_lines: cue_lines, - notes: notes - }; - } else { - return { - valid: false, - cue_lines: [], - notes: [] - }; - } - } + /** + * Setup captions for the player based on context + * @param {Object} player Video.js player instance + */ + var setUpCaptions = function setUpCaptions(player) { + var _textTracks$tracks_; + var textTracks = player.textTracks(); + /* + Filter the text track Video.js adds with an empty label and language + when nativeTextTracks are enabled for iPhones and iPads. + Related links, Video.js => https://github.com/videojs/video.js/issues/2808 and + in Apple => https://developer.apple.com/library/archive/qa/qa1801/_index.html + */ + if (IS_MOBILE && !IS_ANDROID) { + textTracks.on('addtrack', function () { + for (var i = 0; i < textTracks.length; i++) { + if (textTracks[i].language === '' && textTracks[i].label === '') { + player.textTracks().removeTrack(textTracks[i]); + } + /** + * This enables the caption in the native iOS player first playback. + * Only enable caption when captions are turned on. + * First caption is already turned on in the code block below, so read it + * from activeTrackRef + */ + if (startCaptioned && activeTrackRef.current) { + textTracks.tracks_.filter(function (t) { + return t.label === activeTrackRef.current.label && t.language === activeTrackRef.current.language; + })[0].mode = 'showing'; + } + } + }); + } - /** - * Validate the text between 'WEBVTT' at the start and start of - * VTT cues. It looks for REGION and STYLE blocks and skips over these - * blocks. This doesn't validate the content within these blocks. - * When there's text in the header not followed by the keywords REGION and - * STYLE the WebVTT file is marked invalid. - * @param {Array} lines WebVTT file content split into lines - * @returns - */ - function validateWebVTTHeaders(lines) { - var endOfHeadersIndex = 0; - var firstCueIndex = 0; - var hasTextBeforeCues = false; - var notesInHeader = []; + // Turn first caption/subtitle ON and turn captions ON indicator via CSS on first load + if (((_textTracks$tracks_ = textTracks.tracks_) === null || _textTracks$tracks_ === void 0 ? void 0 : _textTracks$tracks_.length) > 0) { + var firstSubCap = null; + // Flag to identify first valid caption for resource + var onFirstCap = false; + // Disable all text tracks to avoid multiple selections and pick the first one as default + for (var i = 0; i < textTracks.tracks_.length; i++) { + var t = textTracks.tracks_[i]; + if ((t.kind === 'subtitles' || t.kind === 'captions') && t.language != '' && t.label != '') { + t.mode = 'disabled'; + if (!onFirstCap) firstSubCap = t; + onFirstCap = true; + } + } - // Remove line numbers for vtt cues - lines = lines.filter(function (l) { - return Number(l) ? false : true; - }); - for (var i = 0; i < lines.length; i++) { - var line = lines[i]; - // Skip REGION and STYLE blocks as these are related to displaying cues as overlays - if (/^REGION$/.test(line.toUpperCase()) || /^STYLE$/.test(line.toUpperCase())) { - // Increment until an empty line is encountered within the header block - i++; - while (i < lines.length && (!lines[i] == '\r' || !lines[i] == '\n' || !lines[i] == '\r\n')) { - i++; + // Enable the first caption when captions are enabled in the session + if (firstSubCap && startCaptioned) { + firstSubCap.mode = 'showing'; + activeTrackRef.current = firstSubCap; + handleCaptionChange(true); } - endOfHeadersIndex = i; } - // Gather comments presented as NOTE(s) in the header block to be displayed as transcript - else if (/^NOTE$/.test(line.toUpperCase())) { - var noteText = line; - i++; - // Increment until an empty line is encountered within the NOTE block - while (i < lines.length && (!lines[i] == '\r' || !lines[i] == '\n' || !lines[i] == '\r\n')) { - noteText = "".concat(noteText, "
      ").concat(lines[i].trim()); - i++; + + // Add/remove CSS to indicate captions/subtitles is turned on + textTracks.on('change', function () { + var trackModes = []; + for (var _i = 0; _i < textTracks.tracks_.length; _i++) { + var _textTracks$_i = textTracks[_i], + mode = _textTracks$_i.mode, + label = _textTracks$_i.label, + kind = _textTracks$_i.kind; + trackModes.push(textTracks[_i].mode); + if (mode === 'showing' && label != '' && (kind === 'subtitles' || kind === 'captions')) { + activeTrackRef.current = textTracks[_i]; + } } - notesInHeader.push({ - times: '', - line: noteText, - tag: TRANSCRIPT_CUE_TYPES.note - }); - } - // Terminate validation once the first cue is reached - else if (line.includes('-->')) { - // Break the loop when it reaches the first vtt cue - firstCueIndex = i; - break; + var subsOn = trackModes.includes('showing') ? true : false; + handleCaptionChange(subsOn); + setStartCaptioned(subsOn); + }); + }; + + /** + * Add CSS class to icon to indicate captions are on/off in player control bar + * @param {Boolean} subsOn flag to indicate captions are on/off + */ + var handleCaptionChange = function handleCaptionChange(subsOn) { + var player = playerRef.current; + /** + * When subsCapsButton is not setup on Video.js initialization step, and is + * later added in updatePlayer() function player.controlBar.getChild() method + * needs to be used to access it. + */ + var subsCapsBtn = player.controlBar.getChild('subsCapsButton'); + /* + For audio instances Video.js is setup to not to build the CC button + in Ramp's player control bar. + */ + if (subsCapsBtn == undefined || !subsCapsBtn || !(subsCapsBtn !== null && subsCapsBtn !== void 0 && subsCapsBtn.children_)) { + return; } - // Flag to check for invalid text before cue lines - else if (typeof line === 'string' && line.trim().length != 0) { - hasTextBeforeCues = true; + if (subsOn) { + subsCapsBtn.children_[0].addClass('captions-on'); + captionsOnRef.current = true; + } else { + subsCapsBtn.children_[0].removeClass('captions-on'); + captionsOnRef.current = false; } - } + }; + + /** + * Handle the 'ended' event fired by the player when a section comes to + * an end. If there are sections ahead move onto the next canvas and + * change the player and the state accordingly. + * Throttle helps to cancel the delayed function call triggered by ended event and + * load the correct item into the player, when the user clicks on a different item + * (not the next item in list) when the current item is coming to its end. + */ + var handleEnded = React.useMemo(function () { + return throttle_1(function () { + var isLastCanvas = cIndexRef.current === lastCanvasIndex; + /** + * Do nothing if Canvas is not multi-sourced AND autoAdvance is turned off + * OR current Canvas is the last Canvas in the Manifest + */ + if ((!autoAdvanceRef.current || isLastCanvas) && !hasMultiItems) { + return; + } else { + // Remove all the existing structure related markers in the player + if (playerRef.current && playerRef.current.markers) { + playerRef.current.pause(); + setFragmentMarker(null); + playerRef.current.markers.removeAll(); + } + if (hasMultiItems) { + // When there are multiple sources in a single canvas + // advance to next source + if (srcIndex + 1 < targets.length) { + manifestDispatch({ + srcIndex: srcIndex + 1, + type: 'setSrcIndex' + }); + playerDispatch({ + currentTime: 0, + type: 'setCurrentTime' + }); + playerRef.current.play(); + } else { + return; + } + } else if ((structures === null || structures === void 0 ? void 0 : structures.length) > 0) { + var nextItem = structures[cIndexRef.current + 1]; + if (nextItem) { + manifestDispatch({ + canvasIndex: cIndexRef.current + 1, + type: 'switchCanvas' + }); - // Return the cues and comments in the header block when the given WebVTT is valid - if (firstCueIndex > endOfHeadersIndex && !hasTextBeforeCues) { - return { - valid: true, - cue_lines: lines.slice(firstCueIndex), - notes: notesInHeader - }; - } else { - return { - valid: false - }; - } - } + // Reset startTime and currentTime to zero + playerDispatch({ + startTime: 0, + type: 'setTimeFragment' + }); + playerDispatch({ + currentTime: 0, + type: 'setCurrentTime' + }); - /** - * Group multi line transcript text values alongside the relevant - * timestamp values. E.g. converts, - * [ - * "00:00:00.000 --> 00:01:00.000", "Transcript", " from multiple lines", - * "00:03:00.000 --> 00:04:00.000", "Next transcript text", - * "NOTE This is a comment" - * ] - * into - * [ - * { times: "00:00:00.000 --> 00:01:00.000", line: "Transcript from multiple lines", tag: "TIMED_CUE" }, - * { times: "00:03:00.000 --> 00:04:00.000", line: "Next transcript text", tag: "TIMED_CUE" }, - * { times: "", line: "NOTE This is a comment", tag: "NOTE" } - * ] - * @param {Array} lines array of lines in the WebVTT file - * @returns {Array} - */ - function groupTimedTextLines(lines) { - var groups = []; - var i; - for (i = 0; i < lines.length; i++) { - var line = lines[i]; - var t = {}; - if (line.includes('-->') || /^NOTE/.test(line)) { - var isNote = /^NOTE/.test(line); - t.times = isNote ? "" : line; - t.tag = isNote ? TRANSCRIPT_CUE_TYPES.note : TRANSCRIPT_CUE_TYPES.timedCue; - // Make sure there is a single space separating NOTE from the comment for single or multi-line comments - t.line = isNote ? line.replace(/^NOTE\s*/, 'NOTE ') : ''; - i++; + // Get first timespan in the next canvas + var firstTimespanInNextCanvas = canvasSegments.filter(function (t) { + return t.canvasIndex === nextItem.canvasIndex && t.itemIndex === 1; + }); + // If the nextItem doesn't have an ID (a Canvas media fragment) pick the first timespan + // in the next Canvas + var nextFirstItem = nextItem.id != undefined ? nextItem : firstTimespanInNextCanvas[0]; + var start = 0; + if (nextFirstItem != undefined && nextFirstItem.id != undefined) { + start = getMediaFragment(nextFirstItem.id, canvasDuration).start; + } - // Increment until an empty line is encountered marking the end of the block - while (i < lines.length && !(lines[i] == '\r' || lines[i] == '\n' || lines[i] == '\r\n' || lines[i] == '')) { - t.line += lines[i].endsWith('-') ? lines[i] : lines[i].replace(/\s*$/, ' '); - i++; + // If there's a timespan item at the start of the next canvas + // mark it as the currentNavItem. Otherwise empty out the currentNavItem. + if (start === 0) { + manifestDispatch({ + item: nextFirstItem, + type: 'switchItem' + }); + } else if (nextFirstItem.isEmpty) { + // Switch the currentNavItem and clear isEnded flag + manifestDispatch({ + item: nextFirstItem, + type: 'switchItem' + }); + playerRef.current.currentTime(start); + // Only play if the next item is not an inaccessible item + if (!nextItem.isEmpty) playerRef.current.play(); + } + } + } + } + }); + }, [cIndexRef.current]); + + /** + * Handle the 'timeUpdate' event emitted by VideoJS player. + * The current time of the playhead used to show structure in the player's + * time rail as the playhead arrives at a start time of an existing structure + * item. When the current time is inside an item, that time fragment is highlighted + * in the player's time rail. + * Using throttle helps for smooth updates by cancelling and cleaning up intermediate + * delayed function calls. + */ + var handleTimeUpdate = React.useMemo(function () { + return throttle_1(function () { + var player = playerRef.current; + if (player && isReadyRef.current) { + var _player$currentTime; + var playerTime = (_player$currentTime = player.currentTime()) !== null && _player$currentTime !== void 0 ? _player$currentTime : currentTimeRef.current; + if (hasMultiItems && srcIndexRef.current > 0) { + playerTime = playerTime + targets[srcIndexRef.current].altStart; + } + var activeSegment = getActiveSegment(playerTime); + // the active segment has changed + if (activeIdRef.current !== (activeSegment === null || activeSegment === void 0 ? void 0 : activeSegment.id)) { + if (!activeSegment) { + /** + * Clear currentNavItem and other related state variables to update the tracker + * in structure navigation and highlights within the player. + */ + manifestDispatch({ + item: null, + type: 'switchItem' + }); + setActiveId(null); + setFragmentMarker(null); + } else { + // Set the active segment in state + manifestDispatch({ + item: activeSegment, + type: 'switchItem' + }); + setActiveId(activeSegment.id); + if (!isPlaylist && player.markers) { + var _getMediaFragment = getMediaFragment(activeSegment.id, activeSegment.canvasDuration), + start = _getMediaFragment.start, + end = _getMediaFragment.end; + playerDispatch({ + endTime: end, + startTime: start, + type: 'setTimeFragment' + }); + if (start !== end) { + // don't let marker extend past the end of the canvas + var markerEnd = end > activeSegment.canvasDuration ? activeSegment.canvasDuration : end; + setFragmentMarker({ + time: start, + duration: markerEnd - start, + text: start, + "class": 'ramp--track-marker--fragment' + }); + } else { + // to prevent zero duration fragments I suppose + setFragmentMarker(null); + } + } else if (fragmentMarker !== null) { + setFragmentMarker(null); + } + } + } + } + }, 10); + }, []); + + /** + * Toggle play/pause on video touch for mobile browsers + * @param {Object} e onTouchEnd event + */ + var mobilePlayToggle = function mobilePlayToggle(e) { + var player = playerRef.current; + if (e.changedTouches[0].clientX == touchX && e.changedTouches[0].clientY == touchY) { + if (player.paused()) { + player.play(); + } else { + player.pause(); } - t.line = t.line.trimEnd(); - groups.push(t); } - } - return groups; - } + }; - /** - * Create a JSON object from the transcript data - * @param {Object} obj - * @param {String} obj.times string with time information - * @param {String} obj.line string with transcript text - * @returns {Object} of the format; - * { - * begin: 0, - * end: 60, - * text: 'Transcript text sample', - * tag: NOTE || TIMED_CUE - * } - */ - function parseTimedTextLine(_ref, isSRT) { - var times = _ref.times, - line = _ref.line, - tag = _ref.tag; - var timestampRegex; - if (isSRT) { - // SRT allows using comma for milliseconds while WebVTT does not - timestampRegex = SRT_TIMESTAMP_REGEX; - } else { - timestampRegex = VTT_TIMESTAMP_REGEX; - } - switch (tag) { - case TRANSCRIPT_CUE_TYPES.note: - return { - begin: 0, - end: 0, - text: line, - tag: tag - }; - case TRANSCRIPT_CUE_TYPES.timedCue: - var _times$split = times.split(' --> '), - _times$split2 = _slicedToArray(_times$split, 2), - start = _times$split2[0], - end = _times$split2[1]; - // FIXME:: remove any styles for now, refine this - end = end.split(' ')[0]; - if (!start.match(timestampRegex) || !end.match(timestampRegex)) { - console.error('Invalid timestamp in line with text; ', line); - return null; + /** + * Save coordinates of touch start for comparison to touch end to prevent play/pause + * when user is scrolling. + * @param {Object} e onTouchStart event + */ + var touchX = null; + var touchY = null; + var saveTouchStartCoords = function saveTouchStartCoords(e) { + touchX = e.touches[0].clientX; + touchY = e.touches[0].clientY; + }; + + /** + * Get the segment, which encapsulates the current time of the playhead, + * from a list of media fragments in the current canvas. + * @param {Number} time playhead's current time + */ + var getActiveSegment = function getActiveSegment(time) { + if (isPlaylist) { + // For playlists timespans and canvasIdex are mapped one-to-one + return canvasSegments[cIndexRef.current]; + } else { + // Find the relevant media segment from the structure + var _iterator = _createForOfIteratorHelper$1(canvasSegments), + _step; + try { + for (_iterator.s(); !(_step = _iterator.n()).done;) { + var segment = _step.value; + var id = segment.id, + isCanvas = segment.isCanvas, + _canvasIndex = segment.canvasIndex; + if (_canvasIndex == cIndexRef.current + 1) { + // Canvases without structure has the Canvas information + // in Canvas-level item as a navigable link + if (isCanvas) { + return segment; + } + var segmentRange = getMediaFragment(id, canvasDuration); + var isInRange = checkSrcRange(segmentRange, canvasDuration); + var isInSegment = time >= segmentRange.start && time < segmentRange.end; + if (isInSegment && isInRange) { + return segment; + } + } + } + } catch (err) { + _iterator.e(err); + } finally { + _iterator.f(); } - return { - begin: timeToS(start), - end: timeToS(end), - text: line, - tag: tag - }; - default: return null; - } + } + }; + + /** + * Click event handler for previous/next buttons in inaccessible + * message display + * @param {Number} c updated Canvas index upon event trigger + */ + var handlePrevNextClick = function handlePrevNextClick(c) { + switchPlayer(c, true); + }; + + /** + * Keydown event handler for previou/next buttons in inaccessible + * message display. + * IMPORTANT: btnName param should be either 'nextBtn' or 'previousBtn' + * @param {Event} e keydown event + * @param {Number} c update Canvas index upon event trigger + * @param {String} btnName name of the pressed button + */ + var handlePrevNextKeydown = function handlePrevNextKeydown(e, c, btnName) { + if (e.which === 32 || e.which === 13) { + switchPlayer(c, true, btnName); + } + }; + return /*#__PURE__*/React__default["default"].createElement("div", null, /*#__PURE__*/React__default["default"].createElement("div", { + "data-vjs-player": true, + "data-canvasindex": cIndexRef.current + }, canvasIsEmptyRef.current && /*#__PURE__*/React__default["default"].createElement("div", { + "data-testid": "inaccessible-message-display" + // These styles needs to be inline for the poster to display within the Video boundaries + , + style: { + position: !playerRef.current ? 'relative' : 'absolute', + top: 0, + left: 0, + right: 0, + bottom: 0, + display: 'flex', + flexDirection: 'column', + justifyContent: 'center', + alignItems: 'center', + fontSize: 'medium', + textAlign: 'center', + color: '#fff', + backgroundColor: 'black', + zIndex: 101, + aspectRatio: !playerRef.current ? '16/9' : '' + } + }, /*#__PURE__*/React__default["default"].createElement("p", { + className: "ramp--media-player_inaccessible-message-content", + "data-testid": "inaccessible-message-content", + dangerouslySetInnerHTML: { + __html: placeholderText + } + }), /*#__PURE__*/React__default["default"].createElement("div", { + className: "ramp--media-player_inaccessible-message-buttons" + }, canvasIndex >= 1 && /*#__PURE__*/React__default["default"].createElement("button", { + "aria-label": "Go back to previous item", + onClick: function onClick() { + return handlePrevNextClick(canvasIndex - 1); + }, + onKeyDown: function onKeyDown(e) { + return handlePrevNextKeydown(e, canvasIndex - 1, 'previousBtn'); + }, + "data-testid": "inaccessible-previous-button" + }, /*#__PURE__*/React__default["default"].createElement(SectionButtonIcon, { + flip: true + }), " Previous"), canvasIndex != lastCanvasIndex && /*#__PURE__*/React__default["default"].createElement("button", { + "aria-label": "Go to next item", + onClick: function onClick() { + return handlePrevNextClick(canvasIndex + 1); + }, + onKeyDown: function onKeyDown(e) { + return handlePrevNextKeydown(e, canvasIndex + 1, 'nextBtn'); + }, + "data-testid": "inaccessible-next-button" + }, "Next ", /*#__PURE__*/React__default["default"].createElement(SectionButtonIcon, null))), canvasIndex != lastCanvasIndex && /*#__PURE__*/React__default["default"].createElement("p", { + "data-testid": "inaccessible-message-timer", + className: cx__default["default"]('ramp--media-player_inaccessible-message-timer', autoAdvanceRef.current ? '' : 'hidden') + }, "Next item in ".concat(messageTime, " second").concat(messageTime === 1 ? '' : 's'))), /*#__PURE__*/React__default["default"].createElement("video", { + "data-testid": "videojs-".concat(isVideo ? 'video' : 'audio', "-element"), + "data-canvasindex": cIndexRef.current, + ref: videoJSRef, + className: cx__default["default"]('video-js vjs-big-play-centered vjs-theme-ramp vjs-disabled', IS_ANDROID ? 'is-mobile' : ''), + onTouchStart: saveTouchStartCoords, + onTouchEnd: mobilePlayToggle, + style: { + display: "".concat(canvasIsEmptyRef.current ? 'none' : '') + } + })), (hasStructure || isPlaylist) && /*#__PURE__*/React__default["default"].createElement("div", { + className: "vjs-track-scrubber-container hidden", + ref: trackScrubberRef, + id: "track_scrubber" + }, /*#__PURE__*/React__default["default"].createElement("p", { + className: "vjs-time track-currenttime", + role: "presentation" + }), /*#__PURE__*/React__default["default"].createElement("span", { + type: "range", + "aria-label": "Track scrubber", + role: "slider", + tabIndex: 0, + className: "vjs-track-scrubber", + style: { + width: '100%' + } + }, !IS_TOUCH_ONLY && /*#__PURE__*/React__default["default"].createElement("span", { + className: "tooltiptext", + ref: scrubberTooltipRef, + "aria-hidden": true, + role: "presentation" + })), /*#__PURE__*/React__default["default"].createElement("p", { + className: "vjs-time track-duration", + role: "presentation" + }))); } + VideoJSPlayer.propTypes = { + enableFileDownload: PropTypes.bool, + enableTitleLink: PropTypes.bool, + isVideo: PropTypes.bool, + options: PropTypes.object, + placeholderText: PropTypes.string, + scrubberTooltipRef: PropTypes.object, + tracks: PropTypes.array, + trackScrubberRef: PropTypes.object, + videoJSLangMap: PropTypes.string, + withCredentials: PropTypes.bool + }; + + var Play = "Play"; + var Pause = "Pause"; + var Replay = "Replay"; + var Duration = "Duration"; + var LIVE = "LIVE"; + var Loaded = "Loaded"; + var Progress = "Progress"; + var Fullscreen = "Fullscreen"; + var Mute = "Mute"; + var Unmute = "Unmute"; + var Subtitles = "Subtitles"; + var Captions = "Captions"; + var Chapters = "Chapters"; + var Descriptions = "Descriptions"; + var Close = "Close"; + var Text = "Text"; + var White = "White"; + var Black = "Black"; + var Red = "Red"; + var Green = "Green"; + var Blue = "Blue"; + var Yellow = "Yellow"; + var Magenta = "Magenta"; + var Cyan = "Cyan"; + var Background = "Background"; + var Window = "Window"; + var Transparent = "Transparent"; + var Opaque = "Opaque"; + var None = "None"; + var Raised = "Raised"; + var Depressed = "Depressed"; + var Uniform = "Uniform"; + var Casual = "Casual"; + var Script = "Script"; + var Reset = "Reset"; + var Done = "Done"; + var Color = "Color"; + var Opacity = "Opacity"; + var en = { + "Audio Player": "Audio Player", + "Video Player": "Video Player", + Play: Play, + Pause: Pause, + Replay: Replay, + "Current Time": "Current Time", + Duration: Duration, + "Remaining Time": "Remaining Time", + "Stream Type": "Stream Type", + LIVE: LIVE, + "Seek to live, currently behind live": "Seek to live, currently behind live", + "Seek to live, currently playing live": "Seek to live, currently playing live", + Loaded: Loaded, + Progress: Progress, + "Progress Bar": "Progress Bar", + "progress bar timing: currentTime={1} duration={2}": "{1} of {2}", + Fullscreen: Fullscreen, + "Exit Fullscreen": "Exit Fullscreen", + Mute: Mute, + Unmute: Unmute, + "Playback Rate": "Playback Rate", + Subtitles: Subtitles, + "subtitles off": "subtitles off", + Captions: Captions, + "captions off": "captions off", + Chapters: Chapters, + Descriptions: Descriptions, + "descriptions off": "descriptions off", + "Audio Track": "Audio Track", + "Volume Level": "Volume Level", + "You aborted the media playback": "You aborted the media playback", + "A network error caused the media download to fail part-way.": "A network error caused the media download to fail part-way.", + "The media could not be loaded, either because the server or network failed or because the format is not supported.": "The media could not be loaded, either because the server or network failed or because the format is not supported.", + "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.", + "No compatible source was found for this media.": "No compatible source was found for this media.", + "The media is encrypted and we do not have the keys to decrypt it.": "The media is encrypted and we do not have the keys to decrypt it.", + "Play Video": "Play Video", + Close: Close, + "Close Modal Dialog": "Close Modal Dialog", + "Modal Window": "Modal Window", + "This is a modal window": "This is a modal window", + "This modal can be closed by pressing the Escape key or activating the close button.": "This modal can be closed by pressing the Escape key or activating the close button.", + ", opens captions settings dialog": ", opens captions settings dialog", + ", opens subtitles settings dialog": ", opens subtitles settings dialog", + ", opens descriptions settings dialog": ", opens descriptions settings dialog", + ", selected": ", selected", + "captions settings": "captions settings", + "subtitles settings": "subtitles settings", + "descriptions settings": "descriptions settings", + Text: Text, + White: White, + Black: Black, + Red: Red, + Green: Green, + Blue: Blue, + Yellow: Yellow, + Magenta: Magenta, + Cyan: Cyan, + Background: Background, + Window: Window, + Transparent: Transparent, + "Semi-Transparent": "Semi-Transparent", + Opaque: Opaque, + "Font Size": "Font Size", + "Text Edge Style": "Text Edge Style", + None: None, + Raised: Raised, + Depressed: Depressed, + Uniform: Uniform, + "Drop shadow": "Drop shadow", + "Font Family": "Font Family", + "Proportional Sans-Serif": "Proportional Sans-Serif", + "Monospace Sans-Serif": "Monospace Sans-Serif", + "Proportional Serif": "Proportional Serif", + "Monospace Serif": "Monospace Serif", + Casual: Casual, + Script: Script, + "Small Caps": "Small Caps", + Reset: Reset, + "restore all settings to the default values": "restore all settings to the default values", + Done: Done, + "Caption Settings Dialog": "Caption Settings Dialog", + "Beginning of dialog window. Escape will cancel and close the window.": "Beginning of dialog window. Escape will cancel and close the window.", + "End of dialog window.": "End of dialog window.", + "{1} is loading.": "{1} is loading.", + "Exit Picture-in-Picture": "Exit Picture-in-Picture", + "Picture-in-Picture": "Picture-in-Picture", + "No content": "No content", + Color: Color, + Opacity: Opacity, + "Text Background": "Text Background", + "Caption Area Background": "Caption Area Background", + "Playing in Picture-in-Picture": "Playing in Picture-in-Picture", + "Skip backward {1} seconds": "Skip backward {1} seconds", + "Skip forward {1} seconds": "Skip forward {1} seconds" + }; + + function ownKeys$3(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } + function _objectSpread$3(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys$3(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys$3(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } + var PLAYER_ID = "iiif-media-player"; /** - * Parse the content search response from the search service, and then use it to calculate - * number of search hits for each transcripts, and create a list of matched transcript - * lines for the search in the current transcript - * @param {Object} response JSON response from content search API - * @param {String} query search query from transcript search - * @param {Array} trancripts content of the displayed transcript with ids - * @param {String} selectedTranscript url of the selected transcript - * @returns a list of matched transcript lines for the current search + * Parse resource related information form the current canvas in manifest, + * and build an options object for Video.js using that information. + * @param {Object} props + * @param {Boolean} props.enableFileDownload + * @param {Boolean} props.enablePIP + * @param {Boolean} props.enablePlaybackRate + * @param {Boolean} props.enableTitleLink + * @param {Boolean} props.withCredentials + * @param {String} props.language */ - var parseContentSearchResponse = function parseContentSearchResponse(response, query, trancripts, selectedTranscript) { - var _response$items; - if (!response || response === undefined) return []; - var hitCounts = []; - var searchHits = []; - if (((_response$items = response.items) === null || _response$items === void 0 ? void 0 : _response$items.length) > 0) { - var items = response.items; - items.map(function (item) { - var anno = new manifesto_js.Annotation(item); - // Exclude annotations without supplementing motivation - if (anno.getMotivation() != 'supplementing') return; - var target = anno.getTarget(); - var targetURI = getCanvasId(target); - var value = anno.getBody()[0].getProperty('value'); - var hitCount = getHitCountForCue(value, query, true); - searchHits.push({ - target: target, - targetURI: targetURI, - value: value, - hitCount: hitCount - }); - }); - } - // Group search responses by transcript - var allSearchHits = groupBy(searchHits, 'targetURI'); + var MediaPlayer = function MediaPlayer(_ref) { + var _ref$enableFileDownlo = _ref.enableFileDownload, + enableFileDownload = _ref$enableFileDownlo === void 0 ? false : _ref$enableFileDownlo, + _ref$enablePIP = _ref.enablePIP, + enablePIP = _ref$enablePIP === void 0 ? false : _ref$enablePIP, + _ref$enablePlaybackRa = _ref.enablePlaybackRate, + enablePlaybackRate = _ref$enablePlaybackRa === void 0 ? false : _ref$enablePlaybackRa, + _ref$enableTitleLink = _ref.enableTitleLink, + enableTitleLink = _ref$enableTitleLink === void 0 ? false : _ref$enableTitleLink, + _ref$withCredentials = _ref.withCredentials, + withCredentials = _ref$withCredentials === void 0 ? false : _ref$withCredentials, + _ref$language = _ref.language, + language = _ref$language === void 0 ? 'en' : _ref$language; + var manifestState = useManifestState(); + var playerState = usePlayerState(); + var _useErrorBoundary = reactErrorBoundary.useErrorBoundary(), + showBoundary = _useErrorBoundary.showBoundary; + var srcIndex = manifestState.srcIndex, + hasStructure = manifestState.hasStructure, + playlist = manifestState.playlist; + var isPlaylist = playlist.isPlaylist; + playerState.playerFocusElement; + var currentTime = playerState.currentTime; + var trackScrubberRef = React.useRef(); + var timeToolRef = React.useRef(); + var videoJSLangMap = React.useRef('{}'); + var _useMediaPlayer = useMediaPlayer(), + canvasIsEmpty = _useMediaPlayer.canvasIsEmpty, + canvasIndex = _useMediaPlayer.canvasIndex, + isMultiCanvased = _useMediaPlayer.isMultiCanvased, + lastCanvasIndex = _useMediaPlayer.lastCanvasIndex; + var _useSetupPlayer = useSetupPlayer({ + enableFileDownload: enableFileDownload, + withCredentials: withCredentials, + lastCanvasIndex: lastCanvasIndex + }), + isMultiSourced = _useSetupPlayer.isMultiSourced, + isVideo = _useSetupPlayer.isVideo, + playerConfig = _useSetupPlayer.playerConfig, + ready = _useSetupPlayer.ready, + renderingFiles = _useSetupPlayer.renderingFiles, + nextItemClicked = _useSetupPlayer.nextItemClicked, + switchPlayer = _useSetupPlayer.switchPlayer; + var error = playerConfig.error, + poster = playerConfig.poster, + sources = playerConfig.sources, + targets = playerConfig.targets, + tracks = playerConfig.tracks; - // Calculate search hit count for each transcript in the Canvas - for (var _i = 0, _Object$entries = Object.entries(allSearchHits); _i < _Object$entries.length; _i++) { - var _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), - key = _Object$entries$_i[0], - value = _Object$entries$_i[1]; - hitCounts.push({ - transcriptURL: key, - numberOfHits: value.reduce(function (acc, a) { - return acc + a.hitCount; - }, 0) + // Using dynamic imports to enforce code-splitting in webpack + // https://webpack.js.org/api/module-methods/#dynamic-expressions-in-import + var loadVideoJSLanguageMap = React.useMemo(function () { + return /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/regenerator.mark(function _callee() { + var resources; + return regenerator.wrap(function _callee$(_context) { + while (1) switch (_context.prev = _context.next) { + case 0: + _context.prev = 0; + _context.next = 3; + return import("video.js/dist/lang/".concat(language, ".json")); + case 3: + resources = _context.sent; + videoJSLangMap.current = JSON.stringify(resources); + _context.next = 11; + break; + case 7: + _context.prev = 7; + _context.t0 = _context["catch"](0); + console.warn("".concat(language, " is not available, defaulting to English")); + videoJSLangMap.current = JSON.stringify(en); + case 11: + case "end": + return _context.stop(); + } + }, _callee, null, [[0, 7]]); + })); + }, [language]); + React.useEffect(function () { + try { + loadVideoJSLanguageMap(); + } catch (e) { + showBoundary(e); + } + }, []); + + // Default VideoJS options not updated with the Canvas data + var defaultOptions = React.useMemo(function () { + return { + autoplay: false, + id: PLAYER_ID, + playbackRates: enablePlaybackRate ? [0.5, 0.75, 1, 1.5, 2] : [], + experimentalSvgIcons: true, + controls: true, + fluid: true, + language: language, + // Setting inactivity timeout to zero in mobile and tablet devices translates to + // user is always active. And the control bar is not hidden when user is active. + // With this user can always use the controls when the media is playing. + inactivityTimeout: IS_MOBILE || IS_TOUCH_ONLY ? 0 : 2000, + // Enable native text track functionality in iPhones and iPads + html5: { + nativeTextTracks: IS_MOBILE && !IS_ANDROID + }, + // Make error display modal dismissable + errorDisplay: { + uncloseable: false + }, + /* + Setting this option helps to override VideoJS's default 'keydown' event handler, whenever + the focus is on a native VideoJS control icon (e.g. play toggle). + E.g. click event on 'playtoggle' sets the focus on the play/pause button, + which has VideoJS's 'handleKeydown' event handler attached to it. Therefore, as long as the + focus is on the play/pause button the 'keydown' event will pass through VideoJS's default + 'keydown' event handler, without ever reaching the 'keydown' handler setup on the document + in Ramp code. + When this option is setup VideoJS's 'handleKeydown' event handler passes the event to the + function setup under the 'hotkeys' option when the native player controls are focused. + In Safari, this works without using 'hotkeys' option, therefore only set this in other browsers. + */ + userActions: { + hotkeys: !IS_SAFARI ? function (e) { + playerHotKeys(e, this); + } : undefined + }, + videoJSTitleLink: enableTitleLink + }; + }, [language, enablePlaybackRate, enableTitleLink]); + + // Build VideoJS options for the current Canvas from defaultOptions + var videoJSOptions = React.useMemo(function () { + return !canvasIsEmpty ? _objectSpread$3(_objectSpread$3({}, defaultOptions), {}, { + aspectRatio: isVideo ? '16:9' : '1:0', + audioOnlyMode: !isVideo, + bigPlayButton: isVideo, + poster: isVideo ? poster : null, + controlBar: { + // Define and order control bar controls + // See https://docs.videojs.com/tutorial-components.html for options of what + // seem to be supported controls + children: [isMultiCanvased ? 'videoJSPreviousButton' : '', 'playToggle', isMultiCanvased ? 'videoJSNextButton' : '', 'videoJSProgress', 'videoJSCurrentTime', 'timeDivider', 'durationDisplay', + // These icons are in reverse order to support `float: inline-end` in CSS + 'fullscreenToggle', enableFileDownload ? 'videoJSFileDownload' : '', enablePIP ? 'pictureInPictureToggle' : '', enablePlaybackRate ? 'playbackRateMenuButton' : '', 'qualitySelector', hasStructure || isPlaylist ? 'videoJSTrackScrubber' : '', tracks.length > 0 && isVideo ? 'subsCapsButton' : '', IS_MOBILE ? 'muteToggle' : 'volumePanel' + // 'vjsYo', custom component + ], + + videoJSProgress: { + srcIndex: srcIndex, + targets: targets, + currentTime: currentTime !== null && currentTime !== void 0 ? currentTime : 0, + nextItemClicked: nextItemClicked + }, + videoJSCurrentTime: { + srcIndex: srcIndex, + targets: targets, + currentTime: currentTime || 0 + }, + videoJSFileDownload: enableFileDownload && { + title: 'Download Files', + controlText: 'Alternate resource download', + files: renderingFiles + }, + videoJSPreviousButton: isMultiCanvased && { + canvasIndex: canvasIndex, + switchPlayer: switchPlayer + }, + videoJSNextButton: isMultiCanvased && { + canvasIndex: canvasIndex, + lastCanvasIndex: lastCanvasIndex, + switchPlayer: switchPlayer + }, + videoJSTrackScrubber: (hasStructure || isPlaylist) && { + trackScrubberRef: trackScrubberRef, + timeToolRef: timeToolRef, + isPlaylist: isPlaylist + } + }, + sources: isMultiSourced ? [sources[srcIndex]] : sources + }) : _objectSpread$3(_objectSpread$3({}, defaultOptions), {}, { + sources: [] }); + }, [isVideo, playerConfig, srcIndex]); + if (ready && videoJSOptions != undefined || canvasIsEmpty) { + return /*#__PURE__*/React__default["default"].createElement("div", { + "data-testid": "media-player", + className: "ramp--media_player", + role: "presentation" + }, /*#__PURE__*/React__default["default"].createElement(VideoJSPlayer, { + enableFileDownload: enableFileDownload, + enableTitleLink: enableTitleLink, + isVideo: isVideo, + options: videoJSOptions, + placeholderText: error, + scrubberTooltipRef: timeToolRef, + tracks: tracks, + trackScrubberRef: trackScrubberRef, + videoJSLangMap: videoJSLangMap.current, + withCredentials: withCredentials + })); + } else { + return null; } - - // Get all the matching transcript lines with the query in the current transcript - var matchedTranscriptLines = getMatchedTranscriptLines(allSearchHits[selectedTranscript], query, trancripts); - return { - matchedTranscriptLines: matchedTranscriptLines, - hitCounts: hitCounts, - allSearchHits: allSearchHits - }; }; + MediaPlayer.propTypes = { + enableFileDownload: PropTypes.bool, + enablePIP: PropTypes.bool, + enablePlaybackRate: PropTypes.bool, + enableTitleLink: PropTypes.bool, + withCredentials: PropTypes.bool, + language: PropTypes.string + }; + + var _extends_1 = createCommonjsModule(function (module) { + function _extends() { + module.exports = _extends = Object.assign ? Object.assign.bind() : function (target) { + for (var i = 1; i < arguments.length; i++) { + var source = arguments[i]; + for (var key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + } + return target; + }, module.exports.__esModule = true, module.exports["default"] = module.exports; + return _extends.apply(this, arguments); + } + module.exports = _extends, module.exports.__esModule = true, module.exports["default"] = module.exports; + }); + + var _extends = /*@__PURE__*/getDefaultExportFromCjs(_extends_1); /** - * Create a list matched transcript lines for the current search for the displayed transcript - * @param {Array} searchHits a list of matched transcript lines with ids from the current transcript - * @param {String} query search query - * @param {Array} transcripts list of all the transcript lines from the current transcript - * @returns a list of matched transcrip lines in the current transcript + * Build leaf-level nodes in the structures in Manifest. These nodes can be + * either timespans (with media fragment) or titles (w/o media fragment). + * @param {Object} props + * @param {Number} props.duration duration of the item + * @param {String} props.id media fragemnt of the item + * @param {Boolean} props.isTitle flag to indicate item w/o mediafragment + * @param {Boolean} props.isCanvas flag to indicate item is at Canvas-level + * @param {Boolean} props.isClickable flag to indicate item is within resource duration + * @param {Boolean} props.isEmpty flag to indicate Canvas associated with item is inaccessible + * @param {String} props.label text label of the item + * @param {String} props.summary summary associated with the item (in playlist context) + * @param {String} props.homepage homepage associated with the item (in playlist context) + * @param {Array} props.items list of children for the item + * @param {Number} props.itemIndex index of the item within the section/canvas + * @param {String} props.rangeId unique id of the item + * @param {Number} props.canvasDuration duration of the Canvas associated with the item + * @param {Object} props.sectionRef React ref of the section element associated with the item + * @param {Object} props.structureContainerRef React ref of the structure container */ - var getMatchedTranscriptLines = function getMatchedTranscriptLines(searchHits, query, transcripts) { - var qStr = query.trim().toLocaleLowerCase(); - var transcriptLines = []; - if (searchHits === undefined) return; - var traversedIds = []; - searchHits.map(function (item, index) { - var target = item.target, - value = item.value; - // Read time offsets and text of the search hit - var timeRange = getMediaFragment(target); - - // Replace all HTML tags - var mappedText = value.replace(/<\/?[^>]+>/gi, ''); - var start = 0, - end = 0; - var transcriptId = undefined; - if (timeRange != undefined) { - // For timed-text - start = timeRange.start; - end = timeRange.end; - transcriptId = transcripts.findIndex(function (t) { - return t.begin == start && t.end == end; - }); - var queryText = qStr.match(/[a-zA-Z]+/gi) ? qStr.match(/[a-zA-Z]+/gi)[0] : qStr; - var matchOffset = mappedText.toLocaleLowerCase().indexOf(queryText); - if (matchOffset !== -1 && transcriptId != undefined) { - var match = markMatchedParts(value, qStr, item.hitCount, true); - transcriptLines.push({ - tag: TRANSCRIPT_CUE_TYPES.timedCue, - begin: start, - end: end, - id: transcriptId, - match: match, - matchCount: item.hitCount, - text: value - }); - } - } else { - /** - * For non timed text, there's no unique id to match the search response to the transcript - * lines in the UI. So use filter() method instead of findIndex() method to get all matching - * transcript lines in the display. - * Use traversedIds array to remember the ids of already processed transcript lines in the list - * to avoid duplication in the matches. - */ - var hitsInfo = matchPartsInUntimedText(transcripts, mappedText, qStr, traversedIds); - traversedIds = hitsInfo.traversedIds; - transcriptLines = [].concat(_toConsumableArray(transcriptLines), _toConsumableArray(hitsInfo.hits)); + var ListItem = function ListItem(_ref) { + var duration = _ref.duration, + id = _ref.id, + isTitle = _ref.isTitle, + isCanvas = _ref.isCanvas, + isClickable = _ref.isClickable, + isEmpty = _ref.isEmpty, + label = _ref.label, + summary = _ref.summary, + homepage = _ref.homepage, + items = _ref.items, + itemIndex = _ref.itemIndex, + rangeId = _ref.rangeId, + canvasDuration = _ref.canvasDuration, + sectionRef = _ref.sectionRef, + structureContainerRef = _ref.structureContainerRef; + var liRef = React.useRef(null); + var _useActiveStructure = useActiveStructure({ + itemId: id, + liRef: liRef, + sectionRef: sectionRef, + isCanvas: isCanvas, + canvasDuration: canvasDuration + }), + handleClick = _useActiveStructure.handleClick, + isActiveLi = _useActiveStructure.isActiveLi, + currentNavItem = _useActiveStructure.currentNavItem, + isPlaylist = _useActiveStructure.isPlaylist; + var subMenu = items && items.length > 0 ? /*#__PURE__*/React__default["default"].createElement(List, { + items: items, + sectionRef: sectionRef, + structureContainerRef: structureContainerRef, + isPlaylist: isPlaylist + }) : null; - /** - * When backend has a single block of text which is chuncked in the UI this helps to - * traverse all transcript cues. - */ - while (index === searchHits.length - 1 && ((_traversedIds = traversedIds) === null || _traversedIds === void 0 ? void 0 : _traversedIds.length) < transcripts.length) { - var _traversedIds; - var _hitsInfo = matchPartsInUntimedText(transcripts, mappedText, qStr, traversedIds); - traversedIds = _hitsInfo.traversedIds; - transcriptLines = [].concat(_toConsumableArray(transcriptLines), _toConsumableArray(_hitsInfo.hits)); - } + /* + Auto-scroll active structure item into view only when user is not actively + interacting with structured navigation + */ + React.useEffect(function () { + if (liRef.current && (currentNavItem === null || currentNavItem === void 0 ? void 0 : currentNavItem.id) == id && liRef.current.isClicked != undefined && !liRef.current.isClicked && structureContainerRef.current.isScrolling != undefined && !structureContainerRef.current.isScrolling) { + autoScroll(liRef.current, structureContainerRef); } - }); - return transcriptLines; + // Reset isClicked if active structure item is set + if (liRef.current) { + liRef.current.isClicked = false; + } + }, [currentNavItem]); + var renderListItem = function renderListItem() { + return /*#__PURE__*/React__default["default"].createElement(React.Fragment, { + key: rangeId + }, /*#__PURE__*/React__default["default"].createElement(React.Fragment, null, isTitle ? /*#__PURE__*/React__default["default"].createElement("span", { + className: "ramp--structured-nav__item-title", + role: "listitem", + "aria-label": label + }, label) : /*#__PURE__*/React__default["default"].createElement(React.Fragment, { + key: id + }, /*#__PURE__*/React__default["default"].createElement("div", { + className: "tracker" + }), isClickable ? /*#__PURE__*/React__default["default"].createElement(React.Fragment, null, isEmpty && /*#__PURE__*/React__default["default"].createElement(LockedSVGIcon, null), /*#__PURE__*/React__default["default"].createElement("a", { + role: "listitem", + href: homepage && homepage != '' ? homepage : id, + onClick: handleClick + }, "".concat(itemIndex, ". "), label, " ", duration.length > 0 ? " (".concat(duration, ")") : '')) : /*#__PURE__*/React__default["default"].createElement("span", { + role: "listitem", + "aria-label": label + }, label)))); + }; + if (label != '') { + return /*#__PURE__*/React__default["default"].createElement("li", { + "data-testid": "list-item", + ref: liRef, + className: cx__default["default"]('ramp--structured-nav__list-item', isActiveLi ? 'active' : ''), + "data-label": label, + "data-summary": summary + }, renderListItem(), subMenu); + } else { + return null; + } + }; + ListItem.propTypes = { + duration: PropTypes.string.isRequired, + id: PropTypes.string, + isTitle: PropTypes.bool.isRequired, + isCanvas: PropTypes.bool.isRequired, + isClickable: PropTypes.bool.isRequired, + isEmpty: PropTypes.bool.isRequired, + label: PropTypes.string.isRequired, + summary: PropTypes.string, + homepage: PropTypes.string, + items: PropTypes.array.isRequired, + itemIndex: PropTypes.number, + rangeId: PropTypes.string.isRequired, + canvasDuration: PropTypes.number.isRequired, + sectionRef: PropTypes.object.isRequired, + structureContainerRef: PropTypes.object.isRequired }; /** - * Build a list of matched indexed transcript lines from content search response. - * In Avalon, docx and plain text files are chunked by paragraphs seperated by 2 or - * more new line characters. So, depending on the way the file is formatted the search - * response could include chunks of the text or the full text. - * In the library (mammoth) used in Transcript component to display docx files; the text is chunked - * into paragraphs seperated by one or more new line characters. - * And the search response doesn't include any text styling in the docx files. Therefore the - * text with style information is reformatted to include text highlights from the search response. - * This function uses the search response to calculate the hit counts and mark them for each indexed transcript - * line in the front-end to get the correct counts. - * @param {Array} transcripts indexed transcript text in UI - * @param {String} mappedText matched text from content search - * @param {String} query search query entered by the user - * @param {Array} traversedIds already included transcript indices - * @returns a list of matched transcript lines + * Build Canvas level range items. When the range has child elements nested make it + * collapsible. + * @param {Object} props + * @param {Number} props.duration range duration + * @param {Boolean} props.hasChildren flag to indicate presence of child structure in range + * @param {String} props.itemId media fragment if associated with the range + * @param {Number} props.itemIndex index of the canvas in structures + * @param {Array} props.items list of children structure items in range + * @param {Boolean} props.isRoot flag to indicate root range on top of structures + * @param {String} props.label text label to be displayed + * @param {Object} props.sectionRef React ref of the section element associated with the item + * @param {Object} props.structureContainerRef React ref of the structure container */ - var matchPartsInUntimedText = function matchPartsInUntimedText(transcripts, mappedText, query, traversedIds) { - var escapedQ = buildRegexReadyText(query, true, false); - // Get hit counts for the current text, ignore matches with query preceded by - or ' - var qRegex = new RegExp(String.raw(_templateObject$1 || (_templateObject$1 = _taggedTemplateLiteral(["\b", "\b"], ["\\b", "\\b"])), escapedQ), 'gi'); - var matched = []; - // Start from the next cue after the last traveresed cue in the transcript - var lastTraversedId = traversedIds[traversedIds.length - 1] + 1 || 0; + var SectionHeading = function SectionHeading(_ref) { + var duration = _ref.duration, + _ref$hasChildren = _ref.hasChildren, + hasChildren = _ref$hasChildren === void 0 ? false : _ref$hasChildren, + itemId = _ref.itemId, + itemIndex = _ref.itemIndex, + items = _ref.items, + _ref$isRoot = _ref.isRoot, + isRoot = _ref$isRoot === void 0 ? false : _ref$isRoot, + label = _ref.label, + sectionRef = _ref.sectionRef, + structureContainerRef = _ref.structureContainerRef; + var _useState = React.useState(false), + _useState2 = _slicedToArray(_useState, 2), + isOpen = _useState2[0], + setIsOpen = _useState2[1]; + var toggleOpen = function toggleOpen(e) { + setIsOpen(!isOpen); + sectionRef.current.isOpen = true; + }; + var _useActiveStructure = useActiveStructure({ + itemIndex: itemIndex, + isRoot: isRoot, + itemId: itemId, + liRef: sectionRef, + sectionRef: sectionRef, + isCanvas: true, + canvasDuration: duration, + setIsOpen: setIsOpen + }), + isActiveSection = _useActiveStructure.isActiveSection, + canvasIndex = _useActiveStructure.canvasIndex, + handleClick = _useActiveStructure.handleClick, + isPlaylist = _useActiveStructure.isPlaylist; - /** - * For untimed text the search response text could be either, - * - mapped one to one with the cue text in Transcript component - * - include a part of the cue text in Transcript component - * When none of these work check if the cue text contains the search query - */ - for (var i = lastTraversedId; i < transcripts.length; i++) { - var t = transcripts[i]; - var cleanedText = t.text.replace(/<\/?[^>]+>/gi, '').trim(); - var matches = _toConsumableArray(cleanedText.matchAll(qRegex)); - var mappedTextCleaned = mappedText.trim(); - if (mappedTextCleaned == cleanedText || mappedTextCleaned.includes(cleanedText) && (matches === null || matches === void 0 ? void 0 : matches.length) > 0) { - t.matchCount = matches === null || matches === void 0 ? void 0 : matches.length; - matched.push(t); - traversedIds.push(t.id); - break; - } else if ((matches === null || matches === void 0 ? void 0 : matches.length) > 0) { - var _ref2; - t.matchCount = (_ref2 = _toConsumableArray(mappedTextCleaned.matchAll(qRegex))) === null || _ref2 === void 0 ? void 0 : _ref2.length; - matched.push(t); - traversedIds.push(t.id); - break; - } else { - traversedIds.push(t.id); + /* + Auto-scroll active section into view only when user is not + actively interacting with structured navigation + */ + React.useEffect(function () { + if (canvasIndex + 1 === itemIndex && sectionRef.current && sectionRef.current.isClicked != undefined && !sectionRef.current.isClicked && structureContainerRef.current.isScrolling != undefined && !structureContainerRef.current.isScrolling) { + autoScroll(sectionRef.current, structureContainerRef); } - } - var hits = []; - matched.map(function (m) { - var value = addStyledHighlights(m.textDisplayed, query); - var match = markMatchedParts(value, query, m.matchCount, true); - hits.push({ - tag: TRANSCRIPT_CUE_TYPES.nonTimedLine, - begin: undefined, - end: undefined, - id: m.id, - match: match, - matchCount: m.matchCount, - text: value - }); - }); - return { - hits: hits, - traversedIds: traversedIds + sectionRef.current.isClicked = false; + }, [canvasIndex]); + var collapsibleButton = function collapsibleButton() { + return /*#__PURE__*/React__default["default"].createElement("button", { + className: "collapse-expand-button", + "data-testid": "section-collapse-icon", + onClick: toggleOpen + }, /*#__PURE__*/React__default["default"].createElement("i", { + className: cx__default["default"]('arrow', isOpen ? 'up' : 'down') + })); }; + return /*#__PURE__*/React__default["default"].createElement("div", { + className: cx__default["default"]('ramp--structured-nav__section', isActiveSection ? 'active' : ''), + role: "listitem", + "data-testid": "listitem-section", + ref: sectionRef, + "data-label": label, + "data-mediafrag": itemId !== null && itemId !== void 0 ? itemId : '' + }, /*#__PURE__*/React__default["default"].createElement("div", { + className: "section-head-buttons" + }, /*#__PURE__*/React__default["default"].createElement("button", { + "data-testid": itemId == undefined ? "listitem-section-span" : "listitem-section-button", + ref: sectionRef, + onClick: handleClick, + className: cx__default["default"]('ramp--structured-nav__section-title', !itemId && 'not-clickable') + }, /*#__PURE__*/React__default["default"].createElement("span", { + className: "ramp--structured-nav__title", + "aria-label": label, + role: "listitem" + }, isRoot ? '' : "".concat(itemIndex, ". "), label, duration != '' && /*#__PURE__*/React__default["default"].createElement("span", { + className: "ramp--structured-nav__section-duration" + }, duration))), hasChildren && collapsibleButton()), isOpen && hasChildren && /*#__PURE__*/React__default["default"].createElement(List, { + items: items, + sectionRef: sectionRef, + key: itemId, + structureContainerRef: structureContainerRef, + isPlaylist: isPlaylist + })); + }; + SectionHeading.propTypes = { + itemIndex: PropTypes.number.isRequired, + canvasIndex: PropTypes.number, + duration: PropTypes.string.isRequired, + label: PropTypes.string.isRequired, + sectionRef: PropTypes.object.isRequired, + itemId: PropTypes.string, + isRoot: PropTypes.bool, + structureContainerRef: PropTypes.object.isRequired, + hasChildren: PropTypes.bool, + items: PropTypes.array }; /** - * Generic function to mark the matched transcript text in the cue where the output has - * surrounding the matched parts - * within the cue. - * @param {String} text matched transcript text/cue - * @param {String} query current search query - * @param {Numner} hitCount number of hits returned in the search response - * @param {Boolean} hasHighlight boolean flag to indicate text has tags - * @returns matched cue with HTML tags added for marking the hightlight + * Build a section of structure using a